#! /usr/bin/perl

# create-admin.pl
# Create an LDAP administration account that is not the root LDAP admin.
# Give it the same password as another user.

# Copyright (C) 2004  Nick Urbanik <nicku(at)vtc.edu.hk>
 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

use warnings;
use strict;

use Carp qw( croak confess verbose );
use Net::LDAP qw/ LDAP_NO_SUCH_OBJECT /;

use constant NEW_TOP_LEVEL_O_NAME => 'ICT';
use constant NEW_SUFFIX => 'o=ICT';
use constant NEW_SYS_SUFFIX => 'ou=sys,o=ICT';
use constant FILTER => '(uid=*)';
use constant DEBUG => 1;
use constant DN => 'uid=nicku,ou=People,ou=sys,o=ICT';
use constant ADMIN_DN => 'cn=admin,ou=sys,o=ICT';

# Error reporting.
# Could use confess instead of croak
sub die_on_error {
    my ( $mesg, $extra_info ) = @_;
    if ( $extra_info ) {
        $extra_info .= ': ' unless index( $extra_info, ':' ) > -1;
    } else {
        $extra_info = "";
    }
    confess $extra_info, '[', $mesg->code, ']', $mesg->error if $mesg->code;
}

sub get_password($$) {
   my ( $ldap, $dn ) = @_;
   my $m = $ldap->search(
                         base => $dn,
                         scope => 'base',
                         filter => '(objectClass=*)',
                         attr => 'userPassword',
                        );
   die_on_error $m;
   my $entry = $m->pop_entry or die "No entry for password: ";
   my $pw = $entry->get_value( 'userPassword' );
   die "no password!" unless $pw;
   return $pw;
}

sub create_admin($$$) {
    my ( $ldap, $dn, $pw ) = @_;
    my $m = $ldap->add(
                       $dn,
                       attr => [
                                objectClass => 'person',
                                cn => 'admin',
                                sn => 'admin',
                                userPassword => $pw,
                               ],
                      );
    die_on_error $m, 'Creating admin';
}

sub bind_not_anonymous($$$) {
    my ( $password_file, $dn, $ldap_object ) = @_;
    open PW, "< $password_file" or die "cannot open \"$password_file\": $!";
    my $password = <PW>;
    chomp $password;
    close PW;
    my $mesg = $ldap_object->bind(
                                  $dn,
                                  password => $password,
                                  version => 3
                                 );
    die_on_error $mesg, "Failed to bind as \"$dn\" to ldap server";
    print "Now bound as \"$dn\"\n" if DEBUG;
}

# parameter is the $ldap object
sub bind_as_admin_to_local_server($) {
    bind_not_anonymous
        "/root/ldapaccounts/ldap-admin-password",
            "cn=admin," . NEW_SUFFIX,
                shift;
}

my $ldap = new Net::LDAP( 'ldap1.tyict.vtc.edu.hk' ) or die "$@";
my $mesg = $ldap->start_tls;
die_on_error $mesg;
bind_as_admin_to_local_server $ldap;
my $pw = get_password $ldap, DN;
create_admin $ldap, ADMIN_DN, $pw;
$ldap->unbind;
