diff options
author | Nick Lewis <nick@puppetlabs.com> | 2011-08-09 13:04:10 -0700 |
---|---|---|
committer | Nick Lewis <nick@puppetlabs.com> | 2011-08-09 15:46:28 -0700 |
commit | b5fd95336e71ad428109cddf6cd2f33bdd31e025 (patch) | |
tree | 367881cdfb2b3a3df543f7fb479429efe65e16bc /lib/puppet/util/adsi.rb | |
parent | 2b9b7c114e7c599f88be4f3be70f504add8072f8 (diff) | |
download | puppet-b5fd95336e71ad428109cddf6cd2f33bdd31e025.tar.gz puppet-b5fd95336e71ad428109cddf6cd2f33bdd31e025.tar.xz puppet-b5fd95336e71ad428109cddf6cd2f33bdd31e025.zip |
(#8408/8409) Add a Windows ADSI helper module
This module (Puppet::Util::ADSI) provides access to Active Directory Services
Interfaces, using win32ole. The base module has methods for generating resource
URIs and connecting to ADSI.
It also provides classes Puppet::Util::ADSI::User and Puppet::Util::ADSI::Group
for managing Active Directory users and groups, along with their properties and
group memberships. This will be used to implement the Windows ADSI user and
group providers.
Based on work by: Joel Rosario <joel.r@.internal.directi.com>
Based on work by: Cameron Thomas <cameron@puppetlabs.com>
Reviewed-By: Matt Robinson <matt@puppetlabs.com>
Diffstat (limited to 'lib/puppet/util/adsi.rb')
-rw-r--r-- | lib/puppet/util/adsi.rb | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb new file mode 100644 index 000000000..f865743e2 --- /dev/null +++ b/lib/puppet/util/adsi.rb @@ -0,0 +1,278 @@ +module Puppet::Util::ADSI + class << self + def connectable?(uri) + begin + !! connect(uri) + rescue + false + end + end + + def connect(uri) + begin + WIN32OLE.connect(uri) + rescue Exception => e + raise Puppet::Error.new( "ADSI connection error: #{e}" ) + end + end + + def create(name, resource_type) + Puppet::Util::ADSI.connect(computer_uri).Create(resource_type, name) + end + + def delete(name, resource_type) + Puppet::Util::ADSI.connect(computer_uri).Delete(resource_type, name) + end + + def computer_name + unless @computer_name + buf = " " * 128 + Win32API.new('kernel32', 'GetComputerName', ['P','P'], 'I').call(buf, buf.length.to_s) + @computer_name = buf.unpack("A*") + end + @computer_name + end + + def computer_uri + "WinNT://#{computer_name}" + end + + def wmi_resource_uri( host = '.' ) + "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" + end + + def uri(resource_name, resource_type) + "#{computer_uri}/#{resource_name},#{resource_type}" + end + + def execquery(query) + connect(wmi_resource_uri).execquery(query) + end + end + + class User + extend Enumerable + + attr_accessor :native_user + attr_reader :name + def initialize(name, native_user = nil) + @name = name + @native_user = native_user + end + + def native_user + @native_user ||= Puppet::Util::ADSI.connect(uri) + end + + def self.uri(name) + Puppet::Util::ADSI.uri(name, 'user') + end + + def uri + self.class.uri(name) + end + + def self.logon(name, password) + fLOGON32_LOGON_NETWORK = 3 + 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(name, "", password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token) != 0 + close_handle.call(token.unpack('L')[0]) + true + else + false + end + end + + def [](attribute) + native_user.Get(attribute) + end + + def []=(attribute, value) + native_user.Put(attribute, value) + end + + def commit + begin + native_user.SetInfo unless native_user.nil? + rescue Exception => e + raise Puppet::Error.new( "User update failed: #{e}" ) + end + self + end + + def password_is?(password) + self.class.logon(name, password) + end + + def add_flag(flag_name, value) + flag = native_user.Get(flag_name) rescue 0 + + native_user.Put(flag_name, flag | value) + + commit + end + + def password=(password) + native_user.SetPassword(password) + commit + fADS_UF_DONT_EXPIRE_PASSWD = 0x10000 + add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD) + end + + def groups + # WIN32OLE objects aren't enumerable, so no map + groups = [] + native_user.Groups.each {|g| groups << g.Name} + groups + end + + def add_to_groups(*group_names) + group_names.each do |group_name| + Puppet::Util::ADSI::Group.new(group_name).add_member(@name) + end + end + alias add_to_group add_to_groups + + def remove_from_groups(*group_names) + group_names.each do |group_name| + Puppet::Util::ADSI::Group.new(group_name).remove_member(@name) + end + end + alias remove_from_group remove_from_groups + + def set_groups(desired_groups, minimum = true) + return if desired_groups.nil? or desired_groups.empty? + + desired_groups = desired_groups.split(',').map(&:strip) + + current_groups = self.groups + + # First we add the user to all the groups it should be in but isn't + groups_to_add = desired_groups - current_groups + add_to_groups(*groups_to_add) + + # Then we remove the user from all groups it is in but shouldn't be, if + # that's been requested + groups_to_remove = current_groups - desired_groups + remove_from_groups(*groups_to_remove) unless minimum + end + + def self.create(name) + new(name, Puppet::Util::ADSI.create(name, 'user')) + end + + def self.exists?(name) + Puppet::Util::ADSI::connectable?(User.uri(name)) + end + + def self.delete(name) + Puppet::Util::ADSI.delete(name, 'user') + end + + def self.each(&block) + wql = Puppet::Util::ADSI.execquery("select * from win32_useraccount") + + users = [] + wql.each do |u| + users << new(u.name, u) + end + + users.each(&block) + end + end + + class Group + extend Enumerable + + attr_accessor :native_group + attr_reader :name + def initialize(name, native_group = nil) + @name = name + @native_group = native_group + end + + def uri + self.class.uri(name) + end + + def self.uri(name) + Puppet::Util::ADSI.uri(name, 'group') + end + + def native_group + @native_group ||= Puppet::Util::ADSI.connect(uri) + end + + def commit + begin + native_group.SetInfo unless native_group.nil? + rescue Exception => e + raise Puppet::Error.new( "Group update failed: #{e}" ) + end + self + end + + def add_members(*names) + names.each do |name| + native_group.Add(Puppet::Util::ADSI::User.uri(name)) + end + end + alias add_member add_members + + def remove_members(*names) + names.each do |name| + native_group.Remove(Puppet::Util::ADSI::User.uri(name)) + end + end + alias remove_member remove_members + + def members + # WIN32OLE objects aren't enumerable, so no map + members = [] + native_group.Members.each {|m| members << m.Name} + members + end + + def set_members(desired_members) + return if desired_members.nil? or desired_members.empty? + + current_members = self.members + + # First we add all missing members + members_to_add = desired_members - current_members + add_members(*members_to_add) + + # Then we remove all extra members + members_to_remove = current_members - desired_members + remove_members(*members_to_remove) + end + + def self.create(name) + new(name, Puppet::Util::ADSI.create(name, 'group')) + end + + def self.exists?(name) + Puppet::Util::ADSI.connectable?(Group.uri(name)) + end + + def self.delete(name) + Puppet::Util::ADSI.delete(name, 'group') + end + + def self.each(&block) + wql = Puppet::Util::ADSI.execquery( "select * from win32_group" ) + + groups = [] + wql.each do |g| + groups << new(g.name, g) + end + + groups.each(&block) + end + end +end |