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