This applies to Grouper 4.5+

Assign a group as provisionable


Note any group needs to let users in the front door too, e.g. add the school/center robin group which is in the overall robin group, e.g. robinHotelingGse for GSE

External system

Create a SCIM key in robin.  It seems that you can only get one, so if you need multiple, you might need to share.

Go to Integrations → SCIM and get your token



Daemon to normalize userName

  • In this example we are using the SCIM userName to search and match.  But users created via CSV or SSO (SAML) get a userName assigned to them.  In this case we will use netId which is subjectIdentifier0
  • The email needs to be SSO ID which is EPPN which is subjectIdentifier1
  • We will have a GSH daemon which gets all the users from Robin, and sees which do not have the right userName, and fix it
  • Note, it might be possible to use externalId with the employee ID to make this not necessary and solely use the provisioning framework
  • There is throttling in Robin so run this daily

Script

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import edu.internet2.middleware.grouper.GrouperSession;
import edu.internet2.middleware.grouper.app.loader.OtherJobScript;
import edu.internet2.middleware.grouper.app.provisioning.ProvisioningObjectChangeAction;
import edu.internet2.middleware.grouper.app.scim2Provisioning.GrouperScim2ApiCommands;
import edu.internet2.middleware.grouper.app.scim2Provisioning.GrouperScim2User;
import edu.internet2.middleware.grouper.util.GrouperUtil;

//public class Test53robinDaemon {
//
//  public static void main(String[] args) {
    List<GrouperScim2User> allScimUsers = GrouperScim2ApiCommands.retrieveScimUsers("robinProd", null);
    GrouperSession grouperSession = GrouperSession.startRootSession();
    int usersChanged = 0;
    for (GrouperScim2User grouperScim2User: allScimUsers) {
      String userName = grouperScim2User.getUserName();
      String email = grouperScim2User.getEmailValue().toLowerCase();
      String pennkeyShouldBe = GrouperUtil.prefixOrSuffix(email, "@", true);
      if (!StringUtils.equals(userName, pennkeyShouldBe)) {
        grouperScim2User.setUserName(pennkeyShouldBe);
        usersChanged++;
        Map<String, ProvisioningObjectChangeAction> fieldToUpdate = new HashMap<>();
        fieldToUpdate.put("userName", ProvisioningObjectChangeAction.update);
        GrouperScim2ApiCommands.patchScimUser("robinProd", null, grouperScim2User, fieldToUpdate);
      }
    }
    OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().setUpdateCount(usersChanged);
    OtherJobScript.retrieveFromThreadLocal().getOtherJobInput().getHib3GrouperLoaderLog().setTotalCount(GrouperUtil.length(allScimUsers));
//  }
//}


Configuration

  • Given/Family name is required and cannot contain accented characters, so use the netId if there is no first name and remove accented chars
  • Do not have accented characters in display name
  • In this case we are not created/deleting/updating groups, we are only assigning memberships to groups
  • We will be creating users who are in assigned groups, but not deleting users or updating users
  • Map the group name from metadata so the groups can contain spacial characters
  • Change the description of the group in Robin so users know that the system of record is Grouper, and do not change the group name
  • There is throttling in Robin so run the full daily and the incremental every minute

Configuration from the UI

Configuration file

