Class: Sys::Admin

Inherits:
Object
  • Object
show all
Extended by:
FFI::Library
Defined in:
lib/sys/admin/common.rb,
lib/sys/admin.rb,
lib/bsd/sys/admin.rb,
lib/unix/sys/admin.rb,
lib/linux/sys/admin.rb,
lib/sunos/sys/admin.rb,
lib/darwin/sys/admin.rb,
lib/windows/sys/admin.rb

Overview

The Admin class provides a unified, cross platform replacement for the Etc module.

Defined Under Namespace

Classes: Error, Group, User

Constant Summary collapse

VERSION =

The version of the sys-admin library.

'1.8.0'.freeze
SidTypeUser =
1
SidTypeGroup =
2
SidTypeDomain =
3
SidTypeAlias =
4
SidTypeWellKnownGroup =
5
SidTypeDeletedAccount =
6
SidTypeInvalid =
7
SidTypeUnknown =
8
SidTypeComputer =
9

Class Method Summary collapse

Class Method Details

.add_group(options = {}) ⇒ Object

Create a new group using options. If no domain option is specified then a local group is created instead.

Examples:

# Create a local group with no options
Sys::Admin.add_group(:name => 'Dudes')

# Create a local group with options
Sys::Admin.add_group(:name => 'Dudes', :description => 'Boys')

# Create a group on a specific domain
Sys::Admin.add_group(
   :name        => 'Ladies',
   :domain      => 'XYZ',
   :description => 'Girls'
)


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/windows/sys/admin.rb', line 254

def self.add_group(options = {})
  options = munge_options(options)

  group  = options.delete(:name) or raise ArgumentError, 'No name given'
  domain = options[:domain]

  if domain.nil?
    domain  = Socket.gethostname
    moniker = "WinNT://#{domain},Computer"
  else
    moniker = "WinNT://#{domain}"
  end

  begin
    adsi = WIN32OLE.connect(moniker)
    group = adsi.create('group', group)
    group.setinfo
    configure_group(options) unless options.empty?
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.add_group_member(user, group, domain = nil) ⇒ Object

Adds user to group on the specified domain, or the localhost if no domain is specified.



280
281
282
283
284
285
286
# File 'lib/windows/sys/admin.rb', line 280

def self.add_group_member(user, group, domain=nil)
  domain ||= Socket.gethostname
  adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
  adsi.Add("WinNT://#{domain}/#{user}")
rescue WIN32OLERuntimeError => err
  raise Error, err
end

.add_user(options = {}) ⇒ Object

Creates the given user. If no domain option is specified, then it defaults to your local host, i.e. a local account is created.

Any options provided are treated as IADsUser interface methods and are called before SetInfo is finally called.

Examples:

# Create a local user with no options
Sys::Admin.add_user(:name => 'asmith')

# Create a local user with options
Sys::Admin.add_user(
   :name        => 'asmith',
   :description => 'Really cool guy',
   :password    => 'abc123'
)

# Create a user on a specific domain
Sys::Admin.add_user(
   :name     => 'asmith',
   :domain   => 'XX',
   :fullname => 'Al Smith'
)

– Most options are passed to the ‘put’ method. However, we handle the password specially since it’s a separate method, and some environments require that it be set up front.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/windows/sys/admin.rb', line 132

def self.add_user(options = {})
  options = munge_options(options)

  name   = options.delete(:name) or raise ArgumentError, 'No user given'
  domain = options[:domain]

  if domain.nil?
    domain  = Socket.gethostname
    moniker = "WinNT://#{domain},Computer"
  else
    moniker = "WinNT://#{domain}"
  end

  begin
    adsi = WIN32OLE.connect(moniker)
    user = adsi.create('user', name)

    options.each{ |option, value|
      if option.to_s == 'password'
        user.setpassword(value)
      else
        user.put(option.to_s, value)
      end
    }

    user.setinfo
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.configure_group(options = {}) ⇒ Object

Configures the group using options. If no domain option is specified then your local host is used, i.e. you are configuring a local group.

See tinyurl.com/cjkzl for a list of valid options.

Examples:

# Configure a local group.
Sys::Admin.configure_group(:name => 'Abba', :description => 'Swedish')

