Add test & user enabled state in set user password

Support for test password and user enabled state in Set user
password command

Unit Test:
1. Performed user enabled & disabled and verified the state change
2. Performed test user password - both pass & fail condition

Change-Id: I0eb91ad849e43072b1d9e90d777304fabdfb40bc
Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp
index 3e4490c..5a4d7ad 100644
--- a/user_channel/user_layer.cpp
+++ b/user_channel/user_layer.cpp
@@ -109,6 +109,11 @@
     return IPMI_CC_OK;
 }
 
+ipmi_ret_t ipmiUserUpdateEnabledState(const uint8_t& userId, const bool& state)
+{
+    return getUserAccessObject().setUserEnabledState(userId, state);
+}
+
 ipmi_ret_t ipmiUserCheckEnabled(const uint8_t& userId, bool& state)
 {
     if (!UserAccess::isValidUserId(userId))
diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp
index 5136e86..8c81188 100644
--- a/user_channel/user_layer.hpp
+++ b/user_channel/user_layer.hpp
@@ -146,6 +146,15 @@
 ipmi_ret_t ipmiUserGetAllCounts(uint8_t& maxChUsers, uint8_t& enabledUsers,
                                 uint8_t& fixedUsers);
 
+/** @brief function to update user enabled state
+ *
+ *  @param[in] userId - user id
+ *..@param[in] state - state of the user to be updated, true - user enabled.
+ *
+ *  @return IPMI_CC_OK for success, others for failure.
+ */
+ipmi_ret_t ipmiUserUpdateEnabledState(const uint8_t& userId, const bool& state);
+
 /** @brief determines whether user is enabled
  *
  *  @param[in] userId - user id
diff --git a/user_channel/user_mgmt.cpp b/user_channel/user_mgmt.cpp
index e320e3f..e90bff6 100644
--- a/user_channel/user_mgmt.cpp
+++ b/user_channel/user_mgmt.cpp
@@ -631,6 +631,33 @@
     return true;
 }
 
+ipmi_ret_t UserAccess::setUserEnabledState(const uint8_t& userId,
+                                           const bool& enabledState)
+{
+    if (!isValidUserId(userId))
+    {
+        return IPMI_CC_PARM_OUT_OF_RANGE;
+    }
+    boost::interprocess::scoped_lock<boost::interprocess::named_recursive_mutex>
+        userLock{*userMutex};
+    UserInfo* userInfo = getUserInfo(userId);
+    std::string userName;
+    userName.assign(reinterpret_cast<char*>(userInfo->userName), 0,
+                    ipmiMaxUserName);
+    if (userName.empty())
+    {
+        log<level::DEBUG>("User name not set / invalid");
+        return IPMI_CC_UNSPECIFIED_ERROR;
+    }
+    if (userInfo->userEnabled != enabledState)
+    {
+        std::string userPath = std::string(userObjBasePath) + "/" + userName;
+        setDbusProperty(bus, getUserServiceName().c_str(), userPath.c_str(),
+                        usersInterface, userEnabledProperty, enabledState);
+    }
+    return IPMI_CC_OK;
+}
+
 ipmi_ret_t UserAccess::setUserPrivilegeAccess(const uint8_t& userId,
                                               const uint8_t& chNum,
                                               const UserPrivAccess& privAccess,
diff --git a/user_channel/user_mgmt.hpp b/user_channel/user_mgmt.hpp
index 16dbd31..5755fa0 100644
--- a/user_channel/user_mgmt.hpp
+++ b/user_channel/user_mgmt.hpp
@@ -183,6 +183,16 @@
      */
     ipmi_ret_t setUserName(const uint8_t& userId, const char* userNameInChar);
 
+    /** @brief to set user enabled state
+     *
+     *  @param[in] userId - user id
+     *  @param[in] enabledState - enabled state of the user
+     *
+     *  @return IPMI_CC_OK for success, others for failure.
+     */
+    ipmi_ret_t setUserEnabledState(const uint8_t& userId,
+                                   const bool& enabledState);
+
     /** @brief to set user privilege and access details
      *
      *  @param[in] userId - user id
diff --git a/user_channel/usercommands.cpp b/user_channel/usercommands.cpp
index 0ed5b8f..84e9456 100644
--- a/user_channel/usercommands.cpp
+++ b/user_channel/usercommands.cpp
@@ -429,25 +429,42 @@
         if (!std::regex_match(passwd.c_str(),
                               std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
         {
-            log<level::DEBUG>("Invalid password fields",
-                              entry("USER-ID:%d", (uint8_t)req->userId));
+            log<level::ERR>("Invalid password fields",
+                            entry("USER-ID:%d", (uint8_t)req->userId));
             return IPMI_CC_INVALID_FIELD_REQUEST;
         }
         if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
         {
-            log<level::DEBUG>("Failed to update password",
-                              entry("USER-ID:%d", (uint8_t)req->userId));
-            return IPMI_CC_UNSPECIFIED_ERROR;
+            log<level::ERR>("Failed to update password",
+                            entry("USER-ID:%d", (uint8_t)req->userId));
+            return IPMI_CC_INVALID_FIELD_REQUEST;
         }
+        return IPMI_CC_OK;
     }
-    else
+    else if (req->operation == enableUser || req->operation == disableUser)
     {
-        // TODO: test the password by reading the encrypted file
-        log<level::ERR>(
-            "Other operations not implemented - TODO yet to implement");
-        return IPMI_CC_INVALID_FIELD_REQUEST;
+        return ipmiUserUpdateEnabledState(req->userId,
+                                          static_cast<bool>(req->operation));
     }
-    return IPMI_CC_OK;
+    else if (req->operation == testPassword)
+    {
+        auto password = ipmiUserGetPassword(userName);
+        std::string testPassword(
+            reinterpret_cast<const char*>(req->userPassword), 0,
+            passwordLength);
+        // Note: For security reasons password size won't be compared and
+        // wrong password size completion code will not be returned if size
+        // doesn't match as specified in IPMI specification.
+        if (password != testPassword)
+        {
+            log<level::DEBUG>("Test password failed",
+                              entry("USER-ID:%d", (uint8_t)req->userId));
+            return static_cast<ipmi_ret_t>(
+                IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch);
+        }
+        return IPMI_CC_OK;
+    }
+    return IPMI_CC_INVALID_FIELD_REQUEST;
 }
 
 void registerUserIpmiFunctions()
diff --git a/user_channel/usercommands.hpp b/user_channel/usercommands.hpp
index ee33b5a..afbe67a 100644
--- a/user_channel/usercommands.hpp
+++ b/user_channel/usercommands.hpp
@@ -29,6 +29,12 @@
     IPMI_CMD_SET_USER_PASSWORD = 0x47,
 };
 
+enum class IPMISetPasswordReturnCodes
+{
+    ipmiCCPasswdFailMismatch = 0x80,
+    ipmiCCPasswdFailWrongSize = 0x81,
+};
+
 static constexpr uint8_t userIdEnabledViaSetPassword = 0x1;
 static constexpr uint8_t userIdDisabledViaSetPassword = 0x2;