diff options
-rw-r--r-- | Redfish.md | 1 | ||||
-rw-r--r-- | redfish-core/lib/account_service.hpp | 158 |
2 files changed, 143 insertions, 16 deletions
diff --git a/Redfish.md b/Redfish.md index 4e5a19b5af..9cd7a106da 100644 --- a/Redfish.md +++ b/Redfish.md @@ -111,6 +111,7 @@ Fields common to all schemas - Password - PasswordChangeRequired - RoleId +- StrictAccountTypes - UserName ### /redfish/v1/AccountService/LDAP/Certificates/ diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp index c61fa46706..06ef0088e2 100644 --- a/redfish-core/lib/account_service.hpp +++ b/redfish-core/lib/account_service.hpp @@ -162,6 +162,120 @@ inline bool translateUserGroup(const std::vector<std::string>& userGroups, return true; } +/** + * @brief Builds User Groups from the Account Types + * + * @param[in] asyncResp Async Response + * @param[in] accountTypes List of Account Types + * @param[out] userGroups List of User Groups mapped from Account Types + * + * @return true if Account Types mapped to User Groups, false otherwise. + */ +inline bool + getUserGroupFromAccountType(crow::Response& res, + const std::vector<std::string>& accountTypes, + std::vector<std::string>& userGroups) +{ + // Need both Redfish and WebUI Account Types to map to 'redfish' User Group + bool redfishType = false; + bool webUIType = false; + + for (const auto& accountType : accountTypes) + { + if (accountType == "Redfish") + { + redfishType = true; + } + else if (accountType == "WebUI") + { + webUIType = true; + } + else if (accountType == "IPMI") + { + userGroups.emplace_back("ipmi"); + } + else if (accountType == "HostConsole") + { + userGroups.emplace_back("hostconsole"); + } + else if (accountType == "ManagerConsole") + { + userGroups.emplace_back("ssh"); + } + else + { + // Invalid Account Type + messages::propertyValueNotInList(res, "AccountTypes", accountType); + return false; + } + } + + // Both Redfish and WebUI Account Types are needed to PATCH + if (redfishType ^ webUIType) + { + BMCWEB_LOG_ERROR + << "Missing Redfish or WebUI Account Type to set redfish User Group"; + messages::strictAccountTypes(res, "AccountTypes"); + return false; + } + + if (redfishType && webUIType) + { + userGroups.emplace_back("redfish"); + } + + return true; +} + +/** + * @brief Sets UserGroups property of the user based on the Account Types + * + * @param[in] accountTypes List of User Account Types + * @param[in] asyncResp Async Response + * @param[in] dbusObjectPath D-Bus Object Path + * @param[in] userSelf true if User is updating OWN Account Types + */ +inline void + patchAccountTypes(const std::vector<std::string>& accountTypes, + const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + const std::string& dbusObjectPath, bool userSelf) +{ + // Check if User is disabling own Redfish Account Type + if (userSelf && + (accountTypes.cend() == + std::find(accountTypes.cbegin(), accountTypes.cend(), "Redfish"))) + { + BMCWEB_LOG_ERROR + << "User disabling OWN Redfish Account Type is not allowed"; + messages::strictAccountTypes(asyncResp->res, "AccountTypes"); + return; + } + + std::vector<std::string> updatedUserGroups; + if (!getUserGroupFromAccountType(asyncResp->res, accountTypes, + updatedUserGroups)) + { + // Problem in mapping Account Types to User Groups, Error already + // logged. + 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); + }, + "xyz.openbmc_project.User.Manager", dbusObjectPath, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.User.Attributes", "UserGroups", + dbus::utility::DbusVariantType{updatedUserGroups}); +} + inline void userErrorMessageHandler( const sd_bus_error* e, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& newUser, const std::string& username) @@ -1201,12 +1315,12 @@ inline void handleLDAPPatch(nlohmann::json& input, }); } -inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp, - const std::string& username, - const std::optional<std::string>& password, - const std::optional<bool>& enabled, - const std::optional<std::string>& roleId, - const std::optional<bool>& locked) +inline void updateUserProperties( + std::shared_ptr<bmcweb::AsyncResp> asyncResp, const std::string& username, + const std::optional<std::string>& password, + const std::optional<bool>& enabled, + const std::optional<std::string>& roleId, const std::optional<bool>& locked, + std::optional<std::vector<std::string>> accountTypes, bool userSelf) { sdbusplus::message::object_path tempObjPath(rootUserDbusPath); tempObjPath /= username; @@ -1214,7 +1328,8 @@ inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp, dbus::utility::checkDbusPathExists( dbusObjectPath, [dbusObjectPath, username, password, roleId, enabled, - locked, asyncResp{std::move(asyncResp)}](int rc) { + locked, accountTypes(std::move(accountTypes)), + userSelf, asyncResp{std::move(asyncResp)}](int rc) { if (rc <= 0) { messages::resourceNotFound(asyncResp->res, "ManagerAccount", @@ -1323,6 +1438,12 @@ inline void updateUserProperties(std::shared_ptr<bmcweb::AsyncResp> asyncResp, "UserLockedForFailedAttempt", dbus::utility::DbusVariantType{*locked}); } + + if (accountTypes) + { + patchAccountTypes(*accountTypes, asyncResp, dbusObjectPath, + userSelf); + } }); } @@ -1871,10 +1992,11 @@ inline void } asyncResp->res.jsonValue["@odata.type"] = - "#ManagerAccount.v1_4_0.ManagerAccount"; + "#ManagerAccount.v1_7_0.ManagerAccount"; asyncResp->res.jsonValue["Name"] = "User Account"; asyncResp->res.jsonValue["Description"] = "User Account"; asyncResp->res.jsonValue["Password"] = nullptr; + asyncResp->res.jsonValue["StrictAccountTypes"] = true; for (const auto& interface : userIt->second) { @@ -2037,6 +2159,9 @@ inline void std::optional<bool> enabled; std::optional<std::string> roleId; std::optional<bool> locked; + std::optional<std::vector<std::string>> accountTypes; + + bool userSelf = (username == req.session->username); if (req.session == nullptr) { @@ -2052,10 +2177,10 @@ inline void if (userHasConfigureUsers) { // Users with ConfigureUsers can modify for all users - if (!json_util::readJsonPatch(req, asyncResp->res, "UserName", - newUserName, "Password", password, - "RoleId", roleId, "Enabled", enabled, - "Locked", locked)) + if (!json_util::readJsonPatch( + req, asyncResp->res, "UserName", newUserName, "Password", + password, "RoleId", roleId, "Enabled", enabled, "Locked", + locked, "AccountTypes", accountTypes)) { return; } @@ -2063,7 +2188,7 @@ inline void else { // ConfigureSelf accounts can only modify their own account - if (username != req.session->username) + if (!userSelf) { messages::insufficientPrivilege(asyncResp->res); return; @@ -2085,13 +2210,14 @@ inline void if (!newUserName || (newUserName.value() == username)) { updateUserProperties(asyncResp, username, password, enabled, roleId, - locked); + locked, accountTypes, userSelf); return; } crow::connections::systemBus->async_method_call( [asyncResp, username, password(std::move(password)), roleId(std::move(roleId)), enabled, newUser{std::string(*newUserName)}, - locked](const boost::system::error_code& ec, sdbusplus::message_t& m) { + locked, userSelf, accountTypes(std::move(accountTypes))]( + const boost::system::error_code ec, sdbusplus::message_t& m) { if (ec) { userErrorMessageHandler(m.get_error(), asyncResp, newUser, @@ -2100,7 +2226,7 @@ inline void } updateUserProperties(asyncResp, newUser, password, enabled, roleId, - locked); + locked, accountTypes, userSelf); }, "xyz.openbmc_project.User.Manager", "/xyz/openbmc_project/user", "xyz.openbmc_project.User.Manager", "RenameUser", username, |