inspec 0.18.0 → 0.19.0
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
- 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
|