Importing groups from application
When synchronizing groups to OpenIAM from the AD Powershell target system, the transformation script typically utilizes a member
attribute. However, in some cases, another attribute needs to be retrieved, such as MemberOf
, etc.
In such instances, the transformation and provisioning scripts will require modification to accommodate this requirement. Therefore, after the AD Groups are synchronized, the following steps are necessary.
- The groups need to display all the other groups they belong to using the
MemberOf
attribute. - It is essential to ensure that when a user is granted access to Group A, they also become a member of all the groups that Group A is a part of.
- Similarly, when a user's access to Group A is revoked, their membership in all the groups that Group A is a part of should also be removed.
For this purpose, the Group transformation script must be updated to synchronize groups with the MemberOf
attribute and establish a parent-child relationship in OpenIAM.
The process should function as follows:
- Group A has Groups B, C, and D listed in the
MemberOf
attribute in AD. - After the groups are synced into OpenIAM, Groups B, C, and D will be considered child groups of Group A.
The transformation script used is provided below.
package org.openiam.sync.service.implimport org.apache.commons.collections.CollectionUtilsimport org.openiam.idm.searchbeans.GroupSearchBeanimport org.openiam.idm.srvc.grp.dto.Groupimport org.openiam.idm.srvc.synch.dto.LineObjectimport org.openiam.sync.service.impl.service.AbstractGroupTransformScriptclass ADGroupSampleTransformationScript extends AbstractGroupTransformScript {@Overrideint execute(LineObject rowObj, Group group) {println "** - Group Transformation script called."try {group.setPolicyId("4000");group.setMdTypeId("AD_GROUP");populateObject(rowObj, group)} catch (Exception ex) {ex.printStackTrace();println "** - Transformation script error."return -1;}println "** - Transformation script completed."return NO_DELETE}private void populateObject(LineObject rowObj, Group group) {def columnMap = rowObj.columnMapif (isNewUser) {group.id = null}group.name = columnMap.get("cn")?.valuegroup.description = columnMap.get("description")?.valueif (group.getDescription() && group.getDescription().length() > 254) {group.setDescription(group.getDescription().substring(0, 254));}group.status = "ACTIVE"addGroupAttribute(group, "sAMAccountName", columnMap.get("sAMAccountName")?.value);addGroupAttribute(group, "DistinguishedName", columnMap.get("DistinguishedName")?.value);def scope = columnMap.get("groupScope")?.valueif (scope) {switch (scope) {case "0":group.setAdGroupScopeId("Domain_Local")breakcase "1":group.setAdGroupScopeId("Global")breakcase "2":group.setAdGroupScopeId("Universal")break}}def category = columnMap.get("groupCategory")?.valueif (category) {switch (category) {case "0":group.setAdGroupTypeId("DISTRIBUTION_GROUP")breakcase "1":group.setAdGroupTypeId("SECURITY_GROUP")break}}// Get the members of the group from the row datadef memberOf = columnMap.get("memberOf")if (memberOf) {final Set<String> groupSet = new HashSet<>()// Check if the members field is a multi-valued field or a single valueif (memberOf.isMultiValued()) {groupSet.addAll(memberOf.getValueList())} else {groupSet.add(memberOf.getValue())}// Iterate over the member DNs and add them as child groups to the current groupgroupSet.each{ dn ->final Group g = getGroupByDn(dn);if (g != null) {addChildGroup(group,g.getId())}}}}/*** Retrieves a {@code Group} object based on its distinguished name (DN).** This method first checks a cache for the presence of the group with the specified DN.* If the group is found in the cache, it is returned immediately. Otherwise, a search* is performed using the provided distinguished name. If a single group is found in the* search results, it is added to the cache before being returned.** @param dn The distinguished name of the group to retrieve.* @return The {@code Group} object corresponding to the specified DN, or {@code null}* if the group is not found.*/private Group getGroupByDn(final String dn) {Group group = groupsByName.get(dn);if (group == null) {final GroupSearchBean groupSearchBean = new GroupSearchBean()groupSearchBean.addAttribute("DistinguishedName", dn)final List<Group> groupList = groupRabbitMQService.findBeans(groupSearchBean, 0, 1)if (CollectionUtils.isNotEmpty(groupList) && groupList.size() == 1) {groupsByName.put(dn, groupList.get(0));group = groupList.get(0);}}return group;}@Overridevoid init() {}}
The preprocessor script to handle addition/removal of child groups need also be amended as follows.
import org.apache.commons.collections.CollectionUtilsimport org.openiam.base.AttributeOperationEnumimport org.openiam.common.beans.mq.GroupRabbitMQServiceimport org.openiam.exception.BasicDataServiceExceptionimport org.openiam.idm.provisioning.preprocessor.user.AbstractUserProvisioningPreProcessorimport org.openiam.idm.searchbeans.GroupSearchBeanimport org.openiam.idm.srvc.auth.dto.Loginimport org.openiam.idm.srvc.entitlements.EntitlementsCollectionimport org.openiam.idm.srvc.grp.dto.Groupimport org.openiam.idm.srvc.membership.dto.MembershipXrefimport org.openiam.idm.srvc.user.dto.UserToGroupMembershipXrefimport org.openiam.provision.dto.PasswordSyncimport org.openiam.provision.dto.ProvisionUserimport org.openiam.provision.dto.user.request.DeleteUserProvisioningRequestimport org.springframework.beans.factory.annotation.Autowired/*** Pre-processor script that is used with the Provisioning service.*/public class ProvisionServicePreProcessor extends AbstractUserProvisioningPreProcessor {@AutowiredGroupRabbitMQService grpService;@Overrideprotected void add(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: add called.");if(CollectionUtils.isNotEmpty(user.getGroups())){List<String> grpIds = new ArrayList<>()for(UserToGroupMembershipXref group : user.getGroups()) {if(group.getOperation() == AttributeOperationEnum.ADD) {grpIds.add(group.getEntityId());}}if(CollectionUtils.isNotEmpty(grpIds)){// Calling Method to add child group to the UseraddChildGroups(user, grpIds,user.getGroups())}}}@Overrideprotected void modify(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: modify called.");if(CollectionUtils.isNotEmpty(user.getGroups())){List<String> grpIds = new ArrayList<>()List<String> removalGrpIds = new ArrayList<>()for(UserToGroupMembershipXref group : user.getGroups()) {if(group.getOperation() == AttributeOperationEnum.ADD) {grpIds.add(group.getEntityId());}if(group.getOperation() == AttributeOperationEnum.DELETE)removalGrpIds.add(group.getEntityId())}if(CollectionUtils.isNotEmpty(grpIds)){// Calling Method to add Child groups to the UseraddChildGroups(user, grpIds,user.getGroups())}if(CollectionUtils.isNotEmpty(removalGrpIds)){// Calling Method to remove Child groups from the UserremoveChildGroups(user, removalGrpIds)}}}@Overrideprotected void enable(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: enable called.");}@Overrideprotected void disable(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: disable called.");}@Overrideprotected void delete(DeleteUserProvisioningRequest object, Login login) throws BasicDataServiceException {println("ProvisionServicePreProcessor: delete called.");}@Overrideprotected void resume(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: resume called.");}@Overrideprotected void suspend(ProvisionUser user) throws BasicDataServiceException {println("ProvisionServicePreProcessor: suspend called.");}@Overrideprotected void setPassword( PasswordSync passwordSync, Login login) throws BasicDataServiceException {println("ProvisionServicePreProcessor: setPassword called.");}@Overrideprotected void resetPassword( PasswordSync passwordSync, Login login) throws BasicDataServiceException {println("ProvisionServicePreProcessor: resetPassword called.");}// Method to Add Nested Groups if Parent Group is Addedvoid addChildGroups(ProvisionUser user, List<String> grpIds,Set<UserToGroupMembershipXref> list){GroupSearchBean gsb = new GroupSearchBean()gsb.setKeySet(grpIds)// List of Immediate Child Groups of the Parent group to be addedList<Group> groupList = grpService.findBeans(gsb, EntitlementsCollection.CHILDRENS as EntitlementsCollection[],0,Integer.MAX_VALUE)if(CollectionUtils.isNotEmpty(groupList)){for (Group g : groupList){Set<MembershipXref> xrefs = g.getChildGroups()if(CollectionUtils.isNotEmpty(xrefs)){List<String> childGroupIds = new ArrayList<>()for (MembershipXref x : xrefs){def membership = list.find({it -> it.getEntityId().equalsIgnoreCase(g.getId())})user.addGroup(grpService.getGroup(x.getEntityId()),membership.getRightNames(),membership.getStartDate(),membership.getEndDate(),membership.getDescription())println("ADDING GROUPS")childGroupIds.add(x.getEntityId())}if(CollectionUtils.isNotEmpty(childGroupIds)){// Recursive call to Add futher child groupsaddChildGroups(user,childGroupIds,list)}}}}}// Method to Remove Nested Groups if Parent Group is Removedvoid removeChildGroups(ProvisionUser user, List<String> grpIds){GroupSearchBean gsb = new GroupSearchBean()gsb.setKeySet(grpIds)//List of Immediate Child Groups of the Parent Group to be removedList<Group> groupList = grpService.findBeans(gsb, EntitlementsCollection.CHILDRENS as EntitlementsCollection[],0,Integer.MAX_VALUE)if(CollectionUtils.isNotEmpty(groupList)){for (Group g : groupList){Set<MembershipXref> xrefs = g.getChildGroups()if(CollectionUtils.isNotEmpty(xrefs)){List<String> childGroupIds = new ArrayList<>()for (MembershipXref x : xrefs){user.removeGroup(x.getEntityId())println("Removing GROUPS")childGroupIds.add(x.getEntityId())}if(CollectionUtils.isNotEmpty(childGroupIds)){// Recursive call to Remove further child groupsremoveChildGroups(user,childGroupIds)}}}}}}