sys-admin 1.8.1-universal-mingw32 → 1.8.2-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 +4 -4
- checksums.yaml.gz.sig +4 -3
- data/CHANGES.md +7 -0
- data/MANIFEST.md +4 -3
- data/README.md +5 -0
- data/Rakefile +5 -2
- data/lib/bsd/sys/admin.rb +28 -24
- data/lib/darwin/sys/admin.rb +13 -9
- data/lib/linux/sys/admin.rb +18 -14
- data/lib/sunos/sys/admin.rb +16 -12
- data/lib/sys/admin/common.rb +5 -4
- data/lib/sys/admin/custom.rb +6 -3
- data/lib/sys/admin.rb +7 -1
- data/lib/sys-admin.rb +2 -0
- data/lib/unix/sys/admin.rb +6 -6
- data/lib/windows/sys/admin.rb +77 -81
- data/spec/spec_helper.rb +10 -1
- data/spec/sys_admin_shared.rb +15 -0
- data/spec/sys_admin_unix_spec.rb +54 -52
- data/spec/sys_admin_windows_spec.rb +79 -86
- data/sys-admin.gemspec +11 -7
- data.tar.gz.sig +0 -0
- metadata +34 -4
- metadata.gz.sig +0 -0
- data/spec/sys_admin_version_spec.rb +0 -11
data/lib/windows/sys/admin.rb
CHANGED
@@ -4,7 +4,9 @@ require 'win32/security'
|
|
4
4
|
require 'win32/registry'
|
5
5
|
require 'socket'
|
6
6
|
|
7
|
+
# The Sys module serves as a namespace only.
|
7
8
|
module Sys
|
9
|
+
# The Admin class provides a unified, cross platform replacement for the Etc module.
|
8
10
|
class Admin
|
9
11
|
extend FFI::Library
|
10
12
|
|
@@ -13,6 +15,7 @@ module Sys
|
|
13
15
|
#
|
14
16
|
class Error < StandardError; end
|
15
17
|
|
18
|
+
# rubocop:disable Naming/ConstantName
|
16
19
|
SidTypeUser = 1
|
17
20
|
SidTypeGroup = 2
|
18
21
|
SidTypeDomain = 3
|
@@ -22,8 +25,9 @@ module Sys
|
|
22
25
|
SidTypeInvalid = 7
|
23
26
|
SidTypeUnknown = 8
|
24
27
|
SidTypeComputer = 9
|
28
|
+
# rubocop:enable Naming/ConstantName
|
25
29
|
|
26
|
-
HKEY =
|
30
|
+
HKEY = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\'.freeze
|
27
31
|
private_constant :HKEY
|
28
32
|
|
29
33
|
# Retrieves the user's home directory. For local accounts query the
|
@@ -59,10 +63,10 @@ module Sys
|
|
59
63
|
def self.munge_options(opts)
|
60
64
|
rhash = {}
|
61
65
|
|
62
|
-
opts.each
|
66
|
+
opts.each do |k, v|
|
63
67
|
k = k.to_s.downcase.to_sym
|
64
68
|
rhash[k] = v
|
65
|
-
|
69
|
+
end
|
66
70
|
|
67
71
|
rhash
|
68
72
|
end
|
@@ -96,7 +100,7 @@ module Sys
|
|
96
100
|
|
97
101
|
# Used by the get_login method
|
98
102
|
ffi_lib :advapi32
|
99
|
-
attach_function :GetUserNameW, [
|
103
|
+
attach_function :GetUserNameW, %i[pointer pointer], :bool
|
100
104
|
private_class_method :GetUserNameW
|
101
105
|
|
102
106
|
# Creates the given +user+. If no domain option is specified,
|
@@ -146,13 +150,13 @@ module Sys
|
|
146
150
|
adsi = WIN32OLE.connect(moniker)
|
147
151
|
user = adsi.create('user', name)
|
148
152
|
|
149
|
-
options.each
|
153
|
+
options.each do |option, value|
|
150
154
|
if option.to_s == 'password'
|
151
155
|
user.setpassword(value)
|
152
156
|
else
|
153
157
|
user.put(option.to_s, value)
|
154
158
|
end
|
155
|
-
|
159
|
+
end
|
156
160
|
|
157
161
|
user.setinfo
|
158
162
|
rescue WIN32OLERuntimeError => err
|
@@ -199,13 +203,13 @@ module Sys
|
|
199
203
|
begin
|
200
204
|
adsi = WIN32OLE.connect("WinNT://#{domain}/#{name},user")
|
201
205
|
|
202
|
-
options.each
|
206
|
+
options.each do |option, value|
|
203
207
|
if option.to_s == 'password'
|
204
208
|
adsi.setpassword(value)
|
205
209
|
else
|
206
210
|
adsi.put(option.to_s, value)
|
207
211
|
end
|
208
|
-
|
212
|
+
end
|
209
213
|
|
210
214
|
adsi.setinfo
|
211
215
|
rescue WIN32OLERuntimeError => err
|
@@ -277,7 +281,7 @@ module Sys
|
|
277
281
|
# Adds +user+ to +group+ on the specified +domain+, or the localhost
|
278
282
|
# if no domain is specified.
|
279
283
|
#
|
280
|
-
def self.add_group_member(user, group, domain=nil)
|
284
|
+
def self.add_group_member(user, group, domain = nil)
|
281
285
|
domain ||= Socket.gethostname
|
282
286
|
adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
|
283
287
|
adsi.Add("WinNT://#{domain}/#{user}")
|
@@ -288,7 +292,7 @@ module Sys
|
|
288
292
|
# Removes +user+ from +group+ on the specified +domain+, or the localhost
|
289
293
|
# if no domain is specified.
|
290
294
|
#
|
291
|
-
def self.remove_group_member(user, group, domain=nil)
|
295
|
+
def self.remove_group_member(user, group, domain = nil)
|
292
296
|
domain ||= Socket.gethostname
|
293
297
|
adsi = WIN32OLE.connect("WinNT://#{domain}/#{group},group")
|
294
298
|
adsi.Remove("WinNT://#{domain}/#{user}")
|
@@ -397,7 +401,7 @@ module Sys
|
|
397
401
|
options = munge_options(options)
|
398
402
|
|
399
403
|
host = options.delete(:host) || Socket.gethostname
|
400
|
-
cs =
|
404
|
+
cs = 'winmgmts:{impersonationLevel=impersonate}!'
|
401
405
|
cs << "//#{host}/root/cimv2"
|
402
406
|
|
403
407
|
begin
|
@@ -406,20 +410,20 @@ module Sys
|
|
406
410
|
raise Error, err
|
407
411
|
end
|
408
412
|
|
409
|
-
query =
|
413
|
+
query = 'select * from win32_useraccount'
|
410
414
|
|
411
415
|
i = 0
|
412
416
|
|
413
|
-
options.each
|
417
|
+
options.each do |opt, val|
|
414
418
|
if i == 0
|
415
419
|
query << " where #{opt} = '#{val}'"
|
416
420
|
i += 1
|
417
421
|
else
|
418
422
|
query << " and #{opt} = '#{val}'"
|
419
423
|
end
|
420
|
-
|
424
|
+
end
|
421
425
|
|
422
|
-
if usr.
|
426
|
+
if usr.is_a?(Numeric)
|
423
427
|
if i == 0
|
424
428
|
query << " where sid like '%-#{usr}'"
|
425
429
|
else
|
@@ -435,14 +439,12 @@ module Sys
|
|
435
439
|
|
436
440
|
domain = options[:domain] || host
|
437
441
|
|
438
|
-
|
442
|
+
# rubocop:disable Metrics/BlockLength
|
443
|
+
wmi.execquery(query).each do |user|
|
439
444
|
uid = user.sid.split('-').last.to_i
|
440
445
|
|
441
|
-
# Because our 'like' query isn't fulproof, let's parse
|
442
|
-
|
443
|
-
if usr.kind_of?(Numeric)
|
444
|
-
next if usr != uid
|
445
|
-
end
|
446
|
+
# Because our 'like' query isn't fulproof, let's parse the SID again to make sure
|
447
|
+
next if usr.is_a?(Numeric) && usr != uid
|
446
448
|
|
447
449
|
groups, primary_group = *get_groups(domain, user.name)
|
448
450
|
|
@@ -470,7 +472,8 @@ module Sys
|
|
470
472
|
end
|
471
473
|
|
472
474
|
return user_object
|
473
|
-
|
475
|
+
end
|
476
|
+
# rubocop:enable Metrics/BlockLength
|
474
477
|
|
475
478
|
# If we're here, it means it wasn't found.
|
476
479
|
raise Error, "no user found for '#{usr}'"
|
@@ -499,32 +502,32 @@ module Sys
|
|
499
502
|
options = munge_options(options)
|
500
503
|
|
501
504
|
host = options.delete(:host) || Socket.gethostname
|
502
|
-
cs =
|
505
|
+
cs = 'winmgmts:{impersonationLevel=impersonate}!'
|
503
506
|
cs << "//#{host}/root/cimv2"
|
504
507
|
|
505
508
|
begin
|
506
509
|
wmi = WIN32OLE.connect(cs)
|
507
|
-
rescue WIN32OLERuntimeError =>
|
508
|
-
raise Error,
|
510
|
+
rescue WIN32OLERuntimeError => err
|
511
|
+
raise Error, err
|
509
512
|
end
|
510
513
|
|
511
|
-
query =
|
514
|
+
query = 'select * from win32_useraccount'
|
512
515
|
|
513
516
|
i = 0
|
514
517
|
|
515
|
-
options.each
|
518
|
+
options.each do |opt, val|
|
516
519
|
if i == 0
|
517
520
|
query << " where #{opt} = '#{val}'"
|
518
521
|
i += 1
|
519
522
|
else
|
520
523
|
query << " and #{opt} = '#{val}'"
|
521
524
|
end
|
522
|
-
|
525
|
+
end
|
523
526
|
|
524
527
|
array = []
|
525
528
|
domain = options[:domain] || host
|
526
529
|
|
527
|
-
wmi.execquery(query).each
|
530
|
+
wmi.execquery(query).each do |user|
|
528
531
|
uid = user.sid.split('-').last.to_i
|
529
532
|
|
530
533
|
usr = User.new do |u|
|
@@ -550,7 +553,7 @@ module Sys
|
|
550
553
|
end
|
551
554
|
|
552
555
|
array.push(usr)
|
553
|
-
|
556
|
+
end
|
554
557
|
|
555
558
|
array
|
556
559
|
end
|
@@ -586,7 +589,7 @@ module Sys
|
|
586
589
|
options = munge_options(options)
|
587
590
|
|
588
591
|
host = options.delete(:host) || Socket.gethostname
|
589
|
-
cs =
|
592
|
+
cs = 'winmgmts:{impersonationLevel=impersonate}!'
|
590
593
|
cs << "//#{host}/root/cimv2"
|
591
594
|
|
592
595
|
begin
|
@@ -595,20 +598,20 @@ module Sys
|
|
595
598
|
raise Error, err
|
596
599
|
end
|
597
600
|
|
598
|
-
query =
|
601
|
+
query = 'select * from win32_group'
|
599
602
|
|
600
603
|
i = 0
|
601
604
|
|
602
|
-
options.each
|
605
|
+
options.each do |opt, val|
|
603
606
|
if i == 0
|
604
607
|
query << " where #{opt} = '#{val}'"
|
605
608
|
i += 1
|
606
609
|
else
|
607
610
|
query << " and #{opt} = '#{val}'"
|
608
611
|
end
|
609
|
-
|
612
|
+
end
|
610
613
|
|
611
|
-
if grp.
|
614
|
+
if grp.is_a?(Integer)
|
612
615
|
if i == 0
|
613
616
|
query << " where sid like '%-#{grp}'"
|
614
617
|
else
|
@@ -624,14 +627,11 @@ module Sys
|
|
624
627
|
|
625
628
|
domain = options[:domain] || host
|
626
629
|
|
627
|
-
wmi.execquery(query).each
|
628
|
-
gid = group.sid.split(
|
630
|
+
wmi.execquery(query).each do |group|
|
631
|
+
gid = group.sid.split('-').last.to_i
|
629
632
|
|
630
|
-
# Because our 'like' query isn't fulproof, let's parse
|
631
|
-
|
632
|
-
if grp.kind_of?(Integer)
|
633
|
-
next if grp != gid
|
634
|
-
end
|
633
|
+
# Because our 'like' query isn't fulproof, let's parse the SID again to make sure
|
634
|
+
next if grp.is_a?(Integer) && grp != gid
|
635
635
|
|
636
636
|
group_object = Group.new do |g|
|
637
637
|
g.caption = group.caption
|
@@ -648,7 +648,7 @@ module Sys
|
|
648
648
|
end
|
649
649
|
|
650
650
|
return group_object
|
651
|
-
|
651
|
+
end
|
652
652
|
|
653
653
|
# If we're here, it means it wasn't found.
|
654
654
|
raise Error, "no group found for '#{grp}'"
|
@@ -678,7 +678,7 @@ module Sys
|
|
678
678
|
options = munge_options(options)
|
679
679
|
|
680
680
|
host = options.delete(:host) || Socket.gethostname
|
681
|
-
cs =
|
681
|
+
cs = 'winmgmts:{impersonationLevel=impersonate}!'
|
682
682
|
cs << "//#{host}/root/cimv2"
|
683
683
|
|
684
684
|
begin
|
@@ -687,28 +687,28 @@ module Sys
|
|
687
687
|
raise Error, err
|
688
688
|
end
|
689
689
|
|
690
|
-
query =
|
690
|
+
query = 'select * from win32_group'
|
691
691
|
|
692
692
|
i = 0
|
693
693
|
|
694
|
-
options.each
|
694
|
+
options.each do |opt, val|
|
695
695
|
if i == 0
|
696
696
|
query << " where #{opt} = '#{val}'"
|
697
697
|
i += 1
|
698
698
|
else
|
699
699
|
query << " and #{opt} = '#{val}'"
|
700
700
|
end
|
701
|
-
|
701
|
+
end
|
702
702
|
|
703
703
|
array = []
|
704
704
|
domain = options[:domain] || host
|
705
705
|
|
706
|
-
wmi.execquery(query).each
|
706
|
+
wmi.execquery(query).each do |group|
|
707
707
|
grp = Group.new do |g|
|
708
708
|
g.caption = group.caption
|
709
709
|
g.description = group.description
|
710
710
|
g.domain = group.domain
|
711
|
-
g.gid = group.sid.split(
|
711
|
+
g.gid = group.sid.split('-').last.to_i
|
712
712
|
g.install_date = group.installdate
|
713
713
|
g.local = group.localaccount
|
714
714
|
g.name = group.name
|
@@ -719,11 +719,12 @@ module Sys
|
|
719
719
|
end
|
720
720
|
|
721
721
|
array.push(grp)
|
722
|
-
|
722
|
+
end
|
723
723
|
|
724
724
|
array
|
725
725
|
end
|
726
726
|
|
727
|
+
# The User class encapsulates the information typically found within an /etc/passwd entry.
|
727
728
|
class User
|
728
729
|
# An account for users whose primary account is in another domain.
|
729
730
|
TEMP_DUPLICATE = 0x0100
|
@@ -839,9 +840,7 @@ module Sys
|
|
839
840
|
|
840
841
|
# Returns the SID type as a human readable string.
|
841
842
|
#
|
842
|
-
|
843
|
-
@sid_type
|
844
|
-
end
|
843
|
+
attr_reader :sid_type
|
845
844
|
|
846
845
|
# Sets the SID (Security Identifier) type to +stype+, which can be
|
847
846
|
# one of the following constant values:
|
@@ -877,7 +876,7 @@ module Sys
|
|
877
876
|
when Admin::SidTypeComputer
|
878
877
|
@sid_type = 'computer'
|
879
878
|
else
|
880
|
-
@sid_type = '
|
879
|
+
@sid_type = 'not_found'
|
881
880
|
end
|
882
881
|
end
|
883
882
|
|
@@ -918,6 +917,7 @@ module Sys
|
|
918
917
|
end
|
919
918
|
end
|
920
919
|
|
920
|
+
# The Group class encapsulates the information typically found within an /etc/group entry.
|
921
921
|
class Group
|
922
922
|
# Short description of the object.
|
923
923
|
attr_accessor :caption
|
@@ -961,14 +961,12 @@ module Sys
|
|
961
961
|
# Returns whether or not the group is a local group.
|
962
962
|
#
|
963
963
|
def local?
|
964
|
-
|
964
|
+
@local
|
965
965
|
end
|
966
966
|
|
967
967
|
# Returns the type of SID (Security Identifier) as a stringified value.
|
968
968
|
#
|
969
|
-
|
970
|
-
@sid_type
|
971
|
-
end
|
969
|
+
attr_reader :sid_type
|
972
970
|
|
973
971
|
# Sets the SID (Security Identifier) type to +stype+, which can be
|
974
972
|
# one of the following constant values:
|
@@ -984,34 +982,32 @@ module Sys
|
|
984
982
|
# * Admin::SidTypeComputer
|
985
983
|
#
|
986
984
|
def sid_type=(stype)
|
987
|
-
if stype.
|
985
|
+
if stype.is_a?(String)
|
988
986
|
@sid_type = stype.downcase
|
989
987
|
else
|
990
988
|
case stype
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
989
|
+
when Admin::SidTypeUser
|
990
|
+
@sid_type = 'user'
|
991
|
+
when Admin::SidTypeGroup
|
992
|
+
@sid_type = 'group'
|
993
|
+
when Admin::SidTypeDomain
|
994
|
+
@sid_type = 'domain'
|
995
|
+
when Admin::SidTypeAlias
|
996
|
+
@sid_type = 'alias'
|
997
|
+
when Admin::SidTypeWellKnownGroup
|
998
|
+
@sid_type = 'well_known_group'
|
999
|
+
when Admin::SidTypeDeletedAccount
|
1000
|
+
@sid_type = 'deleted_account'
|
1001
|
+
when Admin::SidTypeInvalid
|
1002
|
+
@sid_type = 'invalid'
|
1003
|
+
when Admin::SidTypeUnknown
|
1004
|
+
@sid_type = 'unknown'
|
1005
|
+
when Admin::SidTypeComputer
|
1006
|
+
@sid_type = 'computer'
|
1007
|
+
else
|
1008
|
+
@sid_type = 'not_found'
|
1011
1009
|
end
|
1012
1010
|
end
|
1013
|
-
|
1014
|
-
@sid_type
|
1015
1011
|
end
|
1016
1012
|
end
|
1017
1013
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rspec'
|
2
4
|
require 'sys-admin'
|
5
|
+
require 'sys_admin_shared'
|
3
6
|
|
4
7
|
RSpec.configure do |config|
|
8
|
+
config.include_context(Sys::Admin)
|
5
9
|
config.filter_run_excluding(:darwin) if Gem::Platform.local.os != 'darwin'
|
6
10
|
config.filter_run_excluding(:windows) unless Gem.win_platform?
|
7
11
|
|
8
12
|
if Gem.win_platform?
|
9
|
-
config.filter_run_excluding(:unix)
|
10
13
|
require 'win32-security'
|
11
14
|
require 'socket'
|
15
|
+
|
16
|
+
config.filter_run_excluding(:unix)
|
17
|
+
|
18
|
+
config.before(:each, :requires_elevated => true) do
|
19
|
+
skip 'skipped unless administrator privileges' unless Win32::Security.elevated_security?
|
20
|
+
end
|
12
21
|
end
|
13
22
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_examples Sys::Admin do
|
4
|
+
example 'version is set to expected value' do
|
5
|
+
expect(described_class::VERSION).to eq('1.8.2')
|
6
|
+
end
|
7
|
+
|
8
|
+
example 'version constant is frozen' do
|
9
|
+
expect(described_class::VERSION).to be_frozen
|
10
|
+
end
|
11
|
+
|
12
|
+
example 'constructor is private' do
|
13
|
+
expect{ described_class.new }.to raise_error(NoMethodError)
|
14
|
+
end
|
15
|
+
end
|