diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java index 94eae16cb83..eec6cfcab7a 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherService.java @@ -36,8 +36,21 @@ public interface APIPublisherService { void updateScopeRoleMapping() throws APIManagerPublisherException; + /** + * Add default scopes defined in the cdm-config.xml + */ void addDefaultScopesIfNotExist(); - void updateScopeRoleMapping(String roleName, String[] permissions) throws APIManagerPublisherException; + /** + * If the permissions are in the permission list, identify the relevant scopes of the supplied permission list + * and put the role there; if the permissions are in the removedPermission list, update the relevant scopes by + * deleting the role from those scopes. + * + * @param roleName Role Name + * @param permissions List of adding permissions + * @param removedPermissions List of removing permissions + * @throws APIManagerPublisherException If error occurred while updating the scope role mapping + */ + void updateScopeRoleMapping(String roleName, String[] permissions, String[] removedPermissions) throws APIManagerPublisherException; } diff --git a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java index be4def9da61..69f20fa0203 100644 --- a/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java +++ b/components/apimgt-extensions/io.entgra.device.mgt.core.apimgt.webapp.publisher/src/main/java/io/entgra/device/mgt/core/apimgt/webapp/publisher/APIPublisherServiceImpl.java @@ -626,7 +626,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { } @Override - public void updateScopeRoleMapping(String roleName, String[] permissions) throws APIManagerPublisherException { + public void updateScopeRoleMapping(String roleName, String[] permissions, String[] removedPermissions) throws APIManagerPublisherException { APIApplicationServices apiApplicationServices = new APIApplicationServicesImpl(); APIApplicationKey apiApplicationKey; AccessTokenInfo accessTokenInfo; @@ -643,49 +643,14 @@ public class APIPublisherServiceImpl implements APIPublisherService { try { PublisherRESTAPIServices publisherRESTAPIServices = new PublisherRESTAPIServicesImpl(); JSONObject scopeObject = publisherRESTAPIServices.getScopes(apiApplicationKey, accessTokenInfo); - Map permScopeMap = APIPublisherDataHolder.getInstance().getPermScopeMapping(); - for (String permission : permissions) { - String scopeValue = permScopeMap.get(permission); - if (scopeValue == null) { - String msg = "Found invalid permission: " + permission + ". Hence aborting the scope role " + - "mapping process"; - log.error(msg); - throw new APIManagerPublisherException(msg); - } - - JSONArray scopeList = (JSONArray) scopeObject.get("list"); - for (int i = 0; i < scopeList.length(); i++) { - JSONObject scopeObj = scopeList.getJSONObject(i); - if (scopeObj.getString("name").equals(scopeValue)) { - Scope scope = new Scope(); - scope.setName(scopeObj.getString("name")); - scope.setKey(scopeObj.getString("name")); - scope.setDescription(scopeObj.getString("description")); - scope.setId(scopeObj.getString("id")); - - // Including already existing roles - JSONArray existingRolesArray = (JSONArray) scopeObj.get("bindings"); - List existingRoleList = new ArrayList(); - - for (int j = 0; j < existingRolesArray.length(); j++) { - existingRoleList.add((String) existingRolesArray.get(j)); - } - if (!existingRoleList.contains(roleName)) { - existingRoleList.add(roleName); - } - scope.setRoles(String.join(",", existingRoleList)); - - if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getKey())) { - publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope); - } else { - // todo: come to this level means, that scope is removed from API, but haven't removed from the scope-role-permission-mappings list - log.warn(scope.getKey() + " not available as shared scope"); - } - break; - } - } + if (permissions.length != 0) { + updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeObject, permissions, permScopeMap, false); } + if (removedPermissions.length != 0) { + updateScopes(roleName, publisherRESTAPIServices, apiApplicationKey, accessTokenInfo, scopeObject, removedPermissions, permScopeMap, true); + } + try { updatePermissions(roleName, Arrays.asList(permissions)); } catch (UserStoreException e) { @@ -708,6 +673,75 @@ public class APIPublisherServiceImpl implements APIPublisherService { } } + /** + * Update Scopes + * + * @param roleName Role Name + * @param publisherRESTAPIServices {@link PublisherRESTAPIServices} + * @param apiApplicationKey {@link APIApplicationKey} + * @param accessTokenInfo {@link AccessTokenInfo} + * @param scopeObject scope object returning from APIM + * @param permissions List of permissions + * @param permScopeMap Permission Scope map + * @param removingPermissions if list of permissions has to be removed from the role send true, otherwise sends false. + * @throws APIManagerPublisherException If the method receives invalid permission to update. + */ + private void updateScopes (String roleName, PublisherRESTAPIServices publisherRESTAPIServices, + APIApplicationKey apiApplicationKey, AccessTokenInfo accessTokenInfo, + JSONObject scopeObject, String[] permissions, Map permScopeMap, boolean removingPermissions ) + throws APIManagerPublisherException { + for (String permission : permissions) { + String scopeValue = permScopeMap.get(permission); + if (scopeValue == null) { + String msg = "Found invalid permission: " + permission + ". Hence aborting the scope role " + + "mapping process"; + log.error(msg); + throw new APIManagerPublisherException(msg); + } + + JSONArray scopeList = (JSONArray) scopeObject.get("list"); + for (int i = 0; i < scopeList.length(); i++) { + JSONObject scopeObj = scopeList.getJSONObject(i); + if (scopeObj.getString("name").equals(scopeValue)) { + Scope scope = new Scope(); + scope.setName(scopeObj.getString("name")); + scope.setKey(scopeObj.getString("name")); + scope.setDescription(scopeObj.getString("description")); + scope.setId(scopeObj.getString("id")); + + // Including already existing roles + JSONArray existingRolesArray = (JSONArray) scopeObj.get("bindings"); + List existingRoleList = new ArrayList(); + + for (int j = 0; j < existingRolesArray.length(); j++) { + existingRoleList.add((String) existingRolesArray.get(j)); + } + + if (removingPermissions) { + existingRoleList.remove(roleName); + } else { + if (!existingRoleList.contains(roleName)) { + existingRoleList.add(roleName); + } + } + scope.setRoles(String.join(",", existingRoleList)); + + try { + if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getKey())) { + publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope); + } else { + // todo: come to this level means, that scope is removed from API, but haven't removed from the scope-role-permission-mappings list + log.warn(scope.getKey() + " not available as shared scope"); + } + } catch (APIServicesException | BadRequestException | UnexpectedResponseException e) { + log.error("Error occurred while updating role scope mapping via APIM REST endpoint.", e); + } + break; + } + } + } + } + private void updatePermissions(String role, List permissions) throws UserStoreException { AuthorizationManager authorizationManager = APIPublisherDataHolder.getInstance().getUserRealm() .getAuthorizationManager(); diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/RoleInfo.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/RoleInfo.java index 3ad90c07a6d..374efeb6e06 100644 --- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/RoleInfo.java +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/beans/RoleInfo.java @@ -33,6 +33,11 @@ public class RoleInfo { @ApiModelProperty(name = "permissions", value = "Lists out all the permissions associated with roles.", required = true, dataType = "List[java.lang.String]") private String[] permissions; + + @ApiModelProperty(name = "removedPermissions", value = "Lists out all the permissions unassociated with roles.", + required = true, dataType = "List[java.lang.String]") + private String[] removedPermissions; + @ApiModelProperty(name = "users", value = "The list of users assigned to the selected role.", required = true, dataType = "List[java.lang.String]") private String[] users; @@ -76,4 +81,7 @@ public class RoleInfo { this.permissionList = permissionList; } + public String[] getRemovedPermissions() { return removedPermissions; } + + public void setRemovedPermissions(String[] removedPermissions) { this.removedPermissions = removedPermissions; } } diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/RoleManagementServiceImpl.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/RoleManagementServiceImpl.java index a9ea688d056..bca483f6c09 100644 --- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/RoleManagementServiceImpl.java +++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.api/src/main/java/io/entgra/device/mgt/core/device/mgt/api/jaxrs/service/impl/RoleManagementServiceImpl.java @@ -279,6 +279,14 @@ public class RoleManagementServiceImpl implements RoleManagementService { } } + /** + * Retrieve filtered permissions by analyzing all the permission paths. + * + * @param rolePermissions All the permission paths + * @param permissionPaths Permission paths that needs to filter + * @param permissions List of filtered permissions + * @return {@link List} + */ private List processAndFilterPermissions(UIPermissionNode[] rolePermissions, List permissionPaths, List permissions) { for (UIPermissionNode rolePermission : rolePermissions) { @@ -299,6 +307,15 @@ public class RoleManagementServiceImpl implements RoleManagementService { return permissions; } + /** + * Getting platform permissions + * + * @param roleName Role Name + * @param userRealm {@link UserRealm} + * @param permissions list of permissions + * @return {@link List} + * @throws UserAdminException if error occurred when getting {@link UIPermissionNode} + */ private String[] getPlatformUIPermissions(String roleName, UserRealm userRealm, String[] permissions) throws UserAdminException { UIPermissionNode uiPermissionNode = getUIPermissionNode(roleName, userRealm); @@ -403,8 +420,8 @@ public class RoleManagementServiceImpl implements RoleManagementService { try { if (roleInfo.getPermissions() != null && roleInfo.getPermissions().length > 0) { String[] roleName = roleInfo.getRoleName().split("/"); - addPermissions(roleName[roleName.length - 1], roleInfo.getPermissions(), - DeviceMgtAPIUtils.getUserRealm()); + roleInfo.setRemovedPermissions(new String[0]); + updatePermissions(roleName[roleName.length - 1], roleInfo, DeviceMgtAPIUtils.getUserRealm()); } } catch (UserStoreException e) { String msg = "Error occurred while loading the user store."; @@ -546,7 +563,7 @@ public class RoleManagementServiceImpl implements RoleManagementService { if (roleInfo.getPermissions() != null) { String[] roleDetails = roleName.split("/"); - addPermissions(roleDetails[roleDetails.length - 1], roleInfo.getPermissions(), userRealm); + updatePermissions(roleDetails[roleDetails.length - 1], roleInfo, userRealm); } //TODO: Need to send the updated role information in the entity back to the client return Response.status(Response.Status.OK).entity("Role '" + roleInfo.getRoleName() + "' has " + @@ -697,7 +714,14 @@ public class RoleManagementServiceImpl implements RoleManagementService { return rolePermissions; } - private void addPermissions(String roleName, String[] permissions, UserRealm userRealm) { + /** + * Update the role's permissions. This will function in the fire and forget pattern and run on a new thread. + * + * @param roleName Role Name + * @param roleInfo {@link RoleInfo} + * @param userRealm {@link UserRealm} + */ + private void updatePermissions(String roleName, RoleInfo roleInfo, UserRealm userRealm) { String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); Thread thread = new Thread(new Runnable() { @Override @@ -707,7 +731,8 @@ public class RoleManagementServiceImpl implements RoleManagementService { PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); DeviceMgtAPIUtils.getApiPublisher().updateScopeRoleMapping(roleName, RoleManagementServiceImpl.this.getPlatformUIPermissions(roleName, userRealm, - permissions)); + roleInfo.getPermissions()), RoleManagementServiceImpl.this.getPlatformUIPermissions(roleName, userRealm, + roleInfo.getRemovedPermissions())); } catch (APIManagerPublisherException | UserAdminException e) { log.error("Error Occurred while updating role scope mapping. ", e); } finally {