Thanks everyone for the feedback. I am new to scripting and unix world, and
learning my way through.

I want only one way sync from AD --> LDAP(389-ds). The other way has to only
generate a list of users and the AD will approve/reject those. There is no
password sync as it is handled by PAM. I tried the LDAP connector, but it
still needs more development and there are lot of bugs.

I am planning to script this one out and run as a crontab. I was not able to
achieve all my requirements but here it is.

PS: I know this is the worst code you might have ever come across but I am
in the process of breaking down to modules.

Peter: Your idea is great, but I am so new to this that it will take me some
time to implement that.

===========================================================
$user_ldbase = 'ou=people,dc=LDAPDomain,dc=net';
$user_ad="cn=administrator,cn=Users,dc=ADDomain,dc=net";
$user_ld="cn=Directory Manager";
$objectclasses = [ 'person', 'posixAccount', 'top', 'inetorgperson',
'organizationalPerson'];

# attribute defaults
$loginshell='/bin/bash';

# Bind and Initialization
sub init {
        system("/bin/stty -echo");
        print STDERR "Enter Active Directory password: ";
        $passwd_ad=<STDIN>; chop $passwd_ad;
        print STDERR "\nEnter Directory Manager password: ";
        $passwd_ld=<STDIN>; chop $passwd_ld;
        print STDERR "\n";
        system("/bin/stty echo");
        $ldap_ad = Net::LDAP->new('ADDomain2-svr.ADDomain.net') or die "$@";
        $ldap_ld = Net::LDAP->new('centos-lin.LDAPDomain.net', port=>389) or
die "$@";
            $ldap_ad->bind( dn => $user_ad, password =>$passwd_ad );
            $ldap_ld->bind( dn => $user_ld, password =>$passwd_ld );
        $lastuid = 2000;
         }

# Get the list of uids to be synced(next available uid for new user)
sub getUid {
            my($mesg_ld,$entry, $uid, @uids, $uidnumber_av);
            $mesg_ld = $ldap_ld->search ( base   => $user_ldbase,filter =>
"(objectclass=posixAccount)",attrs => ['uidNumber'] );
            print $mesg_ld->error if $mesg_ld->code;
            @entry = $mesg_ld->entries;
            for (@entry) {
                my $uid = $_->get_value("uidNumber");
                      if ($uid)
                    { $uids[$uid] = 1; }
                        }
            while (1) {
                     if ($uids[$lastuid])
                     { $lastuid++; }
                else {
                          if (($ldap_ld->search(base=>$user_ldbase,filter =>
"(uidNumber=$lastuid)" ))->entry())
                    {
                          $uids[$lastuid++] = 1;
                          }
                    else
                    {
                          $uids[$lastuid] = 1;
                          $uidnumber_av = $lastuid++;
                          return $uidnumber_av;
                          }
                           }
                  }
       }

# post the AD user to LDAP
sub disableacct {
             if (($userAccountControl & 0x0002) != 0x00002)
                { $result = $ldap_ld->modify($entry_pf->dn,changes =>
[delete => ['nsAccountLock' => 'true'],add => ['nsAccountLock' =>
'false'],]);
               print "ENABLED account account for $ad_account !\n";
             }
             else
                  { $result = $ldap_ld->modify($entry_pf->dn,changes =>
[delete => ['nsAccountLock' => 'false'],add => ['nsAccountLock' =>
'true'],]); 
               print "DISABLED account account for $ad_account !\n";
                         }
           
                  if($$result{'resultCode'})
             {
              print $ad_account, $$result{'errorMessage'},"!\n";
                 }
             
             print "\n";
          }

#Create the AD user with LDAP schema
sub createADUser4LDAP {
            my($uidNumber)  = @_;
            my($entry,$dn,$mesg, $str,$type, $ad_proxyaddress);
                $entry = Net::LDAP::Entry->new;
            $dn = $ad_account;
            $dn = "uid=" . $ad_account . "," . $user_ldbase;
                $entry->dn($dn);
                $entry->add('objectclass' => $objectclasses);
                $entry->add('uid' => $ad_account);
                $entry->add('uidNumber' => $uidNumber);
                $entry->add('gidNumber' => $uidNumber);
                $entry->add('homedirectory' => "/home/$ad_account");
                $entry->add('cn' => $ad_name );
                $entry->add('gecos' => $ad_account);
                $entry->add('sn' => $sn );
                $entry->add('givenName' => $givenName);
            #$entry->add('description' => $description);
            $entry->add('userPassword' => &get_password);
                $entry->add('loginShell' => $loginshell);
                $entry->add('mail' => $ad_mail);
            if ($userAccountControl & 0x0002 != 0x00002)
                  { $entry->add('nsAccountLock' => "false"); }
            else 
                 { $entry->add('nsAccountLock' => "true"); }
                $mesg = $entry->update( $ldap_ld );
                print $mesg->error,"\n" if $mesg->code;
            print "create $dn\n";
                return $mesg->code;
              }

