syncLDAP.pl 7.37 KB
Newer Older
1
#!/usr/bin/perl -T
2 3 4
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
#
6 7
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
8

9
use 5.10.1;
10
use strict;
11
use warnings;
12

13
use lib qw(. lib);
14 15

use Net::LDAP;
16 17
use Bugzilla;
use Bugzilla::User;
18 19

my $cgi = Bugzilla->cgi;
20
my $dbh = Bugzilla->dbh;
21

22
my $readonly  = 0;
23
my $nodisable = 0;
24 25 26
my $noupdate  = 0;
my $nocreate  = 0;
my $quiet     = 0;
27 28 29 30

###
# Do some preparations
###
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
foreach my $arg (@ARGV) {
  if ($arg eq '-r') {
    $readonly = 1;
  }
  elsif ($arg eq '-d') {
    $nodisable = 1;
  }
  elsif ($arg eq '-u') {
    $noupdate = 1;
  }
  elsif ($arg eq '-c') {
    $nocreate = 1;
  }
  elsif ($arg eq '-q') {
    $quiet = 1;
  }
  else {
    print "LDAP Sync Script\n";
    print
      "Syncronizes the users table from the LDAP server with the Bugzilla users.\n";
    print "Takes mail-attribute from preferences and description from 'cn' or,\n";
    print "if not available, from the uid-attribute.\n\n";
    print "usage:\n syncLDAP.pl [options]\n\n";
    print "options:\n";
    print " -r Readonly, do not make changes to Bugzilla tables\n";
    print " -d No disable, don't disable login by users who are not in LDAP\n";
    print
      " -u No update, don't update users, which have different description in LDAP\n";
    print
      " -c No create, don't create users, which are in LDAP but not in Bugzilla\n";
    print " -q Quiet mode, give less output\n";
    print "\n";
    exit;
  }
65 66 67 68 69 70 71
}

my %ldap_users;

###
# Get current bugzilla users
###
72 73 74 75 76 77 78
my %bugzilla_users = %{
  $dbh->selectall_hashref(
    'SELECT login_name AS new_login_name, realname, disabledtext '
      . 'FROM profiles',
    'new_login_name'
  )
};
79

80
foreach my $login_name (keys %bugzilla_users) {
81 82 83

  # remove whitespaces
  $bugzilla_users{$login_name}{'realname'} =~ s/^\s+|\s+$//g;
84 85 86 87 88
}

###
# Get current LDAP users
###
89
my $LDAPserver = Bugzilla->params->{"LDAPserver"};
90
if ($LDAPserver eq "") {
91 92
  print "No LDAP server defined in bugzilla preferences.\n";
  exit;
93
}
94 95

my $LDAPconn;
96 97 98 99 100 101 102 103 104 105 106
if ($LDAPserver =~ /:\/\//) {

  # if the "LDAPserver" parameter is in uri scheme
  $LDAPconn = Net::LDAP->new($LDAPserver, version => 3);
}
else {
  my $LDAPport = "389";    # default LDAP port
  if ($LDAPserver =~ /:/) {
    ($LDAPserver, $LDAPport) = split(":", $LDAPserver);
  }
  $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3);
107 108
}

109 110 111
if (!$LDAPconn) {
  print "Connecting to LDAP server failed. Check LDAPserver setting.\n";
  exit;
112 113
}
my $mesg;
114
if (Bugzilla->params->{"LDAPbinddn"}) {
115 116
  my ($LDAPbinddn, $LDAPbindpass) = split(":", Bugzilla->params->{"LDAPbinddn"});
  $mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
117 118
}
else {
119
  $mesg = $LDAPconn->bind();
120
}
121 122 123 124 125
if ($mesg->code) {
  print "Binding to LDAP server failed: "
    . $mesg->error
    . "\nCheck LDAPbinddn setting.\n";
  exit;
126 127 128
}

# We've got our anonymous bind;  let's look up the users.
129 130 131 132 133 134 135 136 137 138 139 140
$mesg = $LDAPconn->search(
  base   => Bugzilla->params->{"LDAPBaseDN"},
  scope  => "sub",
  filter => '(&('
    . Bugzilla->params->{"LDAPuidattribute"} . "=*)"
    . Bugzilla->params->{"LDAPfilter"} . ')',
);


if (!$mesg->count) {
  print "LDAP lookup failure. Check LDAPBaseDN setting.\n";
  exit;
141
}
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

my %val = %{$mesg->as_struct};

