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