commit 0df6346aea93b5cf58ff4bc82053d57e6cafc608
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Wed Oct 14 00:46:17 2009 +0200

    [evol] improve config #8 (shadow selection support)

diff --git a/.gitignore b/.gitignore
index b8aefb1..567788f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,5 @@
 lib/ldap_shadows/config.rb
 data/locale
 #
-conf/ldap_shadows/shadow_private.conf
+conf/ldap_shadows/shadows/MilkyPond/shadow_private.conf
 var
diff --git a/bin/shadowwalker b/bin/shadowwalker
index 94a5bff..74d0ac6 100755
--- a/bin/shadowwalker
+++ b/bin/shadowwalker
@@ -47,24 +47,13 @@ cmdparser.options = CmdParse::OptionParserWrapper.new do |opt|
   opt.on("-d", "--debug", "Output debug info without being formated") {|t| $program_options[:debug] = true }
   opt.on("-e", "--expert", "Output extra info for expert users") {|t| $program_options[:expert] = true }
   opt.on("-H", "--handles", "Output with handles (objects/field/... keys used for manipulations)") {|t| $program_options[:handles] = true }
+  opt.on("-s SHADOW", "--shadow SHADOW", "Choose shadow to travel through") {|t| $program_options[:shadow_name] = t }
 end
 
 cmdparser.add_command(CmdParse::HelpCommand.new)
 cmdparser.add_command(CmdParse::VersionCommand.new)
 
 
-$ldapctl = Controller.new
-begin
-  $ldapctl.load_shadow()
-rescue ActiveLdap::Error => e
-  STDERR.puts _("LDAP connection error: %s") % e.to_s
-  exit 2
-rescue
-  STDERR.puts _("Cannot load shadow configuration")
-  exit 2
-end
-
-
 class Command < CmdParse::Command
   attr_accessor :usages_params
 
@@ -78,6 +67,19 @@ class Command < CmdParse::Command
     end
   end
 
+  def execute(args)
+    $ldapctl = Controller.new
+    begin
+      $ldapctl.load_shadow($program_options[:shadow_name])
+    rescue ActiveLdap::Error => e
+      STDERR.puts _("LDAP connection error: %s") % e.to_s
+      exit 2
+    rescue
+      STDERR.puts _("Shadow initialization failed: %s") % $!
+      exit 2
+    end
+  end
+
   protected
 
   def params_shift_location(args)
@@ -133,7 +135,9 @@ class ListCommand < Command
                          ]
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     raise SyntaxError, _("no object name given") if args.empty?
 
     obj_hdl = args.shift.downcase.singularize
@@ -206,7 +210,9 @@ class ShowCommand < Command
     end
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     obj_klass = params_shift_object(args)
     item = params_shift_item(obj_klass, args)
 
@@ -297,7 +303,9 @@ class TreeCommand < Command
     end
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     puts "Tree:"
 
     search_base = nil
@@ -312,9 +320,9 @@ class TreeCommand < Command
       end
     else
       dn_list = []
-      gconfig = $ldapctl.get_global_config()
+      sconfig = $ldapctl.get_shadow_config()
 
-      (gconfig[:tree_objects] || []).each do |obj_hdl|
+      (sconfig[:tree_objects] || []).each do |obj_hdl|
         obj_klass = $ldapctl.find_klass(obj_hdl.downcase)
         unless obj_klass
           raise PreProcessingError, _("Location object '%s' not defined") % obj_hdl
@@ -355,7 +363,9 @@ class ModCommand < Command
                          ]
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     obj_klass = params_shift_object(args)
     item = params_shift_item(obj_klass, args)
 
@@ -388,7 +398,9 @@ class DelCommand < Command
     end
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     obj_klass = params_shift_object(args)
     item = params_shift_item(obj_klass, args)
 
@@ -413,7 +425,9 @@ class CreateCommand < Command
                          ]
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     obj_klass = params_shift_object(args)
 
     raise SyntaxError, _("no item name given") if args.empty?
@@ -447,7 +461,9 @@ class SearchCommand < Command
                          ]
   end
 
-  def execute (args)
+  def execute(args)
+    super
+
     raise SyntaxError, _("no search arguments") if args.empty?
 
     res = LdapObject.items_find_from_strings(args)
