root/lib/ldap_shadows/config.rb @ c3a5f36f
1f9bfec2 | Marc Dequènes (Duck) | #--
|
|
# 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'
|
|||
7f913647 | Marc Dequènes (Duck) | require 'yaml'
|
|
1f9bfec2 | Marc Dequènes (Duck) | require 'kwalify'
|
|
9a7f1b63 | Marc Dequènes (Duck) | require 'facets/hash/recursive_merge'
|
|
1f9bfec2 | Marc Dequènes (Duck) | ||
module LdapShadows
|
|||
class Config
|
|||
include Singleton
|
|||
def initialize
|
|||
@global_config = nil
|
|||
@schemas = {}
|
|||
9a7f1b63 | Marc Dequènes (Duck) | @default_configs = {}
|
|
1f9bfec2 | Marc Dequènes (Duck) | @shadows = {}
|
|
end
|
|||
7f913647 | Marc Dequènes (Duck) | def load_global_config(extra_private_config_list = [])
|
|
9a7f1b63 | Marc Dequènes (Duck) | g_default_config = load_default_config('global')
|
|
1f9bfec2 | Marc Dequènes (Duck) | g_config_file = File.join(CFG_DIR, "global.conf")
|
|
unless File.exists? g_config_file
|
|||
raise PreProcessingError, _("Global LdapShadows config file is missing")
|
|||
end
|
|||
9a7f1b63 | Marc Dequènes (Duck) | g_def_partial = YAML.load_file(g_config_file) || {}
|
|
7f913647 | Marc Dequènes (Duck) | extra_private_config_list += [
|
|
1f9bfec2 | Marc Dequènes (Duck) | File.join(CFG_DIR, "global_private.conf")
|
|
]
|
|||
7f913647 | Marc Dequènes (Duck) | extra_private_config_list.each do |file|
|
|
1f9bfec2 | Marc Dequènes (Duck) | if File.exists?(file)
|
|
9a7f1b63 | Marc Dequènes (Duck) | g_def_partial.merge!(YAML.load_file(file) || {})
|
|
1f9bfec2 | Marc Dequènes (Duck) | break
|
|
end
|
|||
end
|
|||
9a7f1b63 | Marc Dequènes (Duck) | g_def = parse('config', 'global', YAML.dump(g_def_partial))
|
|
g_config = g_default_config.recursive_merge(g_def)
|
|||
1f9bfec2 | Marc Dequènes (Duck) | ||
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
|
|||
b527ce77 | Marc Dequènes (Duck) | # needs 'ldap_shadows/shadow' for this feature to work
|
|
1f9bfec2 | Marc Dequènes (Duck) | 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
|
|||
7f913647 | Marc Dequènes (Duck) | config = File.read(config_file)
|
|
1f9bfec2 | Marc Dequènes (Duck) | ||
b527ce77 | Marc Dequènes (Duck) | shadow = Shadow.new(shadow_name, self)
|
|
953a2b4d | Marc Dequènes (Duck) | # register early (needed to load plugins)
|
|
a65fd5e8 | Marc Dequènes (Duck) | @shadows[shadow_name] = {
|
|
:config_path => shadow_config_path,
|
|||
:shadow => shadow
|
|||
}
|
|||
bbe89518 | Marc Dequènes (Duck) | shadow.set_config(config)
|
|
1f9bfec2 | Marc Dequènes (Duck) | ||
9a7f1b63 | Marc Dequènes (Duck) | load_element_config(shadow_config_path, 'aspects') do |c_name, c_config|
|
|
1f9bfec2 | Marc Dequènes (Duck) | shadow.add_aspect(c_name, c_config)
|
|
end
|
|||
9a7f1b63 | Marc Dequènes (Duck) | load_element_config(shadow_config_path, 'objects') do |c_name, c_config|
|
|
4b046f91 | Marc Dequènes (Duck) | shadow.add_object(c_name, c_config)
|
|
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
90809ae4 | Marc Dequènes (Duck) | shadow.cast
|
|
1f9bfec2 | Marc Dequènes (Duck) | ||
90809ae4 | Marc Dequènes (Duck) | # TODO: find a way to handle translations per-shadow
|
|
1f9bfec2 | Marc Dequènes (Duck) | 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
|
|||
shadow
|
|||
rescue
|
|||
raise PreProcessingError, _("Could not load shadow configuration: %s") % $!
|
|||
end
|
|||
def get_shadow_info(shadow_name)
|
|||
@shadows[shadow_name]
|
|||
end
|
|||
428fdd46 | Marc Dequènes (Duck) | def parse(def_name, type, def_data_raw, validate = true, extra_data = nil)
|
|
1f9bfec2 | Marc Dequènes (Duck) | schema = load_schema(type)
|
|
9a7f1b63 | Marc Dequènes (Duck) | if validate
|
|
validator_klass = case type
|
|||
when 'global'
|
|||
GlobalValidator
|
|||
when 'object'
|
|||
ObjectValidator
|
|||
when 'aspect'
|
|||
AspectValidator
|
|||
else
|
|||
428fdd46 | Marc Dequènes (Duck) | LdapShadowsValidator
|
|
9a7f1b63 | Marc Dequènes (Duck) | end
|
|
validator = validator_klass.new(schema)
|
|||
428fdd46 | Marc Dequènes (Duck) | validator.extra_data = extra_data
|
|
1f9bfec2 | Marc Dequènes (Duck) | else
|
|
9a7f1b63 | Marc Dequènes (Duck) | validator = nil
|
|
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
# 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
|
|||
9a7f1b63 | Marc Dequènes (Duck) | def load_default_config(type)
|
|
default_config = @default_configs[type]
|
|||
if default_config.nil?
|
|||
filename = File.join(DATA_DIR, "default_config", type + ".yaml")
|
|||
default_config_raw = File.read(filename)
|
|||
default_config = parse(':defaults:', type, default_config_raw, false)
|
|||
@default_configs[type] = default_config
|
|||
end
|
|||
default_config
|
|||
rescue
|
|||
raise PreProcessingError, _("Could not load default config for '%s': %s") % [type, $!]
|
|||
end
|
|||
a65fd5e8 | Marc Dequènes (Duck) | def load_hook_content(shadow_name, type, hook_name)
|
|
s_info = get_shadow_info(shadow_name)
|
|||
return nil if s_info.nil?
|
|||
953a2b4d | Marc Dequènes (Duck) | filename = File.join(s_info[:config_path], "plugins", type.pluralize, hook_name.to_s.downcase + ".rb")
|
|
a65fd5e8 | Marc Dequènes (Duck) | return nil unless File.exists?(filename)
|
|
File.read(filename)
|
|||
end
|
|||
9a7f1b63 | Marc Dequènes (Duck) | def load_element_config(shadow_config_path, type)
|
|
1f9bfec2 | Marc Dequènes (Duck) | 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
|
|||
9a7f1b63 | Marc Dequènes (Duck) | raise PreProcessingError, _("Could not load schema for '%s': %s") % [type, $!]
|
|
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
9a7f1b63 | Marc Dequènes (Duck) | protected
|
|
1f9bfec2 | Marc Dequènes (Duck) | 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
|
|||
428fdd46 | Marc Dequènes (Duck) | attr_accessor :extra_data
|
|
1f9bfec2 | Marc Dequènes (Duck) | ## 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
|
|||
428fdd46 | Marc Dequènes (Duck) | def validate_hook_in(value, rule, path, msg_list)
|
|
end
|
|||
1f9bfec2 | Marc Dequènes (Duck) | def validate_hook_relation(value, rule, path, msg_list)
|
|
extra_params = [:type, :object]
|
|||
if value['type'] == 'belongs_to'
|
|||
7f913647 | Marc Dequènes (Duck) | ne_params = value.keys.collect{|k| k.to_sym } -
|
|
ActiveLdap::Associations::ClassMethods::VALID_BELONGS_TO_OPTIONS - extra_params
|
|||
1f9bfec2 | Marc Dequènes (Duck) | unless ne_params.empty?
|
|
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
|||
end
|
|||
else
|
|||
7f913647 | Marc Dequènes (Duck) | ne_params = value.keys.collect{|k| k.to_sym } -
|
|
ActiveLdap::Associations::ClassMethods::VALID_HAS_MANY_OPTIONS - extra_params
|
|||
1f9bfec2 | Marc Dequènes (Duck) | unless ne_params.empty?
|
|
msg_list << _("nonexisting relation mapping parameters (%s)") % ne_params.join(", ")
|
|||
end
|
|||
end
|
|||
end
|
|||
end
|
|||
7f913647 | Marc Dequènes (Duck) | class GlobalValidator < LdapShadowsValidator
|
|
1f9bfec2 | Marc Dequènes (Duck) | def validate_hook_in(value, rule, path, msg_list)
|
|
case rule.name
|
|||
7f913647 | Marc Dequènes (Duck) | when 'LdapMapping'
|
|
ne_params = value.keys.collect{|k| k.to_sym } -
|
|||
ActiveLdap::Configuration::ClassMethods::CONNECTION_CONFIGURATION_KEYS -
|
|||
ActiveLdap::Adapter::Base::VALID_ADAPTER_CONFIGURATION_KEYS
|
|||
unless ne_params.empty?
|
|||
msg_list << _("nonexisting LDAP mapping parameters (%s)") % ne_params.join(", ")
|
|||
end
|
|||
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
end
|
|||
end
|
|||
class ObjectValidator < LdapShadowsValidator
|
|||
def validate_hook_in(value, rule, path, msg_list)
|
|||
case rule.name
|
|||
7f913647 | Marc Dequènes (Duck) | when 'ObjectMapping'
|
|
ne_params = value.keys.collect{|k| k.to_sym } -
|
|||
428fdd46 | Marc Dequènes (Duck) | ActiveLdap::Base::VALID_LDAP_MAPPING_OPTIONS -
|
|
@extra_data[:default_parameters][:mapping].keys
|
|||
1f9bfec2 | Marc Dequènes (Duck) | unless ne_params.empty?
|
|
7f913647 | Marc Dequènes (Duck) | msg_list << _("nonexisting object mapping parameters (%s)") % ne_params.join(", ")
|
|
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
when 'RelationMapping'
|
|||
validate_hook_relation(value, rule, path, msg_list)
|
|||
end
|
|||
end
|
|||
end
|
|||
7f913647 | Marc Dequènes (Duck) | ||
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
|
|||
1f9bfec2 | Marc Dequènes (Duck) | end
|
|
end
|