Project

General

Profile

Download (10 KB) Statistics
| Branch: | Tag: | Revision:
#--
# LdapShadows, a Medium-level LDAP Access Library and Tool.
# Copyright (c) 2009-2010 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 'ldap_shadows/shadow'
require 'singleton'
require 'yaml'
require 'kwalify'
require 'facets/hash/recursive_merge'

module LdapShadows
class Config
include Singleton

def initialize
@global_config = nil
@world_config = nil
@schemas = {}
@default_configs = {}
@shadows = {}
end

def load_global_config
g_default_config = load_default_config('global')

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_def_partial = YAML.load_file(g_config_file) || {}
g_def = parse('config', 'global', YAML.dump(g_def_partial))
g_config = g_default_config.recursive_merge(g_def)

@global_config = g_config
end

def load_world_config(world_name = nil, extra_private_config_list = [])
world_name = self.global_config[:default_world] if world_name.nil?

w_default_config = load_default_config('world')

w_config_path = File.join(CFG_DIR, "worlds", world_name)

w_config_file = File.join(w_config_path, "world.conf")
unless File.exists? w_config_file
raise PreProcessingError, _("General configuration file for World '%s' is missing") % world_name
end

w_def_partial = YAML.load_file(w_config_file) || {}
extra_private_config_list += [
File.join(w_config_path, "world_private.conf")
]
extra_private_config_list.each do |file|
if File.exists?(file)
w_def_partial.merge!(YAML.load_file(file) || {})
break
end
end
w_def = parse('config', 'world', YAML.dump(w_def_partial))
w_config = w_default_config.recursive_merge(w_def)

ActiveLdap::Base.setup_connection(w_config[:ldap])

@world_config = w_config
@world_config_path = w_config_path
end

def global_config
load_global_config() if @global_config.nil?
@global_config
end

def world_config
load_world_config() if @world_config.nil?
@world_config
end

def load_shadow(shadow_name = nil)
shadow_name = self.world_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(@world_config_path, "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 = File.read(config_file)

shadow = Shadow.new(shadow_name, self)
# register early (needed to load plugins)
@shadows[shadow_name] = {
:config_path => shadow_config_path,
:shadow => shadow
}
shadow.set_config(config)

include_path = File.join(shadow_config_path, "plugins", "includes")
$:.unshift(include_path) if File.directory?(include_path)

load_element_config(shadow_config_path, 'aspects') do |c_name, c_config|
shadow.add_aspect(c_name, c_config)
end

load_element_config(shadow_config_path, 'objects') do |c_name, c_config|
shadow.add_object(c_name, c_config)
end

shadow.cast

# TODO: find a way to handle translations per-shadow
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

def parse(def_name, type, def_data_raw, validate = true, extra_data = nil)
schema = load_schema(type)

if validate
validator_klass = case type
when 'world'
WorldValidator
when 'object'
ObjectValidator
when 'aspect'
AspectValidator
else
LdapShadowsValidator
end
validator = validator_klass.new(schema)
validator.extra_data = extra_data
else
validator = nil
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

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

def load_hook_content(shadow_name, type, hook_name)
s_info = get_shadow_info(shadow_name)
return nil if s_info.nil?

filename = File.join(s_info[:config_path], "plugins", type.pluralize, hook_name.to_s.downcase + ".rb")
return nil unless File.exists?(filename)

File.read(filename)
end

def load_element_config(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 for '%s': %s") % [type, $!]
end

protected

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
attr_accessor :extra_data

## 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_in(value, rule, path, msg_list)
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 WorldValidator < LdapShadowsValidator
def validate_hook_in(value, rule, path, msg_list)
case rule.name
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
end
end
end

class ObjectValidator < LdapShadowsValidator
def validate_hook_in(value, rule, path, msg_list)
case rule.name
when 'ObjectMapping'
ne_params = value.keys.collect{|k| k.to_sym } -
ActiveLdap::Base::VALID_LDAP_MAPPING_OPTIONS -
@extra_data[:default_parameters][:mapping].keys
unless ne_params.empty?
msg_list << _("nonexisting object mapping parameters (%s)") % ne_params.join(", ")
end

when 'RelationMapping'
validate_hook_relation(value, rule, path, msg_list)
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
end
end
(2-2/9)