syncLDAP.pl 8.4 KB

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