diff --git a/lib/puppet/util/windows_system.rb
b/lib/puppet/util/windows_system.rb
new file mode 100644
index 0000000..737a218
--- /dev/null
+++ b/lib/puppet/util/windows_system.rb
@@ -0,0 +1,217 @@
+if Puppet.features.windows?
+    require 'win32ole'
+    require 'Win32API'
+end
+
+module Puppet::Util::ADSI
+    def self.connectable?(uri)
+        begin
+            adsi_obj = WIN32OLE.connect(uri)
+            return adsi_obj != nil;
+        rescue
+        end
+
+        return false
+    end
+
+       def self.connect(uri)
+               WIN32OLE.connect(uri)
+       end
+end
+
+module Puppet::Util::Windows
+    class Resource
+        def Resource.uri(resource_name)
+            "#{Computer.resource_uri}/#{resource_name}"
+        end
+    end
+
+    class User
+        def initialize(username, native_adsi_obj = nil)
+            @username = username
+            @user = native_adsi_obj
+        end
+
+        def user
+            @user =
Puppet::Util::ADSI.connect(User.resource_uri(@username)) if @user ==
nil
+            return @user
+        end
+
+        def password_is?(password)
+            API.LogonUser(@username, password)
+        end
+
+        def add_flag(flag_name, value)
+            flag = 0
+
+            begin
+                flag = user.Get(flag_name)
+            rescue
+            end
+
+            user.Put(flag_name, flag | value)
+            user.SetInfo
+        end
+
+        def password=(password)
+            user.SetPassword(password)
+            user.SetInfo
+
+            fADS_UF_DONT_EXPIRE_PASSWD = 0x10000
+            add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD)
+        end
+
+        def groups
+            groups = []
+            user.Groups.each {|group| groups << group.name }
+            return groups
+        end
+
+        def add_to_groups(group_names)
+            group_names.each {|name|
Group.new(name).add_member(@username) } if group_names.length > 0
+        end
+
+        def remove_from_groups(group_names)
+            group_names.each {|name|
Group.new(name).remove_member(@username) } if group_names.length > 0
+        end
+
+        def set_groups(names, minimum = true)
+            return if names == nil || names.strip.length == 0
+
+            names = names.strip.split(',')
+            current_groups = groups
+
+            names_to_add = names.find_all {|name|
!current_groups.include?(name) }
+            add_to_groups(names_to_add)
+
+            names_to_remove = current_groups.find_all {|name|
!names.include?(name) }
+            remove_from_groups(names_to_remove) if minimum == false
+        end
+
+        def User.resource_uri(username)
+            return "#{Resource.uri(username)},user"
+        end
+
+        def User.exists?(username)
+            return
Puppet::Util::ADSI::connectable?(User.resource_uri(username))
+        end
+
+        def User.create(username, password)
+            newuser = new(username, Computer.create("user", username))
+            newuser.password = password
+            yield newuser if block_given?
+            return newuser
+        end
+
+        def User.delete(username)
+            Computer.delete("user", username)
+        end
+    end
+
+    class Group
+        def initialize(groupname, native_adsi_obj = nil)
+            @groupname = groupname
+            @group = native_adsi_obj
+        end
+
+        def resource_uri
+            Group.resource_uri(@groupname)
+        end
+
+        def Group.resource_uri(name)
+            "#{Resource.uri(name)},group"
+        end
+
+        def group
+            @group = Puppet::Util::ADSI.connect(resource_uri) if @group == nil
+            return @group
+        end
+
+        def add_member(name)
+            group.Add(Resource.uri(name))
+            group.SetInfo
+        end
+
+        def remove_member(name)
+            group.Remove(Resource.uri(name))
+            group.SetInfo
+        end
+
+        def members
+            list = []
+            group.Members.each {|member| list << member.Name }
+            list
+        end
+
+        def set_members(members)
+            return nil if members == nil || members.length == 0
+
+            current_members = self.members
+
+            members.inject([]) {|members_to_add, member|
current_members.include?(member) ? members_to_add : members_to_add <<
member }.each {|member| add_member(member) }
+            current_members.inject([]) {|members_to_remove, member|
members.include?(member) ? members_to_remove : members_to_remove <<
member }.each {|member| remove_member(member) }
+        end
+
+        def Group.create(name)
+            newgroup = new(name, Computer.create("group", name))
+            yield newgroup if block_given?
+            return newgroup
+        end
+
+        def Group.exists?(name)
+            return Puppet::Util::ADSI.connectable?(Group.resource_uri(name))
+        end
+
+        def Group.delete(name)
+            Computer.delete("group", name)
+        end
+    end
+
+    module API
+        def self.GetComputerName
+            name = " " * 128
+            size = "128"
+            
Win32API.new('kernel32','GetComputerName',['P','P'],'I').call(name,size)
+            return name.unpack("A*")
+        end
+
+        def self.LogonUser(username, password)
+            fLOGON32_LOGON_NETWORK_CLEARTEXT = 8
+            fLOGON32_PROVIDER_DEFAULT = 0
+
+            logon_user = Win32API.new("advapi32", "LogonUser", ['P',
'P', 'P', 'L', 'L', 'P'], 'L')
+            close_handle = Win32API.new("kernel32", "CloseHandle", ['P'], 'V')
+
+            token = ' ' * 4
+            if logon_user.call(username, "", password,
fLOGON32_LOGON_NETWORK_CLEARTEXT, fLOGON32_PROVIDER_DEFAULT, token) ==
1
+                close_handle.call(token.unpack('L')[0])
+                return true
+            end
+
+            return false
+        end
+    end
+
+    class Computer
+        def Computer.name
+            API.GetComputerName
+        end
+
+        def Computer.resource_uri
+            computer_name = Computer.name
+            return "WinNT://#{computer_name}"
+        end
+
+        def Computer.api
+            return Puppet::Util::ADSI.connect(Computer.resource_uri)
+        end
+
+        def Computer.create(resource_type, name)
+            Computer.api.create(resource_type, name).SetInfo
+        end
+
+        def Computer.delete(resource_type, name)
+            Computer.api.Delete(resource_type, name)
+        end
+    end
+end
diff --git a/spec/unit/util/windows_system.rb b/spec/unit/util/windows_system.rb
new file mode 100644
index 0000000..1f1906f
--- /dev/null
+++ b/spec/unit/util/windows_system.rb
@@ -0,0 +1,254 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe "Puppet::Util::Windows" do
+    confine :true => Puppet.features.windows?
+
+    require 'puppet/util/windows_system'
+
+    before(:each) do
+        @computername = "testcomputername"
+        Puppet::Util::Windows::API.stubs(:GetComputerName).returns
"testcomputername"
+    end
+
+    describe Puppet::Util::Windows::Resource do
+        describe "Given a resource name" do
+            it "should return the computer uri with the resource name
appended to at the end" do
+
Puppet::Util::Windows::Resource.uri("test,user").should
be_eql("WinNT://testcomputername/test,user")
+            end
+        end
+    end
+
+    describe Puppet::Util::Windows::Computer do
+        it "should be able to get the name of the computer" do
+            Puppet::Util::Windows::Computer.name.should
be_eql("testcomputername")
+        end
+
+        it "should be able to provide a WinNT resource uri for the computer" do
+            Puppet::Util::Windows::Computer.resource_uri.should
be_eql("WinNT://testcomputername")
+        end
+
+        describe "When asked for an api object" do
+            it "should connect to the computer resource uri and
return the resulting adsi object" do
+                Puppet::Util::ADSI.expects(:connect).returns "connected"
+                Puppet::Util::Windows::Computer.api.should be_eql("connected")
+            end
+        end
+
+        it "should be able to create a resource" do
+            adsi_obj_mock = mock("adsi_obj")
+            adsi_obj_mock.expects(:SetInfo)
+
+            adsi_mock = mock("adsi")
+            adsi_mock.expects(:create).with("user",
"testuser").returns adsi_obj_mock
+
+            
Puppet::Util::ADSI.expects(:connect).with("WinNT://testcomputername").returns
adsi_mock
+            Puppet::Util::Windows::Computer.create("user", "testuser")
+        end
+
+        it "should be abl to delete a resource" do
+            adsi_mock = mock("adsi")
+            adsi_mock.expects(:Delete).with("user", "testuser")
+
+            
Puppet::Util::ADSI.expects(:connect).with("WinNT://testcomputername").returns
adsi_mock
+            Puppet::Util::Windows::Computer.delete("user", "testuser")
+        end
+    end
+
+    describe Puppet::Util::Windows::Group do
+        before(:each) { @groupname = "testgroup" }
+
+        describe "An instance" do
+            before(:each) do
+                @adsi_mock = mock("adsi")
+                @group = Puppet::Util::Windows::Group.new(@groupname,
@adsi_mock)
+            end
+
+            describe "Given a group named #...@groupname}" do
+                it "should provide a resource uri
WinNT://testcomputername/#...@groupname},group" do
+                    @group.resource_uri.should
be_eql("WinNT://testcomputername/#...@groupname},group")
+                end
+            end
+
+            it "should be able to add a member" do
+
@adsi_mock.expects(:Add).with("WinNT://testcomputername/testuser")
+                @adsi_mock.expects(:SetInfo)
+
+                @group.add_member("testuser")
+            end
+
+            it "should be able to remove a member" do
+
@adsi_mock.expects(:Remove).with("WinNT://testcomputername/testuser")
+                @adsi_mock.expects(:SetInfo)
+
+                @group.remove_member("testuser")
+            end
+
+            describe "when asked for a list of members" do
+                it "should return a list of member names, not objects" do
+                    member_names = ['member1', 'member2']
+                    member_mocks = member_names.collect
{|member_name| member_mock = mock('member');
member_mock.expects(:Name).returns(member_name); member_mock }
+
+                    @adsi_mock.expects(:Members).returns(member_mocks)
+
+                    members = @group.members
+
+                    members.length.should be_eql(2)
+                    members.each {|member|
member_names.include?(member).should be_true }
+                end
+            end
+
+            it "should be able to add a list of users to a group" do
+                @group.expects(:members).returns ['user1', 'user2']
+
+                @group.expects(:remove_member).with('user1')
+                @group.expects(:add_member).with('user3')
+
+                @group.set_members(['user2', 'user3'])
+            end
+        end
+
+        it "should be able to create a group" do
+            Puppet::Util::Windows::Computer.expects(:create).with("group",
@groupname)
+            got_called = false
+
+            group = Puppet::Util::Windows::Group.create(@groupname) {
got_called = true }
+            got_called.should be_true
+            group.is_a?(Puppet::Util::Windows::Group).should be_true
+        end
+
+        it "should be able to confirm the existence of a group" do
+            
Puppet::Util::ADSI.expects(:connectable?).with("WinNT://testcomputername/#...@groupname},group").returns
true
+            Puppet::Util::Windows::Group.exists?(@groupname).should be_true
+        end
+
+        it "should be able to delete a group" do
+            Puppet::Util::Windows::Computer.expects(:delete).with("group",
@groupname)
+            Puppet::Util::Windows::Group.delete(@groupname)
+        end
+    end
+
+    describe Puppet::Util::Windows::User do
+        before(:each) { @username = "testuser" }
+
+        describe "An instance" do
+            before(:each) do
+                @adsi_mock = mock("adsi")
+                @user = Puppet::Util::Windows::User.new(@username, @adsi_mock)
+            end
+
+            describe "when asked for a list of groups which it's a
member of" do
+                it "should provide a list of names, not object" do
+                    group_names = ["group1", "group2"]
+                    group_mocks = group_names.collect {|group_name|
group_mock = mock("group");
group_mock.expects(:name).returns(group_name); group_mock }
+
+                    @adsi_mock.expects(:Groups).returns(group_mocks)
+
+                    groups = @user.groups
+
+                    groups.length.should be_eql(2)
+                    groups.each {|group|
group_names.include?(group).should be_true }
+                end
+            end
+
+            it 'should be able to test whether the given password is
correct' do
+
Puppet::Util::Windows::API.expects(:LogonUser).with(@username,
'pwdwrong').returns(false)
+
Puppet::Util::Windows::API.expects(:LogonUser).with(@username,
'pwdright').returns(true)
+
+                @user.password_is?('pwdwrong').should be_false
+                @user.password_is?('pwdright').should be_true
+            end
+
+            it 'should be able to set a user\'s password' do
+                @adsi_mock.expects(:SetPassword).with('pwd')
+                @adsi_mock.expects(:SetInfo).twice
+
+                flagname = "UserFlags"
+                fADS_UF_DONT_EXPIRE_PASSWD = 0x10000
+
+                @adsi_mock.expects(:Get).with(flagname).returns(0)
+                @adsi_mock.expects(:Put).with(flagname,
fADS_UF_DONT_EXPIRE_PASSWD)
+
+                @user.password= 'pwd'
+            end
+
+            describe 'when given a set of groups to which to add the user' do
+                def mock_object(name)
+                    obj = mock(name)
+                    yield(obj) if block_given?
+                    return obj
+                end
+
+                before(:each) do
+                    @groups_to_set = 'group1,group2'
+                    @user.expects(:groups).returns ['group2', 'group3']
+                end
+
+                describe 'if membership is specified as inclusive' do
+                    it 'should add the user to those groups, and
remove it from groups not in the list' do
+
Puppet::Util::ADSI.expects(:connect).with('WinNT://testcomputername/group1,group').returns
mock_object('adsi') {|m|
+
m.expects(:Add).with('WinNT://testcomputername/testuser')
+                            m.expects(:SetInfo)
+                        }
+
+
Puppet::Util::ADSI.expects(:connect).with('WinNT://testcomputername/group3,group').returns
mock_object('adsi') {|m|
+
m.expects(:Remove).with('WinNT://testcomputername/testuser')
+                            m.expects(:SetInfo)
+                        }
+
+                        @user.set_groups(@groups_to_set, false)
+                    end
+                end
+
+                describe 'if membership is specified as minimum' do
+                    it 'should add the user to the specified groups
without affecting it\'s other memberships' do
+
Puppet::Util::ADSI.expects(:connect).with('WinNT://testcomputername/group1,group').returns
mock_object('adsi') {|m|
+
m.expects(:Add).with('WinNT://testcomputername/testuser')
+                            m.expects(:SetInfo)
+                        }
+
+                        @user.set_groups(@groups_to_set, true)
+                    end
+                end
+            end
+        end
+
+        describe "Given a user named #...@username}" do
+            it "should provide a resource uri
WinNT://testcomputername/testuser,user" do
+
Puppet::Util::Windows::User.resource_uri(@username).should
be_eql("WinNT://testcomputername/testuser,user")
+            end
+        end
+
+        it "should be able to create a user" do
+            password = 'pwd'
+
+            adsi_obj = mock("adsi")
+            adsi_obj.expects(:SetPassword).with(password)
+            adsi_obj.expects(:SetInfo).twice
+
+            flagname = "UserFlags"
+            fADS_UF_DONT_EXPIRE_PASSWD = 0x10000
+
+            adsi_obj.expects(:Get).with(flagname).returns(0)
+            adsi_obj.expects(:Put).with(flagname, fADS_UF_DONT_EXPIRE_PASSWD)
+
+            Puppet::Util::Windows::Computer.expects(:create).with("user",
@username).returns(adsi_obj)
+            got_called = false
+
+            user = Puppet::Util::Windows::User.create(@username,
password) { got_called = true }
+            got_called.should be_true
+            user.is_a?(Puppet::Util::Windows::User).should be_true
+        end
+
+        it "should be able to confirm the existence of a user" do
+            
Puppet::Util::ADSI.expects(:connectable?).with("WinNT://testcomputername/#...@username},user").returns
true
+            Puppet::Util::Windows::User.exists?(@username).should be_true
+        end
+
+        it "should be able to delete a group" do
+            Puppet::Util::Windows::Computer.expects(:delete).with("user",
@username)
+            Puppet::Util::Windows::User.delete(@username)
+        end
+    end
+end

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to