| Chess | Tools | Data | Blog | Poetry | Why? | Wiki | Admin | Logout |

Download


#!/usr/bin/perl -w
# $Revision: 1.4 $
#
# Adds user to ldap server running on ldap://localhost:389
#
# prints LDIF if --ldif is passed
#
use strict;
$|++;    # disable buffer (autoflush)

use Net::LDAP;
use Net::LDAP::Entry;
use URI;
use Getopt::Long;
Getopt::Long::Configure('bundling');

my $USAGE =
  'adduser-ldap [--uid="username"] <--first="First"> <--last="Lastname"> [--password="passwd"] [--email="mail_id"] <--domain="domain.com"> [--organizational-unit|--ou="People"] [--posix] [--nt] [--uid-number] [--gid=number] [--home-path|--home] [-D|--bind] [-w|--bind-password="ldapsecret"] [-W|--bind-password-file="/etc/adduser-ldap.secret"] [--groups="group1,group2,..."] [--host="ldapserver"] [| ldapadd -x -D "cn=Directory Manager" -W]'
  . "

Example: adduser-ldap --uid=ismith --first=ivan --last=smith --email='ismith\@example.org' --domain=example.org --posix --uid-number=1023 --ldif --home-path=/home/ismith --password=secret123 --ou=People --groups=mygroup |ldapadd -x -D 'cn=Directory Manager' -W
";

#my $pass_cmd = "slappasswd";
my $pass_scheme = "\{CRYPT\}";    # SSHA, SHA1, MD5, CRYPT

#my $search_cmd = "ldapsearch -x -Z localhost ";

my $min_uid = "2000";
my $max_uid = "65000";

my $HELP;
my $DEBUG;
my $POSIX;
my $NT;
my $OU;
my $LDIF;

my $LDAPADMINCN      = "";                           # -D or --bind
my $LDAPSERVER       = "localhost";
my $LDAPPASSWORDFILE = "/etc/adduser-ldap.secret";
my $LDAPPASSWORD     = "";

# all possible items we will use. note that some need
# other objectClasses added. order does not matter
my @records = qw(
  carLicense
  cn
  departmentNumber
  employeeNumber
  employeeType
  facsimileTelephoneNumber
  gecos
  gidNumber
  givenName
  homeDirectory
  homePhone
  initials
  labeledURI
  loginShell
  mail
  mobile
  preferredLanguage
  roomNumber
  shadowExpire
  shadowInactive
  shadowMax
  shadowMin
  shadowWarning
  sn
  telephoneNumber
  title
  uid
  uidNumber
  userPassword
  );

my %rec = ();

my $domain = "";    # yields dc=domain,dc=com
my @groups = ();    # --groups
$rec{loginShell} = "/bin/false";    # TODO --shell
my $edomain = "";                   # TODO --email-domain. defaults to $domain

# visible domain from the outside word
# TODO set this interactively:
$rec{'webhost'} = "www";            # name of web server, without the domain

GetOptions(
    'debug'                      => \$DEBUG,
    'posix'                      => \$POSIX,
    'nt'                         => \$NT,
    'f|first=s'                  => sub { $rec{givenName} = $_[1] },
    'l|last=s'                   => sub { $rec{sn} = $_[1] },
    'u|uid=s'                    => sub { $rec{uid} = $_[1] },
    'e|m|email=s'                => sub { $rec{mail} = $_[1] },
    'd|domain=s'                 => \$domain,
    'o|ou|organizational-unit=s' => \$OU,
    'p|password=s'               => sub { $rec{userPassword} = $_[1] },
    'H|home|home-path=s'         => sub { $rec{homeDirectory} = $_[1] },
    'U|uid-number=i'             => sub { $rec{uidNumber} = $_[1] },
    'G|gid-number=i'             => sub { $rec{gidNumber} = $_[1] },
    'D|bind=s'                   => \$LDAPADMINCN,
    'w|bind-password=s'          => \$LDAPPASSWORD,
    'W|bind-password-file=s'     => \$LDAPPASSWORDFILE,
    'host=s'                     => \$LDAPSERVER,
    'groups=s'                   => sub {
        my $str = $_[1];
        foreach my $_g (split(/[[:blank:],]/, $str))
        {
            next if $_g =~ /^\s*$/;
            push @groups, $_g;
        }
    },
    'ldif'   => \$LDIF,
    'h|help' => \$HELP
          );

