======================== openldap wrapper script: ======================== :Title: ldap: wrapper script :Author: Douglas O'Leary :Description: ldap: wrapper script for mundane, repetetive tasks :Disclaimer: Standard: Use the information that follows at your own risk. If you screw up a system, don't blame it on me... .. contents:: Overview: ========= NOTE: I discovered a minor logic bug in that a simple add user will not set the member attribute in the group to which the user is getting added. This screws up the memberof searches which are enabled via the rfc2307bis schema. That's workable via another call to the ldap script but shouldn't be required. That'll be fixed in a later version. LDAP Directory Interchange Format (LDIF) is obviously a fact of life for LDAP maintenance. That being said, there're some activities that are so ubiquitous and mundane that they should be scripted. So, I did. The script handles: * User and group additions and deletions. * Adding (simple) and deleting distinguished names. * Reasonably simple searchs. If you're using a complex filter, use ldapsearch. * Password/Account resets, locks, and unlocks. * Reasonably simple entry edits. If you're adding entire branches or need to modify multiple attributes simultaneously, go back to the LDIF. This is good for modifying account attributes, modifying group membership, etc. It's primary purpose is to circumvent the need for repetetive LDIF files and save some typing on ldapsearch. It's a perl script using the Net::LDAP module which doesn't come standard but can be installed via rpm. ``yum -y install perl-LDAP`` The `Net::LDAP::FAQ`_ is stock full of useful tips/tricks and hints on how to use this wonderful module. .. _Net::LDAP::FAQ: http://search.cpan.org/~gbarr/perl-ldap-0.40/lib/Net/LDAP/FAQ.pod For my purposes, having the password reset and other modification functions all in one script makes sense. I'm a single man shop running ldap as a learning tool. Since the script binds to the ldap server as the admin user, it's probably not the best idea to have it used as a production password reset that operations handles unless the script is protected via root read-only privileges and sudo access. As other lessons learned entries attest, I've been experimenting with posix and groupofnames style groups and have found, so far at least, that the rfc2307bis implementation of posixgroups is quite a bit more flexible. That means, however, that the group definitions are no longer globally applicable. Short version: if you're using rfc2307bis style groups, the script should be good to go. If you're not, set the *bis* flag variable to zero (0) on/about line 407. Also, note, if you're not using rfc2307bis style groups, the memberof searches won't return anything. This script is written for openldap. If you're using a different directory server software, there's a good chance that you'll have to translate the various attributes. For instance, openldap tracks when accounts got locked via the pwdAccountLockedTime attribute. FDS and unboundID track when an account will get unlocked via the accountunlocktime attribute. I would think the algorithms used to update the directory entries are still good. I know, for instance, that password resets, simple modifications, and searches work on FDS and unboundID. Anything outside of that, you'll have to find out on your own. Configuration: ============== Bind info: ---------- To migrate this script, the biggest thing that should be updated is the bind dn, password, users and groups dn, the default base, and ldap server (on/about line 348): :: our $rootdn = 'cn=admin,dc=oci,dc=com'; our $rootpw = 'not_really_my_pwd'; our $base = 'dc=oci,dc=com'; our $userdn = "ou=users,$base"; our $groupdn = "ou=groups,$base"; our $lsvr = 'ldaps://ldapsvr.olearycomputers.com'; Defaults: --------- * New users' passwords default to *1changeme* unless otherwise specified. (on/about line 391) * New users' group will default to ldap-users unless otherwise specified. (on/about line 243) * New users' UID will default to 282 or higher unless otherwise specified. (on/about line 265). * Automatic UID detection will find and use the first un-used UID starting incrementally at 601. Usage: ====== :: # ldap Add, delete, enable, lock, modify, reset, or search: Identify an action! Format: ldap [ options ] [ arguments to options ] -d $dn # delete a specific dn -s $filter # search: eg 'uid=qwer' -a ${add_options} # add an entry -group $group -desc $desc # add a group OR -user $user -gecos $gecos -pwd $pwd \ # add a user [ -shell $shell -min $min -max $max -warn $warn ] OR [-l | -e] -user ${user} # Lock/enable user account -r -user ${user} [-f] # reset user pwd # w/optional force arg -m -dn ${dn} [add|replace|delete] ${p}=${v} # Modify a dn * To search: :: # ldap -search uid=doleary ------------------------------------------------------------------------ dn:uid=doleary,ou=users,dc=oci,dc=com cn: doleary gecos: Doug OLeary objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 601 gidNumber: 614 homeDirectory: /home/doleary uid: doleary memberOf: cn=infra,ou=groups,dc=oci,dc=com cn=ldap-Administrators,ou=groups,dc=oci,dc=com cn=infosec,ou=groups,dc=oci,dc=com cn=dba,ou=groups,dc=oci,dc=com userPassword: [[snipped]] Additional parameters, *pwdAccountLockedTime*, *pwdReset*, *pwdChangedTime*, and *memberof* are displayed, if present, in the users entry. Note that memberof values won't be displayed if you're not using the rfc2307bis schema. Memberof search (only applicable to rfc2307bis schema users): :: # ldap -mof -search uid=doleary cn=infra,ou=groups,dc=oci,dc=com cn=ldap-Administrators,ou=groups,dc=oci,dc=com cn=infosec,ou=groups,dc=oci,dc=com cn=dba,ou=groups,dc=oci,dc=com Other examples: :: # ldap -search uidnumber=621 ------------------------------------------------------------------------ dn:uid=aadc,ou=users,dc=oci,dc=com cn: aadc gecos: aadc test user objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 621 gidNumber: 614 homeDirectory: /home/aadc uid: aadc userPassword: [[snipped]] pwdChangedTime: 20140119174946Z pwdReset: TRUE # ldap -search cn=ldap-users ------------------------------------------------------------------------ dn:cn=ldap-users,ou=groups,dc=oci,dc=com cn: ldap-users objectClass: top posixGroup gidNumber: 614 description: LDAP Users NOTE: if the display shows *pwdAccountLockedTime*, the account is locked. :: # ldap -search uid=aaaa ------------------------------------------------------------------------ dn:uid=aaaa,ou=users,dc=oci,dc=com cn: aaaa gecos: aaaa test user objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 604 gidNumber: 614 homeDirectory: /home/aaaa uid: aaaa userPassword: [[snip]] pwdAccountLockedTime: 20140118203457Z * To delete an entry:: # ldap --delete cn=cac,ou=users,dc=oci,dc=com # ldap --delete cn=ldap-users,ou=groups,dc=oci,dc=com # ldap --search cn=ldap-users # ldap --search uid=cac * To add an entry: :: # ldap -add -group ldap-users -desc 'LDAP Users' -gid 614 # ldap --search cn=ldap-users ------------------------------------------------------------------------ dn:cn=ldap-users,ou=groups,dc=oci,dc=com cn: ldap-users objectClass: top posixGroup gidNumber: 614 description: LDAP Users # ldap -add -user cac -gecos 'cac test user' # ldap --search uid=cac ------------------------------------------------------------------------ dn:cn=cac,ou=users,dc=oci,dc=com uid: cac gecos: cac test user objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 621 gidNumber: 614 homeDirectory: /home/cac cn: cac userPassword: [[snipped]] * To administratively lock an account: :: # ldap -search uid=aaaa | grep -i pwdaccount # ldap -l -user aaaa alter: add -> pwdAccountLockedTime -> 000001010000Z: done # ldap -search uid=aaaa | grep -i pwdaccount pwdAccountLockedTime: 000001010000Z * To Reenable an account: :: # ldap -search uid=aaaa | grep -i pwdaccount pwdAccountLockedTime: 20140118203457Z # ldap -e -user aaaa Account enabled: aaaa # ldap -search uid=aaaa | grep -i pwdaccount * To reset a user's password: :: # ldap -search uid=aa | grep -i pwdchangedtime pwdChangedTime: 20140119174943Z # ldap -reset -user aa -pwd 1changeme User password reset: aa # ldap -search uid=aa | grep -i pwdchangedtime pwdChangedTime: 20140119214649Z * To reset a user's password and force a password change on next login: :: # ldap -reset -user aa -pwd 1changeme -f User password reset w/force option: aa # ldap -search uid=aa ------------------------------------------------------------------------ dn:uid=aa,ou=users,dc=oci,dc=com cn: aa gecos: aa test user objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 602 gidNumber: 614 homeDirectory: /home/aa uid: aa userPassword: [[snipped]] pwdChangedTime: 20140119214749Z pwdReset: TRUE The pwdReset parameter is the forced password change flag. * The ldap script now supports simple modifications to distinguished names. When I say simple, I mean exactly that. The script will add, replace, or delete individual attributes from an entry. It won't check to see if what you're trying to alter makes any sense or even if it'll blow up your entire directory. As in all things where your login ID has special privileges, be **very** careful. Some use cases to which I've put this functionality in my studies: * To remove the pwdReset parameter, if unintentionally applied: :: # ldap -search uid=aa | grep -i pwdreset pwdReset: TRUE # ldap -modify -dn uid=aa,ou=users,dc=oci,dc=com delete pwdReset=TRUE alter: delete -> pwdReset -> TRUE: done # ldap -search uid=aa ------------------------------------------------------------------------ dn:uid=aa,ou=users,dc=oci,dc=com cn: aa gecos: aa test user objectClass: top account posixAccount shadowAccount shadowMin: 0 shadowMax: 90 shadowWarning: 7 loginShell: /bin/bash uidNumber: 602 gidNumber: 614 homeDirectory: /home/aa uid: aa userPassword: [[snipped]] pwdChangedTime: 20140119214749Z Note: the pwdChangedTime parameter cannot be altered, irritatingly enough. * To change an entry's shadowmax parameter to a week: :: # ldap -modify -dn uid=aa,ou=users,dc=oci,dc=com replace shadowmax=7 alter: replace -> shadowmax -> 7: done # ldap -search uid=aa | grep -i shadowmax shadowMax: 7 * To change the default ppolicy pwdmaxage parameter from 90 days to an hour (useful for testing pwd expiration): :: # ldap -search cn=default ------------------------------------------------------------------------ dn:cn=default,ou=policies,dc=oci,dc=com cn: default objectClass: top device pwdPolicyChecker pwdPolicy pwdAttribute: userPassword pwdInHistory: 2 pwdMinLength: 8 pwdMaxFailure: 3 pwdFailureCountInterval: 900 pwdCheckQuality: 0 pwdMustChange: TRUE pwdGraceAuthNLimit: 0 pwdExpireWarning: 604800 pwdLockoutDuration: 1800 pwdLockout: TRUE pwdMaxAge: 7776000 # ldap -modify -dn cn=default,ou=policies,dc=oci,dc=com replace pwdmaxage=3600 alter: replace -> pwdmaxage -> 3600: done # ldap -search cn=default | grep -i pwdmaxage pwdMaxAge: 3600 * To add/delete group membership, use the modify functionality: * posixgroup style: :: # ldap -modify -dn cn=infra,ou=groups,dc=oci,dc=com add memberuid=ddddd alter: add -> memberuid -> ddddd: done # ldap -search cn=infra ------------------------------------------------------------------------ dn:cn=infra,ou=groups,dc=oci,dc=com cn: infra objectClass: top posixGroup gidNumber: 635 description: System Admins memberUid: d dd ddd dddd ddddd # ldap -modify -dn cn=infra,ou=groups,dc=oci,dc=com delete memberuid=ddddd alter: delete -> memberuid -> ddddd: done # ldap -search cn=infra ------------------------------------------------------------------------ dn:cn=infra,ou=groups,dc=oci,dc=com cn: infra objectClass: top posixGroup gidNumber: 635 description: System Admins memberUid: d dd ddd dddd * groupofnames/rfc2307bis style: :: # ldap -mof -search uid=doleary cn=infra,ou=groups,dc=oci,dc=com cn=ldap-Administrators,ou=groups,dc=oci,dc=com cn=infosec,ou=groups,dc=oci,dc=com cn=dba,ou=groups,dc=oci,dc=com # ldap -mod -dn cn=weblogic,ou=groups,dc=oci,dc=com add \ member=uid=doleary,ou=users,dc=oci,dc=com alter: add -> member -> uid=doleary,ou=users,dc=oci,dc=com: done # ldap -mof -search uid=doleary cn=infra,ou=groups,dc=oci,dc=com cn=ldap-Administrators,ou=groups,dc=oci,dc=com cn=infosec,ou=groups,dc=oci,dc=com cn=dba,ou=groups,dc=oci,dc=com cn=weblogic,ou=groups,dc=oci,dc=com Summary: ======== LDIF is and always will be part of ldap maintenance; however, these mundane, routine, and repetitive should definitely be automated. If you don't use the script itself, hopefully, it'll help generate some ideas for your own automation. The last promised update, providing an option for an alternate bind dn and password probably won't be fortcomcing any time soon. If you want to take a hack at it, feel free. As it stands, the script does what I need it to do so I can continue with my research without having to play with redundant LDIF overly much. Script: ======= The script is available at http://www.olearycomputers.com/ll/ldap/ldap or you can copy/paste the 430 some odd lines below. :: #!/usr/bin/perl ##################################################################################### # ldap: Generic wrapper script for the more mundane ldapadd/modify/delete # requirements. Adds/deletes users and groups and does searches. # Authenticates as the directory admin so some care should be # exercised. # Author: Doug O'Leary # Created: 01/09/14 # ##################################################################################### use Net::LDAP; use Net::LDAP::Extension::SetPassword; use Getopt::Long; ##################################################################################### # Functions ##################################################################################### sub add_group { my ($bis, $gid, $group, $desc) = @_; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; my $def_mem = "cn=admin,dc=oci,dc=com"; $ldap->bind( dn => $rootdn, password => $rootpw, ); if ($bis) { # interesting: order of the attributes appears to be important. This was failing # until I moved member directly below the objectclass statements. my $result = $ldap->add( dn => "cn=$group,$groupdn", attr => [ 'cn' => $group, 'objectclass' => 'top', 'objectclass' => 'groupofNames', 'objectclass' => 'posixgroup', 'member' => $def_mem, 'description' => $desc, 'gidNumber' => $gid, ]); } else { my $result = $ldap->add( dn => "cn=$group,$groupdn", attr => [ 'cn' => $group, 'objectclass' => 'top', 'objectclass' => 'posixgroup', 'gidNumber' => $gid, 'description' => $desc, ]); } if ($result->code) { my $msg = "failed to add entry: " . $result->error; usage($msg); } } sub add_user { my ($user, $uid, $gid, $gecos, $pwd, $shell, $min, $max, $warn) = @_; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); my $result = $ldap->add( dn => "uid=$user,$userdn", attr => ['cn' => $user, 'gecos' => $gecos, 'objectclass' => 'top', 'objectclass' => 'account', 'objectclass' => 'posixaccount', 'objectclass' => 'shadowaccount', 'shadowmin' => $min, 'shadowmax' => $max, 'shadowwarning' => $warn, 'loginshell' => $shell, 'uidnumber' => $uid, 'gidnumber' => $gid, 'homedirectory' => "/home/$user", ]); if ($result->code) { my $msg = "failed to add entry: " . $result->error; usage($msg); } $result = $ldap->set_password( user => "uid=$user,$userdn", newpasswd => $pwd, ); if ($result->code) { my $msg = "Password update failed: " . $result->error; usage($msg); } } sub delete_entry { my $dn = shift; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); my $result = $ldap->delete("$dn"); if ($result->code) { my $msg = "failed to add entry: " . $result->error; usage($msg); } } sub enable_user { my $user = shift; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); my $dn = "uid=$user,$userdn"; my $result = $ldap->modify($dn, delete => ['pwdAccountLockedTime']); if ($result->code) { my $msg = "failed to enable user: $user: " . $result->error; usage($msg); } else { printf("Account enabled: %s\n", $user); } } sub modify_dn { my $dn = shift; my @args = @{$_[0]}; my $action = $args[0]; # my ($attr, $value) = split(/=/, $args[1]); my ($attr, $value) = $args[1] =~ m{([^=]*)=(.*)}; usage("Action must be 'add', 'delete', or 'replace': $action") if ($action !~ /add/ && $action !~ /delete/ && $action !~ /replace/); my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); my $result = $ldap->modify($dn, $action => {$attr => "$value"}); if ($result->code) { my $msg = "failed to alter attribute: " . $result->error; usage($msg); } else {print "alter: $action -> $attr -> $value: done\n"; } } sub reset_pwd { my ($user, $pwd, $force) = @_; my $slappasswd = '/usr/sbin/slappasswd'; my $dn = "uid=$user,$userdn"; my ($result,$msg); my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); if (defined($pwd)) { $result = $ldap->set_password( user => $dn, newpasswd => "$pwd" ); } else { $result = $ldap->set_password( user => $dn, ); } if ($result->code) { my $msg = "failed to set pwd: " . $result->error; usage($msg); } else { if (! defined($force)) { (defined($result->gen_password())) ? ($msg = "$user:" . $result->gen_password()) : ($msg = "$user"); print "User password reset: $msg\n"; } } if (defined($force)) { my $result = $ldap->modify($dn, add => { pwdReset => TRUE }); if ($result->code) { my $msg = "failed to force pwd change: " . $result->error; usage($msg); } else { print "User password reset w/force option: $user\n"; } } } sub search { my ($mof, $filter) = @_; my $mesg; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); ($mesg) = $ldap->search( base => $base, filter => $filter, attrs => [ '*', 'pwdAccountLockedTime', 'pwdReset', 'pwdChangedTime', 'pwdMaxAge', 'pwdminage', 'memberof' ], ); if (! $mof) { foreach $entry ($mesg->all_entries) { $entry->dump; } } else { my $entry = $mesg->entry(0); my @memof = $entry->get_value('memberof'); foreach my $mem (@memof) { print "$mem\n"; } } } sub verify_group { my ($gid, $group) = @_; my $def_group = 'ldap-users'; my $group_base = "$groupdn"; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); # No group or gid supplied so use the default ldap-users if (! defined($gid) && ! defined($group)) { my ($mesg) = $ldap->search( base => $group_base, filter => "cn=$def_group", attrs => ['gidnumber'], ); return ($mesg->entry(0))->get_value('gidnumber'); } # If gid supplied, verify it's a valid group: if (defined($gid)) { my ($mesg) = $ldap->search( base => $group_base, filter => "gidnumber=$gid", ); ($mesg->count == 1) ? (return $gid) : (usage("Invalid gid number: $gid")); } if (defined($group)) { my ($mesg) = $ldap->search( base => $group_base, filter => "cn=$group", attrs => ['gidnumber'], ); ($mesg->count == 1) ? (return ($mesg->entry(0))->get_value('gidnumber')) : (usage("Invalid group name: $group")); } usage("Verify group: we **really** shouldn't have gotten here..."); } sub verify_user { my ($uid, $user) = @_; my $uid_min = 601; my @uids; my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); ## Ensure $user doesn't already exist: my ($mesg) = $ldap->search( base => $base, filter => "uid=$user", ); usage("User already exists: $user") if ($mesg->count > 0); ### Ensure UID doesn't already exist: if (defined($uid)) { my ($mesg) = $ldap->search( base => "$userdn", filter => "uidnumber=$uid", ); ($mesg->count > 0) ? (usage("UID already exists: $uid")) : (return $uid); } ### ID next available UID: if (! defined($uid)) { my ($mesg) = $ldap->search( base => "$userdn", filter => 'cn=*', attrs => ['uidnumber'], ); foreach $entry ($mesg->all_entries) { push (@uids, $entry->get_value('uidnumber')); } my $curr_uid = $uid_min - 1; my $next_uid = $uid_min; foreach my $suid (sort {$a <=> $b} @uids) { # printf("Curr: %3d Next: %3d Suid: %3d\n", $curr_uid, $next_uid, $suid); last if ($suid != $next_uid); $curr_uid = $suid; $next_uid += 1; } return $next_uid; } } sub usage { my $msg = shift; print "\n$msg" if (defined($msg)); print "\nFormat:\n"; print " ldap [ options ] [ arguments to options ]\n"; print " -d \$dn # delete a specific dn\n"; print " -s \$filter # search: eg 'uid=qwer'\n"; print " -a \${add_options} # add an entry\n"; print " -group \$group -desc \$desc # add a group\n"; print " OR\n"; print " -user \$user -gecos \$gecos -pwd \$pwd \\ # add a user\n"; print " [ -shell \$shell -min \$min -max \$max -warn \$warn ] \n"; print " OR\n"; print " [-l | -e] -user \${user} # Lock/enable user account\n"; print " -r -user \${user} [-f] # reset user pwd \n"; print " # w/optional force arg\n"; print " -m -dn \${dn} [add|replace|delete] \${p}=\${v} # Modify a dn\n"; exit 1; } ##################################################################################### # Main ##################################################################################### our $rootdn = 'cn=admin,dc=oci,dc=com'; our $rootpw = '3Pizda!!'; our $base = 'dc=oci,dc=com'; our $userdn = "ou=users,$base"; our $groupdn = "ou=groups,$base"; our $lsvr = 'ldaps://ldapsvr.olearycomputers.com'; my ($add, $bis, $del, $desc, $dn, $enable, $force, $gecos, $gid); my ($gruop, $max, $min, $modify, $mof, $pwd, $reset, $search, $shell); my ($uid, $user, $warn); GetOptions ( "add" => \$add, "base=s" => \$base, "bis=i" => \$bis, "delete=s" => \$del, "desc=s" => \$desc, "dn=s" => \$dn, "enable" => \$enable, "force" => \$force, "gecos=s" => \$gecos, "gid=i" => \$gid, "group=s" => \$group, "lock" => \$lock, "min=i" => \$min, "max=i" => \$max, "modify" => \$modify, "mof" => \$mof, "pwd=s" => \$pwd, "reset" => \$reset, "search=s" => \$search, "shell=s" => \$shell, "uid=i" => \$uid, "user=s" => \$user, "warn=i" => \$warn, ); usage("Add, delete, enable, lock, modify, reset, or search: Identify an action!") unless (defined($add) || defined($del) || defined($search) || defined($lock) || defined($enable) || defined($reset) || defined($modify)); if (defined($add)) { switch: { if (defined($user)) { usage("User name, gecos, and password must be specified to add a user") if (! defined($user) || ! defined($gecos)); $pwd = '1changeme' unless (defined($pwd)); $uid = verify_user($uid, $user); $gid = verify_group($gid, $group); $min = 0 unless (defined($min)); $max = 90 unless (defined($max)); $shell = "/bin/bash" unless (defined($shell)); $warn = 7 unless (defined($warn)); add_user($user, $uid, $gid, $gecos, $pwd, $shell, $min, $max, $warn); last switch; } if (defined($group)) { usage("Group description must be defined if adding a group.") if (! defined($desc)); usage("GID must be defined if adding a group.") if (! defined($gid)); $bis = 1 unless (defined($bis)); add_group($bis, $gid, $group, $desc); last switch; } usage("User or group, one or the other..."); } } $mof = 0 unless (defined($mof)); usage("Specify a dn to modify!") if (defined($modify) && ! defined($dn)); usage("Specify user to reenable!") if ((defined($enable)) && (!defined($user))); usage("Specify user to reset!") if ((defined($reset)) && (!defined($user))); usage("Specify user to lock!") if ((defined($lock)) && (!defined($user))); # usage("Specify pwd to set!") if ((defined($reset)) && (!defined($pwd))); modify_dn("uid=$user,$userdn", [ "add", "pwdAccountLockedTime=000001010000Z" ]) if (defined($lock)); modify_dn($dn, \@ARGV) if (defined($modify)); reset_pwd($user, $pwd, $force) if (defined($reset)); enable_user($user) if (defined($enable)); delete_entry($del) if (defined($del)); search($mof, $search) if (length($search) > 0); __DATA__