Redfish: Add PATCH operation support for RemoteRoleMapping
Added PATCH operation support for RemoteRoleMapping property under
LDAP/ActiveDirectory property in AccountService schema.
1. How to add the Role Mapping?
PATCH {"ActiveDirectory":{"RemoteRoleMapping": [{"RemoteGroup":
"Admingroup15","LocalRole": "User"},{"RemoteGroup": "Admingroup13",
"LocalRole": "Administrator"},{"RemoteGroup": "Admingroup14",
"LocalRole": "Operator"}]}}
With the above PATCH request, all the above role mapping gets added.
2. How to delete a specific role mapping?
After adding the above roles mapping, if user want to delete the second mapping
which is ({"RemoteGroup": "Admingroup13", "LocalRole": "Administrator"})
Following PATCH request would be used.
PATCH {"ActiveDirectory":{"RemoteRoleMapping": [{},null,{}]}}
3. How to update specific role mapping ?
Let's take a case where user want to update the second role mapping
PATCH {"ActiveDirectory":{"RemoteRoleMapping": [{},{"RemoteGroup":"Admingroup25","LocalRole": "User"},{}]}}
or
PATCH {"ActiveDirectory":{"RemoteRoleMapping": [{},{"RemoteGroup":"Admingroup25"},{}]}} and \
PATCH {"ActiveDirectory":{"RemoteRoleMapping": [{},{"LocalRole": "User"},{}]}}
Tested:
1. Did a PATCH operation with below given Data:
' {"ActiveDirectory":{"RemoteRoleMapping": [{"RemoteGroup": "Admingroup215","LocalRole": "User"}, \
{"RemoteGroup": "Admingroup213","LocalRole":"Administrator"},{"RemoteGroup":"Admingroup214","LocalRole":"Operator"}]}}'
2. With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "Operator",
"RemoteGroup": "Admingroup214"
},
{
"LocalRole": "Administrator",
"RemoteGroup": "Admingroup213"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
3. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [{},null,{}]}}'
4. With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "Operator",
"RemoteGroup": "Admingroup214"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
5. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [null,null]}}'
6. With GET got below given data:
"RemoteRoleMapping": []
7. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [{"RemoteGroup": "Admingroup215","LocalRole": "User"}, \
{"RemoteGroup": "Admingroup213","LocalRole":"Administrator"},{"RemoteGroup":"Admingroup214","LocalRole":"Operator"}]}}'
8. With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "Administrator",
"RemoteGroup": "Admingroup213"
},
{
"LocalRole": "Operator",
"RemoteGroup": "Admingroup214"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
9. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [{"RemoteGroup": "Admingroup25"},{},{}]}}'
10.With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "Administrator",
"RemoteGroup": "Admingroup25"
},
{
"LocalRole": "Operator",
"RemoteGroup": "Admingroup214"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
11. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [{"LocalRole": "User"},{},{}]}}'
12.With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "User",
"RemoteGroup": "Admingroup25"
},
{
"LocalRole": "Operator",
"RemoteGroup": "Admingroup214"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
13. Did a PATCH operation with below given Data:
'{"ActiveDirectory":{"RemoteRoleMapping": [{},{"RemoteGroup": "Admingroup26","LocalRole": "User"},{}]}}'
14.With GET got below given data:
"RemoteRoleMapping": [
{
"LocalRole": "User",
"RemoteGroup": "Admingroup25"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup26"
},
{
"LocalRole": "User",
"RemoteGroup": "Admingroup215"
}
],
Change-Id: Idc80cee94b8b55d036c2514d50c147a72ed4c7f2
Signed-off-by: Ratan Gupta <ratagupt@linux.vnet.ibm.com>
Signed-off-by: Nagaraju Goruganti <ngorugan@in.ibm.com>
diff --git a/redfish-core/lib/account_service.hpp b/redfish-core/lib/account_service.hpp
index 6cbbdce..d4e1b38 100644
--- a/redfish-core/lib/account_service.hpp
+++ b/redfish-core/lib/account_service.hpp
@@ -37,6 +37,8 @@
constexpr const char* ldapCreateInterface =
"xyz.openbmc_project.User.Ldap.Create";
constexpr const char* ldapEnableInterface = "xyz.openbmc_project.Object.Enable";
+constexpr const char* ldapPrivMapperInterface =
+ "xyz.openbmc_project.User.PrivilegeMapper";
constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
constexpr const char* propertyInterface = "org.freedesktop.DBus.Properties";
constexpr const char* mapperBusName = "xyz.openbmc_project.ObjectMapper";
@@ -149,6 +151,243 @@
}
/**
+ * @brief deletes given RoleMapping Object.
+ */
+static void deleteRoleMappingObject(const std::shared_ptr<AsyncResp>& asyncResp,
+ const std::string& objPath,
+ const std::string& serverType,
+ unsigned int index)
+{
+
+ BMCWEB_LOG_DEBUG << "deleteRoleMappingObject objPath =" << objPath;
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, serverType, index](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"][index] =
+ nullptr;
+ },
+ ldapDbusService, objPath, "xyz.openbmc_project.Object.Delete",
+ "Delete");
+}
+
+/**
+ * @brief sets RoleMapping Object's property with given value.
+ */
+static void setRoleMappingProperty(
+ const std::shared_ptr<AsyncResp>& asyncResp, const std::string& objPath,
+ const std::string& redfishProperty, const std::string& dbusProperty,
+ const std::string& value, const std::string& serverType, unsigned int index)
+{
+ BMCWEB_LOG_DEBUG << "setRoleMappingProperty objPath: " << objPath
+ << "value: " << value;
+
+ // need to get the dbus privilege from the given refish role
+ std::string dbusVal = value;
+ if (redfishProperty == "LocalRole")
+ {
+ dbusVal = getPrivilegeFromRoleId(value);
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, serverType, index, redfishProperty,
+ value](const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ asyncResp->res.jsonValue[serverType]["RemoteRoleMapping"][index]
+ [redfishProperty] = value;
+ },
+ ldapDbusService, objPath, "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.User.PrivilegeMapperEntry",
+ std::move(dbusProperty), std::variant<std::string>(std::move(dbusVal)));
+}
+
+/**
+ * @brief validates given JSON input and then calls appropriate method to
+ * create, to delete or to set Rolemapping object based on the given input.
+ *
+ */
+static void handleRoleMapPatch(
+ const std::shared_ptr<AsyncResp>& asyncResp,
+ const std::vector<std::pair<std::string, LDAPRoleMapData>>& roleMapObjData,
+ const std::string& serverType, const nlohmann::json& input)
+{
+ if (!input.is_array())
+ {
+ messages::propertyValueTypeError(asyncResp->res, input.dump(),
+ "RemoteRoleMapping");
+ return;
+ }
+
+ size_t index = 0;
+ for (const nlohmann::json& thisJson : input)
+ {
+ // Check that entry is not of some unexpected type
+ if (!thisJson.is_object() && !thisJson.is_null())
+ {
+ messages::propertyValueTypeError(asyncResp->res, thisJson.dump(),
+ "RemoteGroup or LocalRole");
+ index++;
+ continue;
+ }
+ BMCWEB_LOG_DEBUG << "JSON=" << thisJson << "\n";
+ // delete the existing object
+ if (thisJson.is_null())
+ {
+ if (input.size() <= roleMapObjData.size())
+ {
+ deleteRoleMappingObject(asyncResp,
+ roleMapObjData.at(index).first,
+ serverType, index);
+ }
+ else
+ {
+ BMCWEB_LOG_ERROR << "Can't delete the object";
+ messages::propertyValueTypeError(
+ asyncResp->res, thisJson.dump(), "RemoteRoleMapping");
+ return;
+ }
+
+ index++;
+ continue;
+ }
+
+ if (thisJson.empty())
+ {
+ if ((input.size() > roleMapObjData.size()) &&
+ (index > roleMapObjData.size()))
+ {
+ BMCWEB_LOG_ERROR << "Empty object can't be inserted";
+ messages::propertyValueTypeError(
+ asyncResp->res, thisJson.dump(), "RemoteRoleMapping");
+ return;
+ }
+
+ index++;
+ continue;
+ }
+
+ const std::string* remoteGroup = nullptr;
+ nlohmann::json::const_iterator remoteGroupIt =
+ thisJson.find("RemoteGroup");
+
+ // extract "RemoteGroup" and "LocalRole" form JSON
+ if (remoteGroupIt != thisJson.end())
+ {
+ remoteGroup = remoteGroupIt->get_ptr<const std::string*>();
+ }
+
+ const std::string* localRole = nullptr;
+ nlohmann::json::const_iterator localRoleIt = thisJson.find("LocalRole");
+ if (localRoleIt != thisJson.end())
+ {
+ localRole = localRoleIt->get_ptr<const std::string*>();
+ }
+
+ // Update existing RoleMapping Object
+ if (roleMapObjData.size() >= input.size())
+ {
+ BMCWEB_LOG_DEBUG << "setRoleMappingProperties: Updating Object";
+ // If "RemoteGroup" info is provided
+ if (remoteGroup != nullptr)
+ {
+ if (remoteGroup->empty())
+ {
+ messages::propertyValueTypeError(
+ asyncResp->res, thisJson.dump(), "RemoteGroup");
+ return;
+ }
+ // check if the given data is not equal to already existing one
+ else if (roleMapObjData.at(index).second.groupName.compare(
+ *remoteGroup) != 0)
+ {
+ setRoleMappingProperty(asyncResp,
+ roleMapObjData.at(index).first,
+ "RemoteGroup", "GroupName",
+ *remoteGroup, serverType, index);
+ }
+ }
+
+ // If "LocalRole" info is provided
+ if (localRole != nullptr)
+ {
+ if (localRole->empty())
+ {
+ messages::propertyValueTypeError(
+ asyncResp->res, thisJson.dump(), "LocalRole");
+ return;
+ }
+ // check if the given data is not equal to already existing one
+ else if (roleMapObjData.at(index).second.privilege.compare(
+ *localRole) != 0)
+ {
+ setRoleMappingProperty(
+ asyncResp, roleMapObjData.at(index).first, "LocalRole",
+ "Privilege", *localRole, serverType, index);
+ }
+ }
+ index++;
+ }
+ // Create a new RoleMapping Object.
+ else
+ {
+ BMCWEB_LOG_DEBUG << "setRoleMappingProperties: Creating new Object";
+ if (localRole == nullptr || remoteGroup == nullptr)
+ {
+ messages::propertyValueTypeError(asyncResp->res,
+ thisJson.dump(),
+ "RemoteGroup or LocalRole");
+ return;
+ }
+ else if (remoteGroup->empty() || localRole->empty())
+ {
+ messages::propertyValueTypeError(
+ asyncResp->res, thisJson.dump(), "RemoteGroup LocalRole");
+ return;
+ }
+
+ std::string dbusObjectPath;
+ if (serverType == "ActiveDirectory")
+ {
+ dbusObjectPath = ADConfigObject;
+ }
+ else if (serverType == "LDAP")
+ {
+ dbusObjectPath = ldapConfigObject;
+ }
+
+ crow::connections::systemBus->async_method_call(
+ [asyncResp, serverType, index, localRole{std::move(*localRole)},
+ remoteGroup{std::move(*remoteGroup)}](
+ const boost::system::error_code ec) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
+ messages::internalError(asyncResp->res);
+ }
+ nlohmann::json& remoteRoleJson =
+ asyncResp->res
+ .jsonValue[serverType]["RemoteRoleMapping"][index];
+ remoteRoleJson["LocalRole"] = localRole;
+ remoteRoleJson["RemoteGroup"] = remoteGroup;
+ },
+ ldapDbusService, dbusObjectPath, ldapPrivMapperInterface,
+ "Create", *remoteGroup, getPrivilegeFromRoleId(*localRole));
+ index++;
+ }
+ }
+}
+
+/**
* Function that retrieves all properties for LDAP config object
* into JSON
*/
@@ -699,12 +938,14 @@
std::optional<std::string> groupsAttribute;
std::optional<std::string> userName;
std::optional<std::string> password;
+ std::optional<nlohmann::json> remoteRoleMapData;
if (!json_util::readJson(input, asyncResp->res, "Authentication",
authentication, "LDAPService", ldapService,
"ServiceAddresses", serviceAddressList,
"AccountProviderType", accountProviderType,
- "ServiceEnabled", serviceEnabled))
+ "ServiceEnabled", serviceEnabled,
+ "RemoteRoleMapping", remoteRoleMapData))
{
return;
}
@@ -745,7 +986,8 @@
// nothing to update, then return
if (!userName && !password && !serviceAddressList && !baseDNList &&
- !userNameAttribute && !groupsAttribute && !serviceEnabled)
+ !userNameAttribute && !groupsAttribute && !serviceEnabled &&
+ !remoteRoleMapData)
{
return;
}
@@ -756,7 +998,7 @@
baseDNList, userNameAttribute,
groupsAttribute, accountProviderType,
serviceAddressList, serviceEnabled,
- dbusObjectPath](
+ dbusObjectPath, remoteRoleMapData](
bool success, LDAPConfigData confData,
const std::string& serverType) {
if (!success)
@@ -823,9 +1065,15 @@
handleServiceEnablePatch(confData.serviceEnabled, asyncResp,
serverType, dbusObjectPath);
}
+
+ if (remoteRoleMapData)
+ {
+
+ handleRoleMapPatch(asyncResp, confData.groupRoleList,
+ serverType, *remoteRoleMapData);
+ }
});
}
-
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override
{