diff --git a/SystemDataBackendLdap.rb b/SystemDataBackendLdap.rb index aab1c27..a660087 100644 --- a/SystemDataBackendLdap.rb +++ b/SystemDataBackendLdap.rb @@ -75,7 +75,7 @@ def load!(kind) @ldapData[kind] = Hash.new if ! @ldapData[kind] - @ldapData[kind][:internal] = @ldap.search( + @ldapData[kind][:int] = @ldap.search( :base => ldapBase(kind), :filter => Net::LDAP::Filter::construct(LDAP_FILTER[kind]) ) @@ -84,7 +84,7 @@ def load(kind) load!(kind) if ! @ldapData[kind] - @ldapData[kind][:external] = @ldapData[kind][:internal].map do |data| + @ldapData[kind][:ext] = @ldapData[kind][:int].map do |data| map = { :dn => :id } map.merge!(LDAP_MAP[kind]) if LDAP_MAP[kind] @@ -93,16 +93,16 @@ ydata.merge!({ map[key] || key => value.size==1?value[0]:value.to_a }) end ydata - end if ! @ldapData[kind][:external] && @ldapData[kind][:internal] + end if ! @ldapData[kind][:ext] && @ldapData[kind][:int] - @ldapData[kind][:external].each{|ydata| yield ydata} if @ldapData[kind][:external] + @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][:external].find{|edata| edata[:id] == data[:id]} + odata = @ldapData[kind][:ext].find{|edata| edata[:id] == data[:id]} data.each do |key,value| pat_key = map[key] ? map[key] : key @@ -136,7 +136,7 @@ def replace(kind, data) puts 'INFO: do replace' puts '----------------' - odata = @ldapData[kind][:external].find{|edata| edata[:id] == data[:id]} + odata = @ldapData[kind][:ext].find{|edata| edata[:id] == data[:id]} delete(odata) puts insert(kind, data) diff --git a/controllers/system_data.rb b/controllers/system_data.rb new file mode 100644 index 0000000..a53089f --- /dev/null +++ b/controllers/system_data.rb @@ -0,0 +1,91 @@ +require 'User' +require 'Group' +require 'Site' +require 'MailAliasRole' +require 'MailAliasPerson' +require 'MailAccount' + +class SystemDataBackend + + def initialize(backend) + @backend = backend + @data = Hash.new + end + + def users + load(:User) + end + + def groups + load(:Group) + end + + def sites + load(:Site) + end + + def mailAliasRoles + load(:MailAliasRole) + end + + def mailAliasPeople + load(:MailAliasPerson) + end + + def mailAccounts + load(:MailAccount) + end + + def userByName(name) + users.find{|user| user.name == name} + end + + def groupByName(name) + groups.find{|group| group.name == name} + end + + def siteByName(name) + sites.find{|site| site.name == name} + end + + def usersInGroup(group) + case + when group.members.instance_of?(Array): + Hash[group.members.map{|uid| [uid, userByName(uid)] if userByName(uid)}] + when goup.members.nil?: + {} + else + {group.members => userByName(group.members)} if userByName(group.members) + end + end + + def mailAliasRolesBySite(site) + mailAliasRoles.find_all{|mail| mail.site == site.name} + end + + def mailAliasPeopleBySite(site) + mailAliasPeople.find_all{|mail| mail.site == site.name} + end + + def mailAccountsBySite(site) + mailAccounts.find_all{|mail| mail.site == site.name} + end + + private + + def load(kind) + load!(kind) if ! @data[kind] + @data[kind] + end + + def load!(kind) + @data[kind] = Array.new + @backend.load(kind) do |data| + data[:backend] = @backend + @data[kind].push( + eval(kind.to_s + '.new(data)') + ) + end + end + +end diff --git a/database.rb b/database.rb new file mode 100644 index 0000000..1db079c --- /dev/null +++ b/database.rb @@ -0,0 +1,10 @@ +module SdAdmin::Database + class Ldap + require 'database/base.rb' + require 'net/ldap' + + include Enumerable + end + + require 'database/ldap' +end diff --git a/database/base.rb b/database/base.rb new file mode 100644 index 0000000..81cbc04 --- /dev/null +++ b/database/base.rb @@ -0,0 +1,13 @@ +class SdAdmin::Database::Base + + def initialize + @data = Hash.new + end + + def _create(data) + end + + def _read(id) + end + +end diff --git a/database/factory.rb b/database/factory.rb new file mode 100644 index 0000000..96ecacb --- /dev/null +++ b/database/factory.rb @@ -0,0 +1,3 @@ +class SdAdmin::Database::Factory + +end diff --git a/database/ldap.rb b/database/ldap.rb new file mode 100644 index 0000000..2bbde01 --- /dev/null +++ b/database/ldap.rb @@ -0,0 +1,53 @@ +class SdAdmin::Database::Ldap + attr_writer :base_dn, :mapper + + def initialize(args = {}) + @con = {:host => 'host.one.virtual', :port => 389} + + @con[:host] = args[:host] if args[:host] + @con[:port] = args[:port] if args[:port] + + @base_dn = args[:base_dn] if args[:base_dn] + + @ldap = Net::LDAP.new(@con) + filter = args[:filter] if args[:filter] + end + + def filter=(filter) + @filter = Net::LDAP::Filter::construct(filter) + end + + def each + _load.each{|key,entry| puts "DEBUG: #{key}"; yield key,entry} + end + + def [](id) + _load[id] + end + + def []=(id, data) + end + + def insert(data) + end + + def delete(id) + end + + private + + def _load! + @data = Hash.new + @ldap.search(:base => @base_dn, :filter => @filter) do |entry| + attributes = Hash.new + entry.each{|attr,value| attributes.merge!({attr => value})} + @data.merge!({attributes[:dn][0] => attributes}) + end + end + + def _load + _load! if ! @data + @data + end + +end diff --git a/loader/user.rb b/loader/user.rb new file mode 100644 index 0000000..e69de29 diff --git a/mappers/ldap.rb b/mappers/ldap.rb new file mode 100644 index 0000000..a660087 --- /dev/null +++ b/mappers/ldap.rb @@ -0,0 +1,197 @@ + 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 diff --git a/model.rb b/model.rb new file mode 100644 index 0000000..37a1e26 --- /dev/null +++ b/model.rb @@ -0,0 +1,16 @@ +module SdAdmin::Model + module Interface + end + + class Base + require 'model/interface' + require 'database' + include SdAdmin::Model::Interface + end + + class User < Base + end + + require 'model/base' + require 'model/user' +end diff --git a/model/base.rb b/model/base.rb new file mode 100644 index 0000000..2569a41 --- /dev/null +++ b/model/base.rb @@ -0,0 +1,9 @@ +class SdAdmin::Model::Base + attr_reader :backend + + def initialize(backend) + @backend = SdAdmin::Database::Ldap.new + @backend = backend if backend + end + +end diff --git a/model/group.rb b/model/group.rb new file mode 100644 index 0000000..e607420 --- /dev/null +++ b/model/group.rb @@ -0,0 +1,14 @@ +require 'SystemData' + +class Group < SystemData + attr_accessor :name, :gid, :members + + def initialize(args = {}) + super(args) + + @name = args[:name] + @gid = args[:gid] + @members = args[:members] + @members = Array.new if @members.nil? + end +end diff --git a/model/interface.rb b/model/interface.rb new file mode 100644 index 0000000..25de225 --- /dev/null +++ b/model/interface.rb @@ -0,0 +1,7 @@ +module SdAdmin::Model::Interface + + def all + Hash[self.backend.map{|key,entry| [key, entry]}] + end + +end diff --git a/model/mail_account.rb b/model/mail_account.rb new file mode 100644 index 0000000..a6ec9fd --- /dev/null +++ b/model/mail_account.rb @@ -0,0 +1,17 @@ +require 'SystemData' + +class MailAccount < SystemData + attr_accessor :mail, :mailbox + + def initialize(args = {}) + super(args) + + @mail = args[:mail] + @home = args[:home] + @mailbox = args[:mailbox] + end + + def site + @mail.sub(/.*@/, '') + end +end diff --git a/model/mail_alias_person.rb b/model/mail_alias_person.rb new file mode 100644 index 0000000..e4308ce --- /dev/null +++ b/model/mail_alias_person.rb @@ -0,0 +1,18 @@ +require 'SystemData' + +class MailAliasPerson < SystemData + attr_accessor :mail, :maildrop, :surname, :name + + def initialize(args = {}) + super(args) + + @mail = args[:mail] + @maildrop = args[:maildrop] + @surname = args[:surname] + @name = args[:name] + end + + def site + @mail.sub(/.*@/, '') + end +end diff --git a/model/mail_alias_role.rb b/model/mail_alias_role.rb new file mode 100644 index 0000000..c16b474 --- /dev/null +++ b/model/mail_alias_role.rb @@ -0,0 +1,17 @@ +require 'SystemData' + +class MailAliasRole < SystemData + attr_accessor :mail, :maildrop, :user + + def initialize(args = {}) + super(args) + + @mail = args[:mail] + @maildrop = args[:maildrop] + @user = args[:user] + end + + def site + @mail.sub(/.*@/, '') + end +end diff --git a/model/site.rb b/model/site.rb new file mode 100644 index 0000000..133ccbe --- /dev/null +++ b/model/site.rb @@ -0,0 +1,11 @@ +require 'SystemData' + +class Site < SystemData + attr_accessor :name + + def initialize(args = {}) + super(args) + + @name = args[:name] + end +end diff --git a/model/system_data.rb b/model/system_data.rb new file mode 100644 index 0000000..ec1652b --- /dev/null +++ b/model/system_data.rb @@ -0,0 +1,24 @@ +class SystemData + attr_reader :id + + def initialize(args = {}) + @backend = args[:backend] + @id = args[:id] + end + + def save + kind = self.class.to_s.to_sym + + if @id + @backend.update(kind, to_h) + else + @backend.insert(kind, to_h) + end + end + + def to_h + Hash[instance_variables.map do |var| + [var[1...var.size].to_sym, eval(var)] if var != '@backend' + end] + end +end diff --git a/model/user.rb b/model/user.rb new file mode 100644 index 0000000..6b9f126 --- /dev/null +++ b/model/user.rb @@ -0,0 +1,19 @@ +class SdAdmin::Model::User + attr_accessor :name, :pass, :uid, :gid, :shell, :home + + def initialize(args={}, backend=nil) + super(backend) + + @backend.base_dn = 'ou=user,o=system,dc=weird-web-workers,dc=org' + @backend.filter = '(objectClass=posixAccount)' + + @name = args[:name] + @pass = args[:pass] + @uid = args[:uid] + @gid = args[:gid] + @shell = args[:shell] + @home = args[:home] + @shadowmax = args[:shadowmax] + @shadowwarning = args[:shadowwarning] + end +end diff --git a/sd_admin.rb b/sd_admin.rb new file mode 100644 index 0000000..30de395 --- /dev/null +++ b/sd_admin.rb @@ -0,0 +1,10 @@ +module SdAdmin + module Model + end + + module Database + end + + require 'model' + require 'database' +end diff --git a/test_dummy2.rb b/test_dummy2.rb new file mode 100644 index 0000000..6fa5b7a --- /dev/null +++ b/test_dummy2.rb @@ -0,0 +1,5 @@ +require 'sd_admin' + +user = SdAdmin::Model::User.new + +puts user.all.inspect