Implement AccountService PATCH method
This patchset implements the AccountService PATCH method, using PAM and
dbus in combination.
Change-Id: I754590f787fc84a21a9453e7e10726c56da5c3f7
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/include/pam_authenticate.hpp b/include/pam_authenticate.hpp
index f51f9aa..643c804 100644
--- a/include/pam_authenticate.hpp
+++ b/include/pam_authenticate.hpp
@@ -55,14 +55,6 @@
if (retval != PAM_SUCCESS)
{
- if (retval == PAM_AUTH_ERR)
- {
- // printf("Authentication failure.\n");
- }
- else
- {
- // printf("pam_authenticate returned %d\n", retval);
- }
pam_end(localAuthHandle, PAM_SUCCESS);
return false;
}
@@ -82,3 +74,31 @@
return true;
}
+
+inline bool pamUpdatePassword(const std::string& username,
+ const std::string& password)
+{
+ const struct pam_conv localConversation = {
+ pamFunctionConversation, const_cast<char*>(password.c_str())};
+ pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
+
+ if (pam_start("passwd", username.c_str(), &localConversation,
+ &localAuthHandle) != PAM_SUCCESS)
+ {
+ return false;
+ }
+ int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
+
+ if (retval != PAM_SUCCESS)
+ {
+ pam_end(localAuthHandle, PAM_SUCCESS);
+ return false;
+ }
+
+ if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 23e77a7..1f88bb3 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -17,6 +17,7 @@
#include "node.hpp"
#include <openbmc_dbus_rest.hpp>
+#include <utils/json_utils.hpp>
namespace redfish
{
@@ -134,6 +135,23 @@
}
};
+template <typename Callback>
+inline void checkDbusPathExists(const std::string& path, Callback&& callback)
+{
+ using GetObjectType =
+ std::vector<std::pair<std::string, std::vector<std::string>>>;
+
+ crow::connections::systemBus->async_method_call(
+ [callback{std::move(callback)}](const boost::system::error_code ec,
+ const GetObjectType& object_names) {
+ callback(ec || object_names.size() == 0);
+ },
+ "xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject", path,
+ std::array<std::string, 0>());
+}
+
class ManagerAccount : public Node
{
public:
@@ -218,6 +236,116 @@
"xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
}
+
+ void doPatch(crow::Response& res, const crow::Request& req,
+ const std::vector<std::string>& params) override
+ {
+ auto asyncResp = std::make_shared<AsyncResp>(res);
+
+ if (params.size() != 1)
+ {
+ res.result(boost::beast::http::status::internal_server_error);
+ return;
+ }
+
+ nlohmann::json patchRequest;
+ if (!json_util::processJsonFromRequest(res, req, patchRequest))
+ {
+ return;
+ }
+
+ // Check the user exists before updating the fields
+ checkDbusPathExists(
+ "/xyz/openbmc_project/users/" + params[0],
+ [username{std::string(params[0])},
+ patchRequest(std::move(patchRequest)),
+ asyncResp](bool userExists) {
+ if (!userExists)
+ {
+ messages::addMessageToErrorJson(
+ asyncResp->res.jsonValue,
+ messages::resourceNotFound(
+ "#ManagerAccount.v1_0_3.ManagerAccount", username));
+
+ asyncResp->res.result(
+ boost::beast::http::status::not_found);
+ return;
+ }
+
+ for (const auto& item : patchRequest.items())
+ {
+ if (item.key() == "Password")
+ {
+ const std::string* passStr =
+ item.value().get_ptr<const std::string*>();
+ if (passStr == nullptr)
+ {
+ messages::addMessageToErrorJson(
+ asyncResp->res.jsonValue,
+ messages::propertyValueFormatError(
+ item.value().dump(), "Password"));
+ return;
+ }
+ BMCWEB_LOG_DEBUG << "Updating user: " << username
+ << " to password " << *passStr;
+ if (!pamUpdatePassword(username, *passStr))
+ {
+ BMCWEB_LOG_ERROR << "pamUpdatePassword Failed";
+ asyncResp->res.result(boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ }
+ else if (item.key() == "Enabled")
+ {
+ const bool* enabledBool =
+ item.value().get_ptr<const bool*>();
+
+ if (enabledBool == nullptr)
+ {
+ messages::addMessageToErrorJson(
+ asyncResp->res.jsonValue,
+ messages::propertyValueFormatError(
+ item.value().dump(), "Enabled"));
+ return;
+ }
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR
+ << "D-Bus responses error: " << ec;
+ asyncResp->res.result(
+ boost::beast::http::status::
+ internal_server_error);
+ return;
+ }
+ // TODO Consider support polling mechanism to
+ // verify status of host and chassis after
+ // execute the requested action.
+ BMCWEB_LOG_DEBUG << "Response with no content";
+ asyncResp->res.result(
+ boost::beast::http::status::no_content);
+ },
+ "xyz.openbmc_project.User.Manager",
+ "/xyz/openbmc_project/users/" + username,
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.User.Attributes"
+ "UserEnabled",
+ sdbusplus::message::variant<bool>{*enabledBool});
+ }
+ else
+ {
+ messages::addMessageToErrorJson(
+ asyncResp->res.jsonValue,
+ messages::propertyNotWritable(item.key()));
+ asyncResp->res.result(
+ boost::beast::http::status::bad_request);
+ return;
+ }
+ }
+ });
+ }
};
} // namespace redfish