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,
  ADS_UF_TEMP_DUPLICATE_ACCOUNT: 0x0100,
  ADS_UF_NORMAL_ACCOUNT: 0x0200,
  ADS_UF_INTERDOMAIN_TRUST_ACCOUNT: 0x0800,
  ADS_UF_WORKSTATION_TRUST_ACCOUNT: 0x1000,
  ADS_UF_SERVER_TRUST_ACCOUNT: 0x2000,
  ADS_UF_DONT_EXPIRE_PASSWD: 0x10000,
  ADS_UF_MNS_LOGON_ACCOUNT: 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
NameUnknown =
0
NameFullyQualifiedDN =
1
NameSamCompatible =
2
NameDisplay =
3
NameUniqueId =
6
NameCanonical =
7
NameUserPrincipal =
8
NameCanonicalEx =
9
NameServicePrincipal =
10
NameDnsDomain =
12
NameGivenName =
13
NameSurname =
14

Instance Attribute Summary

Attributes inherited from ADSIObject

#name

Class Method Summary collapse

Instance Method Summary collapse

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

Constructor Details

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

Class Method Details

.create(name) ⇒ Object

Raises:



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

def create(name)
  # Windows error 1379: The specified local group already exists.
  raise Puppet::Error, _("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_sam_compatible_user_nameObject



557
558
559
# File 'lib/puppet/util/windows/adsi.rb', line 557

def self.current_sam_compatible_user_name
  current_user_name_with_format(NameSamCompatible)
end

.current_user_nameObject



505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/puppet/util/windows/adsi.rb', line 505

def self.current_user_name
  user_name = ''.dup
  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, _("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_name_with_format(format) ⇒ Object



538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
# File 'lib/puppet/util/windows/adsi.rb', line 538

def self.current_user_name_with_format(format)
  user_name = ''.dup
  max_length = 1024

  FFI::MemoryPointer.new(:lpwstr, max_length * 2 + 1) do |buffer|
    FFI::MemoryPointer.new(:dword, 1) do |buffer_size|
      buffer_size.write_dword(max_length + 1)

      if GetUserNameExW(format.to_i, buffer, buffer_size) == FFI::WIN32_FALSE
        raise Puppet::Util::Windows::Error.new(_("Failed to get user name"), FFI.errno)
      end

      user_name = buffer.read_wide_string(buffer_size.read_dword).chomp
    end
  end

  user_name
end

.current_user_sidObject



561
562
563
# File 'lib/puppet/util/windows/adsi.rb', line 561

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

.list_allObject



310
311
312
# File 'lib/puppet/util/windows/adsi.rb', line 310

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

.logon(name, password) ⇒ Object



314
315
316
# File 'lib/puppet/util/windows/adsi.rb', line 314

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

Instance Method Details

#add_flag(flag_name, value) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
# File 'lib/puppet/util/windows/adsi.rb', line 330

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

  native_object.Put(flag_name, flag | value)

  commit
end

#add_group_sids(*sids) ⇒ Object



379
380
381
382
# File 'lib/puppet/util/windows/adsi.rb', line 379

def add_group_sids(*sids)
  group_names = sids.map(&:domain_account)
  add_to_groups(*group_names)
end

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



365
366
367
368
369
# File 'lib/puppet/util/windows/adsi.rb', line 365

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

Returns:

  • (Boolean)


482
483
484
# File 'lib/puppet/util/windows/adsi.rb', line 482

def disabled?
  userflag_set?(:ADS_UF_ACCOUNTDISABLE)
end

#expired?Boolean

Returns:

  • (Boolean)


493
494
495
496
497
498
499
500
501
# File 'lib/puppet/util/windows/adsi.rb', line 493

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



389
390
391
# File 'lib/puppet/util/windows/adsi.rb', line 389

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

#groupsObject



352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/puppet/util/windows/adsi.rb', line 352

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
  begin
    native_object.Groups.each { |g| groups << g.Name }
  rescue
    nil
  end
  groups
end

#locked_out?Boolean

Returns:

  • (Boolean)


486
487
488
489
490
491
# File 'lib/puppet/util/windows/adsi.rb', line 486

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.



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

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



342
343
344
345
346
347
348
349
350
# File 'lib/puppet/util/windows/adsi.rb', line 342

def password=(password)
  unless 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

Returns:

  • (Boolean)


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

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

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



372
373
374
375
376
# File 'lib/puppet/util/windows/adsi.rb', line 372

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



384
385
386
387
# File 'lib/puppet/util/windows/adsi.rb', line 384

def remove_group_sids(*sids)
  group_names = sids.map(&:domain_account)
  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.



397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/puppet/util/windows/adsi.rb', line 397

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

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

  current_hash = group_sids.to_h { |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
  unless 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
  unless 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



474
475
476
# File 'lib/puppet/util/windows/adsi.rb', line 474

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

#unset_userflags(*flags) ⇒ Object



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

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

#userflag_set?(flag) ⇒ Boolean

Returns:

  • (Boolean)


454
455
456
457
# File 'lib/puppet/util/windows/adsi.rb', line 454

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