provisioner.robinProd.addDisabledFullSyncDaemon = true
provisioner.robinProd.addDisabledIncrementalSyncDaemon = true
provisioner.robinProd.bearerTokenExternalSystemConfigId = robinProd
provisioner.robinProd.class = edu.internet2.middleware.grouper.app.scim2Provisioning.GrouperScim2Provisioner
provisioner.robinProd.configureMetadata = true
provisioner.robinProd.customizeEntityCrud = true
provisioner.robinProd.customizeGroupCrud = true
provisioner.robinProd.customizeMembershipCrud = true
provisioner.robinProd.deleteEntities = false
provisioner.robinProd.deleteGroups = false
provisioner.robinProd.deleteMembershipsIfGrouperDeleted = true
provisioner.robinProd.entityAttributeValueCache0entityAttribute = id
provisioner.robinProd.entityAttributeValueCache0has = true
provisioner.robinProd.entityAttributeValueCache0source = target
provisioner.robinProd.entityAttributeValueCache0type = entityAttribute
provisioner.robinProd.entityAttributeValueCache1has = true
provisioner.robinProd.entityAttributeValueCache1source = grouper
provisioner.robinProd.entityAttributeValueCache1translationScript = \u0024{subject.getAttributeValue('firstName')}
provisioner.robinProd.entityAttributeValueCache1type = subjectTranslationScript
provisioner.robinProd.entityAttributeValueCache2has = true
provisioner.robinProd.entityAttributeValueCache2source = grouper
provisioner.robinProd.entityAttributeValueCache2translationScript = \u0024{subject.getAttributeValue('lastName')}
provisioner.robinProd.entityAttributeValueCache2type = subjectTranslationScript
provisioner.robinProd.entityAttributeValueCacheHas = true
provisioner.robinProd.entityMatchingAttribute0name = userName
provisioner.robinProd.entityMatchingAttributeCount = 1
provisioner.robinProd.errorHandlingRequiredValidationIsAnError = false
provisioner.robinProd.errorHandlingShow = true
provisioner.robinProd.groupAttributeValueCache0groupAttribute = id
provisioner.robinProd.groupAttributeValueCache0has = true
provisioner.robinProd.groupAttributeValueCache0source = target
provisioner.robinProd.groupAttributeValueCache0type = groupAttribute
provisioner.robinProd.groupAttributeValueCacheHas = true
provisioner.robinProd.groupMatchingAttribute0name = displayName
provisioner.robinProd.groupMatchingAttributeCount = 1
provisioner.robinProd.hasTargetEntityLink = true
provisioner.robinProd.hasTargetGroupLink = true
provisioner.robinProd.insertGroups = false
provisioner.robinProd.logAllObjectsVerbose = true
provisioner.robinProd.logAllObjectsVerboseToLogFile = false
provisioner.robinProd.logCommandsOnError = true
provisioner.robinProd.makeChangesToEntities = true
provisioner.robinProd.metadata.0.name = md_grouper_robinGroupName
provisioner.robinProd.metadata.0.required = true
provisioner.robinProd.metadata.0.showForGroup = true
provisioner.robinProd.numberOfEntityAttributes = 7
provisioner.robinProd.numberOfGroupAttributes = 2
provisioner.robinProd.numberOfMetadata = 1
provisioner.robinProd.operateOnGrouperEntities = true
provisioner.robinProd.operateOnGrouperGroups = true
provisioner.robinProd.operateOnGrouperMemberships = true
provisioner.robinProd.provisioningType = membershipObjects
provisioner.robinProd.replaceMemberships = true
provisioner.robinProd.scimType = generic
provisioner.robinProd.selectAllEntities = true
provisioner.robinProd.selectAllGroups = false
provisioner.robinProd.showAdvanced = true
provisioner.robinProd.startWith = this is start with read only
provisioner.robinProd.subjectSourcesToProvision = pennperson
provisioner.robinProd.targetEntityAttribute.0.name = id
provisioner.robinProd.targetEntityAttribute.1.name = userName
provisioner.robinProd.targetEntityAttribute.1.required = true
provisioner.robinProd.targetEntityAttribute.1.showAdvancedAttribute = true
provisioner.robinProd.targetEntityAttribute.1.showAttributeValidation = true
provisioner.robinProd.targetEntityAttribute.1.translateExpressionType = grouperProvisioningEntityField
provisioner.robinProd.targetEntityAttribute.1.translateFromGrouperProvisioningEntityField = subjectIdentifier0
provisioner.robinProd.targetEntityAttribute.2.name = givenName
provisioner.robinProd.targetEntityAttribute.2.translateExpression = \u0024{edu.internet2.middleware.grouper.app.loader.ldap.LoaderLdapElUtils.normalize("NFKD", edu.internet2.middleware.grouper.util.GrouperUtil.defaultString(gcGrouperSyncMember.getEntityAttributeValueCache1(), grouperProvisioningEntity.subjectIdentifier0)).replaceAll("\u005C\u005Cp{M}", "")}
provisioner.robinProd.targetEntityAttribute.2.translateExpressionType = translationScript
provisioner.robinProd.targetEntityAttribute.3.name = familyName
provisioner.robinProd.targetEntityAttribute.3.translateExpression = \u0024{edu.internet2.middleware.grouper.app.loader.ldap.LoaderLdapElUtils.normalize("NFKD", edu.internet2.middleware.grouper.util.GrouperUtil.defaultString(gcGrouperSyncMember.getEntityAttributeValueCache2(), grouperProvisioningEntity.subjectIdentifier0)).replaceAll("\u005C\u005Cp{M}", "")}
provisioner.robinProd.targetEntityAttribute.3.translateExpressionType = translationScript
provisioner.robinProd.targetEntityAttribute.4.name = emailValue
provisioner.robinProd.targetEntityAttribute.4.translateExpressionType = grouperProvisioningEntityField
provisioner.robinProd.targetEntityAttribute.4.translateFromGrouperProvisioningEntityField = subjectIdentifier1
provisioner.robinProd.targetEntityAttribute.5.name = emailType
provisioner.robinProd.targetEntityAttribute.5.translateExpressionType = staticValues
provisioner.robinProd.targetEntityAttribute.5.translateFromStaticValues = work
provisioner.robinProd.targetEntityAttribute.6.name = displayName
provisioner.robinProd.targetEntityAttribute.6.translateExpression = \u0024{edu.internet2.middleware.grouper.app.loader.ldap.LoaderLdapElUtils.normalize("NFKD", edu.internet2.middleware.grouper.util.GrouperUtil.defaultString(grouperProvisioningEntity.name, grouperProvisioningEntity.subjectIdentifier0)).replaceAll("\u005C\u005Cp{M}", "")}
provisioner.robinProd.targetEntityAttribute.6.translateExpressionType = translationScript
provisioner.robinProd.targetGroupAttribute.0.name = id
provisioner.robinProd.targetGroupAttribute.1.name = displayName
provisioner.robinProd.targetGroupAttribute.1.translateExpression = \u0024{grouperProvisioningGroup.retrieveAttributeValueString('md_grouper_robinGroupName')}
provisioner.robinProd.targetGroupAttribute.1.translateExpressionType = translationScript
provisioner.robinProd.updateEntities = false
provisioner.robinProd.updateGroups = false



  • No labels