# Perform a search
sub process {
              $mesg_ad = $ldap_ad->search ( base =>
"OU=Users,OU=Corporate,DC=ADDomain,DC=net",filter =>
"(objectClass=person)");
          $entries = $mesg_ad->count;
          if ($entries lt 1)
          {
            print "entries=0 \n";
              exit 1;
          }
         foreach my $entry ( $mesg_ad->entries )
         {
           $dn = $entry->dn;
           $ad_account= $entry->get_value( "sAMAccountName" );
           @ad_proxyAddresses = $entry->get_value( "proxyAddresses" );
           $ad_name = $entry->get_value( "name" );
               $sn = $entry->get_value( "sn" );
               $givenName = $entry->get_value( "givenName" );
               $description = $entry->get_value( "description" );
           $userAccountControl = $entry->get_value("userAccountControl");
           $ad_mail = $entry->get_value( "mail" );
           next if(grep(/$ad_account/i, @ignore_users));
           $mesg_ld = $ldap_ld->search ( base => $user_ldbase,filter =>
"(&(objectClass=posixAccount)(uid=$ad_account))" );
           print "working on user: ",$ad_account,"\n";
               if( $mesg_ld->count)
           {
             print "found user $ad_account on ldap no changes made\n";
         $entry_pf = ($mesg_ld->entries)[0];
         &disableacct($userAccountControl, $ad_account);
           }
               elsif ($map_user_list{$ad_account})
               {
                    $mesg_ld = $ldap_ld->search
(base=>$user_ldbase,filter=>"(&(objectClass=posixAccount)(uid=$map_user_list
{$ad_account}))");
                 if( $mesg_ld->count)
             {
                   $entry_pf = ($mesg_ld->entries)[0];
           print "found user to ADD to LDAP
$ad_account/$map_user_list{$ad_account}\n";
           &disableacct($userAccountControl, $map_user_list{$ad_account});
             }
           }
               else
               {
             my($newuidnum);
             $newuidnum =  &getUid;
             print "new userid#: ", $newuidnum, "\n";
             &createADUser4LDAP($newuidnum);
           }
         } 
      }

#Set password for new user(random pass)
sub get_password { 
           my ($passname) = @_;
             @chars = ('a' .. 'k','m' .. 'z',2..9);
             $u{$passname} = "";
             for (1 .. 8)
                   {
                 $u{$passname} .= $chars[rand @chars];
             }
             $u{$passname} =  enc_passwd($u{$passname});
                 }


#Encrypted password to md5
sub enc_passwd {
            my $password = shift;
           my @chars = ( 'A' .. 'Z', 'a' .. 'z', '0' .. '9', '/', '.' );
           my $salt = "";
             for (1..8)
         {
                $salt .= $chars[rand @chars];
           }
           return ("{md5}" . unix_md5_crypt($password,$salt));
           }

# Terminate routine
sub termiante {
        $ldap_ad->unbind;
        $ldap_ld->unbind;
          }

&init;
&process;
&termiante;

=================================================


On 12/10/09 7:18 PM, "Peter Karman" <pe...@peknet.com> wrote:

