LDAP to Grouper via GrouperClient
This is similar to the reconciliation process used by the University of Washington during our transition from an LDAP registry to a Grouper registry. The reconciler keeps Grouper up to date using a timely ldif from the LDAP directory.
In practice we did things somewhat differently (mostly for efficiency)
- Our web service provides a different API than Grouper's.
- Used c program instead of script.
- Sent data to the our web service directly (using curl library).
- Translated uwnetids (login identifiers used in LDAP) to subject ids (used by grouper) prior to sending memberships.
So this might be more of a template than an example.
LDAP directory
Our directory entries contain:
attribute |
value |
grouper equivalent |
---|---|---|
uwRegID |
entry uuid |
group id |
cn |
group name |
group full name (includes stem) |
displayName |
display name |
display name |
description |
description |
description |
member |
uwnetid or dns |
member |
memberGroup |
group cn |
member |
modifyTimestamp |
last modify time |
last modify time |
owner |
uwnetid of group admin |
administrator |
Process
The process makes two passes over the ldif; first for general group information, second for membership. This example is in Perl. A more production version might be better written in Java. That would save the startup time for each GrouperClient call.
#!/usr/local/bin/perl -w # # scan ldif and update grouper # # ------------------------------------------------------ # This is not a working script. It is an example # showing how a simple reconciler might be structured. # ------------------------------------------------------ use strict; use DBI; # grouperClient command may need id and password args my $grouperClient = "java -jar grouperClient.jar"; # where error go my $error_file = "recon-error.log"; my $error_count = 0; my $group_count = 0; my $ldif_file = $ARGV[0]; open LDIF, "<", $ldif_file or die $!; sub process_group() { my $okgroup = 0; my $skip = 0; my $isgrp = 0; my $regid = ''; my @owner = {}; my $dspname = ''; my $desc = ''; my $mod = ''; my $cn = ''; while (my $line=<NEW>) { chomp ($line); if ( $line =~ /^$/ ) { last; } if ($skip) { next; } if ( $line =~ /^objectClass: uwDepartmentGroup/ ) { $isgrp = 1; } if ( $line =~ /^uwRegID: (.*)$/ ) { $regid = $1; } if ( $line =~ /^cn: (.*)$/ ) { $cn = $1; } if ( $line =~ /^displayName: (.*)$/ ) { $dspname = $1; } if ( $line =~ /^description: (.*)$/ ) { $desc = $1; } if ( $line =~ /^owner: (.*)$/ ) { push(@owners,$1); } if ( $line =~ /^modifyTimestamp: (.*)$/ ) { $mod = $1; } } # send group info to grouper if ($isgrp && $cn!='' ) { print "group: $cn \"$desc\"\n"; my $ret = `$grouperClient --operation=groupSaveWs --name=$cn --displayExtension="$dspname" --description="$desc" --createParentStemsIfNotExist=T `; if ( $ret =~ /SUCCESS/ ) { $group_count += 1; } else { $error_count += 1; # log an error return 0; } # also send the admins if (scalar(@owners)>0) { $subs = join(",", @owners); $ret = `$grouperClient --operation=assignGrouperPrivilegesW --groupName=$cn --privilegeName=admin --allowed=true --subjectIds=$subs` unless ( $ret =~ /SUCCESS/ ) { $error_count += 1; # log an error return 0; } } } } sub process_members() { my $okgroup = 0; my $skip = 0; my $isgrp = 0; my $regid = ''; my $cn = ''; my @member = {}; while (my $line=<NEW>) { chomp ($line); if ( $line =~ /^$/ ) { last; } if ($skip) { next; } if ( $line =~ /^objectClass: uwDepartmentGroup/ ) { $isgrp = 1; } if ( $line =~ /^cn: (.*)$/ ) { $cn = $1; } } ## members are "uwNetID=<userid>" or "cn=<dns name>" if ( $line =~ /^member: uwNetID=(.*)$/ ) { push(@members,$1); } if ( $line =~ /^member: cn=(.*)$/ ) { push(@members,$1); } ## group members are "cn=group name" if ( $line =~ /^memberGroup: cn=(.*)$/ ) { push(@members,$1); } if ( $line =~ /^modifyTimestamp: (.*)$/ ) { $mod = $1; } } # send members to grouper my $members = @members; if ($isgrp && $cn!='' && $members>0 ) { print "group: $cn $members members\n"; $subs = join(",", @members); my $ret = `$grouperClient --operation=addMemberWs --name=$cn --subjectIds=$subs`; if ( $ret =~ /SUCCESS/ ) { $member_count += 1; } else { $error_count += 1; # log an error return 0; } } } # first pass - save groups while (my $line=<LDIF>) { chomp ($line); if ( $line =~ /^dn: (.*)/ ) { &process_group(); } } # second pass - save members while (my $line=<LDIF>) { chomp ($line); if ( $line =~ /^dn: (.*)/ ) { &process_members(); } } print "Completed:\n $group_count groups\n $member_count members\n $error_count errors\n";