Merge pull request #4452 from mhackethal/user-windows-fix

User windows fix
This commit is contained in:
Miah Johnson 2019-10-04 15:30:37 -07:00 committed by GitHub
commit e48d08343b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 151 additions and 9 deletions

View file

@ -34,13 +34,16 @@ A `user` resource block declares a user name, and then one (or more) matchers:
its('mindays') { should eq 0 }
its('maxdays') { should eq 90 }
its('warndays') { should eq 8 }
its('passwordage') { should eq 355 }
its('maxbadpasswords') { should eq nil } // Only valid on Windows OS
its('badpasswordattempts') { should eq 0 }
end
where
* `('root')` is the user to be tested
* `it { should exist }` tests if the user exists
* `gid`, `group`, `groups`, `home`, `maxdays`, `mindays`, `shell`, `uid`, and `warndays` are valid matchers for this resource
* `gid`, `group`, `groups`, `home`, `maxdays`, `mindays`, `shell`, `uid`, `warndays`´, `passwordage`, `maxbadpasswords` and `badpasswordattempts` are valid matchers for this resource
<br>
@ -90,6 +93,7 @@ The `gid` matcher tests the group identifier:
its('gid') { should eq 1234 }
where `1234` represents the user identifier.
The `gid` option is only available on Linux and will return `nil` for Windows os.
### group
@ -98,6 +102,7 @@ The `group` matcher tests the group to which the user belongs:
its('group') { should eq 'root' }
where `root` represents the group.
The `group` option is only available on Linux and will return `nil` for Windows os.
### groups
@ -148,3 +153,29 @@ The `warndays` matcher tests the number of days a user is warned before a passwo
its('warndays') { should eq 5 }
where `5` represents the number of days a user is warned.
### passwordage
The `passwordage` matcher tests the number of days a user changed its password:
its('passwordage') { should_be <= 365 }
where `365` represents the number of days since the last password change.
### maxbadpasswords
The `maxbadpasswords` matcher tests the count of max badpassword settings for a specific user.
its('maxbadpasswords') { should eq 7 }
where `7` is the count of maximum bad password attempts.
### badpasswordattempts
The `badpasswordattempts` matcher tests the count of bad password attempts for a user.
its('badpasswordattempts') { should eq 0 }
where `0` is the count of bad passwords for a user.
On Linux based operating systems it relies on `lastb` and for Windows it uses information stored for the user object.
These settings will be resetted to `0` depending on your operating system configuration.

View file

@ -31,7 +31,7 @@ A `users` resource block declares a user name, and then one (or more) matchers:
where
* `gid`, `group`, `groups`, `home`, `maxdays`, `mindays`, `shell`, `uid`, and `warndays` are valid matchers for this resource
* `gid`, `group`, `groups`, `home`, `maxdays`, `mindays`, `shell`, `uid`, `warndays`, `passwordage`, `maxbadpasswords` and `badpasswordattempts` are valid matchers for this resource
* `where(uid: 0).entries` represents a filter that runs the test only against matching users
For example:
@ -149,3 +149,29 @@ The `warndays` matcher tests the number of days a user is warned before a passwo
its('warndays') { should eq 5 }
where `5` represents the number of days a user is warned.
### passwordage
The `passwordage` matcher tests the number of days a user changed its password:
its('passwordage') { should_be <= 365 }
where `365` represents the number of days since the last password change.
### maxbadpasswords
The `maxbadpasswords` matcher tests the count of max badpassword settings for a specific user.
its('maxbadpasswords') { should eq 7 }
where `7` is the count of maximum bad password attempts.
### badpasswordattempts
The `badpasswordattempts` matcher tests the count of bad password attempts for a user.
its('badpasswordattempts') { should eq 0 }
where `0` is the count of bad passwords for a user.
On Linux based operating systems it relies on `lastb` and for Windows it uses information stored for the user object.
These settings will be resetted to `0` depending on your operating system configuration.

View file

