From: Joel Rosario <[email protected]>
Signed-off-by: James Turnbull <[email protected]> --- Local-branch: tickets/master/6693 lib/puppet/provider/group/groupadd_win.rb | 30 ++++ lib/puppet/provider/user/useradd_win.rb | 42 ++++++ lib/puppet/util/windows_system.rb | 205 +++++++++++++++++++++++++++++ test/ral/providers/group_win.rb | 39 ++++++ test/ral/providers/user/useradd_win.rb | 63 +++++++++ test/ral/providers/windowstest.rb | 95 +++++++++++++ 6 files changed, 474 insertions(+), 0 deletions(-) create mode 100644 lib/puppet/provider/group/groupadd_win.rb create mode 100644 lib/puppet/provider/user/useradd_win.rb create mode 100644 lib/puppet/util/windows_system.rb create mode 100644 test/ral/providers/group_win.rb create mode 100644 test/ral/providers/syslog.rb create mode 100644 test/ral/providers/user/useradd_win.rb create mode 100644 test/ral/providers/windowstest.rb diff --git a/lib/puppet/provider/group/groupadd_win.rb b/lib/puppet/provider/group/groupadd_win.rb new file mode 100644 index 0000000..a2ce88d --- /dev/null +++ b/lib/puppet/provider/group/groupadd_win.rb @@ -0,0 +1,30 @@ +require 'puppet/util/windows_system' + +Puppet::Type.type(:group).provide :groupadd_win do + desc "Group management for windows" + + defaultfor :operatingsystem => :windows + + has_features :manages_members + + def members + Windows::Group.new(@resource[:name]).members + end + + def members=(members) + Windows::Group.new(@resource[:name]).set_members(members) + end + + def create + group = Windows::Group.create(@resource[:name]) + group.set_members(@resource[:members]) + end + + def exists? + Windows::Group.exists?(@resource[:name]) + end + + def delete + Windows::Group.delete(@resource[:name]) + end +end diff --git a/lib/puppet/provider/user/useradd_win.rb b/lib/puppet/provider/user/useradd_win.rb new file mode 100644 index 0000000..5f9b969 --- /dev/null +++ b/lib/puppet/provider/user/useradd_win.rb @@ -0,0 +1,42 @@ +require 'puppet/provider' +require 'puppet/util/windows_system' + +raise "ERROR: A windowsuser resource can only be configured on Windows. This OS is is #{Facter['kernel'].value}" if Facter['kernel'].value != 'windows' + +Puppet::Type.type(:user).provide :useradd_win do + desc "User management for windows" + + defaultfor :operatingsystem => :windows + + has_features :manages_passwords + + def password + name, password = @resource[:name], @resource[:password] + Windows::User.new(name).password_is?(password) ?password :"" rescue :absent + end + + def password=(pwd) + Windows::User.new(@resource[:name]).password = @resource[:password] + end + + def groups + Windows::User.new(@resource[:name]).groups.join(',') rescue :absent + end + + def groups=(groups) + Windows::User.new(@resource[:name]).set_groups(groups) + end + + def create + user = Windows::User.create(@resource[:name], @resource[:password]) + user.set_groups(@resource[:groups], @resource[:membership] == :minimum) + end + + def exists? + return Windows::User.exists?(@resource[:name]) + end + + def delete + Windows::User.delete(@resource[:name]) + end +end diff --git a/lib/puppet/util/windows_system.rb b/lib/puppet/util/windows_system.rb new file mode 100644 index 0000000..83bc52e --- /dev/null +++ b/lib/puppet/util/windows_system.rb @@ -0,0 +1,205 @@ +if Facter.operatingsystem == "windows" + require 'win32ole' + require 'Win32API' +end + +module ADSI + def ADSI.connectable?(uri) + begin + adsi_obj = WIN32OLE.connect(uri) + return adsi_obj != nil; + rescue + end + + return false + end +end + +module 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 = WIN32OLE.connect(User.resource_uri(@username)) if @user == nil + return @user + end + + def password_is?(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 + + 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_user(@username) } if group_names.length > 0 + end + + def remove_from_groups(group_names) + group_names.each {|name| Group.new(name).remove_user(@username) } if group_names.length > 0 + end + + def set_groups(names, minimal = true) + return if names == nil || names.strip.length == 0 + + names = names.strip.split(',') + current_groups = groups + + names.find_all {|name| !current_groups.include?(name) }.tap {|names_to_add| add_to_groups(names_to_add) } + current_groups.find_all {|name| !names.include?(name) }.tap {|names_to_remove| remove_from_groups(names_to_remove) } if minimal == false + end + + def User.resource_uri(username) + return "#{Resource.uri(username)},user" + end + + def User.exists?(username) + return ADSI::connectable?(User.resource_uri(username)) + end + + def User.create(username, password) + User.new(username, Computer.create("user", username)).tap {|user| user.password = password; yield user if block_given? } + 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 = WIN32OLE.connect(resource_uri) if @group == nil + return @group + end + + def add_user(username) + group.Add(User.resource_uri(username)) + group.SetInfo + end + + def remove_user(username) + group.Remove(User.resource_uri(username)) + group.SetInfo + 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) + Windows::Group.new(name, Computer.create("group", name)).tap {|group| yield group if block_given? } + end + + def Group.exists?(name) + return ADSI::connectable?(Group.resource_uri(name)) + end + + def Group.delete(name) + Computer.delete("group", name) + end + end + + class Computer + def Computer.name + name = " " * 128 + size = "128" + Win32API.new('kernel32','GetComputerName',['P','P'],'I').call(name,size) + return name.unpack("A*") + end + + def Computer.resource_uri + computer_name = Computer.name + return "WinNT://#{computer_name}" + end + + def Computer.api + return WIN32OLE.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/test/ral/providers/group_win.rb b/test/ral/providers/group_win.rb new file mode 100644 index 0000000..d341fbd --- /dev/null +++ b/test/ral/providers/group_win.rb @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../lib/puppettest' +require 'windowstest' + +require File.dirname(__FILE__) + '/../../../lib/puppet/provider/group/groupadd_win.rb' + +class TestGroupProvider < Test::Unit::TestCase + include WindowsTest + + def group_provider(resource_configuration) + Puppet::Type::Group::ProviderGroupadd_win.new.tap {|provider| provider.resource = resource_configuration } + end + + def test_groupGetsCreated + groupname = "randomgroup" + register_group groupname + + expected_members = ["test1", "test2"] + mkusers(expected_members) + + provider = group_provider :name => groupname, :members => ['test1', 'test2'] + + assert_nothing_raised { provider.create } + assert_no_missing_member(group(groupname), expected_members) + end + + def test_groupMembersGetSet + groupname = "randomgroup" + group = mkgroup(groupname) + expected_members = ["test1", "test2"] + mkusers(expected_members) + + provider = group_provider :name => groupname, :members => ['test1', 'test2'] + + assert_nothing_raised { provider.members = ['test1', 'test2'] } + assert_no_missing_member(group, expected_members) + end +end diff --git a/test/ral/providers/syslog.rb b/test/ral/providers/syslog.rb new file mode 100644 index 0000000..e69de29 diff --git a/test/ral/providers/user/useradd_win.rb b/test/ral/providers/user/useradd_win.rb new file mode 100644 index 0000000..11d31e7 --- /dev/null +++ b/test/ral/providers/user/useradd_win.rb @@ -0,0 +1,63 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../lib/puppettest' +require 'windowstest' + +require File.dirname(__FILE__) + '/../../../../lib/puppet/provider/user/useradd_win.rb' + +class TestUserProvider < Test::Unit::TestCase + include WindowsTest + + def user_provider(resource_configuration) + Puppet::Type::User::ProviderUseradd_win.new.tap {|provider| provider.resource = resource_configuration } + end + + def test_userIsCreated + expected_groups = ["randomgroup1", "randomgroup2"] + mkgroups(expected_groups) + + username = "testuser" + register_user username + + password = "1234" + + provider = user_provider :name => username, :password => password, :groups => expected_groups.join(",") + + assert_nothing_raised { provider.create } + + user = Windows::User.new(username) + assert(user.password_is?(password), "Password of user #{username} should be #{password}") + user.groups.tap {|groups| + expected_groups.each {|expected_group| assert(groups.include?(expected_group), "User should be a member of #{expected_group}") } + assert(expected_groups.length == groups.length, "The user should be a member of #{expected_groups.length} groups.") + } + end + + def test_userGroupsAreSet + expected_groups = ["randomgroup1", "randomgroup2"] + mkgroups expected_groups + + username = "testuser" + mkuser username + + provider = user_provider :name => username + provider.groups = expected_groups.join(",") + + provider.groups.split(',').collect {|group| group.strip }.tap {|groups| + assert(groups.length == expected_groups.length, "The user should be a member of #{expected_groups.length} groups.") + groups.each {|group| assert(expected_groups.include?(group), "The user should be a member of #{group}") } + } + end + + def test_usersPasswordIsSet + username = "testuser" + password = "11112222" + + user = mkuser username, password + + provider = user_provider :name => username, :password => password + provider.password = password + + assert(user.password_is?(password), "User #{username}'s password should be #{password}.") + end +end diff --git a/test/ral/providers/windowstest.rb b/test/ral/providers/windowstest.rb new file mode 100644 index 0000000..b9ed1e5 --- /dev/null +++ b/test/ral/providers/windowstest.rb @@ -0,0 +1,95 @@ +require File.dirname(__FILE__) + '/../../../lib/puppet/util/windows_system.rb' + +module WindowsTest + class List + def initialize + @list = [] + end + + def clear + destroy + @list = [] + end + + def register(item) + @list << item + end + end + + class Groups < List + def destroy + @list.each {|group| + begin + Windows::Group.delete(group) + rescue + puts "Group #{group} not found" + end + } + end + end + + class Users < List + def destroy + @list.each {|user| + begin + Windows::User.delete(user) + rescue + puts "User #{user} not found" + end + } + end + end + + def helper_users + @users = Users.new if @users == nil + @users + end + + def helper_groups + @groups = Groups.new if @groups == nil + @groups + end + + def clear + helper_groups.clear + helper_users.clear + end + + def register_group(name) + helper_groups.register name + end + + def register_user(name) + helper_users.register name + end + + def mkuser(name, password = "1234567") + Windows::User.create(name, password) { register_user name } + end + + def mkgroup(name) + Windows::Group.create(name) { register_group name } + end + + def mkusers(names) + names.collect {|name| mkuser name } + end + + def mkgroups(names) + names.collect {|name| mkgroup name } + end + + def group(name) + Windows::Group.new(name) + end + + def assert_no_missing_member(group, expected_members) + group.members.tap {|members| + expected_members.each {|member| assert(members.include?(member), "#{member} should be a member") } + } + end + + def teardown + clear + end +end -- 1.7.1 -- 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.
