Include Page | ||||
---|---|---|---|---|
|
Panel | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
These topics are discussed in the "Grouper Provisioning Service Provider" training series. |
Table of Contents |
---|
Introduction
...
Grouper groups, memberships, and stems may be provisioned using the provisioning service provider (PSP, formerly known as LDAPPC-NG).
...
no arguments | Display usage. |
| Calculate provisioning for all identifiers. |
| Determine provisioning difference for all identifiers. |
| Synchronize provisioning for all identifiers. |
| Calculate provisioning for an identifier. |
| Determine provisioning difference for an identifier. |
| Synchronize provisioning for an identifier. |
| Provisioned object or schema entity id. For example, group, member, etc. |
| Number of seconds between the start of recurring provisioning iterations. If omitted, only one provisioning cycle is performed. |
| Path to configuration directory. |
| Log SPML requests and responses. |
| Print SPML responses to output file. Defaults to stdout. |
| Print SPML requests as well as responses. |
| SPML request identifier. |
| Return data (identifier and attributes). |
| Return everything (identifier, attributes, and references). |
| Return identifier only. |
| Target identifier. |
...
For this example, new attribute framework etc:attribute:mailLocalAddress
and etc:attribute:seeAlso
attributes will need to be created in Grouper. Here is example code using the Grouper API :
Code Block | ||||
---|---|---|---|---|
| ||||
GrouperSession.startRootSession();
Stem etcAttributeStem = StemFinder.findByName(GrouperSession.staticGrouperSession(), "etc:attribute", true);
AttributeDef attributeDef = etcAttributeStem.addChildAttributeDef("mailLocalAddressAttributeDef", AttributeDefType.attr);
attributeDef.setAssignToGroup(true);
attributeDef.setMultiValued(true);
attributeDef.setValueType(AttributeDefValueType.string);
attributeDef.store();
etcAttributeStem.addChildAttributeDefName(attributeDef, "mailLocalAddress", "mailLocalAddress");
AttributeDef seeAlsoAttributeDef = etcAttributeStem.addChildAttributeDef("seeAlsoAttributeDef", AttributeDefType.attr);
seeAlsoAttributeDef.setAssignToStem(true);
seeAlsoAttributeDef.setMultiValued(true);
seeAlsoAttributeDef.setValueType(AttributeDefValueType.string);
seeAlsoAttributeDef.store();
etcAttributeStem.addChildAttributeDefName(seeAlsoAttributeDef, "seeAlso", "seeAlso");
|
...
No Format | ||
---|---|---|
| ||
edu.vt.middleware.ldap.ldapUrl=ldap://127.0.0.1:3891 ldap://127.0.0.1:3892
edu.vt.middleware.ldap.connectionHandler=edu.vt.middleware.ldap.handler.DefaultConnectionHandler{{connectionStrategy=ACTIVE_PASSIVE}}
# edu.vt.middleware.ldap.connectionHandler=edu.vt.middleware.ldap.handler.DefaultConnectionHandler{{connectionStrategy=ROUND_ROBIN}}
|
...
No Format | ||
---|---|---|
| ||
edu.vt.middleware.ldap.baseDn = dc=example,dc=edu
|
...
No Format | ||
---|---|---|
| ||
edu.vt.middleware.ldap.bindDn=cn=Manager,dc=example,dc=edu
edu.vt.middleware.ldap.bindCredential=secret
|
...
No Format | ||
---|---|---|
| ||
# The base DN for groups.
edu.internet2.middleware.psp.groupsBaseDn = ou=groups,dc=example,dc=edu
# The base DN for people.
edu.internet2.middleware.psp.peopleBaseDn = ou=people,dc=example,dc=edu
|
...
No Format | ||
---|---|---|
| ||
edu.internet2.middleware.psp.structure=flat
edu.internet2.middleware.psp.cnSourceAttributeID=name
|
...
No Format | ||
---|---|---|
| ||
edu.internet2.middleware.psp.structure=bushy
edu.internet2.middleware.psp.cnSourceAttributeID=extension
|
...
No Format | ||
---|---|---|
| ||
# The default base DN for searches.
edu.vt.middleware.ldap.baseDn=dc=example,dc=edu
# The base DN for groups.
edu.internet2.middleware.psp.groupsBaseDn=ou=groups,dc=example,dc=edu
# The base DN for people.
edu.internet2.middleware.psp.peopleBaseDn=ou=people,dc=example,dc=edu
|
as well as sources.xml
:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<source adapterClass="edu.internet2.middleware.subject.provider.LdapSourceAdapter">
<id>ldap</id>
<name>LdapSourceAdapter</name>
<type>person</type>
<search>
<searchType>searchSubject</searchType>
...
<param>
<param-name>base</param-name>
<param-value>ou=people,dc=example,dc=edu</param-value>
</param>
</search>
<search>
<searchType>searchSubjectByIdentifier</searchType>
...
<param>
<param-name>base</param-name>
<param-value>ou=people,dc=example,dc=edu</param-value>
</param>
</search>
<search>
<searchType>search</searchType>
...
<param>
<param-name>base</param-name>
<param-value>ou=people,dc=example,dc=edu</param-value>
</param>
</search>
|
...
No Format | ||
---|---|---|
| ||
<source adapterClass="edu.internet2.middleware.subject.provider.LdapSourceAdapter">
<id>ldap</id>
<name>LdapSourceAdapter</name>
<type>person</type>
<init-param>
<param-name>ldapProperties_file</param-name>
<param-value>ldap.properties</param-value>
</init-param>
|
...
Also in the psp service configuration psp-services.xml
, the id of the LDAP target to be provisioned, id="ldap"
, is the SPMLv2 targetId which should match the targetId attribute of <identifier/>
elements in the psp configuration file.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<Service
id="ldap"
xsi:type="psp-ldap-target:LdapTarget"
ldapPoolId="ad"
ldapPoolIdSource="grouper">
</Service>
|
In the psp configuration file psp.xml
, the target id of the <identifier/>
element of objects to be provisioned, targetId="ldap"
, should match the LDAP target id <Service id="ldap"/>
as defined in psp-services.xml
. If your Grouper LDAP subject source id is <id>ad</id>
, you do NOT need to change the targetId.
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- The ladp group DN. -->
<identifier
ref="groupDn"
targetId="ldap"
containerId="${edu.internet2.middleware.psp.groupsBaseDn}" />
|
...
The first place that Grouper LDAP subject source id "ldap" appears in the attribute resolver configuration is in the element which defines that the MemberDataConnector
should return the "dn" attribute for Grouper members whose subject source is "ldap". The "dn" attribute is used as the identifier of provisioned member objects. If your Grouper LDAP subject source id is <id>ad</id>
, then the source of the "dn" attribute should be source="ad"
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The MemberDataConnector returns attributes representing the member whose subject id or identifier is the principal name. -->
<resolver:DataConnector
id="MemberDataConnector"
xsi:type="grouper:MemberDataConnector">
<!-- Return the "dn" attribute of members whose subject source id is "ad". -->
<grouper:Attribute
id="dn"
source="ad" />
</resolver:DataConnector>
|
The second place that "ldap" appears in the attribute resolver configuration is in the element which defines that the "id" attribute should be returned as values of the "membersLdap" attribute for Grouper members whose subject source is "ldap". The values of the "membersLdap" attribute definition, Grouper LDAP subject ids, are used to calculate group memberships. If your Grouper LDAP subject source id is <id>ad</id>
, then the source of the "membersLdap" attribute should be source="ad"
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The values of the "membersLdap" attribute are the subject ids of group members from the "ldap" source. -->
<resolver:AttributeDefinition
id="membersLdap"
xsi:type="grouper:Member"
sourceAttributeID="members">
<resolver:Dependency ref="GroupDataConnector" />
<!-- The values of the "id" attribute are the identifiers of subjects whose source id is "ad". -->
<grouper:Attribute
id="id"
source="ad" />
</resolver:AttributeDefinition>
|
The third place that "ldap" appears in the attribute resolver configuration is in the element which defines that the "id" attribute should be returned as values of the "changeLogMembershipLdapSubjectId" attribute for Grouper members whose subject source is "ldap". The values of the "changeLogMembershipLdapSubjectId" attribute definition, Grouper LDAP subject ids, are used to calculate group memberships during processing of change log entries. If your Grouper LDAP subject source id is <id>ad</id>
, then the sourceId should contain "ad", for example, sourceId.getValues().contains("ad")
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The value of the "changeLogMembershipLdapSubjectId" attribute is the subject identifier of the "ldap" source member
of a membership change log entry. -->
<resolver:AttributeDefinition
id="changeLogMembershipLdapSubjectId"
xsi:type="ad:Script">
<resolver:Dependency ref="AddMembershipChangeLogDataConnector" />
<resolver:Dependency ref="DeleteMembershipChangeLogDataConnector" />
<ad:Script><![CDATA[here|Grouper:Notifications (change log)]]></ad:Script>
</resolver:AttributeDefinition>
|
...
No Format | ||
---|---|---|
| ||
# edu.vt.middleware.ldap.searchResultHandlers=edu.internet2.middleware.psp.ldap.QuotedDnResultHandler,...
|
...
The psp-grouper-ldap
project provides a Shibboleth attribute resolver attribute definition which maps Grouper names to ldap DNs.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The LDAP DN of a group. For example, "cn=groupExtension,ou=stem,ou=groups,dc=example,dc=edu". -->
<resolver:AttributeDefinition
id="groupDn"
xsi:type="psp-grouper-ldap:LdapDnFromGrouperNamePSOIdentifier"
structure="${edu.internet2.middleware.psp.structure}"
sourceAttributeID="groupNameInStem"
rdnAttributeName="cn"
baseDn="${edu.internet2.middleware.psp.groupsBaseDn}"
baseStem="${edu.internet2.middleware.psp.baseStem}">
<!-- Dependencies which return a "groupNameInStem" attribute whose value is the group name. -->
<resolver:Dependency ref="groupNameInStem"/>
|
...
By default, the psp-example-* configuration files use the Grouper name to create ldap DNs. It is also possible to create ldap DNs from the Grouper displayName by changing the relevant sourceAttributeID to "displayName".
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<resolver:AttributeDefinition
id="groupNameInStem"
xsi:type="grouper:FilteredName"
sourceAttributeID="displayName">
...
|
...
No Format | ||
---|---|---|
| ||
# The base Grouper stem to be provisioned.
edu.internet2.middleware.psp.baseStem=
|
...
No Format | ||
---|---|---|
| ||
changeLog.consumer.psp.class = edu.internet2.middleware.psp.grouper.PspChangeLogConsumer
|
...
No Format | ||
---|---|---|
| ||
changeLog.consumer.psp.quartzCron = 0 * * * * ?
|
...
No Format | ||
---|---|---|
| ||
changeLog.psp.fullSync.class = edu.internet2.middleware.psp.grouper.PspChangeLogConsumer
changeLog.psp.fullSync.quartzCron = 0 0 5 * * ?
|
...
No Format | ||
---|---|---|
| ||
changeLog.psp.fullSync.runAtStartup = true
|
...
No Format | ||
---|---|---|
| ||
log4j.appender.grouper_error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.grouper_error.File = ${grouper.home}logs/grouper_error.log
log4j.appender.grouper_error.DatePattern = '.'yyyy-MM-dd
log4j.appender.grouper_event = org.apache.log4j.DailyRollingFileAppender
log4j.appender.grouper_event.File = ${grouper.home}logs/grouper_event.log
log4j.appender.grouper_event.DatePattern = '.'yyyy-MM-dd
|
...
1. For Grouper versions prior to 2.1.0, there is a bug which will throw a NullPointerException if the following is not present in sources.xml
:
No Format |
---|
<search>
<searchType>searchSubjectByIdentifierAttributes</searchType>
<param>
<param-name>filter</param-name>
<param-value>
(&(uid=%TERM%)(objectclass=person))
</param-value>
</param>
<param>
<param-name>scope</param-name>
<param-value>SUBTREE_SCOPE</param-value>
</param>
<param>
<param-name>base</param-name>
<param-value>ou=people,dc=example,dc=edu</param-value>
</param>
</search>
|
...
For example, Grouper API version 2.0.3 requires an absolute path to ldap.properties in sourcessources.xml
:
No Format |
---|
<init-param>
<param-name>ldapProperties_file</param-name>
<param-value>/opt/grouper/2.0.3/grouper.apiBinary-2.0.3/conf/ldap.properties</param-value>
</init-param>
|
For Grouper UI and WS versions prior to 2.1.0, the path to ldap.properties
specified in sources.xml
will be different than in the Grouper API since the psp specific search result handlers must be commented out or removed in the Grouper UI :
No Format |
---|
<init-param>
<param-name>ldapProperties_file</param-name>
<param-value>/opt/grouper/2.0.3/grouper.ui-2.0.3/dist/grouper/WEB-INF/classes/ldap.properties</param-value>
</init-param>
|
...
As of Grouper version 2.1.0, which uses Ehcache 2.4, statistics
must be "true"
to collect statistics which are logged at DEBUG
level.
Code Block | |||||||
---|---|---|---|---|---|---|---|
| |||||||
<!-- Subject resolving caching -->
<!-- @see CachingResolver#find(...) -->
<cache name="edu.internet2.middleware.grouper.subj.CachingResolver.Find"
maxElementsInMemory="5000"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="120"
overflowToDisk="false"
statistics="true"
/>
<!-- @see CachingResolver#findAll(...) -->
<cache name="edu.internet2.middleware.grouper.subj.CachingResolver.FindAll"
maxElementsInMemory="5000"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="120"
overflowToDisk="false"
statistics="true"
/>
<!-- @see CachingResolver#findByIdentifier(...) -->
<cache name="edu.internet2.middleware.grouper.subj.CachingResolver.FindByIdentifier"
maxElementsInMemory="5000"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="120"
overflowToDisk="false"
statistics="true"
/>
<!-- @see CachingResolver#findByIdOrIdentifier(...) -->
<cache name="edu.internet2.middleware.grouper.subj.CachingResolver.FindByIdOrIdentifier"
maxElementsInMemory="5000"
eternal="false"
timeToIdleSeconds="30"
timeToLiveSeconds="120"
overflowToDisk="false"
statistics="true"
/>
|
...
The following configures the psp to provision a group
object.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<pso
id="group"
authoritative="true"
allSourceIdentifiersRef="groupNames">
</pso>
|
...
The following configures the psp to provision the identifier of the grouper
object as an LDAP DN returned from the groupDn
Shibboleth attribute definition from psp-resolver.xml
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<pso id="group">
<!-- The ldap group DN. -->
<identifier
ref="groupDn"
targetId="ldap"
containerId="${edu.internet2.middleware.psp.groupsBaseDn}" />
</pso>
|
property | value |
---|---|
| The id of the Shibboleth attribute definition whose value is an SPMLv2 PSO Identifier |
| The id of the provisioned target. Must match the id of a target configured in |
| The string id of the pso identifier containing the object. |
Code Block | |||||||
---|---|---|---|---|---|---|---|
| |||||||
<!-- The LDAP DN of a group. For example, "cn=group,ou=groups,dc=example,dc=edu". -->
<resolver:AttributeDefinition
id="groupDn"
xsi:type="psp-grouper-ldap:LdapDnFromGrouperNamePSOIdentifier"
structure="${edu.internet2.middleware.psp.structure}"
sourceAttributeID="name"
rdnAttributeName="cn"
base="${edu.internet2.middleware.psp.groupsBaseDn}">
<!-- Dependencies which return a "name" attribute whose value is the group name. -->
<resolver:Dependency ref="GroupDataConnector" />
<resolver:Dependency ref="DeleteGroupChangeLogDataConnector" />
<resolver:Dependency ref="UpdateGroupChangeLogDataConnector" />
</resolver:AttributeDefinition>
|
The following is an identifier expressed in SPMLv2
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<psp:pso entityName='group'>
<psoID ID='cn=group,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</psp:pso>
|
...
Code Block | ||
---|---|---|
| ||
dn: cn=group,ou=groups,dc=example,dc=edu
|
...
The optional <identifyingAttribute/>
of a provisioned object has two purposes : (1) to determine the schema entity of target objects returned from a lookup or search request and (2) to be converted to a query when searching a target for all identifiers. If <identifyingAttribute/>
is not present, the provisioned object will be ignored during bulk requests.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- Identifies ldap group objects which exist on the target by objectClass attribute value. -->
<identifyingAttribute
name="objectClass"
value="groupOfNames" />
|
...
The psp evaluates all <identifyingAttribute/> elements, only one should match, otherwise an exception is thrown.
Code Block | |||||||
---|---|---|---|---|---|---|---|
| |||||||
<pso id="stem">
<!-- The ldap organizational unit DN. -->
<identifier
ref="stemDn"
targetId="ldap"
containerId="${edu.internet2.middleware.psp.groupsBaseDn}" />
<!-- Identifies stem objects which exist on the target by objectclass attribute value. -->
<identifyingAttribute
name="objectclass"
value="organizationalUnit" />
</pso>
<pso id="group">
<!-- The ldap group DN. -->
<identifier
ref="groupDn"
targetId="ldap"
containerId="${edu.internet2.middleware.psp.groupsBaseDn}" />
<!-- Identifies stem objects which exist on the target by objectclass attribute value. -->
<identifyingAttribute
name="objectclass"
value="groupOfNames" />
</pso>
|
...
The optional <alternateIdentifier/>
element configures the psp to rename provisioned objects. It refers to an attribute resolver definition whose value is the previous (old) identifier of an object after it has been renamed. If <alternateIdentifier/>
is not present, provisioned objects will not be renamed, instead the old object will be deleted and a new object created.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The "old" ldap group DN calculated from group update change log events. -->
<alternateIdentifier ref="groupDnAlternateChangeLog" />
|
...
The following configures the psp to provision the cn
attribute of a group
. The value of the cn
attribute is returned by the cn
Shibboleth attribute definition from psp-resolver.xml
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<pso id="group">
<attribute name="cn" />
</pso>
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<resolver:AttributeDefinition
id="cn"
xsi:type="ad:Simple"
sourceAttributeID="cn">
<resolver:Dependency ref="GroupDataConnector" />
</resolver:AttributeDefinition>
|
The following is an attribute expressed in SPMLv2
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<psp:pso entityName='group'>
<psoID ID='cn=group,ou=groups,dc=example,dc=edu' targetID='ldap'/>
<data>
<dsml:attr xmlns:dsml='urn:oasis:names:tc:DSML:2:0:core' name='cn'>
<dsml:value>group</dsml:value>
</dsml:attr>
</data>
</psp:pso>
|
...
Code Block | ||
---|---|---|
| ||
dn: cn=group,ou=groups,dc=example,dc=edu
cn: group
|
...
The following configures the psp to provision a reference from a group
to a member
as values of the member
attribute. The values of the reference are returned by the membersLdap
and membersGsa
Shibboleth attribute definitions from psp-resolver.xml
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<pso id="group">
<references name="member">
<reference
ref="membersLdap"
toObject="member" />
<reference
ref="membersGsa"
toObject="group" />
</references>
</pso>
|
Code Block | |||||||
---|---|---|---|---|---|---|---|
| |||||||
<<!-- The values of the "membersLdap" attribute are the subject ids of group members from the "ldap" source. --> <resolver:AttributeDefinition id="membersLdap" xsi:type="grouper:Member" sourceAttributeID="members"> <resolver:Dependency ref="GroupDataConnector" /> <!-- The values of the "id" attribute are the identifiers of subjects whose source id is "ldap". --> <grouper:Attribute id="id" source="ldap" /> </resolver:AttributeDefinition> <!-- The values of the "membersGsa" attribute are the names of group members which are grouper groups. --> <resolver:AttributeDefinition id="membersGsa" xsi:type="grouper:Member" sourceAttributeID="members"> <resolver:Dependency ref="GroupDataConnector" /> <!-- The values of the "name" attribute are the names of groups whose source is "g:gsa". --> <grouper:Attribute id="name" source="g:gsa" /> </resolver:AttributeDefinition> |
The following is a reference expressed in SPMLv2
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<psp:pso entityName='group'>
<psoID ID='cn=group,ou=groups,dc=example,dc=edu' targetID='ldap'/>
<capabilityData mustUnderstand='true' capabilityURI='urn:oasis:names:tc:SPML:2:0:reference'>
<spmlref:reference xmlns='urn:oasis:names:tc:SPML:2:0' xmlns:spmlref='urn:oasis:names:tc:SPML:2:0:reference' typeOfReference='member'>
<spmlref:toPsoID ID='uid=123,ou=people,dc=example,dc=edu' targetID='ldap'/>
</spmlref:reference>
<spmlref:reference xmlns='urn:oasis:names:tc:SPML:2:0' xmlns:spmlref='urn:oasis:names:tc:SPML:2:0:reference' typeOfReference='memberOf'>
<spmlref:toPsoID ID='cn=group,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</spmlref:reference>
</capabilityData>
</psp:pso>
|
...
Code Block | ||
---|---|---|
| ||
dn: cn=group,ou=groups,dc=example,dc=edu
cn: group
member: uid=person,ou=people,dc=example,dc=edu
dn: uid=person,ou=people,dc=example,dc=edu
...
|
...
The values of the identifiers, attributes, and references to be provisioned are defined by a Shibboleth attribute resolver configuration psp-resolver.xml
.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The attribute resolver. -->
<Service
id="psp.AttributeResolver"
xsi:type="attribute-resolver:ShibbolethAttributeResolver">
<ConfigurationResource
file="/psp-resolver.xml"
xsi:type="resource:ClasspathResource">
<ResourceFilter
xsi:type="grouper:ClasspathPropertyReplacement"
xmlns="urn:mace:shibboleth:2.0:resource"
propertyFile="/ldap.properties" />
</ConfigurationResource>
</Service>
|
...
No Format | ||
---|---|---|
| ||
# Provisioning : PSP (version 2.1+)
log4j.logger.edu.internet2.middleware.psp = INFO
# Provisioning : vt-ldap
log4j.logger.edu.vt.middleware.ldap = INFO
# Provisioning : ldap target
# log4j.logger.edu.internet2.middleware.ldap = DEBUG
# Provisioning : Grouper plugin to Shibboleth attribute resolver
log4j.logger.edu.internet2.middleware.grouper.shibboleth = INFO
|
property | default | value |
---|---|---|
|
| If |
|
| If |
|
| If |
|
| The path to the file to which SPML requests and responses are written. |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The provisioning service provider. -->
<Service
id="psp"
xsi:type="psp:ProvisioningServiceProvider"
depends-on="psp.AttributeAuthority"
authority="psp.AttributeAuthority"
logSpml="true"
writeRequests="false"
writeResponses="false"
pathToOutputFile="">
<ConfigurationResource
file="/psp.xml"
xsi:type="resource:ClasspathResource">
<ResourceFilter
xsi:type="grouper:ClasspathPropertyReplacement"
xmlns="urn:mace:shibboleth:2.0:resource"
propertyFile="/ldap.properties" />
</ConfigurationResource>
</Service>
|
...
property | default | value |
---|---|---|
|
| If |
|
| If |
|
| If |
|
| The path to the file to which SPML requests and responses are written. |
|
| Re-use the |
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
<!-- The ldap target. The ldapPoolIdSource is either "grouper" or "spring". -->
<!-- If ldapPoolIdSource is "spring", the ldapPoolId must be the id of the ldap pool bean in the vt-ldap xml spring configuration. -->
<!-- If ldapPoolIdSource is "grouper", the ldapPoolId must be the id of the LdapSourceAdapter in sources.xml -->
<Service
id="ldap"
xsi:type="psp-ldap-target:LdapTarget"
logSpml="true"
ldapPoolId="ldap"
ldapPoolIdSource="grouper">
<!-- A <ConfigurationResource/> is required to instantiate the <Service/>, so supply a do-nothing resource. -->
<ConfigurationResource
file="/edu/internet2/middleware/psp/util/empty-bean.xml"
xsi:type="resource:ClasspathResource" />
</Service>
|
...
No Format | ||
---|---|---|
| ||
changeLog.consumer.psp.class = edu.internet2.middleware.psp.grouper.PspChangeLogConsumer
|
...
For example, when a member is added to a group, the following is part of the psp log :
Code Block |
---|
'ChangeLogEntry[timestamp=2012-05-31 11:59:56.321, sequence=344, category=membership, actionname=addMembership, fieldName=members, subjectId=test.subject.1, sourceId=ldap, membershipType=flattened, groupName=edu:groupA, ...]'
|
...
Change log consumers must extend the ChangeLogConsumerBase class :
Code Block | ||||
---|---|---|---|---|
| ||||
package edu.internet2.middleware.grouper.changeLog;
import java.util.List;
/**
* extend this class and register in the grouper-loader.properties to be a change log consumer
* @author mchyzer
*
*/
public abstract class ChangeLogConsumerBase {
/**
* process the change logs
* @param changeLogEntryList NOTE, DO NOT CHANGE OR EDIT THE OBJECTS IN THIS LIST, THEY MIGHT BE SHARED!
* @param changeLogProcessorMetadata
* @return which sequence number it got up to (which sequence number was the last one processed)
*/
public abstract long processChangeLogEntries(List<ChangeLogEntry> changeLogEntryList,
ChangeLogProcessorMetadata changeLogProcessorMetadata);
}
|
...
Processing an "addMembership" change log entry results in a psp calc request, where the identifier (principal name) to be calculated is the change log sequence number :
Code Block | ||||
---|---|---|---|---|
| ||||
<psp:calcRequest returnData='everything'>
<psp:id ID='change_log_sequence_number:344'/>
</psp:calcRequest>
|
The change log data connectors know how to retrieve a change log entry from grouper via the change log sequence number, and return attributes to the attribute resolver representing the change log entry. In general, the change log data connectors convert a grouper change log entry into (shibboleth attribute authority) attributes. For example, the attribute authority will return the following attributes for an "addMembership" change log event :
Code Block |
---|
'changeLogMembershipGroupDn' : org.openspml.v2.msg.spml.PSOIdentifier@e6acf477
'changeLogMembershipMemberDn' : org.openspml.v2.msg.spml.PSOIdentifier@97ebeb3b
'changeLogMembershipGroupName' : edu:groupA
'changeLogMembershipLdapSubjectId' : test.subject.1
|
...
The "groupMembership" provisioned service object in psp.xml
provisions the member attribute of a group calculated from an add or delete membership change log entry :
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- Provision a group membership triggered by the grouper change log. -->
<pso id="groupMembership">
<!-- The ldap group DN calculated from membership change log events. -->
<identifier
ref="changeLogMembershipGroupDn"
targetId="ldap"
containerId="ou=groups,dc=example,dc=edu" />
<!-- The ldap group "member" attribute. -->
<references name="member">
<reference
ref="changeLogMembershipLdapSubjectId"
toObject="member" />
</references>
</pso>
|
The "memberMembership" provisioned service object in psp.xml
provisions the memberOf attribute of a member calculated from an add or delete membership change log entry :
Code Block | ||||
---|---|---|---|---|
| ||||
<!-- Provision a member's membership triggered by the grouper change log. -->
<pso id="memberMembership">
<!-- The ldap group DN calculated from membership change log events. -->
<identifier
ref="changeLogMembershipMemberDn"
targetId="ldap"
containerId="ou=people,dc=example,dc=edu" />
<!-- The ldap member "memberOf" attribute. -->
<references name="memberOf">
<reference
ref="changeLogMembershipGroupName"
toObject="group" />
</references>
</pso>
|
...
In the following example, the psp change log consumer resolves provisioned service object identifiers for the principal with name 'edu:groupA', and returns an ldap dn 'cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' :
Code Block | ||||
---|---|---|---|---|
| ||||
<psp:calcRequest returnData='identifier'>
<psp:id ID='edu:groupA'/>
<psp:schemaEntity targetID='ldap' entityName='group'/>
</psp:calcRequest>
<psp:calcResponse>
<psp:id ID='edu:groupA'/>
<psp:pso entityName='group'>
<psoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</psp:pso>
</psp:calcResponse>
|
In the following example, the psp change log consumer resolves provisioned service object identifiers for the principal with name 'test.subject.1', and returns an ldap dn 'uid=test.subject.1,ou=people,dc=example,dc=edu' :
Code Block | ||||
---|---|---|---|---|
| ||||
<psp:calcRequest returnData='identifier'>
<psp:id ID='test.subject.1'/>
<psp:schemaEntity targetID='ldap' entityName='member'/>
</psp:calcRequest>
<psp:calcResponse >
<psp:id ID='test.subject.1'/>
<psp:pso entityName='member'>
<psoID ID='uid=test.subject.1,ou=people,dc=example,dc=edu' targetID='ldap'/>
</psp:pso>
</psp:calcResponse>
|
...
After resolving reference identifiers, a calc response is finally returned by the psp for the calc request whose id is the change log sequence number. The calc response returned by the psp represents how an add or delete membership change log entry should be provisioned, for example :
Code Block | ||||
---|---|---|---|---|
| ||||
<psp:calcRequest returnData='everything'>
<psp:id ID='change_log_sequence_number:344'/>
</psp:calcRequest>
<psp:calcResponse >
<psp:id ID='change_log_sequence_number:344'/>
<psp:pso entityName='groupMembership'>
<psoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
<capabilityData ... >
<spmlref:reference typeOfReference='member' ...>
<spmlref:toPsoID ID='uid=test.subject.1,ou=people,dc=example,dc=edu' targetID='ldap'/>
</spmlref:reference>
</capabilityData>
</psp:pso>
<psp:pso entityName='memberMembership'>
<psoID ID='uid=test.subject.1,ou=people,dc=example,dc=edu' targetID='ldap'/>
<capabilityData ... >
<spmlref:reference typeOfReference='memberOf' ... >
<spmlref:toPsoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</spmlref:reference>
</capabilityData>
</psp:pso>
</psp:calcResponse>
|
...
In the following example, the object with identifier 'cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' is queried for whether or not it has a 'member' reference to 'uid=test.subject.1,ou=people,dc=example,dc=edu' :
Code Block | ||||
---|---|---|---|---|
| ||||
<spmlsearch:SearchRequest returnData='identifier' ... >
<spmlsearch:query targetID='ldap' scope='pso'>
<spmlref:hasReference typeOfReference='member'>
<spmlref:toPsoID ID='uid=test.subject.1,ou=people,dc=example,dc=edu' targetID='ldap'/>
</spmlref:hasReference>
<spmlsearch:basePsoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</spmlsearch:query>
</spmlsearch:SearchRequest>
<spmlsearch:searchResponse status='success' ... />
|
...
Because the reference that should exist does not, a modify request is executed by the psp to add a 'member' reference with id 'uid=test.subject.1,ou=people,dc=example,dc=edu' to the object with id 'cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' :
Code Block | ||||
---|---|---|---|---|
| ||||
<modifyRequest entityName='groupMembership' returnData='identifier' ... >
<psoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
<modification modificationMode='add'>
<capabilityData ... >
<spmlref:reference typeOfReference='member' ... >
<spmlref:toPsoID ID='uid=test.subject.1,ou=people,dc=example,dc=edu' targetID='ldap'/>
</spmlref:reference>
</capabilityData>
</modification>
</modifyRequest>
<modifyResponse status='success' ... >
<pso>
<psoID ID='cn=groupA,ou=edu,ou=groups,dc=example,dc=edu' targetID='ldap'/>
</pso>
</modifyResponse>
|
...
Institution | Subject Source | Number of Subjects | Subject ID |
---|---|---|---|
LIGO | LDAP | 1,000 | dn: employeeNumber=882,ou=people,dc=ligo,dc=org |
Penn State | LDAP | 165,000 | dn:uid=xyx123,dc=psu,dc=edu |
UCLA | LDAP | 40,000 |
|
UMontreal | LDAP | 120,000 | sAMAccountName (value same as cn) |
UVienna | Undecided | 155,000 | cn, uid |
UWMadison |
|
|
|
...
Given groupA with memberA and groupB with memberB :
Code Block |
---|
dn : cn=groupA,ou=groups
member: cn=memberA,ou=people
dn: cn=groupB,ou=groups
member: cn=memberB,ou=people
|
If groupB is added as a member to groupA, how do you want groupA to be provisioned :
everything :
Code Block |
---|
dn : cn=groupA,ou=groups
member: cn=memberA,ou=people
member: cn=memberB,ou=people
member: cn=groupB,ou=people
|
immediate :
Code Block |
---|
dn : cn=groupA,ou=groups
member: cn=memberA,ou=people
member: cn=groupB,ou=people
|
...
The same membership structure applies to memberOf :
everything :
Code Block |
---|
dn: cn=memberB,ou=people
memberOf: cn=groupB,ou=groups
memberOf: cn=groupA,ou=groups
|
immediate :
Code Block |
---|
dn: cn=memberB,ou=people
memberOf: cn=groupB,ou=groups
|
Code Block |
---|
|
Institution | member | memberOf |
---|---|---|
LIGO | everything | everything |
Penn State |
|
|
UCLA |
|
|
UMontreal | immediate | immediate |
UVienna | everything | everything |
UWMadison |
|
|
Excluding LDAP provisioning for groups based on group name
This is based on a contribution by NYU which has been integrated into the Grouper API - Selective Group Exclusion When Provisioning to LDAP
In grouper.properties, set:
Code Block hooks.group.class=edu.internet2.middleware.grouper.hooks.examples.LDAPProvisioningHook
Also, in grouper.properties, add the names that you want to exclude (regular expressions):
Code Block LDAPProvisioningHook.exclude.regex.0=.*_excludes$ LDAPProvisioningHook.exclude.regex.1=.*_includes$ LDAPProvisioningHook.exclude.regex.2=.*_systemOfRecord$ LDAPProvisioningHook.exclude.regex.3=.*_systemOfRecordAndIncludes$
In the psp-resolver.xml, in each section (there are a few) that returns groups, subtract the following:
Code Block <grouper:Filter xsi:type="grouper:GroupExactAttribute" name="LDAPProvisioningExclude" value="true" />
So for example, the following:
Code Block <grouper:Filter xsi:type="grouper:MINUS"> <!-- The GroupInStem filter matches groups which are children of the given stem. --> <grouper:Filter xsi:type="grouper:GroupInStem" name="${edu.internet2.middleware.psp.baseStem}" scope="SUB" /> <grouper:Filter xsi:type="grouper:GroupInStem" name="etc" scope="SUB" /> </grouper:Filter>
.. would instead become:
Code Block <grouper:Filter xsi:type="grouper:MINUS"> <grouper:Filter xsi:type="grouper:MINUS"> <!-- The GroupInStem filter matches groups which are children of the given stem. --> <grouper:Filter xsi:type="grouper:GroupInStem" name="${edu.internet2.middleware.psp.baseStem}" scope="SUB" /> <grouper:Filter xsi:type="grouper:GroupInStem" name="etc" scope="SUB" /> </grouper:Filter> <grouper:Filter xsi:type="grouper:GroupExactAttribute" name="LDAPProvisioningExclude" value="true" /> </grouper:Filter>