I've only got a couple of cosmetic comments below.  All of the code  
looks really close.

On May 24, 2009, at 10:51 AM, joel r wrote:

>
> 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

Here you can do 'user.Groups.collect { |group| group.name }' (and  
again in similar code below).  No temporary variable necessary.

>
> +        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

As above, this can be shortened a bit.

>
> +        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
>
> >


-- 
I worry that the person who thought up Muzak may be thinking up
something else. -- Lily Tomlin
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com


--~--~---------~--~----~------------~-------~--~----~
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