mirror of
https://github.com/inspec/inspec
synced 2024-11-10 15:14:23 +00:00
Extend Windows ACL matchers (#1744)
* Adds alias for 'ListDirectory' permission Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Works with Ruby array of permissions as long as possible Converts to PowerShell array just before use. Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Abstracts user-provided permissions to router method Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Adds FullControl as a specifiable permission Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Adds specific permission 'modify' Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Fixes #1743 Limits Windows' broad "read" permission to if it can read all of the above, instead of just the first: - File contents - File attributes - File extended attributes - File permissions This better aligns with how Windows names the permissions. 'read' -> Read instead of 'read' -> ReadData Signed-off-by: David Alexander <opensource@thelonelyghost.com> * 'Execute' Windows ACL has alias of 'Traverse' Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Adds 'Delete' permission Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Adds `should allow('perm').by_user('me')` matcher Provides hooks for later use with Windows ACL matching Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Adds remaining Windows ACL hooks Skips ReadAndExecute on intentionally since it just aliases the combo of 2 permissions into one new one. Signed-off-by: David Alexander <opensource@thelonelyghost.com> * [Rubocop] Reduces ABC / Cyclomatic complexity Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Reduces global scope with `allows()` -> `be_allowed()` RSpec inferred matchers work nicely here. This changes the `by_user()` and `by()` chained matchers to just be an options hash on the underlying `allowed?()` method. Signed-off-by: David Alexander <opensource@thelonelyghost.com> * Fixes integration tests with rename `allows()` -> `be_allowed()` Signed-off-by: David Alexander <opensource@thelonelyghost.com>
This commit is contained in:
parent
e5ce31fcc7
commit
6ed4068fd1
3 changed files with 129 additions and 10 deletions
|
@ -84,6 +84,13 @@ module Inspec::Resources
|
|||
file_permission_granted?('execute', by_usergroup, by_specific_user)
|
||||
end
|
||||
|
||||
def allowed?(permission, opts = {})
|
||||
return false unless exist?
|
||||
return skip_resource '`allowed?` is not supported on your OS yet.' if @perms_provider.nil?
|
||||
|
||||
file_permission_granted?(permission, opts[:by], opts[:by_user])
|
||||
end
|
||||
|
||||
def mounted?(expected_options = nil, identical = false)
|
||||
mounted = file.mounted
|
||||
|
||||
|
@ -206,18 +213,82 @@ module Inspec::Resources
|
|||
end
|
||||
|
||||
def check_file_permission_by_user(access_type, user, path)
|
||||
access_rule = case access_type
|
||||
when 'read'
|
||||
'@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'Read\', \'ListDirectory\')'
|
||||
when 'write'
|
||||
'@(\'FullControl\', \'Modify\', \'Write\')'
|
||||
when 'execute'
|
||||
'@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'ExecuteFile\')'
|
||||
else
|
||||
raise 'Invalid access_type provided'
|
||||
end
|
||||
access_rule = translate_perm_names(access_type)
|
||||
access_rule = convert_to_powershell_array(access_rule)
|
||||
|
||||
cmd = inspec.command("@(@((Get-Acl '#{path}').access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq '#{user}' }) | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {#{access_rule} -contains $_}) -ne $null}) | measure | % { $_.Count }")
|
||||
cmd.stdout.chomp == '0' ? false : true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def convert_to_powershell_array(arr)
|
||||
if arr.empty?
|
||||
'@()'
|
||||
else
|
||||
%{@('#{arr.join("', '")}')}
|
||||
end
|
||||
end
|
||||
|
||||
# Translates a developer-friendly string into a list of acceptable
|
||||
# FileSystemRights that match it, because Windows has a fun heirarchy
|
||||
# of permissions that are able to be noted in multiple ways.
|
||||
#
|
||||
# See also: https://www.codeproject.com/Reference/871338/AccessControl-FileSystemRights-Permissions-Table
|
||||
def translate_perm_names(access_type)
|
||||
names = translate_common_perms(access_type)
|
||||
names ||= translate_granular_perms(access_type)
|
||||
names ||= translate_uncommon_perms(access_type)
|
||||
raise 'Invalid access_type provided' unless names
|
||||
end
|
||||
|
||||
def translate_common_perms(access_type)
|
||||
case access_type
|
||||
when 'full-control'
|
||||
%w{FullControl}
|
||||
when 'modify'
|
||||
translate_perm_names('full-control') + %w{Modify}
|
||||
when 'read'
|
||||
translate_perm_names('modify') + %w{ReadAndExecute Read}
|
||||
when 'write'
|
||||
translate_perm_names('modify') + %w{Write}
|
||||
when 'execute'
|
||||
translate_perm_names('modify') + %w{ReadAndExecute ExecuteFile Traverse}
|
||||
when 'delete'
|
||||
translate_perm_names('modify') + %w{Delete}
|
||||
end
|
||||
end
|
||||
|
||||
def translate_uncommon_perms(access_type)
|
||||
case access_type
|
||||
when 'delete-subdirectories-and-files'
|
||||
translate_perm_names('full-control') + %w{DeleteSubdirectoriesAndFiles}
|
||||
when 'change-permissions'
|
||||
translate_perm_names('full-control') + %w{ChangePermissions}
|
||||
when 'take-ownership'
|
||||
translate_perm_names('full-control') + %w{TakeOwnership}
|
||||
end
|
||||
end
|
||||
|
||||
def translate_granular_perms(access_type)
|
||||
case access_type
|
||||
when 'write-data', 'create-files'
|
||||
translate_perm_names('write') + %w{WriteData CreateFiles}
|
||||
when 'append-data', 'create-directories'
|
||||
translate_perm_names('write') + %w{CreateDirectories AppendData}
|
||||
when 'write-extended-attributes'
|
||||
translate_perm_names('write') + %w{WriteExtendedAttributes}
|
||||
when 'write-attributes'
|
||||
translate_perm_names('write') + %w{WriteAttributes}
|
||||
when 'read-data', 'list-directory'
|
||||
translate_perm_names('read') + %w{ReadData ListDirectory}
|
||||
when 'read-attributes'
|
||||
translate_perm_names('read') + %w{ReadAttributes}
|
||||
when 'read-extended-attributes'
|
||||
translate_perm_names('read') + %w{ReadExtendedAttributes}
|
||||
when 'read-permissions'
|
||||
translate_perm_names('read') + %w{ReadPermissions}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -67,31 +67,50 @@ if os.unix?
|
|||
its('sticky') { should eq false }
|
||||
|
||||
it { should be_readable }
|
||||
it { should be_allowed('read') }
|
||||
it { should be_readable.by('owner') }
|
||||
it { should be_allowed('read', by: 'owner') }
|
||||
it { should be_readable.by('group') }
|
||||
it { should be_allowed('read', by: 'group') }
|
||||
it { should be_readable.by('other') }
|
||||
it { should be_allowed('read', by: 'other') }
|
||||
it { should be_readable.by_user(filedata[:user]) }
|
||||
it { should be_allowed('read', by_user: filedata[:user]) }
|
||||
it { should_not be_readable.by_user('noroot') }
|
||||
it { should_not be_allowed('read', by_user: 'noroot') }
|
||||
# for server spec compatibility
|
||||
it { should be_readable.by('others') }
|
||||
it { should be_allowed('read', by: 'others') }
|
||||
|
||||
it { should be_writable }
|
||||
it { should be_allowed('write') }
|
||||
it { should be_writable.by('owner') }
|
||||
it { should be_allowed('write', by: 'owner') }
|
||||
it { should be_writable.by('group') }
|
||||
it { should be_allowed('write', by: 'group') }
|
||||
it { should_not be_writable.by('other') }
|
||||
it { should_not be_allowed('write', by: 'other') }
|
||||
it { should be_writable.by_user(filedata[:user]) }
|
||||
it { should be_allowed('write', by_user: filedata[:user]) }
|
||||
# it { should_not be_writable.by_user('noroot') }
|
||||
# for server spec compatibility
|
||||
it { should_not be_writable.by('others') }
|
||||
it { should_not be_allowed('write', by: 'others') }
|
||||
|
||||
it { should be_executable }
|
||||
it { should be_allowed('execute') }
|
||||
it { should be_executable.by('owner') }
|
||||
it { should be_allowed('execute', by: 'owner') }
|
||||
it { should_not be_executable.by('group') }
|
||||
it { should_not be_allowed('execute', by: 'group') }
|
||||
it { should be_executable.by('other') }
|
||||
it { should be_allowed('execute', by: 'other') }
|
||||
it { should be_executable.by_user(filedata[:user]) }
|
||||
it { should be_allowed('execute', by_user: filedata[:user]) }
|
||||
# it { should_not be_executable.by_user('noroot') }
|
||||
# for server spec compatibility
|
||||
it { should be_executable.by('others') }
|
||||
it { should be_allowed('execute', by: 'others') }
|
||||
|
||||
# test extended linux attributes
|
||||
# it { should be_immutable }
|
||||
|
@ -167,22 +186,34 @@ if os.windows?
|
|||
it { should exist }
|
||||
it { should be_file }
|
||||
it { should be_readable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('read', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should be_writable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('write', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should be_executable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('execute', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should_not be_readable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('read', by_user: filedata[:user]) }
|
||||
it { should_not be_writable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('write', by_user: filedata[:user]) }
|
||||
it { should_not be_executable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('execute', by_user: filedata[:user]) }
|
||||
end
|
||||
|
||||
describe file('C:/Test Directory') do
|
||||
it { should exist }
|
||||
it { should be_directory }
|
||||
it { should be_readable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('read', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should be_writable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('write', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should be_executable.by_user('NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('execute', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should_not be_readable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('read', by_user: filedata[:user]) }
|
||||
it { should_not be_writable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('write', by_user: filedata[:user]) }
|
||||
it { should_not be_executable.by_user(filedata[:user]) }
|
||||
it { should_not be_allowed('execute', by_user: filedata[:user]) }
|
||||
end
|
||||
|
||||
describe file("C:/Program Files (x86)/Windows NT/Accessories/wordpad.exe") do
|
||||
|
@ -195,5 +226,7 @@ if os.windows?
|
|||
describe directory('C:/opscode/chef') do
|
||||
its('owner') { should cmp 'NT AUTHORITY\SYSTEM' }
|
||||
it { should be_owned_by 'NT AUTHORITY\SYSTEM' }
|
||||
it { should be_allowed('full-control', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
it { should be_allowed('modify', by_user: 'NT AUTHORITY\SYSTEM') }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,8 +26,11 @@ describe Inspec::Resources::FileResource do
|
|||
_(resource.mounted?).must_equal true
|
||||
_(resource.to_s).must_equal 'File /fakepath/fakefile'
|
||||
_(resource.readable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('read', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.writable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('write', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.executable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('execute', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.suid).must_equal true
|
||||
_(resource.sgid).must_equal true
|
||||
_(resource.sticky).must_equal true
|
||||
|
@ -40,12 +43,23 @@ describe Inspec::Resources::FileResource do
|
|||
resource.stubs(:file_permission_granted?).with('read', 'by_usergroup', 'by_specific_user').returns('test_result')
|
||||
resource.stubs(:file_permission_granted?).with('write', 'by_usergroup', 'by_specific_user').returns('test_result')
|
||||
resource.stubs(:file_permission_granted?).with('execute', 'by_usergroup', 'by_specific_user').returns('test_result')
|
||||
resource.stubs(:file_permission_granted?).with('full-control', 'by_usergroup', 'by_specific_user').returns('test_result')
|
||||
_(resource.content).must_equal 'content'
|
||||
_(resource.exist?).must_equal true
|
||||
_(resource.mounted?).must_equal true
|
||||
_(resource.readable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('read', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.writable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('write', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.executable?('by_usergroup', 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('execute', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
_(resource.allowed?('full-control', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal 'test_result'
|
||||
end
|
||||
it 'does not support Windows-style ACL on Ubuntu' do
|
||||
resource = MockLoader.new(:ubuntu1404).load_resource('file', '/fakepath/fakefile')
|
||||
resource.stubs(:exist?).returns(true)
|
||||
proc { resource.send('allowed?', 'full-control', { by: 'by_usergroup', by_user: 'by_specific_user' }) }.must_raise(RuntimeError)
|
||||
proc { resource.send('allowed?', 'modify', { by: 'by_usergroup', by_user: 'by_specific_user' }) }.must_raise(RuntimeError)
|
||||
end
|
||||
it 'does not support check by mask on Windows' do
|
||||
resource = MockLoader.new(:windows).load_resource('file', 'C:/fakepath/fakefile')
|
||||
|
@ -61,6 +75,7 @@ describe Inspec::Resources::FileResource do
|
|||
_(resource.readable?('by_usergroup', 'by_specific_user')).must_equal '`readable?` is not supported on your OS yet.'
|
||||
_(resource.writable?('by_usergroup', 'by_specific_user')).must_equal '`writable?` is not supported on your OS yet.'
|
||||
_(resource.executable?('by_usergroup', 'by_specific_user')).must_equal '`executable?` is not supported on your OS yet.'
|
||||
_(resource.allowed?('permission', by: 'by_usergroup', by_user: 'by_specific_user')).must_equal '`allowed?` is not supported on your OS yet.'
|
||||
proc { resource.send(:contain, nil) }.must_raise(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue