Class: Puppet::Util::Windows::ADSI::User

Inherits:
ADSIObject show all
Extended by:
FFI::Library
Defined in:
lib/puppet/util/windows.rb,
lib/puppet/util/windows/adsi.rb

Constant Summary collapse

ADS_USERFLAGS =

Declare all of the available user flags on the system. Note that ADS_UF is read as ADS_UserFlag

https://docs.microsoft.com/en-us/windows/desktop/api/iads/ne-iads-ads_user_flag

and

https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user--pro

for the flag values.

{
  ADS_UF_SCRIPT:                                 0x0001,
  ADS_UF_ACCOUNTDISABLE:                         0x0002,
  ADS_UF_HOMEDIR_REQUIRED:                       0x0008,
  ADS_UF_LOCKOUT:                                0x0010,                          
  ADS_UF_PASSWD_NOTREQD:                         0x0020,                   
  ADS_UF_PASSWD_CANT_CHANGE:                     0x0040,               
  ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED:        0x0080,       
  :                 0x0100,           
  :                         0x0200,                   
  :              0x0800,        
  :              0x1000,        
  :                   0x2000,             
  ADS_UF_DONT_EXPIRE_PASSWD:                     0x10000,            
  :                      0x20000,               
  ADS_UF_SMARTCARD_REQUIRED:                     0x40000,              
  ADS_UF_TRUSTED_FOR_DELEGATION:                 0x80000,          
  ADS_UF_NOT_DELEGATED:                          0x100000,                  
  ADS_UF_USE_DES_KEY_ONLY:                       0x200000,               
  ADS_UF_DONT_REQUIRE_PREAUTH:                   0x400000,               
  ADS_UF_PASSWORD_EXPIRED:                       0x800000,               
  ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0x1000000
}
MAX_USERNAME_LENGTH =

UNLEN from lmcons.h - stackoverflow.com/a/2155176

256

Instance Attribute Summary

Attributes inherited from ADSIObject

#name

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FFI::Library

attach_function_private

Methods inherited from ADSIObject

#[], #[]=, #commit, delete, each, exists?, get_sids, #initialize, localized_domains, name_sid_hash, #native_object, #object_class, parse_name, #sid, uri, #uri

Methods included from Enumerable

#uniq

Constructor Details

This class inherits a constructor from Puppet::Util::Windows::ADSI::ADSIObject

Class Method Details

.create(name) ⇒ Object

Raises:


281
282
283
284
285
# File 'lib/puppet/util/windows/adsi.rb', line 281

def create(name)
  # Windows error 1379: The specified local group already exists.
  raise Puppet::Error.new(_("Cannot create user if group '%{name}' exists.") % { name: name }) if Puppet::Util::Windows::ADSI::Group.exists? name
  new(name, Puppet::Util::Windows::ADSI.create(name, @object_class))
end

.current_user_nameObject


459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/puppet/util/windows/adsi.rb', line 459

def self.current_user_name
  user_name = ''
  max_length = MAX_USERNAME_LENGTH + 1 # NULL terminated
  FFI::MemoryPointer.new(max_length * 2) do |buffer| # wide string
    FFI::MemoryPointer.new(:dword, 1) do |buffer_size|
      buffer_size.write_dword(max_length) # length in TCHARs

      if GetUserNameW(buffer, buffer_size) == FFI::WIN32_FALSE
        raise Puppet::Util::Windows::Error.new(_("Failed to get user name"))
      end
      # buffer_size includes trailing NULL
      user_name = buffer.read_wide_string(buffer_size.read_dword - 1)
    end
  end

  user_name
end

.current_user_sidObject


477
478
479
# File 'lib/puppet/util/windows/adsi.rb', line 477

def self.current_user_sid
  Puppet::Util::Windows::SID.name_to_principal(current_user_name)
end

.list_allObject


273
274
275
# File 'lib/puppet/util/windows/adsi.rb', line 273

def list_all
  Puppet::Util::Windows::ADSI.execquery('select name from win32_useraccount where localaccount = "TRUE"')
end

.logon(name, password) ⇒ Object


277
278
279
# File 'lib/puppet/util/windows/adsi.rb', line 277

def logon(name, password)
  Puppet::Util::Windows::User.password_is?(name, password)
end

Instance Method Details

#add_flag(flag_name, value) ⇒ Object


292
293
294
295
296
297
298
# File 'lib/puppet/util/windows/adsi.rb', line 292

def add_flag(flag_name, value)
  flag = native_object.Get(flag_name) rescue 0

  native_object.Put(flag_name, flag | value)

  commit
end

#add_group_sids(*sids) ⇒ Object


334
335
336
337
# File 'lib/puppet/util/windows/adsi.rb', line 334

def add_group_sids(*sids)
  group_names = sids.map { |s| s. }
  add_to_groups(*group_names)
end

#add_to_groups(*group_names) ⇒ Object Also known as: add_to_group


319
320
321
322
323
# File 'lib/puppet/util/windows/adsi.rb', line 319

def add_to_groups(*group_names)
  group_names.each do |group_name|
    Puppet::Util::Windows::ADSI::Group.new(group_name).add_member_sids(sid)
  end
end

#disabled?Boolean


437
438
439
# File 'lib/puppet/util/windows/adsi.rb', line 437

def disabled?
  userflag_set?(:ADS_UF_ACCOUNTDISABLE)
end

#expired?Boolean


448
449
450
451
452
453
454
455
# File 'lib/puppet/util/windows/adsi.rb', line 448

def expired?
  expires = native_object.Get('AccountExpirationDate')
  expires && expires < Time.now
rescue WIN32OLERuntimeError => e
  # This OLE error code indicates the property can't be found in the cache
  raise e unless e.message =~ /8000500D/m
  false
end

#group_sidsObject


344
345
346
# File 'lib/puppet/util/windows/adsi.rb', line 344

def group_sids
  self.class.get_sids(native_object.Groups)
end

#groupsObject


310
311
312
313
314
315
316
317
# File 'lib/puppet/util/windows/adsi.rb', line 310

def groups
  # https://msdn.microsoft.com/en-us/library/aa746342.aspx
  # WIN32OLE objects aren't enumerable, so no map
  groups = []
  # Setting WIN32OLE.codepage ensures values are returned as UTF-8
  native_object.Groups.each {|g| groups << g.Name} rescue nil
  groups
end

#locked_out?Boolean


441
442
443
444
445
446
# File 'lib/puppet/util/windows/adsi.rb', line 441

def locked_out?
  # Note that the LOCKOUT flag is known to be inaccurate when using the
  # LDAP IADsUser provider, but this class consistently uses the WinNT
  # provider, which is expected to be accurate.
  userflag_set?(:ADS_UF_LOCKOUT)
end

#op_userflags(*flags, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Common helper for set_userflags and unset_userflags.


417
418
419
420
421
422
423
424
425
426
427
# File 'lib/puppet/util/windows/adsi.rb', line 417

def op_userflags(*flags, &block)
  # Avoid an unnecessary set + commit operation.
  return if flags.empty?

  unrecognized_flags = flags.reject { |flag| ADS_USERFLAGS.keys.include?(flag) }
  unless unrecognized_flags.empty?
    raise ArgumentError, _("Unrecognized ADS UserFlags: %{unrecognized_flags}") % { unrecognized_flags: unrecognized_flags.join(', ') }
  end

  self['UserFlags'] = flags.inject(self['UserFlags'], &block)
end

#password=(password) ⇒ Object


300
301
302
303
304
305
306
307
308
# File 'lib/puppet/util/windows/adsi.rb', line 300

def password=(password)
  if !password.nil?
    native_object.SetPassword(password)
    commit
  end

  fADS_UF_DONT_EXPIRE_PASSWD = 0x10000
  add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD)
end

#password_is?(password) ⇒ Boolean


288
289
290
# File 'lib/puppet/util/windows/adsi.rb', line 288

def password_is?(password)
  self.class.logon(name, password)
end

#remove_from_groups(*group_names) ⇒ Object Also known as: remove_from_group


326
327
328
329
330
# File 'lib/puppet/util/windows/adsi.rb', line 326

def remove_from_groups(*group_names)
  group_names.each do |group_name|
    Puppet::Util::Windows::ADSI::Group.new(group_name).remove_member_sids(sid)
  end
end

#remove_group_sids(*sids) ⇒ Object


339
340
341
342
# File 'lib/puppet/util/windows/adsi.rb', line 339

def remove_group_sids(*sids)
  group_names = sids.map { |s| s. }
  remove_from_groups(*group_names)
end

#set_groups(desired_groups, minimum = true) ⇒ Object

TODO: This code's pretty similar to set_members in the Group class. Would be nice to refactor them into the ADSIObject class at some point. This was not done originally because these use different methods to do stuff that are also aliased to other methods, so the shared code isn't exactly a 1:1 mapping.


352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/puppet/util/windows/adsi.rb', line 352

def set_groups(desired_groups, minimum = true)
  return if desired_groups.nil?

  desired_groups = desired_groups.split(',').map(&:strip)

  current_hash = Hash[ self.group_sids.map { |sid| [sid.sid, sid] } ]
  desired_hash = self.class.name_sid_hash(desired_groups)

  # First we add the user to all the groups it should be in but isn't
  if !desired_groups.empty?
    groups_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] }
    add_group_sids(*groups_to_add)
  end

  # Then we remove the user from all groups it is in but shouldn't be, if
  # that's been requested
  if !minimum
    if desired_hash.empty?
      groups_to_remove = current_hash.values
    else
      groups_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] }
    end

    remove_group_sids(*groups_to_remove)
  end
end

#set_userflags(*flags) ⇒ Object


429
430
431
# File 'lib/puppet/util/windows/adsi.rb', line 429

def set_userflags(*flags)
  op_userflags(*flags) { |userflags, flag| userflags | ADS_USERFLAGS[flag] }
end

#unset_userflags(*flags) ⇒ Object


433
434
435
# File 'lib/puppet/util/windows/adsi.rb', line 433

def unset_userflags(*flags)
  op_userflags(*flags) { |userflags, flag| userflags & ~ADS_USERFLAGS[flag] }
end

#userflag_set?(flag) ⇒ Boolean


409
410
411
412
# File 'lib/puppet/util/windows/adsi.rb', line 409

def userflag_set?(flag)
  flag_value = ADS_USERFLAGS[flag] || 0
  ! (self['UserFlags'] & flag_value).zero?
end