diff --git a/conf/ldap_shadows/aspects/fs.conf b/conf/ldap_shadows/aspects/fs.conf
deleted file mode 100644
index 49f4a1e..0000000
--- a/conf/ldap_shadows/aspects/fs.conf
+++ /dev/null
@@ -1,15 +0,0 @@
-mapping:
-  classes: ['fsUser']
-presentation:
-  associated_relations: ['primaryGroup', 'secondaryGroups']
-relations:
-  primaryGroup:
-    type: :belongs_to
-    object: group
-    foreign_key: gidNumber
-    primary_key: gidNumber
-  secondaryGroups:
-    type: :belongs_to
-    object: group
-    many: uniqueMember
-    foreign_key: dn
diff --git a/conf/ldap_shadows/aspects/ftp.conf b/conf/ldap_shadows/aspects/ftp.conf
deleted file mode 100644
index a839e44..0000000
--- a/conf/ldap_shadows/aspects/ftp.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-mapping:
-  classes: ['ftpUser']
-  depend_aspects: ['primary', 'fs']
-presentation:
-relations:
diff --git a/conf/ldap_shadows/aspects/jabber.conf b/conf/ldap_shadows/aspects/jabber.conf
deleted file mode 100644
index 7124b08..0000000
--- a/conf/ldap_shadows/aspects/jabber.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-mapping:
-  classes: ['jabberUser']
-presentation:
-relations:
diff --git a/conf/ldap_shadows/aspects/mail.conf b/conf/ldap_shadows/aspects/mail.conf
deleted file mode 100644
index f70e952..0000000
--- a/conf/ldap_shadows/aspects/mail.conf
+++ /dev/null
@@ -1,4 +0,0 @@
-mapping:
-  classes: ['emailUser']
-presentation:
-relations:
diff --git a/conf/ldap_shadows/aspects/primary.conf b/conf/ldap_shadows/aspects/primary.conf
deleted file mode 100644
index 3e737d7..0000000
--- a/conf/ldap_shadows/aspects/primary.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-mapping:
-  classes: ['primaryAccount']
-presentation:
-  associated_attributes: ['uid']
-relations:
diff --git a/conf/ldap_shadows/aspects/shell.conf b/conf/ldap_shadows/aspects/shell.conf
deleted file mode 100644
index e771450..0000000
--- a/conf/ldap_shadows/aspects/shell.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-mapping:
-  classes: ['shellUser']
-  depend_aspects: ['primary', 'fs']
-presentation:
-  associated_attributes: ['loginShell']
-relations:
diff --git a/conf/ldap_shadows/aspects/web.conf b/conf/ldap_shadows/aspects/web.conf
deleted file mode 100644
index 19cb38b..0000000
--- a/conf/ldap_shadows/aspects/web.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-mapping:
-  classes: ['webUser']
-  depend_aspects: ['primary']
-presentation:
-relations:
diff --git a/conf/ldap_shadows/global.conf b/conf/ldap_shadows/global.conf
new file mode 100644
index 0000000..97f9ee5
--- /dev/null
+++ b/conf/ldap_shadows/global.conf
@@ -0,0 +1,2 @@
+---
+default_shadow: MilkyPond
diff --git a/conf/ldap_shadows/hooks/aspects/fs.rb b/conf/ldap_shadows/hooks/aspects/fs.rb
deleted file mode 100644
index 1956a0c..0000000
--- a/conf/ldap_shadows/hooks/aspects/fs.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-
-# 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)
-  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/conf/ldap_shadows/objects/bot.conf b/conf/ldap_shadows/objects/bot.conf
deleted file mode 100644
index ffc10b0..0000000
--- a/conf/ldap_shadows/objects/bot.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-mapping:
-  dn_attribute: uid
-  prefix: ''
-  classes: ['bot']
-  sort_by: uid
-presentation:
-  allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
-  hidden_attributes: ['uid']
-  expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
diff --git a/conf/ldap_shadows/objects/domain.conf b/conf/ldap_shadows/objects/domain.conf
deleted file mode 100644
index 4080849..0000000
--- a/conf/ldap_shadows/objects/domain.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-mapping:
-  dn_attribute: cn
-  prefix: ''
-  classes: ['genericDomain']
-  sort_by: cn
-presentation:
-  allowed_aspects: []
diff --git a/conf/ldap_shadows/objects/entity.conf b/conf/ldap_shadows/objects/entity.conf
deleted file mode 100644
index 77d800b..0000000
--- a/conf/ldap_shadows/objects/entity.conf
+++ /dev/null
@@ -1,21 +0,0 @@
-mapping:
-  dn_attribute: o
-  prefix: ''
-  classes: ['entity']
-  excluded_classes: ['dcObject']
-  sort_by: o
-presentation:
-  name_attribute: o
-  hidden_attributes: ['founder']
-  associated_relations: ['foundersIndividuals', 'foundersEntities']
-relations:
-  foundersIndividuals:
-    type: :has_many
-    object: individual
-    foreign_key: founder
-    primary_key: dn
-  foundersEntities:
-    type: :has_many
-    object: entity
-    foreign_key: founder
-    primary_key: dn
diff --git a/conf/ldap_shadows/objects/group.conf b/conf/ldap_shadows/objects/group.conf
deleted file mode 100644
index 7a377b9..0000000
--- a/conf/ldap_shadows/objects/group.conf
+++ /dev/null
@@ -1,31 +0,0 @@
-mapping:
-  dn_attribute: cn
-  prefix: ''
-  classes: ['posixGroup', 'groupOfMembers']
-  sort_by: cn
-presentation:
-  allowed_aspects: []
-  hidden_attributes: ['uniqueMember']
-  expert_attributes: ['gidNumber']
-  associated_relations: ['individualsPrimaryMembers', 'botsPrimaryMembers', 'individualsSecondaryMembers', 'botsSecondaryMembers']
-relations:
-  individualsPrimaryMembers:
-    type: :belongs_to
-    object: individual
-    many: gidNumber
-    foreign_key: gidNumber
-  botsPrimaryMembers:
-    type: :belongs_to
-    object: bot
-    many: gidNumber
-    foreign_key: gidNumber
-  individualsSecondaryMembers:
-    type: :has_many
-    object: individual
-    foreign_key: uniqueMember
-    primary_key: dn
-  botsSecondaryMembers:
-    type: :has_many
-    object: bot
-    foreign_key: uniqueMember
-    primary_key: dn
diff --git a/conf/ldap_shadows/objects/individual.conf b/conf/ldap_shadows/objects/individual.conf
deleted file mode 100644
index bd35d9e..0000000
--- a/conf/ldap_shadows/objects/individual.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-mapping:
-  dn_attribute: uid
-  prefix: ''
-  classes: ['individual']
-  sort_by: uid
-presentation:
-  allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
-  hidden_attributes: ['uid']
-  expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
diff --git a/conf/ldap_shadows/objects/secondary_account.conf b/conf/ldap_shadows/objects/secondary_account.conf
deleted file mode 100644
index c7bb48a..0000000
--- a/conf/ldap_shadows/objects/secondary_account.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-mapping:
-  dn_attribute: uid
-  prefix: ''
-  classes: ['secondaryAccount']
-  sort_by: uid
-presentation:
-  allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
diff --git a/conf/ldap_shadows/objects/system_account.conf b/conf/ldap_shadows/objects/system_account.conf
deleted file mode 100644
index 3cf103d..0000000
--- a/conf/ldap_shadows/objects/system_account.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-mapping:
-  dn_attribute: uid
-  prefix: ''
-  classes: ['systemAccount']
-  sort_by: uid
-presentation:
-  allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
diff --git a/conf/ldap_shadows/objects/unit.conf b/conf/ldap_shadows/objects/unit.conf
deleted file mode 100644
index bf03083..0000000
--- a/conf/ldap_shadows/objects/unit.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-mapping:
-  dn_attribute: ou
-  prefix: ''
-  classes: ['organizationalUnit']
-  # no sort_by means sort by DN
-presentation:
-  name_attribute: dn
diff --git a/conf/ldap_shadows/shadow.conf b/conf/ldap_shadows/shadow.conf
deleted file mode 100644
index 6f9d97b..0000000
--- a/conf/ldap_shadows/shadow.conf
+++ /dev/null
@@ -1,4 +0,0 @@
----
-presentation:
-  hidden_attributes: ['objectClass', 'userPassword', 'hasSubordinates', 'entryUUID', 'entryDN', 'structuralObjectClass', 'subschemaSubentry']
-  tree_objects: ['unit', 'entity']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/fs.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/fs.conf
new file mode 100644
index 0000000..49f4a1e
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/fs.conf
@@ -0,0 +1,15 @@
+mapping:
+  classes: ['fsUser']
+presentation:
+  associated_relations: ['primaryGroup', 'secondaryGroups']
+relations:
+  primaryGroup:
+    type: :belongs_to
+    object: group
+    foreign_key: gidNumber
+    primary_key: gidNumber
+  secondaryGroups:
+    type: :belongs_to
+    object: group
+    many: uniqueMember
+    foreign_key: dn
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/ftp.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/ftp.conf
new file mode 100644
index 0000000..a839e44
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/ftp.conf
@@ -0,0 +1,5 @@
+mapping:
+  classes: ['ftpUser']
+  depend_aspects: ['primary', 'fs']
+presentation:
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/jabber.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/jabber.conf
new file mode 100644
index 0000000..7124b08
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/jabber.conf
@@ -0,0 +1,4 @@
+mapping:
+  classes: ['jabberUser']
+presentation:
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/mail.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/mail.conf
new file mode 100644
index 0000000..f70e952
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/mail.conf
@@ -0,0 +1,4 @@
+mapping:
+  classes: ['emailUser']
+presentation:
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/primary.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/primary.conf
new file mode 100644
index 0000000..3e737d7
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/primary.conf
@@ -0,0 +1,5 @@
+mapping:
+  classes: ['primaryAccount']
+presentation:
+  associated_attributes: ['uid']
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/shell.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/shell.conf
new file mode 100644
index 0000000..e771450
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/shell.conf
@@ -0,0 +1,6 @@
+mapping:
+  classes: ['shellUser']
+  depend_aspects: ['primary', 'fs']
+presentation:
+  associated_attributes: ['loginShell']
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/aspects/web.conf b/conf/ldap_shadows/shadows/MilkyPond/aspects/web.conf
new file mode 100644
index 0000000..19cb38b
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/aspects/web.conf
@@ -0,0 +1,5 @@
+mapping:
+  classes: ['webUser']
+  depend_aspects: ['primary']
+presentation:
+relations:
diff --git a/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb b/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
new file mode 100644
index 0000000..1956a0c
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/hooks/aspects/fs.rb
@@ -0,0 +1,41 @@
+
+# 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)
+  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/conf/ldap_shadows/shadows/MilkyPond/objects/bot.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/bot.conf
new file mode 100644
index 0000000..ffc10b0
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/bot.conf
@@ -0,0 +1,9 @@
+mapping:
+  dn_attribute: uid
+  prefix: ''
+  classes: ['bot']
+  sort_by: uid
+presentation:
+  allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
+  hidden_attributes: ['uid']
+  expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/domain.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/domain.conf
new file mode 100644
index 0000000..4080849
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/domain.conf
@@ -0,0 +1,7 @@
+mapping:
+  dn_attribute: cn
+  prefix: ''
+  classes: ['genericDomain']
+  sort_by: cn
+presentation:
+  allowed_aspects: []
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/entity.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/entity.conf
new file mode 100644
index 0000000..77d800b
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/entity.conf
@@ -0,0 +1,21 @@
+mapping:
+  dn_attribute: o
+  prefix: ''
+  classes: ['entity']
+  excluded_classes: ['dcObject']
+  sort_by: o
+presentation:
+  name_attribute: o
+  hidden_attributes: ['founder']
+  associated_relations: ['foundersIndividuals', 'foundersEntities']
+relations:
+  foundersIndividuals:
+    type: :has_many
+    object: individual
+    foreign_key: founder
+    primary_key: dn
+  foundersEntities:
+    type: :has_many
+    object: entity
+    foreign_key: founder
+    primary_key: dn
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/group.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/group.conf
new file mode 100644
index 0000000..7a377b9
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/group.conf
@@ -0,0 +1,31 @@
+mapping:
+  dn_attribute: cn
+  prefix: ''
+  classes: ['posixGroup', 'groupOfMembers']
+  sort_by: cn
+presentation:
+  allowed_aspects: []
+  hidden_attributes: ['uniqueMember']
+  expert_attributes: ['gidNumber']
+  associated_relations: ['individualsPrimaryMembers', 'botsPrimaryMembers', 'individualsSecondaryMembers', 'botsSecondaryMembers']
+relations:
+  individualsPrimaryMembers:
+    type: :belongs_to
+    object: individual
+    many: gidNumber
+    foreign_key: gidNumber
+  botsPrimaryMembers:
+    type: :belongs_to
+    object: bot
+    many: gidNumber
+    foreign_key: gidNumber
+  individualsSecondaryMembers:
+    type: :has_many
+    object: individual
+    foreign_key: uniqueMember
+    primary_key: dn
+  botsSecondaryMembers:
+    type: :has_many
+    object: bot
+    foreign_key: uniqueMember
+    primary_key: dn
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/individual.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/individual.conf
new file mode 100644
index 0000000..bd35d9e
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/individual.conf
@@ -0,0 +1,9 @@
+mapping:
+  dn_attribute: uid
+  prefix: ''
+  classes: ['individual']
+  sort_by: uid
+presentation:
+  allowed_aspects: ['primary', 'mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
+  hidden_attributes: ['uid']
+  expert_attributes: ['uidNumber', 'gidNumber', 'gecos']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/secondary_account.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/secondary_account.conf
new file mode 100644
index 0000000..c7bb48a
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/secondary_account.conf
@@ -0,0 +1,7 @@
+mapping:
+  dn_attribute: uid
+  prefix: ''
+  classes: ['secondaryAccount']
+  sort_by: uid
+presentation:
+  allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/system_account.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/system_account.conf
new file mode 100644
index 0000000..3cf103d
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/system_account.conf
@@ -0,0 +1,7 @@
+mapping:
+  dn_attribute: uid
+  prefix: ''
+  classes: ['systemAccount']
+  sort_by: uid
+presentation:
+  allowed_aspects: ['mail', 'fs', 'shell', 'ftp', 'web', 'jabber']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/objects/unit.conf b/conf/ldap_shadows/shadows/MilkyPond/objects/unit.conf
new file mode 100644
index 0000000..bf03083
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/objects/unit.conf
@@ -0,0 +1,7 @@
+mapping:
+  dn_attribute: ou
+  prefix: ''
+  classes: ['organizationalUnit']
+  # no sort_by means sort by DN
+presentation:
+  name_attribute: dn
diff --git a/conf/ldap_shadows/shadows/MilkyPond/shadow.conf b/conf/ldap_shadows/shadows/MilkyPond/shadow.conf
new file mode 100644
index 0000000..6f9d97b
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/shadow.conf
@@ -0,0 +1,4 @@
+---
+presentation:
+  hidden_attributes: ['objectClass', 'userPassword', 'hasSubordinates', 'entryUUID', 'entryDN', 'structuralObjectClass', 'subschemaSubentry']
+  tree_objects: ['unit', 'entity']
diff --git a/conf/ldap_shadows/shadows/MilkyPond/translations/en.yml b/conf/ldap_shadows/shadows/MilkyPond/translations/en.yml
new file mode 100644
index 0000000..4521b30
--- /dev/null
+++ b/conf/ldap_shadows/shadows/MilkyPond/translations/en.yml
@@ -0,0 +1,69 @@
+---
+en:
+  objects:
+    bot: "Bot"
+    domain: "Domain"
+    entity: "Entity"
+    group: "Group"
+    individual: "Individual"
+    secondary_account: "Secondary Account"
+    systemAccount: "System Account"
+    unit: "Unit"
+  attribute_types:
+    allowGlobalDirectory: "Disclose Own Contact Information in GLobal Directory"
+    cn: "Full Name"
+    createTimestamp: "Item creation time"
+    creatorsName: "Item Creator"
+    birthday: "Birthday"
+    birthlocation: "Birth Location"
+    description: "Description"
+    entryUUID: "Item UUID"
+    foundingDate: "Founding Date"
+    gecos: "GECOS"
+    gidNumber: "Primary Group (numeric)"
+    givenName: "FirstName"
+    homeDirectory: "Home Directory"
+    homePostalAddress: "Home Postal Address"
+    host: "Shell Allowed Hosts"
+    jid: "Jabber ID"
+    jpegPhoto: "Photo"
+    keyFingerPrint: "GPG/PGP Key Fingerprint"
+    labeledURI: "Web Site"
+    loginShell: "Shell Interpreter"
+    mail: "eMail address(es)"
+    mailForward: "eMail Forward Adress(es)"
+    mailQuota: "Maximum Mailbox Size"
+    manager: "Manager(s)"
+    mobile: "Mobile Phone"
+    modifiersName: "Item Last Modifier"
+    modifyTimestamp: "Item Last Modification Time"
+    occupation: "Job / Studies"
+    o: "Organization(s)"
+    structuralObjectClass: "Item Structural Classes"
+    owner: "Owner(s)"
+    preferredLanguage: "Language Preference"
+    sn: "Surname"
+    sshAuthKey: "SSH Public Key(s)"
+    uid: "Identifier (Login)"
+    uidNumber: "Identifier (numeric)"
+    uniqueAbbreviation: "Unique Abbreviation"
+    uniqueMember: "Group member"
+    userPassword: "Password"
+    webVirtualHost: "Hosted Web Sites"
+  relations:
+    primaryGroup: "Primary Group"
+    secondaryGroups: "Secondary Groups"
+    individualsPrimaryMembers: "Primary Member(s) Individual(s)"
+    botsPrimaryMembers: "Primary Member(s) Bot(s)"
+    individualsSecondaryMembers: "Secondary Member(s) Individual(s)"
+    botsSecondaryMembers: "Secondary Member(s) Bot(s)"
+    foundersIndividuals: "Founder(s) Individual(s)"
+    foundersEntities: "Founder(s) Entity(ies)"
+  aspects:
+    fs: "FileSystem Account"
+    ftp: "FTP Account"
+    jabber: "Jabber Account"
+    mail: "eMail Account"
+    primary: "Primary Account"
+    shell: "Shell Account"
+    web: "Web Account"
diff --git a/conf/ldap_shadows/translations/en.yml b/conf/ldap_shadows/translations/en.yml
deleted file mode 100644
index 4521b30..0000000
--- a/conf/ldap_shadows/translations/en.yml
+++ /dev/null
@@ -1,69 +0,0 @@
----
-en:
-  objects:
-    bot: "Bot"
-    domain: "Domain"
-    entity: "Entity"
-    group: "Group"
-    individual: "Individual"
-    secondary_account: "Secondary Account"
-    systemAccount: "System Account"
-    unit: "Unit"
-  attribute_types:
-    allowGlobalDirectory: "Disclose Own Contact Information in GLobal Directory"
-    cn: "Full Name"
-    createTimestamp: "Item creation time"
-    creatorsName: "Item Creator"
-    birthday: "Birthday"
-    birthlocation: "Birth Location"
-    description: "Description"
-    entryUUID: "Item UUID"
-    foundingDate: "Founding Date"
-    gecos: "GECOS"
-    gidNumber: "Primary Group (numeric)"
-    givenName: "FirstName"
-    homeDirectory: "Home Directory"
-    homePostalAddress: "Home Postal Address"
-    host: "Shell Allowed Hosts"
-    jid: "Jabber ID"
-    jpegPhoto: "Photo"
-    keyFingerPrint: "GPG/PGP Key Fingerprint"
-    labeledURI: "Web Site"
-    loginShell: "Shell Interpreter"
-    mail: "eMail address(es)"
-    mailForward: "eMail Forward Adress(es)"
-    mailQuota: "Maximum Mailbox Size"
-    manager: "Manager(s)"
-    mobile: "Mobile Phone"
-    modifiersName: "Item Last Modifier"
-    modifyTimestamp: "Item Last Modification Time"
-    occupation: "Job / Studies"
-    o: "Organization(s)"
-    structuralObjectClass: "Item Structural Classes"
-    owner: "Owner(s)"
-    preferredLanguage: "Language Preference"
-    sn: "Surname"
-    sshAuthKey: "SSH Public Key(s)"
-    uid: "Identifier (Login)"
-    uidNumber: "Identifier (numeric)"
-    uniqueAbbreviation: "Unique Abbreviation"
-    uniqueMember: "Group member"
-    userPassword: "Password"
-    webVirtualHost: "Hosted Web Sites"
-  relations:
-    primaryGroup: "Primary Group"
-    secondaryGroups: "Secondary Groups"
-    individualsPrimaryMembers: "Primary Member(s) Individual(s)"
-    botsPrimaryMembers: "Primary Member(s) Bot(s)"
-    individualsSecondaryMembers: "Secondary Member(s) Individual(s)"
-    botsSecondaryMembers: "Secondary Member(s) Bot(s)"
-    foundersIndividuals: "Founder(s) Individual(s)"
-    foundersEntities: "Founder(s) Entity(ies)"
-  aspects:
-    fs: "FileSystem Account"
-    ftp: "FTP Account"
-    jabber: "Jabber Account"
-    mail: "eMail Account"
-    primary: "Primary Account"
-    shell: "Shell Account"
-    web: "Web Account"
diff --git a/lib/ldap_shadows/controller.rb b/lib/ldap_shadows/controller.rb
index 701f614..2004484 100755
--- a/lib/ldap_shadows/controller.rb
+++ b/lib/ldap_shadows/controller.rb
@@ -26,6 +26,8 @@ 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
 
@@ -34,19 +36,22 @@ module LdapShadows
     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 = {}
-      @shadow_dir = nil
     end
 
-    def set_global_config(global_config)
-      @global_config = global_config
+    def set_shadow_config(shadow_config)
+      @shadow_config = shadow_config
     end
 
-    def get_global_config
-      @global_config
+    def get_shadow_config
+      @shadow_config
     end
 
     def set_aspect(aspect_name, aspect_def)
@@ -63,8 +68,7 @@ module LdapShadows
           end
           EOS
         rescue
-          STDERR.puts "Could not load Aspect plugin '#{aspect_name}'"
-          exit 1
+          raise PreProcessingError, _("Could not load Aspect plugin '%s'") % aspect_name
         end
 
         @aspects2[aspect_name] = Aspects.const_get(klass_name)
@@ -118,8 +122,7 @@ module LdapShadows
           obj_def[:presentation][:allowed_aspects].each do |aspect|
             aspect_data = get_aspect(aspect)
             if aspect_data.nil?
-              STDERR.puts "Aspect '%s' is missing" % aspect
-              exit 1
+              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
@@ -132,8 +135,7 @@ module LdapShadows
         obj_rel.each_pair do |field_name, rel|
           foreign_klass = find_klass(rel[:object])
           if foreign_klass.nil?
-            STDERR.puts "Relation '%s' for object '%s' is impossible: foreign object '%s' is missing" % [field_name, obj_name, rel[:object]]
-            exit 1
+            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
 
@@ -163,14 +165,34 @@ module LdapShadows
     def load_shadow(shadow_name = nil)
       clear_shadow
 
-      # TODO: local global config and use shadow name to load corresponding Shadow (or defautl Shadow if nil)
-      @shadow_dir = LdapShadows::Config::CFG_DIR
+      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_str = IO.read(File.join(@shadow_dir, "shadow.conf"))
-      config = YAML.load(config_str)
+      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_dir, "shadow_private.conf")
+        File.join(@shadow_config_path, "shadow_private.conf")
         ]
       config_str_prv_filelist.each do |file|
         if File.exists?(file)
