sys-admin 1.4.4-x86-mswin32-60 → 1.5.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,20 @@
1
+ == 1.5.0 - 29-Mar-2009
2
+ * INTERFACE CHANGE (WINDOWS ONLY): The interface for MS Windows has undergone
3
+ a radical change. Most methods now accept a hash of options that are
4
+ passed directly to the underlying WMI class. Please see the documentation
5
+ for details.
6
+ * Now works on various BSD flavors.
7
+ * Added the User#groups method. This returns an array of groups that the
8
+ user belongs to. Suggestion inspired by Gonzalo Garramuno.
9
+ * Added the Group#members method. The returns an array of users that the
10
+ group contains.
11
+ * Changed User#class to User#access_class for UNIX flavors to avoid
12
+ conflicts with the Ruby core Object method.
13
+ * Added more tests and renamed the test files.
14
+ * Removed an unnecessary function call where a platform might try to
15
+ get lastlog information even if the lastlog.h or utmp.h headers couldn't
16
+ be found.
17
+
1
18
  == 1.4.4 - 19-Nov-2008
2
19
  * Added the User#uid method for MS Windows (which is just the user's relative
3
20
  identifier).
data/README CHANGED
@@ -1,8 +1,11 @@
1
1
  == Description
2
- The sys-admin package is a unified, cross platform replacement for the
2
+ The sys-admin library is a unified, cross platform replacement for the
3
3
  Etc module.
4
4
 
5
5
  == Installation
6
+ = Typical Gem Installation
7
+ gem install sys-admin
8
+ = Local installation
6
9
  rake test (optional)
7
10
  rake install
8
11
 
@@ -36,39 +39,33 @@
36
39
  Admin.get_login
37
40
  Returns the user name (only) of the current login.
38
41
 
39
- Admin.get_user(name, host=localhost)
40
- Admin.get_user(uid, host=localhost, local=true)
41
- Returns a User object based on +name+ or +uid+.
42
+ Admin.get_user(name, options = {})
43
+ Admin.get_user(uid, options = {})
44
+ Returns a User object based on +name+ or +uid+. The +options+ hash is
45
+ for MS Windows only, and allows you to restrict the search based on the
46
+ options you provide, e.g. 'domain' or 'localaccount'.
42
47
 
43
- Windows only: you may specify a host from which information is retrieved.
44
- The default is the local machine. You may also specify whether to
45
- retrieve a local or global account. The default is local.
46
-
47
- Admin.get_group(name, host=localhost, local=true)
48
- Admin.get_group(gid, host=localhost, local=true)
49
- Returns a Group object based on +name+ or +uid+.
50
-
51
- Windows only: you may specify a host from which information is retrieved.
52
- The default is the local machine. You can retrieve either a global or
53
- local group, depending on the value of the +local+ argument.
54
-
55
- Admin.groups(host=localhost, local=true)
56
- Admin.groups(host=localhost, local=true){ |group| ... }
48
+ Admin.get_group(name, options = {})
49
+ Admin.get_group(gid, options = {})
50
+ Returns a Group object based on +name+ or +uid+. The +options+ hash is
51
+ for MS Windows only, and allows you to restrict the search based on the
52
+ options you provide, e.g. 'domain' or 'localaccount'.
53
+
54
+ Admin.groups(options = {})
55
+ Admin.groups(options = {}){ |group| ... }
57
56
  In block form, yields a Group object for each user on the system. In
58
57
  non-block form, returns an Array of Group objects.
59
58
 
60
- Windows only: you may specify a host from which information is retrieved.
61
- The default is the local machine. You can retrieve either a global or
62
- local group, depending on the value of the +local+ argument.
59
+ The +options+ hash is for MS Windows only, and allows you to restrict the
60
+ search based on the options you provide, e.g. 'domain' or 'localaccount'.
63
61
 
64
- Admin.users(host=localhost, local=true)
65
- Admin.users(host=localhost, local=true){ |user| ... }
62
+ Admin.users(options = {})
63
+ Admin.users(options = {}){ |user| ... }
66
64
  In block form, yields a User object for each user on the system. In
67
65
  non-block form, returns an Array of User objects.
68
66
 
69
- Windows only: you may specify a host from which information is retrieved.
70
- The default is the local machine. You can retrieve either a global or
71
- local group, depending on the value of the +local+ argument.
67
+ The +options+ hash is for MS Windows only, and allows you to restrict the
68
+ search based on the options you provide, e.g. 'domain' or 'localaccount'.
72
69
 
73
70
  == User class
74
71
  === User (Windows)
@@ -140,33 +137,14 @@ Admin::Error < StandardError
140
137
  information. This means that the WMI service must be running on the
