sys-admin 1.4.4-x86-mswin32-60 → 1.5.0-x86-mswin32-60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +17 -0
- data/README +25 -47
- data/Rakefile +6 -1
- data/lib/sys/admin.rb +417 -201
- data/sys-admin.gemspec +4 -4
- data/test/{tc_admin.rb → test_sys_admin.rb} +12 -5
- data/test/test_sys_admin_unix.rb +246 -0
- data/test/{tc_windows.rb → test_sys_admin_windows.rb} +117 -111
- metadata +12 -10
- data/test/tc_unix.rb +0 -81
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
|
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,
|
40
|
-
Admin.get_user(uid,
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
61
|
-
|
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(
|
65
|
-
Admin.users(
|
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
|
-
|
70
|
-
|
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-
|
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.
|
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.
|
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
|
-
#
|
310
|
-
#
|
311
|
-
#
|
312
|
-
#
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
-
|
396
|
+
if option.to_s == 'password'
|
397
|
+
user.setpassword(value)
|
398
|
+
else
|
399
|
+
user.put(option.to_s, value)
|
400
|
+
end
|
318
401
|
}
|
319
|
-
|
402
|
+
user.setinfo
|
320
403
|
rescue WIN32OLERuntimeError => err
|
321
404
|
raise Error, err
|
322
405
|
end
|
323
406
|
end
|
324
407
|
|
325
|
-
# Configures the
|
326
|
-
# is
|
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
|
-
|
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://#{
|
445
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")
|
333
446
|
options.each{ |option, value|
|
334
|
-
|
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
|
-
#
|
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.
|
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(
|
347
|
-
|
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
|
-
#
|
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
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
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(
|
371
|
-
adsi.
|
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
|
-
#
|
378
|
-
#
|
379
|
-
|
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
|
-
|
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
|
-
#
|
405
|
-
#
|
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.
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
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(
|
438
|
-
obj = adsi.
|
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
|
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
|
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,
|
486
|
-
# get_user(uid,
|
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
|
-
#
|
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
|
-
|
493
|
-
|
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 =>
|
500
|
-
raise Error,
|
627
|
+
rescue WIN32OLERuntimeError => err
|
628
|
+
raise Error, err
|
501
629
|
end
|
502
630
|
|
503
631
|
query = "select * from win32_useraccount"
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
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 << "
|
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
|
-
|
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
|
531
|
-
u.caption
|
532
|
-
u.description
|
533
|
-
u.disabled
|
534
|
-
u.domain
|
535
|
-
u.full_name
|
536
|
-
u.install_date
|
537
|
-
u.local
|
538
|
-
u.lockout
|
539
|
-
u.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
|
544
|
-
u.sid_type
|
545
|
-
u.status
|
546
|
-
u.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.
|
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(
|
561
|
-
# users(
|
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
|
-
#
|
564
|
-
#
|
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(
|
568
|
-
|
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
|
-
|
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
|
585
|
-
u.caption
|
586
|
-
u.description
|
587
|
-
u.disabled
|
588
|
-
u.domain
|
589
|
-
u.full_name
|
590
|
-
u.install_date
|
591
|
-
u.local
|
592
|
-
u.lockout
|
593
|
-
u.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
|
598
|
-
u.sid_type
|
599
|
-
u.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 +
|
773
|
+
# Returns a Group object based on either +name+ or +gid+.
|
612
774
|
#
|
613
775
|
# call-seq:
|
614
|
-
# get_group(name,
|
615
|
-
# get_group(gid,
|
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
|
-
#
|
618
|
-
#
|
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,
|
622
|
-
|
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
|
-
|
626
|
-
|
806
|
+
|
627
807
|
begin
|
628
808
|
wmi = WIN32OLE.connect(cs)
|
629
|
-
rescue WIN32OLERuntimeError =>
|
630
|
-
raise Error,
|
809
|
+
rescue WIN32OLERuntimeError => err
|
810
|
+
raise Error, err
|
631
811
|
end
|
632
812
|
|
633
813
|
query = "select * from win32_group"
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
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 << "
|
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
|
-
|
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(
|
684
|
-
# groups(
|
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
|
-
#
|
687
|
-
#
|
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
|
-
|
691
|
-
|
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 =>
|
698
|
-
raise Error,
|
898
|
+
rescue WIN32OLERuntimeError => err
|
899
|
+
raise Error, err
|
699
900
|
end
|
700
901
|
|
701
902
|
query = "select * from win32_group"
|
702
|
-
|
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
|