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";

  • No labels