> Peter Karman wrote on 12/10/09 5:48 PM:
>> Prashanth Sundaram wrote on 12/10/09 4:59 PM:
>>>    Folks,
>>> 
>>> I am a n00b to perl scripting and need help to start building my own.
>>> I am
>>> currently working on a project where the LDAP(389-ds) and Active
>>> Directory
>>> are always in sync.  I have a very minimal set of attributes and
>>> conditions
>>> to keep them in sync.
>>> 
>>> Can anyone share their code, so that I can build around it? Here¹s my
>>> requirement:
>>> * Sync New users from AD to LDAP with attributes: sAMAccountName, sn,
>>> givenName, description, userAccountControl(disable/enable),
>>> * Delete LDAP accounts which are not present in AD and vice versa.
>>> * Generate the next available uidnumber by parsing thru ldap, so new
>>> users
>>> can be created * Check memberOf for 2 groups and if true add them to
>>> corresponding groups
>>> in LDAP
>>> 
>>> If you have any of these modules written already, that would be great
>>> help.
>>> I am digging through the archive looking for related code.
>> 
>> Net::LDAP::Class should make most if not all of those requirements
>> pretty easy to implement. It was written to ease keeping a rdbms, LDAP
>> and AD all in sync.
>> 
>> 
> 
> I suppose I should back that up with some actual code.
> 
> I would set it up like this. These files:
> 
>   lib/MyLDAP/User.pm
>   lib/MyLDAP/Group.pm
>   lib/MyAD/User.pm
>   lib/MyAD/Group.pm
> 
> Containing code like this. (NOTE the code is *NOT* tested).
> 
> lib/MyLDAP/User.pm:
> 
>   package MyLDAP::User;
>   use base qw( Net::LDAP::Class::User::POSIX );
> 
>   __PACKAGE__->metadata->setup(
>       base_dn             => 'dc=yourcompany,dc=com',
>       attributes          => __PACKAGE__->POSIX_attributes,
>       unique_attributes   => __PACKAGE__->POSIX_unique_attributes,
>   );
> 
>   sub init_group_class { 'MyLDAP::Group' }
> 
>   1;
> 
> lib/MyLDAP/Group.pm:
> 
>   package MyLDAP::User;
>   use base qw( Net::LDAP::Class::Group::POSIX );
> 
>   __PACKAGE__->metadata->setup(
>       base_dn             => 'dc=yourcompany,dc=com',
>       attributes          => __PACKAGE__->POSIX_attributes,
>       unique_attributes   => __PACKAGE__->POSIX_unique_attributes,
>   );
> 
>   sub init_user_class { 'MyLDAP::User' }
> 
>   1;
> 
> lib/MyAD/User.pm:
> 
>   package MyAD::User;
>   use base qw( Net::LDAP::Class::User::AD );
> 
>   __PACKAGE__->metadata->setup(
>       base_dn             => 'dc=yourcompany,dc=com',
>       attributes          => __PACKAGE__->AD_attributes,
>       unique_attributes   => __PACKAGE__->AD_unique_attributes,
>   );
> 
>   sub init_group_class { 'MyAD::Group' }
> 
>   1;
> 
> lib/MyAD/Group.pm:
> 
>   package MyAD::Group;
>   use base qw( Net::LDAP::Class::Group::AD );
> 
>   __PACKAGE__->metadata->setup(
>       base_dn             => 'dc=yourcompany,dc=com',
>       attributes          => __PACKAGE__->AD_attributes,
>       unique_attributes   => __PACKAGE__->AD_unique_attributes,
>   );
> 
>   sub init_user_class { 'MyAD::User' }
> 
>   1;
> 
> 
> 
> Now you have your .pm class files set up, and you can write a script (or more
> likely, multiple scripts, one per action) to use them to handle all your
> requirements:
> 
>   # example to sync between AD and LDAP based on uid/sAMAccountName (username)
>   use strict;
>   use warnings;
>   use lib 'lib';
>   use Net::LDAP;
>   use MyLDAP::User;
>   use MyLDAP::Group;
>   use MyAD::User;
>   use MyAD::Group;
> 
>   # create all the ldap connections and bind
>   my $ldap_dn   = 'your-authenticated-user-dn';
>   my $ldap_pass = 'your-authenticated-user-password';
>   my $ldap_host = 'ldap://yourldapserver';
>   my $ldap      = Net::LDAP->new($ldap_host);
> 
>   my $ad_dn     = 'your-authenticated-user-dn';
>   my $ad_pass   = 'your-authenticated-user-password';
>   my $ad_host   = 'ldap://youradserver';
>   my $ad        = Net::LDAP->new($ad_host);
> 
>   # could use ssl cert here too instead of password
>   my $mesg = $ldap->bind( $ldap_dn, password => $ldap_pass );
>   $mesg->code and die Net::LDAP::Class->get_ldap_error($mesg);
>   $mesg    = $ad->bind( $ad_dn, password => $ad_pass );
>   $mesg->code and die Net::LDAP::Class->get_ldap_error($mesg);
> 
>   # iterate over all LDAP users, checking if they are in AD
>   my $num_ldap_checked = MyLDAP::User->act_on_all(
>       \&check_ldap_users,
>       { ldap => $ldap }
>   );
> 
>   sub check_ldap_users {
>       my $ldap_user = shift;
>       my $ad_user = MyAD::User->new( ldap => $ad, username => "$ldap_user" );
>       if (!$ad_user->read) {
>           warn "$ldap_user is not in AD!";
> 
>           # decide what action should be taken. Delete from ldap?
>           $ldap_user->delete;
> 
>           # or create a AD user?
>           # add them with whatever attributes you need
>           # see the perldoc for Net::LDAP::Class::User::AD
>           # ...
>           # then save to AD
>           $ad_user->create or die "can't create AD user $ad_user";
>       }
>   }
> 
>   # now do the reverse, checking AD
>   my $num_ad_checked = MyAD::User->new(
>       \&check_ad_users,
>       { ldap => $ad }
>   );
> 
>   sub check_ad_users {
>       my $ad_user = shift;
> 
>       # similar to above in check_ldap_users.
>   }
> 
>   print "Checked $num_ldap_checked LDAP users and $num_ad_checked AD users\n";
>   exit(0);
> 
> 
> 
> The basic idea is that you can set up .pm classes to handle all the dirty work
> of managing user/group relationships, setting attributes, reading/writing from
> the server, etc, and then use those classes over and over in your management
> scripts.

Reply via email to