win32-security 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +7 -1
- data/lib/win32/security.rb +1 -1
- data/lib/win32/security/sid.rb +311 -0
- data/test/test_acl.rb +67 -0
- data/test/test_security.rb +1 -1
- data/test/test_sid.rb +11 -1
- data/win32-security.gemspec +25 -26
- metadata +14 -11
data/CHANGES
CHANGED
@@ -1,2 +1,8 @@
|
|
1
|
+
= 0.1.1 - 14-Jul-2009
|
2
|
+
* Added some well known SID's as constants to the Win32::Security::SID class
|
3
|
+
for convenience, e.g. SID::World, SID::Everyone.
|
4
|
+
* Fixes for the gemspec.
|
5
|
+
* Changed license to Artistic 2.0.
|
6
|
+
|
1
7
|
= 0.1.0 - 17-Dec-2008
|
2
|
-
* Initial release
|
8
|
+
* Initial release
|
data/lib/win32/security.rb
CHANGED
@@ -0,0 +1,311 @@
|
|
1
|
+
require 'windows/security'
|
2
|
+
require 'windows/error'
|
3
|
+
require 'windows/msvcrt/string'
|
4
|
+
require 'windows/msvcrt/buffer'
|
5
|
+
require 'socket'
|
6
|
+
|
7
|
+
# The Win32 module serves as a namespace only.
|
8
|
+
module Win32
|
9
|
+
|
10
|
+
# The Security class serves as a toplevel class namespace.
|
11
|
+
class Security
|
12
|
+
|
13
|
+
# The SID class encapsulates a Security Identifier.
|
14
|
+
class SID
|
15
|
+
include Windows::Security
|
16
|
+
include Windows::Error
|
17
|
+
include Windows::MSVCRT::String
|
18
|
+
|
19
|
+
extend Windows::Security
|
20
|
+
extend Windows::Error
|
21
|
+
extend Windows::MSVCRT::String
|
22
|
+
extend Windows::MSVCRT::Buffer
|
23
|
+
|
24
|
+
# Error class typically raised if any of the SID methods fail
|
25
|
+
class Error < StandardError; end
|
26
|
+
|
27
|
+
# The version of the Win32::Security::SID class.
|
28
|
+
VERSION = '0.1.1'
|
29
|
+
|
30
|
+
# Some constant SID's for your convenience, in string format.
|
31
|
+
# See http://support.microsoft.com/kb/243330 for details.
|
32
|
+
|
33
|
+
Null = 'S-1-0'
|
34
|
+
Nobody = 'S-1-0-0'
|
35
|
+
World = 'S-1-1'
|
36
|
+
Everyone = 'S-1-1-0'
|
37
|
+
Local = 'S-1-2'
|
38
|
+
Creator = 'S-1-3'
|
39
|
+
CreatorOwner = 'S-1-3-0'
|
40
|
+
CreatorGroup = 'S-1-3-1'
|
41
|
+
CreatorOwnerServer = 'S-1-3-2'
|
42
|
+
CreatorGroupServer = 'S-1-3-3'
|
43
|
+
NonUnique = 'S-1-4'
|
44
|
+
Nt = 'S-1-5'
|
45
|
+
Dialup = 'S-1-5-1'
|
46
|
+
Network = 'S-1-5-2'
|
47
|
+
Batch = 'S-1-5-3'
|
48
|
+
Interactive = 'S-1-5-4'
|
49
|
+
Service = 'S-1-5-6'
|
50
|
+
Anonymous = 'S-1-5-7'
|
51
|
+
Proxy = 'S-1-5-8'
|
52
|
+
EnterpriseDomainControllers = 'S-1-5-9'
|
53
|
+
PrincipalSelf = 'S-1-5-10'
|
54
|
+
AuthenticatedUsers = 'S-1-5-11'
|
55
|
+
RestrictedCode = 'S-1-5-12'
|
56
|
+
TerminalServerUsers = 'S-1-5-13'
|
57
|
+
LocalSystem = 'S-1-5-18'
|
58
|
+
NtLocal = 'S-1-5-19'
|
59
|
+
NtNetwork = 'S-1-5-20'
|
60
|
+
BuiltinAdministrators = 'S-1-5-32-544'
|
61
|
+
BuiltinUsers = 'S-1-5-32-545'
|
62
|
+
Guests = 'S-1-5-32-546'
|
63
|
+
PowerUsers = 'S-1-5-32-547'
|
64
|
+
AccountOperators = 'S-1-5-32-548'
|
65
|
+
ServerOperators = 'S-1-5-32-549'
|
66
|
+
PrintOperators = 'S-1-5-32-550'
|
67
|
+
BackupOperators = 'S-1-5-32-551'
|
68
|
+
Replicators = 'S-1-5-32-552'
|
69
|
+
|
70
|
+
# The binary SID object itself.
|
71
|
+
attr_reader :sid
|
72
|
+
|
73
|
+
# The account name passed to the constructor.
|
74
|
+
attr_reader :account
|
75
|
+
|
76
|
+
# The SID account type, e.g. 'user, 'group', etc.
|
77
|
+
attr_reader :account_type
|
78
|
+
|
79
|
+
# The domain the SID is on.
|
80
|
+
attr_reader :domain
|
81
|
+
|
82
|
+
# The host passed to the constructor, or the localhost if none
|
83
|
+
# was specified.
|
84
|
+
attr_reader :host
|
85
|
+
|
86
|
+
# Converts a binary SID to a string in S-R-I-S-S... format.
|
87
|
+
#
|
88
|
+
def self.sid_to_string(sid)
|
89
|
+
sid_addr = [sid].pack('p*').unpack('L')[0]
|
90
|
+
sid_buf = 0.chr * 80
|
91
|
+
sid_ptr = 0.chr * 4
|
92
|
+
|
93
|
+
unless ConvertSidToStringSid(sid_addr, sid_ptr)
|
94
|
+
raise Error, get_last_error
|
95
|
+
end
|
96
|
+
|
97
|
+
strcpy(sid_buf, sid_ptr.unpack('L')[0])
|
98
|
+
sid_buf.strip
|
99
|
+
end
|
100
|
+
|
101
|
+
# Converts a string in S-R-I-S-S... format back to a binary SID.
|
102
|
+
#
|
103
|
+
def self.string_to_sid(string)
|
104
|
+
sid_buf = 0.chr * 80
|
105
|
+
string_addr = [string].pack('p*').unpack('L')[0]
|
106
|
+
|
107
|
+
unless ConvertStringSidToSid(string_addr, sid_buf)
|
108
|
+
raise Error, get_last_error
|
109
|
+
end
|
110
|
+
|
111
|
+
sid_buf.strip
|
112
|
+
end
|
113
|
+
|
114
|
+
# Creates a new SID with +authority+ and up to 8 +subauthorities+,
|
115
|
+
# and returns new Win32::Security::SID object.
|
116
|
+
#
|
117
|
+
# Example:
|
118
|
+
#
|
119
|
+
# sec = Security::SID.create(
|
120
|
+
# Security::SID::SECURITY_WORLD_SID_AUTHORITY,
|
121
|
+
# Security::SID::SECURITY_WORLD_RID
|
122
|
+
# )
|
123
|
+
#
|
124
|
+
# p sec
|
125
|
+
#
|
126
|
+
# #<Win32::Security::SID:0x2c5a95c
|
127
|
+
# @host="your_host",
|
128
|
+
# @account="Everyone",
|
129
|
+
# @account_type="well known group",
|
130
|
+
# @sid="\001\001\000\000\000\000\000\001\000\000\000\000",
|
131
|
+
# @domain=""
|
132
|
+
# >
|
133
|
+
#
|
134
|
+
def self.create(authority, *sub_authorities)
|
135
|
+
if sub_authorities.length > 8
|
136
|
+
raise ArgumentError, "maximum of 8 subauthorities allowed"
|
137
|
+
end
|
138
|
+
|
139
|
+
sid = 0.chr * GetSidLengthRequired(sub_authorities.length)
|
140
|
+
|
141
|
+
auth = 0.chr * 5 + authority.chr
|
142
|
+
|
143
|
+
unless InitializeSid(sid, auth, sub_authorities.length)
|
144
|
+
raise Error, get_last_error
|
145
|
+
end
|
146
|
+
|
147
|
+
sub_authorities.each_index do |i|
|
148
|
+
value = [sub_authorities[i]].pack('L')
|
149
|
+
auth_ptr = GetSidSubAuthority(sid, i)
|
150
|
+
memcpy(auth_ptr, value, 4)
|
151
|
+
end
|
152
|
+
|
153
|
+
new(sid)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Creates and returns a new Win32::Security::SID object, based on
|
157
|
+
# the account name, which may also be a binary SID. If a host is
|
158
|
+
# provided, then the information is retrieved from that host.
|
159
|
+
# Otherwise, the local host is used.
|
160
|
+
#
|
161
|
+
# Note that this does NOT create a new SID, but merely retrieves
|
162
|
+
# information for an existing SID. To create a new SID, use the
|
163
|
+
# SID.create method.
|
164
|
+
#
|
165
|
+
# Examples:
|
166
|
+
#
|
167
|
+
# # User 'john' on the localhost
|
168
|
+
# Win32::Security::SID.new('john')
|
169
|
+
#
|
170
|
+
# # User 'jane' on a remote machine
|
171
|
+
# Win32::Security::SID.new('jane', 'some_host')
|
172
|
+
#
|
173
|
+
# # Binary SID
|
174
|
+
# Win32::Security::SID.new("\001\000\000\000\000\000\001\000\000\000\000")
|
175
|
+
#
|
176
|
+
def initialize(account, host=Socket.gethostname)
|
177
|
+
bool = false
|
178
|
+
sid = 0.chr * 28
|
179
|
+
sid_cb = [sid.size].pack('L')
|
180
|
+
|
181
|
+
domain_buf = 0.chr * 80
|
182
|
+
domain_cch = [domain_buf.size].pack('L')
|
183
|
+
|
184
|
+
sid_name_use = 0.chr * 4
|
185
|
+
|
186
|
+
# If characters in the 0-10 range, assume it's a binary SID.
|
187
|
+
if account[0] < 10
|
188
|
+
bool = LookupAccountSid(
|
189
|
+
host,
|
190
|
+
[account].pack('p*').unpack('L')[0],
|
191
|
+
sid,
|
192
|
+
sid_cb,
|
193
|
+
domain_buf,
|
194
|
+
domain_cch,
|
195
|
+
sid_name_use
|
196
|
+
)
|
197
|
+
else
|
198
|
+
bool = LookupAccountName(
|
199
|
+
host,
|
200
|
+
account,
|
201
|
+
sid,
|
202
|
+
sid_cb,
|
203
|
+
domain_buf,
|
204
|
+
domain_cch,
|
205
|
+
sid_name_use
|
206
|
+
)
|
207
|
+
end
|
208
|
+
|
209
|
+
unless bool
|
210
|
+
raise Error, get_last_error
|
211
|
+
end
|
212
|
+
|
213
|
+
# The arguments are flipped if the account argument is binary
|
214
|
+
if account[0] < 10
|
215
|
+
@sid = account
|
216
|
+
@account = sid.strip
|
217
|
+
else
|
218
|
+
@sid = sid.strip
|
219
|
+
@account = account
|
220
|
+
end
|
221
|
+
|
222
|
+
@host = host
|
223
|
+
@domain = domain_buf.strip
|
224
|
+
|
225
|
+
@account_type = get_account_type(sid_name_use.unpack('L')[0])
|
226
|
+
end
|
227
|
+
|
228
|
+
# Synonym for SID.new.
|
229
|
+
#
|
230
|
+
def self.open(account, host=Socket.gethostname)
|
231
|
+
new(account, host)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Returns the binary SID in string format suitable for display,
|
235
|
+
# storage or transmission.
|
236
|
+
#
|
237
|
+
def to_s
|
238
|
+
sid_addr = [@sid].pack('p*').unpack('L').first
|
239
|
+
sid_buf = 0.chr * 80
|
240
|
+
sid_ptr = 0.chr * 4
|
241
|
+
|
242
|
+
unless ConvertSidToStringSid(sid_addr, sid_ptr)
|
243
|
+
raise Error, get_last_error
|
244
|
+
end
|
245
|
+
|
246
|
+
strcpy(sid_buf, sid_ptr.unpack('L').first)
|
247
|
+
sid_buf.strip
|
248
|
+
end
|
249
|
+
|
250
|
+
alias to_str to_s
|
251
|
+
|
252
|
+
# Returns whether or not the SID object is equal to +other+.
|
253
|
+
#
|
254
|
+
def ==(other)
|
255
|
+
EqualSid(@sid, other.sid)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Returns whether or not the SID is a valid sid.
|
259
|
+
#
|
260
|
+
def valid?
|
261
|
+
IsValidSid(@sid)
|
262
|
+
end
|
263
|
+
|
264
|
+
# Returns whether or not the SID is a well known SID.
|
265
|
+
#
|
266
|
+
# Requires Windows XP or later. Earlier versions will raise a
|
267
|
+
# NoMethodError.
|
268
|
+
#
|
269
|
+
def well_known?
|
270
|
+
if defined? IsWellKnownSid
|
271
|
+
IsWellKnownSid(@sid)
|
272
|
+
else
|
273
|
+
raise NoMethodError, 'requires Windows XP or later'
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# Returns the length of the SID object, in bytes.
|
278
|
+
#
|
279
|
+
def length
|
280
|
+
GetLengthSid(@sid)
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
# Converts a numeric account type into a human readable string.
|
286
|
+
#
|
287
|
+
def get_account_type(value)
|
288
|
+
case value
|
289
|
+
when SidTypeUser
|
290
|
+
'user'
|
291
|
+
when SidTypeGroup
|
292
|
+
'group'
|
293
|
+
when SidTypeDomain
|
294
|
+
'domain'
|
295
|
+
when SidTypeAlias
|
296
|
+
'alias'
|
297
|
+
when SidTypeWellKnownGroup
|
298
|
+
'well known group'
|
299
|
+
when SidTypeDeletedAccount
|
300
|
+
'deleted account'
|
301
|
+
when SidTypeInvalid
|
302
|
+
'invalid'
|
303
|
+
when SidTypeUnknown
|
304
|
+
'unknown'
|
305
|
+
when SidComputer
|
306
|
+
'computer'
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
data/test/test_acl.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
########################################################################
|
2
|
+
# test_acl.rb
|
3
|
+
#
|
4
|
+
# Test suite for the Win32::Security::ACL class. You should run these
|
5
|
+
# tests via the 'rake test' task.
|
6
|
+
########################################################################
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'test-unit'
|
9
|
+
|
10
|
+
require 'win32/security'
|
11
|
+
require 'test/unit'
|
12
|
+
|
13
|
+
class TC_Win32_Security_Acl < Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@acl = Security::ACL.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_version
|
19
|
+
assert_equal('0.1.0', Security::ACL::VERSION)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_ace_count
|
23
|
+
assert_respond_to(@acl, :ace_count)
|
24
|
+
assert_kind_of(Fixnum, @acl.ace_count)
|
25
|
+
assert_equal(0, @acl.ace_count)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_acl
|
29
|
+
assert_respond_to(@acl, :acl)
|
30
|
+
assert_kind_of(String, @acl.acl)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_add_access_allowed_ace
|
34
|
+
assert_respond_to(@acl, :add_access_allowed_ace)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_add_access_denied_ace
|
38
|
+
assert_respond_to(@acl, :add_access_denied_ace)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_add_ace
|
42
|
+
assert_respond_to(@acl, :add_ace)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_delete_ace
|
46
|
+
assert_respond_to(@acl, :delete_ace)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_find_ace
|
50
|
+
assert_respond_to(@acl, :find_ace)
|
51
|
+
assert_kind_of(Fixnum, @acl.find_ace)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_revision
|
55
|
+
assert_respond_to(@acl, :revision)
|
56
|
+
assert_kind_of(Fixnum, @acl.revision)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_is_valid
|
60
|
+
assert_respond_to(@acl, :valid?)
|
61
|
+
assert_equal(true, @acl.valid?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown
|
65
|
+
@acl = nil
|
66
|
+
end
|
67
|
+
end
|
data/test/test_security.rb
CHANGED
data/test/test_sid.rb
CHANGED
@@ -23,7 +23,7 @@ class TC_Win32_Security_Sid < Test::Unit::TestCase
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def test_version
|
26
|
-
assert_equal('0.1.
|
26
|
+
assert_equal('0.1.1', Security::SID::VERSION)
|
27
27
|
end
|
28
28
|
|
29
29
|
def test_sid
|
@@ -108,6 +108,16 @@ class TC_Win32_Security_Sid < Test::Unit::TestCase
|
|
108
108
|
assert_raise(Security::SID::Error){ Security::SID.new('bogus') }
|
109
109
|
end
|
110
110
|
|
111
|
+
def test_well_known_sid_constants
|
112
|
+
assert_equal('S-1-0', Security::SID::Null)
|
113
|
+
assert_equal('S-1-0-0', Security::SID::Nobody)
|
114
|
+
assert_equal('S-1-1', Security::SID::World)
|
115
|
+
assert_equal('S-1-1-0', Security::SID::Everyone)
|
116
|
+
assert_equal('S-1-5-32-544', Security::SID::BuiltinAdministrators)
|
117
|
+
assert_equal('S-1-5-32-545', Security::SID::BuiltinUsers)
|
118
|
+
assert_equal('S-1-5-32-546', Security::SID::Guests)
|
119
|
+
end
|
120
|
+
|
111
121
|
def teardown
|
112
122
|
@sid = nil
|
113
123
|
end
|
data/win32-security.gemspec
CHANGED
@@ -1,32 +1,31 @@
|
|
1
|
-
require
|
1
|
+
require 'rubygems'
|
2
2
|
|
3
3
|
spec = Gem::Specification.new do |gem|
|
4
|
-
gem.name
|
5
|
-
gem.version
|
6
|
-
gem.authors
|
7
|
-
gem.email
|
8
|
-
gem.homepage
|
9
|
-
gem.platform
|
10
|
-
gem.summary
|
11
|
-
gem.test_files
|
12
|
-
gem.has_rdoc
|
13
|
-
gem.files
|
14
|
-
gem.
|
15
|
-
|
16
|
-
gem.
|
17
|
-
|
18
|
-
gem.add_dependency("windows-pr", ">= 0.9.8")
|
19
|
-
gem.add_dependency("test-unit", ">= 2.0.1")
|
20
|
-
gem.add_dependency("sys-admin", ">= 1.4.4")
|
4
|
+
gem.name = 'win32-security'
|
5
|
+
gem.version = '0.1.1'
|
6
|
+
gem.authors = ['Daniel J. Berger', 'Park Heesob']
|
7
|
+
gem.email = 'djberg96@gmail.com'
|
8
|
+
gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
|
9
|
+
gem.platform = Gem::Platform::RUBY
|
10
|
+
gem.summary = 'A library for dealing with aspects of Windows security.'
|
11
|
+
gem.test_files = Dir['test/*.rb']
|
12
|
+
gem.has_rdoc = true
|
13
|
+
gem.files = Dir['**/*'].reject{ |f| f.include?('CVS') }
|
14
|
+
gem.license = 'Artistic 2.0'
|
15
|
+
|
16
|
+
gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
17
|
+
gem.rubyforge_project = 'win32utils'
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
gem.add_dependency('windows-pr', '>= 0.9.8')
|
20
|
+
gem.add_dependency('test-unit', '>= 2.0.1')
|
21
|
+
gem.add_dependency('sys-admin', '>= 1.4.4')
|
25
22
|
|
26
|
-
gem.description =
|
23
|
+
gem.description = <<-EOF
|
24
|
+
The win32-security library provides an interface for dealing with
|
25
|
+
security related aspects of MS Windows. At the moment it provides an
|
26
|
+
interface for inspecting or creating SID's.
|
27
|
+
EOF
|
27
28
|
end
|
28
29
|
|
29
|
-
if
|
30
|
-
|
31
|
-
Gem::Builder.new(spec).build
|
32
|
-
end
|
30
|
+
Gem.manage_gems if Gem::RubyGemsVersion.to_f < 1.0
|
31
|
+
Gem::Builder.new(spec).build
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-security
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel J. Berger
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2009-07-14 00:00:00 -06:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
- !ruby/object:Gem::Version
|
44
44
|
version: 1.4.4
|
45
45
|
version:
|
46
|
-
description: The win32-security library provides an interface for dealing with
|
46
|
+
description: " The win32-security library provides an interface for dealing with\n security related aspects of MS Windows. At the moment it provides an\n interface for inspecting or creating SID's.\n"
|
47
47
|
email: djberg96@gmail.com
|
48
48
|
executables: []
|
49
49
|
|
@@ -54,18 +54,20 @@ extra_rdoc_files:
|
|
54
54
|
- CHANGES
|
55
55
|
- MANIFEST
|
56
56
|
files:
|
57
|
-
- lib/win32/security.rb
|
58
|
-
- test/test_security.rb
|
59
|
-
- test/test_sid.rb
|
60
57
|
- CHANGES
|
61
|
-
- lib
|
58
|
+
- lib/win32/security/sid.rb
|
59
|
+
- lib/win32/security.rb
|
62
60
|
- MANIFEST
|
63
61
|
- Rakefile
|
64
62
|
- README
|
65
|
-
- test
|
63
|
+
- test/test_acl.rb
|
64
|
+
- test/test_security.rb
|
65
|
+
- test/test_sid.rb
|
66
66
|
- win32-security.gemspec
|
67
67
|
has_rdoc: true
|
68
68
|
homepage: http://www.rubyforge.org/projects/win32utils
|
69
|
+
licenses:
|
70
|
+
- Artistic 2.0
|
69
71
|
post_install_message:
|
70
72
|
rdoc_options: []
|
71
73
|
|
@@ -85,11 +87,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
87
|
version:
|
86
88
|
requirements: []
|
87
89
|
|
88
|
-
rubyforge_project:
|
89
|
-
rubygems_version: 1.3.
|
90
|
+
rubyforge_project: win32utils
|
91
|
+
rubygems_version: 1.3.4
|
90
92
|
signing_key:
|
91
|
-
specification_version:
|
93
|
+
specification_version: 3
|
92
94
|
summary: A library for dealing with aspects of Windows security.
|
93
95
|
test_files:
|
96
|
+
- test/test_acl.rb
|
94
97
|
- test/test_security.rb
|
95
98
|
- test/test_sid.rb
|