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
MemberOfattribute. - 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
MemberOfattribute 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)}}}}}}