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.

Reply via email to