141
138
  target machine in order to work (which it is, by default).
142
139
 
143
- Note that, by default, local user and group information is retrieved
144
- instead of global. You probably do NOT want to iterate over global users
145
- or groups because there can be quite a few on your domain.
146
-
147
140
  === UNIX
148
141
  The underlying implementation is similar to core Ruby's Etc implementation.
149
142
  But, in addition to the different interface, I use the re-entrant version
150
143
  of the appropriate functions when available.
151
144
 
152
145
  == Future Plans
153
- Add the following methods for UNIX:
154
-
155
- * Admin.add_local_user
156
- * Admin.config_local_user
157
- * Admin.delete_local_user
158
- * Admin.add_global_user
159
- * Admin.config_global_user
160
- * Admin.delete_global_user
161
-
162
- * Admin.add_local_group
163
- * Admin.config_local_group
164
- * Admin.delete_local_group
165
- * Admin.add_global_group
166
- * Admin.config_global_group
167
- * Admin.delete_global_group
168
-
169
146
  Make the User and Group objects comparable.
147
+ Add ability to add, configure and delete users on Unix platforms.
170
148
 
171
149
  == Known Bugs
172
150
  None that I'm aware of. If you find any, please log them on the project
@@ -176,7 +154,7 @@ Admin::Error < StandardError
176
154
  Ruby's
177
155
 
178
156
  == Copyright
179
- (C) 2005-2008, Daniel J. Berger
157
+ (C) 2005-2009, Daniel J. Berger
180
158
  All Rights Reserved
181
159
 
182
160
  == Author
data/Rakefile CHANGED
@@ -52,5 +52,10 @@ Rake::TestTask.new("test") do |t|
52
52
  t.libs << 'ext'
53
53
  t.libs.delete('lib')
54
54
  end
55
- t.test_files = FileList['test/tc_admin.rb']
55
+ t.libs << 'test'
56
+ t.test_files = FileList['test/test_sys_admin.rb']
57
+ end
58
+
59
+ task :test do
60
+ Rake.application[:clean].execute
56
61
  end
data/lib/sys/admin.rb CHANGED
@@ -31,6 +31,9 @@ module Sys
31
31
  # Sets whether or not the group is local (as opposed to global).
32
32
  attr_writer :local
33
33
 
34
+ # An array of members for that group. May contain SID's.
35
+ attr_accessor :members
36
+
34
37
  # Creates and returns a new Group object. This class encapsulates
35
38
  # the information for a group account, whether it be global or local.
36
39
  #
@@ -128,6 +131,9 @@ module Sys
128
131
 
129
132
  # Full name of a local user.
130
133
  attr_accessor :full_name
134
+
135
+ # An array of groups to which the user belongs.
136
+ attr_accessor :groups
131
137
 
132
138
  # Date the user account was created.
133
139
  attr_accessor :install_date
@@ -282,7 +288,7 @@ module Sys
282
288
  end
283
289
 
284
290
  class Admin
285
- VERSION = '1.4.4'
291
+ VERSION = '1.5.0'
286
292
 
287
293
  # This is the error raised in the majority of cases if anything goes wrong
288
294
  # with any of the Sys::Admin methods.
@@ -300,222 +306,348 @@ module Sys
300
306
  SidTypeComputer = 9
301
307
 
302
308
  private
309
+
310
+ # A private method that lower cases all keys, and converts them
311
+ # all to symbols.
312
+ #
313
+ def self.munge_options(opts)
314
+ rhash = {}
315
+
316
+ opts.each{ |k, v|
317
+ k = k.to_s.downcase.to_sym
318
+ rhash[k] = v
319
+ }
320
+
321
+ rhash
322
+ end
323
+
324
+ # An internal, private method for getting a list of groups for
325
+ # a particular user.
326
+ #
327
+ def self.get_groups(domain, user)
328
+ array = []
329
+ adsi = WIN32OLE.connect("WinNT://#{domain}/#{user}")
330
+ adsi.groups.each{ |g| array << g.name }
331
+ array
332
+ end
333
+
334
+ # An internal, private method for getting a list of members for
335
+ # any particular group.
336
+ #
337
+ def self.get_members(domain, group)
338
+ array = []
339
+ adsi = WIN32OLE.connect("WinNT://#{domain}/#{group}")
340
+ adsi.members.each{ |g| array << g.name }
341
+ array
342
+ end
303
343
 
304
344
  # Used by the get_login method
305
345
  GetUserName = Win32API.new('advapi32', 'GetUserName', 'PP', 'L') # :nodoc:
306
346
 
307
347
  public
308
348
 
