inspec 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -2
- data/README.md +27 -2
- data/docs/resources.rst +49 -3
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/README.md +2 -1
- data/lib/bundles/inspec-compliance/api.rb +60 -102
- data/lib/bundles/inspec-compliance/cli.rb +133 -14
- data/lib/bundles/inspec-compliance/configuration.rb +43 -1
- data/lib/bundles/inspec-compliance/http.rb +80 -0
- data/lib/bundles/inspec-compliance.rb +1 -0
- data/lib/inspec/metadata.rb +40 -27
- data/lib/inspec/objects/test.rb +2 -1
- data/lib/inspec/resource.rb +1 -0
- data/lib/inspec/rspec_json_formatter.rb +1 -1
- data/lib/inspec/runner.rb +19 -13
- data/lib/inspec/runner_rspec.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +32 -13
- data/lib/resources/grub_conf.rb +186 -0
- data/lib/resources/json.rb +1 -1
- data/lib/resources/service.rb +9 -3
- data/lib/utils/base_cli.rb +2 -1
- data/lib/utils/hash_map.rb +37 -0
- data/test/functional/inspec_compliance_test.rb +60 -0
- data/test/functional/inspec_exec_test.rb +49 -10
- data/test/helper.rb +3 -0
- data/test/integration/default/compare_matcher_spec.rb +21 -0
- data/test/unit/metadata_test.rb +49 -23
- data/test/unit/mock/cmd/systemctl-show-all-dbus +6 -0
- data/test/unit/mock/files/grub.conf +21 -0
- data/test/unit/mock/profiles/resource-tiny/inspec.yml +10 -0
- data/test/unit/mock/profiles/resource-tiny/libraries/resource.rb +3 -0
- data/test/unit/mock/profiles/supported_inspec/inspec.yml +2 -0
- data/test/unit/mock/profiles/unsupported_inspec/inspec.yml +2 -0
- data/test/unit/profile_test.rb +1 -1
- data/test/unit/resources/grub_conf_test.rb +29 -0
- data/test/unit/resources/service_test.rb +9 -0
- data/test/unit/utils/hash_map_test.rb +63 -0
- metadata +26 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f8721f5a1366ca4d03be5b1cbfe3b8f61ba315f
|
4
|
+
data.tar.gz: 68a9c4ce7b72cde464e322d449ce8775f0afa6a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 595a34ad34f066fffe31f2ee6799d5692010521949d6dbe57c3d306f391922e7c4ea295f6504156d94b8b835553930623757a2a97e10ce706ae1340ccc40006f
|
7
|
+
data.tar.gz: d117f5aecbcdce653511e263b1b5c9ae394a354ae38bc5fdb93aa73850d9328d6e7934b3acdb5568e689d0163795134a15aef2e167212920baf6306d6d5e4e24
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,39 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [0.
|
4
|
-
[Full Changelog](https://github.com/chef/inspec/compare/v0.
|
3
|
+
## [0.19.0](https://github.com/chef/inspec/tree/0.19.0) (2016-04-17)
|
4
|
+
[Full Changelog](https://github.com/chef/inspec/compare/v0.18.0...0.19.0)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Add required inspec version to inspec.yml [\#644](https://github.com/chef/inspec/issues/644)
|
9
|
+
- Resource grub conf [\#652](https://github.com/chef/inspec/pull/652) ([arlimus](https://github.com/arlimus))
|
10
|
+
- fail on unsupported os/platform [\#651](https://github.com/chef/inspec/pull/651) ([arlimus](https://github.com/arlimus))
|
11
|
+
- specify required inspec version in inspec.yml [\#648](https://github.com/chef/inspec/pull/648) ([arlimus](https://github.com/arlimus))
|
12
|
+
- feature: `cmp \< / \> / \<= / \>= / == / != sth` matcher [\#643](https://github.com/chef/inspec/pull/643) ([arlimus](https://github.com/arlimus))
|
13
|
+
- Add 'static' value as enabled to systemd service enabled check [\#637](https://github.com/chef/inspec/pull/637) ([jmccann](https://github.com/jmccann))
|
14
|
+
- add dockerized inspec [\#635](https://github.com/chef/inspec/pull/635) ([arlimus](https://github.com/arlimus))
|
15
|
+
- inspec-compliance + Compliance 1.0 [\#576](https://github.com/chef/inspec/pull/576) ([srenatus](https://github.com/srenatus))
|
16
|
+
|
17
|
+
**Fixed bugs:**
|
18
|
+
|
19
|
+
- `add\_test': undefined method error on Ubuntu 15.10 with Ruby 2.1 [\#642](https://github.com/chef/inspec/issues/642)
|
20
|
+
- Install failed on Ubuntu with Ruby 2.1 [\#641](https://github.com/chef/inspec/issues/641)
|
21
|
+
- Inspec json resource . example not working [\#631](https://github.com/chef/inspec/issues/631)
|
22
|
+
- Checking on services on SLES 11 fails [\#627](https://github.com/chef/inspec/issues/627)
|
23
|
+
- Inspec check fails on `examples/profile` [\#485](https://github.com/chef/inspec/issues/485)
|
24
|
+
- bugfix: rspec world handling on rspec 3.5 [\#650](https://github.com/chef/inspec/pull/650) ([arlimus](https://github.com/arlimus))
|
25
|
+
- Prevent its\(:to\_i\) from generated tests [\#639](https://github.com/chef/inspec/pull/639) ([alexpop](https://github.com/alexpop))
|
26
|
+
- bugfix: non-profile execution with json formatter [\#632](https://github.com/chef/inspec/pull/632) ([arlimus](https://github.com/arlimus))
|
27
|
+
|
28
|
+
**Merged pull requests:**
|
29
|
+
|
30
|
+
- add usage instructions for inspec container [\#649](https://github.com/chef/inspec/pull/649) ([chris-rock](https://github.com/chris-rock))
|
31
|
+
- update documentation for json resource [\#647](https://github.com/chef/inspec/pull/647) ([chris-rock](https://github.com/chris-rock))
|
32
|
+
- Add support for suse 11 to service resource [\#638](https://github.com/chef/inspec/pull/638) ([spuranam](https://github.com/spuranam))
|
33
|
+
- Add -i to ssh example, link to cli options [\#636](https://github.com/chef/inspec/pull/636) ([vjeffrey](https://github.com/vjeffrey))
|
34
|
+
|
35
|
+
## [v0.18.0](https://github.com/chef/inspec/tree/v0.18.0) (2016-04-09)
|
36
|
+
[Full Changelog](https://github.com/chef/inspec/compare/v0.17.1...v0.18.0)
|
5
37
|
|
6
38
|
**Implemented enhancements:**
|
7
39
|
|
@@ -11,6 +43,7 @@
|
|
11
43
|
|
12
44
|
**Merged pull requests:**
|
13
45
|
|
46
|
+
- 0.18.0 [\#629](https://github.com/chef/inspec/pull/629) ([arlimus](https://github.com/arlimus))
|
14
47
|
- Encourage sharing of profiles [\#625](https://github.com/chef/inspec/pull/625) ([nathenharvey](https://github.com/nathenharvey))
|
15
48
|
- add travis and appveyor badges [\#622](https://github.com/chef/inspec/pull/622) ([chris-rock](https://github.com/chris-rock))
|
16
49
|
- remove unused profile.tar.gz [\#621](https://github.com/chef/inspec/pull/621) ([chris-rock](https://github.com/chris-rock))
|
data/README.md
CHANGED
@@ -18,14 +18,14 @@ describe inetd_conf do
|
|
18
18
|
end
|
19
19
|
```
|
20
20
|
|
21
|
-
InSpec makes it easy to run your tests wherever you need.
|
21
|
+
InSpec makes it easy to run your tests wherever you need. More options listed here: https://github.com/chef/inspec/blob/master/docs/ctl_inspec.rst
|
22
22
|
|
23
23
|
```bash
|
24
24
|
# run test locally
|
25
25
|
inspec exec test.rb
|
26
26
|
|
27
27
|
# run test on remote host on SSH
|
28
|
-
inspec exec test.rb -t ssh://user@hostname
|
28
|
+
inspec exec test.rb -t ssh://user@hostname -i /path/to/key
|
29
29
|
|
30
30
|
# run test on remote windows host on WinRM
|
31
31
|
inspec exec test.rb -t winrm://Administrator@windowshost --password 'your-password'
|
@@ -51,6 +51,31 @@ InSpec requires Ruby ( >1.9 ).
|
|
51
51
|
gem install inspec
|
52
52
|
```
|
53
53
|
|
54
|
+
### Usage via Docker
|
55
|
+
|
56
|
+
Download the image and define an alias for convenience:
|
57
|
+
|
58
|
+
```
|
59
|
+
docker pull chef/inspec
|
60
|
+
alias inspec='docker run -it --rm -v $(pwd):/share chef/inspec'
|
61
|
+
```
|
62
|
+
|
63
|
+
If you call inspec from cli, it automatically mounts the current directory into the work directory. Therefore you can easily use local tests and key files. Note: Only files in the current directory are available to the container.
|
64
|
+
|
65
|
+
```
|
66
|
+
$ ls -1
|
67
|
+
vagrant
|
68
|
+
test.rb
|
69
|
+
|
70
|
+
|
71
|
+
$ inspec exec test.rb -t ssh://root@192.168.64.2:11022 -i vagrant
|
72
|
+
..
|
73
|
+
|
74
|
+
Finished in 0.04321 seconds (files took 0.54917 seconds to load)
|
75
|
+
2 examples, 0 failures
|
76
|
+
```
|
77
|
+
|
78
|
+
|
54
79
|
### Install it from source
|
55
80
|
|
56
81
|
That requires [bundler](http://bundler.io/):
|
data/docs/resources.rst
CHANGED
@@ -18,6 +18,7 @@ The following InSpec audit resources are available:
|
|
18
18
|
* `file`_
|
19
19
|
* `gem`_
|
20
20
|
* `group <https://github.com/chef/inspec/blob/master/docs/resources.rst#group-1/>`_
|
21
|
+
* `grub_conf`_
|
21
22
|
* `host`_
|
22
23
|
* `inetd_conf`_
|
23
24
|
* `interface`_
|
@@ -1567,6 +1568,38 @@ The following examples show how to use this InSpec audit resource.
|
|
1567
1568
|
|
1568
1569
|
|
1569
1570
|
|
1571
|
+
|
1572
|
+
grub_conf
|
1573
|
+
=====================================================
|
1574
|
+
|
1575
|
+
Test both Grub 1 and Grub 2 configurations.
|
1576
|
+
|
1577
|
+
**Stability: Experimental**
|
1578
|
+
|
1579
|
+
Syntax
|
1580
|
+
-----------------------------------------------------
|
1581
|
+
A ``grub_conf`` resource is used to specify a configuration file and boot configuration.
|
1582
|
+
|
1583
|
+
.. code-block:: ruby
|
1584
|
+
|
1585
|
+
describe grub_conf('/etc/grub.conf', 'default') do
|
1586
|
+
its('kernel') { should include '/vmlinuz-2.6.32-573.7.1.el6.x86_64' }
|
1587
|
+
its('initrd') { should include '/initramfs-2.6.32-573.el6.x86_64.img=1' }
|
1588
|
+
its('default') { should_not eq '1' }
|
1589
|
+
its('timeout') { should eq '5' }
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
You can also check specific kernels:
|
1593
|
+
|
1594
|
+
.. code-block:: ruby
|
1595
|
+
|
1596
|
+
grub_conf('/etc/grub.conf', 'CentOS (2.6.32-573.12.1.el6.x86_64)') do
|
1597
|
+
its('kernel') { should include 'audit=1' }
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
|
1601
|
+
|
1602
|
+
|
1570
1603
|
host
|
1571
1604
|
=====================================================
|
1572
1605
|
Use the ``host`` |inspec resource| to test the name used to refer to a specific host and its availability, including the Internet protocols and ports over which that host name should be available.
|
@@ -1872,12 +1905,25 @@ Use the ``json`` |inspec resource| to test data in a |json| file.
|
|
1872
1905
|
|
1873
1906
|
Syntax
|
1874
1907
|
-----------------------------------------------------
|
1875
|
-
A ``json`` |inspec resource| block declares the data to be tested:
|
1908
|
+
A ``json`` |inspec resource| block declares the data to be tested. Assume the following json file:
|
1909
|
+
|
1910
|
+
.. code-block:: json
|
1911
|
+
|
1912
|
+
{
|
1913
|
+
"name" : "hello",
|
1914
|
+
"meta" : {
|
1915
|
+
"creator" : "John Doe"
|
1916
|
+
}
|
1917
|
+
}
|
1918
|
+
|
1919
|
+
|
1920
|
+
This file can be queried via:
|
1876
1921
|
|
1877
1922
|
.. code-block:: ruby
|
1878
1923
|
|
1879
|
-
describe json do
|
1880
|
-
|
1924
|
+
describe json('/paht/to/name.json') do
|
1925
|
+
its('name') { should eq 'hello' }
|
1926
|
+
its(['meta','creator']) { should eq 'John Doe' }
|
1881
1927
|
end
|
1882
1928
|
|
1883
1929
|
where
|
data/inspec.gemspec
CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency 'rainbow', '~> 2'
|
31
31
|
spec.add_dependency 'method_source', '~> 0.8'
|
32
32
|
spec.add_dependency 'rubyzip', '~> 1.1'
|
33
|
-
spec.add_dependency 'rspec', '~> 3
|
33
|
+
spec.add_dependency 'rspec', '~> 3'
|
34
34
|
spec.add_dependency 'rspec-its', '~> 1.2'
|
35
35
|
spec.add_dependency 'pry', '~> 0'
|
36
36
|
|
@@ -8,7 +8,8 @@ This extensions offers the following features:
|
|
8
8
|
|
9
9
|
To use the CLI, this InSpec add-on adds the following commands:
|
10
10
|
|
11
|
-
* `$ inspec compliance
|
11
|
+
* `$ inspec compliance api_token server --token TOKEN --user USER` - save the Chef Compliance API token for user
|
12
|
+
* `$ inspec compliance login` - authentication of the API token against Chef Compliance
|
12
13
|
* `$ inspec compliance profiles` - list all available Chef Compliance profiles
|
13
14
|
* `$ inspec compliance exec profile` - runs a Chef Compliance profile
|
14
15
|
* `$ inspec compliance upload path/to/local/profile` - uploads a local profile to Chef Compliance
|
@@ -8,45 +8,44 @@ require 'uri'
|
|
8
8
|
module Compliance
|
9
9
|
# API Implementation does not hold any state by itself,
|
10
10
|
# everything will be stored in local Configuration store
|
11
|
-
class API
|
12
|
-
#
|
13
|
-
def self.
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
class API
|
12
|
+
# login method for pre-1.0 compliance server
|
13
|
+
def self.legacy_login_post(url, username, password, insecure)
|
14
|
+
# form request
|
15
|
+
# TODO: reuse post function
|
16
|
+
uri = URI.parse(url)
|
17
|
+
req = Net::HTTP::Post.new(uri.path)
|
18
|
+
req.basic_auth(username, password)
|
19
|
+
req.form_data={}
|
20
|
+
|
21
|
+
send_request(uri, req, insecure)
|
22
|
+
end
|
17
23
|
|
18
|
-
|
24
|
+
# return all compliance profiles available for the user
|
25
|
+
def self.profiles(config)
|
26
|
+
url = "#{config['server']}/user/compliance"
|
27
|
+
# TODO, api should not be dependent on .supported?
|
28
|
+
response = Compliance::HTTP.get(url, config['token'], config['insecure'], !config.supported?(:oidc))
|
29
|
+
data = response.body
|
19
30
|
if !data.nil?
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
msg = 'Successfully authenticated'
|
28
|
-
else
|
29
|
-
msg = 'Reponse does not include a token'
|
30
|
-
end
|
31
|
+
profiles = JSON.parse(data)
|
32
|
+
# iterate over profiles
|
33
|
+
profiles.map do |owner, ps|
|
34
|
+
ps.keys.map do |name|
|
35
|
+
{ org: owner, name: name }
|
36
|
+
end
|
37
|
+
end.flatten
|
31
38
|
else
|
32
|
-
|
39
|
+
[]
|
33
40
|
end
|
34
|
-
[success, msg]
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.logout
|
38
|
-
config = Compliance::Configuration.new
|
39
|
-
url = "#{config['server']}/logout"
|
40
|
-
Compliance::API.post(url, config['token'], nil, config['insecure'])
|
41
|
-
config.destroy
|
42
41
|
end
|
43
42
|
|
44
43
|
# return the server api version
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
# NB this method does not use Compliance::Configuration to allow for using
|
45
|
+
# it before we know the version (e.g. oidc or not)
|
46
|
+
def self.version(url, insecure)
|
47
|
+
response = Compliance::HTTP.get(url+'/version', nil, insecure)
|
48
|
+
data = response.body
|
50
49
|
if !data.nil?
|
51
50
|
JSON.parse(data)
|
52
51
|
else
|
@@ -54,30 +53,9 @@ module Compliance
|
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
57
|
-
# return all compliance profiles available for the user
|
58
|
-
def self.profiles
|
59
|
-
config = Compliance::Configuration.new
|
60
|
-
url = "#{config['server']}/user/compliance"
|
61
|
-
_success, data = get(url, config['token'], '', config['insecure'])
|
62
|
-
|
63
|
-
if !data.nil?
|
64
|
-
profiles = JSON.parse(data)
|
65
|
-
val = []
|
66
|
-
# iterate over profiles
|
67
|
-
profiles.each_key { |org|
|
68
|
-
profiles[org].each_key { |name|
|
69
|
-
val.push({ org: org, name: name })
|
70
|
-
}
|
71
|
-
}
|
72
|
-
val
|
73
|
-
else
|
74
|
-
[]
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
56
|
# verifies that a profile
|
79
|
-
def self.exist?(profile)
|
80
|
-
profiles = Compliance::API.profiles
|
57
|
+
def self.exist?(config, profile)
|
58
|
+
profiles = Compliance::API.profiles(config)
|
81
59
|
if !profiles.empty?
|
82
60
|
index = profiles.index { |p| "#{p[:org]}/#{p[:name]}" == profile }
|
83
61
|
!index.nil? && index >= 0
|
@@ -86,57 +64,37 @@ module Compliance
|
|
86
64
|
end
|
87
65
|
end
|
88
66
|
|
89
|
-
def self.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
send_request(uri, req, insecure)
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.post(url, username, password, insecure)
|
98
|
-
# form request
|
99
|
-
uri = URI.parse(url)
|
100
|
-
req = Net::HTTP::Post.new(uri.path)
|
101
|
-
req.basic_auth username, password
|
102
|
-
req.form_data={}
|
103
|
-
|
104
|
-
send_request(uri, req, insecure)
|
105
|
-
end
|
106
|
-
|
107
|
-
# upload a file
|
108
|
-
def self.post_file(url, username, password, file_path, insecure)
|
109
|
-
uri = URI.parse(url)
|
110
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
111
|
-
|
112
|
-
# set connection flags
|
113
|
-
http.use_ssl = (uri.scheme == 'https')
|
114
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if insecure
|
115
|
-
|
116
|
-
req = Net::HTTP::Post.new(uri.path)
|
117
|
-
req.basic_auth username, password
|
118
|
-
|
119
|
-
req.body_stream=File.open(file_path, 'rb')
|
120
|
-
req.add_field('Content-Length', File.size(file_path))
|
121
|
-
req.add_field('Content-Type', 'application/x-gtar')
|
122
|
-
|
123
|
-
boundary = 'INSPEC-PROFILE-UPLOAD'
|
124
|
-
req.add_field('session', boundary)
|
125
|
-
res=http.request(req)
|
126
|
-
|
67
|
+
def self.upload(config, owner, profile_name, archive_path)
|
68
|
+
# upload the tar to Chef Compliance
|
69
|
+
url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
|
70
|
+
res = Compliance::HTTP.post_file(url, config['token'], archive_path, config['insecure'], !config.supported?(:oidc))
|
127
71
|
[res.is_a?(Net::HTTPSuccess), res.body]
|
128
72
|
end
|
129
73
|
|
130
|
-
def self.
|
131
|
-
|
132
|
-
|
133
|
-
}
|
134
|
-
|
74
|
+
def self.post_refresh_token(url, token, insecure)
|
75
|
+
uri = URI.parse("#{url}/login")
|
76
|
+
req = Net::HTTP::Post.new(uri.path)
|
77
|
+
# req['Authorization'] = "Bearer #{token}"
|
78
|
+
req.body = { token: token }.to_json
|
79
|
+
access_token = nil
|
80
|
+
response = Compliance::HTTP.send_request(uri, req, insecure)
|
81
|
+
data = response.body
|
82
|
+
if !data.nil?
|
83
|
+
begin
|
84
|
+
tokendata = JSON.parse(data)
|
85
|
+
access_token = tokendata['access_token']
|
86
|
+
msg = 'Successfully fetched access token'
|
87
|
+
success = true
|
88
|
+
rescue JSON::ParserError => e
|
89
|
+
success = false
|
90
|
+
msg = e.message
|
91
|
+
end
|
92
|
+
else
|
93
|
+
success = false
|
94
|
+
msg = 'Invalid refresh_token'
|
95
|
+
end
|
135
96
|
|
136
|
-
|
137
|
-
http.request(req)
|
138
|
-
}
|
139
|
-
[res.is_a?(Net::HTTPSuccess), res.body]
|
97
|
+
[success, msg, access_token]
|
140
98
|
end
|
141
99
|
end
|
142
100
|
end
|
@@ -10,16 +10,44 @@ module Compliance
|
|
10
10
|
namespace 'compliance'
|
11
11
|
|
12
12
|
desc 'login SERVER', 'Log in to a Chef Compliance SERVER'
|
13
|
-
option :
|
14
|
-
desc: 'Chef Compliance Username'
|
15
|
-
option :password, type: :string, required: true,
|
16
|
-
desc: 'Chef Compliance Password'
|
13
|
+
option :server, type: :string, desc: 'Chef Compliance Server URL'
|
17
14
|
option :insecure, aliases: :k, type: :boolean,
|
18
15
|
desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
|
16
|
+
option :user, type: :string, required: false,
|
17
|
+
desc: 'Chef Compliance Username (for legacy auth)'
|
18
|
+
option :password, type: :string, required: false,
|
19
|
+
desc: 'Chef Compliance Password (for legacy auth)'
|
19
20
|
option :apipath, type: :string, default: '/api',
|
20
21
|
desc: 'Set the path to the API, defaults to /api'
|
21
|
-
|
22
|
-
|
22
|
+
option :token, type: :string, required: false,
|
23
|
+
desc: 'Chef Compliance access token'
|
24
|
+
option :refresh_token, type: :string, required: false,
|
25
|
+
desc: 'Chef Compliance refresh token'
|
26
|
+
def login(server) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, PerceivedComplexity
|
27
|
+
# show warning if the Compliance Server does not support
|
28
|
+
if !Compliance::Configuration.new.supported?(:oidc) && (!options['token'].nil? || !options['refresh_token'].nil?)
|
29
|
+
puts 'Your server supports --user and --password only'
|
30
|
+
end
|
31
|
+
|
32
|
+
options['server'] = server
|
33
|
+
url = options['server'] + options['apipath']
|
34
|
+
if !options['user'].nil? && !options['password'].nil?
|
35
|
+
# username / password
|
36
|
+
success, msg = login_legacy(url, options['user'], options['password'], options['insecure'])
|
37
|
+
elsif !options['user'].nil? && !options['token'].nil?
|
38
|
+
# access token
|
39
|
+
success, msg = store_access_token(url, options['user'], options['token'], options['insecure'])
|
40
|
+
elsif !options['refresh_token'].nil? && !options['user'].nil?
|
41
|
+
# refresh token
|
42
|
+
success, msg = store_refresh_token(url, options['refresh_token'], true, options['user'], options['insecure'])
|
43
|
+
# TODO: we should login with the refreshtoken here
|
44
|
+
elsif !options['refresh_token'].nil?
|
45
|
+
success, msg = login_refreshtoken(url, options)
|
46
|
+
else
|
47
|
+
puts 'Please run `inspec compliance login` with options --token or --refresh_token and --user'
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
|
23
51
|
if success
|
24
52
|
puts 'Successfully authenticated'
|
25
53
|
else
|
@@ -29,7 +57,8 @@ module Compliance
|
|
29
57
|
|
30
58
|
desc 'profiles', 'list all available profiles in Chef Compliance'
|
31
59
|
def profiles
|
32
|
-
|
60
|
+
config = Compliance::Configuration.new
|
61
|
+
profiles = Compliance::API.profiles(config)
|
33
62
|
if !profiles.empty?
|
34
63
|
# iterate over profiles
|
35
64
|
headline('Available profiles:')
|
@@ -56,6 +85,11 @@ module Compliance
|
|
56
85
|
option :overwrite, type: :boolean, default: false,
|
57
86
|
desc: 'Overwrite existing profile on Chef Compliance.'
|
58
87
|
def upload(path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, PerceivedComplexity
|
88
|
+
unless File.exist?(path)
|
89
|
+
puts "Directory #{path} does not exist."
|
90
|
+
exit 1
|
91
|
+
end
|
92
|
+
|
59
93
|
o = options.dup
|
60
94
|
configure_logger(o)
|
61
95
|
# check the profile, we only allow to upload valid profiles
|
@@ -88,7 +122,7 @@ module Compliance
|
|
88
122
|
|
89
123
|
# check that the profile is not uploaded already,
|
90
124
|
# confirm upload to the user (overwrite with --force)
|
91
|
-
if Compliance::API.exist?("#{owner}/#{profile_name}") && !options['overwrite']
|
125
|
+
if Compliance::API.exist?(config, "#{owner}/#{profile_name}") && !options['overwrite']
|
92
126
|
error.call('Profile exists on the server, use --overwrite')
|
93
127
|
end
|
94
128
|
|
@@ -111,11 +145,9 @@ module Compliance
|
|
111
145
|
puts "Start upload to #{owner}/#{profile_name}"
|
112
146
|
pname = ERB::Util.url_encode(profile_name)
|
113
147
|
|
114
|
-
|
115
|
-
|
148
|
+
puts 'Uploading to Chef Compliance'
|
149
|
+
success, msg = Compliance::API.upload(config, owner, pname, archive_path)
|
116
150
|
|
117
|
-
puts "Uploading to #{url}"
|
118
|
-
success, msg = Compliance::API.post_file(url, config['token'], '', archive_path, config['insecure'])
|
119
151
|
if success
|
120
152
|
puts 'Successfully uploaded profile'
|
121
153
|
else
|
@@ -126,7 +158,8 @@ module Compliance
|
|
126
158
|
|
127
159
|
desc 'version', 'displays the version of the Chef Compliance server'
|
128
160
|
def version
|
129
|
-
|
161
|
+
config = Compliance::Configuration.new
|
162
|
+
info = Compliance::API.version(config['server'], config['insecure'])
|
130
163
|
if !info.nil? && info['version']
|
131
164
|
puts "Chef Compliance version: #{info['version']}"
|
132
165
|
else
|
@@ -136,12 +169,98 @@ module Compliance
|
|
136
169
|
|
137
170
|
desc 'logout', 'user logout from Chef Compliance'
|
138
171
|
def logout
|
139
|
-
|
172
|
+
config = Compliance::Configuration.new
|
173
|
+
unless config.supported?(:oidc) || config['token'].nil?
|
174
|
+
config = Compliance::Configuration.new
|
175
|
+
url = "#{config['server']}/logout"
|
176
|
+
Compliance::API.post(url, config['token'], config['insecure'], !config.supported?(:oidc))
|
177
|
+
end
|
178
|
+
|
179
|
+
success = config.destroy
|
180
|
+
|
181
|
+
if success
|
140
182
|
puts 'Successfully logged out'
|
141
183
|
else
|
142
184
|
puts 'Could not log out'
|
143
185
|
end
|
144
186
|
end
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def login_refreshtoken(url, options)
|
191
|
+
success, msg, access_token = Compliance::API.post_refresh_token(url, options['refresh_token'], options['insecure'])
|
192
|
+
if success
|
193
|
+
config = Compliance::Configuration.new
|
194
|
+
config['server'] = url
|
195
|
+
config['token'] = access_token
|
196
|
+
config['insecure'] = options['insecure']
|
197
|
+
config['version'] = Compliance::API.version(url, options['insecure'])
|
198
|
+
config.store
|
199
|
+
end
|
200
|
+
|
201
|
+
[success, msg]
|
202
|
+
end
|
203
|
+
|
204
|
+
def login_legacy(url, username, password, insecure)
|
205
|
+
config = Compliance::Configuration.new
|
206
|
+
success, data = Compliance::API.legacy_login_post(url+'/oauth/token', username, password, insecure)
|
207
|
+
if !data.nil?
|
208
|
+
tokendata = JSON.parse(data)
|
209
|
+
if tokendata['access_token']
|
210
|
+
config['server'] = url
|
211
|
+
config['user'] = username
|
212
|
+
config['token'] = tokendata['access_token']
|
213
|
+
config['insecure'] = insecure
|
214
|
+
config['version'] = Compliance::API.version(url, insecure)
|
215
|
+
config.store
|
216
|
+
success = true
|
217
|
+
msg = 'Successfully authenticated'
|
218
|
+
else
|
219
|
+
msg = 'Reponse does not include a token'
|
220
|
+
end
|
221
|
+
else
|
222
|
+
msg = "Authentication failed for Server: #{url}"
|
223
|
+
end
|
224
|
+
[success, msg]
|
225
|
+
end
|
226
|
+
|
227
|
+
# saves a user access token (limited time)
|
228
|
+
def store_access_token(url, user, token, insecure)
|
229
|
+
config = Compliance::Configuration.new
|
230
|
+
config['server'] = url
|
231
|
+
config['insecure'] = insecure
|
232
|
+
config['user'] = user
|
233
|
+
config['token'] = token
|
234
|
+
config['version'] = Compliance::API.version(url, insecure)
|
235
|
+
config.store
|
236
|
+
|
237
|
+
[true, 'access token stored']
|
238
|
+
end
|
239
|
+
|
240
|
+
# saves the a user refresh token supplied by the user
|
241
|
+
def store_refresh_token(url, refresh_token, verify, user, insecure)
|
242
|
+
config = Compliance::Configuration.new
|
243
|
+
config['server'] = url
|
244
|
+
config['refresh_token'] = refresh_token
|
245
|
+
config['user'] = user
|
246
|
+
config['insecure'] = insecure
|
247
|
+
config['version'] = Compliance::API.version(url, insecure)
|
248
|
+
|
249
|
+
if !verify
|
250
|
+
config.store
|
251
|
+
success = true
|
252
|
+
msg = 'refresh token stored'
|
253
|
+
else
|
254
|
+
success, msg, access_token = Compliance::API.post_refresh_token(url, refresh_token, insecure)
|
255
|
+
if success
|
256
|
+
config['token'] = access_token
|
257
|
+
config.store
|
258
|
+
msg = 'token verified and stored'
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
[success, msg]
|
263
|
+
end
|
145
264
|
end
|
146
265
|
|
147
266
|
# register the subcommand to Inspec CLI registry
|
@@ -47,7 +47,49 @@ module Compliance
|
|
47
47
|
|
48
48
|
# deletes data
|
49
49
|
def destroy
|
50
|
-
File.
|
50
|
+
if File.exist?(@config_file)
|
51
|
+
File.delete(@config_file)
|
52
|
+
else
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# return if the (stored) api version does not support a certain feature
|
58
|
+
def supported?(feature)
|
59
|
+
sup = version_with_support(feature)
|
60
|
+
|
61
|
+
# we do not know the version, therefore we do not know if its possible to use the feature
|
62
|
+
return if self['version'].nil? || self['version']['version'].nil?
|
63
|
+
|
64
|
+
if sup.is_a?(Array)
|
65
|
+
Gem::Version.new(self['version']['version']) >= sup[0] &&
|
66
|
+
Gem::Version.new(self['version']['version']) < sup[1]
|
67
|
+
else
|
68
|
+
Gem::Version.new(self['version']['version']) >= sup
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# exit 1 if the version of compliance that we're working with doesn't support odic
|
73
|
+
def legacy_check!(feature)
|
74
|
+
if !supported?(feature)
|
75
|
+
puts "This feature (#{feature}) is not available for legacy installations."
|
76
|
+
puts 'Please upgrade to a recent version of Chef Compliance.'
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# for a feature, returns either:
|
84
|
+
# - a version v0: v supports v0 iff v0 <= v
|
85
|
+
# - an array [v0, v1] of two versions: v supports [v0, v1] iff v0 <= v < v1
|
86
|
+
def version_with_support(feature)
|
87
|
+
case feature.to_sym
|
88
|
+
when :oidc
|
89
|
+
Gem::Version.new('0.16.19')
|
90
|
+
else
|
91
|
+
Gem::Version.new('0.0.0')
|
92
|
+
end
|
51
93
|
end
|
52
94
|
end
|
53
95
|
end
|