if ($HELP) { print $USAGE; exit 0; }

print STDERR ($USAGE) and exit(1)
  if (not exists $rec{givenName} || not exists $rec{sn} || not $domain);

# helpers
sub _debug
{
    my $str = shift;
    print STDERR "DEBUG: $str
" if ($DEBUG);
}

sub random_password
{
    my $num = shift;
    if ($num !~ /^[[:digit:]]+$/ or $num < 8)
    {
        $num = 8;
    }
    my $count          = $num;
    my @password_chars = ('.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z');
    my $_password      = undef;
    for (1 .. $count)
    {
        $_password .= (@password_chars)[rand(@password_chars)];
    }
    return $_password;
}

sub hash_password
{
    my $str     = shift;
    my $_scheme = shift;
    my $hash    = $str;
    if ($_scheme =~ /crypt/i)
    {

        # generates an MD5 sum salted password with 8 random chars
        $hash = crypt($str, "\$1\$" . random_password(8) . "\$");
    }
    return $hash;
}

sub _create_entry
{
    my ($ldap, $whatToCreate) = @_; # $whatToCreate is Net::LDAP::Entry
    my $result = $ldap->add($whatToCreate);
    return $result;
}

# @param whatToModify a hashref like " userPassword => 'foo' "
sub _modify_entry
{
    my ($ldap, $dn, $whatToModify) = @_;
    my $result = $ldap->modify($dn, 'replace' => {%$whatToModify});
    return $result;
}

sub _add_entry
{
    my ($ldap, $dn, $whatToModify) = @_;
    my $result = $ldap->modify($dn, 'add' => {%$whatToModify});
    return $result;
}