309
- # Configures the global +user+ on +domain+ using options.
310
- #
311
- # See http://tinyurl.com/3hjv9 for a list of valid options.
312
- #
313
- def self.config_global_user(user, options, domain)
314
- begin
315
- adsi = WIN32OLE.connect("WinNT://#{domain}/#{user},user")
349
+ # Creates the given +user+. If no domain option is specified,
350
+ # then it defaults to your local host, i.e. a local account is
351
+ # created.
352
+ #
353
+ # Any options provided are treated as IADsUser interface methods
354
+ # and are called before SetInfo is finally called.
355
+ #
356
+ # Examples:
357
+ #
358
+ # # Create a local user with no options
359
+ # Sys::Admin.add_user(:name => 'asmith')
360
+ #
361
+ # # Create a local user with options
362
+ # Sys::Admin.add_user(
363
+ # :name => 'asmith',
364
+ # :description => 'Really cool guy',
365
+ # :password => 'abc123'
366
+ # )
367
+ #
368
+ # # Create a user on a specific domain
369
+ # Sys::Admin.add_user(
370
+ # :name => 'asmith',
371
+ # :domain => 'XX',
372
+ # :fullname => 'Al Smith'
373
+ # )
374
+ #--
375
+ # Most options are passed to the 'put' method. However, we handle the
376
+ # password specially since it's a separate method, and some environments
377
+ # require that it be set up front.
378
+ #
379
+ def self.add_user(options = {})
380
+ options = munge_options(options)
381
+
382
+ name = options.delete(:name) or raise ArgumentError, 'No user given'
383
+ domain = options[:domain]
384
+
385
+ if domain.nil?
386
+ domain = Socket.gethostname
387
+ moniker = "WinNT://#{domain},Computer"
388
+ else
389
+ moniker = "WinNT://#{domain}"
390
+ end
391
+
392
+ begin
393
+ adsi = WIN32OLE.connect(moniker)
394
+ user = adsi.create('user', name)
316
395
  options.each{ |option, value|
317
- adsi.put(option.to_s, value)
396
+ if option.to_s == 'password'
397
+ user.setpassword(value)
398
+ else
399
+ user.put(option.to_s, value)
400
+ end
318
401
  }
319
- adsi.setinfo
402
+ user.setinfo
320
403
  rescue WIN32OLERuntimeError => err
321
404
  raise Error, err
322
405
  end
323
406
  end
324
407
 
325
- # Configures the local +user+ on +host+ using +options+. If no host
326
- # is specified, the default is localhost.
408
+ # Configures the +user+ using +options+. If no domain option is
409
+ # specified then your local host is used, i.e. you are configuring
410
+ # a local user account.
327
411
  #
328
412
  # See http://tinyurl.com/3hjv9 for a list of valid options.
329
- #
330
- def self.config_local_user(user, options, host=Socket.gethostname)
413
+ #
414
+ # In the case of a password change, pass a two element array as the
415
+ # old value and new value.
416
+ #
417
+ # Examples:
418
+ #
419
+ # # Configure a local user
420
+ # Sys::Admin.configure_user(
421
+ # :name => 'djberge',
422
+ # :description => 'Awesome'
423
+ # )
424
+ #
425
+ # # Change the password
426
+ # Sys::Admin.configure_user(
427
+ # :name => 'asmith',
428
+ # :password => [old_pass, new_pass]
429
+ # )
430
+ #
431
+ # # Configure a user on a specific domain
432
+ # Sys::Admin.configure_user(
433
+ # :name => 'jsmrz',
434
+ # :domain => 'XX',
435
+ # :firstname => 'Jo'
436
+ # )
437
+ #
438
+ def self.configure_user(options = {})
439
+ options = munge_options(options)
440
+
441
+ name = options.delete(:name) or raise ArgumentError, 'No name given'
442
+ domain = options[:domain] || Socket.gethostname
443
+
331
444
  begin
332
- adsi = WIN32OLE.connect("WinNT://#{host}/#{user},user")
445
+ adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")
333
446
  options.each{ |option, value|
334
- adsi.put(option.to_s, value)
447
+ if option.to_s == 'password'
448
+ adsi.changepassword(value[0], value[1])
449
+ else
450
+ adsi.put(option.to_s, value)
451
+ end
335
452
  }
336
453
  adsi.setinfo
337
454
  rescue WIN32OLERuntimeError => err
338
455
  raise Error, err
339
456
  end
340
457
  end
341
-
342
- # Adds the global +user+ on +domain+
458
+
459
+ # Deletes the given +user+ on +domain+. If no domain is specified,
460
+ # then it defaults to your local host, i.e. a local account is
461
+ # deleted.
343
462
  #
