diff --git a/SystemDataBackendLdap.rb b/SystemDataBackendLdap.rb index 5fcca2e..6fdf68b 100644 --- a/SystemDataBackendLdap.rb +++ b/SystemDataBackendLdap.rb @@ -1,100 +1,108 @@ -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_SITE_MAP = {:o => :name} - - LDAP_MAP = { - :User => LDAP_USER_MAP, - :Group => LDAP_GROUP_MAP, - :Site => LDAP_SITE_MAP - } - - LDAP_FILTER = { - :User => Net::LDAP::Filter::eq('objectClass', 'posixAccount'), - :Group => Net::LDAP::Filter::eq('objectClass', 'posixGroup'), - :Site => Net::LDAP::Filter::eq('objectClass', 'organization') & - (~Net::LDAP::Filter::eq('o', 'hosting')), - :MailAlias => Net::LDAP::Filter::eq('objectClass', 'mailAlias'), - :MailAccount => Net::LDAP::Filter::eq('objectClass', 'mailAccount') - } - - LDAP_OBJECTCLASS = { - :User => [ 'account', 'posixAccount', 'shadowAccount' ], - :Group => 'posixGroup' - } - - 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 + 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_SITE_MAP = {:o => :name} + + LDAP_MAP = { + :User => LDAP_USER_MAP, + :Group => LDAP_GROUP_MAP, + :Site => LDAP_SITE_MAP + } + + LDAP_FILTER = { + :User => Net::LDAP::Filter::eq('objectClass', 'posixAccount'), + :Group => Net::LDAP::Filter::eq('objectClass', 'posixGroup'), + :Site => Net::LDAP::Filter::eq('objectClass', 'organization') & + (~Net::LDAP::Filter::eq('o', 'hosting')), + :MailAlias => Net::LDAP::Filter::eq('objectClass', 'mailAlias'), + :MailAccount => Net::LDAP::Filter::eq('objectClass', 'mailAccount') + } + + LDAP_OBJECTCLASS = { + :User => [ 'account', 'posixAccount', 'shadowAccount' ], + :Group => 'posixGroup', + :Site => 'organization' + } + + 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 - } + LDAP_LAMBDA = { + :User => LDAP_LAMBDA_USER + } - def initialize(host, port, baseDn, args={}) - @baseDn = baseDn - @systemDn = 'o=system,' + @baseDn - @hostingDn = 'o=hosting,' + @baseDn + 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] + @systemDn = args[:systemDn] if args[:systemDn] + @hostingDn = args[:hostingDn] if args[:hostingDn] - @ldap = Net::LDAP.new(:host => host, :port => port) - @ldapData = Hash.new - end + @ldap = Net::LDAP.new(:host => host, :port => port) + @ldapData = Hash.new + end - def load!(kind) - @ldapData[kind] = Hash.new if ! @ldapData[kind] + def load!(kind) + @ldapData[kind] = Hash.new if ! @ldapData[kind] - @ldapData[kind][:internal] = @ldap.search( - :base => ldapBase(kind), - :filter => LDAP_FILTER[kind] - ) - end + @ldapData[kind][:internal] = @ldap.search( + :base => ldapBase(kind), + :filter => LDAP_FILTER[kind] + ) + end - def load(kind) - load!(kind) if ! @ldapData[kind] + 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] + @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] + 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][:external].each{|ydata| yield ydata} + @ldapData[kind][:external].each{|ydata| yield ydata} end def update(kind, data) map = {} map.merge!(LDAP_MAP[kind].invert) if LDAP_MAP[kind] - entry = Net::LDAP::Entry.new(data[:id]) - 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) @@ -105,17 +113,52 @@ class SystemDataBackendLdap entry[key] = value end - if not replace.empty? + 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 - else - puts 'INFO: no changes' 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) @@ -125,4 +168,17 @@ class SystemDataBackendLdap 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)}" + else + "not yet implemented" + end + end + end