sub _get_password
{
    my $file = shift;
    if (-r $file)
    {
        open(PW, "<$file") or die("Could not read file $file. " . $!);
        undef $/;    # slurp mode
        my $content = <PW>;
        $content =~ s/(
|
)//g;
        return $content;
    }
    else
    {
        die("Could not read password file $file
");
    }
}

# end helpers

# main()
# set some internal vars:
my @domain_parts = split(/\./, $domain);
$edomain = (not $edomain) ? $domain : $edomain;

# create UID and MID using scheme:
#   (first letter of first name) + (last name)
$rec{uid} =
  (exists $rec{uid} and $rec{uid} ne "")
  ? $rec{uid}
  : lc(substr($rec{givenName}, 0, 1) . $rec{sn});

if (exists $rec{mail} and $rec{mail})
{
    if ($rec{mail} !~ /@/)
    {
        $rec{mail} .= "\@" . $edomain;
    }
}
else
{
    $rec{mail} = lc(substr($rec{givenName}, 0, 1) . $rec{sn}) . "\@" . $edomain;
}

$rec{homeDirectory} =
  (exists $rec{homeDirectory})
  ? $rec{homeDirectory}
  : "/home/${rec{uid}}";    # --posix only

$rec{cn} = ucfirst($rec{givenName}) . " " . ucfirst($rec{sn});
$rec{initials} = substr($rec{givenName}, 0, 1) . substr($rec{sn}, 0, 1);

# --posix only
# TODO calculate uid number from previous LDAP user_list
$rec{uidNumber} =
  (exists $rec{uidNumber} and $rec{uidNumber} >= 0)
  ? $rec{uidNumber}
  : sprintf("%02d", rand(my @ary = ($min_uid .. $max_uid)));
$rec{gidNumber} =
  (exists $rec{gidNumber} and $rec{gidNumber} >= 0)
  ? $rec{gidNumber}
  : "100";    # TODO get a good GID number from LDAP

$rec{userPassword} =
  (exists $rec{userPassword} and $rec{userPassword})
  ? hash_password($rec{userPassword}, $pass_scheme)
  : hash_password(random_password(8), $pass_scheme);

$rec{labeledURI} =
  "http://people.$edomain/${rec{givenName}}_${rec{sn}} User Home Page";

# TODO
$rec{title} = "Employee Title";
$rec{carLicense} = "000";
$rec{departmentNumber} = "0";
$rec{employeeNumber} = "0";
$rec{roomNumber} = "0";
$rec{employeeType} = "Full Time";
$rec{facsimileTelephoneNumber} = "000-000-0000";
$rec{homePhone} = "000-000-0000";
$rec{telephoneNumber} = "000-000-0000";
$rec{mobile} = "000-000-0000";
$rec{preferredLanguage}="en";

$rec{gecos} = "${rec{givenName}} ${rec{sn}},Home Address Here,${rec{telephoneNumber}},${rec{homePhone}},${rec{mobile}}";

# TODO sanity checks: mid, uid, uidNumber, gidNumber, ...

my $ou = ($OU) ? "ou=$OU, " : "";
my $domain_joined = "";
foreach (@domain_parts)
{
    $domain_joined .= "dc=$_, ";
}
$domain_joined =~ s/, $//;

# this $ldif is used only if --ldif
my $ldif = "dn: uid=${rec{uid}}, $ou $domain_joined
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson";

$ldif .= "
objectClass: posixAccount
objectClass: shadowAccount
" if ($POSIX);

foreach my $_item (@records)
{
    if (exists $rec{$_item})
    {
        $ldif .= "${_item}: " . $rec{$_item} . "
";
    }
}

if ((@groups + 0))
{
    foreach my $_group (@groups)
    {
        $ldif .= "

dn: cn=$_group,ou=Groups,$domain_joined
changetype: modify
add: uniqueMember
uniqueMember: uid=${rec{uid}}, $ou $domain_joined
        "
    }
}

# FIXME remove 1 when $ldap auth is done
if ($LDIF)
{
    print STDOUT ($ldif, "
");
    exit 0;
}

$LDAPADMINCN = ($LDAPADMINCN) ? $LDAPADMINCN : "cn=admin, $domain_joined";

_debug("connecting to $LDAPSERVER as $LDAPADMINCN");

my $ldap = Net::LDAP->new($LDAPSERVER);
my $mesg = $ldap->bind(
    $LDAPADMINCN,

    # --bind-password takes precedence
    'password' => ($LDAPPASSWORD)
    ? $LDAPPASSWORD
    : _get_password($LDAPPASSWORDFILE),
    'version' => '3'
                      );    # if bind() it binds anonymously
my $_dn   = "uid=${rec{uid}},${ou}${domain_joined}";
my $entry = Net::LDAP::Entry->new($_dn);
my $CODE = 0;               # assume we won't have errors

if ($POSIX)
{
    $entry->add(
                objectClass => [
                                "top",                  "person",
                                "organizationalPerson", "inetorgperson",
                                "shadowAccount",        "posixAccount"
                               ]
               );
    $rec{'shadowMin'}      = "-1";
    $rec{'shadowMax'}      = "99999";
    $rec{'shadowWarning'}  = "7";
    $rec{'shadowInactive'} = "-1";
    $rec{'shadowExpire'}   = "-1";
}
else
{
    $entry->add(objectClass =>
                ["top", "person", "organizationalPerson", "inetorgperson"]);
}

foreach my $_item (@records)
{
    $entry->add($_item => $rec{$_item});
}

my $entry_result = _create_entry($ldap, $entry);

if ($entry_result->code())
{
    print STDERR
      " Error while creating entry for uid ${rec{uid}} on \$LDAPSERVER
";

    print STDERR (  ". Server message => code: "
                  . $entry_result->code()
                  . ". name: "
                  . $entry_result->error_name()
                  . ". text: "
                  . $entry_result->error_text());
    $CODE = 1;
    goto EXIT;
}

if (@groups + 0)
{
    my $add_hash = undef;
    foreach my $_group (@groups)
    {
        my $_dn = "cn=$_group,ou=Groups,$domain_joined";
        my $add_hash =
          ['uniqueMember' => "uid=" . $rec{uid} . ", $ou $domain_joined"];
    }

    my $entry_result = _add_entry($ldap, $_dn, $add_hash);
    if ($entry_result->code())
    {
        print(
            " Error while adding uid ${rec{uid}} to group $_dn on $LDAPSERVER: "
              . $entry_result->error_text());
        goto EXIT;
    }
}

EXIT:

$ldap->unbind();    # take down session
$ldap->disconnect();

exit $CODE;

Advertisement