#!/usr/bin/perl -w # Copyright 2003 by intrigeri@boum.org # Licensed to the public under the terms of the GNU GPL, version 2 # Latest version is on https://wiki.boum.org/ConnectFr/GensDansLdap # # This program removes a user (managed by LDAP) from one or more # groups he/she is a member of. It requires at a minimum that schemas # are provided for : # objectClass: posixAccount # objectClass: shadowAccount # Exit codes: # # 0: Success # 1: User specified showed up more than once in the database # 2: Attempting to delete home directories without being root # 3: No username specified, or showhelp was called. # 4: LDAP del (user) failed # 5: LDAP modify (group) failed # Changelog # # intrigeri - 2003 09 25 - version 0.1 # TODO # - manage opt_quiet, STDERR and print less erraticlay ;) # - check if ldap->unbind is called each time the program exits use strict; use warnings; use English; use Digest::MD5 qw(md5_base64); use Getopt::Long qw(:config bundling); # the "bundling" option sets short options case sensitive use Net::LDAP; use Net::LDAP::Entry; use Term::ReadPassword; # General Configuration Options my $version = 0.1; # LDAP Variables my $ldap; # LDAP connection my $ldaphost; # LDAP server FQDN or IP address my $ldapversion;# LDAP version (2 or 3) my $userfilter; # Filter string to show just user account entries. my $groupfilter;# Filter string to show just group entries my $result; # Error handling variable my $search; # Stores results from LDAP searches my $entry; # Individual LDAP entry my $bindcn; # CN to bind with to authorize adding an entry my $bindpass; # LDAP Bind Password my $base; # Base under which everything is stored my $userou; # Organizational unit under which user accounts are stored my $groupou; # Organizational unit under which group information is stored my $userbase; # Complete search base for searching for users my $groupbase; # Complete search base for searching for groups my $cn; # User entry cn $ldaphost = "127.0.0.1"; $ldapversion = 3; $userfilter = "(objectClass=posixAccount)"; $groupfilter = "(objectClass=posixGroup)"; $bindcn = "cn=admin,dc=tanneries,dc=taz"; $base = "dc=tanneries,dc=taz"; $userou = "ou=People"; $groupou = "ou=Group"; if(defined $userou) { $userbase = $userou.",".$base; } else { $userbase = $base; } if(defined $groupou) { $groupbase = $groupou.",".$base; } else { $groupbase = $base; } # User data variables my $username; # Username # Potential command line variables my $opt_help; my $opt_quiet; my $opt_verbose; my $opt_simulate; GetOptions("help|h|?" => sub { showhelp() }, "quiet|q" => \$opt_quiet, "verbose|v" => \$opt_verbose, "simulate|no-act" => \$opt_simulate, ); # After the option parsing is finished, the first leftover argument is # a username, and the other ones are groupnames to be handled. Those # get shifted out of the @ARGV array one at a time into the scalar # variable $groupname and then processed. # # Do not be confused by the use of "uid" in LDAP filters -- in the # LDAP database uid refers to the username, uidNumber refers to the # numeric user ID, and gidNumber refers to the numeric group ID. # # If @ARGV contains no remaining entries, print help and exit # # ALGO # si pas assez d'arguments alors exit # connecter à la base LDAP # si username n'existe pas alors exit # pour chaque groupe # si ce groupe n'existe pas # warning # next # sinon # ajouter "supprimer l'user de ce groupe" à la liste des modifs à effectuer sur la base LDAP # fin si # fin pour chaque groupe # si pas simulation alors faire les modifs en vrai # If @ARGV doesn't contain enough arguments, print help and exit. if(scalar(@ARGV) < 2) { print STDERR "You must at least specify a username and a groupname.\n\n"; showhelp(); exit 3; # This is redundant, but is probably a good sanity-check } $username = shift @ARGV; # Connect to LDAP $ldap = Net::LDAP->new($ldaphost,version => $ldapversion) or die $!; $result = $ldap->start_tls(); die $result->error() if $result->code(); $bindpass = read_password("LDAP Bind Password: "); $result = $ldap->bind($bindcn, password => $bindpass); die $result->error() if $result->code(); # Does this user exist in the database ? $search = $ldap->search(base => $userbase, scope => "one", filter => "(&(uid=".$username.")".$userfilter.")" ); die $search->error() if $search->code(); if($search->count > 1) { print STDERR "More than one entry found for (&(uid=$username)$userfilter)\n"; print STDERR "This should not happen. Please check that the LDAP filter you specified\n"; print STDERR "is correct, and that your database is not corrupted.\n"; exit 1; } if($search->count == 0) { print STDERR "The specified username ($username) doesn't exists in the database.\n"; exit 4; } # For each group specified on the command line... while(@ARGV) { my $groupname = shift @ARGV; # Créer l'entrée modifiée pour le groupe. # L'écriture effective dans la base sera faite ensuite. # # ALGO # si (l'user créé appartient déjà au groupe) alors # on ne touche pas au groupe # sinon # ajouter un champ memberUid au groupe # fin si $search = $ldap->search(base => $groupbase, scope => "one", filter => "(&(cn=".$groupname.")".$groupfilter.")" ); die $search->error() if $search->code(); if($search->count > 1) { print STDERR "More than one entry found for (&(cn=$groupname)$groupfilter)\n"; print STDERR "This should not happen. Please check that the LDAP filter you specified\n"; print STDERR "is correct, and that your database is not corrupted.\n"; exit 6; } if ($search->count == 0) { print STDERR "The group ".$groupname." does not exist in the database. Skipping.\n"; next; } my $groupentry = $search->entry(0); if( ! user_in_group($groupentry, $username) ) { print STDERR "WARNING: ".$username." is not a member of group ".$groupname.". Skipping.\n"; next; } else { $groupentry->changetype('modify'); $groupentry->delete('memberUid' => [$username]); } # Écriture effective dans la base if(!defined $opt_simulate) { $result = $groupentry->update($ldap); if( $result->code() ) { print STDERR "ERROR: LDAP update returned: ".$result->error()."\n"; $ldap->unbind(); exit 7; } print "Account ".$username." successfully removed from group ".$groupname." in the database.\n" if !defined $opt_quiet; } } $ldap->unbind(); exit 0; ############################################################################### # # sub showhelp # # Displays a help message displaying usage options and then exits with # an error. # # Takes no arguments, does not return. # sub showhelp { print "To add a user to some LDAP groups:\n"; print "ldapaddusertogroup [--verbose | --quiet]\n"; print " [--simulate] [--summary]\n"; print " username group1 group2 ...\n"; print "\n"; exit 3; } # # sub user_in_group # # Takes as arguments: # - a LDAP posixGroup contained in an object Net::LDAP::Entry # - a string storing a user's name # Returns 1 if this user is a member of this group, and 0 else. # sub user_in_group { my $groupentry = shift; my $username = shift; return 0 if(group_is_empty($groupentry)); my @values = $groupentry->get_value('memberUid'); foreach my $value ( @values ) { return 1 if($value eq $username); } return 0; } # # sub group_is_empty # # Takes as argument a LDAP posixGroup contained in an object Net::LDAP::Entry. # Returns 1 if this group has no member, and 0 else. # sub group_is_empty { my $groupentry = pop; my @attributs = $groupentry->attributes(); foreach my $attribut ( @attributs ) { return 0 if($attribut eq 'memberUid') } return 1; }