Redfish: Add the PATCH support for user locked property.

With this commit PATCH operation on the Locked property of
ManagerAccount schema would allow the user/admin to unlock
an account,however admin would not be allowed to lock an
account as account can be locked automatically by configured
consecutive authentication failures.

TestedBy: Run the following patch request to test the locked property.

GET https://${BMC_IP}/redfish/v1/AccountService/Accounts/esalinux0
{
  "@odata.context": "/redfish/v1/$metadata#ManagerAccount.ManagerAccount",
  "@odata.id": "/redfish/v1/AccountService/Accounts/esalinux0",
  "@odata.type": "#ManagerAccount.v1_0_3.ManagerAccount",
  "Description": "User Account",
  "Enabled": true,
  "Id": "esalinux0",
  "Links": {
    "Role": {
      "@odata.id": "/redfish/v1/AccountService/Roles/Administrator"
    }
  },
  "Locked": true,
  "Locked@Redfish.AllowableValues": [
    false
  ],
  "Name": "User Account",
  "Password": null,
  "RoleId": "Administrator",
  "UserName": "esalinux0"
}

PATCH https://${BMC_IP}/redfish/v1/AccountService/Accounts/esalinux0 -d '{"Locked" : false}'

{
  "@Message.ExtendedInfo": [
    {
      "@odata.type": "/redfish/v1/$metadata#Message.v1_0_0.Message",
      "Message": "Successfully Completed Request",
      "MessageArgs": [],
      "MessageId": "Base.1.4.0.Success",
      "Resolution": "None",
      "Severity": "OK"
    }
  ]
}

Change-Id: I5d7a3d8f6330bbf01292ed4079542a1c65c9cc35
Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index d9388cf..f698901 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -16,6 +16,7 @@
 #pragma once
 #include "node.hpp"
 
+#include <dbus_utility.hpp>
 #include <error_messages.hpp>
 #include <openbmc_dbus_rest.hpp>
 #include <utils/json_utils.hpp>
@@ -455,6 +456,9 @@
                                 }
                                 asyncResp->res.jsonValue["Locked"] =
                                     *userLocked;
+                                asyncResp->res.jsonValue
+                                    ["Locked@Redfish.AllowableValues"] = {
+                                    false};
                             }
                             else if (property.first == "UserPrivilege")
                             {
@@ -510,9 +514,10 @@
         std::optional<std::string> password;
         std::optional<bool> enabled;
         std::optional<std::string> roleId;
+        std::optional<bool> locked;
         if (!json_util::readJson(req, res, "UserName", newUserName, "Password",
-                                 password, "RoleId", roleId, "Enabled",
-                                 enabled))
+                                 password, "RoleId", roleId, "Enabled", enabled,
+                                 "Locked", locked))
         {
             return;
         }
@@ -523,8 +528,8 @@
         {
             // If the username isn't being updated, we can update the properties
             // directly
-            updateUserProperties(asyncResp, username, password, enabled,
-                                 roleId);
+            updateUserProperties(asyncResp, username, password, enabled, roleId,
+                                 locked);
             return;
         }
         else
@@ -532,7 +537,7 @@
             crow::connections::systemBus->async_method_call(
                 [this, asyncResp, username, password(std::move(password)),
                  roleId(std::move(roleId)), enabled(std::move(enabled)),
-                 newUser{std::string(*newUserName)}](
+                 newUser{std::string(*newUserName)}, locked(std::move(locked))](
                     const boost::system::error_code ec) {
                     if (ec)
                     {
@@ -544,7 +549,7 @@
                     }
 
                     updateUserProperties(asyncResp, newUser, password, enabled,
-                                         roleId);
+                                         roleId, locked);
                 },
                 "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
                 "xyz.openbmc_project.User.Manager", "RenameUser", username,
@@ -556,7 +561,8 @@
                               const std::string& username,
                               std::optional<std::string> password,
                               std::optional<bool> enabled,
-                              std::optional<std::string> roleId)
+                              std::optional<std::string> roleId,
+                              std::optional<bool> locked)
     {
         if (password)
         {
@@ -568,52 +574,101 @@
             }
         }
 