# Configure a group on a specific domain.
Sys::Admin.configure_group(
   :name        => 'Web Team',
   :domain      => 'Foo',
   :description => 'Web programming cowboys'
)


317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/windows/sys/admin.rb', line 317

def self.configure_group(options = {})
  options = munge_options(options)

  group  = options.delete(:name) or raise ArgumentError, 'No name given'
  domain = options[:domain] || Socket.gethostname

  begin
    adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
    options.each{ |option, value| adsi.put(option.to_s, value) }
    adsi.setinfo
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.configure_user(options = {}) ⇒ Object

Configures the user using options. If no domain option is specified then your local host is used, i.e. you are configuring a local user account.

See tinyurl.com/3hjv9 for a list of valid options.

In the case of a password change, pass a two element array as the old value and new value.

Examples:

# Configure a local user
Sys::Admin.configure_user(
   :name        => 'djberge',
   :description => 'Awesome'
)

# Change the password
Sys::Admin.configure_user(
   :name     => 'asmith',
   :password => 'new_password'
)

# Configure a user on a specific domain
Sys::Admin.configure_user(
   :name      => 'jsmrz',
   :domain    => 'XX',
   :firstname => 'Jo'
)


193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/windows/sys/admin.rb', line 193

def self.configure_user(options = {})
  options = munge_options(options)

  name   = options.delete(:name) or raise ArgumentError, 'No name given'
  domain = options[:domain] || Socket.gethostname

  begin
    adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")

    options.each{ |option, value|
      if option.to_s == 'password'
        adsi.setpassword(value)
      else
        adsi.put(option.to_s, value)
      end
    }

    adsi.setinfo
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.delete_group(group, domain = nil) ⇒ Object

Delete the group from domain. If no domain is specified, then you are deleting a local group.



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/windows/sys/admin.rb', line 335

def self.delete_group(group, domain = nil)
  if domain.nil?
    domain = Socket.gethostname
    moniker = "WinNT://#{domain},Computer"
  else
    moniker = "WinNT://#{domain}"
  end

  begin
    adsi = WIN32OLE.connect(moniker)
    adsi.delete('group', group)
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.delete_user(user, domain = nil) ⇒ Object

Deletes the given user on domain. If no domain is specified, then it defaults to your local host, i.e. a local account is deleted.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/windows/sys/admin.rb', line 220

def self.delete_user(user, domain = nil)
  if domain.nil?
    domain  = Socket.gethostname
    moniker = "WinNT://#{domain},Computer"
  else
    moniker = "WinNT://#{domain}"
  end

  begin
    adsi = WIN32OLE.connect(moniker)
    adsi.delete('user', user)
  rescue WIN32OLERuntimeError => err
    raise Error, err
  end
end

.get_group(grp, options = {}) ⇒ Object

Returns a Group object based on either name or gid.

call-seq:

Sys::Admin.get_group(name, options = {})
Sys::Admin.get_group(gid, options = {})

If a numeric value is sent as the first parameter, it is treated as a RID and is checked against the SID for a match.

You may specify a host as an option from which information is retrieved. The default is the local host.

All other options are passed as WQL parameters to the Win32_Group WMI object. See tinyurl.com/bngc8s for a list of possible options.

Examples:

# Find a group by name
Sys::Admin.get_group('Web Team')

# Find a group by id
Sys::Admin.get_group(31667)

# Find a group on a specific domain
Sys::Admin.get_group('Web Team', :domain => 'FOO')

Raises:



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/bsd/sys/admin.rb', line 127

def self.get_group(gid)
  size = 1024
  buf  = FFI::MemoryPointer.new(:char, size)
  pbuf = FFI::MemoryPointer.new(PasswdStruct)
  temp = GroupStruct.new

  begin
    if gid.is_a?(String)
      val = getgrnam_r(gid, temp, buf, buf.size, pbuf)
      fun = 'getgrnam_r'
    else
      val = getgrgid_r(gid, temp, buf, buf.size, pbuf)
      fun = 'getgrgid_r'
    end
    raise SystemCallError.new(fun, val) if val != 0
  rescue Errno::ERANGE
    size += 1024
    raise if size > BUF_MAX
    buf = FFI::MemoryPointer.new(:char, size)
    retry
  end

  ptr = pbuf.read_pointer

  if ptr.null?
    raise Error, "no group found for '#{gid}'"
  end

  grp = GroupStruct.new(ptr)
  get_group_from_struct(grp)
