Class: Sys::Admin

Inherits:
Object
  • Object
show all
Extended by:
FFI::Library
Defined in:
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/sys/admin/common.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.9.0'
SidTypeUser =

rubocop:disable Naming/ConstantName

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'
)


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/windows/sys/admin.rb', line 258

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.



284
285
286
287
288
289
290
# File 'lib/windows/sys/admin.rb', line 284

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.



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
162
163
164
165
# File 'lib/windows/sys/admin.rb', line 136

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 do |option, value|
      if option.to_s == 'password'
        user.setpassword(value)
      else
        user.put(option.to_s, value)
      end
    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'
)


321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/windows/sys/admin.rb', line 321

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'
)


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/windows/sys/admin.rb', line 197

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 do |option, value|
      if option.to_s == 'password'
        adsi.setpassword(value)
      else
        adsi.put(option.to_s, value)
      end
    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.



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/windows/sys/admin.rb', line 339

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.



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/windows/sys/admin.rb', line 224

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:



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
158
159
160
161
162
163
164
165
# File 'lib/bsd/sys/admin.rb', line 131

def self.get_group(gid)
  size = 1024
  buf  = FFI::MemoryPointer.new(:char, size)
  pbuf = FFI::MemoryPointer.new(GroupStruct)
  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

    if pbuf.null?
      raise SystemCallError.new(fun, val) if val != 0
      raise Error, "group '#{gid}' not found"
    end
  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.



80
81
82
83
84
85
86
87
88
# File 'lib/bsd/sys/admin.rb', line 80

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:



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

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')


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/bsd/sys/admin.rb', line 188

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.



295
296
297
298
299
300
301
# File 'lib/windows/sys/admin.rb', line 295

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')


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/bsd/sys/admin.rb', line 169

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