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