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

Inherits:
Object
  • Object
show all
Extended by:
Enumerable, FFI::Library, Shared
Defined in:
lib/puppet/util/windows.rb,
lib/puppet/util/windows/adsi.rb

Constant Summary collapse

MAX_USERNAME_LENGTH =

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

256

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

uniq

Methods included from Shared

get_sids, localized_domains, name_sid_hash, parse_name

Methods included from FFI::Library

attach_function_private

Constructor Details

#initialize(name, native_user = nil) ⇒ User

Returns a new instance of User.



176
177
178
179
# File 'lib/puppet/util/windows/adsi.rb', line 176

def initialize(name, native_user = nil)
  @name = name
  @native_user = native_user
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



175
176
177
# File 'lib/puppet/util/windows/adsi.rb', line 175

def name
  @name
end

#native_userObject

Returns the value of attribute native_user.



174
175
176
# File 'lib/puppet/util/windows/adsi.rb', line 174

def native_user
  @native_user
end

#sidObject (readonly)

Returns the value of attribute sid.



175
176
177
# File 'lib/puppet/util/windows/adsi.rb', line 175

def sid
  @sid
end

Class Method Details

.create(name) ⇒ Object

Raises:



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

def self.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, 'user'))
end

.current_user_nameObject



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/puppet/util/windows/adsi.rb', line 320

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



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

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

.delete(name) ⇒ Object



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

def self.delete(name)
  Puppet::Util::Windows::ADSI.delete(name, 'user')
end

.each(&block) ⇒ Object



370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/puppet/util/windows/adsi.rb', line 370

def self.each(&block)
  wql = Puppet::Util::Windows::ADSI.execquery('select name from win32_useraccount where localaccount = "TRUE"')

  users = []
  wql.each do |u|
    # Setting WIN32OLE.codepage in the microsoft_windows feature ensures
    # values are returned as UTF-8
    users << new(u.name)
  end

  users.each(&block)
end

.exists?(name_or_sid) ⇒ Boolean

Returns:

  • (Boolean)


342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/puppet/util/windows/adsi.rb', line 342

def self.exists?(name_or_sid)
  well_known = false
  if (sid = Puppet::Util::Windows::SID.name_to_sid_object(name_or_sid))
    return true if sid. == :SidTypeUser

    # 'well known group' is special as it can be a group like Everyone OR a user like SYSTEM
    # so try to resolve it
    # https://msdn.microsoft.com/en-us/library/cc234477.aspx
    well_known = sid. == :SidTypeWellKnownGroup
    return false if sid. != :SidTypeAlias && !well_known
    name_or_sid = "#{sid.domain}\\#{sid.}"
  end

  user = Puppet::Util::Windows::ADSI.connect(User.uri(*User.parse_name(name_or_sid)))
  # otherwise, verify that the account is actually a User account
  user.Class == 'User'
rescue
  # special accounts like SYSTEM cannot resolve via moniker like WinNT://./SYSTEM,user
  # and thus fail to connect - so given a validly resolved SID, this failure is ambiguous as it
  # may indicate either a group like Service or an account like SYSTEM
  well_known
end

.logon(name, password) ⇒ Object



193
194
195
# File 'lib/puppet/util/windows/adsi.rb', line 193

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

Instance Method Details

#[](attribute) ⇒ Object



197
198
199
200
201
# File 'lib/puppet/util/windows/adsi.rb', line 197

def [](attribute)
  # Setting WIN32OLE.codepage in the microsoft_windows feature ensures
  # values are returned as UTF-8
  native_user.Get(attribute)
end

#[]=(attribute, value) ⇒ Object



203
204
205
# File 'lib/puppet/util/windows/adsi.rb', line 203

def []=(attribute, value)
  native_user.Put(attribute, value)
end

#add_flag(flag_name, value) ⇒ Object



228
229
230
231
232
233
234
# File 'lib/puppet/util/windows/adsi.rb', line 228

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

  native_user.Put(flag_name, flag | value)

  commit
end

#add_group_sids(*sids) ⇒ Object



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

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



256
257
258
259
260
# File 'lib/puppet/util/windows/adsi.rb', line 256

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

#commitObject



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/puppet/util/windows/adsi.rb', line 207

def commit
  begin
    native_user.SetInfo unless native_user.nil?
  rescue WIN32OLERuntimeError => e
    # ERROR_BAD_USERNAME 2202L from winerror.h
    if e.message =~ /8007089A/m
      raise Puppet::Error.new(
       _("Puppet is not able to create/delete domain users with the user resource."),
       e
      )
    end

    raise Puppet::Error.new( _("User update failed: %{e}") % { e: e }, e )
  end
  self
end

#group_sidsObject



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

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

#groupsObject



246
247
248
249
250
251
252
253
254
# File 'lib/puppet/util/windows/adsi.rb', line 246

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

#password=(password) ⇒ Object



236
237
238
239
240
241
242
243
244
# File 'lib/puppet/util/windows/adsi.rb', line 236

def password=(password)
  if !password.nil?
    native_user.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)


224
225
226
# File 'lib/puppet/util/windows/adsi.rb', line 224

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

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



263
264
265
266
267
# File 'lib/puppet/util/windows/adsi.rb', line 263

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



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

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

#set_groups(desired_groups, minimum = true) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/puppet/util/windows/adsi.rb', line 285

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

#uriObject



189
190
191
# File 'lib/puppet/util/windows/adsi.rb', line 189

def uri
  self.class.uri(sid., sid.domain)
end