-        if (enabled)
-        {
-            crow::connections::systemBus->async_method_call(
-                [asyncResp](const boost::system::error_code ec) {
-                    if (ec)
-                    {
-                        BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
-                        messages::internalError(asyncResp->res);
-                        return;
-                    }
-                    messages::success(asyncResp->res);
+        std::string dbusObjectPath = "/xyz/openbmc_project/user/" + username;
+        dbus::utility::escapePathForDbus(dbusObjectPath);
+
+        checkDbusPathExists(
+            dbusObjectPath,
+            [dbusObjectPath(std::move(dbusObjectPath)), username,
+             password(std::move(password)), roleId(std::move(roleId)),
+             enabled(std::move(enabled)), locked(std::move(locked)),
+             asyncResp{std::move(asyncResp)}](int rc) {
+                if (!rc)
+                {
+                    messages::invalidObject(asyncResp->res, username.c_str());
                     return;
-                },
-                "xyz.openbmc_project.User.Manager",
-                "/xyz/openbmc_project/user/" + username,
-                "org.freedesktop.DBus.Properties", "Set",
-                "xyz.openbmc_project.User.Attributes", "UserEnabled",
-                std::variant<bool>{*enabled});
-        }
+                }
+                if (enabled)
+                {
+                    crow::connections::systemBus->async_method_call(
+                        [asyncResp](const boost::system::error_code ec) {
+                            if (ec)
+                            {
+                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
+                                                 << ec;
+                                messages::internalError(asyncResp->res);
+                                return;
+                            }
+                            messages::success(asyncResp->res);
+                            return;
+                        },
+                        "xyz.openbmc_project.User.Manager",
+                        dbusObjectPath.c_str(),
+                        "org.freedesktop.DBus.Properties", "Set",
+                        "xyz.openbmc_project.User.Attributes", "UserEnabled",
+                        std::variant<bool>{*enabled});
+                }
 
-        if (roleId)
-        {
-            std::string priv = getRoleIdFromPrivilege(*roleId);
-            if (priv.empty())
-            {
-                messages::propertyValueNotInList(asyncResp->res, *roleId,
-                                                 "RoleId");
-                return;
-            }
-
-            crow::connections::systemBus->async_method_call(
-                [asyncResp](const boost::system::error_code ec) {
-                    if (ec)
+                if (roleId)
+                {
+                    std::string priv = getRoleIdFromPrivilege(*roleId);
+                    if (priv.empty())
                     {
-                        BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
-                        messages::internalError(asyncResp->res);
+                        messages::propertyValueNotInList(asyncResp->res,
+                                                         *roleId, "RoleId");
                         return;
                     }
-                    messages::success(asyncResp->res);
-                },
-                "xyz.openbmc_project.User.Manager",
-                "/xyz/openbmc_project/user/" + username,
-                "org.freedesktop.DBus.Properties", "Set",
-                "xyz.openbmc_project.User.Attributes", "UserPrivilege",
-                std::variant<std::string>{priv});
-        }
+
+                    crow::connections::systemBus->async_method_call(
+                        [asyncResp](const boost::system::error_code ec) {
+                            if (ec)
+                            {
+                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
+                                                 << ec;
+                                messages::internalError(asyncResp->res);
+                                return;
+                            }
+                            messages::success(asyncResp->res);
+                        },
+                        "xyz.openbmc_project.User.Manager",
+                        dbusObjectPath.c_str(),
+                        "org.freedesktop.DBus.Properties", "Set",
+                        "xyz.openbmc_project.User.Attributes", "UserPrivilege",
+                        std::variant<std::string>{priv});
+                }
+
+                if (locked)
+                {
+                    // admin can unlock the account which is locked by
+                    // successive authentication failures but admin should not
+                    // be allowed to lock an account.
+                    if (*locked)
+                    {
+                        messages::propertyValueNotInList(asyncResp->res, "true",
+                                                         "Locked");
+                        return;
+                    }
+
+                    crow::connections::systemBus->async_method_call(
+                        [asyncResp](const boost::system::error_code ec) {
+                            if (ec)
+                            {
+                                BMCWEB_LOG_ERROR << "D-Bus responses error: "
+                                                 << ec;
+                                messages::internalError(asyncResp->res);
+                                return;
+                            }
+                            messages::success(asyncResp->res);
+                            return;
+                        },
+                        "xyz.openbmc_project.User.Manager",
+                        dbusObjectPath.c_str(),
+                        "org.freedesktop.DBus.Properties", "Set",
+                        "xyz.openbmc_project.User.Attributes",
+                        "UserLockedForFailedAttempt",
+                        sdbusplus::message::variant<bool>{*locked});
+                }
+            });
     }
 
     void doDelete(crow::Response& res, const crow::Request& req,