Skip to content

Commit

Permalink
Support federated IDP groups to role assignments
Browse files Browse the repository at this point in the history
  • Loading branch information
SujanSanjula96 committed Nov 15, 2023
1 parent 81000df commit d35a791
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 26 deletions.
5 changes: 5 additions & 0 deletions components/org.wso2.carbon.identity.scim2.common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.idp.mgt</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.password.policy</artifactId>
Expand Down Expand Up @@ -244,6 +248,7 @@
version="${org.wso2.carbon.identity.organization.management.core.version.range}",
org.wso2.carbon.identity.handler.event.account.lock.*;
version="${carbon.identity.account.lock.handler.imp.pkg.version.range}",
org.wso2.carbon.idp.mgt.*;version="${carbon.identity.framework.imp.pkg.version.range}",
</Import-Package>
<Export-Package>
!org.wso2.carbon.identity.scim2.common.internal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.identity.application.common.model.IdPGroup;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil;
Expand All @@ -33,13 +34,16 @@
import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException;
import org.wso2.carbon.identity.role.v2.mgt.core.model.AssociatedApplication;
import org.wso2.carbon.identity.role.v2.mgt.core.model.GroupBasicInfo;
import org.wso2.carbon.identity.role.v2.mgt.core.model.IdpGroup;
import org.wso2.carbon.identity.role.v2.mgt.core.model.Permission;
import org.wso2.carbon.identity.role.v2.mgt.core.model.Role;
import org.wso2.carbon.identity.role.v2.mgt.core.model.RoleBasicInfo;
import org.wso2.carbon.identity.role.v2.mgt.core.model.UserBasicInfo;
import org.wso2.carbon.identity.role.v2.mgt.core.util.UserIDResolver;
import org.wso2.carbon.identity.scim2.common.internal.SCIMCommonComponentHolder;
import org.wso2.carbon.identity.scim2.common.utils.SCIMCommonConstants;
import org.wso2.carbon.identity.scim2.common.utils.SCIMCommonUtils;
import org.wso2.carbon.idp.mgt.IdentityProviderManagementException;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
Expand Down Expand Up @@ -71,6 +75,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
Expand Down Expand Up @@ -136,10 +141,26 @@ public RoleV2 createRole(RoleV2 role)
LOG.debug("Creating role: " + role.getDisplayName() + " for organization.");
}
}

List<String> groupIds = role.getGroups();
// Get valid IdP groups from the given group IDs.
List<IdPGroup> idpGroups = SCIMCommonComponentHolder.getIdpManagerService().getValidIdPGroupsByIdPGroupIds(
groupIds, tenantDomain);
List<IdpGroup> idpGroupList = idpGroups.stream()
.map(this::convertToIdpGroup)
.collect(Collectors.toList());
List<String> validIdpGroupIds = idpGroupList.stream()
.map(IdpGroup::getGroupId)
.collect(Collectors.toList());
// Exclude the valid Idp groups from the given group IDs for role creation.
List<String> localGroupIds = groupIds.stream()
.filter(groupId -> !validIdpGroupIds.contains(groupId))
.collect(Collectors.toList());
RoleBasicInfo roleBasicInfo =
roleManagementService.addRole(role.getDisplayName(), role.getUsers(), role.getGroups(),
roleManagementService.addRole(role.getDisplayName(), role.getUsers(), localGroupIds,
permissionList, audienceType, role.getAudienceValue(), tenantDomain);

roleManagementService.updateIdpGroupListOfRole(roleBasicInfo.getId(), idpGroupList, new ArrayList<>(),
tenantDomain);
RoleV2 createdRole = new RoleV2();
createdRole.setId(roleBasicInfo.getId());
String locationURI = SCIMCommonUtils.getSCIMRoleV2URL(roleBasicInfo.getId());
Expand All @@ -160,6 +181,10 @@ public RoleV2 createRole(RoleV2 role)
}
throw new CharonException(
String.format("Error occurred while adding a new role: %s", role.getDisplayName()), e);
} catch (IdentityProviderManagementException e) {
throw new CharonException(
String.format("Error occurred while retrieving IdP groups for role: %s", role.getDisplayName()),
e);
}
}

Expand Down Expand Up @@ -202,20 +227,34 @@ public RoleV2 getRole(String roleID, Map<String, Boolean> requiredAttributes)
}
}

