The info on this page applies to Grouper v4 and above. See Also Grouper Provisioning Framework 

The LDAP provisioner in Grouper v4+  replaces the old PSPNG.

The major differences from PSPNG is how groups are identified as provisionable, bulk operations for performance, using sync objects for caching and performance, configuration using the UI, etc.

High level summary

FAQ

How can I provision a boolean to LDAP

LDAP provisioning types



Default Translations

Translation examples

RDN value adjusting group name (remove prefix)

${edu.internet2.middleware.grouper.util.GrouperUtil.ldapEscapeRdnValue(edu.internet2.middleware.grouper.util.GrouperUtil.stripPrefix(grouperProvisioningGroup.name, 'w:e:'))}

DN adjusting group name (remove prefix)

${edu.internet2.middleware.grouper.util.GrouperUtil.ldapBushyDn(edu.internet2.middleware.grouper.util.GrouperUtil.stripPrefix(grouperProvisioningGroup.name, 'w:e:'), 'cn', 'ou', true, false) + ',ou=groups,dc=school,dc=edu'}


LDAP specific configuration

Default values

Fields and attributes can have default values.  This is particularly important for some LDAP products that always require a member for groups.  In other words, if you have a group in Grouper with no members and that is provisioned to LDAP, the LDAP group must have a member.  You can have a default value for the member attribute that could either be some DN in your LDAP system.  Or if supported by your LDAP, you could use <emptyString> if the default value should be an empty string.

Best practices

Lets craft some best practices

Best practiceDescription
If using AD, use bushy provisioningCN in AD can only be 64 which the group name frequently is longer than
If bushy provisioning, provision the group name somehowAn attribute could hold the group name, or sAMAccountName could hold
group name with colons as underscores
Should put gidNumber or Grouper UUID in an LDAP attributeHelps with renaming
If you want to track which groups are from which provisioners
you could provision the provisionerId to an attribute
Could be used in a filter or so its easy to see which grouper provisioner
created/or/manages that group
Note: you dont need this if all groups for provisioner are in a dedicated OU


Supported LDAP DAO Operations

These are the supported LDAP DAO operations.  The operations that are actually called are based on how you've configured the provisioner.   For example, you may or may not want Grouper to insert entities to LDAP.

Low level logging

If you are troubleshooting issues, you can enable low level Ldaptive logging in the Advanced configuration to get details about the actual operations sent to LDAP and the responses.  The logs will go to the container logs.

Example:

2021-11-04 15:28:40,182: [Thread-21] INFO  GrouperProvisioningLogCommands.infoLog(25) -  - Command log for provisioner 'ldapProvTest' - 'u5vmmuhb', retrieveAllEntities: Ldaptive searchRequest: [org.ldaptive.SearchRequest@-349354149::baseDn=ou=People,dc=example,dc=edu, searchFilter=[org.ldaptive.SearchFilter@1043503778::filter=(&(objectClass=person)(uid=*)), parameters={}], returnAttributes=[cn, objectClass, sn, uid], searchScope=SUBTREE, timeLimit=0, sizeLimit=0, derefAliases=null, typesOnly=false, binaryAttributes=null, sortBehavior=UNORDERED, searchEntryHandlers=null, searchReferenceHandlers=null, controls=null, referralHandler=null, intermediateResponseHandlers=null]


GSH troubleshooting

Run an ldap filter

    import edu.internet2.middleware.grouper.ldap.*;
    GrouperSession grouperSession = GrouperSession.startRootSession();
    List<LdapEntry> ldapEntries = LdapSessionUtils.ldapSession().list("personLdap", "ou=People,dc=example,dc=edu", LdapSearchScope.SUBTREE_SCOPE, "uid=*", (String[])(Object)GrouperUtil.toArray("uid"), null);
    System.out.println(GrouperUtil.length(ldapEntries));


Migrate from pspng

Run this:

./gsh.sh -pspngAttributesToProvisioningAttributes pspngConfigId provisioningFrameworkConfigId readonly|notReadonly deleteOrphans|dontDeleteOrphans

deleteOrphans means remove provisioning framework provisionable assignments that do not exist in pspng.  dontDeleteOrphans means leave those alone and the provisioning framework provisionable assignments are potentially a superset of pspng

Once the new provisioner is provisioning the data, turn off the pspng jobs

Caching