344
- def self.add_global_user(user, domain)
463
+ def self.delete_user(user, domain = nil)
464
+ if domain.nil?
465
+ domain = Socket.gethostname
466
+ moniker = "WinNT://#{domain},Computer"
467
+ else
468
+ moniker = "WinNT://#{domain}"
469
+ end
470
+
345
471
  begin
346
- adsi = WIN32OLE.connect("WinNT://#{host},Computer")
347
- user = adsi.create("user", user)
348
- user.setinfo
472
+ adsi = WIN32OLE.connect(moniker)
473
+ adsi.delete('user', user)
349
474
  rescue WIN32OLERuntimeError => err
350
475
  raise Error, err
351
476
  end
352
477
  end
353
-
354
- # Adds the local +user+ on +host+, or the localhost if none is specified.
478
+
479
+ # Create a new +group+ using +options+. If no domain option is specified
480
+ # then a local group is created instead.
355
481
  #
356
- def self.add_local_user(user, host=Socket.gethostname)
357
- begin
358
- adsi = WIN32OLE.connect("WinNT://#{host},Computer")
359
- user = adsi.create("user", user)
360
- user.setinfo
361
- rescue WIN32OLERuntimeError => err
362
- raise Error, err
482
+ # Examples:
483
+ #
484
+ # # Create a local group with no options
485
+ # Sys::Admin.add_group(:name => 'Dudes')
486
+ #
487
+ # # Create a local group with options
488
+ # Sys::Admin.add_group(:name => 'Dudes', :description => 'Boys')
489
+ #
490
+ # # Create a group on a specific domain
491
+ # Sys::Admin.add_group(
492
+ # :name => 'Ladies',
493
+ # :domain => 'XYZ',
494
+ # :description => 'Girls'
495
+ # )
496
+ #
497
+ def self.add_group(options = {})
498
+ options = munge_options(options)
499
+
500
+ group = options.delete(:name) or raise ArgumentError, 'No name given'
501
+ domain = options[:domain]
502
+
503
+ if domain.nil?
504
+ domain = Socket.gethostname
505
+ moniker = "WinNT://#{domain},Computer"
506
+ else
507
+ moniker = "WinNT://#{domain}"
363
508
  end
364
- end
365
509
 
366
- # Deletes the local +user+ from +host+, or localhost if no host specified.
367
- #
368
- def self.delete_local_user(user, host=Socket.gethostname)
369
510
  begin
370
- adsi = WIN32OLE.connect("WinNT://#{host},Computer")
371
- adsi.delete("user", user)
511
+ adsi = WIN32OLE.connect(moniker)
512
+ group = adsi.create('group', group)
513
+ group.setinfo
514
+ configure_group(options) unless options.empty?
372
515
  rescue WIN32OLERuntimeError => err
373
516
  raise Error, err
374
517
  end
375
518
  end
376
-
377
- # Deletes the global +user+ from +domain+.
378
- #
379
- def self.delete_global_user(user, domain)
380
- begin
381
- adsi = WIN32OLE.connect("WinNT://#{domain}")
382
- adsi.delete("user", user)
383
- rescue WIN32OLERuntimeError => err
384
- raise Error, err
385
- end
386
- end
387
-
388
- # Configures the global +group+ on +domain+ using +options+.
519
+
520
+ # Configures the +group+ using +options+. If no domain option is
521
+ # specified then your local host is used, i.e. you are configuring
522
+ # a local group.
389
523
  #
390
524
  # See http://tinyurl.com/cjkzl for a list of valid options.
391
525
  #
392
- def self.config_global_group(group, options, domain)
526
+ # Examples:
527
+ #
528
+ # # Configure a local group.
529
+ # Sys::Admin.configure_group(:name => 'Abba', :description => 'Swedish')
530
+ #
531
+ # # Configure a group on a specific domain.
532
+ # Sys::Admin.configure_group(
533
+ # :name => 'Web Team',
534
+ # :domain => 'Foo',
535
+ # :description => 'Web programming cowboys'
536
+ # )
537
+ #
538
+ def self.configure_group(options = {})
539
+ options = munge_options(options)
540
+
541
+ group = options.delete(:name) or raise ArgumentError, 'No name given'
542
+ domain = options[:domain] || Socket.gethostname
543
+
393
544
  begin
394
545
  adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
395
- options.each{ |option, value|
396
- adsi.put(option.to_s, value)
397
- }
546
+ options.each{ |option, value| adsi.put(option.to_s, value) }
398
547
  adsi.setinfo
399
548
  rescue WIN32OLERuntimeError => err
