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, GroupStruct, LastlogStruct, LastlogxStruct, PasswdStruct, User

Constant Summary collapse

VERSION =

The version of the sys-admin library.

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


540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/windows/sys/admin.rb', line 540

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.



566
567
568
569
570
571
572
# File 'lib/windows/sys/admin.rb', line 566

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.



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/windows/sys/admin.rb', line 418

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


603
604
605
606
607
608
609
610
611
612
613
614
615
616
# File 'lib/windows/sys/admin.rb', line 603

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


479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/windows/sys/admin.rb', line 479

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.



621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/windows/sys/admin.rb', line 621

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.



506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
# File 'lib/windows/sys/admin.rb', line 506

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:



125
126
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
# File 'lib/bsd/sys/admin.rb', line 125

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.



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

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:



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

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


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

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.



577
578
579
580
581
582
583
# File 'lib/windows/sys/admin.rb', line 577

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


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

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