123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- #!/usr/local/bin/ruby
- # Queries an LDAP server for all email addresses (tested against Exchange 5.5),
- # and makes nice bugzilla user entries out of them. Also disables Bugzilla users
- # that are not found in LDAP.
- # $Id$
- require 'ldap'
- require 'dbi'
- require 'getoptlong'
- opts = GetoptLong.new(
- ['--dbname', '-d', GetoptLong::OPTIONAL_ARGUMENT],
- ['--dbpassword', '-p', GetoptLong::OPTIONAL_ARGUMENT],
- ['--dbuser', '-u', GetoptLong::OPTIONAL_ARGUMENT],
- ['--dbpassfile', '-P', GetoptLong::OPTIONAL_ARGUMENT],
- ['--ldaphost', '-h', GetoptLong::REQUIRED_ARGUMENT],
- ['--ldapbase', '-b', GetoptLong::OPTIONAL_ARGUMENT],
- ['--ldapquery', '-q', GetoptLong::OPTIONAL_ARGUMENT],
- ['--maildomain', '-m', GetoptLong::OPTIONAL_ARGUMENT],
- ['--noremove', '-n', GetoptLong::OPTIONAL_ARGUMENT],
- ['--defaultpass', '-D', GetoptLong::OPTIONAL_ARGUMENT],
- ['--checkmode', '-c', GetoptLong::OPTIONAL_ARGUMENT]
- )
- # in hash to make it easy
- optHash = Hash.new
- opts.each do |opt, arg|
- optHash[opt]=arg
- end
- # grab password from file if it's an option
- if optHash['--dbpassfile']
- dbPassword=File.open(optHash['--dbpassfile'], 'r').readlines[0].chomp!
- else
- dbPassword=optHash['--dbpassword'] || nil
- end
- # make bad assumptions.
- dbName = optHash['--dbname'] || 'bugzilla'
- dbUser = optHash['--dbuser'] || 'bugzilla'
- ldapHost = optHash['--ldaphost'] || 'ldap'
- ldapBase = optHash['--ldapbase'] || ''
- mailDomain = optHash['--maildomain'] || `domainname`.chomp!
- ldapQuery = optHash['--ldapquery'] || "(&(objectclass=person)(rfc822Mailbox=*@#{mailDomain}))"
- checkMode = optHash['--checkmode'] || nil
- noRemove = optHash['--noremove'] || nil
- defaultPass = optHash['--defaultpass'] || 'bugzilla'
- if (! dbPassword)
- puts "bugzilla_ldapsync v1.3 (c) 2003 Thomas Stromberg <thomas+bugzilla@stromberg.org>"
- puts ""
- puts " -d | --dbname name of MySQL database [#{dbName}]"
- puts " -u | --dbuser username for MySQL database [#{dbUser}]"
- puts " -p | --dbpassword password for MySQL user [#{dbPassword}]"
- puts " -P | --dbpassfile filename containing password for MySQL user"
- puts " -h | --ldaphost hostname for LDAP server [#{ldapHost}]"
- puts " -b | --ldapbase Base of LDAP query, for instance, o=Bugzilla.com"
- puts " -q | --ldapquery LDAP query, uses maildomain [#{ldapQuery}]"
- puts " -m | --maildomain e-mail domain to use records from"
- puts " -n | --noremove do not remove Bugzilla users that are not in LDAP"
- puts " -c | --checkmode checkmode, does not perform any SQL changes"
- puts " -D | --defaultpass default password for new users [#{defaultPass}]"
- puts
- puts "example:"
- puts
- puts " bugzilla_ldapsync.rb -c -u taskzilla -P /tmp/test -d taskzilla -h bhncmail -m \"bowebellhowell.com\""
- exit
- end
- if (checkMode)
- puts '(checkmode enabled, no SQL writes will actually happen)'
- puts "ldapquery is #{ldapQuery}"
- puts
- end
-
- bugzillaUsers = Hash.new
- ldapUsers = Hash.new
- encPassword = defaultPass.crypt('xx')
- sqlNewUser = "INSERT INTO profiles VALUES ('', ?, '#{encPassword}', ?, '', 1, NULL, '0000-00-00 00:00:00');"
- # presumes that the MySQL database is local.
- dbh = DBI.connect("DBI:Mysql:#{dbName}", dbUser, dbPassword)
- # select all e-mail addresses where there is no disabledtext defined. Only valid users, please!
- dbh.select_all('select login_name, realname, disabledtext from profiles') { |row|
- login = row[0].downcase
- bugzillaUsers[login] = Hash.new
- bugzillaUsers[login]['desc'] = row[1]
- bugzillaUsers[login]['disabled'] = row[2]
- #puts "bugzilla has #{login} - \"#{bugzillaUsers[login]['desc']}\" (#{bugzillaUsers[login]['disabled']})"
- }
- LDAP::Conn.new(ldapHost, 389).bind{|conn|
- sub = nil
- # perform the query, but only get the e-mail address, location, and name returned to us.
- conn.search(ldapBase, LDAP::LDAP_SCOPE_SUBTREE, ldapQuery,
- ['rfc822Mailbox', 'physicalDeliveryOfficeName', 'cn']) { |entry|
-
- # Get the users first (primary) e-mail address, but I only want what's before the @ sign.
- entry.vals("rfc822Mailbox")[0] =~ /([\w\.-]+)\@/
- email = $1
-
- # We put the officename in the users description, and nothing otherwise.
- if entry.vals("physicalDeliveryOfficeName")
- location = entry.vals("physicalDeliveryOfficeName")[0]
- else
- location = ''
- end
-
- # for whatever reason, we get blank entries. Do some double checking here.
- if (email && (email.length > 4) && (location !~ /Generic/) && (entry.vals("cn")))
- if (location.length > 2)
- desc = entry.vals("cn")[0] + " (" + location + ")"
- else
- desc = entry.vals("cn")[0]
- end
-
- # take care of the whitespace.
- desc.sub!("\s+$", "")
- desc.sub!("^\s+", "")
-
- # dumb hack. should be properly escaped, and apostrophes should never ever ever be in email.
- email.sub!("\'", "%")
- email.sub!('%', "\'")
- email=email.downcase
- ldapUsers[email.downcase] = Hash.new
- ldapUsers[email.downcase]['desc'] = desc
- ldapUsers[email.downcase]['disabled'] = nil
- #puts "ldap has #{email} - #{ldapUsers[email.downcase]['desc']}"
- end
- }
- }
- # This is the loop that takes the users that we found in Bugzilla originally, and
- # checks to see if they are still in the LDAP server. If they are not, away they go!
- ldapUsers.each_key { |user|
- # user does not exist at all.
- #puts "checking ldap user #{user}"
- if (! bugzillaUsers[user])
- puts "+ Adding #{user} - #{ldapUsers[user]['desc']}"
- if (! checkMode)
- dbh.do(sqlNewUser, user, ldapUsers[user]['desc'])
- end
-
- # short-circuit now.
- next
- end
- if (bugzillaUsers[user]['desc'] != ldapUsers[user]['desc'])
- puts "* Changing #{user} from \"#{bugzillaUsers[user]['desc']}\" to \"#{ldapUsers[user]['desc']}\""
- if (! checkMode)
- # not efficient.
- dbh.do("UPDATE profiles SET realname = ? WHERE login_name = ?", ldapUsers[user]['desc'], user)
- end
- end
- if (bugzillaUsers[user]['disabled'].length > 0)
- puts "+ Enabling #{user} (was \"#{bugzillaUsers[user]['disabled']}\")"
- if (! checkMode)
- dbh.do("UPDATE profiles SET disabledtext = NULL WHERE login_name=\"#{user}\"")
- end
- end
- }
- if (! noRemove)
- bugzillaUsers.each_key { |user|
- if ((bugzillaUsers[user]['disabled'].length < 1) && (! ldapUsers[user]))
- puts "- Disabling #{user} (#{bugzillaUsers[user]['disabled']})"
- if (! checkMode)
- dbh.do("UPDATE profiles SET disabledtext = \'auto-disabled by ldap sync\' WHERE login_name=\"#{user}\"")
- end
- end
- }
- end
- dbh.disconnect
|