400
549
  raise Error, err
401
550
  end
402
551
  end
403
552
 
404
- # Configures the local +group+ on +host+ using +options+. If no host
405
- # is specified, the default is localhost.
406
- #
407
- # See http://tinyurl.com/cjkzl for a list of valid options.
553
+ # Delete the +group+ from +domain+. If no domain is specified, then
554
+ # you are deleting a local group.
408
555
  #
409
- def self.config_local_group(group, options, host=Socket.gethostname)
410
- begin
411
- adsi = WIN32OLE.connect("WinNT://#{host}/#{group},group")
412
- options.each{ |option, value|
413
- adsi.put(option.to_s, value)
414
- }
415
- adsi.setinfo
416
- rescue WIN32OLERuntimeError => err
417
- raise Error, err
418
- end
419
- end
420
-
421
- # Add global +group+ to +domain+.
422
- #
423
- def self.add_global_group(group, domain)
424
- begin
425
- adsi = WIN32OLE.connect("WinNT://#{domain},Computer")
426
- obj = adsi.create("group", group)
427
- obj.setinfo
428
- rescue WIN32OLERuntimeError => err
429
- raise Error, err
556
+ def self.delete_group(group, domain = nil)
557
+ if domain.nil?
558
+ domain = Socket.gethostname
559
+ moniker = "WinNT://#{domain},Computer"
560
+ else
561
+ moniker = "WinNT://#{domain}"
430
562
  end
431
- end
432
563
 
433
- # Add local +group+ to +host+, or the localhost if no host is specified.
434
- #
435
- def self.add_local_group(group, host=Socket.gethostname)
436
564
  begin
437
- adsi = WIN32OLE.connect("WinNT://#{host},Computer")
438
- obj = adsi.create("group", group)
439
- obj.setinfo
565
+ adsi = WIN32OLE.connect(moniker)
566
+ obj = adsi.delete('group', group)
440
567
  rescue WIN32OLERuntimeError => err
441
568
  raise Error, err
442
569
  end
443
570
  end
444
571
 
445
- # Delete the global +group+ from +domain+.
446
- #
447
- def self.delete_global_group(groupid, domain)
448
- begin
449
- adsi = WIN32OLE.connect("WinNT://#{domain},Computer")
450
- obj = adsi.delete("group", groupid)
451
- rescue WIN32OLERuntimeError => err
452
- raise Error, err
453
- end
454
- end
455
-
456
- # Delete the local +group+ from +host+, or localhost if no host specified.
457
- #
458
- def self.delete_local_group(groupid, host=Socket.gethostname)
459
- begin
460
- adsi = WIN32OLE.connect("WinNT://#{host},Computer")
461
- obj = adsi.delete("group", groupid)
462
- rescue WIN32OLERuntimeError => err
463
- raise Error, err
464
- end
465
- end
466
-
467
572
  # Returns the user name (only) of the current login.
468
573
  #
469
574
  def self.get_login
470
575
  buffer = 0.chr * 256
471
- nsize = [buffer.size].pack("L")
576
+ nsize = [buffer.size].pack("L")
472
577
 
473
578
  if GetUserName.call(buffer, nsize) == 0
474
579
  raise Error, 'GetUserName() call failed in get_login'
475
580
  end
476
581
 
477
- length = nsize.unpack('L')[0]
582
+ length = nsize.unpack('L')[0]
478
583
  username = buffer[0 ... length].chop
479
584
  username
480
585
  end
481
-
586
+
482
587
  # Returns a User object based on either +name+ or +uid+.
483
588
  #
484
589
  # call-seq:
485
- # get_user(name, host=localhost)
486
- # get_user(uid, host=localhost, local=true)
590
+ # Sys::Admin.get_user(name, options = {})
591
+ # Sys::Admin.get_user(uid, options = {})
592
+ #
593
+ # Looks for +usr+ information based on the options you specify, where
594
+ # the +usr+ argument can be either a user name or a RID.
595
+ #
596
+ # If a 'host' option is specified, information is retrieved from that
597
+ # host. Otherwise, the local host is used.
598
+ #
599
+ # All other options are converted to WQL statements against the
600
+ # Win32_UserAccount WMI object. See http://tinyurl.com/by9nvn for a
601
+ # list of possible options.
487
602
  #
488
- # You may specify a +host+ from which information is retrieved. The
489
- # default is the local machine. You may also specify whether to
490
- # retrieve a local or global account. The default is local.
603
+ # Examples:
491
604
  #