Sync objects can cache information in LDAP.  The cached data below are examples.  You can cache whatever data you want.  Synced from full sync (if doesnt exist or if errors), incremental (if doesnt exist or if errors), and the nightly (scheduled) subject resolution daemon (full refresh)

ObjectFieldCached data
gcGrouperSyncGroupgroupToId2group DN
gcGrouperSyncGroupgroupToId3whatever attribute value the user attribute refers to
gcGrouperSyncGroupgroupFromId2ldap group object attribute value that looks up group
gcGrouperSyncMembermemberToId2user DN
gcGrouperSyncMembermemberToId3whatever attribute value the group attribute refers to users as
gcGrouperSyncMembermemberFromId2ldap person object attribute value that looks up user
gcGrouperSyncMembermemberFromId3subject attribute value that helps look up user


Developer Documentation Below (old documentation)

Configuration

Common config attributes for LDAP are below.

ConfigExampleDescriptionNotes
class

edu.internet2.middleware.grouper.app.ldapProvisioning.LdapSync

Class extends the base provisioner classThis class informs configuration decisions. Required. Read-only.
hasSubjectLink

true

false

If the subject API is needed to resolve attribute on subjectrequired, drives requirements of other configurations. defaults to false.
hasTargetUserLink

true

false

If subjects need to be resolved in the target before provisioning

defaults to false. required.

show if groupMemberships

hasTargetGroupLink

true

false

If groups need to be resolved in the target before provisioning

defaults to false. required.

show if userAttributes

subjectSourcesToProvisionpennpersonsubject sources to provisionrequired. defaults to all except g:gsa, grouperExternal, g:isa, localEntities. comma separated list. checkboxes. 
createMissingUserstrue or false

defaults false. required.

show if userAttributes or hasTargetUserLink

createMissingGroupstrue or false

defaults to true.  required.

show if groupMemberships or hasTargetGroupLink

deleteInTargetIfInTargetAndNotGroupertrue or falseif groups in full sync should be deleted if in group all filter and not in grouper
or for attributes delete other attribute not provisioned by grouper
default to false
deleteInTargetIfDeletedInGroupertrue or falseif groups that were created in grouper were deleted should it be deleted in ldap?
or for attributes, delete attribute value if deleted in grouper
default to true
membershipFields

members

read,admin

update,admin

admin

if provisioning normal memberships or privilegesdefault to "members" for normal memberships
userSearchFilter

ldap example:

(&(objectClass=person)(uid=${targetEntity.retrieveAttributeValue('uid')}))

how to find a user

optional. show if userAttributes or hasTargetUserLink

userSearchAllFilter

ldap example:

(&(objectClass=person)(uid=*))

filter users when searching all

optional. show if userAttributes or hasTargetUserLink
groupSearchFilter

ldap example:

(&(objectClass=group)(gidNumber=${targetGroup.retrieveAttributeValue('gidNumber')}))

find a single group (other than by DN)optional. show if groupMemberships or hasTargetGroupLink
groupSearchAllFilter

ldap example:

(&(objectclass=group)(gidNumber=*))

find all groupsoptional. show if groupMemberships or hasTargetGroupLink

refreshGroupLinkIfLessThanAmount

20

refresh target group link if less than this amount

show if hasTargetGroupLink

common.groupLink.groupFromId2

${targetGroup.getName()}

Target group link - groupFromId2

show if hasTargetGroupLink

common.groupLink.groupFromId3


Target group link - groupFromId3

show if hasTargetGroupLink

common.groupLink.groupToId2


Target group link - groupToId2

show if hasTargetGroupLink

common.groupLink.groupToId3


Target group link - groupToId3

show if hasTargetGroupLink

refreshEntityLinkIfLessThanAmount

20

refresh target user link if less than this amount

show if hasTargetUserLink

common.entityLink.memberFromId2

${targetEntity.getName()}

Target user link - memberFromId2

optional

show if hasTargetUserLink

common.entityLink.memberFromId3


Target user link - memberFromId3

optional

show if hasTargetUserLink

common.entityLink.memberToId2


Target user link - memberToId2

optional

show if hasTargetUserLink

common.entityLink.memberToId3


Target user link - memberToId3

optional

show if hasTargetUserLink

refreshSubjectLinkIfLessThanAmount

20

refresh subject link if less than this amount

show if hasSubjectLink

