1#!/usr/local/bin/perl -wT 2# This Source Code Form is subject to the terms of the Mozilla Public 3# License, v. 2.0. If a copy of the MPL was not distributed with this 4# file, You can obtain one at http://mozilla.org/MPL/2.0/. 5# 6# This Source Code Form is "Incompatible With Secondary Licenses", as 7# defined by the Mozilla Public License, v. 2.0. 8 9use strict; 10 11use lib qw(. lib); 12 13use Net::LDAP; 14use Bugzilla; 15use Bugzilla::User; 16 17my $cgi = Bugzilla->cgi; 18my $dbh = Bugzilla->dbh; 19 20my $readonly = 0; 21my $nodisable = 0; 22my $noupdate = 0; 23my $nocreate = 0; 24my $quiet = 0; 25 26### 27# Do some preparations 28### 29foreach my $arg (@ARGV) 30{ 31 if($arg eq '-r') { 32 $readonly = 1; 33 } 34 elsif($arg eq '-d') { 35 $nodisable = 1; 36 } 37 elsif($arg eq '-u') { 38 $noupdate = 1; 39 } 40 elsif($arg eq '-c') { 41 $nocreate = 1; 42 } 43 elsif($arg eq '-q') { 44 $quiet = 1; 45 } 46 else { 47 print "LDAP Sync Script\n"; 48 print "Syncronizes the users table from the LDAP server with the Bugzilla users.\n"; 49 print "Takes mail-attribute from preferences and description from 'cn' or,\n"; 50 print "if not available, from the uid-attribute.\n\n"; 51 print "usage:\n syncLDAP.pl [options]\n\n"; 52 print "options:\n"; 53 print " -r Readonly, do not make changes to Bugzilla tables\n"; 54 print " -d No disable, don't disable login by users who are not in LDAP\n"; 55 print " -u No update, don't update users, which have different description in LDAP\n"; 56 print " -c No create, don't create users, which are in LDAP but not in Bugzilla\n"; 57 print " -q Quiet mode, give less output\n"; 58 print "\n"; 59 exit; 60 } 61} 62 63my %ldap_users; 64 65### 66# Get current bugzilla users 67### 68my %bugzilla_users = %{ $dbh->selectall_hashref( 69 'SELECT login_name AS new_login_name, realname, disabledtext ' . 70 'FROM profiles', 'new_login_name') }; 71 72foreach my $login_name (keys %bugzilla_users) { 73 # remove whitespaces 74 $bugzilla_users{$login_name}{'realname'} =~ s/^\s+|\s+$//g; 75} 76 77### 78# Get current LDAP users 79### 80my $LDAPserver = Bugzilla->params->{"LDAPserver"}; 81if ($LDAPserver eq "") { 82 print "No LDAP server defined in bugzilla preferences.\n"; 83 exit; 84} 85 86my $LDAPconn; 87if($LDAPserver =~ /:\/\//) { 88 # if the "LDAPserver" parameter is in uri scheme 89 $LDAPconn = Net::LDAP->new($LDAPserver, version => 3); 90} else { 91 my $LDAPport = "389"; # default LDAP port 92 if($LDAPserver =~ /:/) { 93 ($LDAPserver, $LDAPport) = split(":",$LDAPserver); 94 } 95 $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3); 96} 97 98if(!$LDAPconn) { 99 print "Connecting to LDAP server failed. Check LDAPserver setting.\n"; 100 exit; 101} 102my $mesg; 103if (Bugzilla->params->{"LDAPbinddn"}) { 104 my ($LDAPbinddn,$LDAPbindpass) = split(":",Bugzilla->params->{"LDAPbinddn"}); 105 $mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass); 106} 107else { 108 $mesg = $LDAPconn->bind(); 109} 110if($mesg->code) { 111 print "Binding to LDAP server failed: " . $mesg->error . "\nCheck LDAPbinddn setting.\n"; 112 exit; 113} 114 115# We've got our anonymous bind; let's look up the users. 116$mesg = $LDAPconn->search( base => Bugzilla->params->{"LDAPBaseDN"}, 117 scope => "sub", 118 filter => '(&(' . Bugzilla->params->{"LDAPuidattribute"} . "=*)" . Bugzilla->params->{"LDAPfilter"} . ')', 119 ); 120 121 122if(! $mesg->count) { 123 print "LDAP lookup failure. Check LDAPBaseDN setting.\n"; 124 exit; 125} 126 127my %val = %{ $mesg->as_struct }; 128 129while( my ($key, $value) = each(%val) ) { 130 131 my @login_name = @{ $value->{Bugzilla->params->{"LDAPmailattribute"}} }; 132 my @realname = @{ $value->{"cn"} }; 133 134 # no mail entered? go to next 135 if(! @login_name) { 136 print "$key has no valid mail address\n"; 137 next; 138 } 139 140 # no cn entered? use uid instead 141 if(! @realname) { 142 @realname = @{ $value->{Bugzilla->params->{"LDAPuidattribute"}} }; 143 } 144 145 my $login = shift @login_name; 146 my $real = shift @realname; 147 $ldap_users{$login} = { realname => $real }; 148} 149 150print "\n" unless $quiet; 151 152### 153# Sort the users into disable/update/create-Lists and display everything 154### 155my %disable_users; 156my %update_users; 157my %create_users; 158 159print "Bugzilla-Users: \n" unless $quiet; 160while( my ($key, $value) = each(%bugzilla_users) ) { 161 print " " . $key . " '" . $value->{'realname'} . "' " . $value->{'disabledtext'} ."\n" unless $quiet==1; 162 if(!exists $ldap_users{$key}){ 163 if($value->{'disabledtext'} eq '') { 164 $disable_users{$key} = $value; 165 } 166 } 167} 168 169print "\nLDAP-Users: \n" unless $quiet; 170while( my ($key, $value) = each(%ldap_users) ) { 171 print " " . $key . " '" . $value->{'realname'} . "'\n" unless $quiet==1; 172 if(!defined $bugzilla_users{$key}){ 173 $create_users{$key} = $value; 174 } 175 else { 176 my $bugzilla_user_value = $bugzilla_users{$key}; 177 if($bugzilla_user_value->{'realname'} ne $value->{'realname'}) { 178 $update_users{$key} = $value; 179 } 180 } 181} 182 183print "\nDetecting email changes: \n" unless $quiet; 184while( my ($create_key, $create_value) = each(%create_users) ) { 185 while( my ($disable_key, $disable_value) = each(%disable_users) ) { 186 if($create_value->{'realname'} eq $disable_value->{'realname'}) { 187 print " " . $disable_key . " => " . $create_key ."'\n" unless $quiet==1; 188 $update_users{$disable_key} = { realname => $create_value->{'realname'}, 189 new_login_name => $create_key }; 190 delete $create_users{$create_key}; 191 delete $disable_users{$disable_key}; 192 } 193 } 194} 195 196if($quiet == 0) { 197 print "\nUsers to disable login for: \n"; 198 while( my ($key, $value) = each(%disable_users) ) { 199 print " " . $key . " '" . $value->{'realname'} . "'\n"; 200 } 201 202 print "\nUsers to update: \n"; 203 while( my ($key, $value) = each(%update_users) ) { 204 print " " . $key . " '" . $value->{'realname'} . "' "; 205 if(defined $value->{'new_login_name'}) { 206 print "has changed email to " . $value->{'new_login_name'}; 207 } 208 print "\n"; 209 } 210 211 print "\nUsers to create: \n"; 212 while( my ($key, $value) = each(%create_users) ) { 213 print " " . $key . " '" . $value->{'realname'} . "'\n"; 214 } 215 216 print "\n\n"; 217} 218 219 220### 221# now do the DB-Update 222### 223if($readonly == 0) { 224 print "Performing DB update:\nPhase 1: disabling login for users not in LDAP... " unless $quiet; 225 226 my $sth_disable = $dbh->prepare( 227 'UPDATE profiles 228 SET disabledtext = ? 229 WHERE ' . $dbh->sql_istrcmp('login_name', '?')); 230 231 if($nodisable == 0) { 232 while( my ($key, $value) = each(%disable_users) ) { 233 $sth_disable->execute('auto-disabled by ldap sync', $key); 234 } 235 print "done!\n" unless $quiet; 236 } 237 else { 238 print "disabled!\n" unless $quiet; 239 } 240 241 print "Phase 2: updating existing users... " unless $quiet; 242 243 if($noupdate == 0) { 244 while( my ($key, $value) = each(%update_users) ) { 245 my $user = Bugzilla::User->check($key); 246 if(defined $value->{'new_login_name'}) { 247 $user->set_login($value->{'new_login_name'}); 248 } else { 249 $user->set_name($value->{'realname'}); 250 } 251 $user->update(); 252 } 253 print "done!\n" unless $quiet; 254 } 255 else { 256 print "disabled!\n" unless $quiet; 257 } 258 259 print "Phase 3: creating new users... " unless $quiet; 260 if($nocreate == 0) { 261 while( my ($key, $value) = each(%create_users) ) { 262 Bugzilla::User->create({ 263 login_name => $key, 264 realname => $value->{'realname'}, 265 cryptpassword => '*'}); 266 } 267 print "done!\n" unless $quiet; 268 } 269 else { 270 print "disabled!\n" unless $quiet; 271 } 272} 273else 274{ 275 print "No changes to DB because readonly mode\n" unless $quiet; 276} 277