sys-admin 1.5.4-x86-mingw32 → 1.5.5-x86-mingw32
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 +8 -0
- data/Rakefile +12 -13
- data/lib/sys/admin.rb +944 -936
- data/sys-admin.gemspec +1 -1
- data/test/test_sys_admin.rb +1 -1
- data/test/test_sys_admin_windows.rb +306 -295
- metadata +6 -8
data/CHANGES
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 1.5.5 - 5-Jul-2011
|
2
|
+
* Modified lastlog handling, and ignore getpwent_r and getgrent_r, on AIX.
|
3
|
+
Thanks go to Rick Ohnemus for the spot and patches.
|
4
|
+
* Explicitly set spec.cpu on Windows to 'universal' in the gem creation task.
|
5
|
+
* Fixed a bug in the User.get_login and User.get_group methods where the query
|
6
|
+
being generated was incorrect if no options were passed. Thanks go to
|
7
|
+
Matthew Brown for the spot.
|
8
|
+
|
1
9
|
== 1.5.4 - 7-Oct-2010
|
2
10
|
* Prefer the getlastlogx() function over lastlog() where supported.
|
3
11
|
|
data/Rakefile
CHANGED
@@ -2,21 +2,19 @@ require 'rake'
|
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'rbconfig'
|
5
|
+
include Config
|
5
6
|
|
6
|
-
WINDOWS =
|
7
|
+
WINDOWS = CONFIG['host_os'] =~ /msdos|mswin|win32|mingw|cygwin|windows/i
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
9
|
+
CLEAN.include(
|
10
|
+
'**/*.gem', # Gem files
|
11
|
+
'**/*.rbc', # Rubinius
|
12
|
+
'**/*.o', # C object file
|
13
|
+
'**/*.log', # Ruby extension build log
|
14
|
+
'**/Makefile', # C Makefile
|
15
|
+
'**/conftest.dSYM', # OS X build directory
|
16
|
+
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
17
|
+
)
|
20
18
|
|
21
19
|
desc "Build the sys-admin library on UNIX systems (but don't install it)"
|
22
20
|
task :build => [:clean] do
|
@@ -37,6 +35,7 @@ namespace :gem do
|
|
37
35
|
|
38
36
|
if WINDOWS
|
39
37
|
spec.platform = Gem::Platform::CURRENT
|
38
|
+
spec.platform.cpu = 'universal'
|
40
39
|
spec.files = spec.files.reject{ |f| f.include?('ext') }
|
41
40
|
spec.add_dependency('win32-security', '>= 0.1.2')
|
42
41
|
else
|
data/lib/sys/admin.rb
CHANGED
@@ -4,977 +4,985 @@ require 'win32/registry'
|
|
4
4
|
require 'socket'
|
5
5
|
|
6
6
|
module Sys
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
7
|
+
class Group
|
8
|
+
# Short description of the object.
|
9
|
+
attr_accessor :caption
|
10
|
+
|
11
|
+
# Description of the group.
|
12
|
+
attr_accessor :description
|
13
|
+
|
14
|
+
# Name of the Windows domain to which the group account belongs.
|
15
|
+
attr_accessor :domain
|
16
|
+
|
17
|
+
# Date the group was added.
|
18
|
+
attr_accessor :install_date
|
19
|
+
|
20
|
+
# Name of the Windows group account on the Group#domain specified.
|
21
|
+
attr_accessor :name
|
22
|
+
|
23
|
+
# Security identifier for this group.
|
24
|
+
attr_accessor :sid
|
25
|
+
|
26
|
+
# Current status for the group, such as "ok", "error", etc.
|
27
|
+
attr_accessor :status
|
28
|
+
|
29
|
+
# The group ID.
|
30
|
+
attr_accessor :gid
|
31
|
+
|
32
|
+
# Sets whether or not the group is local (as opposed to global).
|
33
|
+
attr_writer :local
|
34
|
+
|
35
|
+
# An array of members for that group. May contain SID's.
|
36
|
+
attr_accessor :members
|
37
|
+
|
38
|
+
# Creates and returns a new Group object. This class encapsulates
|
39
|
+
# the information for a group account, whether it be global or local.
|
40
|
+
#
|
41
|
+
# Yields +self+ if a block is given.
|
42
|
+
#
|
43
|
+
def initialize
|
44
|
+
yield self if block_given?
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns whether or not the group is a local group.
|
48
|
+
#
|
49
|
+
def local?
|
50
|
+
@local
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the type of SID (Security Identifier) as a stringified value.
|
54
|
+
#
|
55
|
+
def sid_type
|
56
|
+
@sid_type
|
57
|
+
end
|
46
58
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@sid_type =
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
when Admin::SidTypeWellKnownGroup
|
86
|
-
@sid_type = "well_known_group"
|
87
|
-
when Admin::SidTypeDeletedAccount
|
88
|
-
@sid_type = "deleted_account"
|
89
|
-
when Admin::SidTypeInvalid
|
90
|
-
@sid_type = "invalid"
|
91
|
-
when Admin::SidTypeUnknown
|
92
|
-
@sid_type = "unknown"
|
93
|
-
when Admin::SidTypeComputer
|
94
|
-
@sid_type = "computer"
|
95
|
-
else
|
96
|
-
@sid_type = "unknown"
|
97
|
-
end
|
59
|
+
# Sets the SID (Security Identifier) type to +stype+, which can be
|
60
|
+
# one of the following constant values:
|
61
|
+
#
|
62
|
+
# * Admin::SidTypeUser
|
63
|
+
# * Admin::SidTypeGroup
|
64
|
+
# * Admin::SidTypeDomain
|
65
|
+
# * Admin::SidTypeAlias
|
66
|
+
# * Admin::SidTypeWellKnownGroup
|
67
|
+
# * Admin::SidTypeDeletedAccount
|
68
|
+
# * Admin::SidTypeInvalid
|
69
|
+
# * Admin::SidTypeUnknown
|
70
|
+
# * Admin::SidTypeComputer
|
71
|
+
#
|
72
|
+
def sid_type=(stype)
|
73
|
+
if stype.kind_of?(String)
|
74
|
+
@sid_type = stype.downcase
|
75
|
+
else
|
76
|
+
case stype
|
77
|
+
when Admin::SidTypeUser
|
78
|
+
@sid_type = "user"
|
79
|
+
when Admin::SidTypeGroup
|
80
|
+
@sid_type = "group"
|
81
|
+
when Admin::SidTypeDomain
|
82
|
+
@sid_type = "domain"
|
83
|
+
when Admin::SidTypeAlias
|
84
|
+
@sid_type = "alias"
|
85
|
+
when Admin::SidTypeWellKnownGroup
|
86
|
+
@sid_type = "well_known_group"
|
87
|
+
when Admin::SidTypeDeletedAccount
|
88
|
+
@sid_type = "deleted_account"
|
89
|
+
when Admin::SidTypeInvalid
|
90
|
+
@sid_type = "invalid"
|
91
|
+
when Admin::SidTypeUnknown
|
92
|
+
@sid_type = "unknown"
|
93
|
+
when Admin::SidTypeComputer
|
94
|
+
@sid_type = "computer"
|
95
|
+
else
|
96
|
+
@sid_type = "unknown"
|
98
97
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
98
|
+
end
|
99
|
+
@sid_type
|
100
|
+
end
|
101
|
+
end
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
103
|
+
class User
|
104
|
+
# An account for users whose primary account is in another domain.
|
105
|
+
TEMP_DUPLICATE = 0x0100
|
106
|
+
|
107
|
+
# Default account type that represents a typical user.
|
108
|
+
NORMAL = 0x0200
|
109
|
+
|
110
|
+
# A permit to trust account for a domain that trusts other domains.
|
111
|
+
INTERDOMAIN_TRUST = 0x0800
|
112
|
+
|
113
|
+
# An account for a Windows NT/2000 workstation or server that is a
|
114
|
+
# member of this domain.
|
115
|
+
WORKSTATION_TRUST = 0x1000
|
116
|
+
|
117
|
+
# A computer account for a backup domain controller that is a member
|
118
|
+
# of this domain.
|
119
|
+
SERVER_TRUST = 0x2000
|
120
|
+
|
121
|
+
# Domain and username of the account.
|
122
|
+
attr_accessor :caption
|
123
|
+
|
124
|
+
# Description of the account.
|
125
|
+
attr_accessor :description
|
126
|
+
|
127
|
+
# Name of the Windows domain to which a user account belongs.
|
128
|
+
attr_accessor :domain
|
129
|
+
|
130
|
+
# The user's password.
|
131
|
+
attr_accessor :password
|
132
|
+
|
133
|
+
# Full name of a local user.
|
134
|
+
attr_accessor :full_name
|
135
|
+
|
136
|
+
# An array of groups to which the user belongs.
|
137
|
+
attr_accessor :groups
|
138
|
+
|
139
|
+
# Date the user account was created.
|
140
|
+
attr_accessor :install_date
|
141
|
+
|
142
|
+
# Name of the Windows user account on the domain that the User#domain
|
143
|
+
# property specifies.
|
144
|
+
attr_accessor :name
|
145
|
+
|
146
|
+
# The user's security identifier.
|
147
|
+
attr_accessor :sid
|
148
|
+
|
149
|
+
# Current status for the user, such as "ok", "error", etc.
|
150
|
+
attr_accessor :status
|
151
|
+
|
152
|
+
# The user's id (RID).
|
153
|
+
attr_accessor :uid
|
154
|
+
|
155
|
+
# The user's home directory
|
156
|
+
attr_accessor :dir
|
157
|
+
|
158
|
+
# Used to set whether or not the account is disabled.
|
159
|
+
attr_writer :disabled
|
160
|
+
|
161
|
+
# Sets whether or not the account is defined on the local computer.
|
162
|
+
attr_writer :local
|
163
|
+
|
164
|
+
# Sets whether or not the account is locked out of the OS.
|
165
|
+
attr_writer :lockout
|
166
|
+
|
167
|
+
# Sets whether or not the password for the account can be changed.
|
168
|
+
attr_writer :password_changeable
|
169
|
+
|
170
|
+
# Sets whether or not the password for the account expires.
|
171
|
+
attr_writer :password_expires
|
172
|
+
|
173
|
+
# Sets whether or not a password is required for the account.
|
174
|
+
attr_writer :password_required
|
175
|
+
|
176
|
+
# Returns the account type as a human readable string.
|
177
|
+
attr_reader :account_type
|
112
178
|
|
113
|
-
|
114
|
-
|
115
|
-
|
179
|
+
# Creates an returns a new User object. A User object encapsulates a
|
180
|
+
# user account on the operating system.
|
181
|
+
#
|
182
|
+
# Yields +self+ if a block is provided.
|
183
|
+
#
|
184
|
+
def initialize
|
185
|
+
yield self if block_given?
|
186
|
+
end
|
116
187
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
# Date the user account was created.
|
140
|
-
attr_accessor :install_date
|
141
|
-
|
142
|
-
# Name of the Windows user account on the domain that the User#domain
|
143
|
-
# property specifies.
|
144
|
-
attr_accessor :name
|
145
|
-
|
146
|
-
# The user's security identifier.
|
147
|
-
attr_accessor :sid
|
148
|
-
|
149
|
-
# Current status for the user, such as "ok", "error", etc.
|
150
|
-
attr_accessor :status
|
151
|
-
|
152
|
-
# The user's id (RID).
|
153
|
-
attr_accessor :uid
|
154
|
-
|
155
|
-
# The user's home directory
|
156
|
-
attr_accessor :dir
|
157
|
-
|
158
|
-
# Used to set whether or not the account is disabled.
|
159
|
-
attr_writer :disabled
|
160
|
-
|
161
|
-
# Sets whether or not the account is defined on the local computer.
|
162
|
-
attr_writer :local
|
163
|
-
|
164
|
-
# Sets whether or not the account is locked out of the OS.
|
165
|
-
attr_writer :lockout
|
166
|
-
|
167
|
-
# Sets whether or not the password for the account can be changed.
|
168
|
-
attr_writer :password_changeable
|
169
|
-
|
170
|
-
# Sets whether or not the password for the account expires.
|
171
|
-
attr_writer :password_expires
|
172
|
-
|
173
|
-
# Sets whether or not a password is required for the account.
|
174
|
-
attr_writer :password_required
|
175
|
-
|
176
|
-
# Returns the account type as a human readable string.
|
177
|
-
attr_reader :account_type
|
178
|
-
|
179
|
-
# Creates an returns a new User object. A User object encapsulates a
|
180
|
-
# user account on the operating system.
|
181
|
-
#
|
182
|
-
# Yields +self+ if a block is provided.
|
183
|
-
#
|
184
|
-
def initialize
|
185
|
-
yield self if block_given?
|
186
|
-
end
|
187
|
-
|
188
|
-
# Sets the account type for the account. Possible values are:
|
189
|
-
#
|
190
|
-
# * User::TEMP_DUPLICATE
|
191
|
-
# * User::NORMAL
|
192
|
-
# * User::INTERDOMAIN_TRUST
|
193
|
-
# * User::WORKSTATION_TRUST
|
194
|
-
# * User::SERVER_TRUST
|
195
|
-
#
|
196
|
-
def account_type=(type)
|
197
|
-
case type
|
198
|
-
when TEMP_DUPLICATE
|
199
|
-
@account_type = 'duplicate'
|
200
|
-
when NORMAL
|
201
|
-
@account_type = 'normal'
|
202
|
-
when INTERDOMAIN_TRUST
|
203
|
-
@account_type = 'interdomain_trust'
|
204
|
-
when WORKSTATION_TRUST
|
205
|
-
@account_type = 'workstation_trust'
|
206
|
-
when SERVER_TRUST
|
207
|
-
@account_type = 'server_trust'
|
208
|
-
else
|
209
|
-
@account_type = 'unknown'
|
210
|
-
end
|
188
|
+
# Sets the account type for the account. Possible values are:
|
189
|
+
#
|
190
|
+
# * User::TEMP_DUPLICATE
|
191
|
+
# * User::NORMAL
|
192
|
+
# * User::INTERDOMAIN_TRUST
|
193
|
+
# * User::WORKSTATION_TRUST
|
194
|
+
# * User::SERVER_TRUST
|
195
|
+
#
|
196
|
+
def account_type=(type)
|
197
|
+
case type
|
198
|
+
when TEMP_DUPLICATE
|
199
|
+
@account_type = 'duplicate'
|
200
|
+
when NORMAL
|
201
|
+
@account_type = 'normal'
|
202
|
+
when INTERDOMAIN_TRUST
|
203
|
+
@account_type = 'interdomain_trust'
|
204
|
+
when WORKSTATION_TRUST
|
205
|
+
@account_type = 'workstation_trust'
|
206
|
+
when SERVER_TRUST
|
207
|
+
@account_type = 'server_trust'
|
208
|
+
else
|
209
|
+
@account_type = 'unknown'
|
211
210
|
end
|
211
|
+
end
|
212
212
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
213
|
+
# Returns the SID type as a human readable string.
|
214
|
+
#
|
215
|
+
def sid_type
|
216
|
+
@sid_type
|
217
|
+
end
|
218
218
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
# Returns whether or not the account is disabled.
|
258
|
-
#
|
259
|
-
def disabled?
|
260
|
-
@disabled
|
219
|
+
# Sets the SID (Security Identifier) type to +stype+, which can be
|
220
|
+
# one of the following constant values:
|
221
|
+
#
|
222
|
+
# * Admin::SidTypeUser
|
223
|
+
# * Admin::SidTypeGroup
|
224
|
+
# * Admin::SidTypeDomain
|
225
|
+
# * Admin::SidTypeAlias
|
226
|
+
# * Admin::SidTypeWellKnownGroup
|
227
|
+
# * Admin::SidTypeDeletedAccount
|
228
|
+
# * Admin::SidTypeInvalid
|
229
|
+
# * Admin::SidTypeUnknown
|
230
|
+
# * Admin::SidTypeComputer
|
231
|
+
#
|
232
|
+
def sid_type=(stype)
|
233
|
+
case stype
|
234
|
+
when Admin::SidTypeUser
|
235
|
+
@sid_type = 'user'
|
236
|
+
when Admin::SidTypeGroup
|
237
|
+
@sid_type = 'group'
|
238
|
+
when Admin::SidTypeDomain
|
239
|
+
@sid_type = 'domain'
|
240
|
+
when Admin::SidTypeAlias
|
241
|
+
@sid_type = 'alias'
|
242
|
+
when Admin::SidTypeWellKnownGroup
|
243
|
+
@sid_type = 'well_known_group'
|
244
|
+
when Admin::SidTypeDeletedAccount
|
245
|
+
@sid_type = 'deleted_account'
|
246
|
+
when Admin::SidTypeInvalid
|
247
|
+
@sid_type = 'invalid'
|
248
|
+
when Admin::SidTypeUnknown
|
249
|
+
@sid_type = 'unknown'
|
250
|
+
when Admin::SidTypeComputer
|
251
|
+
@sid_type = 'computer'
|
252
|
+
else
|
253
|
+
@sid_type = 'unknown'
|
261
254
|
end
|
255
|
+
end
|
262
256
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
257
|
+
# Returns whether or not the account is disabled.
|
258
|
+
#
|
259
|
+
def disabled?
|
260
|
+
@disabled
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns whether or not the account is local.
|
264
|
+
#
|
265
|
+
def local?
|
266
|
+
@local
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns whether or not the account is locked out.
|
270
|
+
#
|
271
|
+
def lockout?
|
272
|
+
@lockout
|
273
|
+
end
|
274
274
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
275
|
+
# Returns whether or not the password for the account is changeable.
|
276
|
+
#
|
277
|
+
def password_changeable?
|
278
|
+
@password_changeable
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns whether or not the password for the account is changeable.
|
282
|
+
#
|
283
|
+
def password_expires?
|
284
|
+
@password_expires
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns whether or not the a password is required for the account.
|
288
|
+
#
|
289
|
+
def password_required?
|
290
|
+
@password_required
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class Admin
|
295
|
+
# The version of the sys-admin library
|
296
|
+
VERSION = '1.5.5'
|
297
|
+
|
298
|
+
# This is the error raised in the majority of cases if anything goes wrong
|
299
|
+
# with any of the Sys::Admin methods.
|
300
|
+
#
|
301
|
+
class Error < StandardError; end
|
302
|
+
|
303
|
+
SidTypeUser = 1
|
304
|
+
SidTypeGroup = 2
|
305
|
+
SidTypeDomain = 3
|
306
|
+
SidTypeAlias = 4
|
307
|
+
SidTypeWellKnownGroup = 5
|
308
|
+
SidTypeDeletedAccount = 6
|
309
|
+
SidTypeInvalid = 7
|
310
|
+
SidTypeUnknown = 8
|
311
|
+
SidTypeComputer = 9
|
312
|
+
|
313
|
+
private
|
314
|
+
|
315
|
+
HKEY = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\"
|
316
|
+
|
317
|
+
# Retrieves the user's home directory. For local accounts query the
|
318
|
+
# registry. For domain accounts use ADSI and use the HomeDirectory.
|
319
|
+
#
|
320
|
+
def self.get_home_dir(user, local = false, domain = nil)
|
321
|
+
if local
|
322
|
+
sec = Win32::Security::SID.open(user)
|
323
|
+
key = HKEY + sec.to_s
|
324
|
+
dir = nil
|
325
|
+
|
326
|
+
begin
|
327
|
+
Win32::Registry::HKEY_LOCAL_MACHINE.open(key) do |reg|
|
328
|
+
dir = reg['ProfileImagePath']
|
329
|
+
end
|
330
|
+
rescue Win32::Registry::Error
|
331
|
+
# Not every local user has a home directory
|
332
|
+
end
|
333
|
+
else
|
334
|
+
domain ||= Socket.gethostname
|
335
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{user},user")
|
336
|
+
dir = adsi.get('HomeDirectory')
|
285
337
|
end
|
338
|
+
|
339
|
+
dir
|
340
|
+
end
|
341
|
+
|
342
|
+
# A private method that lower cases all keys, and converts them
|
343
|
+
# all to symbols.
|
344
|
+
#
|
345
|
+
def self.munge_options(opts)
|
346
|
+
rhash = {}
|
347
|
+
|
348
|
+
opts.each{ |k, v|
|
349
|
+
k = k.to_s.downcase.to_sym
|
350
|
+
rhash[k] = v
|
351
|
+
}
|
352
|
+
|
353
|
+
rhash
|
354
|
+
end
|
355
|
+
|
356
|
+
# An internal, private method for getting a list of groups for
|
357
|
+
# a particular user.
|
358
|
+
#
|
359
|
+
def self.get_groups(domain, user)
|
360
|
+
array = []
|
361
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{user}")
|
362
|
+
adsi.groups.each{ |g| array << g.name }
|
363
|
+
array
|
364
|
+
end
|
365
|
+
|
366
|
+
# An internal, private method for getting a list of members for
|
367
|
+
# any particular group.
|
368
|
+
#
|
369
|
+
def self.get_members(domain, group)
|
370
|
+
array = []
|
371
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{group}")
|
372
|
+
adsi.members.each{ |g| array << g.name }
|
373
|
+
array
|
374
|
+
end
|
286
375
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
else
|
334
|
-
domain ||= Socket.gethostname
|
335
|
-
adsi = WIN32OLE.connect("WinNT://#{domain}/#{user},user")
|
336
|
-
dir = adsi.get('HomeDirectory')
|
337
|
-
end
|
338
|
-
|
339
|
-
dir
|
376
|
+
# Used by the get_login method
|
377
|
+
GetUserName = Win32::API.new('GetUserName', 'PP', 'L', 'advapi32') # :nodoc:
|
378
|
+
|
379
|
+
public
|
380
|
+
|
381
|
+
# Creates the given +user+. If no domain option is specified,
|
382
|
+
# then it defaults to your local host, i.e. a local account is
|
383
|
+
# created.
|
384
|
+
#
|
385
|
+
# Any options provided are treated as IADsUser interface methods
|
386
|
+
# and are called before SetInfo is finally called.
|
387
|
+
#
|
388
|
+
# Examples:
|
389
|
+
#
|
390
|
+
# # Create a local user with no options
|
391
|
+
# Sys::Admin.add_user(:name => 'asmith')
|
392
|
+
#
|
393
|
+
# # Create a local user with options
|
394
|
+
# Sys::Admin.add_user(
|
395
|
+
# :name => 'asmith',
|
396
|
+
# :description => 'Really cool guy',
|
397
|
+
# :password => 'abc123'
|
398
|
+
# )
|
399
|
+
#
|
400
|
+
# # Create a user on a specific domain
|
401
|
+
# Sys::Admin.add_user(
|
402
|
+
# :name => 'asmith',
|
403
|
+
# :domain => 'XX',
|
404
|
+
# :fullname => 'Al Smith'
|
405
|
+
# )
|
406
|
+
#--
|
407
|
+
# Most options are passed to the 'put' method. However, we handle the
|
408
|
+
# password specially since it's a separate method, and some environments
|
409
|
+
# require that it be set up front.
|
410
|
+
#
|
411
|
+
def self.add_user(options = {})
|
412
|
+
options = munge_options(options)
|
413
|
+
|
414
|
+
name = options.delete(:name) or raise ArgumentError, 'No user given'
|
415
|
+
domain = options[:domain]
|
416
|
+
|
417
|
+
if domain.nil?
|
418
|
+
domain = Socket.gethostname
|
419
|
+
moniker = "WinNT://#{domain},Computer"
|
420
|
+
else
|
421
|
+
moniker = "WinNT://#{domain}"
|
340
422
|
end
|
341
423
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
424
|
+
begin
|
425
|
+
adsi = WIN32OLE.connect(moniker)
|
426
|
+
user = adsi.create('user', name)
|
427
|
+
options.each{ |option, value|
|
428
|
+
if option.to_s == 'password'
|
429
|
+
user.setpassword(value)
|
430
|
+
else
|
431
|
+
user.put(option.to_s, value)
|
432
|
+
end
|
433
|
+
}
|
434
|
+
user.setinfo
|
435
|
+
rescue WIN32OLERuntimeError => err
|
436
|
+
raise Error, err
|
354
437
|
end
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
438
|
+
end
|
439
|
+
|
440
|
+
# Configures the +user+ using +options+. If no domain option is
|
441
|
+
# specified then your local host is used, i.e. you are configuring
|
442
|
+
# a local user account.
|
443
|
+
#
|
444
|
+
# See http://tinyurl.com/3hjv9 for a list of valid options.
|
445
|
+
#
|
446
|
+
# In the case of a password change, pass a two element array as the
|
447
|
+
# old value and new value.
|
448
|
+
#
|
449
|
+
# Examples:
|
450
|
+
#
|
451
|
+
# # Configure a local user
|
452
|
+
# Sys::Admin.configure_user(
|
453
|
+
# :name => 'djberge',
|
454
|
+
# :description => 'Awesome'
|
455
|
+
# )
|
456
|
+
#
|
457
|
+
# # Change the password
|
458
|
+
# Sys::Admin.configure_user(
|
459
|
+
# :name => 'asmith',
|
460
|
+
# :password => [old_pass, new_pass]
|
461
|
+
# )
|
462
|
+
#
|
463
|
+
# # Configure a user on a specific domain
|
464
|
+
# Sys::Admin.configure_user(
|
465
|
+
# :name => 'jsmrz',
|
466
|
+
# :domain => 'XX',
|
467
|
+
# :firstname => 'Jo'
|
468
|
+
# )
|
469
|
+
#
|
470
|
+
def self.configure_user(options = {})
|
471
|
+
options = munge_options(options)
|
472
|
+
|
473
|
+
name = options.delete(:name) or raise ArgumentError, 'No name given'
|
474
|
+
domain = options[:domain] || Socket.gethostname
|
475
|
+
|
476
|
+
begin
|
477
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")
|
478
|
+
options.each{ |option, value|
|
479
|
+
if option.to_s == 'password'
|
480
|
+
adsi.changepassword(value[0], value[1])
|
481
|
+
else
|
482
|
+
adsi.put(option.to_s, value)
|
483
|
+
end
|
484
|
+
}
|
485
|
+
adsi.setinfo
|
486
|
+
rescue WIN32OLERuntimeError => err
|
487
|
+
raise Error, err
|
364
488
|
end
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
489
|
+
end
|
490
|
+
|
491
|
+
# Deletes the given +user+ on +domain+. If no domain is specified,
|
492
|
+
# then it defaults to your local host, i.e. a local account is
|
493
|
+
# deleted.
|
494
|
+
#
|
495
|
+
def self.delete_user(user, domain = nil)
|
496
|
+
if domain.nil?
|
497
|
+
domain = Socket.gethostname
|
498
|
+
moniker = "WinNT://#{domain},Computer"
|
499
|
+
else
|
500
|
+
moniker = "WinNT://#{domain}"
|
374
501
|
end
|
375
|
-
|
376
|
-
# Used by the get_login method
|
377
|
-
GetUserName = Win32::API.new('GetUserName', 'PP', 'L', 'advapi32') # :nodoc:
|
378
|
-
|
379
|
-
public
|
380
|
-
|
381
|
-
# Creates the given +user+. If no domain option is specified,
|
382
|
-
# then it defaults to your local host, i.e. a local account is
|
383
|
-
# created.
|
384
|
-
#
|
385
|
-
# Any options provided are treated as IADsUser interface methods
|
386
|
-
# and are called before SetInfo is finally called.
|
387
|
-
#
|
388
|
-
# Examples:
|
389
|
-
#
|
390
|
-
# # Create a local user with no options
|
391
|
-
# Sys::Admin.add_user(:name => 'asmith')
|
392
|
-
#
|
393
|
-
# # Create a local user with options
|
394
|
-
# Sys::Admin.add_user(
|
395
|
-
# :name => 'asmith',
|
396
|
-
# :description => 'Really cool guy',
|
397
|
-
# :password => 'abc123'
|
398
|
-
# )
|
399
|
-
#
|
400
|
-
# # Create a user on a specific domain
|
401
|
-
# Sys::Admin.add_user(
|
402
|
-
# :name => 'asmith',
|
403
|
-
# :domain => 'XX',
|
404
|
-
# :fullname => 'Al Smith'
|
405
|
-
# )
|
406
|
-
#--
|
407
|
-
# Most options are passed to the 'put' method. However, we handle the
|
408
|
-
# password specially since it's a separate method, and some environments
|
409
|
-
# require that it be set up front.
|
410
|
-
#
|
411
|
-
def self.add_user(options = {})
|
412
|
-
options = munge_options(options)
|
413
|
-
|
414
|
-
name = options.delete(:name) or raise ArgumentError, 'No user given'
|
415
|
-
domain = options[:domain]
|
416
|
-
|
417
|
-
if domain.nil?
|
418
|
-
domain = Socket.gethostname
|
419
|
-
moniker = "WinNT://#{domain},Computer"
|
420
|
-
else
|
421
|
-
moniker = "WinNT://#{domain}"
|
422
|
-
end
|
423
502
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
user.setpassword(value)
|
430
|
-
else
|
431
|
-
user.put(option.to_s, value)
|
432
|
-
end
|
433
|
-
}
|
434
|
-
user.setinfo
|
435
|
-
rescue WIN32OLERuntimeError => err
|
436
|
-
raise Error, err
|
437
|
-
end
|
503
|
+
begin
|
504
|
+
adsi = WIN32OLE.connect(moniker)
|
505
|
+
adsi.delete('user', user)
|
506
|
+
rescue WIN32OLERuntimeError => err
|
507
|
+
raise Error, err
|
438
508
|
end
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
def self.configure_user(options = {})
|
471
|
-
options = munge_options(options)
|
472
|
-
|
473
|
-
name = options.delete(:name) or raise ArgumentError, 'No name given'
|
474
|
-
domain = options[:domain] || Socket.gethostname
|
475
|
-
|
476
|
-
begin
|
477
|
-
adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")
|
478
|
-
options.each{ |option, value|
|
479
|
-
if option.to_s == 'password'
|
480
|
-
adsi.changepassword(value[0], value[1])
|
481
|
-
else
|
482
|
-
adsi.put(option.to_s, value)
|
483
|
-
end
|
484
|
-
}
|
485
|
-
adsi.setinfo
|
486
|
-
rescue WIN32OLERuntimeError => err
|
487
|
-
raise Error, err
|
488
|
-
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# Create a new +group+ using +options+. If no domain option is specified
|
512
|
+
# then a local group is created instead.
|
513
|
+
#
|
514
|
+
# Examples:
|
515
|
+
#
|
516
|
+
# # Create a local group with no options
|
517
|
+
# Sys::Admin.add_group(:name => 'Dudes')
|
518
|
+
#
|
519
|
+
# # Create a local group with options
|
520
|
+
# Sys::Admin.add_group(:name => 'Dudes', :description => 'Boys')
|
521
|
+
#
|
522
|
+
# # Create a group on a specific domain
|
523
|
+
# Sys::Admin.add_group(
|
524
|
+
# :name => 'Ladies',
|
525
|
+
# :domain => 'XYZ',
|
526
|
+
# :description => 'Girls'
|
527
|
+
# )
|
528
|
+
#
|
529
|
+
def self.add_group(options = {})
|
530
|
+
options = munge_options(options)
|
531
|
+
|
532
|
+
group = options.delete(:name) or raise ArgumentError, 'No name given'
|
533
|
+
domain = options[:domain]
|
534
|
+
|
535
|
+
if domain.nil?
|
536
|
+
domain = Socket.gethostname
|
537
|
+
moniker = "WinNT://#{domain},Computer"
|
538
|
+
else
|
539
|
+
moniker = "WinNT://#{domain}"
|
489
540
|
end
|
490
541
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
moniker = "WinNT://#{domain},Computer"
|
499
|
-
else
|
500
|
-
moniker = "WinNT://#{domain}"
|
501
|
-
end
|
502
|
-
|
503
|
-
begin
|
504
|
-
adsi = WIN32OLE.connect(moniker)
|
505
|
-
adsi.delete('user', user)
|
506
|
-
rescue WIN32OLERuntimeError => err
|
507
|
-
raise Error, err
|
508
|
-
end
|
542
|
+
begin
|
543
|
+
adsi = WIN32OLE.connect(moniker)
|
544
|
+
group = adsi.create('group', group)
|
545
|
+
group.setinfo
|
546
|
+
configure_group(options) unless options.empty?
|
547
|
+
rescue WIN32OLERuntimeError => err
|
548
|
+
raise Error, err
|
509
549
|
end
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
begin
|
543
|
-
adsi = WIN32OLE.connect(moniker)
|
544
|
-
group = adsi.create('group', group)
|
545
|
-
group.setinfo
|
546
|
-
configure_group(options) unless options.empty?
|
547
|
-
rescue WIN32OLERuntimeError => err
|
548
|
-
raise Error, err
|
549
|
-
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# Configures the +group+ using +options+. If no domain option is
|
553
|
+
# specified then your local host is used, i.e. you are configuring
|
554
|
+
# a local group.
|
555
|
+
#
|
556
|
+
# See http://tinyurl.com/cjkzl for a list of valid options.
|
557
|
+
#
|
558
|
+
# Examples:
|
559
|
+
#
|
560
|
+
# # Configure a local group.
|
561
|
+
# Sys::Admin.configure_group(:name => 'Abba', :description => 'Swedish')
|
562
|
+
#
|
563
|
+
# # Configure a group on a specific domain.
|
564
|
+
# Sys::Admin.configure_group(
|
565
|
+
# :name => 'Web Team',
|
566
|
+
# :domain => 'Foo',
|
567
|
+
# :description => 'Web programming cowboys'
|
568
|
+
# )
|
569
|
+
#
|
570
|
+
def self.configure_group(options = {})
|
571
|
+
options = munge_options(options)
|
572
|
+
|
573
|
+
group = options.delete(:name) or raise ArgumentError, 'No name given'
|
574
|
+
domain = options[:domain] || Socket.gethostname
|
575
|
+
|
576
|
+
begin
|
577
|
+
adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
|
578
|
+
options.each{ |option, value| adsi.put(option.to_s, value) }
|
579
|
+
adsi.setinfo
|
580
|
+
rescue WIN32OLERuntimeError => err
|
581
|
+
raise Error, err
|
550
582
|
end
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
#
|
563
|
-
# # Configure a group on a specific domain.
|
564
|
-
# Sys::Admin.configure_group(
|
565
|
-
# :name => 'Web Team',
|
566
|
-
# :domain => 'Foo',
|
567
|
-
# :description => 'Web programming cowboys'
|
568
|
-
# )
|
569
|
-
#
|
570
|
-
def self.configure_group(options = {})
|
571
|
-
options = munge_options(options)
|
572
|
-
|
573
|
-
group = options.delete(:name) or raise ArgumentError, 'No name given'
|
574
|
-
domain = options[:domain] || Socket.gethostname
|
575
|
-
|
576
|
-
begin
|
577
|
-
adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
|
578
|
-
options.each{ |option, value| adsi.put(option.to_s, value) }
|
579
|
-
adsi.setinfo
|
580
|
-
rescue WIN32OLERuntimeError => err
|
581
|
-
raise Error, err
|
582
|
-
end
|
583
|
+
end
|
584
|
+
|
585
|
+
# Delete the +group+ from +domain+. If no domain is specified, then
|
586
|
+
# you are deleting a local group.
|
587
|
+
#
|
588
|
+
def self.delete_group(group, domain = nil)
|
589
|
+
if domain.nil?
|
590
|
+
domain = Socket.gethostname
|
591
|
+
moniker = "WinNT://#{domain},Computer"
|
592
|
+
else
|
593
|
+
moniker = "WinNT://#{domain}"
|
583
594
|
end
|
584
595
|
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
domain = Socket.gethostname
|
591
|
-
moniker = "WinNT://#{domain},Computer"
|
592
|
-
else
|
593
|
-
moniker = "WinNT://#{domain}"
|
594
|
-
end
|
595
|
-
|
596
|
-
begin
|
597
|
-
adsi = WIN32OLE.connect(moniker)
|
598
|
-
obj = adsi.delete('group', group)
|
599
|
-
rescue WIN32OLERuntimeError => err
|
600
|
-
raise Error, err
|
601
|
-
end
|
596
|
+
begin
|
597
|
+
adsi = WIN32OLE.connect(moniker)
|
598
|
+
obj = adsi.delete('group', group)
|
599
|
+
rescue WIN32OLERuntimeError => err
|
600
|
+
raise Error, err
|
602
601
|
end
|
602
|
+
end
|
603
603
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
end
|
613
|
-
|
614
|
-
length = nsize.unpack('L')[0]
|
615
|
-
username = buffer[0 ... length].chop
|
616
|
-
username
|
604
|
+
# Returns the user name (only) of the current login.
|
605
|
+
#
|
606
|
+
def self.get_login
|
607
|
+
buffer = 0.chr * 256
|
608
|
+
nsize = [buffer.size].pack("L")
|
609
|
+
|
610
|
+
if GetUserName.call(buffer, nsize) == 0
|
611
|
+
raise Error, 'GetUserName() call failed in get_login'
|
617
612
|
end
|
618
613
|
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
if usr.kind_of?(Fixnum)
|
690
|
-
next if usr != uid
|
691
|
-
end
|
692
|
-
|
693
|
-
user_object = User.new do |u|
|
694
|
-
u.account_type = user.accounttype
|
695
|
-
u.caption = user.caption
|
696
|
-
u.description = user.description
|
697
|
-
u.disabled = user.disabled
|
698
|
-
u.domain = user.domain
|
699
|
-
u.full_name = user.fullname
|
700
|
-
u.install_date = user.installdate
|
701
|
-
u.local = user.localaccount
|
702
|
-
u.lockout = user.lockout
|
703
|
-
u.name = user.name
|
704
|
-
u.password_changeable = user.passwordchangeable
|
705
|
-
u.password_expires = user.passwordexpires
|
706
|
-
u.password_required = user.passwordrequired
|
707
|
-
u.sid = user.sid
|
708
|
-
u.sid_type = user.sidtype
|
709
|
-
u.status = user.status
|
710
|
-
u.uid = uid
|
711
|
-
u.groups = get_groups(domain, user.name)
|
712
|
-
u.dir = get_home_dir(user.name, options[:localaccount], domain)
|
713
|
-
end
|
714
|
-
|
715
|
-
return user_object
|
716
|
-
}
|
717
|
-
|
718
|
-
# If we're here, it means it wasn't found.
|
719
|
-
raise Error, "no user found for '#{usr}'"
|
614
|
+
length = nsize.unpack('L')[0]
|
615
|
+
username = buffer[0 ... length].chop
|
616
|
+
username
|
617
|
+
end
|
618
|
+
|
619
|
+
# Returns a User object based on either +name+ or +uid+.
|
620
|
+
#
|
621
|
+
# call-seq:
|
622
|
+
# Sys::Admin.get_user(name, options = {})
|
623
|
+
# Sys::Admin.get_user(uid, options = {})
|
624
|
+
#
|
625
|
+
# Looks for +usr+ information based on the options you specify, where
|
626
|
+
# the +usr+ argument can be either a user name or a RID.
|
627
|
+
#
|
628
|
+
# If a 'host' option is specified, information is retrieved from that
|
629
|
+
# host. Otherwise, the local host is used.
|
630
|
+
#
|
631
|
+
# All other options are converted to WQL statements against the
|
632
|
+
# Win32_UserAccount WMI object. See http://tinyurl.com/by9nvn for a
|
633
|
+
# list of possible options.
|
634
|
+
#
|
635
|
+
# Examples:
|
636
|
+
#
|
637
|
+
# # Get a user by name
|
638
|
+
# Admin.get_user('djberge')
|
639
|
+
#
|
640
|
+
# # Get a user by uid
|
641
|
+
# Admin.get_user(100)
|
642
|
+
#
|
643
|
+
# # Get a user on a specific domain
|
644
|
+
# Admin.get_user('djberge', :domain => 'TEST')
|
645
|
+
#--
|
646
|
+
# The reason for keeping the +usr+ variable as a separate argument
|
647
|
+
# instead of rolling it into the options hash was to keep a unified
|
648
|
+
# API between the Windows and UNIX versions.
|
649
|
+
#
|
650
|
+
def self.get_user(usr, options = {})
|
651
|
+
options = munge_options(options)
|
652
|
+
|
653
|
+
host = options.delete(:host) || Socket.gethostname
|
654
|
+
cs = "winmgmts:{impersonationLevel=impersonate}!"
|
655
|
+
cs << "//#{host}/root/cimv2"
|
656
|
+
|
657
|
+
begin
|
658
|
+
wmi = WIN32OLE.connect(cs)
|
659
|
+
rescue WIN32OLERuntimeError => err
|
660
|
+
raise Error, err
|
661
|
+
end
|
662
|
+
|
663
|
+
query = "select * from win32_useraccount"
|
664
|
+
|
665
|
+
i = 0
|
666
|
+
|
667
|
+
options.each{ |opt, val|
|
668
|
+
if i == 0
|
669
|
+
query << " where #{opt} = '#{val}'"
|
670
|
+
i += 1
|
671
|
+
else
|
672
|
+
query << " and #{opt} = '#{val}'"
|
673
|
+
end
|
674
|
+
}
|
675
|
+
|
676
|
+
if usr.kind_of?(Fixnum)
|
677
|
+
query << " and sid like '%-#{usr}'"
|
678
|
+
else
|
679
|
+
if i > 0
|
680
|
+
query << " and name = '#{usr}'"
|
681
|
+
else
|
682
|
+
query << " where name = '#{usr}'"
|
683
|
+
end
|
720
684
|
end
|
685
|
+
|
686
|
+
domain = options[:domain] || host
|
687
|
+
|
688
|
+
wmi.execquery(query).each{ |user|
|
689
|
+
uid = user.sid.split('-').last.to_i
|
690
|
+
|
691
|
+
# Because our 'like' query isn't fulproof, let's parse
|
692
|
+
# the SID again to make sure
|
693
|
+
if usr.kind_of?(Fixnum)
|
694
|
+
next if usr != uid
|
695
|
+
end
|
696
|
+
|
697
|
+
user_object = User.new do |u|
|
698
|
+
u.account_type = user.accounttype
|
699
|
+
u.caption = user.caption
|
700
|
+
u.description = user.description
|
701
|
+
u.disabled = user.disabled
|
702
|
+
u.domain = user.domain
|
703
|
+
u.full_name = user.fullname
|
704
|
+
u.install_date = user.installdate
|
705
|
+
u.local = user.localaccount
|
706
|
+
u.lockout = user.lockout
|
707
|
+
u.name = user.name
|
708
|
+
u.password_changeable = user.passwordchangeable
|
709
|
+
u.password_expires = user.passwordexpires
|
710
|
+
u.password_required = user.passwordrequired
|
711
|
+
u.sid = user.sid
|
712
|
+
u.sid_type = user.sidtype
|
713
|
+
u.status = user.status
|
714
|
+
u.uid = uid
|
715
|
+
u.groups = get_groups(domain, user.name)
|
716
|
+
u.dir = get_home_dir(user.name, options[:localaccount], domain)
|
717
|
+
end
|
718
|
+
|
719
|
+
return user_object
|
720
|
+
}
|
721
|
+
|
722
|
+
# If we're here, it means it wasn't found.
|
723
|
+
raise Error, "no user found for '#{usr}'"
|
724
|
+
end
|
721
725
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
end
|
758
|
-
|
759
|
-
query = "select * from win32_useraccount"
|
760
|
-
|
761
|
-
i = 0
|
762
|
-
|
763
|
-
options.each{ |opt, val|
|
764
|
-
if i == 0
|
765
|
-
query << " where #{opt} = '#{val}'"
|
766
|
-
i += 1
|
767
|
-
else
|
768
|
-
query << " and #{opt} = '#{val}'"
|
769
|
-
end
|
770
|
-
}
|
771
|
-
|
772
|
-
array = []
|
773
|
-
domain = options[:domain] || host
|
774
|
-
|
775
|
-
wmi.execquery(query).each{ |user|
|
776
|
-
uid = user.sid.split('-').last.to_i
|
777
|
-
|
778
|
-
usr = User.new do |u|
|
779
|
-
u.account_type = user.accounttype
|
780
|
-
u.caption = user.caption
|
781
|
-
u.description = user.description
|
782
|
-
u.disabled = user.disabled
|
783
|
-
u.domain = user.domain
|
784
|
-
u.full_name = user.fullname
|
785
|
-
u.install_date = user.installdate
|
786
|
-
u.local = user.localaccount
|
787
|
-
u.lockout = user.lockout
|
788
|
-
u.name = user.name
|
789
|
-
u.password_changeable = user.passwordchangeable
|
790
|
-
u.password_expires = user.passwordexpires
|
791
|
-
u.password_required = user.passwordrequired
|
792
|
-
u.sid = user.sid
|
793
|
-
u.sid_type = user.sidtype
|
794
|
-
u.status = user.status
|
795
|
-
u.groups = get_groups(domain, user.name)
|
796
|
-
u.uid = uid
|
797
|
-
u.dir = get_home_dir(user.name, options[:localaccount], host)
|
798
|
-
end
|
799
|
-
|
800
|
-
if block_given?
|
801
|
-
yield usr
|
802
|
-
else
|
803
|
-
array.push(usr)
|
804
|
-
end
|
805
|
-
}
|
806
|
-
|
807
|
-
return array unless block_given?
|
726
|
+
# In block form, yields a User object for each user on the system. In
|
727
|
+
# non-block form, returns an Array of User objects.
|
728
|
+
#
|
729
|
+
# call-seq:
|
730
|
+
# Sys::Admin.users(options = {})
|
731
|
+
# Sys::Admin.users(options = {}){ |user| ... }
|
732
|
+
#
|
733
|
+
# You may specify a host from which information is retrieved. The
|
734
|
+
# default is the local host.
|
735
|
+
#
|
736
|
+
# All other arguments are passed as WQL query parameters against
|
737
|
+
# the Win32_UserAccont WMI object.
|
738
|
+
#
|
739
|
+
# Examples:
|
740
|
+
#
|
741
|
+
# # Get all local account users
|
742
|
+
# Sys::Admin.users(:localaccount => true)
|
743
|
+
#
|
744
|
+
# # Get all user accounts on a specific domain
|
745
|
+
# Sys::Admin.users(:domain => 'FOO')
|
746
|
+
#
|
747
|
+
# # Get a single user from a domain
|
748
|
+
# Sys::Admin.users(:name => 'djberge', :domain => 'FOO')
|
749
|
+
#
|
750
|
+
def self.users(options = {})
|
751
|
+
options = munge_options(options)
|
752
|
+
|
753
|
+
host = options.delete(:host) || Socket.gethostname
|
754
|
+
cs = "winmgmts:{impersonationLevel=impersonate}!"
|
755
|
+
cs << "//#{host}/root/cimv2"
|
756
|
+
|
757
|
+
begin
|
758
|
+
wmi = WIN32OLE.connect(cs)
|
759
|
+
rescue WIN32OLERuntimeError => e
|
760
|
+
raise Error, e
|
808
761
|
end
|
762
|
+
|
763
|
+
query = "select * from win32_useraccount"
|
764
|
+
|
765
|
+
i = 0
|
766
|
+
|
767
|
+
options.each{ |opt, val|
|
768
|
+
if i == 0
|
769
|
+
query << " where #{opt} = '#{val}'"
|
770
|
+
i += 1
|
771
|
+
else
|
772
|
+
query << " and #{opt} = '#{val}'"
|
773
|
+
end
|
774
|
+
}
|
775
|
+
|
776
|
+
array = []
|
777
|
+
domain = options[:domain] || host
|
778
|
+
|
779
|
+
wmi.execquery(query).each{ |user|
|
780
|
+
uid = user.sid.split('-').last.to_i
|
781
|
+
|
782
|
+
usr = User.new do |u|
|
783
|
+
u.account_type = user.accounttype
|
784
|
+
u.caption = user.caption
|
785
|
+
u.description = user.description
|
786
|
+
u.disabled = user.disabled
|
787
|
+
u.domain = user.domain
|
788
|
+
u.full_name = user.fullname
|
789
|
+
u.install_date = user.installdate
|
790
|
+
u.local = user.localaccount
|
791
|
+
u.lockout = user.lockout
|
792
|
+
u.name = user.name
|
793
|
+
u.password_changeable = user.passwordchangeable
|
794
|
+
u.password_expires = user.passwordexpires
|
795
|
+
u.password_required = user.passwordrequired
|
796
|
+
u.sid = user.sid
|
797
|
+
u.sid_type = user.sidtype
|
798
|
+
u.status = user.status
|
799
|
+
u.groups = get_groups(domain, user.name)
|
800
|
+
u.uid = uid
|
801
|
+
u.dir = get_home_dir(user.name, options[:localaccount], host)
|
802
|
+
end
|
803
|
+
|
804
|
+
if block_given?
|
805
|
+
yield usr
|
806
|
+
else
|
807
|
+
array.push(usr)
|
808
|
+
end
|
809
|
+
}
|
810
|
+
|
811
|
+
return array unless block_given?
|
812
|
+
end
|
809
813
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
wmi.execquery(query).each{ |group|
|
872
|
-
gid = group.sid.split("-").last.to_i
|
873
|
-
|
874
|
-
# Because our 'like' query isn't fulproof, let's parse
|
875
|
-
# the SID again to make sure
|
876
|
-
if grp.kind_of?(Fixnum)
|
877
|
-
next if grp != gid
|
878
|
-
end
|
879
|
-
|
880
|
-
group_object = Group.new do |g|
|
881
|
-
g.caption = group.caption
|
882
|
-
g.description = group.description
|
883
|
-
g.domain = group.domain
|
884
|
-
g.gid = gid
|
885
|
-
g.install_date = group.installdate
|
886
|
-
g.local = group.localaccount
|
887
|
-
g.name = group.name
|
888
|
-
g.sid = group.sid
|
889
|
-
g.sid_type = group.sidtype
|
890
|
-
g.status = group.status
|
891
|
-
g.members = get_members(domain, group.name)
|
892
|
-
end
|
893
|
-
|
894
|
-
return group_object
|
895
|
-
}
|
896
|
-
|
897
|
-
# If we're here, it means it wasn't found.
|
898
|
-
raise Error, "no group found for '#{grp}'"
|
814
|
+
# Returns a Group object based on either +name+ or +gid+.
|
815
|
+
#
|
816
|
+
# call-seq:
|
817
|
+
# Sys::Admin.get_group(name, options = {})
|
818
|
+
# Sys::Admin.get_group(gid, options = {})
|
819
|
+
#
|
820
|
+
# If a numeric value is sent as the first parameter, it is treated
|
821
|
+
# as a RID and is checked against the SID for a match.
|
822
|
+
#
|
823
|
+
# You may specify a host as an option from which information is
|
824
|
+
# retrieved. The default is the local host.
|
825
|
+
#
|
826
|
+
# All other options are passed as WQL parameters to the Win32_Group
|
827
|
+
# WMI object. See http://tinyurl.com/bngc8s for a list of possible
|
828
|
+
# options.
|
829
|
+
#
|
830
|
+
# Examples:
|
831
|
+
#
|
832
|
+
# # Find a group by name
|
833
|
+
# Sys::Admin.get_group('Web Team')
|
834
|
+
#
|
835
|
+
# # Find a group by id
|
836
|
+
# Sys::Admin.get_group(31667)
|
837
|
+
#
|
838
|
+
# # Find a group on a specific domain
|
839
|
+
# Sys::Admin.get_group('Web Team', :domain => 'FOO')
|
840
|
+
#
|
841
|
+
def self.get_group(grp, options = {})
|
842
|
+
options = munge_options(options)
|
843
|
+
|
844
|
+
host = options.delete(:host) || Socket.gethostname
|
845
|
+
cs = "winmgmts:{impersonationLevel=impersonate}!"
|
846
|
+
cs << "//#{host}/root/cimv2"
|
847
|
+
|
848
|
+
begin
|
849
|
+
wmi = WIN32OLE.connect(cs)
|
850
|
+
rescue WIN32OLERuntimeError => err
|
851
|
+
raise Error, err
|
852
|
+
end
|
853
|
+
|
854
|
+
query = "select * from win32_group"
|
855
|
+
|
856
|
+
i = 0
|
857
|
+
|
858
|
+
options.each{ |opt, val|
|
859
|
+
if i == 0
|
860
|
+
query << " where #{opt} = '#{val}'"
|
861
|
+
i += 1
|
862
|
+
else
|
863
|
+
query << " and #{opt} = '#{val}'"
|
864
|
+
end
|
865
|
+
}
|
866
|
+
|
867
|
+
if grp.kind_of?(Fixnum)
|
868
|
+
query << " and sid like '%-#{grp}'"
|
869
|
+
else
|
870
|
+
if i > 0
|
871
|
+
query << " and name = '#{grp}'"
|
872
|
+
else
|
873
|
+
query << " where name = '#{grp}'"
|
874
|
+
end
|
899
875
|
end
|
876
|
+
|
877
|
+
domain = options[:domain] || host
|
878
|
+
|
879
|
+
wmi.execquery(query).each{ |group|
|
880
|
+
gid = group.sid.split("-").last.to_i
|
881
|
+
|
882
|
+
# Because our 'like' query isn't fulproof, let's parse
|
883
|
+
# the SID again to make sure
|
884
|
+
if grp.kind_of?(Fixnum)
|
885
|
+
next if grp != gid
|
886
|
+
end
|
887
|
+
|
888
|
+
group_object = Group.new do |g|
|
889
|
+
g.caption = group.caption
|
890
|
+
g.description = group.description
|
891
|
+
g.domain = group.domain
|
892
|
+
g.gid = gid
|
893
|
+
g.install_date = group.installdate
|
894
|
+
g.local = group.localaccount
|
895
|
+
g.name = group.name
|
896
|
+
g.sid = group.sid
|
897
|
+
g.sid_type = group.sidtype
|
898
|
+
g.status = group.status
|
899
|
+
g.members = get_members(domain, group.name)
|
900
|
+
end
|
901
|
+
|
902
|
+
return group_object
|
903
|
+
}
|
904
|
+
|
905
|
+
# If we're here, it means it wasn't found.
|
906
|
+
raise Error, "no group found for '#{grp}'"
|
907
|
+
end
|
900
908
|
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
end
|
938
|
-
|
939
|
-
query = "select * from win32_group"
|
940
|
-
|
941
|
-
i = 0
|
942
|
-
|
943
|
-
options.each{ |opt, val|
|
944
|
-
if i == 0
|
945
|
-
query << " where #{opt} = '#{val}'"
|
946
|
-
i += 1
|
947
|
-
else
|
948
|
-
query << " and #{opt} = '#{val}'"
|
949
|
-
end
|
950
|
-
}
|
951
|
-
|
952
|
-
array = []
|
953
|
-
domain = options[:domain] || host
|
954
|
-
|
955
|
-
wmi.execquery(query).each{ |group|
|
956
|
-
grp = Group.new do |g|
|
957
|
-
g.caption = group.caption
|
958
|
-
g.description = group.description
|
959
|
-
g.domain = group.domain
|
960
|
-
g.gid = group.sid.split("-").last.to_i
|
961
|
-
g.install_date = group.installdate
|
962
|
-
g.local = group.localaccount
|
963
|
-
g.name = group.name
|
964
|
-
g.sid = group.sid
|
965
|
-
g.sid_type = group.sidtype
|
966
|
-
g.status = group.status
|
967
|
-
g.members = get_members(domain, group.name)
|
968
|
-
end
|
969
|
-
|
970
|
-
if block_given?
|
971
|
-
yield grp
|
972
|
-
else
|
973
|
-
array.push(grp)
|
974
|
-
end
|
975
|
-
}
|
976
|
-
|
977
|
-
return array unless block_given?
|
909
|
+
# In block form, yields a Group object for each user on the system. In
|
910
|
+
# non-block form, returns an Array of Group objects.
|
911
|
+
#
|
912
|
+
# call-seq:
|
913
|
+
# groups(options = {})
|
914
|
+
# groups(options = {}){ |group| ... }
|
915
|
+
#
|
916
|
+
# You may specify a host option from which information is retrieved.
|
917
|
+
# The default is the local host.
|
918
|
+
#
|
919
|
+
# All other options are passed as WQL parameters to the Win32_Group
|
920
|
+
# WMI object. See http://tinyurl.com/bngc8s for a list of possible
|
921
|
+
# options.
|
922
|
+
#
|
923
|
+
# Examples:
|
924
|
+
#
|
925
|
+
# # Get local group information
|
926
|
+
# Sys::Admin.groups(:localaccount => true)
|
927
|
+
#
|
928
|
+
# # Get all groups on a specific domain
|
929
|
+
# Sys::Admin.groups(:domain => 'FOO')
|
930
|
+
#
|
931
|
+
# # Get a specific group on a domain
|
932
|
+
# Sys::Admin.groups(:name => 'Some Group', :domain => 'FOO')
|
933
|
+
#
|
934
|
+
def self.groups(options = {})
|
935
|
+
options = munge_options(options)
|
936
|
+
|
937
|
+
host = options.delete(:host) || Socket.gethostname
|
938
|
+
cs = "winmgmts:{impersonationLevel=impersonate}!"
|
939
|
+
cs << "//#{host}/root/cimv2"
|
940
|
+
|
941
|
+
begin
|
942
|
+
wmi = WIN32OLE.connect(cs)
|
943
|
+
rescue WIN32OLERuntimeError => err
|
944
|
+
raise Error, err
|
978
945
|
end
|
979
|
-
|
946
|
+
|
947
|
+
query = "select * from win32_group"
|
948
|
+
|
949
|
+
i = 0
|
950
|
+
|
951
|
+
options.each{ |opt, val|
|
952
|
+
if i == 0
|
953
|
+
query << " where #{opt} = '#{val}'"
|
954
|
+
i += 1
|
955
|
+
else
|
956
|
+
query << " and #{opt} = '#{val}'"
|
957
|
+
end
|
958
|
+
}
|
959
|
+
|
960
|
+
array = []
|
961
|
+
domain = options[:domain] || host
|
962
|
+
|
963
|
+
wmi.execquery(query).each{ |group|
|
964
|
+
grp = Group.new do |g|
|
965
|
+
g.caption = group.caption
|
966
|
+
g.description = group.description
|
967
|
+
g.domain = group.domain
|
968
|
+
g.gid = group.sid.split("-").last.to_i
|
969
|
+
g.install_date = group.installdate
|
970
|
+
g.local = group.localaccount
|
971
|
+
g.name = group.name
|
972
|
+
g.sid = group.sid
|
973
|
+
g.sid_type = group.sidtype
|
974
|
+
g.status = group.status
|
975
|
+
g.members = get_members(domain, group.name)
|
976
|
+
end
|
977
|
+
|
978
|
+
if block_given?
|
979
|
+
yield grp
|
980
|
+
else
|
981
|
+
array.push(grp)
|
982
|
+
end
|
983
|
+
}
|
984
|
+
|
985
|
+
return array unless block_given?
|
986
|
+
end
|
987
|
+
end
|
980
988
|
end
|