|
#--
|
|
# LdapShadows, a Medium-level LDAP Access Library and Tool.
|
|
# Copyright (c) 2009 Marc Dequènes (Duck) <Duck@DuckCorp.org>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#++
|
|
|
|
|
|
$KCODE = 'UTF8'
|
|
require 'jcode'
|
|
require 'active_ldap'
|
|
require 'ldap_shadows/activeldap_fixes'
|
|
require 'ldap_shadows/object'
|
|
require 'ldap_shadows/aspect'
|
|
|
|
|
|
module LdapShadows
|
|
class Shadow
|
|
attr_reader :name
|
|
|
|
def initialize(name)
|
|
@name = name
|
|
|
|
LdapShadows::LdapObject.mapper = self
|
|
|
|
@config = Config.instance
|
|
|
|
@object_definitions = {}
|
|
@aspects = {}
|
|
# TODO: should replace @aspects properly one day
|
|
@aspects2 = {}
|
|
end
|
|
|
|
def set_shadow_config(shadow_def_raw)
|
|
@shadow_config = @config.parse_and_validate(@name, 'shadow', shadow_def_raw)
|
|
end
|
|
|
|
def get_shadow_config
|
|
# TODO: remove this workaround
|
|
@shadow_config[:presentation]
|
|
end
|
|
|
|
def add_aspect(aspect_name, aspect_def_raw)
|
|
aspect_def = @config.parse_and_validate(aspect_name, 'aspect', aspect_def_raw)
|
|
|
|
aspect_name = aspect_name.to_sym
|
|
@aspects[aspect_name] = aspect_def
|
|
|
|
klass_content = @config.load_hook_content(@name, 'aspect', aspect_name)
|
|
|
|
klass_name = "LdapAspect" + aspect_name.to_s.capitalize
|
|
begin
|
|
Aspects.module_eval(<<-EOS)
|
|
class #{klass_name} < LdapAspect
|
|
#{klass_content}
|
|
end
|
|
EOS
|
|
rescue
|
|
raise PreProcessingError, _("Could not load Aspect plugin '%s'") % aspect_name
|
|
end
|
|
|
|
@aspects2[aspect_name] = Aspects.const_get(klass_name)
|
|
end
|
|
|
|
def get_aspect(aspect_name)
|
|
@aspects[aspect_name.to_sym]
|
|
end
|
|
|
|
def get_aspect_klass(aspect_name)
|
|
@aspects2[aspect_name.to_sym]
|
|
end
|
|
|
|
def self.object_name_to_klass_name(object_name)
|
|
"LdapObject" + object_name.to_s.capitalize
|
|
end
|
|
|
|
def add_object(object_name, object_def_raw)
|
|
object_def = @config.parse_and_validate(object_name, 'object', object_def_raw)
|
|
|
|
object_name = object_name.to_sym
|
|
klass_name = self.class.object_name_to_klass_name(object_name)
|
|
klass_content = @config.load_hook_content(@name, 'object', object_name)
|
|
|
|
# create class
|
|
Objects.module_eval(<<-EOS)
|
|
class #{klass_name} < LdapObject
|
|
#{klass_content}
|
|
end
|
|
EOS
|
|
|
|
# configure class
|
|
klass = find_klass(object_name)
|
|
klass.handle = object_name
|
|
klass.presentation = object_def[:presentation]
|
|
klass.mapper = self
|
|
klass.ldap_mapping object_def[:mapping]
|
|
|
|
# store definition for later relations processing
|
|
@object_definitions[object_name] = object_def
|
|
end
|
|
|
|
def find_klass(object_name)
|
|
klass_name = self.class.object_name_to_klass_name(object_name)
|
|
return nil unless Objects.const_defined?(klass_name)
|
|
Objects.const_get(klass_name)
|
|
end
|
|
|
|
# run it _once_ when all objects are loaded
|
|
def load_relations
|
|
@object_definitions.each_pair do |object_name, object_def|
|
|
object_rel = {}
|
|
object_rel.merge!(object_def[:relations]) if object_def.include?(:relations)
|
|
if object_def[:presentation].has_key?(:allowed_aspects)
|
|
object_def[:presentation][:allowed_aspects].each do |aspect|
|
|
aspect_data = get_aspect(aspect)
|
|
if aspect_data.nil?
|
|
raise PreProcessingError, _("Aspect '%s' is missing") % aspect
|
|
end
|
|
object_rel.merge!(aspect_data[:relations]) if aspect_data.has_key?(:relations) and aspect_data[:relations]
|
|
end
|
|
end
|
|
next if object_rel.empty?
|
|
|
|
klass = find_klass(object_name)
|
|
|
|
object_relations_info = {}
|
|
object_rel.each_pair do |field_name, rel|
|
|
foreign_klass = find_klass(rel[:object])
|
|
if foreign_klass.nil?
|
|
raise PreProcessingError, _("Relation '%s' for object '%s' is impossible: foreign object '%s' is missing") % [field_name, object_name, rel[:object]]
|
|
end
|
|
rel[:class_name] = foreign_klass.to_s
|
|
|
|
case rel[:type]
|
|
when 'belongs_to'
|
|
klass.belongs_to field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS.include?(key) }
|
|
when 'has_many'
|
|
klass.has_many field_name, rel.reject {|key, val| not ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS.include?(key) }
|
|
else
|
|
raise "bug in '#{object_name}' object relations (wrong type)"
|
|
end
|
|
|
|
object_relations_info[field_name] = {
|
|
:foreign_klass => foreign_klass,
|
|
:single_value => ActiveLdap::Base.schema.attribute(rel[:foreign_key]).single_value?,
|
|
:read_only => rel[:read_only] || false
|
|
}
|
|
end
|
|
klass.relations_info = object_relations_info
|
|
end
|
|
end
|
|
|
|
def objects
|
|
@object_definitions.keys.collect{|key| key.to_s }.sort
|
|
end
|
|
end
|
|
|
|
# default location for mapped objects
|
|
module Objects
|
|
end
|
|
|
|
# default location for mapped aspects
|
|
module Aspects
|
|
end
|
|
end
|
|
|