sys-admin 1.3.1-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGES +31 -0
  2. data/MANIFEST +17 -0
  3. data/README +174 -0
  4. data/lib/sys/admin.rb +403 -0
  5. data/test/tc_admin.rb +14 -0
  6. metadata +51 -0
data/CHANGES ADDED
@@ -0,0 +1,31 @@
1
+ == 1.3.1 - 29-Jun-2005
2
+ * Fixed a bug where the inability to read the lastlog file caused an error.
3
+ From now on that error is ignored, and the lastlog attributes of the User
4
+ object are set to nil.
5
+ * Added a beta version of Admin.delete_user (Windows only).
6
+
7
+ == 1.3.0 - 3-Jun-2005
8
+ * Bug fixes for Linux.
9
+ * Removed the version.h file - no longer needed since the Win32 version is
10
+ pure Ruby.
11
+
12
+ == 1.2.0 - 30-Apr-2005
13
+ * Replaced the Win32 version with a pure Ruby version that uses Win32API and
14
+ win32ole + WMI.
15
+ * The LocalGroup class no longer exists in the Win32 version. Instead, it is
16
+ now an attribute of a Group object. The issue was forced by WMI.
17
+ * The default for users and groups on Win32 systems is now local rather than
18
+ global. See the documentation for why you probably don't want to iterate
19
+ over global accounts.
20
+ * Corresponding doc changes and test suite changes.
21
+
22
+ == 1.1.0 - 1-Apr-2005
23
+ * Fixed bug where a segfault could occur when trying to retrieve a user or
24
+ group by an ID that didn't exist (Unix).
25
+ * Added tests for intentional failures.
26
+ * Added lastlog information tothe User class (Unix).
27
+ * Modified the way User objects are created internally (Unix).
28
+ * Fixed a bug in the User#shell attribute (Unix).
29
+
30
+ == 1.0.0 - 25-Mar-2005
31
+ * Initial release
@@ -0,0 +1,17 @@
1
+ extconf.rb
2
+ install.rb
3
+ sys-admin.gemspec
4
+ CHANGES
5
+ MANIFEST
6
+ README
7
+
8
+ examples/groups.rb
9
+ examples/users.rb
10
+
11
+ lib/sys/unix.c
12
+ lib/sys/unix.h
13
+ lib/sys/win32.rb
14
+
15
+ test/tc_admin.rb
16
+ test/tc_unix.rb
17
+ test/tc_win32.rb
data/README ADDED
@@ -0,0 +1,174 @@
1
+ == Description
2
+ The sys-admin package is a unified, cross platform replacement for the
3
+ Etc module.
4
+
5
+ == Installation
6
+ === Win32
7
+ ruby test\tc_admin.rb # optional
8
+ ruby install.rb
9
+
10
+ === Unix
11
+ ruby extconf.rb
12
+ nmake
13
+ ruby test\tc_admin.rb # optional
14
+ nmake site-install
15
+
16
+ == Synopsis
17
+ require "sys/admin"
18
+ include Sys
19
+
20
+ # Yields a User object for each user
21
+ Admin.users{ |user|
22
+ p user
23
+ }
24
+
25
+ # Returns an Array of User objects
26
+ a = Admin.users
27
+
28
+ # Yields a Group object for each group
29
+ Admin.groups{ |group|
30
+ p group
31
+ }
32
+
33
+ # Returns an Array of Group objects
34
+ g = Admin.groups
35
+
36
+ # Get information about a particular user
37
+ p Admin.get_user("nobody")
38
+
39
+ # Get information about a particular group
40
+ p Admin.get_group("adm")
41
+
42
+ == Admin
43
+ Admin.get_login
44
+ Returns the user name (only) of the current login.
45
+
46
+ Admin.get_user(name, host=localhost)
47
+ Admin.get_user(uid, host=localhost, local=true)
48
+ Returns a User object based on +name+ or +uid+.
49
+
50
+ Win32 only: you may specify a host from which information is retrieved.
51
+ The default is the local machine. You may also specify whether to
52
+ retrieve a local or global account. The default is local.
53
+
54
+ Admin.get_group(name, host=localhost, local=true)
55
+ Admin.get_group(gid, host=localhost, local=true)
56
+ Returns a Group object based on +name+ or +uid+.
57
+
58
+ Win32 only: you may specify a host from which information is retrieved.
59
+ The default is the local machine. You can retrieve either a global or
60
+ local group, depending on the value of the +local+ argument.
61
+
62
+ Admin.groups(host=localhost, local=true)
63
+ Admin.groups(host=localhost, local=true){ |group| ... }
64
+ In block form, yields a Group object for each user on the system. In
65
+ non-block form, returns an Array of Group objects.
66
+
67
+ Win32 only: you may specify a host from which information is retrieved.
68
+ The default is the local machine. You can retrieve either a global or
69
+ local group, depending on the value of the +local+ argument.
70
+
71
+ Admin.users(host=localhost, local=true)
72
+ Admin.users(host=localhost, local=true){ |user| ... }
73
+ In block form, yields a User object for each user on the system. In
74
+ non-block form, returns an Array of User objects.
75
+
76
+ Win32 only: you may specify a host from which information is retrieved.
77
+ The default is the local machine. You can retrieve either a global or
78
+ local group, depending on the value of the +local+ argument.
79
+
80
+ == User class
81
+ === User (Win32)
82
+ The User class has the following attributes on Win32 systems:
83
+
84
+ * account_type
85
+ * caption
86
+ * description
87
+ * domain
88
+ * password
89
+ * full_name
90
+ * install_date
91
+ * name
92
+ * sid
93
+ * status
94
+ * disabled?
95
+ * local?
96
+ * lockout?
97
+ * password_changeable?
98
+ * password_expires?
99
+ * password_required?
100
+
101
+ === User (Unix)
102
+ The User class has the following attributes on Unix systems:
103
+
104
+ * name
105
+ * passwd
106
+ * uid
107
+ * gid
108
+ * dir
109
+ * shell
110
+ * gecos
111
+ * quota
112
+ * age
113
+ * class
114
+ * comment
115
+ * expire
116
+
117
+ == Group Classes
118
+ === Group (Win32)
119
+ The Group class has the following attributes on Win32 systems:
120
+
121
+ * caption
122
+ * description
123
+ * domain
124
+ * install_date
125
+ * name
126
+ * sid
127
+ * status
128
+ * gid
129
+ * local?
130
+
131
+ === Group (Unix)
132
+ The Group class has the following attributes on Unix systems:
133
+
134
+ * name
135
+ * gid
136
+ * members
137
+ * passwd
138
+
139
+ == Error Classes
140
+ AdminError < StandardError
141
+ Raised if anything goes wrong with any of the above methods.
142
+
143
+ == Developer's Notes
144
+ === Win32
145
+ The Win32 version now uses a win32ole + WMI approach to getting
146
+ information. This means that the WMI service must be running on the
147
+ target machine in order to work (which it is, by default).
148
+
149
+ Note that, by default, local user and group information is retrieved
150
+ instead of global. You probably do NOT want to iterate over global users
151
+ or groups because there can be quite a few on your domain.
152
+
153
+ == Future Plans
154
+ The following methods will be added for both platforms:
155
+
156
+ * Admin.add_user
157
+ * Admin.config_user
158
+ * Admin.delete_user
159
+
160
+ == Known Bugs
161
+ None that I'm aware of. If you find any, please log them on the project
162
+ page at http://www.rubyforge.org/projects/sysutils.
163
+
164
+ == License
165
+ Ruby's
166
+
167
+ == Copyright
168
+ (C) 2005, Daniel J. Berger
169
+ All Rights Reserved
170
+
171
+ == Author
172
+ Daniel J. Berger
173
+ djberg96@yahoo.com
174
+ IRC nickname: imperator/mok/rubyhacker1
@@ -0,0 +1,403 @@
1
+ require "win32ole"
2
+ require "Win32API"
3
+ require "socket"
4
+
5
+ module Sys
6
+ class AdminError < StandardError; end
7
+
8
+ class Group
9
+ attr_accessor :caption, :description, :domain, :install_date
10
+ attr_accessor :name, :sid, :status, :gid
11
+ attr_writer :local
12
+ def initialize
13
+ yield self if block_given?
14
+ end
15
+
16
+ def local?
17
+ @local
18
+ end
19
+
20
+ def sid_type
21
+ @sid_type
22
+ end
23
+
24
+ def sid_type=(stype)
25
+ case stype
26
+ when 1
27
+ @sid_type = "user"
28
+ when 2
29
+ @sid_type = "group"
30
+ when 3
31
+ @sid_type = "domain"
32
+ when 4
33
+ @sid_type = "alias"
34
+ when 5
35
+ @sid_type = "well_known_group"
36
+ when 6
37
+ @sid_type = "deleted_account"
38
+ when 7
39
+ @sid_type = "invalid"
40
+ when 8
41
+ @sid_type = "unknown"
42
+ when 9
43
+ @sid_type = "computer"
44
+ else
45
+ @sid_type = "unknown"
46
+ end
47
+ @sid_type
48
+ end
49
+ end
50
+
51
+ class User
52
+
53
+ attr_accessor :caption, :description, :domain, :password
54
+ attr_accessor :full_name, :install_date, :name, :sid, :status
55
+ attr_writer :disabled, :local, :lockout, :password_changeable
56
+ attr_writer :password_expires, :password_required
57
+ attr_reader :account_type
58
+
59
+ def initialize
60
+ yield self if block_given?
61
+ end
62
+
63
+ def account_type=(type)
64
+ case type
65
+ when 256
66
+ @account_type = "duplicate"
67
+ when 512
68
+ @account_type = "normal"
69
+ when 2048
70
+ @account_type = "interdomain_trust"
71
+ when 4096
72
+ @account_type = "workstation_trust"
73
+ when 8192
74
+ @account_type = "server_trust"
75
+ else
76
+ @account_type = "unknown"
77
+ end
78
+ end
79
+
80
+ def sid_type
81
+ @sid_type
82
+ end
83
+
84
+ def sid_type=(stype)
85
+ case stype
86
+ when 1
87
+ @sid_type = "user"
88
+ when 2
89
+ @sid_type = "group"
90
+ when 3
91
+ @sid_type = "domain"
92
+ when 4
93
+ @sid_type = "alias"
94
+ when 5
95
+ @sid_type = "well_known_group"
96
+ when 6
97
+ @sid_type = "deleted_account"
98
+ when 7
99
+ @sid_type = "invalid"
100
+ when 8
101
+ @sid_type = "unknown"
102
+ when 9
103
+ @sid_type = "computer"
104
+ else
105
+ @sid_type = "unknown"
106
+ end
107
+ end
108
+
109
+ def disabled?
110
+ @disabled
111
+ end
112
+
113
+ def local?
114
+ @local
115
+ end
116
+
117
+ def lockout?
118
+ @lockout
119
+ end
120
+
121
+ def password_changeable?
122
+ @password_changeable
123
+ end
124
+
125
+ def password_expires?
126
+ @password_expires
127
+ end
128
+
129
+ def password_required?
130
+ @password_required
131
+ end
132
+ end
133
+
134
+ class Admin
135
+ VERSION = "1.3.1"
136
+
137
+ # Deletes +userid+ from the given +host+, or the local host if no host
138
+ # is specified.
139
+ #
140
+ def self.delete_user(userid=nil, host=Socket.gethostname)
141
+ begin
142
+ adsi = WIN32OLE.connect("WinNT://#{host},Computer")
143
+ rescue WIN32OLERuntimeError => err
144
+ raise AdminError, err
145
+ end
146
+
147
+ begin
148
+ adsi.delete("user", userid)
149
+ rescue WIN32OLERuntimeError => err
150
+ raise AdminError, err
151
+ end
152
+ end
153
+
154
+ # Returns the user name (only) of the current login.
155
+ #
156
+ def self.get_login
157
+ getlogin = Win32API.new("advapi32","GetUserName",['P','P'],'L')
158
+ buffer = "\0" * 256;
159
+ nsize = [256].pack("L")
160
+ getlogin.call(buffer,nsize)
161
+ len = nsize.unpack("L")[0]
162
+ username = buffer[0 ... len].chop
163
+ username
164
+ end
165
+
166
+ # Returns a User object based on either +name+ or +uid+.
167
+ #
168
+ # call-seq:
169
+ # get_user(name, host=localhost)
170
+ # get_user(uid, host=localhost, local=true)
171
+ #
172
+ # You may specify a +host+ from which information is retrieved. The
173
+ # default is the local machine. You may also specify whether to
174
+ # retrieve a local or global account. The default is local.
175
+ #
176
+ def self.get_user(uid, host=Socket.gethostname, local=true)
177
+ host = Socket.gethostname if host.nil?
178
+ cs = "winmgmts:{impersonationLevel=impersonate}!"
179
+ cs << "//#{host}/root/cimv2"
180
+
181
+ begin
182
+ wmi = WIN32OLE.connect(cs)
183
+ rescue WIN32OLERuntimeError => e
184
+ raise AdminError, e
185
+ end
186
+
187
+ query = "select * from win32_useraccount"
188
+ query << " where localaccount = true" if local
189
+
190
+ if uid.kind_of?(Fixnum)
191
+ if local
192
+ query << " and sid like '%-#{uid}'"
193
+ else
194
+ query << " where sid like '%-#{uid}'"
195
+ end
196
+ else
197
+ if local
198
+ query << " and name = '#{uid}'"
199
+ else
200
+ query << " where name = '#{uid}'"
201
+ end
202
+ end
203
+
204
+ wmi.execquery(query).each{ |user|
205
+ # Because our 'like' query isn't fulproof, let's parse
206
+ # the SID again to make sure
207
+ if uid.kind_of?(Fixnum)
208
+ if user.sid.split("-").last.to_i != uid
209
+ next
210
+ end
211
+ end
212
+ usr = User.new do |u|
213
+ u.account_type = user.accounttype
214
+ u.caption = user.caption
215
+ u.description = user.description
216
+ u.disabled = user.disabled
217
+ u.domain = user.domain
218
+ u.full_name = user.fullname
219
+ u.install_date = user.installdate
220
+ u.local = user.localaccount
221
+ u.lockout = user.lockout
222
+ u.name = user.name
223
+ u.password_changeable = user.passwordchangeable
224
+ u.password_expires = user.passwordexpires
225
+ u.password_required = user.passwordrequired
226
+ u.sid = user.sid
227
+ u.sid_type = user.sidtype
228
+ u.status = user.status
229
+ end
230
+ return usr
231
+ }
232
+ end
233
+
234
+ # In block form, yields a User object for each user on the system. In
235
+ # non-block form, returns an Array of User objects.
236
+ #
237
+ # call-seq:
238
+ # users(host=localhost, local=true)
239
+ # users(host=localhost, local=true){ |user| ... }
240
+ #
241
+ # You may specify a host from which information is retrieved. The
242
+ # default is the local machine. You can retrieve either a global or
243
+ # group, depending on the value of the +local+ argument.
244
+ #
245
+ def self.users(host=Socket.gethostname, local=true)
246
+ host = Socket.gethostname if host.nil?
247
+ cs = "winmgmts:{impersonationLevel=impersonate}!"
248
+ cs << "//#{host}/root/cimv2"
249
+
250
+ begin
251
+ wmi = WIN32OLE.connect(cs)
252
+ rescue WIN32OLERuntimeError => e
253
+ raise AdminError, e
254
+ end
255
+
256
+ query = "select * from win32_useraccount"
257
+ query << " where localaccount = true" if local
258
+ array = []
259
+
260
+ wmi.execquery(query).each{ |user|
261
+ usr = User.new do |u|
262
+ u.account_type = user.accounttype
263
+ u.caption = user.caption
264
+ u.description = user.description
265
+ u.disabled = user.disabled
266
+ u.domain = user.domain
267
+ u.full_name = user.fullname
268
+ u.install_date = user.installdate
269
+ u.local = user.localaccount
270
+ u.lockout = user.lockout
271
+ u.name = user.name
272
+ u.password_changeable = user.passwordchangeable
273
+ u.password_expires = user.passwordexpires
274
+ u.password_required = user.passwordrequired
275
+ u.sid = user.sid
276
+ u.sid_type = user.sidtype
277
+ u.status = user.status
278
+ end
279
+
280
+ if block_given?
281
+ yield usr
282
+ else
283
+ array.push(usr)
284
+ end
285
+ }
286
+ return array unless block_given?
287
+ end
288
+
289
+ # Returns a Group object based on either +name+ or +uid+.
290
+ #
291
+ # call-seq:
292
+ # get_group(name, host=localhost, local=true)
293
+ # get_group(gid, host=localhost, local=true)
294
+ #
295
+ # You may specify a host from which information is retrieved.
296
+ # The default is the local machine. You can retrieve either a global or
297
+ # local group, depending on the value of the +local+ argument.
298
+ #
299
+ def self.get_group(grp, host=Socket.gethostname, local=true)
300
+ host = Socket.gethostname if host.nil?
301
+ cs = "winmgmts:{impersonationLevel=impersonate}!"
302
+ cs << "//#{host}/root/cimv2"
303
+ gid = nil
304
+
305
+ begin
306
+ wmi = WIN32OLE.connect(cs)
307
+ rescue WIN32OLERuntimeError => e
308
+ raise AdminError, e
309
+ end
310
+
311
+ query = "select * from win32_group"
312
+ query << " where localaccount = true" if local
313
+
314
+ if grp.kind_of?(Fixnum)
315
+ if local
316
+ query << " and sid like '%-#{grp}'"
317
+ else
318
+ query << " where sid like '%-#{grp}'"
319
+ end
320
+ else
321
+ if local
322
+ query << " and name = '#{grp}'"
323
+ else
324
+ query << " where name = '#{grp}'"
325
+ end
326
+ end
327
+
328
+ wmi.execquery(query).each{ |group|
329
+ gid = group.sid.split("-").last.to_i
330
+
331
+ # Because our 'like' query isn't fulproof, let's parse
332
+ # the SID again to make sure
333
+ if grp.kind_of?(Fixnum)
334
+ next if grp != gid
335
+ end
336
+
337
+ grp = Group.new do |g|
338
+ g.caption = group.caption
339
+ g.description = group.description
340
+ g.domain = group.domain
341
+ g.gid = gid
342
+ g.install_date = group.installdate
343
+ g.local = group.localaccount
344
+ g.name = group.name
345
+ g.sid = group.sid
346
+ g.sid_type = group.sidtype
347
+ g.status = group.status
348
+ end
349
+ return grp
350
+ }
351
+ # If we're here, it means it wasn't found.
352
+ raise AdminError, "no group found for '#{grp}'"
353
+ end
354
+
355
+ # In block form, yields a Group object for each user on the system. In
356
+ # non-block form, returns an Array of Group objects.
357
+ #
358
+ # call-seq:
359
+ # groups(host=localhost, local=true)
360
+ # groups(host=localhost, local=true){ |group| ... }
361
+ #
362
+ # You may specify a host from which information is retrieved.
363
+ # The default is the local machine. You can retrieve either a global or
364
+ # local group, depending on the value of the +local+ argument.
365
+ #
366
+ def self.groups(host=Socket.gethostname, local=true)
367
+ host = Socket.gethostname if host.nil?
368
+ cs = "winmgmts:{impersonationLevel=impersonate}!"
369
+ cs << "//#{host}/root/cimv2"
370
+
371
+ begin
372
+ wmi = WIN32OLE.connect(cs)
373
+ rescue WIN32OLERuntimeError => e
374
+ raise AdminError, e
375
+ end
376
+
377
+ query = "select * from win32_group"
378
+ query << " where localaccount = true" if local
379
+
380
+ array = []
381
+ wmi.execquery(query).each{ |group|
382
+ grp = Group.new do |g|
383
+ g.caption = group.caption
384
+ g.description = group.description
385
+ g.domain = group.domain
386
+ g.gid = group.sid.split("-").last.to_i
387
+ g.install_date = group.installdate
388
+ g.local = group.localaccount
389
+ g.name = group.name
390
+ g.sid = group.sid
391
+ g.sid_type = group.sidtype
392
+ g.status = group.status
393
+ end
394
+ if block_given?
395
+ yield grp
396
+ else
397
+ array.push(grp)
398
+ end
399
+ }
400
+ return array unless block_given?
401
+ end
402
+ end
403
+ end
@@ -0,0 +1,14 @@
1
+ ###############################################################################
2
+ # tc_admin.rb
3
+ #
4
+ # This exists mostly for the sake of the gemspec, so that it calls the right
5
+ # test suite based on the platform.
6
+ ###############################################################################
7
+ $LOAD_PATH.unshift Dir.pwd
8
+ $LOAD_PATH.unshift Dir.pwd + "/test"
9
+
10
+ if File::ALT_SEPARATOR
11
+ require "tc_win32"
12
+ else
13
+ require "tc_unix"
14
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: sys-admin
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.3.1
7
+ date: 2006-11-19 00:00:00 -07:00
8
+ summary: A unified, cross platform replacement for the 'etc' package.
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/sysutils
13
+ rubyforge_project: sysutils
14
+ description: A unified, cross platform replacement for the 'etc' package.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.2
24
+ version:
25
+ platform: mswin32
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - lib/sys/admin.rb
33
+ - CHANGES
34
+ - README
35
+ - MANIFEST
36
+ test_files:
37
+ - test/tc_admin.rb
38
+ rdoc_options: []
39
+
40
+ extra_rdoc_files:
41
+ - CHANGES
42
+ - README
43
+ - MANIFEST
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ requirements: []
49
+
50
+ dependencies: []
51
+