@ -3,6 +3,7 @@ require "inspec/utils/convert"
require "inspec/utils/filter"
require "inspec/utils/simpleconfig"
require "inspec/resources/powershell"
require "date"
module Inspec::Resources
# This file contains two resources, the `user` and `users` resource.
@ -116,6 +117,24 @@ module Inspec::Resources
# its('mindays') { should eq 0 }
# its('maxdays') { should eq 99 }
# its('warndays') { should eq 5 }
# its('passwordage') { should be >= 0 }
# its('maxbadpasswords') { should eq nil } // not yet supported on linux
# its('badpasswordattempts') { should eq 0 }
# end
# describe user('Administrator') do
# it { should exist }
# its('uid') { should eq "S-1-5-21-1759981009-4135989804-1844563890-500" }
# its('gid') { should eq nil } // not supported on Windows
# its('group') { should eq nil } // not supported on Windows
# its('groups') { should eq ['Administrators', 'Users']}
# its('home') { should eq '' }
# its('shell') { should eq nil } // not supported on Windows
# its('mindays') { should eq 0 }
# its('maxdays') { should eq 42 }
# its('warndays') { should eq nil }
# its('passwordage') { should eq 355 }
# its('maxbadpasswords') { should eq 0 }
# its('badpasswordattempts') { should eq 0 }
# end
#
# The following Serverspec matchers are deprecated in favor for direct value access
@ -196,6 +215,14 @@ module Inspec::Resources
meta_info[:shell] unless meta_info.nil?
end
def domain
meta_info[:domain] unless meta_info.nil?
end
def userflags
meta_info[:userflags] unless meta_info.nil?
end
# returns the minimum days between password changes
def mindays
credentials[:mindays] unless credentials.nil?
@ -211,6 +238,18 @@ module Inspec::Resources
credentials[:warndays] unless credentials.nil?
end
def badpasswordattempts
credentials[:badpasswordattempts] unless credentials.nil?
end
def maxbadpasswords
credentials[:maxbadpasswords] unless credentials.nil?
end
def passwordage
credentials[:passwordage] unless credentials.nil?
end
# implement 'mindays' method to be compatible with serverspec
def minimum_days_between_password_change
Inspec.deprecate(:resource_user_serverspec_compat, "The user resource `minimum_days_between_password_change` property is deprecated. Please use `mindays`.")
@ -425,10 +464,17 @@ module Inspec::Resources
multiple_values: false
).params
dparse = Date.parse "#{params['Last password change']}"
dayslastset = (Date.today - dparse).to_i
cmd = inspec.command("lastb -w -a | grep #{username} | wc -l")
badpasswordattempts = convert_to_i(cmd.stdout.chomp) if cmd.exit_status == 0
{
mindays: convert_to_i(params["Minimum number of days between password change"]),
maxdays: convert_to_i(params["Maximum number of days between password change"]),
warndays: convert_to_i(params["Number of days of warning before password expires"]),
passwordage: dayslastset,
badpasswordattempts: badpasswordattempts,
}
end
end
@ -481,6 +527,8 @@ module Inspec::Resources
mindays: user_sec[1].to_i * 7,
maxdays: user_sec[2].to_i * 7,
warndays: user_sec[3].to_i,
passwordage: nil,
badpasswordattempts: nil,
}
end
end
@ -573,6 +621,31 @@ module Inspec::Resources
res[0] unless res.empty?
end
def meta_info(username)
res = identity(username)
return if res.nil?
{
home: res[:home],
shell: res[:shell],
domain: res[:domain],
userflags: res[:userflags],
}
end
def credentials(username)
res = identity(username)
return if res.nil?
{
mindays: res[:mindays],
maxdays: res[:maxdays],
warndays: res[:warndays],
badpasswordattempts: res[:badpasswordattempts],
maxbadpasswords: res[:maxbadpasswords],
minpasswordlength: res[:minpasswordlength],
passwordage: res[:passwordage],
}
end
def list_users
collect_user_details.map { |user| user[:username] }
end

View file

@ -91,6 +91,12 @@ if os.windows?
# should return the SID of the user
its('uid') { should_not eq nil}
its('groups') { should include userinfo[:groups] }
its('mindays') { should eq 0 }
its('maxdays') { should eq 42 }
its('warndays') { should eq nil }
its('passwordage') { should_be > 5 }
its('maxbadpasswords') { should eq 0 }
its('badpasswordattempts') { should eq 0 }
end
# also support simple username for local users without domain
@ -99,7 +105,13 @@ if os.windows?
# should return the SID of the user
its('uid') { should_not eq nil}
its('groups') { should include userinfo[:groups] }
end
its('mindays') { should eq 0 }
its('maxdays') { should eq 42 }
its('warndays') { should eq nil }
its('passwordage') { should_be > 5 }
its('maxbadpasswords') { should eq 0 }
its('badpasswordattempts') { should eq 0 }
end
else
# test single `user` resource
describe user(userinfo[:username]) do

View file

@ -120,10 +120,10 @@ describe "Inspec::Resources::User" do
_(resource.exists?).must_equal true
_(resource.group).must_be_nil
_(resource.groups).must_equal %w{Administrators Users}
_(resource.home).must_be_nil
_(resource.home).wont_be_nil
_(resource.shell).must_be_nil
_(resource.mindays).must_be_nil
_(resource.maxdays).must_be_nil
_(resource.mindays).wont_be_nil
_(resource.maxdays).wont_be_nil
_(resource.warndays).must_be_nil
_(resource.disabled?).must_equal false
end
@ -134,10 +134,10 @@ describe "Inspec::Resources::User" do
_(resource.exists?).must_equal true
_(resource.group).must_be_nil
_(resource.groups).must_equal ["Users"]
_(resource.home).must_be_nil
_(resource.home).wont_be_nil
_(resource.shell).must_be_nil
_(resource.mindays).must_be_nil
_(resource.maxdays).must_be_nil
_(resource.mindays).wont_be_nil
_(resource.maxdays).wont_be_nil
_(resource.warndays).must_be_nil
_(resource.disabled?).must_equal true
end