[asl-commit] ActiveSambaLdap r1:

null at cozmixng.org null at cozmixng.org
Fri Aug 3 22:30:38 EDT 2007


retro	2007-08-04 11:30:36 +0900 (Sat, 04 Aug 2007)

  New Revision: 1

  Added directories:
    activesambaldap/
    activesambaldap/branches/
    activesambaldap/tags/
    activesambaldap/trunk/
    activesambaldap/trunk/bin/
    activesambaldap/trunk/lib/
    activesambaldap/trunk/lib/active_samba_ldap/
    activesambaldap/trunk/lib/samba/
    activesambaldap/trunk/test/
    branchs/
    tags/
    trunk/
    trunk/lib/
    trunk/lib/webrick/
    trunk/lib/webrick/httpservlet/
    trunk/lib/webrick/httpstatus/
    trunk/sample/
    trunk/test/
  Added files:
    activesambaldap/trunk/README
    activesambaldap/trunk/Rakefile
    activesambaldap/trunk/bin/asl-groupadd
    activesambaldap/trunk/bin/asl-groupdel
    activesambaldap/trunk/bin/asl-groupmod
    activesambaldap/trunk/bin/asl-groupshow
    activesambaldap/trunk/bin/asl-passwd
    activesambaldap/trunk/bin/asl-populate
    activesambaldap/trunk/bin/asl-useradd
    activesambaldap/trunk/bin/asl-userdel
    activesambaldap/trunk/bin/asl-usermod
    activesambaldap/trunk/bin/asl-usershow
    activesambaldap/trunk/lib/active_samba_ldap.rb
    activesambaldap/trunk/lib/active_samba_ldap/account.rb
    activesambaldap/trunk/lib/active_samba_ldap/base.rb
    activesambaldap/trunk/lib/active_samba_ldap/command.rb
    activesambaldap/trunk/lib/active_samba_ldap/computer.rb
    activesambaldap/trunk/lib/active_samba_ldap/dc.rb
    activesambaldap/trunk/lib/active_samba_ldap/default_config.rb
    activesambaldap/trunk/lib/active_samba_ldap/group.rb
    activesambaldap/trunk/lib/active_samba_ldap/idmap.rb
    activesambaldap/trunk/lib/active_samba_ldap/ou.rb
    activesambaldap/trunk/lib/active_samba_ldap/populate.rb
    activesambaldap/trunk/lib/active_samba_ldap/unix_id_pool.rb
    activesambaldap/trunk/lib/active_samba_ldap/user.rb
    activesambaldap/trunk/lib/active_samba_ldap/user_password.rb
    activesambaldap/trunk/lib/active_samba_ldap/version.rb
    activesambaldap/trunk/lib/samba/encrypt.rb
    activesambaldap/trunk/test/asl_test_utils.rb
    activesambaldap/trunk/test/command.rb
    activesambaldap/trunk/test/command_support.rb
    activesambaldap/trunk/test/run-test.rb
    activesambaldap/trunk/test/test_asl_groupadd.rb
    activesambaldap/trunk/test/test_asl_groupdel.rb
    activesambaldap/trunk/test/test_asl_groupmod.rb
    activesambaldap/trunk/test/test_asl_groupshow.rb
    activesambaldap/trunk/test/test_asl_passwd.rb
    activesambaldap/trunk/test/test_asl_populate.rb
    activesambaldap/trunk/test/test_asl_useradd.rb
    activesambaldap/trunk/test/test_asl_userdel.rb
    activesambaldap/trunk/test/test_asl_usermod.rb
    activesambaldap/trunk/test/test_asl_usershow.rb
    activesambaldap/trunk/test/test_samba_encrypt.rb
    activesambaldap/trunk/test/test_user_password.rb
    svn-commit.tmp
    trunk/lib/webrick/httpservlet/svnhandler.rb
    trunk/lib/webrick/httpstatus/webdav.rb
    trunk/sample/svnserver.rb
    trunk/test/run-test.rb
    trunk/test/test_propfind.rb

  Log:


  Added: activesambaldap/

  Added: activesambaldap/branches/

  Added: activesambaldap/tags/

  Added: activesambaldap/trunk/

  Added: activesambaldap/trunk/bin/

  Added: activesambaldap/trunk/lib/

  Added: activesambaldap/trunk/lib/active_samba_ldap/

  Added: activesambaldap/trunk/lib/samba/

  Added: activesambaldap/trunk/test/

  Added: branchs/

  Added: tags/

  Added: trunk/

  Added: trunk/lib/

  Added: trunk/lib/webrick/

  Added: trunk/lib/webrick/httpservlet/

  Added: trunk/lib/webrick/httpstatus/

  Added: trunk/sample/

  Added: trunk/test/

  Added: activesambaldap/trunk/test/test_asl_usermod.rb (+550 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_usermod.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_usermod.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,550 @@
+require 'test/unit'
+require 'command_support'
+require 'asl_test_utils'
+require 'fileutils'
+require 'time'
+
+require 'active_samba_ldap'
+
+class AslUserModTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_usermod = File.join(@bin_dir, "asl-usermod")
+  end
+
+  def test_not_exist_user
+    assert_equal([false, "user 'not-exist' doesn't exist.\n"],
+                 run_asl_usermod("not-exist"))
+  end
+
+  def test_gecos
+    make_dummy_user do |user, password|
+      old_gecos = user.gecos(true)
+      new_gecos = "New gecos"
+      assert_not_equal(old_gecos, new_gecos)
+      args = ["--gecos", new_gecos]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_gecos, new_user.gecos(true))
+      assert_equal(new_gecos, new_user.description(true))
+      assert_equal(new_gecos, new_user.displayName(true))
+    end
+  end
+
+  def test_home_directory
+    make_dummy_user do |user, password|
+      old_home_directory = user.homeDirectory(true)
+      new_home_directory = "#{old_home_directory}.new"
+      args = ["--home-directory", new_home_directory]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_home_directory, new_user.homeDirectory(true))
+    end
+  end
+
+  def test_move_home_directory
+    make_dummy_user do |user, password|
+      begin
+        old_home_directory = user.homeDirectory(true)
+        new_home_directory = "#{old_home_directory}.new"
+        assert(!File.exist?(new_home_directory))
+        args = ["--home-directory", new_home_directory, "--move-home-directory"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+        new_user = @user_class.new(user.uid)
+        assert_equal(new_home_directory, new_user.homeDirectory(true))
+        assert(File.exist?(new_home_directory))
+      ensure
+        FileUtils.rm_rf(new_home_directory)
+      end
+    end
+  end
+
+  def test_rename
+    make_dummy_user do |user, password|
+      begin
+        old_uid = user.uid(true)
+        new_uid = "#{old_uid}-new"
+
+        new_user = @user_class.new(new_uid)
+        assert(!new_user.exists?)
+
+        args = ["--rename", new_uid]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+
+        old_user = @user_class.new(old_uid)
+        assert(!old_user.exists?)
+        new_user = @user_class.new(new_uid)
+        assert(new_user.exists?)
+
+        assert_equal(new_uid, new_user.uid(true))
+        assert_equal(new_uid, new_user.cn(true))
+      ensure
+        new_user = @user_class.new(new_uid)
+        begin
+          new_user.destroy
+        rescue ActiveLDAP::DeleteError
+        end
+      end
+    end
+  end
+
+  def test_uid_number
+    make_dummy_user do |user, password|
+      old_uid_number = user.uidNumber(true)
+      old_samba_sid = user.sambaSID(true)
+      new_uid_number = old_uid_number.succ
+
+      old_rid = (2 * Integer(old_uid_number) + 1000).to_s
+      new_rid = (2 * Integer(new_uid_number) + 1000).to_s
+      new_samba_sid = old_samba_sid.sub(/#{Regexp.escape(old_rid)}$/, new_rid)
+
+      args = ["--uid", new_uid_number]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_uid_number, new_user.uidNumber(true))
+      assert_equal(new_samba_sid, new_user.sambaSID(true))
+    end
+  end
+
+  def test_uid_number_non_unique
+    make_dummy_user do |user, password|
+      old_uid_number = user.uidNumber(true)
+      new_uid_number = old_uid_number.succ
+      make_dummy_user(:name => "#{user.uid}2",
+                      :uid_number => new_uid_number) do |user2, password2|
+        old_samba_sid = user.sambaSID(true)
+        old_rid = (2 * Integer(old_uid_number) + 1000).to_s
+        new_rid = (2 * Integer(new_uid_number) + 1000).to_s
+        new_samba_sid = old_samba_sid.sub(/#{Regexp.escape(old_rid)}$/, new_rid)
+
+        message = "uid number '#{new_uid_number}' already exists\n"
+        args = ["--uid", new_uid_number]
+        assert_asl_usermod_failed(user.uid, password, message, *args)
+
+        new_user = @user_class.new(user.uid)
+        assert_equal(old_uid_number, new_user.uidNumber(true))
+        assert_equal(old_samba_sid, new_user.sambaSID(true))
+      end
+    end
+  end
+
+  def test_uid_number_allow_non_unique
+    make_dummy_user do |user, password|
+      old_uid_number = user.uidNumber(true)
+      new_uid_number = old_uid_number.succ
+      make_dummy_user(:name => "#{user.uid}2",
+                      :uid_number => new_uid_number) do |user2, password2|
+        old_samba_sid = user.sambaSID(true)
+        old_rid = (2 * Integer(old_uid_number) + 1000).to_s
+        new_rid = (2 * Integer(new_uid_number) + 1000).to_s
+        new_samba_sid = old_samba_sid.sub(/#{Regexp.escape(old_rid)}$/, new_rid)
+
+        args = ["--uid", new_uid_number, "--allow-non-unique-uid"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+
+        new_user = @user_class.new(user.uid)
+        assert_equal(new_uid_number, new_user.uidNumber(true))
+        assert_equal(new_samba_sid, new_user.sambaSID(true))
+      end
+    end
+  end
+
+  def test_gid_number
+    make_dummy_group do |group|
+      make_dummy_user(:gid_number => group.gidNumber(true)) do |user, password|
+        make_dummy_group do |new_group|
+          args = ["--gid", new_group.gidNumber(true)]
+          assert_asl_usermod_successfully(user.uid, password, *args)
+
+          new_user = @user_class.new(user.uid)
+          assert_equal(new_group.gidNumber(true), new_user.gidNumber(true))
+          assert_equal(new_group.sambaSID(true),
+                       new_user.sambaPrimaryGroupSID(true))
+        end
+      end
+    end
+  end
+
+  def test_gid_number_not_exist
+    make_dummy_user do |user, password|
+      make_dummy_group do |group|
+        old_gid_number = user.gidNumber(true)
+        new_gid_number = group.gidNumber(true)
+        old_samba_primary_group_sid = user.sambaPrimaryGroupSID(true)
+
+        group.destroy
+        args = ["--gid", new_gid_number]
+        message = "gid number '#{new_gid_number}' doesn't exist\n"
+        assert_asl_usermod_failed(user.uid, password, message, *args)
+
+        new_user = @user_class.new(user.uid)
+        assert_equal(old_gid_number, new_user.gidNumber(true))
+        assert_equal(old_samba_primary_group_sid,
+                     new_user.sambaPrimaryGroupSID(true))
+      end
+    end
+  end
+
+  def test_groups
+    make_dummy_group do |group1|
+      make_dummy_group do |group2|
+        make_dummy_group do |group3|
+          new_gid_number1 = group1.gidNumber(true)
+          new_gid_number2 = group2.gidNumber(true)
+          new_gid_number3 = group3.gidNumber(true)
+          new_gid_numbers = [new_gid_number1, new_gid_number2, new_gid_number3]
+
+          make_dummy_user do |user, password|
+            old_gid_number = user.gidNumber(true)
+            old_groups = @group_class.find_all(:attribute => "memberUid",
+                                               :value => user.uid)
+
+            args = ["--groups", new_gid_numbers.join(",")]
+            assert_asl_usermod_successfully(user.uid, password, *args)
+
+
+            new_user = @user_class.new(user.uid)
+            new_groups = @group_class.find_all(:attribute => "memberUid",
+                                               :value => new_user.uid)
+            assert_equal([group1.cn(true),
+                          group2.cn(true),
+                          group3.cn(true)].sort,
+                         (new_groups - old_groups).sort)
+          end
+        end
+      end
+    end
+  end
+
+  def test_groups_no_merge
+    make_dummy_group do |group1|
+      make_dummy_group do |group2|
+        make_dummy_group do |group3|
+          new_gid_number1 = group1.gidNumber(true)
+          new_gid_number2 = group2.gidNumber(true)
+          new_gid_number3 = group3.gidNumber(true)
+          new_gid_numbers = [new_gid_number1, new_gid_number2, new_gid_number3]
+          make_dummy_user do |user, password|
+            old_gid_number = user.gidNumber(true)
+            old_groups = @group_class.find_all(:attribute => "memberUid",
+                                               :value => user.uid)
+
+            args = ["--groups", new_gid_numbers[0]]
+            assert_asl_usermod_successfully(user.uid, password, *args)
+
+            new_user = @user_class.new(user.uid)
+            new_groups = @group_class.find_all(:attribute => "memberUid",
+                                               :value => new_user.uid)
+            assert_equal([group1.cn(true)].sort,
+                         (new_groups - old_groups).sort)
+
+
+            args = ["--groups", new_gid_numbers[1..-1].join(","),
+                    "--no-merge-groups"]
+            assert_asl_usermod_successfully(user.uid, password, *args)
+
+            new_user = @user_class.new(user.uid)
+            new_groups = @group_class.find_all(:attribute => "memberUid",
+                                               :value => new_user.uid)
+            assert_equal([group2.cn(true), group3.cn(true)].sort,
+                         new_groups.sort)
+          end
+        end
+      end
+    end
+  end
+
+  def test_groups_not_exist
+    make_dummy_group do |group1|
+      make_dummy_group do |group2|
+        new_gid_number1 = group1.gidNumber(true)
+        new_gid_number2 = group2.gidNumber(true)
+        new_gid_numbers = [new_gid_number1, new_gid_number2]
+
+        group1.destroy
+        group2.destroy
+
+        make_dummy_user do |user, password|
+          old_gid_number = user.gidNumber(true)
+
+          assert(!group1.exists?)
+
+          old_groups = @group_class.find_all(:attribute => "memberUid",
+                                             :value => user.uid)
+
+          args = ["--groups", new_gid_numbers.join(",")]
+          message = "gid number '#{new_gid_numbers[0]}' doesn't exist\n"
+          assert_asl_usermod_failed(user.uid, password, message, *args)
+
+          new_user = @user_class.new(user.uid)
+          new_groups = @group_class.find_all(:attribute => "memberUid",
+                                             :value => new_user.uid)
+          assert_equal(old_groups.sort, new_groups.sort)
+        end
+      end
+    end
+  end
+
+  def test_shell
+    make_dummy_user do |user, password|
+      old_shell = user.loginShell(true)
+      new_shell = "/bin/zsh"
+
+      assert_not_equal(old_shell, new_shell)
+
+      args = ["--shell", new_shell]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_shell, new_user.loginShell(true))
+    end
+  end
+
+  def test_canonical_name
+    make_dummy_user do |user, password|
+      old_cn = user.cn(true)
+      new_cn = "new-#{new_cn}"
+
+      args = ["--canonical-name", new_cn]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_cn, new_user.cn(true))
+    end
+  end
+
+  def test_surname
+    make_dummy_user do |user, password|
+      old_sn = user.sn(true)
+      new_sn = "new-#{old_sn}"
+
+      args = ["--surname", new_sn]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_sn, new_user.sn(true))
+    end
+  end
+
+  def test_given_name
+    make_dummy_user do |user, password|
+      old_given_name = user.givenName(true)
+      new_given_name = "new-#{old_given_name}"
+
+      args = ["--given-name", new_given_name]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_given_name, new_user.givenName(true))
+    end
+  end
+
+  def test_expire_date
+    make_dummy_user do |user, password|
+      old_expire_date = user.sambaKickoffTime(true)
+      new_expire_date = Time.now + 60 * 24
+
+      unless old_expire_date.nil?
+        assert_not_equal(Time.at(old_expire_date.to_i), new_expire_date)
+      end
+
+      args = ["--expire-date", new_expire_date.iso8601]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_expire_date.to_i.to_s, new_user.sambaKickoffTime(true))
+    end
+  end
+
+  def test_can_change_password
+    make_dummy_user do |user, password|
+      unless user.can_change_password?
+        args = ["--can-change-password"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+        new_user = @user_class.new(user.uid)
+        assert(new_user.can_change_password?)
+      end
+
+      args = ["--no-can-change-password"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(!new_user.can_change_password?)
+
+      args = ["--can-change-password"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(new_user.can_change_password?)
+    end
+  end
+
+  def test_must_change_password
+    make_dummy_user do |user, password|
+      unless user.must_change_password?
+        args = ["--must-change-password"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+        new_user = @user_class.new(user.uid)
+        assert(new_user.must_change_password?)
+      end
+
+      args = ["--no-must-change-password"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(!new_user.must_change_password?)
+
+      args = ["--must-change-password"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(new_user.must_change_password?)
+    end
+  end
+
+  def test_samba_home_path
+    make_dummy_user do |user, password|
+      old_samba_home_path = user.sambaHomePath(true)
+      new_samba_home_path = "//PDC/NEW-HOME"
+
+      assert_not_equal(old_samba_home_path, new_samba_home_path)
+
+      args = ["--samba-home-path", new_samba_home_path]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_samba_home_path, new_user.sambaHomePath(true))
+    end
+  end
+
+  def test_samba_home_drive
+    make_dummy_user do |user, password|
+      old_samba_home_drive = user.sambaHomeDrive(true)
+      new_samba_home_drive = "X:"
+
+      assert_not_equal(old_samba_home_drive, new_samba_home_drive)
+
+      args = ["--samba-home-drive", new_samba_home_drive]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_samba_home_drive, new_user.sambaHomeDrive(true))
+    end
+  end
+
+  def test_samba_logon_script
+    make_dummy_user do |user, password|
+      old_samba_logon_script = user.sambaLogonScript(true)
+      new_samba_logon_script = "\\\\PDC\\scripts\\logon-new.bat"
+
+      assert_not_equal(old_samba_logon_script, new_samba_logon_script)
+
+      args = ["--samba-logon-script", new_samba_logon_script]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_samba_logon_script, new_user.sambaLogonScript(true))
+    end
+  end
+
+  def test_samba_profile_path
+    make_dummy_user do |user, password|
+      old_samba_profile_path = user.sambaProfilePath(true)
+      new_samba_profile_path = "\\\\PDC\\profiles\\new-profile"
+
+      assert_not_equal(old_samba_profile_path, new_samba_profile_path)
+
+      args = ["--samba-profile-path", new_samba_profile_path]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_samba_profile_path, new_user.sambaProfilePath(true))
+    end
+  end
+
+  def test_samba_account_flags
+    make_dummy_user do |user, password|
+      old_samba_account_flags = user.sambaAcctFlags(true)
+      new_samba_account_flags = "[UX]"
+
+      assert_not_equal(old_samba_account_flags, new_samba_account_flags)
+
+      args = ["--samba-account-flags", new_samba_account_flags]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+
+      new_user = @user_class.new(user.uid)
+      assert_equal(new_samba_account_flags, new_user.sambaAcctFlags(true))
+    end
+  end
+
+  def test_enable
+    make_dummy_user do |user, password|
+      unless user.enabled?
+        args = ["--enable-user"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+        new_user = @user_class.new(user.uid)
+        assert(new_user.enabled?)
+        assert(!new_user.disabled?)
+      end
+
+      args = ["--no-enable-user"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(!new_user.enabled?)
+      assert(new_user.disabled?)
+
+      args = ["--enable-user"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(new_user.enabled?)
+      assert(!new_user.disabled?)
+    end
+  end
+
+  def test_disable
+    make_dummy_user do |user, password|
+      unless user.disabled?
+        args = ["--disable-user"]
+        assert_asl_usermod_successfully(user.uid, password, *args)
+        new_user = @user_class.new(user.uid)
+        assert(!new_user.enabled?)
+        assert(new_user.disabled?)
+      end
+
+      args = ["--no-disable-user"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(new_user.enabled?)
+      assert(!new_user.disabled?)
+
+      args = ["--disable-user"]
+      assert_asl_usermod_successfully(user.uid, password, *args)
+      new_user = @user_class.new(user.uid)
+      assert(!new_user.enabled?)
+      assert(new_user.disabled?)
+    end
+  end
+
+  private
+  def run_asl_usermod(*other_args, &block)
+    run_ruby(*[@asl_usermod, *other_args], &block)
+  end
+
+  def assert_asl_usermod_successfully(name, password, *args)
+    args << name
+    assert_equal([true, "Enter your password: \n"],
+                 run_asl_usermod(*args) do |input, output|
+                   output.puts password
+                   output.puts password
+                 end)
+  end
+
+  def assert_asl_usermod_failed(name, password, message, *args)
+    args << name
+    assert_equal([false, "Enter your password: \n#{message}"],
+                 run_asl_usermod(*args) do |input, output|
+                   output.puts password
+                   output.puts password
+                 end)
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap.rb (+37 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,37 @@
+def require_gem_if_need(name, gem_name=nil, *options)
+  begin
+    require name
+  rescue LoadError
+    require 'rubygems'
+    gem_name ||= name
+    require_gem gem_name, *options
+  end
+end
+
+require_gem_if_need("active_record", "activerecord")
+
+require 'active_samba_ldap/version'
+begin
+  require "active_samba_ldap/config"
+rescue LoadError
+  require "active_samba_ldap/default_config"
+  module ActiveSambaLdap
+    class Config < DefaultConfig
+    end
+  end
+end
+
+require 'active_samba_ldap/base'
+require 'active_samba_ldap/populate'
+
+ActiveSambaLdap::Base.class_eval do
+  include ActiveSambaLdap::Populate
+end
+
+require 'active_samba_ldap/user'
+require 'active_samba_ldap/group'
+require 'active_samba_ldap/computer'
+require 'active_samba_ldap/idmap'
+require 'active_samba_ldap/unix_id_pool'
+require 'active_samba_ldap/ou'
+require 'active_samba_ldap/dc'

  Added: activesambaldap/trunk/lib/active_samba_ldap/user_password.rb (+50 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/user_password.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/user_password.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,50 @@
+require 'base64'
+require 'md5'
+require 'sha1'
+
+module ActiveSambaLdap
+  class UserPassword
+    class << self
+      def crypt(password, salt=nil)
+        salt ||= "$1$#{make_salt(8)}"
+        "{CRYPT}#{password.crypt(salt)}"
+      end
+
+      def md5(password)
+        "{MD5}#{Base64.encode64(MD5.md5(password).digest).chomp}"
+      end
+
+      def smd5(password, salt=nil)
+        if salt and salt.size != 4
+          raise ArgumentError.new("salt size must be == 4")
+        end
+        salt ||= make_salt(4)
+        md5_hash_with_salt = "#{MD5.md5(password + salt).digest}#{salt}"
+        "{SMD5}#{Base64.encode64(md5_hash_with_salt).chomp}"
+      end
+
+      def sha(password)
+        "{SHA}#{Base64.encode64(SHA1.sha1(password).digest).chomp}"
+      end
+
+      def ssha(password, salt=nil)
+        if salt and salt.size != 4
+          raise ArgumentError.new("salt size must be == 4")
+        end
+        salt ||= make_salt(4)
+        sha1_hash_with_salt = "#{SHA1.sha1(password + salt).digest}#{salt}"
+        "{SSHA}#{Base64.encode64(sha1_hash_with_salt).chomp}"
+      end
+
+      SALT_CHARS = ['.', '/', '0'..'9', 'A'..'Z', 'a'..'z'].collect do |x|
+        x.to_a
+      end.flatten
+
+      def make_salt(length)
+        salt = ""
+        length.times {salt << SALT_CHARS[rand(SALT_CHARS.length)]}
+        salt
+      end
+    end
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/idmap.rb (+16 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/idmap.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/idmap.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,16 @@
+module ActiveSambaLdap
+  class Idmap < Base
+    class << self
+      def ldap_mapping(options={})
+        Config.required_variables :idmap_prefix
+        default_options = {
+          :dnattr => "sambaSID",
+          :prefix => Config.idmap_prefix,
+          :classes => ["top", "sambaIdmapEntry"],
+        }
+        options = default_options.merge(options)
+        super options
+      end
+    end
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/command.rb (+43 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/command.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/command.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,43 @@
+require 'optparse'
+require 'ostruct'
+
+module ActiveSambaLdap
+  module Command
+    module_function
+    def parse_options(argv=nil)
+      argv ||= ARGV.dup
+      options = OpenStruct.new
+      opts = OptionParser.new do |opts|
+        yield(opts, options)
+
+        opts.separator ""
+        opts.separator "Common options:"
+
+        opts.on_tail("--config=CONFIG", "Specify configuration file") do |file|
+          DefaultConfig::FILES << file
+        end
+
+        opts.on_tail("-h", "--help", "Show this message") do
+          puts opts
+          exit
+        end
+
+        opts.on_tail("--version", "Show version") do
+          puts VERSION
+          exit
+        end
+      end
+      opts.parse!(argv)
+      [argv, opts, options]
+    end
+
+    def read_password(prompt, input=STDIN, output=STDOUT)
+      output.print prompt
+      system "/bin/stty -echo" if input.tty?
+      password = input.gets.chomp
+      system "/bin/stty echo" if input.tty?
+      output.puts
+      password
+    end
+  end
+end

  Property changed: activesambaldap/trunk/bin/asl-groupshow (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-groupshow (+29 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-groupshow	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-groupshow	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  opts.banner += " GROUP_NAME"
+end
+
+name = nil
+if argv.size == 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+ActiveSambaLdap::Base.establish_connection({}, true)
+
+class Group < ActiveSambaLdap::Group
+  ldap_mapping
+end
+
+group = Group.new(name)
+unless group.exists?
+  puts "group '#{name}' doesn't exist."
+  exit 1
+end
+puts group

  Property changed: activesambaldap/trunk/bin/asl-groupdel (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-groupdel (+58 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-groupdel	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-groupdel	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,58 @@
+#!/usr/bin/env ruby
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  options.force = false
+
+  opts.banner += " GROUP_NAME"
+
+  opts.on("-f", "--[no-]force",
+          "force delete group (#{options.force})") {|options.force|}
+end
+
+name = nil
+if argv.size == 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+unless Process.uid.zero?
+  puts "need root authority."
+  exit 1
+end
+
+ActiveSambaLdap::Base.establish_connection({}, false)
+
+class Group < ActiveSambaLdap::Group
+  ldap_mapping
+end
+
+class User < ActiveSambaLdap::User
+  ldap_mapping
+end
+
+class Computer < ActiveSambaLdap::Computer
+  ldap_mapping
+end
+
+group = Group.new(name)
+unless group.exists?
+  puts "group '#{name}' doesn't exist."
+  exit 1
+end
+
+begin
+  group.destroy(:remove_members => true,
+                :force_change_primary_members => options.force)
+rescue ActiveSambaLdap::Error
+  puts $!
+  exit 1
+end
+
+ActiveSambaLdap::Base.restart_nscd
+
+ActiveSambaLdap::Base.close

  Added: trunk/test/test_propfind.rb (+75 -0)
===================================================================
--- trunk/test/test_propfind.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ trunk/test/test_propfind.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,75 @@
+require "test/unit"
+
+require "stringio"
+require "webrick"
+require "webrick/httpservlet/svnhandler"
+
+class SvnServletPROPFINDTest < Test::Unit::TestCase
+  def handler
+    config = {:Logger => WEBrick::Log.new}
+    repos_path = "#{File.dirname(__FILE__)}/tmp/repos"
+    WEBrick::HTTPServlet::SvnHandler.new(config, repos_path)
+  end
+
+  def make_propfind_request_body(dav_props, svn_props)
+    default_dav_props = ["version-controlled-configuration",
+                         "resourcetype"]
+    default_svn_props = ["baseline-relative-path",
+                         "repository-uuid"],
+
+    dav_props = (dav_props || []) | default_dav_props
+    svn_props = (svn_props || []) | default_svn_props
+
+    dav_props = dav_props.collect {|prop| "<#{prop}/>"}.join("\n")
+    svn_ns = ' xmlns="http://subversion.tigris.org/xmlns/dav/"'
+    svn_props = svn_props.collect {|prop| "<#{prop}#{svn_ns}/>"}.join("\n")
+    <<-EOX
+<?xml version="1.0" encoding="utf-8"?>
+<propfind xmlns="DAV:">
+  <prop>
+#{dav_props}
+#{svn_props}
+  </prop>
+</propfind>
+EOX
+  end
+
+  def make_propfind_request(path, props=nil, depth=0)
+    props ||= {}
+    body = make_propfind_request_body(props[:dav], props[:svn])
+    depth = "Depth: #{depth}" if depth
+    header = <<-EOR
+PROPFIND #{path} HTTP/1.1
+Host: localhost:10080
+User-Agent: SVN/1.4.0 (r21228) neon/0.25.5
+Content-Length: #{body.length}
+Content-Type: text/xml
+#{depth}
+EOR
+    StringIO.new("#{header.strip}\r\n\r\n#{body}")
+  end
+
+  def propfind(path, props=nil, depth=0)
+    req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
+    req.parse(make_propfind_request(path, props, depth))
+    res = WEBrick::HTTPResponse.new(WEBrick::Config::HTTP)
+    handler.do_PROPFIND(req, res)
+    res
+  end
+
+  def test_depth
+    assert_raises(WEBrick::HTTPStatus::Forbidden) do
+      propfind("/", nil, nil)
+    end
+    assert_raises(WEBrick::HTTPStatus::Forbidden) do
+      propfind("/", nil, "infinity")
+    end
+    assert_raises(WEBrick::HTTPStatus::BadRequest) do
+      propfind("/", nil, "NOT-A-NUMBER")
+    end
+
+    assert_nothing_raised do
+      propfind("/")
+    end
+  end
+end

  Added: trunk/lib/webrick/httpservlet/svnhandler.rb (+40 -0)
===================================================================
--- trunk/lib/webrick/httpservlet/svnhandler.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ trunk/lib/webrick/httpservlet/svnhandler.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,40 @@
+require 'svn/repos'
+
+require 'webrick/httpstatus/webdav'
+require 'webrick/httpservlet/abstract'
+
+module WEBrick
+  module HTTPServlet
+    class SvnHandler < AbstractServlet
+      def initialize(server, repos_path, options={}, default={})
+        super(server, options)
+        @repos_path = File.expand_path(repos_path)
+      end
+
+      def do_PROPFIND(req, res)
+        depth = normalize_depth(req, res)
+        check_infinite_depth(req, res, depth)
+      end
+
+      private
+      def normalize_depth(req, res)
+        depth = req['Depth']
+        @logger.debug "propfind request depth=#{depth}"
+        depth = nil if depth == "infinity"
+        begin
+          depth = Integer(depth) if depth
+        rescue ArgumentError
+          res.body = "invalid Depth value: #{depth}"
+          raise HTTPStatus::BadRequest
+        end
+        depth
+      end
+
+      def check_infinite_depth(req, res, depth)
+        if depth.nil?
+          raise HTTPStatus::Forbidden
+        end
+      end
+    end
+  end
+end

  Added: activesambaldap/trunk/test/test_asl_userdel.rb (+61 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_userdel.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_userdel.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,61 @@
+require 'test/unit'
+require 'command_support'
+require 'fileutils'
+
+require 'active_samba_ldap'
+
+class AslUserDelTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_userdel = File.join(@bin_dir, "asl-userdel")
+  end
+
+  def test_run_as_normal_user
+    assert_equal([false, "need root authority.\n"],
+                 run_asl_userdel_as_normal_user("user-name"))
+  end
+
+  def test_not_exist_user
+    assert_equal([false, "user 'not-exist' doesn't exist.\n"],
+                 run_asl_userdel("not-exist"))
+  end
+
+  def test_exist_user
+    make_dummy_user do |user, password|
+      assert(File.exist?(user.homeDirectory(true)))
+      assert_equal([true, ""], run_asl_userdel(user.uid))
+      assert(File.exist?(user.homeDirectory(true)))
+    end
+  end
+
+  def test_belong_to_group
+    make_dummy_user do |user, password|
+      assert(File.exist?(user.homeDirectory(true)))
+      make_dummy_group do |group|
+        group.add_member(user)
+        assert_equal([true, ""], run_asl_userdel(user.uid))
+      end
+      assert(File.exist?(user.homeDirectory(true)))
+    end
+  end
+
+  def test_remove_home_directory
+    make_dummy_user do |user, password|
+      assert(File.exist?(user.homeDirectory(true)))
+      assert_equal([true, ""], run_asl_userdel("-r", user.uid))
+      assert(!File.exist?(user.homeDirectory(true)))
+    end
+  end
+
+  private
+  def run_asl_userdel(*other_args, &block)
+    run_ruby_with_fakeroot(*[@asl_userdel, *other_args], &block)
+  end
+
+  def run_asl_userdel_as_normal_user(*other_args, &block)
+    run_ruby(*[@asl_userdel, *other_args], &block)
+  end
+end

  Added: activesambaldap/trunk/test/test_asl_useradd.rb (+699 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_useradd.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_useradd.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,699 @@
+require 'test/unit'
+require 'command_support'
+require 'asl_test_utils'
+require 'fileutils'
+require 'time'
+require 'tmpdir'
+
+require 'active_samba_ldap'
+
+class AslUserAddTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_useradd = File.join(@bin_dir, "asl-useradd")
+  end
+
+  def test_run_as_normal_user
+    assert_equal([false, "need root authority.\n"],
+                 run_asl_useradd_as_normal_user("user-name"))
+    assert_equal([false, "need root authority.\n"],
+                 run_asl_useradd_as_normal_user("computer-name$",
+                                                "--computer-account"))
+  end
+
+  def test_exist_user
+    make_dummy_user do |user, password|
+      assert(@user_class.new(user.uid).exists?)
+      assert_equal([false, "user '#{user.uid}' already exists.\n"],
+                   run_asl_useradd(user.uid(true)))
+      assert(@user_class.new(user.uid).exists?)
+    end
+  end
+
+  def test_exist_computer
+    make_dummy_computer do |computer, password|
+      uid = computer.uid(true)
+      assert(@computer_class.new(uid).exists?)
+      assert_equal([false, "computer '#{uid}' already exists.\n"],
+                   run_asl_useradd(uid, "--computer-account"))
+      assert(@computer_class.new(uid).exists?)
+    end
+  end
+
+  def test_add_user
+    ensure_delete_user("test-user") do |uid,|
+      assert_asl_useradd_successfully(uid)
+    end
+  end
+
+  def test_add_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      assert_asl_useradd_successfully(uid, "--computer-account")
+    end
+
+    ensure_delete_computer("test-computer") do |uid,|
+      assert_asl_useradd_successfully(uid, "--computer-account")
+    end
+  end
+
+  def test_ou_user
+    ensure_delete_ou("SubOu") do |ou|
+      ou_class = Class.new(ActiveSambaLdap::Ou)
+      ou_class.ldap_mapping :prefix => @user_class.prefix
+      assert(!ou_class.new(ou).exists?)
+
+      ensure_delete_user("test-user") do |uid,|
+        user_class = Class.new(ActiveSambaLdap::User)
+        user_class.ldap_mapping :prefix => "ou=#{ou},#{@user_class.prefix}"
+        assert_raises(ActiveLDAP::ConnectionError) do
+          user_class.new(name)
+        end
+
+        assert_equal([true, ""], run_asl_useradd(uid, "--ou", ou))
+        user = user_class.new(uid)
+        assert(user.exists?)
+
+        assert_match(/\Auid=#{uid},ou=#{ou},/, user.dn)
+      end
+
+      assert(ou_class.new(ou).exists?)
+    end
+  end
+
+  def test_ou_computer
+    ensure_delete_ou("SubOu") do |ou|
+      ou_class = Class.new(ActiveSambaLdap::Ou)
+      ou_class.ldap_mapping :prefix => @computer_class.prefix
+      assert(!ou_class.new(ou).exists?)
+
+      ensure_delete_computer("test-computer$") do |uid,|
+        computer_class = Class.new(ActiveSambaLdap::Computer)
+        computer_class.ldap_mapping :prefix =>
+                                    "ou=#{ou},#{@computer_class.prefix}"
+        assert_raises(ActiveLDAP::ConnectionError) do
+          computer_class.new(name)
+        end
+
+        assert_equal([true, ""], run_asl_useradd(uid, "--computer-account",
+                                                 "--ou", ou))
+        computer = computer_class.new(uid)
+        assert(computer.exists?)
+
+        assert_match(/\Auid=#{Regexp.escape(uid)},ou=#{ou},/, computer.dn)
+      end
+
+      assert(ou_class.new(ou).exists?)
+    end
+  end
+
+  def test_uid_number
+    ensure_delete_user("test-user") do |uid,|
+      uid_number = Integer(next_uid_number) + 10
+      assert_asl_useradd_successfully(uid, "--uid", uid_number)
+      user = @user_class.new(uid)
+      assert_equal(uid_number, user.uidNumber(true).to_i)
+    end
+
+    ensure_delete_computer("test-computer$") do |uid,|
+      uid_number = Integer(next_uid_number) + 10
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--uid", uid_number)
+      computer = @computer_class.new(uid)
+      assert_equal(uid_number, computer.uidNumber(true).to_i)
+    end
+  end
+
+  def test_gid_number
+    make_dummy_group("test-group") do |group|
+      gid_number = group.gidNumber(true)
+
+      ensure_delete_user("test-user") do |uid,|
+        assert_asl_useradd_successfully(uid, "--gid", gid_number)
+        user = @user_class.new(uid)
+        assert_equal(gid_number, user.gidNumber(true))
+      end
+
+      ensure_delete_computer("test-computer") do |uid,|
+        assert_asl_useradd_successfully(uid, "--computer-account",
+                                        "--gid", gid_number)
+        computer = @computer_class.new(uid)
+        assert_equal(gid_number, computer.gidNumber(true))
+      end
+    end
+  end
+
+  def test_groups
+    make_dummy_group do |group1|
+      make_dummy_group do |group2|
+        make_dummy_group do |group3|
+          gid_numbers = [group1.gidNumber(true),
+                         group2.gidNumber(true),
+                         group3.gidNumber(true)]
+
+          ensure_delete_user("test-user") do |uid,|
+            args = ["--groups", gid_numbers.join(",")]
+            assert_asl_useradd_successfully(uid, *args)
+
+            user = @user_class.new(uid)
+            primary_group = @group_class.find(:attribute => "gidNumber",
+                                              :value => user.gidNumber)
+            groups = @group_class.find_all(:attribute => "memberUid",
+                                           :value => uid)
+            assert_equal([primary_group,
+                          group1.cn(true),
+                          group2.cn(true),
+                          group3.cn(true)].sort,
+                         groups.sort)
+          end
+
+          ensure_delete_computer("test-computer$") do |uid,|
+            args = ["--computer-account", "--groups", gid_numbers.join(",")]
+            assert_asl_useradd_successfully(uid, *args)
+
+            computer = @computer_class.new(uid)
+            primary_group = @group_class.find(:attribute => "gidNumber",
+                                              :value => computer.gidNumber)
+            groups = @group_class.find_all(:attribute => "memberUid",
+                                           :value => uid)
+            assert_equal([primary_group,
+                          group1.cn(true),
+                          group2.cn(true),
+                          group3.cn(true)].sort,
+                         groups.sort)
+          end
+        end
+      end
+    end
+  end
+
+  def test_create_group_user
+    ensure_delete_group("test-user") do |gid,|
+      ensure_delete_user("test-user") do |uid,|
+        assert_equal(gid, uid)
+        assert(!@group_class.new(gid).exists?)
+        assert_asl_useradd_successfully(uid, "--create-group")
+        group = @group_class.new(gid)
+        assert(group.exists?)
+
+        user = @user_class.new(uid)
+        assert_equal(group.gidNumber, user.gidNumber)
+      end
+    end
+  end
+
+  def test_create_group_computer
+    ensure_delete_group("test-computer") do |gid,|
+      ensure_delete_computer("test-computer$") do |uid,|
+        assert_equal("#{gid}$", uid)
+        assert(!@group_class.new(gid).exists?)
+        assert_asl_useradd_successfully(uid, "--create-group",
+                                        "--computer-account")
+        group = @group_class.new(gid)
+        assert(group.exists?)
+
+        computer = @computer_class.new(uid)
+        assert_equal(group.gidNumber, computer.gidNumber)
+      end
+    end
+  end
+
+  def test_comment_user
+    ensure_delete_user("test-user") do |uid,|
+      gecos = "gecos for the user #{uid}"
+      assert_asl_useradd_successfully(uid, "--comment", gecos)
+      user = @user_class.new(uid)
+      assert_equal(gecos, user.gecos(true))
+    end
+  end
+
+  def test_comment_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      gecos = "gecos for the computer #{uid}"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--comment", gecos)
+      computer = @computer_class.new(uid)
+      assert_equal(gecos, computer.gecos(true))
+    end
+  end
+
+  def test_shell_user
+    ensure_delete_user("test-user") do |uid,|
+      shell = "/bin/zsh"
+      assert_asl_useradd_successfully(uid, "--shell", shell)
+      user = @user_class.new(uid)
+      assert_equal(shell, user.loginShell(true))
+    end
+  end
+
+  def test_shell_computer
+    ensure_delete_computer("test-computer") do |uid,|
+      shell = "/bin/zsh"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--shell", shell)
+      computer = @computer_class.new(uid)
+      assert_equal(shell, computer.loginShell(true))
+    end
+  end
+
+  def test_canonical_name_user
+    ensure_delete_user("test-user") do |uid,|
+      cn = "John Kennedy"
+      assert_asl_useradd_successfully(uid, "--canonical-name", cn)
+      user = @user_class.new(uid)
+      assert_equal(uid, user.givenName(true))
+      assert_equal(uid, user.surname(true))
+      assert_equal(cn, user.cn(true))
+    end
+  end
+
+  def test_canonical_name_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      cn = "A computer"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--canonical-name", cn)
+      computer = @computer_class.new(uid)
+      assert_equal(uid, computer.givenName(true))
+      assert_equal(uid, computer.surname(true))
+      assert_equal(cn, computer.cn(true))
+    end
+  end
+
+  def test_given_name_user
+    ensure_delete_user("test-user") do |uid,|
+      given_name = "John"
+      assert_asl_useradd_successfully(uid, "--given-name", given_name)
+      user = @user_class.new(uid)
+      assert_equal(given_name, user.givenName(true))
+      assert_equal(uid, user.cn(true))
+    end
+  end
+
+  def test_given_name_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      given_name = "John"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--given-name", given_name)
+      computer = @computer_class.new(uid)
+      assert_equal(given_name, computer.givenName(true))
+      assert_equal(uid, computer.cn(true))
+    end
+  end
+
+  def test_surname_user
+    ensure_delete_user("test-user") do |uid,|
+      surname = "Kennedy"
+      assert_asl_useradd_successfully(uid, "--surname", surname)
+      user = @user_class.new(uid)
+      assert_equal(surname, user.surname(true))
+      assert_equal(uid, user.cn(true))
+    end
+  end
+
+  def test_surname_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      surname = "Kennedy"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--surname", surname)
+      computer = @computer_class.new(uid)
+      assert_equal(surname, computer.surname(true))
+      assert_equal(uid, computer.cn(true))
+    end
+  end
+
+  def test_given_name_and_surname_user
+    ensure_delete_user("test-user") do |uid,|
+      given_name = "John"
+      surname = "Kennedy"
+      assert_asl_useradd_successfully(uid,
+                                      "--given-name", given_name,
+                                      "--surname", surname)
+      user = @user_class.new(uid)
+      assert_equal(given_name, user.givenName(true))
+      assert_equal(surname, user.surname(true))
+      assert_equal("#{given_name} #{surname}", user.cn(true))
+    end
+  end
+
+  def test_given_name_and_surname_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      given_name = "John"
+      surname = "Kennedy"
+      assert_asl_useradd_successfully(uid,
+                                      "--computer-account",
+                                      "--given-name", given_name,
+                                      "--surname", surname)
+      computer = @computer_class.new(uid)
+      assert_equal(given_name, computer.givenName(true))
+      assert_equal(surname, computer.surname(true))
+      assert_equal("#{given_name} #{surname}", computer.cn(true))
+    end
+  end
+
+  def test_home_directory_user
+    ensure_delete_user("test-user") do |uid,|
+      home_directory = "/tmp/#{File.basename(__FILE__)}.#{Process.pid}"
+      begin
+        assert_asl_useradd_successfully(uid,
+                                        "--home-directory", home_directory,
+                                        "--setup-home-directory")
+        assert(File.exist?(home_directory))
+      ensure
+        FileUtils.rm_rf(home_directory)
+      end
+    end
+  end
+
+  def test_home_directory_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      home_directory = "/tmp/#{File.basename(__FILE__)}.#{Process.pid}"
+      begin
+        assert_asl_useradd_successfully(uid,
+                                        "--computer-account",
+                                        "--home-directory", home_directory,
+                                        "--setup-home-directory")
+        assert(File.exist?(home_directory))
+      ensure
+        FileUtils.rm_rf(home_directory)
+      end
+    end
+  end
+
+  def test_skeleton_directory
+    home_directory = File.join(Dir.tmpdir,
+                               "#{File.basename(__FILE__)}.#{Process.pid}")
+    skeleton_directory = "#{home_directory}.skel"
+    begin
+      normal_file = "hello"
+      dot_file = ".hello"
+      deep_file = File.join("dir", "hello")
+      FileUtils.touch([normal_file, dot_file, deep_file].collect do |f|
+                        target = File.join(skeleton_directory, f)
+                        FileUtils.mkdir_p(File.dirname(target))
+                        target
+                      end)
+
+      ensure_delete_user("test-user") do |uid,|
+        assert_asl_useradd_successfully(uid,
+                                        "--home-directory", home_directory,
+                                        "--skeleton-directory",
+                                        skeleton_directory,
+                                        "--setup-home-directory")
+        assert(File.exist?(home_directory))
+        assert(File.exist?(File.join(home_directory, normal_file)))
+        assert(File.exist?(File.join(home_directory, dot_file)))
+        assert(File.exist?(File.join(home_directory, deep_file)))
+      end
+
+      ensure_delete_computer("test-computer$") do |uid,|
+        assert_asl_useradd_successfully(uid,
+                                        "--computer-account",
+                                        "--home-directory", home_directory,
+                                        "--skeleton-directory",
+                                        skeleton_directory,
+                                        "--setup-home-directory")
+        assert(File.exist?(home_directory))
+        assert(File.exist?(File.join(home_directory, normal_file)))
+        assert(File.exist?(File.join(home_directory, dot_file)))
+        assert(File.exist?(File.join(home_directory, deep_file)))
+      end
+    ensure
+      FileUtils.rm_rf([home_directory, skeleton_directory])
+    end
+  end
+
+  def test_expire_date_user
+    ensure_delete_user("test-user") do |uid,|
+      expire_date = Time.now + 60 * 24
+      assert_asl_useradd_successfully(uid, "--expire-date", expire_date.iso8601)
+      user = @user_class.new(uid)
+      assert_equal(expire_date.to_i.to_s, user.sambaKickoffTime(true))
+    end
+  end
+
+  def test_expire_date_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      expire_date = Time.now + 60 * 24
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--expire-date", expire_date.iso8601)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaKickoffTime(true))
+    end
+  end
+
+  def test_can_change_password_user
+    ensure_delete_user("test-user") do |uid,|
+      assert_asl_useradd_successfully(uid, "--can-change-password")
+      user = @user_class.new(uid)
+      assert(user.can_change_password?)
+    end
+  end
+
+  def test_can_change_password_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      ensure_delete_computer("test-computer2$") do |uid2,|
+        assert_asl_useradd_successfully(uid, "--computer-account")
+        assert_asl_useradd_successfully(uid2, "--computer-account",
+                                        "--can-change-password")
+        computer = @computer_class.new(uid)
+        computer2 = @computer_class.new(uid2)
+        assert_equal(computer.can_change_password?,
+                     computer2.can_change_password?)
+      end
+    end
+  end
+
+  def test_no_can_change_password_user
+    ensure_delete_user("test-user") do |uid,|
+      assert_asl_useradd_successfully(uid, "--no-can-change-password")
+      user = @user_class.new(uid)
+      assert(!user.can_change_password?)
+    end
+  end
+
+  def test_no_can_change_password_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      ensure_delete_computer("test-computer2$") do |uid2,|
+        assert_asl_useradd_successfully(uid, "--computer-account")
+        assert_asl_useradd_successfully(uid2, "--computer-account",
+                                        "--no-can-change-password")
+        computer = @computer_class.new(uid)
+        computer2 = @computer_class.new(uid2)
+        assert_equal(computer.can_change_password?,
+                     computer2.can_change_password?)
+      end
+    end
+  end
+
+  def test_must_change_password_user
+    ensure_delete_user("test-user") do |uid,|
+      assert_asl_useradd_successfully(uid, "--must-change-password")
+      user = @user_class.new(uid)
+      assert(user.must_change_password?)
+    end
+  end
+
+  def test_must_change_password_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      ensure_delete_computer("test-computer2$") do |uid2,|
+        assert_asl_useradd_successfully(uid, "--computer-account")
+        assert_asl_useradd_successfully(uid2, "--computer-account",
+                                        "--must-change-password")
+        computer = @computer_class.new(uid)
+        computer2 = @computer_class.new(uid2)
+        assert_equal(computer.must_change_password?,
+                     computer2.must_change_password?)
+      end
+    end
+  end
+
+  def test_no_must_change_password_user
+    ensure_delete_user("test-user") do |uid,|
+      assert_asl_useradd_successfully(uid, "--no-must-change-password")
+      user = @user_class.new(uid)
+      assert(!user.must_change_password?)
+    end
+  end
+
+  def test_no_must_change_password_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      ensure_delete_computer("test-computer2$") do |uid2,|
+        assert_asl_useradd_successfully(uid, "--computer-account")
+        assert_asl_useradd_successfully(uid2, "--computer-account",
+                                        "--no-must-change-password")
+        computer = @computer_class.new(uid)
+        computer2 = @computer_class.new(uid2)
+        assert_equal(computer.must_change_password?,
+                     computer2.must_change_password?)
+      end
+    end
+  end
+
+  def test_samba_home_path_user
+    ensure_delete_user("test-user") do |uid,|
+      home_path = "\\\\ANYWHERE\\here\\%U"
+      assert_asl_useradd_successfully(uid, "--samba-home-path", home_path)
+      user = @user_class.new(uid)
+      assert_equal(home_path.gsub(/%U/, uid), user.sambaHomePath(true))
+    end
+  end
+
+  def test_samba_home_path_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      home_path = "\\\\ANYWHERE\\here\\%U"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-home-path", home_path)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaHomePath(true))
+    end
+  end
+
+  def test_samba_home_drive_user
+    ensure_delete_user("test-user") do |uid,|
+      home_drive = "X:"
+      assert_asl_useradd_successfully(uid, "--samba-home-drive", home_drive)
+      user = @user_class.new(uid)
+      assert_equal(home_drive, user.sambaHomeDrive(true))
+    end
+  end
+
+  def test_samba_home_drive_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      home_drive = "X:"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-home-drive", home_drive)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaHomeDrive(true))
+    end
+  end
+
+  def test_samba_home_drive_abbrev_user
+    ensure_delete_user("test-user") do |uid,|
+      home_drive = "X"
+      assert_asl_useradd_successfully(uid, "--samba-home-drive", home_drive)
+      user = @user_class.new(uid)
+      assert_equal("#{home_drive}:", user.sambaHomeDrive(true))
+    end
+  end
+
+  def test_samba_home_drive_abbrev_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      home_drive = "X"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-home-drive", home_drive)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaHomeDrive(true))
+    end
+  end
+
+  def test_samba_logon_script_user
+    ensure_delete_user("test-user") do |uid,|
+      script = "%U-logon.bat"
+      assert_asl_useradd_successfully(uid, "--samba-logon-script", script)
+      user = @user_class.new(uid)
+      assert_equal(script.gsub(/%U/, uid), user.sambaLogonScript(true))
+    end
+  end
+
+  def test_samba_logon_script_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      script = "%U-logon.bat"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-logon-script", script)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaLogonScript(true))
+    end
+  end
+
+  def test_samba_profile_path_user
+    ensure_delete_user("test-user") do |uid,|
+      profile = "\\\\ANYWHERE\\profiles\\profile-%U"
+      assert_asl_useradd_successfully(uid, "--samba-profile-path", profile)
+      user = @user_class.new(uid)
+      assert_equal(profile.gsub(/%U/, uid), user.sambaProfilePath(true))
+    end
+  end
+
+  def test_samba_profile_path_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      profile = "\\\\ANYWHERE\\profiles\\profile-%U"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-profile-path", profile)
+      computer = @computer_class.new(uid)
+      assert_nil(computer.sambaProfilePath(true))
+    end
+  end
+
+  def test_samba_account_flags_user
+    ensure_delete_user("test-user") do |uid,|
+      flags = "[UX]"
+      assert_asl_useradd_successfully(uid, "--samba-account-flags", flags)
+      user = @user_class.new(uid)
+      assert_equal(flags, user.sambaAcctFlags(true))
+    end
+  end
+
+  def test_samba_account_flags_computer
+    ensure_delete_computer("test-computer$") do |uid,|
+      flags = "[WX]"
+      assert_asl_useradd_successfully(uid, "--computer-account",
+                                      "--samba-account-flags", flags)
+      computer = @computer_class.new(uid)
+      assert_equal(flags, computer.sambaAcctFlags(true))
+    end
+  end
+
+  private
+  def run_asl_useradd(*other_args, &block)
+    other_args = prepare_args(other_args)
+    run_ruby_with_fakeroot(*[@asl_useradd, *other_args], &block)
+  end
+
+  def run_asl_useradd_as_normal_user(*other_args, &block)
+    other_args = prepare_args(other_args)
+    run_ruby(*[@asl_useradd, *other_args], &block)
+  end
+
+  def prepare_args(args)
+    args = args.dup
+    if args.grep(/\A--(no-)?setup-home-directory\z/).empty?
+      args << "--no-setup-home-directory"
+    end
+    if args.grep(/\A--(no-)?create-group\z/).empty?
+      args << "--no-create-group"
+    end
+    args
+  end
+
+  def assert_asl_useradd_successfully(name, *args)
+    _wrap_assertion do
+      if args.grep(/\A--computer-account\z/).empty?
+        member_class = @user_class
+      else
+        name = name.sub(/\$*\z/, '') + "$"
+        member_class = @computer_class
+      end
+      assert(!member_class.new(name).exists?)
+      args << name
+      assert_equal([true, ""], run_asl_useradd(*args))
+      assert(member_class.new(name).exists?)
+    end
+  end
+
+  def assert_asl_useradd_failed(name, message, *args)
+    _wrap_assertion do
+      if args.grep(/\A--computer-account\z/).empty?
+        member_class = @user_class
+      else
+        name = name.sub(/\$*\z/, '') + "$"
+        member_class = @computer_class
+      end
+      assert(!member_class.new(name).exists?)
+      args << name
+      assert_equal([false, message], run_asl_useradd(*args))
+      assert(!member_class.new(name).exists?)
+    end
+  end
+end

  Property changed: activesambaldap/trunk/bin/asl-groupadd (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-groupadd (+62 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-groupadd	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-groupadd	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  options.gid = nil
+  options.group_type = "domain"
+  options.print_gid_number = false
+
+  opts.banner += " GROUP_NAME"
+
+  opts.on("-g", "--gid=GID", Integer, "GID number") {|options.gid|}
+  opts.on("-t", "--type=TYPE",
+          "group type (#{options.group_type})") {|options.group_type|}
+  opts.on("-p", "--[no-]print-gid-number",
+          "print the gid number to stdout",
+          "(#{options.print_gid_number})") {|options.print_gid_number|}
+end
+
+name = nil
+if argv.size == 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+unless Process.uid.zero?
+  puts "need root authority."
+  exit 1
+end
+
+ActiveSambaLdap::Base.establish_connection({}, true)
+
+class Group < ActiveSambaLdap::Group
+  ldap_mapping
+end
+
+class UnixIdPool < ActiveSambaLdap::UnixIdPool
+  ldap_mapping
+end
+
+if Group.exists?(name)
+  puts "group '#{name}' already exists."
+  exit 1
+end
+
+create_options = {
+  :gid_number => options.gid,
+  :pool_class => UnixIdPool,
+  :group_type => options.group_type,
+}
+group = Group.create(name, create_options)
+
+if options.print_gid_number
+  puts group.gidNumber(true)
+end
+
+ActiveSambaLdap::Base.restart_nscd
+
+ActiveSambaLdap::Base.close

  Added: activesambaldap/trunk/test/test_asl_populate.rb (+105 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_populate.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_populate.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,105 @@
+require 'test/unit'
+require 'command_support'
+require 'asl_test_utils'
+require 'fileutils'
+require 'time'
+
+require 'active_samba_ldap'
+
+class AslPopulateTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_populate = File.join(@bin_dir, "asl-populate")
+  end
+
+  def test_run_as_normal_user
+    assert_equal([false, "need root authority.\n"],
+                 run_asl_populate_as_normal_user)
+  end
+
+  def test_populate
+    ActiveSambaLdap::Base.destroy_all
+    assert_equal([], ActiveSambaLdap::Base.search)
+    assert_asl_populate_successfully("Administrator")
+    base = ActiveSambaLdap::Base.base
+
+    results = ActiveSambaLdap::Base.search
+
+    users_prefix = ActiveSambaLdap::Config.users_prefix
+    groups_prefix = ActiveSambaLdap::Config.groups_prefix
+    computers_prefix = ActiveSambaLdap::Config.computers_prefix
+    idmap_prefix = ActiveSambaLdap::Config.idmap_prefix
+    domain = ActiveSambaLdap::Config.samba_domain
+    assert_equal([
+                  nil,
+                  users_prefix,
+                  groups_prefix,
+                  computers_prefix,
+                  idmap_prefix,
+                  "sambaDomainName=#{domain}",
+                  "uid=Administrator,#{users_prefix}",
+                  "uid=Guest,#{users_prefix}",
+                  "cn=Users,#{groups_prefix}",
+                  "cn=Account Operators,#{groups_prefix}",
+                  "cn=Administrators,#{groups_prefix}",
+                  "cn=Backup Operators,#{groups_prefix}",
+                  "cn=Domain Admins,#{groups_prefix}",
+                  "cn=Domain Computers,#{groups_prefix}",
+                  "cn=Domain Guests,#{groups_prefix}",
+                  "cn=Domain Users,#{groups_prefix}",
+                  "cn=Guests,#{groups_prefix}",
+                  "cn=Power Users,#{groups_prefix}",
+                  "cn=Print Operators,#{groups_prefix}",
+                  "cn=Replicators,#{groups_prefix}",
+                  "cn=System Operators,#{groups_prefix}",
+                 ].collect {|x| [x, base].compact.join(",")}.sort,
+                 results.collect {|result| result["dn"][0]}.sort)
+  end
+
+  def test_wrong_password
+    ActiveSambaLdap::Base.destroy_all
+    assert_asl_populate_miss_match_password
+  end
+
+  private
+  def run_asl_populate(*other_args, &block)
+    run_ruby_with_fakeroot(*[@asl_populate, *other_args], &block)
+  end
+
+  def run_asl_populate_as_normal_user(*other_args, &block)
+    run_ruby(*[@asl_populate, *other_args], &block)
+  end
+
+  def assert_asl_populate_successfully(password, name=nil, *args)
+    name ||= ActiveSambaLdap::User::DOMAIN_ADMIN_NAME
+    assert_equal([true,
+                  [
+                   "Password for #{name}: ",
+                   "Retype password for #{name}: ",
+                  ].join("\n") + "\n",
+                 ],
+                 run_asl_populate(*args) do |input, output|
+                   output.puts(password)
+                   output.puts(password)
+                 end)
+  end
+
+  def assert_asl_populate_miss_match_password(name=nil, *args)
+    name ||= ActiveSambaLdap::User::DOMAIN_ADMIN_NAME
+    password = "password"
+    assert_equal([false,
+                  [
+                   "Password for #{name}: ",
+                   "Retype password for #{name}: ",
+                   "Passwords don't match.",
+                  ].join("\n") + "\n",
+                 ],
+                 run_asl_populate(*args) do |input, output|
+                   output.puts(password)
+                   output.puts(password + password.reverse)
+                 end)
+  end
+end

  Added: activesambaldap/trunk/test/test_asl_groupshow.rb (+31 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_groupshow.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_groupshow.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,31 @@
+require 'test/unit'
+require 'command_support'
+require 'fileutils'
+
+require 'active_samba_ldap'
+
+class AslGroupShowTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_groupshow = File.join(@bin_dir, "asl-groupshow")
+  end
+
+  def test_exist_group
+    make_dummy_group do |group|
+      assert_equal([true, group.to_s], run_asl_groupshow(group.cn(true)))
+    end
+  end
+
+  def test_not_exist_group
+    assert_equal([false, "group 'not-exist' doesn't exist.\n"],
+                 run_asl_groupshow("not-exist"))
+  end
+
+  private
+  def run_asl_groupshow(*other_args, &block)
+    run_ruby(*[@asl_groupshow, *other_args], &block)
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/group.rb (+309 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/group.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/group.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,309 @@
+require 'English'
+
+module ActiveSambaLdap
+  class Group < Base
+    # from source/include/rpc_misc.c in Samba
+    DOMAIN_ADMINS_RID = 0x00000200
+    DOMAIN_USERS_RID = 0x00000201
+    DOMAIN_GUESTS_RID = 0x00000202
+    DOMAIN_COMPUTERS_RID = 0x00000203
+
+    LOCAL_ADMINS_RID = 0x00000220
+    LOCAL_USERS_RID = 0x00000221
+    LOCAL_GUESTS_RID = 0x00000222
+    LOCAL_POWER_USERS_RID = 0x00000223
+
+    LOCAL_ACCOUNT_OPERATORS_RID = 0x00000224
+    LOCAL_SYSTEM_OPERATORS_RID = 0x00000225
+    LOCAL_PRINT_OPERATORS_RID = 0x00000226
+    LOCAL_BACKUP_OPERATORS_RID = 0x00000227
+
+    LOCAL_REPLICATORS_RID = 0x00000228
+
+
+    # from source/rpc_server/srv_util.c in Samba
+    DOMAIN_ADMINS_NAME = "Domain Administrators"
+    DOMAIN_USERS_NAME = "Domain Users"
+    DOMAIN_GUESTS_NAME = "Domain Guests"
+    DOMAIN_COMPUTERS_NAME = "Domain Computers"
+
+
+    WELL_KNOWN_RIDS = []
+    WELL_KNOWN_NAMES = []
+    constants.each do |name|
+      case name
+      when /_RID$/
+        WELL_KNOWN_RIDS << const_get(name)
+      when /_NAME$/
+        WELL_KNOWN_NAMES << const_get(name)
+      end
+    end
+
+
+    # from source/librpc/idl/lsa.idl in Samba
+    TYPES = {
+      "domain" => 2,
+      "local" => 4,
+      "builtin" => 5,
+    }
+
+    class << self
+      def ldap_mapping(options={})
+        Config.required_variables :groups_prefix
+        default_options = {
+          :dnattr => "cn",
+          :prefix => Config.groups_prefix,
+          :classes => ["posixGroup", "sambaGroupMapping"],
+
+          :member_local_key => "memberUid",
+          :user_member_class_name => "User",
+          :computer_member_class_name => "Computer",
+
+          :primary_member_foreign_key => "gidNumber",
+          :primary_member_local_key => "gidNumber",
+          :primary_user_member_class_name => "User",
+          :primary_computer_member_class_name => "Computer",
+        }
+        options = default_options.merge(options)
+        super options
+        init_associations(options)
+      end
+
+      def create(name, options={})
+        group = new(name)
+        gid_number, pool = ensure_gid_number(options)
+        group.change_gid_number(gid_number)
+        group.change_type(options[:group_type] || "domain")
+        group.description = options[:description] || name
+        group.displayName = options[:display_name] || name
+        if group.save and pool
+          pool.gidNumber = Integer(group.gidNumber(true)).succ
+          pool.save!
+        end
+        group
+      end
+
+      def destroy(name, options={})
+        new(name).destroy(options)
+      end
+
+      def find_by_name_or_gid_number(key)
+        group = nil
+        begin
+          gid_number = Integer(key)
+          group = find_by_gid_number(gid_number)
+          raise GidNumberDoesNotExist.new(gid_number) if group.nil?
+        rescue ArgumentError
+          group = new(key)
+          raise GroupDoesNotExist.new(key) unless group.exists?
+        end
+        group
+      end
+
+      def find_by_gid_number(number)
+        options = {:objects => true}
+        attribute = "gidNumber"
+        value = Integer(number).to_s
+        if Base::OLD_ACTIVE_LDAP
+          options[:attribute] = attribute
+          options[:value] = value
+        else
+          options[:filter] = "(#{attribute}=#{value})"
+        end
+        find(options)
+      end
+
+      def gid2rid(gid)
+        gid = Integer(gid)
+        if WELL_KNOWN_RIDS.include?(gid)
+          gid
+        else
+          2 * gid + 1001
+        end
+      end
+
+      def rid2gid(rid)
+        rid = Integer(rid)
+        if WELL_KNOWN_RIDS.include?(rid)
+          rid
+        else
+          (rid - 1001) / 2
+        end
+      end
+
+      def start_gid
+        ActiveSambaLdap::Config.required_variables :start_gid
+        Integer(ActiveSambaLdap::Config.start_gid)
+      end
+
+      def start_rid
+        gid2rid(start_gid)
+      end
+
+      def find_available_gid_number(pool)
+        gid_number = pool.gidNumber(true) || start_gid
+
+        100.times do |i|
+          if find(:attribute => "gidNumber", :value => gid_number).nil?
+            return gid_number
+          end
+          gid_number = gid_number.succ
+        end
+
+        nil
+      end
+
+      private
+      def init_associations(options)
+        association_options = {}
+        options.each do |key, value|
+          case key.to_s
+          when /^((?:primary_)?(?:(?:user|computer)_)?member)_/
+            association_options[$1] ||= {}
+            association_options[$1][$POSTMATCH.to_sym] = value
+          end
+        end
+
+        member_opts = association_options["member"] || {}
+        user_member_opts = association_options["user_member"] || {}
+        computer_member_opts = association_options["computer_member"] || {}
+        has_many :user_members, member_opts.merge(user_member_opts)
+        has_many :computer_members,
+                 member_opts.merge(computer_member_opts)
+
+        primary_member_opts = association_options["primary_member"] || {}
+        primary_user_member_opts =
+          association_options["primary_user_member"] || {}
+        primary_computer_member_opts =
+          association_options["primary_computer_member"] || {}
+        belongs_to :primary_user_members,
+                    primary_member_opts.merge(primary_user_member_opts)
+        belongs_to :primary_computer_members,
+                    primary_member_opts.merge(primary_computer_member_opts)
+      end
+
+      def ensure_gid_number(options)
+        gid_number = options[:gid_number]
+        pool = nil
+        unless gid_number
+          pool_class = options[:pool_class] || Class.new(UnixIdPool)
+          pool = pool_class.new(options[:samba_domain] || Config[:samba_domain])
+          gid_number = find_available_gid_number(pool)
+        end
+        [gid_number, pool]
+      end
+    end
+
+    def members(*args)
+      user_members(*args) + computer_members(*args)
+    end
+
+    def primary_members(*args)
+      primary_user_members(*args) + primary_computer_members(*args)
+    end
+
+    def change_gid_number(gid, allow_non_unique=false)
+      check_unique_gid_number(gid) unless allow_non_unique
+      rid = self.class.gid2rid(gid)
+      self.gidNumber = gid.to_s
+      change_sid(rid, allow_non_unique)
+    end
+
+    def change_gid_number_by_rid(rid, allow_non_unique=false)
+      change_uid_number(self.class.rid2gid(rid), allow_non_unique)
+    end
+
+    def change_sid(rid, allow_non_unique=false)
+      sid = "#{ActiveSambaLdap::Config.sid}-#{rid}"
+      # check_unique_sid_number(sid) unless allow_non_unique
+      self.sambaSID = sid
+    end
+
+    def rid
+      Integer(sambaSID(true).split(/-/).last)
+    end
+
+    def change_type(type)
+      normalized_type = type.to_s.downcase
+      if TYPES.has_key?(normalized_type)
+        type = TYPES[normalized_type]
+      elsif TYPES.values.include?(type.to_i)
+	# pass
+      else
+        raise ArgumentError, "invalid type: #{type}"
+      end
+      self.sambaGroupType = type.to_s
+    end
+
+    def remove_member(member_or_uid)
+      uid = ensure_uid(member_or_uid)
+      new_memberUid = memberUid.dup
+      unless new_memberUid.reject! {|_uid| uid == _uid}.nil?
+        self.memberUid = new_memberUid
+        save!
+      end
+    end
+
+    def add_member(member_or_uid)
+      uid = ensure_uid(member_or_uid)
+      unless memberUid.find {|_uid| uid == _uid}
+        self.memberUid = (memberUid + [uid]).sort
+        save!
+      end
+    end
+
+    def destroy(options={})
+      if options[:remove_members]
+        if options[:force_change_primary_members]
+          change_primary_members(options)
+        end
+        pr_members = primary_members
+        unless pr_members.empty?
+          raise PrimaryGroupCanNotBeDestroyed.new(cn(true), pr_members)
+        end
+        members(true).each do |member|
+          remove_member(member)
+        end
+      end
+      super()
+    end
+
+    private
+    def ensure_uid(member_or_uid)
+      if member_or_uid.is_a?(String)
+        member_or_uid
+      else
+        member_or_uid.uid(true)
+      end
+    end
+
+    def check_unique_gid_number(gid_number)
+      ActiveSambaLdap::Base.restart_nscd do
+        if self.class.find_by_gid_number(Integer(gid_number))
+          raise GidNumberAlreadyExists.new(gid_number)
+        end
+      end
+    end
+
+    def change_primary_members(options={})
+      name = cn(true)
+
+      pr_members = primary_members(true)
+      cannot_removed_members = []
+      pr_members.each do |member|
+        if (member.groups - [name]).empty?
+          cannot_removed_members << member.uid(true)
+        end
+      end
+      unless cannot_removed_members.empty?
+        raise CanNotChangePrimaryGroup.new(name, cannot_removed_members)
+      end
+
+      pr_members.each do |member|
+        new_group = member.groups(true).find {|gr| gr.cn != name}
+        member.change_group(new_group.gidNumber(true))
+        member.save!
+      end
+    end
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/account.rb (+297 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/account.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/account.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,297 @@
+require 'time'
+require 'fileutils'
+require 'English'
+
+require 'active_samba_ldap/user_password'
+
+module ActiveSambaLdap
+  module Account
+    def self.included(base)
+      super
+      base.extend(ClassMethods)
+      base.class_eval(<<-EOC, __FILE__, __LINE__ + 1)
+        cattr_accessor :group_class_name
+        cattr_writer :group_class
+        def self.group_class
+          @@group_class || super
+        end
+      EOC
+    end
+
+    # from source/include/rpc_misc.c in Samba
+    DOMAIN_ADMIN_RID = 0x000001F4
+    DOMAIN_GUEST_RID = 0x000001F5
+
+    # from source/rpc_server/srv_util.c in Samba
+    DOMAIN_ADMIN_NAME = "Administrator"
+    DOMAIN_GUEST_NAME = "Guest"
+
+    WELL_KNOWN_RIDS = []
+    WELL_KNOWN_NAMES = []
+    constants.each do |name|
+      case name
+      when /_RID$/
+        WELL_KNOWN_RIDS << const_get(name)
+      when /_NAME$/
+        WELL_KNOWN_NAMES << const_get(name)
+      end
+    end
+
+    FAR_FUTURE_TIME = Time.parse("2050/01/01").to_i.to_s
+    ACCOUNT_FLAGS_RE = /\A\[([NDHTUMWSLXI ]+)\]\z/
+    NAME_RE = /\A(?!\d)[\w @_\-\.]+\z/
+
+    module ClassMethods
+      def valid_name?(name)
+        NAME_RE =~ name ? true : false
+      end
+
+      def ldap_scope
+        LDAP::LDAP_SCOPE_SUBTREE
+      end
+
+      def group_class
+        group_class_name.split(/::/).inject(self) do |ret, name|
+          ret.const_get(name)
+        end
+      end
+
+      def find_by_uid_number(number)
+        options = {:objects => true}
+        attribute = "uidNumber"
+        value = Integer(number)
+        if Base::OLD_ACTIVE_LDAP
+          options[:attribute] = attribute
+          options[:value] = value
+        else
+          options[:filter] = "(#{attribute}=#{value})"
+        end
+        find(options)
+      end
+
+      def uid2rid(uid)
+        uid = Integer(uid)
+        if WELL_KNOWN_RIDS.include?(uid)
+          uid
+        else
+          2 * uid + 1000
+        end
+      end
+
+      def rid2uid(rid)
+        rid = Integer(rid)
+        if WELL_KNOWN_RIDS.include?(rid)
+          rid
+        else
+          (Integer(rid) - 1000) / 2
+        end
+      end
+
+      def start_uid
+        ActiveSambaLdap::Config.required_variables :start_uid
+        Integer(ActiveSambaLdap::Config.start_uid)
+      end
+
+      def start_rid
+        uid2rid(start_uid)
+      end
+
+      def find_available_uid_number(pool)
+        uid_number = pool.uidNumber(true) || start_uid
+
+        100.times do |i|
+          if find(:attribute => "uidNumber", :value => uid_number).nil?
+            return uid_number
+          end
+          uid_number = uid_number.succ
+        end
+
+        nil
+      end
+    end
+
+    def init(uid_number, gid_number)
+      self.cn = uid
+      self.sn = uid
+      self.gecos = uid
+      self.homeDirectory = substituted_value(:user_home) {"/nonexistent"}
+      self.loginShell = substituted_value(:user_login_shell) {"/bin/false"}
+      self.sambaHomePath = substituted_value(:user_samba_home)
+      self.sambaHomeDrive = substituted_value(:user_home_drive)
+      self.sambaProfilePath = substituted_value(:user_profile)
+      self.sambaLogonScript = substituted_value(:user_script)
+      self.sambaLogonTime = "0"
+      self.sambaLogoffTime = FAR_FUTURE_TIME
+      self.sambaKickoffTime = FAR_FUTURE_TIME
+      self.sambaAcctFlags = default_account_flags
+
+      self.change_uid_number(uid_number)
+      group = self.change_group(gid_number)
+
+      self.userPassword = "{crypt}x"
+      self.sambaLMPassword = "XXX"
+      self.sambaNTPassword = "XXX"
+      self.sambaPwdLastSet = "0"
+      self.enable_password_change
+      self.disable_forcing_password_change
+
+      self.disable
+
+      group
+    end
+
+    def destroy(options={})
+      if options[:removed_from_group]
+        groups(true).each do |group|
+          group.remove_member(self)
+        end
+      end
+      home_directory = homeDirectory(true)
+      super()
+      if !exists? and options[:remove_home_directory] and
+          File.directory?(home_directory)
+        if options[:remove_home_directory_interactive]
+          system("rm", "-r", "-i", home_directory)
+        else
+          FileUtils.rm_r(home_directory)
+        end
+      end
+      exists?
+    end
+
+    def change_uid_number(uid, allow_non_unique=false)
+      check_unique_uid_number(uid) unless allow_non_unique
+      rid = self.class.uid2rid(uid)
+      self.uidNumber = Integer(uid).to_s
+      change_sid(rid, allow_non_unique)
+    end
+
+    def change_uid_number_by_rid(rid, allow_non_unique=false)
+      change_uid_number(self.class.rid2uid(rid), allow_non_unique)
+    end
+
+    def change_sid(rid, allow_non_unique=false)
+      sid = "#{ActiveSambaLdap::Config.sid}-#{rid}"
+      # check_unique_sid_number(sid) unless allow_non_unique
+      self.sambaSID = sid
+    end
+
+    def rid
+      Integer(sambaSID(true).split(/-/).last)
+    end
+
+    def change_group(gid)
+      if self.class.group_class === gid
+        group = gid
+      else
+        group = self.class.group_class.find_by_name_or_gid_number(gid)
+      end
+      gid_number = group.gidNumber(true)
+      samba_sid = group.sambaSID(true)
+      if samba_sid.nil? or samba_sid.empty?
+        raise GroupDoesNotHaveSambaSID.new(gid_number)
+      end
+      if gidNumber(true)
+        old_gid = gidNumber(true)
+        old_group = self.class.group_class.find_by_name_or_gid_number(old_gid)
+        old_group.remove_member(self)
+      end
+      self.gidNumber = gid_number
+      self.sambaPrimaryGroupSID = samba_sid
+      group
+    end
+
+    def change_password(password)
+      self.userPassword = ActiveSambaLdap::UserPassword.ssha(password)
+    end
+
+    def change_samba_password(password)
+      self.sambaLMPassword = Samba::Encrypt.lm_hash(password)
+      self.sambaNTPassword = Samba::Encrypt.ntlm_hash(password)
+      self.sambaPwdLastSet = Time.now.to_i.to_s
+    end
+
+    def enable_password_change
+      self.sambaPwdCanChange = "0"
+    end
+
+    def disable_password_change
+      self.sambaPwdCanChange = FAR_FUTURE_TIME
+    end
+
+    def can_change_password?
+      sambaPwdCanChange(true).nil? or
+        Time.at(sambaPwdCanChange(true).to_i) <= Time.now
+    end
+
+    def enable_forcing_password_change
+      self.sambaPwdMustChange = "0"
+      if /X/ =~ sambaAcctFlags(true).to_s
+        self.sambaAcctFlags = sambaAcctFlags(true).sub(/X/, '')
+      end
+      if sambaPwdLastSet(true).to_i.zero?
+        self.sambaPwdLastSet = FAR_FUTURE_TIME
+      end
+    end
+
+    def disable_forcing_password_change
+      self.sambaPwdMustChange = FAR_FUTURE_TIME
+    end
+
+    def must_change_password?
+      !(/X/ =~ sambaAcctFlags(true).to_s or
+        sambaPwdMustChange(true).nil? or
+        Time.at(sambaPwdMustChange(true).to_i) > Time.now)
+    end
+
+    def enable
+      if /D/ =~ sambaAcctFlags(true).to_s
+        self.sambaAcctFlags = sambaAcctFlags(true).gsub(/D/, '')
+      end
+    end
+
+    def disable
+      flags = ""
+      if ACCOUNT_FLAGS_RE =~ sambaAcctFlags(true).to_s
+        flags = $1
+        return if /D/ =~ flags
+      end
+      self.sambaAcctFlags = "[D#{flags}]"
+    end
+
+    def enabled?
+      !disabled?
+    end
+
+    def disabled?
+      (/D/ =~ sambaAcctFlags(true).to_s) ? true : false
+    end
+
+    private
+    def check_unique_uid_number(uid_number)
+      ActiveSambaLdap::Base.restart_nscd do
+        if self.class.find_by_uid_number(uid_number)
+          raise UidNumberAlreadyExists.new(uid_number)
+        end
+      end
+    end
+
+    def substitute_template(template)
+      template.gsub(/%U/, uid(true))
+    end
+
+    def substituted_value(key)
+      if block_given?
+        value = ActiveSambaLdap::Config.__send__(key)
+        if value
+          substitute_template(value)
+        else
+          yield
+        end
+      else
+        ActiveSambaLdap::Config.required_variables key
+        substitute_template(ActiveSambaLdap::Config.__send__(key))
+      end
+    end
+  end
+end

  Property changed: activesambaldap/trunk/bin/asl-groupmod (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-groupmod (+114 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-groupmod	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-groupmod	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,114 @@
+#!/usr/bin/env ruby
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  options.gid = nil
+  options.allow_non_unique_gid_number = false
+  options.new_group_name = nil
+  options.members_to_add = nil
+  options.members_to_delete = nil
+
+  opts.banner += " GROUP_NAME"
+
+  opts.on("-g", "--gid=GID", "gid") {|options.gid|}
+  opts.on("--[no-]allow-non-unique-gid",
+          "gid can be non unique " +
+          "(#{options.allow_non_unique_gid_number})") do |bool|
+    options.allow_non_unique_gid_number = bool
+  end
+  opts.on("-r", "--rename=NEW_NAME",
+          "new group name") {|options.new_group_name|}
+  opts.on("-m", "--add-members=MEMBER1,MEMBER2,MEBMER3", Array,
+          "add members (comma delimited)") {|options.members_to_add|}
+  opts.on("-d", "--delete-members=MEMBER1,MEMBER2,MEBMER3", Array,
+          "delete members (comma delimited)") {|options.members_to_delete|}
+end
+
+name = nil
+if argv.size == 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+unless Process.uid.zero?
+  puts "need root authority."
+  exit 1
+end
+
+ActiveSambaLdap::Base.establish_connection({}, false)
+
+class Group < ActiveSambaLdap::Group
+  ldap_mapping
+end
+
+class User < ActiveSambaLdap::User
+  ldap_mapping
+end
+
+class Computer < ActiveSambaLdap::Computer
+  ldap_mapping
+end
+
+group = Group.new(name)
+unless group.exists?
+  puts "group '#{name}' doesn't exist."
+  exit 1
+end
+
+if options.gid
+  begin
+    group.change_gid_number(options.gid, options.allow_non_unique_gid_number)
+  rescue ActiveSambaLdap::GidNumberAlreadyExists
+    puts $!.message
+    exit 1
+  end
+end
+
+if options.members_to_add and options.members_to_delete
+  duplicated_members = options.members_to_add & options.members_to_delete
+  unless duplicated_members.empty?
+    message = "there are duplicated members in adding and deleting members: "
+    message << duplicated_members.join(", ")
+    puts message
+    exit 1
+  end
+end
+
+if options.members_to_add
+  options.members_to_add.each do |member|
+    group.add_member(member)
+  end
+end
+
+if options.members_to_delete
+  options.members_to_delete.each do |member|
+    group.remove_member(member)
+  end
+end
+
+group.save!
+
+if options.new_group_name
+  new_group = Group.new(options.new_group_name)
+  if new_group.exists?
+    puts "group '#{options.new_group_name}' always exists."
+    exit 1
+  end
+
+  new_group.attributes = group.attributes.reject do |key, value|
+    %w(cn).include?(key)
+  end
+  new_group.save!
+  group.primary_members(true).each do |member|
+    member.change_group(new_group.gidNumber(true))
+  end
+  group.destroy(:remove_members => true)
+end
+
+ActiveSambaLdap::Base.restart_nscd
+
+ActiveSambaLdap::Base.close

  Added: activesambaldap/trunk/test/test_samba_encrypt.rb (+34 -0)
===================================================================
--- activesambaldap/trunk/test/test_samba_encrypt.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_samba_encrypt.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,34 @@
+require 'test/unit'
+require 'stringio'
+
+require 'samba/encrypt'
+
+class SambaEncryptTest < Test::Unit::TestCase
+  def test_lm_hash
+    assert_equal("E52CAC67419A9A224A3B108F3FA6CB6D",
+                 Samba::Encrypt.lm_hash("password"))
+    assert_equal("E52CAC67419A9A224A3B108F3FA6CB6D",
+                 Samba::Encrypt.lm_hash("paSSWOrd"))
+    assert_equal("E0C510199CC66ABDE0C510199CC66ABD",
+                 Samba::Encrypt.lm_hash("abcdefgabcdefg"))
+    assert_equal("FF3750BCC2B22412C2265B23734E0DAC",
+                 Samba::Encrypt.lm_hash("SecREt01"))
+    begin
+      stderr = $stderr
+      $stderr = StringIO.new
+      assert_equal("E0C510199CC66ABDE0C510199CC66ABD",
+                   Samba::Encrypt.lm_hash("abcdefgabcdefg" + "X"))
+      assert_equal("E0C510199CC66ABDE0C510199CC66ABD",
+                   Samba::Encrypt.lm_hash("abcdefgabcdefg" + "X" * 100))
+      assert_equal("password is truncated to 14 characters\n" * 2,
+                   $stderr.string)
+    ensure
+      $stderr = stderr
+    end
+  end
+
+  def test_ntlm_hash
+    assert_equal("8846F7EAEE8FB117AD06BDD830B7586C",
+                 Samba::Encrypt.ntlm_hash("password"))
+  end
+end

  Added: activesambaldap/trunk/test/asl_test_utils.rb (+162 -0)
===================================================================
--- activesambaldap/trunk/test/asl_test_utils.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/asl_test_utils.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,162 @@
+module AslTestUtils
+  def setup
+    super
+    ActiveSambaLdap::Base.establish_connection({}, false)
+
+    @user_class = Class.new(ActiveSambaLdap::User)
+    @user_class.ldap_mapping
+    @computer_class = Class.new(ActiveSambaLdap::Computer)
+    @computer_class.ldap_mapping
+    @group_class = Class.new(ActiveSambaLdap::Group)
+    @group_class.ldap_mapping
+
+    @user_class.group_class = @group_class
+    @computer_class.group_class = @group_class
+
+    @original_ldifs = ""
+    @user_index = 0
+    @computer_index = 0
+    @group_index = 0
+    begin
+      @original_ldifs = ActiveSambaLdap::Base.dump
+      ActiveSambaLdap::Base.destroy_all
+      ActiveSambaLdap::Base.populate
+    rescue ActiveLDAP::ConnectionError
+    end
+  end
+
+  def teardown
+    ActiveSambaLdap::Base.destroy_all
+    ActiveSambaLdap::Base.load(@original_ldifs)
+    ActiveSambaLdap::Base.close
+    super
+  end
+
+  def make_dummy_user(config={})
+    @user_index += 1
+    name = config[:name] || "test-user#{@user_index}"
+    home_directory = config[:home_directory] || "/tmp/#{name}-#{Process.pid}"
+    ensure_delete_user(name, home_directory) do
+      password = config[:password] || "password"
+      uid_number = config[:uid_number] || "100000#{@user_index}"
+      default_user_gid = ActiveSambaLdap::DefaultConfig.default_user_gid
+      gid_number = config[:gid_number] || default_user_gid
+      _wrap_assertion do
+        user = @user_class.new(name)
+        assert(!user.exists?)
+        user.init(uid_number, gid_number)
+        user.homeDirectory = home_directory
+        user.change_password(password)
+        user.change_samba_password(password)
+        user.save!
+        FileUtils.mkdir(home_directory)
+        assert(user.exists?)
+        yield(user, password)
+      end
+    end
+  end
+
+  def ensure_delete_user(uid, home=nil)
+    yield(uid, home)
+  ensure
+    FileUtils.rm_rf(home) if home
+    user = @user_class.new(uid)
+    if user.exists?
+      groups = @group_class.find_all(:attribute => "memberUid",
+                                     :value => user.uid)
+      groups.each do |group|
+        @group_class.new(group).remove_member(user)
+      end
+      user.destroy
+    end
+  end
+
+  def make_dummy_computer(config={})
+    @computer_index += 1
+    name = config[:name] || "test-computer#{@computer_index}$"
+    home_directory = config[:home_directory] || "/tmp/#{name}-#{Process.pid}"
+    ensure_delete_computer(name, home_directory) do |name, home_directory|
+      password = config[:password]
+      uid_number = config[:uid_number] || "100000#{@computer_index}"
+      default_computer_gid = ActiveSambaLdap::DefaultConfig.default_computer_gid
+      gid_number = config[:gid_number] || default_computer_gid
+      _wrap_assertion do
+        computer = @computer_class.new(name)
+        assert(!computer.exists?)
+        computer.init(uid_number, gid_number)
+        if password
+          computer.change_password(password)
+          computer.change_samba_password(password)
+        end
+        computer.save!
+        FileUtils.mkdir(home_directory)
+        assert(computer.exists?)
+        yield(computer, password)
+      end
+    end
+  end
+
+  def ensure_delete_computer(uid, home=nil)
+    yield(uid.sub(/\$+\z/, '') + "$", home)
+  ensure
+    FileUtils.rm_rf(home) if home
+    computer = @computer_class.new(uid)
+    if computer.exists?
+      groups = @group_class.find_all(:attribute => "memberUid",
+                                     :value => computer.uid)
+      groups.each do |group|
+        @group_class.new(group).remove_member(computer)
+      end
+      computer.destroy
+    end
+  end
+
+  def make_dummy_group(config={})
+    @group_index += 1
+    name = config[:name] || "test-group#{@group_index}"
+    ensure_delete_group(name) do
+      gid_number = config[:gid_number] || "200000#{@group_index}"
+      group_type = config[:group_type] || "domain"
+      _wrap_assertion do
+        group = @group_class.new(name)
+        assert(!group.exists?)
+        group.change_gid_number(gid_number)
+        group.change_type(group_type)
+        group.save!
+        assert(group.exists?)
+        yield(group)
+      end
+    end
+  end
+
+  def ensure_delete_group(name)
+    yield(name)
+  ensure
+    group = @group_class.new(name)
+    group.destroy if group.exists?
+  end
+
+  def ensure_delete_ou(ou)
+    yield(ou)
+  ensure
+    ou_class = Class.new(ActiveSambaLdap::Ou)
+    ou_class.ldap_mapping
+    ou_obj = ou_class.new(ou)
+    ou_obj.destroy if ou_obj.exists?
+  end
+
+  def pool
+    pool_class = Class.new(ActiveSambaLdap::UnixIdPool)
+    pool_class.ldap_mapping
+    ActiveSambaLdap::Config.required_variables :samba_domain
+    pool_class.new(ActiveSambaLdap::Config.samba_domain)
+  end
+
+  def next_uid_number
+    pool.uidNumber(true) || @user_class.start_uid
+  end
+
+  def next_gid_number
+    pool.gidNumber(true) || @group_class.start_gid
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/base.rb (+379 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/base.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/base.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,379 @@
+require_gem_if_need "activeldap", nil, ">= 0.8.0"
+
+module ActiveSambaLdap
+  class Error < StandardError
+  end
+
+  class RequiredVariableIsNotSet < Error
+    attr_reader :name
+    def initialize(name)
+      @name = name
+      super("required variable '#{name}' is not set")
+    end
+  end
+
+  class UidNumberAlreadyExists < Error
+    attr_reader :number
+    def initialize(number)
+      @number = number
+      super("uid number '#{@number}' already exists")
+    end
+  end
+
+  class GroupDoesNotExist < Error
+    attr_reader :name
+    def initialize(name)
+      @name = name
+      super("group '#{@name}' doesn't exist")
+    end
+  end
+
+  class GidNumberAlreadyExists < Error
+    attr_reader :number
+    def initialize(number)
+      @number = number
+      super("gid number '#{@number}' already exists")
+    end
+  end
+
+  class GidNumberDoesNotExist < Error
+    attr_reader :number
+    def initialize(number)
+      @number = number
+      super("gid number '#{@number}' doesn't exist")
+    end
+  end
+
+  class GroupDoesNotHaveSambaSID < Error
+    attr_reader :number
+    def initialize(number)
+      @number = number
+      super("sambaSID attribute doesn't exist for gid number '#{@number}'")
+    end
+  end
+
+  class CanNotChangePrimaryGroup < Error
+    attr_reader :group, :members
+    def initialize(group, members)
+      @group = group
+      @members = members
+      message = "cannot change primary group from '#{group}' to other group "
+      message << "due to no other belonged groups: #{members.join(', ')}"
+      super(message)
+    end
+  end
+
+  class PrimaryGroupCanNotBeDestroyed < Error
+    attr_reader :group, :members
+    def initialize(group, members)
+      @group = group
+      @members = members
+      message = "cannot destroy group '#{group}' due to members who belong "
+      message << "to the group as primary group: #{members.join(', ')}"
+      super(message)
+    end
+  end
+
+  class Base < ActiveLDAP::Base
+    include Reloadable::Subclasses
+
+    OLD_ACTIVE_LDAP = (ActiveLDAP::VERSION.split(/\./) <=> %w(0 8 0)) < 0
+
+    if OLD_ACTIVE_LDAP
+      class << ActiveLDAP::Base
+        def establish_connection(config={})
+          connect(config)
+        end
+      end
+
+      alias_method :save, :write
+
+      def initialize(val)
+        val = val.first if val.is_a?(Array) and val.size == 1
+        super(val)
+      end
+
+      def update_attribute(name, value)
+        if self.attribute_names.member?(name)
+          send(:attribute_method=, name, value)
+        end
+        save
+      end
+
+      def update_attributes(h)
+        self.attributes = h
+        save
+      end
+
+      alias_method :attribute_names, :attributes
+      def attributes
+        Marshal.load(Marshal.dump(@data))
+      end
+
+      def attributes=(h)
+        if h.respond_to?(:keys) and h.respond_to?(:[])
+          h.each_pair do |key, value|
+            key = key.to_s.downcase
+            if self.attribute_names.member?(key)
+              send(:attribute_method=, key, value)
+            end
+          end
+        end
+      end
+    end
+
+    class << self
+      def prefix
+        base.gsub(/,?#{Regexp.escape(self.ancestors[1].base)}\z/, '')
+      end
+
+      def instantiate(record)
+        object = allocate
+        object.instance_variable_set("@attributes", record)
+        object
+      end
+
+      def human_attribute_name(attribute_key_name)
+        attribute_key_name.humanize
+      end
+
+      def establish_connection(config={}, reference_only=true)
+        Config.init
+        Config.required_variables :suffix
+        default_config = {:base => Config.suffix}
+        if reference_only
+          Config.required_variables :reference_host, :reference_port
+          default_config[:host] = Config.reference_host
+          default_config[:port] = Config.reference_port
+          default_config[:bind_format] = Config.reference_bind_format
+          default_config[:user] = Config.reference_user
+          default_config[:password] = Config.reference_password
+          default_config[:method] = :tls if Config.reference_use_tls
+          default_config[:allow_anonymous] = Config.reference_allow_anonymous
+        else
+          Config.required_variables :update_host, :update_port
+          default_config[:host] = Config.update_host
+          default_config[:port] = Config.update_port
+          default_config[:bind_format] = Config.update_bind_format
+          default_config[:user] = Config.update_user
+          default_config[:password] = Config.update_password
+          default_config[:method] = :tls if Config.update_use_tls
+          default_config[:allow_anonymous] = Config.update_allow_anonymous
+        end
+        default_config.each do |key, value|
+          default_config.delete(key) if value.nil?
+        end
+        super(default_config.merge(config))
+      end
+
+      def destroy_all(config={})
+        targets = []
+        begin
+          targets = search(config).collect do |entry|
+            entry["dn"][0]
+          end.sort_by do |dn|
+            dn.reverse
+          end.reverse
+	rescue RuntimeError
+        end
+        return if targets.empty?
+
+        connection do |conn|
+          targets.each do |target|
+            conn.delete(target)
+          end
+        end
+      end
+
+      def dump(config={})
+        ldifs = []
+        search(config).each do |entry|
+          ldif = LDAP::LDIF.to_ldif("dn", entry.delete("dn"))
+          entry.each do |key, values|
+            ldif << LDAP::LDIF.to_ldif(key, values)
+          end
+          ldifs << ldif
+        end
+        ldifs.join("\n")
+      end
+
+      def load(ldifs)
+        connection do |conn|
+          ldifs.split(/(?:\r?\n){2,}/).each do |ldif|
+            LDAP::LDIF.parse_entry(ldif).send(conn)
+          end
+        end
+      end
+
+      def search(*args, &block)
+        super(*args, &block)
+      rescue RuntimeError
+        []
+      end
+
+      def exists?(dn_value)
+        new(dn_value).exists?
+      end
+
+      def restart_nscd
+        if system("/etc/init.d/nscd status >/dev/null 2>&1")
+          system("/etc/init.d/nscd stop >/dev/null 2>&1")
+          begin
+            yield if block_given?
+          ensure
+            system("/etc/init.d/nscd start >/dev/null 2>&1")
+          end
+        end
+      end
+    end
+
+    alias_method :respond_to_without_attributes?, :respond_to?
+
+    alias_method :validate_ldap, :validate
+    alias_method :save_ldap, :save
+    alias_method :destroy, :delete
+    alias_method :new_record?, :exists?
+
+    def save!
+      enforce_types
+      save_ldap
+    end
+
+    def save
+      create_or_update
+    end
+
+    def to_ldif
+      ldif = ::LDAP::LDIF.to_ldif("dn", [@dn.dup])
+      @data.sort_by do |key, values|
+        key
+      end.each do |key, values|
+        ldif << ::LDAP::LDIF.to_ldif(key, values.collect {|value| value.dup})
+      end
+
+      ldif
+    end
+    alias_method :to_s, :to_ldif
+
+    private
+    def create_or_update
+      begin
+        save!
+        true
+      rescue RuntimeError => e
+        if /^ActiveLDAP::/ =~ e.class.name
+          false
+        else
+          raise
+        end
+      end
+    end
+
+    def create
+      save
+    end
+
+    def update
+      save
+    end
+
+    include ActiveRecord::Validations
+    include ActiveRecord::Callbacks
+  end
+end
+
+require "ldap/ldif"
+
+class LDAP::Mod
+  unless instance_method(:to_s).arity.zero?
+    def to_s
+      inspect
+    end
+  end
+
+  if ActiveSambaLdap::Base::OLD_ACTIVE_LDAP
+    alias_method :_initialize, :initialize
+    def initialize(op, type, vals)
+      if (LDAP::VERSION.split(/\./).collect {|x| x.to_i} <=> [0, 9, 7]) <= 0
+        @op, @type, @vals = op, type, vals # to protect from GC
+      end
+      _initialize(op, type, vals)
+    end
+  end
+end
+
+if ActiveSambaLdap::Base::OLD_ACTIVE_LDAP
+  class LDAP::Schema2
+    def attr(sub, type, at)
+      return [] if sub.empty?
+      return [] if type.empty?
+      return [] if at.empty?
+
+      type = type.downcase # We're going case insensitive.
+
+      # Check already parsed options first
+      if @@attr_cache.has_key? sub \
+        and @@attr_cache[sub].has_key? type \
+        and @@attr_cache[sub][type].has_key? at
+          return @@attr_cache[sub][type][at].dup
+      end
+
+      # Initialize anything that is required
+      unless @@attr_cache.has_key? sub
+        @@attr_cache[sub] = {}
+      end
+      
+      unless @@attr_cache[sub].has_key? type
+        @@attr_cache[sub][type] = {}
+      end
+
+      at = at.upcase
+      self[sub].each do |s|
+        line = nil
+        if type[0..0] =~ /[0-9]/
+          if s =~ /\(\s+(?i:#{type})\s+(?:[A-Z]|\))/
+            line = s
+          end
+        else
+          # support NAME 'dsa' or NAME ( 'das' 'dsa' ... )
+          if s =~ /NAME\s+(?:(?:\(.*'(?i:#{type})'.*?\))|(?:'(?i:#{type})'))\s+(?:[A-Z]|\))/
+            line = s
+          end
+        end
+        next if line.nil?
+
+        # I need to check, but I think some of these matchs
+        # overlap. I'll need to check these when I'm less sleepy.
+        multi = nil
+        case line
+          when /#{at}\s+[\)A-Z]/
+            @@attr_cache[sub][type][at] = ['TRUE']
+            return ['TRUE']
+          when /#{at}\s+'(.+?)'/
+            @@attr_cache[sub][type][at] = [$1]
+            return [$1]
+          when /#{at}\s+\((.+?)\)/
+            multi = $1
+          when /#{at}\s+\(([\w\d\s\.]+)\)/
+            multi = $1
+          when /#{at}\s+([\w\d\.]+)/
+            @@attr_cache[sub][type][at] = [$1]
+            return [$1]
+        end
+        next if multi.nil?
+        # Split up multiple matches
+        # if oc then it is sep'd by $
+        # if attr then bu spaces
+        if multi.match(/\$/)
+          @@attr_cache[sub][type][at] = multi.split("$").collect{|attr| attr.strip}
+          return @@attr_cache[sub][type][at].dup
+        elsif not multi.empty?
+          @@attr_cache[sub][type][at] = multi.gsub(/'/, '').split(' ').collect{|attr| attr.strip}
+          return @@attr_cache[sub][type][at].dup
+        end
+      end
+      @@attr_cache[sub][type][at] = []
+      return []
+    end
+  end
+end

  Property changed: activesambaldap/trunk/bin/asl-usershow (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-usershow (+29 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-usershow	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-usershow	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  opts.banner += " USER_NAME"
+end
+
+name = nil
+if argv.size == 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+ActiveSambaLdap::Base.establish_connection({}, true)
+
+class User < ActiveSambaLdap::User
+  ldap_mapping
+end
+
+user = User.new(name)
+unless user.exists?
+  puts "user '#{name}' doesn't exist."
+  exit 1
+end
+puts user

  Property changed: trunk/sample/svnserver.rb (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: trunk/sample/svnserver.rb (+18 -0)
===================================================================
--- trunk/sample/svnserver.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ trunk/sample/svnserver.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,18 @@
+#!/usr/bin/ruby
+
+if ARGV.size != 1
+  puts "USAGE: #{$0} repository_path"
+  exit 1
+end
+
+require 'webrick'
+require 'webrick/httpservlet/svnhandler'
+
+repos_path = ARGV.shift
+
+log = WEBrick::Log.new
+log.level = WEBrick::Log::DEBUG if $DEBUG
+server = WEBrick::HTTPServer.new({:Port => 10080, :Logger => log})
+server.mount("/", WEBrick::HTTPServlet::SvnHandler, repos_path)
+trap(:INT){server.shutdown}
+server.start

  Added: activesambaldap/trunk/test/test_asl_usershow.rb (+31 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_usershow.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_usershow.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,31 @@
+require 'test/unit'
+require 'command_support'
+require 'fileutils'
+
+require 'active_samba_ldap'
+
+class AslUserShowTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_usershow = File.join(@bin_dir, "asl-usershow")
+  end
+
+  def test_exist_user
+    make_dummy_user do |user, password|
+      assert_equal([true, user.to_s], run_asl_usershow(user.uid(true)))
+    end
+  end
+
+  def test_not_exist_user
+    assert_equal([false, "user 'not-exist' doesn't exist.\n"],
+                 run_asl_usershow("not-exist"))
+  end
+
+  private
+  def run_asl_usershow(*other_args, &block)
+    run_ruby(*[@asl_usershow, *other_args], &block)
+  end
+end

  Added: activesambaldap/trunk/test/test_asl_passwd.rb (+139 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_passwd.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_passwd.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,139 @@
+require 'test/unit'
+require 'command_support'
+require 'asl_test_utils'
+
+require 'active_samba_ldap'
+
+class AslPasswdTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_passwd = File.join(@bin_dir, "asl-passwd")
+  end
+
+  def teardown
+    ActiveSambaLdap::Base.close
+  end
+
+  def test_unknown_user
+    assert_equal([false, "user 'unknown' doesn't exist.\n"],
+                 run_asl_passwd("unknown"))
+  end
+
+  def test_change_password
+    make_dummy_user do |user, password|
+      new_password = "new#{password}"
+
+      assert_samba_password(user, password)
+
+      assert_change_password_successfully(user.uid(true),
+                                          password, new_password)
+
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, new_password)
+
+      assert_change_password_with_wrong_current_password(user.uid(true),
+                                                         password)
+
+      assert_change_password_successfully(user.uid(true),
+                                          new_password, password)
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, password)
+    end
+  end
+
+  def test_change_password_only_unix
+    make_dummy_user do |user, password|
+      new_password = "new#{password}"
+      args = ["--no-samba-password"]
+
+      assert_samba_password(user, password)
+
+      assert_change_password_successfully(user.uid(true),
+                                          password, new_password,
+                                          *args)
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, password)
+
+      assert_change_password_with_wrong_current_password(user.uid(true),
+                                                         password, *args)
+
+      assert_change_password_successfully(user.uid(true),
+                                          new_password, password, *args)
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, password)
+    end
+  end
+
+  def test_change_password_only_samba
+    make_dummy_user do |user, password|
+      new_password = "new#{password}"
+      args = ["--no-unix-password"]
+
+      assert_samba_password(user, password)
+
+      assert_change_password_successfully(user.uid(true),
+                                          password, new_password, *args)
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, new_password)
+
+      assert_change_password_with_wrong_current_password(user.uid(true),
+                                                         new_password, *args)
+
+      assert_change_password_successfully(user.uid(true), password, password,
+                                          *args)
+      user = @user_class.new(user.uid(true))
+      assert_samba_password(user, password)
+    end
+  end
+
+  private
+  def run_asl_passwd(*other_args, &block)
+    run_ruby(*[@asl_passwd, *other_args], &block)
+  end
+
+  def change_password(name, old_password, new_password, *args)
+    run_asl_passwd(name, *args) do |input, output|
+      output.puts(old_password)
+      output.puts(new_password)
+      output.puts(new_password)
+      output.flush
+    end
+  end
+
+  def assert_samba_password(user, password)
+    _wrap_assertion do
+      assert_equal(Samba::Encrypt.lm_hash(password),
+                   user.sambaLMPassword(true))
+      assert_equal(Samba::Encrypt.ntlm_hash(password),
+                   user.sambaNTPassword(true))
+    end
+  end
+
+  def assert_change_password_successfully(name, old_password, new_password,
+                                          *args)
+    assert_equal([true,
+                  [
+                   "Enter your current password: ",
+                   "New password: ",
+                   "Retype new password: ",
+                  ].join("\n") + "\n",
+                 ],
+                 change_password(name, old_password, new_password, *args))
+  end
+
+  def assert_change_password_with_wrong_current_password(name, password, *args)
+    assert_equal([false,
+                  [
+                   "Enter your current password: ",
+                   "password isn't match",
+                  ].join("\n") + "\n",
+                 ],
+                 run_asl_passwd(name, *args) do |input, output|
+                   output.puts(password)
+                   output.flush
+                 end)
+  end
+end

  Added: activesambaldap/trunk/lib/active_samba_ldap/computer.rb (+35 -0)
===================================================================
--- activesambaldap/trunk/lib/active_samba_ldap/computer.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/lib/active_samba_ldap/computer.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,35 @@
+require 'active_samba_ldap/account'
+
+module ActiveSambaLdap
+  class Computer < Base
+    include Account
+    class << self
+      def ldap_mapping(options={})
+        Config.required_variables :computers_prefix
+        default_options = {
+          :dnattr => "uid",
+          :prefix => Config.computers_prefix,
+          :classes => ["top", "inetOrgPerson", "posixAccount",
+                       "sambaSamAccount"],
+          :group_class_name => "Group",
+          :group_foreign_key => "memberUid",
+        }
+        options = default_options.merge(options)
+        super options
+        belongs_to :groups,
+                   :class_name => options[:group_class_name],
+                   :foreign_key => options[:group_foreign_key]
+        self.group_class_name = options[:group_class_name]
+      end
+
+      def valid_name?(name)
+        /\$\Z/ =~ name and User.valid_name?($PREMATCH)
+      end
+    end
+
+    private
+    def default_account_flags
+      "[W]"
+    end
+  end
+end

  Property changed: activesambaldap/trunk/bin/asl-passwd (+0 -0)
___________________________________________________________________
Name: svn:executable
   + 

  Added: activesambaldap/trunk/bin/asl-passwd (+91 -0)
===================================================================
--- activesambaldap/trunk/bin/asl-passwd	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/bin/asl-passwd	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,91 @@
+#!/usr/bin/env ruby
+
+require 'etc'
+
+require 'active_samba_ldap'
+require 'active_samba_ldap/command'
+
+argv, opts, options = ActiveSambaLdap::Command.parse_options do |opts, options|
+  options.update_samba_password = true
+  options.update_unix_password = true
+
+  opts.banner += " [USER_NAME]"
+
+  opts.on("-s", "--[no-]samba-password",
+          "update samba password (#{options.update_samba_password})") do |bool|
+    options.update_samba_password = bool
+  end
+
+  opts.on("-u", "--[no-]unix-password",
+          "update UNIX password (#{options.update_unix_password})") do |bool|
+    options.update_unix_password = bool
+  end
+end
+
+name = nil
+case argv.size
+when 0
+  name = Etc.getpwuid(Process.uid).name
+when 1
+  name = argv.first
+else
+  puts opts
+  exit 1
+end
+
+if !options.update_samba_password and !options.update_unix_password
+  puts "do nothing"
+  exit
+end
+
+ActiveSambaLdap::Base.establish_connection({}, true)
+
+class User < ActiveSambaLdap::User
+  ldap_mapping
+end
+
+user = User.new(name)
+unless user.exists?
+  puts "user '#{name}' doesn't exist."
+  exit 1
+end
+
+unless Process.uid.zero?
+  prompt = "Enter your current password: "
+  old_password = ActiveSambaLdap::Command.read_password(prompt)
+
+  ActiveSambaLdap::Base.close
+  begin
+    ActiveSambaLdap::Base.establish_connection({:bind_format => user.dn,
+                                                 :password => old_password,
+                                                 :allow_anonymous => false},
+                                               false)
+  rescue LDAP::InvalidCredentials
+    puts "password isn't match"
+    exit 1
+  end
+end
+
+password = ActiveSambaLdap::Command.read_password("New password: ")
+password2 = ActiveSambaLdap::Command.read_password("Retype new password: ")
+
+unless password == password2
+  puts "New passwords don't match."
+  exit 1
+end
+
+changed = false
+
+if options.update_unix_password
+  user.change_password(password)
+  changed = true
+end
+
+if options.update_samba_password
+  user.change_samba_password(password)
+  changed = true
+end
+
+user.save! if changed
+
+ActiveSambaLdap::Base.close

  Added: activesambaldap/trunk/test/test_asl_groupmod.rb (+278 -0)
===================================================================
--- activesambaldap/trunk/test/test_asl_groupmod.rb	2006-11-10 00:40:39 -15:00 (rev 0)
+++ activesambaldap/trunk/test/test_asl_groupmod.rb	2007-08-04 11:30:36 +09:00 (rev 1)
@@ -0,0 +1,278 @@
+require 'test/unit'
+require 'command_support'
+require 'asl_test_utils'
+require 'fileutils'
+require 'time'
+
+require 'active_samba_ldap'
+
+class AslGroupModTest < Test::Unit::TestCase
+  include CommandSupport
+  include AslTestUtils
+
+  def setup
+    super
+    @asl_groupmod = File.join(@bin_dir, "asl-groupmod")
+  end
+
+  def test_not_exist_group
+    assert_equal([false, "group 'not-exist' doesn't exist.\n"],
+                 run_asl_groupmod("not-exist"))
+  end
+
+  def test_rename
+    make_dummy_group do |group|
+      old_cn = group.cn(true)
+      new_cn = "#{old_cn}-new"
+      ensure_delete_group(new_cn) do
+        new_group = @group_class.new(new_cn)
+        assert(!new_group.exists?)
+
+        args = ["--rename", new_cn]
+        assert_asl_groupmod_successfully(group.cn(true), *args)
+
+        old_group = @group_class.new(old_cn)
+        assert(!old_group.exists?)
+        new_group = @group_class.new(new_cn)
+        assert(new_group.exists?)
+      end
+    end
+  end
+
+  def test_rename_with_members
+    make_dummy_user do |user1, password1|
+      make_dummy_user do |user2, password2|
+        make_dummy_group do |group|
+          group.add_member(user1)
+          group.add_member(user2)
+
+          old_cn = group.cn(true)
+          new_cn = "#{old_cn}-new"
+          ensure_delete_group(new_cn) do
+            new_group = @group_class.new(new_cn)
+            assert(!new_group.exists?)
+
+            args = ["--rename", new_cn]
+            assert_asl_groupmod_successfully(group.cn(true), *args)
+
+            old_group = @group_class.new(old_cn)
+            assert(!old_group.exists?)
+            new_group = @group_class.new(new_cn)
+            assert(new_group.exists?)
+
+            members = []
+            new_group.memberUid.each do |uid|
+              members.concat(@user_class.find_all(:attribute => "uid",
+                                                  :value => uid))
+            end
+            assert_equal([user1.uid(true), user2.uid(true)].sort,
+                         members.sort)
+          end
+        end
+      end
+    end
+  end
+
+  def test_rename_with_members_primary
+    make_dummy_user do |user1, password1|
+      make_dummy_user do |user2, password2|
+        make_dummy_group do |group|
+          user1.change_group(group)
+          user2.change_group(group)
+
+          old_cn = group.cn(true)
+          new_cn = "#{old_cn}-new"
+          ensure_delete_group(new_cn) do
+            new_group = @group_class.new(new_cn)
+            assert(!new_group.exists?)
+
+            args = ["--rename", new_cn]
+            assert_asl_groupmod_successfully(group.cn(true), *args)
+
+            old_group = @group_class.new(old_cn)
+            assert(!old_group.exists?)
+            new_group = @group_class.new(new_cn)
+            assert(new_group.exists?)
+
+            assert_equal(new_group.gidNumber, user1.gidNumber)
+            assert_equal(new_group.gidNumber, user2.gidNumber)
+          end
+        end
+      end
+    end
+  end
+
+  def test_gid_number
+    make_dummy_group do |group|
+      old_gid_number = group.gidNumber(true)
+      old_samba_sid = group.sambaSID(true)
+      new_gid_number = old_gid_number.succ
+
+      old_rid = (2 * Integer(old_gid_number) + 1001).to_s
+      new_rid = (2 * Integer(new_gid_number) + 1001).to_s
+      new_samba_sid = old_samba_sid.sub(/#{Regexp.escape(old_rid)}$/, new_rid)
+
+      args = ["--gid", new_gid_number]
+      assert_asl_groupmod_successfully(group.cn(true), *args)
+
+      new_group = @group_class.new(group.cn(true))
+      assert_equal(new_gid_number, new_group.gidNumber(true))
+      assert_equal(new_samba_sid, new_group.sambaSID(true))
+    end
+  end
+
+  def test_gid_number_non_unique
+    make_dummy_group do |group|
+      old_gid_number = group.gidNumber(true)
+      make_