commit dba6a81bf7e3ffa1413705d69285a5f97a6a32d9
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Thu Jul 30 15:36:31 2009 +0200

    [evol] preliminary (dirty) work on hooks

diff --git a/bin/shadowwalker b/bin/shadowwalker
index 61d1828..3a30aee 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -536,6 +536,11 @@ class ModCommand < Command
 
     if modification_done
       missing_fields = item.missing_attributes
+      ### temporarily deactivated
+      # It is needed to test preliminary work on hooks.
+      # This check should be moved into the save() method, raise an exception is any problem,
+      # and exceptions caught in a centralized place in this file.
+      missing_fields = []
       unless missing_fields.empty?
         STDERR.puts "Cannot save the modifications; the following fields are missing:"
         missing_fields.each do |field|
diff --git a/conf/ldap_shadows/hooks/aspects/fs.rb b/conf/ldap_shadows/hooks/aspects/fs.rb
new file mode 100644
index 0000000..0e87a06
--- /dev/null
+++ b/conf/ldap_shadows/hooks/aspects/fs.rb
@@ -0,0 +1,42 @@
+
+# should be in the configuration file
+MIN_UID = 10000
+MAX_UID = 65535
+STEP_UID = 100
+DEFAULT_GROUP = 'dc-users'
+
+def self.hook_mod(mapper, item)
+  # does not work, but why ?
+  unless item.attribute_present?('gidNumber')
+    item.primaryGroup = [mapper.find_klass(:group).find(:first, DEFAULT_GROUP)]
+  end
+
+  unless item.attribute_present?('uidNumber')
+    groups = ActiveLdap::Base.search(:scope => :sub, :filter => "(uidNumber=*)", :attributes => ['uidNumber'])
+    uidnumbers =  groups.collect {|group| group[1]['uidNumber'].first.to_i }
+
+    avail_uidnumber = nil
+    min_uidn = MIN_UID
+    max_uidn = [min_uidn + STEP_UID, MAX_UID].min
+    while avail_uidnumber.nil?
+      avail = (min_uidn..max_uidn).to_a - uidnumbers
+      unless avail.empty?
+        avail_uidnumber = avail.first
+        break
+      end
+      min_uidn = max_uidn
+      max_uidn = [min_uidn + STEP_UID, MAX_UID].min
+    end
+
+    if avail_uidnumber.nil?
+      STDERR.puts "Available UID range is depleted."
+      exit 4
+    else
+      item.uidNumber = avail_uidnumber
+    end
+  end
+
+  unless item.attribute_present?('homeDirectory')
+    item.homeDirectory = "/home/" + item.uid
+  end
+end
diff --git a/lib/ldap_shadows/controller.rb b/lib/ldap_shadows/controller.rb
index 0235753..b2b59fa 100755
--- a/lib/ldap_shadows/controller.rb
+++ b/lib/ldap_shadows/controller.rb
@@ -29,6 +29,8 @@ module LdapShadows
       @mod_container = mod_container
       @object_definitions = {}
       @aspects = {}
+      # should replace @aspects properly one day
+      @aspects2 = {}
     end
 
     def set_global_config(global_config)
@@ -41,12 +43,34 @@ module LdapShadows
 
     def set_aspect(aspect_name, aspect_def)
       @aspects[aspect_name] = aspect_def
+
+      filename = File.join(Config::CFG_DIR, "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
+          STDERR.puts "Could not load Aspect plugin '#{aspect_name}'"
+          exit 1
+        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
@@ -124,5 +148,16 @@ module LdapShadows
   # default location for mapped objects
   module Objects
   end
+
+  class Aspect
+    def hook_create
+    end
+
+    def hook_mod
+    end
+  end
+
+  module Aspects
+  end
 end
 
diff --git a/lib/ldap_shadows/object.rb b/lib/ldap_shadows/object.rb
index 808c343..e190f1e 100755
--- a/lib/ldap_shadows/object.rb
+++ b/lib/ldap_shadows/object.rb
@@ -43,7 +43,7 @@ module LdapShadows
         obj_list = [obj_stuff]
       end
       obj_list.each do |obj|
-        obj.load_initial_attribute_list
+        obj.load_initial_attribute_list unless obj.nil?
       end
       return obj_stuff
     end
@@ -315,8 +315,31 @@ module LdapShadows
       self.must.collect{|attr| attr.name } - self.nonempty_attributes - ['objectClass']
     end
 
+    def save
+      check_hooks
+      super
+    end
+
+    def save!
+      check_hooks
+      super
+    end
+
     protected
 
+    def check_hooks
+      self.aspects.each do |aspect|
+        aklass = self.class.mapper.get_aspect_klass(aspect)
+        next if aklass.nil?
+
+        if self.new_entry?
+          aklass.hook_create(self.class.mapper, self)
+        else
+          aklass.hook_mod(self.class.mapper, self)
+        end
+      end
+    end
+
     def fetch_attributes_data(attr_list, expert_attributes)
       attr_data = self.attributes.collect do |key, val|
         if attr_list.include?(key)
