You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.3 KiB
197 lines
5.3 KiB
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][:int] = @ldap.search(
|
|
:base => ldapBase(kind),
|
|
:filter => Net::LDAP::Filter::construct(LDAP_FILTER[kind])
|
|
)
|
|
end
|
|
|
|
def load(kind)
|
|
load!(kind) if ! @ldapData[kind]
|
|
|
|
@ldapData[kind][:ext] = @ldapData[kind][:int].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][:ext] && @ldapData[kind][:int]
|
|
|
|
@ldapData[kind][:ext].each{|ydata| yield ydata} if @ldapData[kind][:ext]
|
|
end
|
|
|
|
def update(kind, data)
|
|
map = {}
|
|
map.merge!(LDAP_MAP[kind].invert) if LDAP_MAP[kind]
|
|
|
|
odata = @ldapData[kind][:ext].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][:ext].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
|