summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbhishek Patel <Abhishek.Patel@ibm.com>2022-02-02 17:54:25 +0300
committerEd Tanous <ed@tanous.net>2023-06-07 01:33:02 +0300
commit583458567282adc738ce937fe956c1fb54ed7987 (patch)
treed9953e29156c5243f2365264ba0937784adac41d
parentfc903b3d9b6b61a6b215aabf4ae68408c04787ef (diff)
downloadbmcweb-583458567282adc738ce937fe956c1fb54ed7987.tar.xz
PATCH userGroups Information ("AccountTypes")
This commit enhances the redfish API to set and unset userGroups information for each user account. Users with ConfigureUsers level privilege can patch (Set and Unset) AccountTypes of each user role. In addition, a user with "ConfigureSelf" level privilege can only set or Update their password. "Redfish" is always enabled in each user role. However, "ConfigureUsers" can disable other user redfish services. But if "ConfigureUsers" try to disable its redfish service, that generates an error. In this commit, users can enable and disable "redfish", "ssh", "hostconsole" and "ipmi" services from each user where ssh is a special case. The 'web' group does not control access to the web interface, and doesn't appear to do anything. The 'redfish' in the UserGroups is mapped to both Redfish and WebUI AccountTypes. To enable redfish User Group both of these account types should be specified, and none to disable it. Tested: Testing was done using curl command with ConfigureUsers and ConfigureSelf. $ curl -k -X PATCH https://$bmc:18080/redfish/v1/AccountService/Accounts/webuser -d '{"AccountTypes": ["Redfish", "WebUI", "ManagerConsole", "HostConsole"]}' { "@Message.ExtendedInfo": [ { "@odata.type": "#Message.v1_1_1.Message", "Message": "The request completed successfully.", "MessageArgs": [], "MessageId": "Base.1.13.0.Success", "MessageSeverity": "OK", "Resolution": "None" } ] } Also ran following cases: $ curl -k -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user99 -d '{"AccountTypes": ["HostConsole"]}' $ curl -k -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user99 -d '{"AccountTypes": ["IPMI"]}' $ curl -k -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user99 -d '{"AccountTypes": ["Redfish", "WebUI"]}' $ curl -k -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user99 -d '{"AccountTypes": ["ManagerConsole"]}' $ curl -k -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user99 -d '{"AccountTypes": ["Redfish", "IPMI", "HostConsole", "ManagerConsole", "WebUI"]}' { "error": { "@Message.ExtendedInfo": [ { "@odata.type": "#Message.v1_1_1.Message", "Message": "There are insufficient privileges for the account or credentials associated with the current session to perform the requested operation.", "MessageArgs": [], "MessageId": "Base.1.13.0.InsufficientPrivilege", "MessageSeverity": "Critical", "Resolution": "Either abandon the operation or change the associated access rights and resubmit the request if the operation failed." } ], "code": "Base.1.13.0.InsufficientPrivilege", "message": "There are insufficient privileges for the account or credentials associated with the current session to perform the requested operation." } $ curl -k -H 'X-Auth-Token: IpnCBj1Lozh53Jhzxu7T' -X PATCH https://${bmc}/redfish/v1/AccountService/Accounts/user999 -d '{"Password":"0penBmc123"}' { "@Message.ExtendedInfo": [ { "@odata.type": "#Message.v1_1_1.Message", "Message": "The request completed successfully.", "MessageArgs": [], "MessageId": "Base.1.13.0.Success", "MessageSeverity": "OK", "Resolution": "None" } ] Signed-off-by: Ninad Palsule <ninadpalsule@us.ibm.com> Signed-off-by: Abhishek Patel <Abhishek.Patel@ibm.com> Change-Id: I1a0344ca45556b820bb77c3dcb459f27eb032501 Signed-off-by: Shantappa Teekappanavar <shantappa.teekappanavar@ibm.com>
-rw-r--r--Redfish.md1
-rw-r--r--redfish-core/lib/account_service.hpp158
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,