end

.get_loginObject

Returns the user name (only) of the current login.



76
77
78
79
80
81
82
83
84
# File 'lib/bsd/sys/admin.rb', line 76

def self.
  buf = FFI::MemoryPointer.new(:char, 256)

  if getlogin_r(buf, buf.size) != 0
    raise Error, "getlogin_r function failed: " + strerror(FFI.errno)
  end

  buf.read_string
end

.get_user(usr, options = {}) ⇒ Object

Returns a User object based on either name or uid.

call-seq:

Sys::Admin.get_user(name, options = {})
Sys::Admin.get_user(uid, options = {})

Looks for usr information based on the options you specify, where the usr argument can be either a user name or a RID.

If a ‘host’ option is specified, information is retrieved from that host. Otherwise, the local host is used.

All other options are converted to WQL statements against the Win32_UserAccount WMI object. See tinyurl.com/by9nvn for a list of possible options.

Examples:

# Get a user by name
Admin.get_user('djberge')

# Get a user by uid
Admin.get_user(100)

# Get a user on a specific domain
Admin.get_user('djberge', :domain => 'TEST')

– The reason for keeping the usr variable as a separate argument instead of rolling it into the options hash was to keep a unified API between the Windows and UNIX versions.

Raises:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/bsd/sys/admin.rb', line 94

def self.get_user(uid)
  buf  = FFI::MemoryPointer.new(:char, 1024)
  pbuf = FFI::MemoryPointer.new(PasswdStruct)
  temp = PasswdStruct.new

  if uid.is_a?(String)
    if getpwnam_r(uid, temp, buf, buf.size, pbuf) != 0
      raise Error, "getpwnam_r function failed: " + strerror(FFI.errno)
    end
  else
    if getpwuid_r(uid, temp, buf, buf.size, pbuf) != 0
      raise Error, "getpwuid_r function failed: " + strerror(FFI.errno)
    end
  end

  ptr = pbuf.read_pointer

  if ptr.null?
    raise Error, "no user found for #{uid}"
  end

  pwd = PasswdStruct.new(ptr)
  get_user_from_struct(pwd)
end

.groups(options = {}) ⇒ Object

Returns an array of Group objects for each user on the system.

You may specify a host option from which information is retrieved. The default is the local host.

All other options are passed as WQL parameters to the Win32_Group WMI object. See tinyurl.com/bngc8s for a list of possible options.

Examples:

# Get local group information
Sys::Admin.groups(:localaccount => true)

# Get all groups on a specific domain
Sys::Admin.groups(:domain => 'FOO')

# Get a specific group on a domain
Sys::Admin.groups(:name => 'Some Group', :domain => 'FOO')


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/bsd/sys/admin.rb', line 180

def self.groups
  groups = []

  begin
    setgrent()

    until (ptr = getgrent()).null?
      grp = GroupStruct.new(ptr)
      groups << get_group_from_struct(grp)
    end
  ensure
    endgrent()
  end

  groups
end

.remove_group_member(user, group, domain = nil) ⇒ Object

Removes user from group on the specified domain, or the localhost if no domain is specified.



291
292
293
294
295
296
297
# File 'lib/windows/sys/admin.rb', line 291

def self.remove_group_member(user, group, domain=nil)
  domain ||= Socket.gethostname
  adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
  adsi.Remove("WinNT://#{domain}/#{user}")
rescue WIN32OLERuntimeError => err
  raise Error, err
end

.users(options = {}) ⇒ Object

Returns an array of User objects for each user on the system.

You may specify a host from which information is retrieved. The default is the local host.

All other arguments are passed as WQL query parameters against the Win32_UserAccont WMI object.

Examples:

# Get all local account users
Sys::Admin.users(:localaccount => true)

# Get all user accounts on a specific domain
Sys::Admin.users(:domain => 'FOO')

# Get a single user from a domain
Sys::Admin.users(:name => 'djberge', :domain => 'FOO')


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/bsd/sys/admin.rb', line 161

def self.users
  users = []

  begin
    setpwent()

    until (ptr = getpwent()).null?
      pwd = PasswdStruct.new(ptr)
      users << get_user_from_struct(pwd)
    end
  ensure
    endpwent()
  end

  users
end