Revision 1f9bfec2
Added by Marc Dequènes about 15 years ago
- ID 1f9bfec2d26868fa820cc1637af26718a0381c8f
.gitignore | ||
---|---|---|
\#*#
|
||
# generated by installer
|
||
.config
|
||
lib/ldap_shadows/config.rb
|
||
lib/ldap_shadows/config_setup.rb
|
||
data/locale
|
||
#
|
||
conf/ldap_shadows/shadows/MilkyPond/shadow_private.conf
|
||
conf/ldap_shadows/global_private.conf
|
||
var
|
bin/shadowwalker | ||
---|---|---|
|
||
require 'ldap_shadows'
|
||
require 'ldap_shadows/display_utils'
|
||
require 'yaml'
|
||
require 'cmdparse2'
|
||
|
||
include LdapShadows
|
||
... | ... | |
end
|
||
|
||
def execute(args)
|
||
$ldapctl = Controller.new
|
||
config = LdapShadows::Config.instance
|
||
begin
|
||
$ldapctl.load_shadow($program_options[:shadow_name])
|
||
$ldapctl = config.load_shadow($program_options[:shadow_name])
|
||
rescue ActiveLdap::Error => e
|
||
STDERR.puts _("LDAP connection error: %s") % e.to_s
|
||
exit 2
|
lib/ldap_shadows.rb | ||
---|---|---|
require 'ldap_shadows/config'
|
||
require 'ldap_shadows/lang_additions'
|
||
require 'ldap_shadows/exceptions'
|
||
require 'ldap_shadows/controller'
|
||
require 'gettext'
|
||
require 'pp'
|
||
|
lib/ldap_shadows/aspect.rb | ||
---|---|---|
#--
|
||
# 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/>.
|
||
#++
|
||
|
||
|
||
module LdapShadows
|
||
class LdapAspect
|
||
def self.hook_before_create
|
||
end
|
||
|
||
def self.hook_before_modify
|
||
end
|
||
|
||
protected
|
||
|
||
def self.raise_error(msg)
|
||
raise ProcessingError, msg
|
||
end
|
||
end
|
||
end
|
lib/ldap_shadows/config.rb | ||
---|---|---|
#--
|
||
# 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/>.
|
||
#++
|
||
|
||
require 'ldap_shadows/config_setup'
|
||
require 'singleton'
|
||
require 'kwalify'
|
||
require 'ldap_shadows/shadow'
|
||
|
||
module LdapShadows
|
||
class Config
|
||
include Singleton
|
||
|
||
def initialize
|
||
@global_config = nil
|
||
@schemas = {}
|
||
@shadows = {}
|
||
end
|
||
|
||
def load_global_config
|
||
g_config_file = File.join(CFG_DIR, "global.conf")
|
||
unless File.exists? g_config_file
|
||
raise PreProcessingError, _("Global LdapShadows config file is missing")
|
||
end
|
||
|
||
g_default_config = {}
|
||
g_config = g_default_config.merge(YAML.load_file(g_config_file) || {})
|
||
g_config_str_prv_filelist = [
|
||
File.join(ENV['HOME'], ".shadowwalker"),
|
||
File.join(CFG_DIR, "global_private.conf")
|
||
]
|
||
g_config_str_prv_filelist.each do |file|
|
||
if File.exists?(file)
|
||
g_config.merge!(YAML.load_file(file))
|
||
break
|
||
end
|
||
end
|
||
g_config.recursive_symbolize_keys!
|
||
|
||
ActiveLdap::Base.setup_connection(g_config[:ldap])
|
||
|
||
@global_config = g_config
|
||
end
|
||
|
||
def global_config
|
||
load_global_config() if @global_config.nil?
|
||
@global_config
|
||
end
|
||
|
||
def load_shadow(shadow_name = nil)
|
||
shadow_name = self.global_config[:default_shadow] if shadow_name.nil?
|
||
if shadow_name.nil?
|
||
raise PreProcessingError, _("Could not determine which Shadow to travel through")
|
||
end
|
||
|
||
shadow_config_path = File.join(CFG_DIR, "shadows", shadow_name)
|
||
unless File.exists? shadow_config_path
|
||
raise PreProcessingError, _("Configuration directory for Shadow '%s' is missing") % shadow_name
|
||
end
|
||
|
||
config_file = File.join(shadow_config_path, "shadow.conf")
|
||
unless File.exists? config_file
|
||
raise PreProcessingError, _("General configuration file for Shadow '%s' is missing") % shadow_name
|
||
end
|
||
config = YAML.load_file(config_file)
|
||
config.recursive_symbolize_keys!
|
||
|
||
shadow = Shadow.new(shadow_name)
|
||
shadow.set_shadow_config(config[:presentation])
|
||
|
||
load_config_components(shadow_config_path, 'aspects') do |c_name, c_config|
|
||
shadow.add_aspect(c_name, c_config)
|
||
end
|
||
|
||
load_config_components(shadow_config_path, 'objects') do |c_name, c_config|
|
||
shadow.load_object(c_name, c_config)
|
||
end
|
||
|
||
shadow.load_relations
|
||
|
||
translation_path = File.join(shadow_config_path, "translations")
|
||
if File.exists? translation_path
|
||
# load interface translation
|
||
I18n.load_path += Dir[File.join(translation_path, "**", "*.yml")]
|
||
end
|
||
|
||
@shadows[shadow_name] = {
|
||
:config_path => shadow_config_path,
|
||
:shadow => shadow
|
||
}
|
||
|
||
shadow
|
||
rescue
|
||
raise PreProcessingError, _("Could not load shadow configuration: %s") % $!
|
||
end
|
||
|
||
def get_shadow_info(shadow_name)
|
||
@shadows[shadow_name]
|
||
end
|
||
|
||
def parse_and_validate(def_name, type, def_data_raw)
|
||
schema = load_schema(type)
|
||
|
||
validator_klass = case type
|
||
when 'object'
|
||
ObjectValidator
|
||
when 'aspect'
|
||
AspectValidator
|
||
else
|
||
Kwalify::Validator
|
||
end
|
||
validator = validator_klass.new(schema)
|
||
|
||
# validate config with schema
|
||
parser = Kwalify::Yaml::Parser.new(validator)
|
||
def_data = parser.parse(def_data_raw, def_name)
|
||
raise_if_validation_errors("#{type.capitalize} '#{def_name}'", parser.errors)
|
||
|
||
def_data.recursive_symbolize_keys!
|
||
end
|
||
|
||
protected
|
||
|
||
def load_config_components(shadow_config_path, type)
|
||
c_config_dir = File.join(shadow_config_path, type)
|
||
c_config_pattern = File.join(c_config_dir, "**", "*.conf")
|
||
|
||
Dir.glob(c_config_pattern).each do |f|
|
||
next if f[0..0] == "."
|
||
|
||
c_name = File.basename(f).sub(".conf", "")
|
||
c_config = File.read(f)
|
||
|
||
yield(c_name, c_config)
|
||
end
|
||
end
|
||
|
||
def load_schema(type)
|
||
schema = @schemas[type]
|
||
if schema.nil?
|
||
schema_file = File.join(DATA_DIR, "schema", type + ".yaml")
|
||
schema = YAML.load_file(schema_file)
|
||
|
||
# validate schema
|
||
metavalidator = Kwalify::MetaValidator.instance
|
||
errors = metavalidator.validate(schema)
|
||
raise_if_validation_errors("'#{type}' schema", errors)
|
||
|
||
@schemas[type] = schema
|
||
end
|
||
schema
|
||
rescue
|
||
raise PreProcessingError, _("Could not load schema: %s") % $!
|
||
end
|
||
|
||
def raise_if_validation_errors(name, errors)
|
||
if errors and not errors.empty?
|
||
err_msg = []
|
||
for e in errors
|
||
err_msg << "[#{e.path}] #{e.message}"
|
||
end
|
||
raise PreProcessingError, _("%s is not valid:\n%s") % [name, err_msg.join("\n")]
|
||
end
|
||
end
|
||
|
||
class LdapShadowsValidator < Kwalify::Validator
|
||
## hook method called by Validator#validate()
|
||
def validate_hook(value, rule, path, errors)
|
||
msg_list = []
|
||
|
||
validate_hook_in(value, rule, path, msg_list)
|
||
|
||
msg_list.each do |msg|
|
||
errors << Kwalify::ValidationError.new(msg, path)
|
||
end
|
||
end
|
||
|
||
def validate_hook_relation(value, rule, path, msg_list)
|
||
extra_params = [:type, :object]
|
||
|
||
if value['type'] == 'belongs_to'
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS - extra_params
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
else
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS - extra_params
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
class AspectValidator < LdapShadowsValidator
|
||
def validate_hook_in(value, rule, path, msg_list)
|
||
case rule.name
|
||
when 'RelationMapping'
|
||
validate_hook_relation(value, rule, path, msg_list)
|
||
end
|
||
end
|
||
end
|
||
|
||
class ObjectValidator < LdapShadowsValidator
|
||
def validate_hook_in(value, rule, path, msg_list)
|
||
case rule.name
|
||
when 'Mapping'
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Base::VALID_LDAP_MAPPING_OPTIONS
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
|
||
when 'RelationMapping'
|
||
validate_hook_relation(value, rule, path, msg_list)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
lib/ldap_shadows/controller.rb | ||
---|---|---|
#--
|
||
# 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 'kwalify'
|
||
require 'ldap_shadows/activeldap_fixes'
|
||
require 'ldap_shadows/object'
|
||
|
||
|
||
module LdapShadows
|
||
class Controller
|
||
attr_reader :shadow_name, :shadow_config_path
|
||
|
||
def initialize(mod_container = LdapShadows::Objects)
|
||
@mod_container = mod_container
|
||
|
||
@schema = {}
|
||
|
||
clear_shadow
|
||
LdapShadows::LdapObject.mapper = self
|
||
end
|
||
|
||
def clear_shadow
|
||
@shadow_name = nil
|
||
@shadow_config_path = nil
|
||
@global_config = nil
|
||
@shadow_config = nil
|
||
@object_definitions = {}
|
||
@aspects = {}
|
||
# TODO: should replace @aspects properly one day
|
||
@aspects2 = {}
|
||
end
|
||
|
||
def set_shadow_config(shadow_config)
|
||
@shadow_config = shadow_config
|
||
end
|
||
|
||
def get_shadow_config
|
||
@shadow_config
|
||
end
|
||
|
||
class LdapShadowsValidator < Kwalify::Validator
|
||
## hook method called by Validator#validate()
|
||
def validate_hook(value, rule, path, errors)
|
||
msg_list = []
|
||
|
||
validate_hook_in(value, rule, path, msg_list)
|
||
|
||
msg_list.each do |msg|
|
||
errors << Kwalify::ValidationError.new(msg, path)
|
||
end
|
||
end
|
||
|
||
def validate_hook_relation(value, rule, path, msg_list)
|
||
extra_params = [:type, :object]
|
||
|
||
if value['type'] == 'belongs_to'
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS - extra_params
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
else
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS - extra_params
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
class AspectValidator < LdapShadowsValidator
|
||
def validate_hook_in(value, rule, path, msg_list)
|
||
case rule.name
|
||
when 'RelationMapping'
|
||
validate_hook_relation(value, rule, path, msg_list)
|
||
end
|
||
end
|
||
end
|
||
|
||
def set_aspect(aspect_name, aspect_def)
|
||
schema_name = 'aspect'
|
||
schema = load_schema(schema_name)
|
||
|
||
# validate config with schema
|
||
validator = AspectValidator.new(schema)
|
||
parser = Kwalify::Yaml::Parser.new(validator)
|
||
aspect_def = parser.parse(aspect_def, aspect_name)
|
||
raise_if_validation_errors("#{schema_name.capitalize} '#{aspect_name}'", parser.errors)
|
||
|
||
aspect_name = aspect_name.to_sym
|
||
aspect_def.recursive_symbolize_keys!
|
||
@aspects[aspect_name] = aspect_def
|
||
|
||
return if @shadow_config_path.nil?
|
||
filename = File.join(@shadow_config_path, "hooks", "aspects", aspect_name.to_s.downcase + ".rb")
|
||
if File.exists?(filename)
|
||
klass_name = "LdapAspect" + aspect_name.to_s.capitalize
|
||
klass_content = IO.read(filename)
|
||
begin
|
||
Aspects.module_eval(<<-EOS)
|
||
class #{klass_name} < Aspect
|
||
#{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
|
||
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(obj_name)
|
||
"LdapObject" + obj_name.to_s.capitalize
|
||
end
|
||
|
||
class ObjectValidator < LdapShadowsValidator
|
||
def validate_hook_in(value, rule, path, msg_list)
|
||
case rule.name
|
||
when 'Mapping'
|
||
ne_params = value.keys.collect{|k| k.to_sym } - ActiveLdap::Base::VALID_LDAP_MAPPING_OPTIONS
|
||
unless ne_params.empty?
|
||
msg_list << _("nonexisting mapping parameters (%s)") % ne_params.join(", ")
|
||
end
|
||
|
||
when 'RelationMapping'
|
||
validate_hook_relation(value, rule, path, msg_list)
|
||
end
|
||
end
|
||
end
|
||
|
||
def load_object(obj_name, obj_def)
|
||
schema_name = 'object'
|
||
schema = load_schema(schema_name)
|
||
|
||
# validate config with schema
|
||
validator = ObjectValidator.new(schema)
|
||
parser = Kwalify::Yaml::Parser.new(validator)
|
||
obj_def = parser.parse(obj_def, obj_name)
|
||
raise_if_validation_errors("#{schema_name.capitalize} '#{obj_name}'", parser.errors)
|
||
|
||
obj_name = obj_name.to_sym
|
||
obj_def.recursive_symbolize_keys!
|
||
|
||
klass_name = self.class.object_name_to_klass_name(obj_name)
|
||
|
||
# create class
|
||
@mod_container.module_eval(<<-EOS)
|
||
class #{klass_name} < LdapShadows::LdapObject; end
|
||
EOS
|
||
|
||
# configure class
|
||
klass = find_klass(obj_name)
|
||
klass.handle = obj_name
|
||
klass.presentation = obj_def[:presentation]
|
||
klass.mapper = self
|
||
klass.ldap_mapping obj_def[:mapping]
|
||
|
||
# store definition for later relations processing
|
||
@object_definitions[obj_name] = obj_def
|
||
end
|
||
|
||
def find_klass(obj_name)
|
||
klass_name = self.class.object_name_to_klass_name(obj_name)
|
||
return nil unless @mod_container.const_defined?(klass_name)
|
||
@mod_container.const_get(klass_name)
|
||
end
|
||
|
||
# run it _once_ when all objects are loaded
|
||
def load_relations
|
||
@object_definitions.each_pair do |obj_name, obj_def|
|
||
obj_rel = {}
|
||
obj_rel.merge!(obj_def[:relations]) if obj_def.include?(:relations)
|
||
if obj_def[:presentation].has_key?(:allowed_aspects)
|
||
obj_def[:presentation][:allowed_aspects].each do |aspect|
|
||
aspect_data = get_aspect(aspect)
|
||
if aspect_data.nil?
|
||
raise PreProcessingError, _("Aspect '%s' is missing") % aspect
|
||
end
|
||
obj_rel.merge!(aspect_data[:relations]) if aspect_data.has_key?(:relations) and aspect_data[:relations]
|
||
end
|
||
end
|
||
next if obj_rel.empty?
|
||
|
||
klass = find_klass(obj_name)
|
||
|
||
obj_relations_info = {}
|
||
obj_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, obj_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 '#{obj_name}' object relations (wrong type)"
|
||
end
|
||
|
||
obj_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 = obj_relations_info
|
||
end
|
||
end
|
||
|
||
def objects
|
||
@object_definitions.keys.collect{|key| key.to_s }.sort
|
||
end
|
||
|
||
def load_shadow(shadow_name = nil)
|
||
clear_shadow
|
||
|
||
g_config_file = File.join(LdapShadows::Config::CFG_DIR, "global.conf")
|
||
unless File.exists? g_config_file
|
||
raise PreProcessingError, _("Global LdapShadows config file is missing")
|
||
end
|
||
|
||
g_default_config = {}
|
||
g_config = g_default_config.merge(YAML.load(IO.read(g_config_file)) || {})
|
||
g_config.recursive_symbolize_keys!
|
||
@global_config = g_config
|
||
|
||
@shadow_name = shadow_name || @global_config[:default_shadow]
|
||
if @shadow_name.nil?
|
||
raise PreProcessingError, _("Could not determine which Shadow to travel through")
|
||
end
|
||
|
||
@shadow_config_path = File.join(LdapShadows::Config::CFG_DIR, "shadows", @shadow_name)
|
||
unless File.exists? @shadow_config_path
|
||
raise PreProcessingError, _("Configuration directory for Shadow '%s' is missing") % @shadow_name
|
||
end
|
||
|
||
config_file = File.join(@shadow_config_path, "shadow.conf")
|
||
unless File.exists? config_file
|
||
raise PreProcessingError, _("General configuration file for Shadow '%s' is missing") % @shadow_name
|
||
end
|
||
config = YAML.load(IO.read(config_file))
|
||
config_str_prv_filelist = [
|
||
File.join(ENV['HOME'], ".shadowwalker"),
|
||
File.join(@shadow_config_path, "shadow_private.conf")
|
||
]
|
||
config_str_prv_filelist.each do |file|
|
||
if File.exists?(file)
|
||
config_str_prv = IO.read(file)
|
||
config.merge!(YAML.load(config_str_prv))
|
||
break
|
||
end
|
||
end
|
||
config.recursive_symbolize_keys!
|
||
|
||
$ldapctl.set_shadow_config(config[:presentation])
|
||
|
||
load_config_components("aspects") do |c_name, c_config|
|
||
$ldapctl.set_aspect(c_name, c_config)
|
||
end
|
||
|
||
ActiveLdap::Base.setup_connection(config[:ldap])
|
||
|
||
load_config_components("objects") do |c_name, c_config|
|
||
$ldapctl.load_object(c_name, c_config)
|
||
end
|
||
|
||
$ldapctl.load_relations
|
||
|
||
translation_path = File.join($ldapctl.shadow_config_path, "translations")
|
||
if File.exists? translation_path
|
||
# load interface translation
|
||
I18n.load_path += Dir[File.join(translation_path, "**", "*.yml")]
|
||
end
|
||
rescue
|
||
raise PreProcessingError, _("Could not load shadow configuration: %s") % $!
|
||
end
|
||
|
||
protected
|
||
|
||
def load_config_components(type)
|
||
c_config_dir = File.join(@shadow_config_path, type)
|
||
c_config_pattern = File.join(c_config_dir, "**", "*.conf")
|
||
|
||
Dir.glob(c_config_pattern).each do |f|
|
||
next if f[0..0] == "."
|
||
|
||
c_name = File.basename(f).sub(".conf", "")
|
||
c_config = File.read(f)
|
||
|
||
yield(c_name, c_config)
|
||
end
|
||
end
|
||
|
||
def load_schema(type)
|
||
schema = @schema[type]
|
||
if schema.nil?
|
||
schema_file = File.join(Config::DATA_DIR, "schema", type + ".yaml")
|
||
schema = YAML.load_file(schema_file)
|
||
|
||
# validate schema
|
||
metavalidator = Kwalify::MetaValidator.instance
|
||
errors = metavalidator.validate(schema)
|
||
raise_if_validation_errors("'#{type}' schema", errors)
|
||
|
||
@schema[type] = schema
|
||
end
|
||
schema
|
||
rescue
|
||
raise PreProcessingError, _("Could not load schema: %s") % $!
|
||
end
|
||
|
||
def raise_if_validation_errors(name, errors)
|
||
if errors and not errors.empty?
|
||
err_msg = []
|
||
for e in errors
|
||
err_msg << "[#{e.path}] #{e.message}"
|
||
end
|
||
raise PreProcessingError, _("%s is not valid:\n%s") % [name, err_msg.join("\n")]
|
||
end
|
||
end
|
||
end
|
||
|
||
# default location for mapped objects
|
||
module Objects
|
||
end
|
||
|
||
class Aspect
|
||
def self.hook_before_create
|
||
end
|
||
|
||
def self.hook_before_modify
|
||
end
|
||
|
||
protected
|
||
|
||
def self.raise_error(msg)
|
||
raise ProcessingError, msg
|
||
end
|
||
end
|
||
|
||
module Aspects
|
||
end
|
||
end
|
||
|
lib/ldap_shadows/shadow.rb | ||
---|---|---|
#--
|
||
# 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_config)
|
||
@shadow_config = shadow_config
|
||
end
|
||
|
||
def get_shadow_config
|
||
@shadow_config
|
||
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
|
||
|
||
return if @shadow_config_path.nil?
|
||
filename = File.join(@shadow_config_path, "hooks", "aspects", aspect_name.to_s.downcase + ".rb")
|
||
if File.exists?(filename)
|
||
klass_name = "LdapAspect" + aspect_name.to_s.capitalize
|
||
klass_content = File.read(filename)
|
||
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
|
||
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 load_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)
|
||
|
||
# create class
|
||
Objects.module_eval(<<-EOS)
|
||
class #{klass_name} < LdapObject; 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
|
||
|
post-clean.rb | ||
---|---|---|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
#++
|
||
|
||
conffile = "lib/cyborghood/config.rb"
|
||
conffile = "lib/cyborghood/config_setup.rb"
|
||
File.unlink(conffile) if FileTest.exists?(conffile)
|
post-config.rb | ||
---|---|---|
|
||
# General header.
|
||
header = <<HEADER
|
||
# config.rb - Coin-diff setup configuration
|
||
# config_setup.rb - Coin-diff setup configuration
|
||
|
||
#--
|
||
# LdapShadows, a Medium-level LDAP Access Library and Tool.
|
||
... | ... | |
|
||
# Generate config.rb containing general compile/setup time configuration
|
||
# information (in the CoinDiff::Config module).
|
||
File.open('lib/ldap_shadows/config.rb', 'w') do |file|
|
||
File.open('lib/ldap_shadows/config_setup.rb', 'w') do |file|
|
||
|
||
file.print header
|
||
file.print <<-CONFIG
|
Also available in: Unified diff
[evol] improve config #13 (reorganized config/schema loading/validating, transform Controler into Shadow, and began reworking things to get a real Shadow view)