commit 7f913647af33494ef7a79a0cd1949daa103ab3a0
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Oct 17 11:17:06 2009 +0200

    [evol] improve config #14 (validate global and shadow config, and removed shadowwalker's stuff from the general classes)

diff --git a/TODO b/TODO
index 706680e..02f267f 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,5 @@
 - improve configuration:
   * allow global relations to share them among objects ???
-  * validate each file with a schema (using kwalify)
 - select parent at creation with :parent if present, or use
   a default location (object-specific) ? this could be integrated in
   a common code to be able to relocate an item
diff --git a/bin/shadowwalker b/bin/shadowwalker
index 8b10268..d52115c 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -68,6 +68,7 @@ class Command < CmdParse::Command
 
   def execute(args)
     config = LdapShadows::Config.instance
+    config.load_global_config([File.join(ENV['HOME'], ".shadowwalker")])
     begin
       $ldapctl = config.load_shadow($program_options[:shadow_name])
     rescue ActiveLdap::Error => e
diff --git a/data/ldap_shadows/schema/global.yaml b/data/ldap_shadows/schema/global.yaml
new file mode 100644
index 0000000..3327daa
--- /dev/null
+++ b/data/ldap_shadows/schema/global.yaml
@@ -0,0 +1,12 @@
+---
+type: map
+mapping:
+  "default_shadow": {type: str}
+  "ldap":
+    type: map
+    required: true
+    name: LdapMapping
+    mapping:
+      =:
+        type: any
+
diff --git a/data/ldap_shadows/schema/object.yaml b/data/ldap_shadows/schema/object.yaml
index ba05623..78967ae 100644
--- a/data/ldap_shadows/schema/object.yaml
+++ b/data/ldap_shadows/schema/object.yaml
@@ -4,7 +4,7 @@ mapping:
   "mapping":
     type: map
     required: true
-    name: Mapping
+    name: ObjectMapping
     mapping:
       =:
         type: any
diff --git a/data/ldap_shadows/schema/shadow.yaml b/data/ldap_shadows/schema/shadow.yaml
new file mode 100644
index 0000000..5bcdf42
--- /dev/null
+++ b/data/ldap_shadows/schema/shadow.yaml
@@ -0,0 +1,15 @@
+---
+type: map
+mapping:
+  "presentation":
+    type: map
+    mapping:
+      "hidden_attributes":
+        type: seq
+        sequence:
+          - type: str
+      "tree_objects":
+        type: seq
+        sequence:
+          - type: str
+
diff --git a/lib/ldap_shadows/config.rb b/lib/ldap_shadows/config.rb
index f3d3cc8..d442bf2 100644
--- a/lib/ldap_shadows/config.rb
+++ b/lib/ldap_shadows/config.rb
@@ -18,6 +18,7 @@
 
 require 'ldap_shadows/config_setup'
 require 'singleton'
+require 'yaml'
 require 'kwalify'
 require 'ldap_shadows/shadow'
 
@@ -31,25 +32,23 @@ module LdapShadows
       @shadows = {}
     end
 
-    def load_global_config
+    def load_global_config(extra_private_config_list = [])
       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"),
+      g_config_partial = YAML.load_file(g_config_file) || {}
+      extra_private_config_list += [
         File.join(CFG_DIR, "global_private.conf")
         ]
-      g_config_str_prv_filelist.each do |file|
+      extra_private_config_list.each do |file|
         if File.exists?(file)
-          g_config.merge!(YAML.load_file(file))
+          g_config_partial.merge!(YAML.load_file(file) || {})
           break
         end
       end
-      g_config.recursive_symbolize_keys!
+      g_config = parse_and_validate('config', 'global', YAML.dump(g_config_partial))
 
       ActiveLdap::Base.setup_connection(g_config[:ldap])
 
@@ -76,11 +75,10 @@ module LdapShadows
       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!
+      config = File.read(config_file)
 
       shadow = Shadow.new(shadow_name)
-      shadow.set_shadow_config(config[:presentation])
+      shadow.set_shadow_config(config)
 
       load_config_components(shadow_config_path, 'aspects') do |c_name, c_config|
         shadow.add_aspect(c_name, c_config)
@@ -116,6 +114,8 @@ module LdapShadows
       schema = load_schema(type)
 
       validator_klass = case type
+      when 'global'
+        GlobalValidator
       when 'object'
         ObjectValidator
       when 'aspect'
@@ -193,12 +193,14 @@ module LdapShadows
         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
+          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
+          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
@@ -206,11 +208,16 @@ module LdapShadows
       end
     end
 
-    class AspectValidator < LdapShadowsValidator
+    class GlobalValidator < LdapShadowsValidator
       def validate_hook_in(value, rule, path, msg_list)
         case rule.name
-        when 'RelationMapping'
-          validate_hook_relation(value, rule, path, msg_list)
+        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
@@ -218,10 +225,11 @@ module LdapShadows
     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
+        when 'ObjectMapping'
+          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(", ")
+            msg_list << _("nonexisting object mapping parameters (%s)") % ne_params.join(", ")
           end
 
         when 'RelationMapping'
@@ -229,5 +237,14 @@ module LdapShadows
         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
diff --git a/lib/ldap_shadows/shadow.rb b/lib/ldap_shadows/shadow.rb
index b9eab8a..29e7b26 100755
--- a/lib/ldap_shadows/shadow.rb
+++ b/lib/ldap_shadows/shadow.rb
@@ -42,12 +42,13 @@ module LdapShadows
       @aspects2 = {}
     end
 
-    def set_shadow_config(shadow_config)
-      @shadow_config = shadow_config
+    def set_shadow_config(shadow_def_raw)
+      @shadow_config = @config.parse_and_validate(@name, 'shadow', shadow_def_raw)
     end
 
     def get_shadow_config
-      @shadow_config
+      # TODO: remove this workaround
+      @shadow_config[:presentation]
     end
 
     def add_aspect(aspect_name, aspect_def_raw)
