inspec 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -2
  3. data/README.md +27 -2
  4. data/docs/resources.rst +49 -3
  5. data/inspec.gemspec +1 -1
  6. data/lib/bundles/inspec-compliance/README.md +2 -1
  7. data/lib/bundles/inspec-compliance/api.rb +60 -102
  8. data/lib/bundles/inspec-compliance/cli.rb +133 -14
  9. data/lib/bundles/inspec-compliance/configuration.rb +43 -1
  10. data/lib/bundles/inspec-compliance/http.rb +80 -0
  11. data/lib/bundles/inspec-compliance.rb +1 -0
  12. data/lib/inspec/metadata.rb +40 -27
  13. data/lib/inspec/objects/test.rb +2 -1
  14. data/lib/inspec/resource.rb +1 -0
  15. data/lib/inspec/rspec_json_formatter.rb +1 -1
  16. data/lib/inspec/runner.rb +19 -13
  17. data/lib/inspec/runner_rspec.rb +1 -1
  18. data/lib/inspec/version.rb +1 -1
  19. data/lib/matchers/matchers.rb +32 -13
  20. data/lib/resources/grub_conf.rb +186 -0
  21. data/lib/resources/json.rb +1 -1
  22. data/lib/resources/service.rb +9 -3
  23. data/lib/utils/base_cli.rb +2 -1
  24. data/lib/utils/hash_map.rb +37 -0
  25. data/test/functional/inspec_compliance_test.rb +60 -0
  26. data/test/functional/inspec_exec_test.rb +49 -10
  27. data/test/helper.rb +3 -0
  28. data/test/integration/default/compare_matcher_spec.rb +21 -0
  29. data/test/unit/metadata_test.rb +49 -23
  30. data/test/unit/mock/cmd/systemctl-show-all-dbus +6 -0
  31. data/test/unit/mock/files/grub.conf +21 -0
  32. data/test/unit/mock/profiles/resource-tiny/inspec.yml +10 -0
  33. data/test/unit/mock/profiles/resource-tiny/libraries/resource.rb +3 -0
  34. data/test/unit/mock/profiles/supported_inspec/inspec.yml +2 -0
  35. data/test/unit/mock/profiles/unsupported_inspec/inspec.yml +2 -0
  36. data/test/unit/profile_test.rb +1 -1
  37. data/test/unit/resources/grub_conf_test.rb +29 -0
  38. data/test/unit/resources/service_test.rb +9 -0
  39. data/test/unit/utils/hash_map_test.rb +63 -0
  40. metadata +26 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5fcd7959abdb3de12c11854323ad29f484f0dbda
4
- data.tar.gz: 79bbc6496cc71d18e02a9b05b4e6fbd3a5043315
3
+ metadata.gz: 3f8721f5a1366ca4d03be5b1cbfe3b8f61ba315f
4
+ data.tar.gz: 68a9c4ce7b72cde464e322d449ce8775f0afa6a8
5
5
  SHA512:
6
- metadata.gz: 4256867dac4a3a9978b4f5dfda36aba67f9d97461d9d074e694f048d51ae22cd2e835593524e5dcbcc9d3f87a34279cbcd295f6f66d0d372939c8daf5e73688f
7
- data.tar.gz: c6cc148cae574dd9dce10ad9781cb9e088fff6c52fdde9f3b40463d7166689e23923b539ba077a4d224e3611f33b0fd7c3b2799e9308590516a6231f536a78cc
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.18.0](https://github.com/chef/inspec/tree/0.18.0) (2016-04-09)
4
- [Full Changelog](https://github.com/chef/inspec/compare/v0.17.1...0.18.0)
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
- its('name') { should eq 'foo' }
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.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 login user password` - authentication against Chef 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 # rubocop:disable Metrics/ClassLength
12
- # logs into the server, retrieves a token and stores it locally
13
- def self.login(server, username, password, insecure, apipath)
14
- config = Compliance::Configuration.new
15
- config['server'] = "#{server}#{apipath}"
16
- url = "#{config['server']}/oauth/token"
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
- success, data = Compliance::API.post(url, username, password, insecure)
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
- tokendata = JSON.parse(data)
21
- if tokendata['access_token']
22
- config['user'] = username
23
- config['token'] = tokendata['access_token']
24
- config['insecure'] = insecure
25
- config.store
26
- success = true
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
- msg = "Authentication failed for Server: #{url}"
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
- def self.version
46
- config = Compliance::Configuration.new
47
- url = "#{config['server']}/version"
48
-
49
- _success, data = Compliance::API.get(url, nil, nil, config['insecure'])
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.get(url, username, password, insecure)
90
- uri = URI.parse(url)
91
- req = Net::HTTP::Get.new(uri.path)
92
- req.basic_auth username, password
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.send_request(uri, req, insecure)
131
- opts = {
132
- use_ssl: uri.scheme == 'https',
133
- }
134
- opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if insecure
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
- res = Net::HTTP.start(uri.host, uri.port, opts) {|http|
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 :user, type: :string, required: true,
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
- def login(server)
22
- success, msg = Compliance::API.login(server, options['user'], options['password'], options['insecure'], options['apipath'])
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
- profiles = Compliance::API.profiles
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
- # upload the tar to Chef Compliance
115
- url = "#{config['server']}/owners/#{owner}/compliance/#{pname}/tar"
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
- info = Compliance::API.version
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
- if Compliance::API.logout
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.delete(@config_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