http://qs321.pair.com?node_id=32126

   1: #!/usr/bin/perl -w
   2: # by Steve Haslam
   3: 
   4: # description:
   5: 
   6: #  adds a user into an LDAP group- where "users" and "groups" are the
   7: #  objects seen by nss_ldap.
   8: 
   9: #  would be nice to be able to use pam-ldap here, but I've not got
  10: #  that to work yet.
  11: 
  12: # notes:
  13: #  at least on my machine, Term::Readkey gives loads of "use of
  14: #  uninitialized value" errors. ymmv.
  15: 
  16: #  illustrates lots of things, and a resonably nice showcase for some
  17: #  techniques for working with Net::LDAP (e.g. the ldapassert() sub)
  18: 
  19: require 5;
  20: use strict;
  21: use Net::LDAP;
  22: use Net::LDAP::Util qw(ldap_error_text);
  23: use Term::ReadKey;
  24: use Getopt::Std;
  25: use vars qw($hostname $basedn $ldap $sr $username $groupname $userdn $groupdn $entry $admindn $adminpw $verbose %opts);
  26: 
  27: getopts('h:D:b:v', \%opts) && @ARGV == 2 or die "Syntax: $0 [-v] [-h hostname] [-D binddn] [-b basedn] username groupname";
  28: 
  29: ($username, $groupname) = (lc($ARGV[0]), lc($ARGV[1]));
  30: 
  31: $verbose = 1 if ($opts{'v'});
  32: 
  33: # Print something if the -v switch was given
  34: sub printv {
  35:   print(@_) if ($verbose);
  36: }
  37: 
  38: # Wrapper for a Net::LDAP call- assert the server message is a "success"
  39: # code- die with a decoded error message if it isn't
  40: sub ldapassert {
  41:   my $mesg = shift;
  42:   my $op = shift;
  43:   $mesg->code && die "LDAP error".($op?" during $op":"").": ".ldap_error_text($mesg->code)."\n";
  44:   $mesg;
  45: }
  46: 
  47: # Extract a configuration option from the nss-ldap configuration file
  48: # /etc/libnss-ldap.conf is the Debian conf file location
  49: # /etc/ldap.conf is an alternative plausible location
  50: sub confoption {
  51:   my $optname = lc(shift);
  52:   my $conffile;
  53: 
  54:   foreach $conffile (qw|/etc/libnss-ldap.conf /etc/ldap.conf|) {
  55:     if (-f $conffile) {
  56:       open(LDAPCONF, $conffile) or die "Unable to open nss-ldap configuration file $conffile: $!\n";
  57:       while (<LDAPCONF>) {
  58: 	s/\#.*//;
  59: 	chomp;
  60: 	my($keyword, $value) = split(/ +/, $_, 2);
  61: 	next unless (defined($keyword) && defined($value));
  62: 	$keyword = lc($keyword);
  63: 	if ($keyword eq $optname) {
  64: 	  close(LDAPCONF);
  65: 	  printv "[ldapconf $conffile] using \"$value\" for \"$optname\"\n";
  66: 	  return $value;
  67: 	}
  68:       }
  69:       return undef;
  70:     }
  71:   }
  72: 
  73:   printv "[ldapconf] no value for \"$optname\"\n";
  74:   
  75:   return undef;
  76: }
  77: 
  78: $hostname = $opts{'h'} || confoption('host');
  79: $basedn = $opts{'b'} || confoption('base');
  80: 
  81: $ldap = Net::LDAP->new($hostname) or die "$@";
  82: 
  83: # Bind as administrator user
  84: $admindn = $opts{'D'} || confoption('binddn');
  85: 
  86: # Get admin password iff a bind dn was specified
  87: if ($admindn) {
  88:   print "LDAP password: ";
  89:   ReadMode('noecho');
  90:   $adminpw = ReadLine;
  91:   chomp($adminpw);
  92:   ReadMode(0);
  93:   print "\n";
  94: }
  95: 
  96: # Perform bind
  97: # bind anonymously if no pw given
  98: if ($adminpw) {
  99:   printv "Binding as $admindn\n";
 100:   ldapassert($ldap->bind(dn => $admindn, password => $adminpw), "bind");
 101: }
 102: else {
 103:   printv "Binding anonymously\n";
 104:   ldapassert($ldap->bind, "anonymous bind");
 105: }
 106: 
 107: # Find the user- get the user dn
 108: $sr = ldapassert($ldap->search(base => "ou=People, $basedn", filter => "(&(objectClass=posixAccount)(uid=$username))"), "user search");
 109: 
 110: if ($sr->count == 0) {
 111:   die "Unknown user '$username'\n";
 112: }
 113: elsif ($sr->count > 1) {
 114:   die "Ambiguous user '$username' (this is really bad)\n";
 115: }
 116: 
 117: $entry = $sr->shift_entry;
 118: $userdn = $entry->dn;
 119: 
 120: # Find the group- get the group dn
 121: $sr = ldapassert($ldap->search(base => "ou=Group, $basedn", filter => "(&(objectClass=posixGroup)(cn=$groupname))"), "group search");
 122: 
 123: if ($sr->count == 0) {
 124:   die "Unknown group '$groupname'\n";
 125: }
 126: elsif ($sr->count > 1) {
 127:   die "Ambiguous group '$groupname' (this is really bad)\n";
 128: }
 129: 
 130: $entry = $sr->shift_entry;
 131: $groupdn = $entry->dn;
 132: 
 133: # Is the user already in the group?
 134: foreach (@{$entry->get('memberuid')}) {
 135:   if (lc($_) eq lc($username)) {
 136:     print "$username is already a member of $groupname\n";
 137:     exit(0);
 138:   }
 139: }
 140: 
 141: # OK, now update the group entry
 142: # $entry is the group entry
 143: printv "Adding [$userdn] to [$groupdn]\n";
 144: 
 145: $entry->add(memberuid => $username);
 146: 
 147: ldapassert($entry->update($ldap), "update"); # Write updated entry to directory server
 148: 
 149: exit(0);