From bdf5eae15e9ddcd7939c45aeb0fdb629fc8761f2 Mon Sep 17 00:00:00 2001 From: Victoria Jeffrey Date: Tue, 15 Nov 2016 14:19:39 -0500 Subject: [PATCH 1/3] Extend inspec compliance cli to support automate backend Signed-off-by: Victoria Jeffrey --- lib/bundles/inspec-compliance/api.rb | 26 +++++--- lib/bundles/inspec-compliance/cli.rb | 75 +++++++++++++++++++---- lib/bundles/inspec-compliance/http.rb | 24 ++++++-- lib/bundles/inspec-compliance/target.rb | 23 +++++-- lib/fetchers/url.rb | 16 ++++- test/functional/inspec_compliance_test.rb | 7 +++ 6 files changed, 138 insertions(+), 33 deletions(-) diff --git a/lib/bundles/inspec-compliance/api.rb b/lib/bundles/inspec-compliance/api.rb index 3c91fc40a..e28143450 100755 --- a/lib/bundles/inspec-compliance/api.rb +++ b/lib/bundles/inspec-compliance/api.rb @@ -8,12 +8,12 @@ require 'uri' module Compliance # API Implementation does not hold any state by itself, # everything will be stored in local Configuration store - class API + class API # rubocop:disable Metrics/ClassLength # return all compliance profiles available for the user def self.profiles(config) - url = "#{config['server']}/user/compliance" + config['automate'][0] ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/user/compliance" # TODO, api should not be dependent on .supported? - response = Compliance::HTTP.get(url, config['token'], config['insecure'], !config.supported?(:oidc)) + response = Compliance::HTTP.get(url, config['token'], config['insecure'], config['user'], !config.supported?(:oidc), config['automate'], config['ent']) data = response.body response_code = response.code case response_code @@ -21,11 +21,17 @@ module Compliance msg = 'success' profiles = JSON.parse(data) # iterate over profiles - mapped_profiles = profiles.map do |owner, ps| - ps.keys.map do |name| - { org: owner, name: name } - end - end.flatten + if config['automate'][0] + mapped_profiles = profiles.map do |owner, ps| + { org: ps['owner_id'], name: owner } + end.flatten + else + mapped_profiles = profiles.map do |owner, ps| + ps.keys.map do |name| + { org: owner, name: name } + end + end.flatten + end return msg, mapped_profiles when '401' msg = '401 Unauthorized. Please check your token.' @@ -69,8 +75,8 @@ Please login using `inspec compliance login https://compliance.test --user admin def self.upload(config, owner, profile_name, archive_path) # upload the tar to Chef Compliance - url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar" - res = Compliance::HTTP.post_file(url, config['token'], archive_path, config['insecure'], !config.supported?(:oidc)) + config['automate'][0] ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar" + res = Compliance::HTTP.post_file(url, config['token'], config['user'], archive_path, config['insecure'], !config.supported?(:oidc), config['automate'], config['ent']) [res.is_a?(Net::HTTPSuccess), res.body] end diff --git a/lib/bundles/inspec-compliance/cli.rb b/lib/bundles/inspec-compliance/cli.rb index afbbeb830..9a9bfaf6f 100644 --- a/lib/bundles/inspec-compliance/cli.rb +++ b/lib/bundles/inspec-compliance/cli.rb @@ -36,6 +36,7 @@ module Compliance options['server'] = server url = options['server'] + options['apipath'] + if !options['user'].nil? && !options['password'].nil? # username / password _success, msg = login_username_password(url, options['user'], options['password'], options['insecure']) @@ -56,6 +57,33 @@ module Compliance puts '', msg end + desc "login_automate SERVER --user='USER' --ent='ENT' --dctoken or --usertoken='TOKEN'", 'Log in to an Automate SERVER' + option :dctoken, type: :string, + desc: 'Data Collector token' + option :usertoken, type: :string, + desc: 'Automate user token' + option :user, type: :string, + desc: 'Automate username' + option :ent, type: :string, + desc: 'Enterprise for Chef Automate reporting' + def login_automate(server) # rubocop:disable Metrics/AbcSize + options['server'] = server + url = options['server'] + '/compliance/profiles' + + if url && !options['user'].nil? && !options['ent'].nil? + if !options['dctoken'].nil? || !options['usertoken'].nil? + msg = login_automate_config(url, options['user'], options['dctoken'], options['usertoken'], options['ent']) + else + puts "Please specify a token using --dctoken='DATA_COLLECTOR_TOKEN' or usertoken='AUTOMATE_TOKEN' " + exit 1 + end + else + puts "Please login to your automate instance using 'inspec compliance automate SERVER --user AUTOMATE_USER --ent AUTOMATE_ENT --dctoken DC_TOKEN or --usertoken USER_TOKEN' " + exit 1 + end + puts '', msg + end + desc 'profiles', 'list all available profiles in Chef Compliance' def profiles config = Compliance::Configuration.new @@ -79,10 +107,8 @@ module Compliance def exec(*tests) config = Compliance::Configuration.new return if !loggedin(config) - # iterate over tests and add compliance scheme tests = tests.map { |t| 'compliance://' + t } - # execute profile from inspec exec implementation diagnose run_tests(tests, opts) @@ -154,7 +180,8 @@ module Compliance puts "Start upload to #{owner}/#{profile_name}" pname = ERB::Util.url_encode(profile_name) - puts 'Uploading to Chef Compliance' + config['automate'] ? upload_msg = 'Uploading to Chef Automate' : upload_msg = 'Uploading to Chef Compliance' + puts upload_msg success, msg = Compliance::API.upload(config, owner, pname, archive_path) if success @@ -169,24 +196,27 @@ module Compliance desc 'version', 'displays the version of the Chef Compliance server' def version config = Compliance::Configuration.new - info = Compliance::API.version(config['server'], config['insecure']) - if !info.nil? && info['version'] - puts "Chef Compliance version: #{info['version']}" + if config['automate'] + puts 'Version not available when logged in with Automate.' else - puts 'Could not determine server version.' - exit 1 + info = Compliance::API.version(config['server'], config['insecure']) + if !info.nil? && info['version'] + puts "Chef Compliance version: #{info['version']}" + else + puts 'Could not determine server version.' + exit 1 + end end end desc 'logout', 'user logout from Chef Compliance' def logout config = Compliance::Configuration.new - unless config.supported?(:oidc) || config['token'].nil? + unless config.supported?(:oidc) || config['token'].nil? || config['automate'] config = Compliance::Configuration.new url = "#{config['server']}/logout" Compliance::API.post(url, config['token'], config['insecure'], !config.supported?(:oidc)) end - success = config.destroy if success @@ -198,6 +228,29 @@ module Compliance private + def login_automate_config(url, user, dctoken, usertoken, ent) + config = Compliance::Configuration.new + config['server'] = url + config['ent'] = ent + config['user'] = user + + # determine token method being used + if !dctoken.nil? + config['token'] = dctoken + token_type = 'dctoken' + token_msg = 'data collector token' + else + config['token'] = usertoken + token_type = 'usertoken' + token_msg = 'automate user token' + end + + config['automate'] = [true, token_type] + config.store + msg = "You have logged into your automate instance: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}" + msg + end + def login_refreshtoken(url, options) success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure']) if success @@ -267,7 +320,7 @@ module Compliance def loggedin(config) serverknown = !config['server'].nil? - puts 'You need to login first with `inspec compliance login`' if !serverknown + puts 'You need to login first with `inspec compliance login` or `inspec compliance automate`' if !serverknown serverknown end end diff --git a/lib/bundles/inspec-compliance/http.rb b/lib/bundles/inspec-compliance/http.rb index 7918cfc08..c99150756 100644 --- a/lib/bundles/inspec-compliance/http.rb +++ b/lib/bundles/inspec-compliance/http.rb @@ -9,13 +9,21 @@ module Compliance # implements a simple http abstraction on top of Net::HTTP class HTTP # generic get requires - def self.get(url, token, insecure, basic_auth = false) + def self.get(url, token, insecure, user, basic_auth = false, automate = false, ent = nil) # rubocop:disable Metrics/ParameterLists uri = URI.parse(url) req = Net::HTTP::Get.new(uri.path) return send_request(uri, req, insecure) if token.nil? - if basic_auth + if automate[0] + req.add_field('chef-delivery-enterprise', ent) + if automate[1] == 'dctoken' + req.add_field('x-data-collector-token', token) + else + req.add_field('chef-delivery-user', user) + req.add_field('chef-delivery-token', token) + end + elsif basic_auth req.basic_auth(token, '') else req['Authorization'] = "Bearer #{token}" @@ -39,7 +47,7 @@ module Compliance end # post a file - def self.post_file(url, token, file_path, insecure, basic_auth = false) + def self.post_file(url, token, user, file_path, insecure, basic_auth = false, automate = false, ent = nil) # rubocop:disable Metrics/ParameterLists uri = URI.parse(url) fail "Unable to parse URL: #{url}" if uri.nil? || uri.host.nil? http = Net::HTTP.new(uri.host, uri.port) @@ -49,7 +57,15 @@ module Compliance http.verify_mode = OpenSSL::SSL::VERIFY_NONE if insecure req = Net::HTTP::Post.new(uri.path) - if basic_auth + if automate[0] + req.add_field('chef-delivery-enterprise', ent) + if automate[1] == 'dctoken' + req.add_field('x-data-collector-token', token) + else + req.add_field('chef-delivery-user', user) + req.add_field('chef-delivery-token', token) + end + elsif basic_auth req.basic_auth token, '' else req['Authorization'] = "Bearer #{token}" diff --git a/lib/bundles/inspec-compliance/target.rb b/lib/bundles/inspec-compliance/target.rb index 6954471ab..cf8073e2b 100644 --- a/lib/bundles/inspec-compliance/target.rb +++ b/lib/bundles/inspec-compliance/target.rb @@ -13,8 +13,7 @@ module Compliance class Fetcher < Fetchers::Url name 'compliance' priority 500 - - def self.resolve(target) + def self.resolve(target) # rubocop:disable PerceivedComplexity uri = if target.is_a?(String) && URI(target).scheme == 'compliance' URI(target) elsif target.respond_to?(:key?) && target.key?(:compliance) @@ -26,14 +25,21 @@ module Compliance # check if we have a compliance token config = Compliance::Configuration.new if config['token'].nil? + if config['automate'][0] + server = 'automate' + msg = 'inspec compliance automate https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --usertoken USERTOKEN' + else + server = 'compliance' + msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' " + end fail Inspec::FetcherFailure, < '.zip', 'application/zip' => '.zip', @@ -126,7 +126,19 @@ module Fetchers Inspec::Log.debug("Fetching URL: #{@target}") http_opts = {} http_opts['ssl_verify_mode'.to_sym] = OpenSSL::SSL::VERIFY_NONE if @insecure - http_opts['Authorization'] = "Bearer #{@token}" if @token + if @config + if @config['automate'] + automate = true + http_opts['chef-delivery-enterprise'] = @config['ent'] + if @config['automate'][1] == 'dctoken' + http_opts['x-data-collector-token'] = @config['token'] + else + http_opts['chef-delivery-user'] = @config['user'] + http_opts['chef-delivery-token'] = @config['token'] + end + end + end + http_opts['Authorization'] = "Bearer #{@token}" if @token && automate.nil? remote = open(@target, http_opts) @archive_type = file_type_from_remote(remote) # side effect :( archive = Tempfile.new(['inspec-dl-', @archive_type]) diff --git a/test/functional/inspec_compliance_test.rb b/test/functional/inspec_compliance_test.rb index 11ebdf9d1..8b0834529 100644 --- a/test/functional/inspec_compliance_test.rb +++ b/test/functional/inspec_compliance_test.rb @@ -35,6 +35,13 @@ describe 'inspec compliance' do out.stdout.must_include 'Please run `inspec compliance login SERVER` with options' end + it 'automate with missing parameters' do + out = inspec('compliance login_automate http://example.com') + out.exit_status.must_equal 1 + #TODO: inspec should really use stderr for errors + out.stdout.must_include 'Please login to your automate instance using' + end + it 'inspec compliance profiles without authentication' do out = inspec('compliance profile') out.stdout.must_include 'You need to login first with `inspec compliance login`' From 60009b292bb4cf0e037ca30fbddf8979fbdd6c74 Mon Sep 17 00:00:00 2001 From: Victoria Jeffrey Date: Tue, 29 Nov 2016 09:35:16 -0500 Subject: [PATCH 2/3] address comments Signed-off-by: Victoria Jeffrey --- lib/bundles/inspec-compliance/api.rb | 10 +++++----- lib/bundles/inspec-compliance/cli.rb | 22 ++++++++++++++-------- lib/bundles/inspec-compliance/http.rb | 16 ++++++++-------- lib/bundles/inspec-compliance/target.rb | 6 +++--- lib/fetchers/url.rb | 10 +++++----- 5 files changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/bundles/inspec-compliance/api.rb b/lib/bundles/inspec-compliance/api.rb index e28143450..966537443 100755 --- a/lib/bundles/inspec-compliance/api.rb +++ b/lib/bundles/inspec-compliance/api.rb @@ -11,9 +11,9 @@ module Compliance class API # rubocop:disable Metrics/ClassLength # return all compliance profiles available for the user def self.profiles(config) - config['automate'][0] ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/user/compliance" + config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/user/compliance" # TODO, api should not be dependent on .supported? - response = Compliance::HTTP.get(url, config['token'], config['insecure'], config['user'], !config.supported?(:oidc), config['automate'], config['ent']) + response = Compliance::HTTP.get(url, config['token'], config['insecure'], config['user'], !config.supported?(:oidc), config['automate'], config['server_type']) data = response.body response_code = response.code case response_code @@ -21,7 +21,7 @@ module Compliance msg = 'success' profiles = JSON.parse(data) # iterate over profiles - if config['automate'][0] + if config['server_type'] == 'automate' mapped_profiles = profiles.map do |owner, ps| { org: ps['owner_id'], name: owner } end.flatten @@ -75,8 +75,8 @@ Please login using `inspec compliance login https://compliance.test --user admin def self.upload(config, owner, profile_name, archive_path) # upload the tar to Chef Compliance - config['automate'][0] ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar" - res = Compliance::HTTP.post_file(url, config['token'], config['user'], archive_path, config['insecure'], !config.supported?(:oidc), config['automate'], config['ent']) + config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar" + res = Compliance::HTTP.post_file(url, config['token'], config['user'], archive_path, config['insecure'], !config.supported?(:oidc), config['automate'], config['server_type']) [res.is_a?(Net::HTTPSuccess), res.body] end diff --git a/lib/bundles/inspec-compliance/cli.rb b/lib/bundles/inspec-compliance/cli.rb index 9a9bfaf6f..aff9f816d 100644 --- a/lib/bundles/inspec-compliance/cli.rb +++ b/lib/bundles/inspec-compliance/cli.rb @@ -78,7 +78,7 @@ module Compliance exit 1 end else - puts "Please login to your automate instance using 'inspec compliance automate SERVER --user AUTOMATE_USER --ent AUTOMATE_ENT --dctoken DC_TOKEN or --usertoken USER_TOKEN' " + puts "Please login to your automate instance using 'inspec compliance login_automate SERVER --user AUTOMATE_USER --ent AUTOMATE_ENT --dctoken DC_TOKEN or --usertoken USER_TOKEN' " exit 1 end puts '', msg @@ -180,7 +180,7 @@ module Compliance puts "Start upload to #{owner}/#{profile_name}" pname = ERB::Util.url_encode(profile_name) - config['automate'] ? upload_msg = 'Uploading to Chef Automate' : upload_msg = 'Uploading to Chef Compliance' + config['server_type'] == 'automate' ? upload_msg = 'Uploading to Chef Automate' : upload_msg = 'Uploading to Chef Compliance' puts upload_msg success, msg = Compliance::API.upload(config, owner, pname, archive_path) @@ -196,7 +196,7 @@ module Compliance desc 'version', 'displays the version of the Chef Compliance server' def version config = Compliance::Configuration.new - if config['automate'] + if config['server_type'] == 'automate' puts 'Version not available when logged in with Automate.' else info = Compliance::API.version(config['server'], config['insecure']) @@ -212,12 +212,14 @@ module Compliance desc 'logout', 'user logout from Chef Compliance' def logout config = Compliance::Configuration.new - unless config.supported?(:oidc) || config['token'].nil? || config['automate'] + unless config.supported?(:oidc) || config['token'].nil? || config['server_type'] == 'automate' config = Compliance::Configuration.new url = "#{config['server']}/logout" Compliance::API.post(url, config['token'], config['insecure'], !config.supported?(:oidc)) end success = config.destroy + config['token'] = '' + config['server'] = '' if success puts 'Successfully logged out' @@ -230,9 +232,11 @@ module Compliance def login_automate_config(url, user, dctoken, usertoken, ent) config = Compliance::Configuration.new - config['server'] = url - config['ent'] = ent config['user'] = user + config['server'] = url + config['automate'] = {} + config['automate']['ent'] = ent + config['server_type'] = 'automate' # determine token method being used if !dctoken.nil? @@ -245,7 +249,7 @@ module Compliance token_msg = 'automate user token' end - config['automate'] = [true, token_type] + config['automate']['token_type'] = token_type config.store msg = "You have logged into your automate instance: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}" msg @@ -259,6 +263,7 @@ module Compliance config['token'] = access_token config['insecure'] = options['insecure'] config['version'] = Compliance::API.version(url, options['insecure']) + config['server_type'] = 'compliance' config.store end @@ -274,6 +279,7 @@ module Compliance config['token'] = api_token config['insecure'] = insecure config['version'] = Compliance::API.version(url, insecure) + config['server_type'] = 'compliance' config.store success = true end @@ -320,7 +326,7 @@ module Compliance def loggedin(config) serverknown = !config['server'].nil? - puts 'You need to login first with `inspec compliance login` or `inspec compliance automate`' if !serverknown + puts 'You need to login first with `inspec compliance login` or `inspec compliance login_automate`' if !serverknown serverknown end end diff --git a/lib/bundles/inspec-compliance/http.rb b/lib/bundles/inspec-compliance/http.rb index c99150756..32287597b 100644 --- a/lib/bundles/inspec-compliance/http.rb +++ b/lib/bundles/inspec-compliance/http.rb @@ -9,15 +9,15 @@ module Compliance # implements a simple http abstraction on top of Net::HTTP class HTTP # generic get requires - def self.get(url, token, insecure, user, basic_auth = false, automate = false, ent = nil) # rubocop:disable Metrics/ParameterLists + def self.get(url, token, insecure, user, basic_auth = false, automate = nil, server_type) # rubocop:disable Metrics/ParameterLists uri = URI.parse(url) req = Net::HTTP::Get.new(uri.path) return send_request(uri, req, insecure) if token.nil? - if automate[0] - req.add_field('chef-delivery-enterprise', ent) - if automate[1] == 'dctoken' + if server_type == 'automate' + req.add_field('chef-delivery-enterprise', automate['ent']) + if automate['token_type'] == 'dctoken' req.add_field('x-data-collector-token', token) else req.add_field('chef-delivery-user', user) @@ -47,7 +47,7 @@ module Compliance end # post a file - def self.post_file(url, token, user, file_path, insecure, basic_auth = false, automate = false, ent = nil) # rubocop:disable Metrics/ParameterLists + def self.post_file(url, token, user, file_path, insecure, basic_auth = false, automate = nil, server_type) # rubocop:disable Metrics/ParameterLists uri = URI.parse(url) fail "Unable to parse URL: #{url}" if uri.nil? || uri.host.nil? http = Net::HTTP.new(uri.host, uri.port) @@ -57,9 +57,9 @@ module Compliance http.verify_mode = OpenSSL::SSL::VERIFY_NONE if insecure req = Net::HTTP::Post.new(uri.path) - if automate[0] - req.add_field('chef-delivery-enterprise', ent) - if automate[1] == 'dctoken' + if server_type == 'automate' + req.add_field('chef-delivery-enterprise', automate['ent']) + if automate['token_type'] == 'dctoken' req.add_field('x-data-collector-token', token) else req.add_field('chef-delivery-user', user) diff --git a/lib/bundles/inspec-compliance/target.rb b/lib/bundles/inspec-compliance/target.rb index cf8073e2b..04cbb4797 100644 --- a/lib/bundles/inspec-compliance/target.rb +++ b/lib/bundles/inspec-compliance/target.rb @@ -25,9 +25,9 @@ module Compliance # check if we have a compliance token config = Compliance::Configuration.new if config['token'].nil? - if config['automate'][0] + if config['server_type'] == 'automate' server = 'automate' - msg = 'inspec compliance automate https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --usertoken USERTOKEN' + msg = 'inspec compliance login_automate https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --usertoken USERTOKEN' else server = 'compliance' msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' " @@ -54,7 +54,7 @@ EOF end def self.target_url(profile, config) - if config['automate'][0] + if config['server_type'] == 'automate' target = "#{config['server']}/#{profile}/tar" else owner, id = profile.split('/') diff --git a/lib/fetchers/url.rb b/lib/fetchers/url.rb index 787da5fb3..85078a4ef 100644 --- a/lib/fetchers/url.rb +++ b/lib/fetchers/url.rb @@ -127,18 +127,18 @@ module Fetchers http_opts = {} http_opts['ssl_verify_mode'.to_sym] = OpenSSL::SSL::VERIFY_NONE if @insecure if @config - if @config['automate'] - automate = true - http_opts['chef-delivery-enterprise'] = @config['ent'] - if @config['automate'][1] == 'dctoken' + if @config['server_type'] == 'automate' + http_opts['chef-delivery-enterprise'] = @config['automate']['ent'] + if @config['automate']['token_type'] == 'dctoken' http_opts['x-data-collector-token'] = @config['token'] else http_opts['chef-delivery-user'] = @config['user'] http_opts['chef-delivery-token'] = @config['token'] end + elsif @token + http_opts['Authorization'] = "Bearer #{@token}" end end - http_opts['Authorization'] = "Bearer #{@token}" if @token && automate.nil? remote = open(@target, http_opts) @archive_type = file_type_from_remote(remote) # side effect :( archive = Tempfile.new(['inspec-dl-', @archive_type]) From d8b512ec8cabb5bfcfc6e8944b3e1d816a3d7d07 Mon Sep 17 00:00:00 2001 From: Victoria Jeffrey Date: Wed, 30 Nov 2016 09:30:11 -0500 Subject: [PATCH 3/3] refactor headers retrieval into get_headers method Signed-off-by: Victoria Jeffrey --- lib/bundles/inspec-compliance/api.rb | 22 ++++++++++++++--- lib/bundles/inspec-compliance/cli.rb | 11 +++++---- lib/bundles/inspec-compliance/http.rb | 35 ++++++--------------------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/lib/bundles/inspec-compliance/api.rb b/lib/bundles/inspec-compliance/api.rb index 966537443..743a6d47f 100755 --- a/lib/bundles/inspec-compliance/api.rb +++ b/lib/bundles/inspec-compliance/api.rb @@ -12,8 +12,8 @@ module Compliance # return all compliance profiles available for the user def self.profiles(config) config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/user/compliance" - # TODO, api should not be dependent on .supported? - response = Compliance::HTTP.get(url, config['token'], config['insecure'], config['user'], !config.supported?(:oidc), config['automate'], config['server_type']) + headers = get_headers(config) + response = Compliance::HTTP.get(url, headers, config['insecure']) data = response.body response_code = response.code case response_code @@ -76,7 +76,8 @@ Please login using `inspec compliance login https://compliance.test --user admin def self.upload(config, owner, profile_name, archive_path) # upload the tar to Chef Compliance config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar" - res = Compliance::HTTP.post_file(url, config['token'], config['user'], archive_path, config['insecure'], !config.supported?(:oidc), config['automate'], config['server_type']) + headers = get_headers(config) + res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure']) [res.is_a?(Net::HTTPSuccess), res.body] end @@ -127,5 +128,20 @@ Please login using `inspec compliance login https://compliance.test --user admin [success, msg, access_token] end + + def self.get_headers(config) + if config['server_type'] == 'automate' + headers = { 'chef-delivery-enterprise' => config['automate']['ent'] } + if config['automate']['token_type'] == 'dctoken' + headers['x-data-collector-token'] = config['token'] + else + headers['chef-delivery-user'] = config['user'] + headers['chef-delivery-token'] = config['token'] + end + else + headers = { 'Authorization' => "Bearer #{config['token']}" } + end + headers + end end end diff --git a/lib/bundles/inspec-compliance/cli.rb b/lib/bundles/inspec-compliance/cli.rb index aff9f816d..9de9aecf5 100644 --- a/lib/bundles/inspec-compliance/cli.rb +++ b/lib/bundles/inspec-compliance/cli.rb @@ -66,13 +66,15 @@ module Compliance desc: 'Automate username' option :ent, type: :string, desc: 'Enterprise for Chef Automate reporting' + option :insecure, aliases: :k, type: :boolean, + desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers' def login_automate(server) # rubocop:disable Metrics/AbcSize options['server'] = server url = options['server'] + '/compliance/profiles' if url && !options['user'].nil? && !options['ent'].nil? if !options['dctoken'].nil? || !options['usertoken'].nil? - msg = login_automate_config(url, options['user'], options['dctoken'], options['usertoken'], options['ent']) + msg = login_automate_config(url, options['user'], options['dctoken'], options['usertoken'], options['ent'], options['insecure']) else puts "Please specify a token using --dctoken='DATA_COLLECTOR_TOKEN' or usertoken='AUTOMATE_TOKEN' " exit 1 @@ -218,8 +220,6 @@ module Compliance Compliance::API.post(url, config['token'], config['insecure'], !config.supported?(:oidc)) end success = config.destroy - config['token'] = '' - config['server'] = '' if success puts 'Successfully logged out' @@ -230,13 +230,14 @@ module Compliance private - def login_automate_config(url, user, dctoken, usertoken, ent) + def login_automate_config(url, user, dctoken, usertoken, ent, insecure) # rubocop:disable Metrics/ParameterLists config = Compliance::Configuration.new config['user'] = user config['server'] = url config['automate'] = {} config['automate']['ent'] = ent config['server_type'] = 'automate' + config['insecure'] = insecure # determine token method being used if !dctoken.nil? @@ -251,7 +252,7 @@ module Compliance config['automate']['token_type'] = token_type config.store - msg = "You have logged into your automate instance: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}" + msg = "Stored configuration for Chef Automate: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}" msg end diff --git a/lib/bundles/inspec-compliance/http.rb b/lib/bundles/inspec-compliance/http.rb index 32287597b..e2327783c 100644 --- a/lib/bundles/inspec-compliance/http.rb +++ b/lib/bundles/inspec-compliance/http.rb @@ -9,24 +9,13 @@ module Compliance # implements a simple http abstraction on top of Net::HTTP class HTTP # generic get requires - def self.get(url, token, insecure, user, basic_auth = false, automate = nil, server_type) # rubocop:disable Metrics/ParameterLists + def self.get(url, headers = nil, insecure) uri = URI.parse(url) req = Net::HTTP::Get.new(uri.path) - - return send_request(uri, req, insecure) if token.nil? - - if server_type == 'automate' - req.add_field('chef-delivery-enterprise', automate['ent']) - if automate['token_type'] == 'dctoken' - req.add_field('x-data-collector-token', token) - else - req.add_field('chef-delivery-user', user) - req.add_field('chef-delivery-token', token) + if !headers.nil? + headers.each do |key, value| + req.add_field(key, value) end - elsif basic_auth - req.basic_auth(token, '') - else - req['Authorization'] = "Bearer #{token}" end send_request(uri, req, insecure) end @@ -47,7 +36,7 @@ module Compliance end # post a file - def self.post_file(url, token, user, file_path, insecure, basic_auth = false, automate = nil, server_type) # rubocop:disable Metrics/ParameterLists + def self.post_file(url, headers, file_path, insecure) uri = URI.parse(url) fail "Unable to parse URL: #{url}" if uri.nil? || uri.host.nil? http = Net::HTTP.new(uri.host, uri.port) @@ -57,18 +46,8 @@ module Compliance http.verify_mode = OpenSSL::SSL::VERIFY_NONE if insecure req = Net::HTTP::Post.new(uri.path) - if server_type == 'automate' - req.add_field('chef-delivery-enterprise', automate['ent']) - if automate['token_type'] == 'dctoken' - req.add_field('x-data-collector-token', token) - else - req.add_field('chef-delivery-user', user) - req.add_field('chef-delivery-token', token) - end - elsif basic_auth - req.basic_auth token, '' - else - req['Authorization'] = "Bearer #{token}" + headers.each do |key, value| + req.add_field(key, value) end req.body_stream=File.open(file_path, 'rb')