On Tue, Jun 2, 2009 at 8:20 PM, Luke Kanies<[email protected]> wrote:
>
> 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.
>
Trouble here is that the user method returns and win32ole object.
Groups is a win32ole collection. The win32ole provider does not seem
to support collect at this point.
>>
>> + 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
>> + �[email protected]_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)
>> +
>> + �[email protected]_member("testuser")
>> + end
>> +
>> + it "should be able to remove a member" do
>> +
>> @adsi_mock.expects(:Remove).with("WinNT://testcomputername/testuser")
>> + �...@adsi_mock.expects(:SetInfo)
>> +
>> + �[email protected]_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
>> + �[email protected](:members).returns ['user1', 'user2']
>> +
>> + �[email protected](:remove_member).with('user1')
>> + �[email protected](:add_member).with('user3')
>> +
>> + �[email protected]_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)
>> +
>> + �[email protected]_is?('pwdwrong').should be_false
>> + �[email protected]_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)
>> +
>> + �[email protected]= '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'
>> + �[email protected](: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)
>> + }
>> +
>> + �[email protected]_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)
>> + }
>> +
>> + �[email protected]_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
-~----------~----~----~----~------~----~------~--~---