@@ -181,7 +203,7 @@ module LdapShadows
       end
       config.recursive_symbolize_keys!
 
-      $ldapctl.set_global_config(config[:presentation])
+      $ldapctl.set_shadow_config(config[:presentation])
 
       load_config_components("aspects") do |c_name, c_config|
         $ldapctl.set_aspect(c_name, c_config)
@@ -194,12 +216,20 @@ module LdapShadows
       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 configiguration: %s") % $!
     end
 
     protected
 
     def load_config_components(type)
-      c_config_dir = File.join(@shadow_dir, 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|
diff --git a/lib/ldap_shadows/display_utils.rb b/lib/ldap_shadows/display_utils.rb
index 75c16a5..c2f144c 100644
--- a/lib/ldap_shadows/display_utils.rb
+++ b/lib/ldap_shadows/display_utils.rb
@@ -21,7 +21,6 @@ require 'active_support'
 
 module LdapShadows
   module Translator
-    I18n.load_path += Dir[File.join(Config::CFG_DIR, "translations", "**", "*.yml")]
     I18n.default_locale = :en
 
     def self.translate_object_name(obj_hdl)
diff --git a/lib/ldap_shadows/object.rb b/lib/ldap_shadows/object.rb
index c458137..c8380c9 100755
--- a/lib/ldap_shadows/object.rb
+++ b/lib/ldap_shadows/object.rb
@@ -130,7 +130,7 @@ module LdapShadows
     end
 
     def organized_data
-      ignored_attrs = self.mapper.get_global_config[:hidden_attributes] || []
+      ignored_attrs = self.mapper.get_shadow_config[:hidden_attributes] || []
       ignored_attrs += self.class.presentation[:hidden_attributes] || []
       attr_list = self.nonempty_attributes - ignored_attrs
 