while (my ($key, $value) = each(%val)) {

  my @login_name = @{$value->{Bugzilla->params->{"LDAPmailattribute"}}};
  my @realname   = @{$value->{"cn"}};

  # no mail entered? go to next
  if (!@login_name) {
    print "$key has no valid mail address\n";
    next;
  }

  # no cn entered? use uid instead
  if (!@realname) {
    @realname = @{$value->{Bugzilla->params->{"LDAPuidattribute"}}};
  }

  my $login = shift @login_name;
  my $real  = shift @realname;
  $ldap_users{$login} = {realname => $real};
164 165 166 167 168 169 170 171 172 173 174 175
}

print "\n" unless $quiet;

###
# Sort the users into disable/update/create-Lists and display everything
###
my %disable_users;
my %update_users;
my %create_users;

print "Bugzilla-Users: \n" unless $quiet;
176 177 178 179 180 181 182 183 184 185
while (my ($key, $value) = each(%bugzilla_users)) {
  print " "
    . $key . " '"
    . $value->{'realname'} . "' "
    . $value->{'disabledtext'} . "\n"
    unless $quiet == 1;
  if (!exists $ldap_users{$key}) {
    if ($value->{'disabledtext'} eq '') {
      $disable_users{$key} = $value;
    }
186 187 188 189
  }
}

print "\nLDAP-Users: \n" unless $quiet;
190 191 192
while (my ($key, $value) = each(%ldap_users)) {
  print " " . $key . " '" . $value->{'realname'} . "'\n" unless $quiet == 1;
  if (!defined $bugzilla_users{$key}) {
193 194
    $create_users{$key} = $value;
  }
195
  else {
196
    my $bugzilla_user_value = $bugzilla_users{$key};
197
    if ($bugzilla_user_value->{'realname'} ne $value->{'realname'}) {
198 199 200 201 202 203
      $update_users{$key} = $value;
    }
  }
}

print "\nDetecting email changes: \n" unless $quiet;
204 205 206 207 208 209 210 211
while (my ($create_key, $create_value) = each(%create_users)) {
  while (my ($disable_key, $disable_value) = each(%disable_users)) {
    if ($create_value->{'realname'} eq $disable_value->{'realname'}) {
      print " " . $disable_key . " => " . $create_key . "'\n" unless $quiet == 1;
      $update_users{$disable_key}
        = {realname => $create_value->{'realname'}, new_login_name => $create_key};
      delete $create_users{$create_key};
      delete $disable_users{$disable_key};
212 213 214 215
    }
  }
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
if ($quiet == 0) {
  print "\nUsers to disable login for: \n";
  while (my ($key, $value) = each(%disable_users)) {
    print " " . $key . " '" . $value->{'realname'} . "'\n";
  }

  print "\nUsers to update: \n";
  while (my ($key, $value) = each(%update_users)) {
    print " " . $key . " '" . $value->{'realname'} . "' ";
    if (defined $value->{'new_login_name'}) {
      print "has changed email to " . $value->{'new_login_name'};
    }
    print "\n";
  }

  print "\nUsers to create: \n";
  while (my ($key, $value) = each(%create_users)) {
    print " " . $key . " '" . $value->{'realname'} . "'\n";
  }

  print "\n\n";
237 238 239 240 241 242
}


###
# now do the DB-Update
###
243 244 245 246
if ($readonly == 0) {
  print
    "Performing DB update:\nPhase 1: disabling login for users not in LDAP... "
    unless $quiet;
247

248 249
  my $sth_disable = $dbh->prepare(
    'UPDATE profiles
250
           SET disabledtext = ?
251 252
         WHERE ' . $dbh->sql_istrcmp('login_name', '?')
  );
253

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
  if ($nodisable == 0) {
    while (my ($key, $value) = each(%disable_users)) {
      $sth_disable->execute('auto-disabled by ldap sync', $key);
    }
    print "done!\n" unless $quiet;
  }
  else {
    print "disabled!\n" unless $quiet;
  }

  print "Phase 2: updating existing users... " unless $quiet;

  if ($noupdate == 0) {
    while (my ($key, $value) = each(%update_users)) {
      my $user = Bugzilla::User->check($key);
      if (defined $value->{'new_login_name'}) {
        $user->set_login($value->{'new_login_name'});
271
      }
272 273
      else {
        $user->set_name($value->{'realname'});
274
      }
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
      $user->update();
    }
    print "done!\n" unless $quiet;
  }
  else {
    print "disabled!\n" unless $quiet;
  }

  print "Phase 3: creating new users... " unless $quiet;
  if ($nocreate == 0) {
    while (my ($key, $value) = each(%create_users)) {
      Bugzilla::User->create({
        login_name => $key, realname => $value->{'realname'}, cryptpassword => '*'
      });
    }
    print "done!\n" unless $quiet;
  }
  else {
    print "disabled!\n" unless $quiet;
  }
295
}
296 297
else {
  print "No changes to DB because readonly mode\n" unless $quiet;
298
}