// Set role's assigned groups.
List<GroupBasicInfo> assignedGroups = role.getGroups();
if (assignedGroups != null) {
for (GroupBasicInfo groupInfo : assignedGroups) {
groupInfo.getId();
String groupLocationURI = SCIMCommonUtils.getSCIMGroupURL(groupInfo.getId());
// Set role's assigned userstore groups.
List<GroupBasicInfo> assignedUserstoreGroups = role.getGroups();
if (assignedUserstoreGroups != null) {
for (GroupBasicInfo groupInfo : assignedUserstoreGroups) {
String groupId = groupInfo.getId();
String groupLocationURI = SCIMCommonUtils.getSCIMGroupURL(groupId);
Group group = new Group();
group.setDisplayName(groupInfo.getName());
group.setId(groupInfo.getId());
group.setId(groupId);
group.setLocation(groupLocationURI);
scimRole.setGroup(group);
}
}

// Set role's assigned idp groups.
List<IdpGroup> assignedIdpGroups = role.getIdpGroups();
if (assignedIdpGroups != null) {
for (IdpGroup idpGroup : assignedIdpGroups) {
String idpGroupId = idpGroup.getGroupId();
String idpGroupLocationURI = SCIMCommonUtils.getIdpGroupURL(idpGroup.getIdpId(), idpGroupId);
Group group = new Group();
group.setDisplayName(idpGroup.getGroupName());
group.setId(idpGroupId);
group.setLocation(idpGroupLocationURI);
scimRole.setGroup(group);
}
}

// Set associated applications.
List<MultiValuedComplexType> associatedApps =
convertAssociatedAppsToMultivaluedComplexType(role.getAssociatedApplications());
Expand Down Expand Up @@ -866,34 +905,81 @@ private void updateGroups(String roleId, List<PatchOperation> groupOperations)

try {
Collections.sort(groupOperations);

Set<String> givenAddedGroupIds = new HashSet<>();
Set<String> givenDeletedGroupIds = new HashSet<>();
Set<String> givenReplaceGroupsIds = new HashSet<>();

Set<String> addedGroupIds = new HashSet<>();
Set<String> deletedGroupIds = new HashSet<>();
Set<String> replaceGroupsIds = new HashSet<>();
Set<String> replaceGroupIds = new HashSet<>();

List<GroupBasicInfo> groupListOfRole = roleManagementService.getGroupListOfRole(roleId, tenantDomain);
Set<String> addedIdpGroupIds = new HashSet<>();
Set<String> deletedIdpGroupIds = new HashSet<>();
Set<String> replaceIdpGroupIds = new HashSet<>();

List<GroupBasicInfo> groupListOfRole = roleManagementService.getGroupListOfRole(roleId, tenantDomain);
for (PatchOperation groupOperation : groupOperations) {
if (groupOperation.getValues() instanceof Map) {
Map<String, String> groupObject = (Map<String, String>) groupOperation.getValues();
prepareAddedRemovedGroupLists(addedGroupIds, deletedGroupIds, replaceGroupsIds,
groupOperation, groupObject, groupListOfRole);
prepareInitialGroupLists(givenAddedGroupIds, givenDeletedGroupIds, givenReplaceGroupsIds,
groupOperation, groupObject);
} else if (groupOperation.getValues() instanceof List) {
List<Map<String, String>> groupOperationValues =
(List<Map<String, String>>) groupOperation.getValues();
for (Map<String, String> groupObject : groupOperationValues) {
prepareAddedRemovedGroupLists(addedGroupIds, deletedGroupIds, replaceGroupsIds,
groupOperation, groupObject, groupListOfRole);
prepareInitialGroupLists(givenAddedGroupIds, givenDeletedGroupIds, givenReplaceGroupsIds,
groupOperation, groupObject);
}
}
prepareReplacedGroupLists(groupListOfRole, addedGroupIds, deletedGroupIds, replaceGroupsIds);
}

Set<String> givenUniqueGroupIds = new HashSet<>();
givenUniqueGroupIds.addAll(givenAddedGroupIds);
givenUniqueGroupIds.addAll(givenDeletedGroupIds);
givenUniqueGroupIds.addAll(givenReplaceGroupsIds);

List<IdPGroup> idpGroups = SCIMCommonComponentHolder.getIdpManagerService().getValidIdPGroupsByIdPGroupIds(
new ArrayList<>(givenUniqueGroupIds), tenantDomain);
Set<String> idpGroupIds = idpGroups.stream().map(IdPGroup::getIdpGroupId).collect(Collectors.toSet());

Set<String> groupIdListOfRole =
groupListOfRole.stream().map(GroupBasicInfo::getId).collect(Collectors.toSet());
List<IdpGroup> idpGroupListOfRole = roleManagementService.getIdpGroupListOfRole(roleId, tenantDomain);
Set<String> idpGroupIdListOfRole =
idpGroupListOfRole.stream().map(IdpGroup::getGroupId).collect(Collectors.toSet());

seperatedAddedGroupLists(givenAddedGroupIds, idpGroupIds, idpGroupIdListOfRole, groupIdListOfRole,
addedIdpGroupIds, addedGroupIds);
seperateRemovedGroupLists(givenDeletedGroupIds, idpGroupIds, deletedIdpGroupIds, deletedGroupIds);

seperateReplacedGroupLists(givenReplaceGroupsIds, idpGroupIds, replaceIdpGroupIds, replaceGroupIds);
prepareReplacedGroupLists(groupListOfRole, addedGroupIds, deletedGroupIds, replaceGroupIds);
prepareReplacedIdPGroupLists(idpGroupListOfRole, addedIdpGroupIds, deletedIdpGroupIds, replaceIdpGroupIds);

if (isNotEmpty(addedGroupIds) || isNotEmpty(deletedGroupIds)) {
doUpdateGroups(roleId, addedGroupIds, deletedGroupIds);
}
if (isNotEmpty(addedIdpGroupIds) || isNotEmpty(deletedIdpGroupIds)) {
Map<String, IdPGroup> idpGroupMap = idpGroups.stream()
.collect(Collectors.toMap(IdPGroup::getIdpGroupId, Function.identity()));
List<IdpGroup> addedIdpGroups = addedIdpGroupIds.stream()
.map(idpGroupMap::get)
.map(this::convertToIdpGroup)
.collect(Collectors.toList());
List<IdpGroup> deletedIdpGroups = deletedIdpGroupIds.stream()
.map(idpGroupMap::get)
.map(this::convertToIdpGroup)
.collect(Collectors.toList());
doUpdateIdPGroups(roleId, addedIdpGroups, deletedIdpGroups);
}
} catch (IdentityRoleManagementException e) {
throw new CharonException(
String.format("Error occurred while retrieving the group list for role with ID: %s", roleId), e);
} catch (IdentityProviderManagementException e) {
throw new CharonException(
String.format("Error occurred while retrieving the IDP group list for role with ID: %s", roleId),
e);
}
}

Expand Down Expand Up @@ -969,6 +1055,23 @@ private void doUpdateGroups(String roleId, Set<String> newGroupIDList, Set<Strin
}
}