492
- def self.get_user(usr, host=Socket.gethostname, local=true)
493
- host = Socket.gethostname if host.nil?
605
+ # # Get a user by name
606
+ # Admin.get_user('djberge')
607
+ #
608
+ # # Get a user by uid
609
+ # Admin.get_user(100)
610
+ #
611
+ # # Get a user on a specific domain
612
+ # Admin.get_user('djberge', :domain => 'TEST')
613
+ #--
614
+ # The reason for keeping the +usr+ variable as a separate argument
615
+ # instead of rolling it into the options hash was to keep a unified
616
+ # API between the Windows and UNIX versions.
617
+ #
618
+ def self.get_user(usr, options = {})
619
+ options = munge_options(options)
620
+
621
+ host = options.delete(:host) || Socket.gethostname
494
622
  cs = "winmgmts:{impersonationLevel=impersonate}!"
495
623
  cs << "//#{host}/root/cimv2"
496
624
 
497
625
  begin
498
626
  wmi = WIN32OLE.connect(cs)
499
- rescue WIN32OLERuntimeError => e
500
- raise Error, e
627
+ rescue WIN32OLERuntimeError => err
628
+ raise Error, err
501
629
  end
502
630
 
503
631
  query = "select * from win32_useraccount"
504
- query << " where localaccount = true" if local
505
-
506
- if usr.kind_of?(Fixnum)
507
- if local
508
- query << " and sid like '%-#{usr}'"
632
+
633
+ i = 0
634
+
635
+ options.each{ |opt, val|
636
+ if i == 0
637
+ query << " where #{opt} = '#{val}'"
638
+ i += 1
509
639
  else
510
- query << " where sid like '%-#{usr}'"
640
+ query << " and #{opt} = '#{val}'"
511
641
  end
642
+ }
643
+
644
+ if usr.kind_of?(Fixnum)
645
+ query << " and sid like '%-#{usr}'"
512
646
  else
513
- if local
514
- query << " and name = '#{usr}'"
515
- else
516
- query << " where name = '#{usr}'"
517
- end
647
+ query << " and name = '#{usr}'"
518
648
  end
649
+
650
+ domain = options[:domain] || host
519
651
 
