require 'active_support/secure_random' require 'net/ldap' class SystemDataBackendLdap LDAP_USER_MAP = { :uid => :name, :userpassword => :pass, :uidnumber => :uid, :gidnumber => :gid, :loginshell => :shell, :homedirectory => :home } LDAP_GROUP_MAP = { :cn => :name, :gidnumber => :gid, :memberuid => :members } LDAP_MAILALIASPERSON_MAP = { :sn => :surname, :cn => :name } LDAP_MAP = { :User => LDAP_USER_MAP, :Group => LDAP_GROUP_MAP, :Site => { :o => :name }, :MailAliasRole => { :cn => :user }, :MailAliasPerson => LDAP_MAILALIASPERSON_MAP, :mailAccount => { :homedirectory => :home } } LDAP_FILTER = { :User => '(objectClass=posixAccount)', :Group => '(objectClass=posixGroup)', :Site => '(&(objectClass=organization)(!(o=hosting)))', :MailAliasRole => '(&(objectClass=MailAlias)(objectClass=organizationalrole))', :MailAliasPerson => '(&(objectClass=MailAlias)(objectClass=person))', :MailAccount => '(objectClass=mailAccount)' } LDAP_OBJECTCLASS = { :User => [ 'account', 'posixAccount', 'shadowAccount' ], :Group => 'posixGroup', :Site => 'organization', :MailAliasRole => [ 'organizationalRole', 'MailAlias' ], :MailAliasPerson => [ 'person', 'MailAlias' ], :MailAccount => [ 'person', 'MailAccount' ] } LDAP_LAMBDA_USER = lambda do |entry| entry[:cn] = entry[:uid] entry[:shadowlastchange] = (Time::now.to_i/60/60/24).to_s entry[:replace] += ['shadowreplace'] if entry[:replace] end LDAP_LAMBDA = { :User => LDAP_LAMBDA_USER } def initialize(host, port, baseDn, args={}) @baseDn = baseDn @systemDn = 'o=system,' + @baseDn @hostingDn = 'o=hosting,' + @baseDn @systemDn = args[:systemDn] if args[:systemDn] @hostingDn = args[:hostingDn] if args[:hostingDn] @ldap = Net::LDAP.new(:host => host, :port => port) @ldapData = Hash.new end def load!(kind) @ldapData[kind] = Hash.new if ! @ldapData[kind] @ldapData[kind][:internal] = @ldap.search( :base => ldapBase(kind), :filter => Net::LDAP::Filter::construct(LDAP_FILTER[kind]) ) end def load(kind) load!(kind) if ! @ldapData[kind] @ldapData[kind][:external] = @ldapData[kind][:internal].map do |data| map = { :dn => :id } map.merge!(LDAP_MAP[kind]) if LDAP_MAP[kind] ydata = {} data.each do |key,value| ydata.merge!({ map[key] || key => value.size==1?value[0]:value.to_a }) end ydata end if ! @ldapData[kind][:external] && @ldapData[kind][:internal] @ldapData[kind][:external].each{|ydata| yield ydata} if @ldapData[kind][:external] end def update(kind, data) map = {} map.merge!(LDAP_MAP[kind].invert) if LDAP_MAP[kind] odata = @ldapData[kind][:external].find{|edata| edata[:id] == data[:id]} data.each do |key,value| pat_key = map[key] ? map[key] : key if odata[:id] =~ /(^|, *)#{pat_key.to_s}=([^, ]+)/ && $2 != value return replace(kind, data) end end entry = Net::LDAP::Entry.new(data[:id]) data = data.find_all{|key,value| value != odata[key]} data.delete(:id) replace = Array.new data.each do |key,value| key = map[key] if map[key] replace.push(key.to_s) entry[key] = value end if replace.empty? puts 'INFO: no changes' else entry[:changetype] = 'modify' entry[:replace] = replace LDAP_LAMBDA[kind].call(entry) if LDAP_LAMBDA[kind] puts entry.to_ldif end end def replace(kind, data) puts 'INFO: do replace' puts '----------------' odata = @ldapData[kind][:external].find{|edata| edata[:id] == data[:id]} delete(odata) puts insert(kind, data) puts '----------------' end def delete(data) entry = Net::LDAP::Entry.new(data[:id]) entry[:changetype] = 'delete' puts entry.to_ldif end def insert(kind, data) map = {} map.merge!(LDAP_MAP[kind].invert) if LDAP_MAP[kind] data.delete(:id) entry = Net::LDAP::Entry.new(ldapDn(kind, data)) entry[:changetype] = 'add' entry[:objectclass] = LDAP_OBJECTCLASS[kind] data.each do |key,value| key = map[key] if map[key] entry[key] = value end LDAP_LAMBDA[kind].call(entry) if LDAP_LAMBDA[kind] puts entry.to_ldif end private def ldapBase(kind) case(kind) when :User, :Group: @systemDn when :Site, :MailAliasRole, :MailAliasPerson, :MailAccount: @hostingDn end end def ldapDn(kind, data) case(kind) when :User "uid=#{data[:name]},ou=user,#{ldapBase(kind)}" when :Group "cn=#{data[:name]},ou=group,#{ldapBase(kind)}" when :Site "o=#{data[:name]},#{ldapBase(kind)}" when :MailAliasRole "cn=#{data[:user]},o=#{data[:mail].sub(/.*@/, '')},#{ldapBase(kind)}" when :MailAliasPerson "mail=#{data[:mail]},o=#{data[:mail].sub(/.*@/, '')},#{ldapBase(kind)}" when :MailAccount "mail=#{data[:mail]},o=#{data[:mail].sub(/.*@/, '')},#{ldapBase(kind)}" end end end