private void doUpdateIdPGroups(String roleId, List<IdpGroup> newGroupIDList, List<IdpGroup> deleteGroupIDList)
throws CharonException, BadRequestException {

// Update the role with added groups and deleted groups.
if (isNotEmpty(newGroupIDList) || isNotEmpty(deleteGroupIDList)) {
try {
roleManagementService.updateIdpGroupListOfRole(roleId, newGroupIDList, deleteGroupIDList, tenantDomain);
} catch (IdentityRoleManagementException e) {
if (RoleConstants.Error.INVALID_REQUEST.getCode().equals(e.getErrorCode())) {
throw new BadRequestException(e.getMessage());
}
throw new CharonException(
String.format("Error occurred while updating groups in the role with ID: %s", roleId), e);
}
}
}

private void doUpdateUsers(Set<String> newUserList, Set<String> deletedUserList, Set<Object> newlyAddedMemberIds,
String roleId) throws CharonException, BadRequestException, ForbiddenException {

Expand Down Expand Up @@ -1031,27 +1134,64 @@ private List<String> getUserIDList(List<String> userList, String tenantDomain) t
return userIDList;
}

private void prepareAddedRemovedGroupLists(Set<String> addedGroupsIds, Set<String> removedGroupsIds,
Set<String> replacedGroupsIds, PatchOperation groupOperation,
Map<String, String> groupObject, List<GroupBasicInfo> groupListOfRole) {
private void prepareInitialGroupLists(Set<String> givenAddedGroupsIds, Set<String> givenRemovedGroupsIds,
Set<String> givenReplacedGroupsIds, PatchOperation groupOperation,
Map<String, String> groupObject) {

switch (groupOperation.getOperation()) {
case (SCIMConstants.OperationalConstants.ADD):
removedGroupsIds.remove(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
if (!isGroupExist(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE), groupListOfRole)) {
addedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
}
givenRemovedGroupsIds.remove(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
givenAddedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
break;
case (SCIMConstants.OperationalConstants.REMOVE):
addedGroupsIds.remove(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
removedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
givenAddedGroupsIds.remove(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
givenRemovedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
break;
case (SCIMConstants.OperationalConstants.REPLACE):
replacedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
givenReplacedGroupsIds.add(groupObject.get(SCIMConstants.CommonSchemaConstants.VALUE));
break;
default:
break;
}
}

private void seperatedAddedGroupLists(Set<String> givenAddedGroupIds, Set<String> idpGroupIds,
Set<String> idpGroupIdListOfRole,Set<String> groupIdListOfRole,
Set<String> addedIdpGroupIds, Set<String> addedGroupIds) {

for (String groupId : givenAddedGroupIds) {
if (idpGroupIds.contains(groupId) && !idpGroupIdListOfRole.contains(groupId)) {
addedIdpGroupIds.add(groupId);
} else if (!groupIdListOfRole.contains(groupId)) {
addedGroupIds.add(groupId);
}
}
}

private void seperateRemovedGroupLists(Set<String> givenDeletedGroupIds, Set<String> idpGroupIds,
Set<String> deletedIdpGroupIds, Set<String> deletedGroupIds) {

for (String groupId : givenDeletedGroupIds) {
if (idpGroupIds.contains(groupId)) {
deletedIdpGroupIds.add(groupId);
} else {
deletedGroupIds.add(groupId);
}
}
}

private void seperateReplacedGroupLists(Set<String> givenReplaceGroupsIds, Set<String> idpGroupIds,
Set<String> replaceIdpGroupIds, Set<String> replaceGroupIds) {

for (String groupId : givenReplaceGroupsIds) {
if (idpGroupIds.contains(groupId)) {
replaceIdpGroupIds.add(groupId);
} else {
replaceGroupIds.add(groupId);
}
}
}

private void prepareAddedRemovedUserLists(Set<String> addedMembers, Set<String> removedMembers,
Set<Object> newlyAddedMemberIds, PatchOperation memberOperation,
Map<String, String> memberObject, String currentRoleName)
Expand Down Expand Up @@ -1119,6 +1259,25 @@ private void prepareReplacedGroupLists(List<GroupBasicInfo> groupListOfRole, Set
addedGroupIds.addAll(replacedGroupsIds);
}

private void prepareReplacedIdPGroupLists(List<IdpGroup> idpGroupListOfRole, Set<String> addedGroupIds,
Set<String> removedGroupsIds, Set<String> replacedGroupsIds) {

if (replacedGroupsIds.isEmpty()) {
return;
}

if (!idpGroupListOfRole.isEmpty()) {
for (IdpGroup idpGroupInfo : idpGroupListOfRole) {
if (!replacedGroupsIds.contains(idpGroupInfo.getGroupId())) {
removedGroupsIds.add(idpGroupInfo.getGroupId());
} else {
replacedGroupsIds.remove(idpGroupInfo.getGroupId());
}
}
}
addedGroupIds.addAll(replacedGroupsIds);
}

private boolean isGroupExist(String groupId, List<GroupBasicInfo> groupListOfRole) {

return groupListOfRole != null &&
Expand Down Expand Up @@ -1154,4 +1313,11 @@ private boolean isRoleModificationAllowedForTenant(String tenantDomain) throws C
throw new CharonException("Error while checking whether the tenant is an organization.", e);
}
}

private IdpGroup convertToIdpGroup(IdPGroup idpGroup) {

IdpGroup convertedGroup = new IdpGroup(idpGroup.getIdpGroupId(), idpGroup.getIdpId());
convertedGroup.setGroupName(idpGroup.getIdpGroupName());
return convertedGroup;
}
}
Loading

0 comments on commit d35a791

Please sign in to comment.