520
652
  wmi.execquery(query).each{ |user|
521
653
  uid = user.sid.split('-').last.to_i
@@ -527,23 +659,24 @@ module Sys
527
659
  end
528
660
 
529
661
  user_object = User.new do |u|
530
- u.account_type = user.accounttype
531
- u.caption = user.caption
532
- u.description = user.description
533
- u.disabled = user.disabled
534
- u.domain = user.domain
535
- u.full_name = user.fullname
536
- u.install_date = user.installdate
537
- u.local = user.localaccount
538
- u.lockout = user.lockout
539
- u.name = user.name
662
+ u.account_type = user.accounttype
663
+ u.caption = user.caption
664
+ u.description = user.description
665
+ u.disabled = user.disabled
666
+ u.domain = user.domain
667
+ u.full_name = user.fullname
668
+ u.install_date = user.installdate
669
+ u.local = user.localaccount
670
+ u.lockout = user.lockout
671
+ u.name = user.name
540
672
  u.password_changeable = user.passwordchangeable
541
673
  u.password_expires = user.passwordexpires
542
674
  u.password_required = user.passwordrequired
543
- u.sid = user.sid
544
- u.sid_type = user.sidtype
545
- u.status = user.status
546
- u.uid = uid
675
+ u.sid = user.sid
676
+ u.sid_type = user.sidtype
677
+ u.status = user.status
678
+ u.uid = uid
679
+ u.groups = get_groups(domain, user.name)
547
680
  end
548
681
 
549
682
  return user_object
@@ -553,19 +686,34 @@ module Sys
553
686
  raise Error, "no user found for '#{usr}'"
554
687
  end
555
688
 
556
- # In block form, yields a User object for each user on the system. In
689
+ # In block form, yields a User object for each user on the system. In
557
690
  # non-block form, returns an Array of User objects.
558
691
  #
559
692
  # call-seq:
560
- # users(host=localhost, local=true)
561
- # users(host=localhost, local=true){ |user| ... }
693
+ # Sys::Admin.users(options = {})
694
+ # Sys::Admin.users(options = {}){ |user| ... }
695
+ #
696
+ # You may specify a host from which information is retrieved. The
697
+ # default is the local host.
698
+ #
699
+ # All other arguments are passed as WQL query parameters against
700
+ # the Win32_UserAccont WMI object.
701
+ #
702
+ # Examples:
703
+ #
704
+ # # Get all local account users
705
+ # Sys::Admin.users(:localaccount => true)
706
+ #
707
+ # # Get all user accounts on a specific domain
708
+ # Sys::Admin.users(:domain => 'FOO')
562
709
  #
563
- # You may specify a host from which information is retrieved. The
564
- # default is the local machine. You can retrieve either a global or
565
- # local group, depending on the value of the +local+ argument.
710
+ # # Get a single user from a domain
711
+ # Sys::Admin.users(:name => 'djberge', :domain => 'FOO')
566
712
  #
567
- def self.users(host=Socket.gethostname, local=true)
568
- host = Socket.gethostname if host.nil?
713
+ def self.users(options = {})
714
+ options = munge_options(options)
715
+
716
+ host = options.delete(:host) || Socket.gethostname
569
717
  cs = "winmgmts:{impersonationLevel=impersonate}!"
570
718
  cs << "//#{host}/root/cimv2"
571
719
 
@@ -576,27 +724,40 @@ module Sys
576
724
  end
577
725
 
578
726
  query = "select * from win32_useraccount"
579
- query << " where localaccount = true" if local
727
+
728
+ i = 0
729
+
730
+ options.each{ |opt, val|
731
+ if i == 0
732
+ query << " where #{opt} = '#{val}'"
733
+ i += 1
734
+ else
735
+ query << " and #{opt} = '#{val}'"
736
+ end
737
+ }
738
+
580
739
  array = []
740
+ domain = options[:domain] || host
581
741
 
582
742
  wmi.execquery(query).each{ |user|
583
743
  usr = User.new do |u|
584
- u.account_type = user.accounttype
585
- u.caption = user.caption
586
- u.description = user.description
587
- u.disabled = user.disabled
588
- u.domain = user.domain
589
- u.full_name = user.fullname
590
- u.install_date = user.installdate
591
- u.local = user.localaccount
592
- u.lockout = user.lockout
593
- u.name = user.name
744
+ u.account_type = user.accounttype
745
+ u.caption = user.caption
746
+ u.description = user.description
747
+ u.disabled = user.disabled
748
+ u.domain = user.domain
749
+ u.full_name = user.fullname
750
+ u.install_date = user.installdate
751
+ u.local = user.localaccount
752
+ u.lockout = user.lockout
753
+ u.name = user.name
594
754
  u.password_changeable = user.passwordchangeable
595
755
  u.password_expires = user.passwordexpires
596
756
  u.password_required = user.passwordrequired
597
- u.sid = user.sid
598
- u.sid_type = user.sidtype
599
- u.status = user.status
757
+ u.sid = user.sid
758
+ u.sid_type = user.sidtype
759
+ u.status = user.status
760
+ u.groups = get_groups(domain, user.name)
600
761
  end
601
762
 
602
763
  if block_given?
@@ -605,47 +766,70 @@ module Sys
605
766
  array.push(usr)
606
767
  end
607
768
  }
769
+
608
770
  return array unless block_given?
609
771
  end
610
772
 
611
- # Returns a Group object based on either +name+ or +uid+.
773
+ # Returns a Group object based on either +name+ or +gid+.
612
774
  #
613
775
  # call-seq:
614
- # get_group(name, host=localhost, local=true)
615
- # get_group(gid, host=localhost, local=true)
776
+ # Sys::Admin.get_group(name, options = {})
777
+ # Sys::Admin.get_group(gid, options = {})
778
+ #
779
+ # If a numeric value is sent as the first parameter, it is treated
780
+ # as a RID and is checked against the SID for a match.
781
+ #
782
+ # You may specify a host as an option from which information is
783
+ # retrieved. The default is the local host.
784
+ #
785
+ # All other options are passed as WQL parameters to the Win32_Group
786
+ # WMI object. See http://tinyurl.com/bngc8s for a list of possible
787
+ # options.
788
+ #
789
+ # Examples:
790
+ #
791
+ # # Find a group by name
792
+ # Sys::Admin.get_group('Web Team')
793
+ #
794
+ # # Find a group by id
795
+ # Sys::Admin.get_group(31667)
616
796
  #
617
- # You may specify a host from which information is retrieved.
618
- # The default is the local machine. You can retrieve either a global or
619
- # local group, depending on the value of the +local+ argument.
797
+ # # Find a group on a specific domain
798
+ # Sys::Admin.get_group('Web Team', :domain => 'FOO')
620
799
  #
621
- def self.get_group(grp, host=Socket.gethostname, local=true)
622
- host = Socket.gethostname if host.nil?
800
+ def self.get_group(grp, options = {})
801
+ options = munge_options(options)
802
+
803
+ host = options.delete(:host) || Socket.gethostname
623
804
  cs = "winmgmts:{impersonationLevel=impersonate}!"
624
805
  cs << "//#{host}/root/cimv2"
625
- gid = nil
626
-
806
+
627
807
  begin
628
808
  wmi = WIN32OLE.connect(cs)
629
- rescue WIN32OLERuntimeError => e
630
- raise Error, e
809
+ rescue WIN32OLERuntimeError => err
810
+ raise Error, err
631
811
  end
632
812
 
633
813
  query = "select * from win32_group"
634
- query << " where localaccount = true" if local
635
-
636
- if grp.kind_of?(Fixnum)
637
- if local
638
- query << " and sid like '%-#{grp}'"
814
+
815
+ i = 0
816
+
817
+ options.each{ |opt, val|
818
+ if i == 0
819
+ query << " where #{opt} = '#{val}'"
820
+ i += 1
639
821
  else
640
- query << " where sid like '%-#{grp}'"
822
+ query << " and #{opt} = '#{val}'"
641
823
  end
824
+ }
825
+
826
+ if grp.kind_of?(Fixnum)
827
+ query << " and sid like '%-#{grp}'"
642
828
  else
643
- if local
644
- query << " and name = '#{grp}'"
645
- else
646
- query << " where name = '#{grp}'"
647
- end
829
+ query << " and name = '#{grp}'"
648
830
  end
831
+
832
+ domain = options[:domain] || host
649
833
 
650
834
  wmi.execquery(query).each{ |group|
651
835
  gid = group.sid.split("-").last.to_i
@@ -667,6 +851,7 @@ module Sys
667
851
  g.sid = group.sid
668
852
  g.sid_type = group.sidtype
669
853
  g.status = group.status
854
+ g.members = get_members(domain, group.name)
670
855
  end
671
856
 
672
857
  return group_object
@@ -680,28 +865,56 @@ module Sys
680
865
  # non-block form, returns an Array of Group objects.
681
866
  #
682
867
  # call-seq:
683
- # groups(host=localhost, local=true)
684
- # groups(host=localhost, local=true){ |group| ... }
868
+ # groups(options = {})
869
+ # groups(options = {}){ |group| ... }
870
+ #
871
+ # You may specify a host option from which information is retrieved.
872
+ # The default is the local host.
873
+ #
874
+ # All other options are passed as WQL parameters to the Win32_Group
875
+ # WMI object. See http://tinyurl.com/bngc8s for a list of possible
876
+ # options.
877
+ #
878
+ # Examples:
685
879
  #
686
- # You may specify a host from which information is retrieved.
687
- # The default is the local machine. You can retrieve either a global or
688
- # local group, depending on the value of the +local+ argument.
880
+ # # Get local group information
881
+ # Sys::Admin.groups(:localaccount => true)
689
882
  #
690
- def self.groups(host=Socket.gethostname, local=true)
691
- host = Socket.gethostname if host.nil?
883
+ # # Get all groups on a specific domain
884
+ # Sys::Admin.groups(:domain => 'FOO')
885
+ #
886
+ # # Get a specific group on a domain
887
+ # Sys::Admin.groups(:name => 'Some Group', :domain => 'FOO')
888
+ #
889
+ def self.groups(options = {})
890
+ options = munge_options(options)
891
+
892
+ host = options.delete(:host) || Socket.gethostname
692
893
  cs = "winmgmts:{impersonationLevel=impersonate}!"
693
894
  cs << "//#{host}/root/cimv2"
694
895
 
695
896
  begin
696
897
  wmi = WIN32OLE.connect(cs)
697
- rescue WIN32OLERuntimeError => e
698
- raise Error, e
898
+ rescue WIN32OLERuntimeError => err
899
+ raise Error, err
699
900
  end
700
901
 
701
902
  query = "select * from win32_group"
702
- query << " where localaccount = true" if local
703
-
903
+
904
+ i = 0
905
+
906
+ options.each{ |opt, val|
907
+ if i == 0
908
+ query << " where #{opt} = '#{val}'"
909
+ i += 1
910
+ else
911
+ query << " and #{opt} = '#{val}'"
912
+ end
913
+ }
914
+
704
915
  array = []
916
+ domain = options[:domain] || host
917
+
705
918
  wmi.execquery(query).each{ |group|
706
919
  grp = Group.new do |g|
707
920
  g.caption = group.caption
@@ -714,13 +927,16 @@ module Sys
714
927
  g.sid = group.sid
715
928
  g.sid_type = group.sidtype
716
929
  g.status = group.status
930
+ g.members = get_members(domain, group.name)
717
931
  end
932
+
718
933
  if block_given?
719
934
  yield grp
720
935
  else
721
936
  array.push(grp)
722
937
  end
723
938
  }
939
+
724
940
  return array unless block_given?
725
941
  end
726
942
  end