common.subjectLink.memberFromId2


Subject link - memberFromId2

optional

show if hasSubjectLink

common.subjectLink.memberFromId3


Subject link - memberFromId3

optional

show if hasSubjectLink

common.subjectLink.memberToId2


Subject link - memberToId2

optional

show if hasSubjectLink

common.subjectLink.memberToId3


Subject link - memberToId3

optional

show if hasSubjectLink

groupAllowedToAssign

(group name)

group allowed to assign

only grouper system admin is allowed when no specific group is allowed to assign the given target

optional

allowAssignmentsOnlyOnOneStem

true or false

allow assignment only on one stem

default to false

readOnly

true or falseread onlydefault to false

debugLog

true or falseenable debug logdefault to false

logAllObjectsVerbose

true or falselog all objects verbosedefault to false

targetEntityAttributeCount

5

number of attributes for users

default to 0

Should be 0-20

show if userAttributes or hasTargetUserLink

targetGroupAttributeCount

5number of attributes for groups

default to 0

Should be 0-20

show if groupMemberships or hasTargetGroupLink

targetEntityAttribute.[0-19].name

uidname of the attributerequired

targetEntityAttribute.[0-19].isFieldElseAttribute

true or false

is field else attribute?

default to false

targetEntityAttribute.[0-19].valueType

stringvalue type

required

Should be string or int or long

targetEntityAttribute.[0-19].insert

true or false

insert attribute?

default to false

targetEntityAttribute.[0-19].update

true or false

update attribute?

default to false

targetEntityAttribute.[0-19].delete

true or false

delete attribute?

default to false

targetEntityAttribute.[0-19].select

true or false

select attribute?

default to false

targetEntityAttribute.[0-19].matchingId

true or false

matching id attribute?

default to false

targetEntityAttribute.[0-19].multiValued

true or false

multi-valued attribute?

default to false

targetEntityAttribute.[0-19].membershipAttribute

true or false

is this the membership attribute?

default to false

targetEntityAttribute.[0-19].translateExpressionFromMembership

${gcGrouperSyncGroup.getGroupToId2()}

translate expression from membership

show and require if membershipAttribute

targetEntityAttribute.[0-19].translateExpressionCreateOnly

${grouperUtil.toSet('top', 'person')}

translate expression when creating objects only

show if not membershipAttribute and insert = true

targetEntityAttribute.[0-19].translateExpression

${grouperProvisioningEntity.getSubjectId()}

translate expression otherwise

show and require if not membershipAttribute

targetGroupAttribute.[0-19].name

gidNumbername of the attributerequired

targetGroupAttribute.[0-19].isFieldElseAttribute

true or false

is field else attribute?

default to false

targetGroupAttribute.[0-19].valueType

longvalue type

required

Should be string or int or long

targetGroupAttribute.[0-19].insert

true or false

insert attribute?

default to false

targetGroupAttribute.[0-19].update

true or false

update attribute?

default to false

targetGroupAttribute.[0-19].delete

true or false

delete attribute?

default to false

targetGroupAttribute.[0-19].select

true or false

select attribute?

default to false

targetGroupAttribute.[0-19].matchingId

true or false

matching id attribute?

default to false

targetGroupAttribute.[0-19].multiValued

true or false

multi-valued attribute?

default to false

targetGroupAttribute.[0-19].membershipAttribute

true or falseis this the membership attribute?default to false

targetGroupAttribute.[0-19].translateExpressionFromMembership

${gcGrouperSyncMember.getMemberToId2()}

translate expression from membershipshow and require if membershipAttribute

targetGroupAttribute.[0-19].translateExpressionCreateOnly

${grouperUtil.toSet('top', 'posixGroup')}

translate expression when creating objects onlyshow if not membershipAttribute and insert = true

targetGroupAttribute.[0-19].translateExpression

${grouperProvisioningGroup.getIdIndex()}

translate expression otherwiseshow and require if not membershipAttribute


Discussion topics


Translation example

Make a bushy DN and take off a prefix

${edu.internet2.middleware.grouper.util.GrouperUtil.ldapBushyDn(edu.internet2.middleware.grouper.util.GrouperUtil.stripPrefix(grouperProvisioningGroup.name, 'org:something:'), 'cn', 'ou', true, false) +  ',OU=Grouper,DC=else,DC=something,DC=net'}