2016-02-05 07:38:45 +00:00
|
|
|
# encoding: utf-8
|
|
|
|
# author: Christoph Hartmann
|
|
|
|
# author: Dominik Richter
|
|
|
|
|
|
|
|
require 'net/http'
|
|
|
|
require 'uri'
|
|
|
|
|
|
|
|
module Compliance
|
|
|
|
# API Implementation does not hold any state by itself,
|
|
|
|
# everything will be stored in local Configuration store
|
2016-11-15 19:19:39 +00:00
|
|
|
class API # rubocop:disable Metrics/ClassLength
|
2016-04-13 11:47:33 +00:00
|
|
|
# return all compliance profiles available for the user
|
|
|
|
def self.profiles(config)
|
2016-11-29 14:35:16 +00:00
|
|
|
config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/user/compliance"
|
2016-11-30 14:30:11 +00:00
|
|
|
headers = get_headers(config)
|
|
|
|
response = Compliance::HTTP.get(url, headers, config['insecure'])
|
2016-04-13 11:47:33 +00:00
|
|
|
data = response.body
|
2016-08-17 16:15:11 +00:00
|
|
|
response_code = response.code
|
|
|
|
case response_code
|
|
|
|
when '200'
|
|
|
|
msg = 'success'
|
2016-04-13 11:47:33 +00:00
|
|
|
profiles = JSON.parse(data)
|
|
|
|
# iterate over profiles
|
2016-11-29 14:35:16 +00:00
|
|
|
if config['server_type'] == 'automate'
|
2016-11-15 19:19:39 +00:00
|
|
|
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
|
2016-08-17 16:15:11 +00:00
|
|
|
return msg, mapped_profiles
|
|
|
|
when '401'
|
|
|
|
msg = '401 Unauthorized. Please check your token.'
|
|
|
|
return msg, []
|
2016-04-04 15:29:13 +00:00
|
|
|
else
|
2016-08-17 16:15:11 +00:00
|
|
|
msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
|
|
|
|
return msg, []
|
2016-04-04 15:29:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# return the server api version
|
|
|
|
# NB this method does not use Compliance::Configuration to allow for using
|
|
|
|
# it before we know the version (e.g. oidc or not)
|
|
|
|
def self.version(url, insecure)
|
2016-08-16 14:52:21 +00:00
|
|
|
if url.nil?
|
|
|
|
puts "
|
|
|
|
Server configuration information is missing.
|
|
|
|
Please login using `inspec compliance login https://compliance.test --user admin --insecure --token 'PASTE TOKEN HERE' `
|
|
|
|
"
|
|
|
|
else
|
|
|
|
response = Compliance::HTTP.get(url+'/version', nil, insecure)
|
|
|
|
data = response.body
|
|
|
|
end
|
2016-02-05 07:38:45 +00:00
|
|
|
if !data.nil?
|
|
|
|
JSON.parse(data)
|
|
|
|
else
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-02-05 10:06:00 +00:00
|
|
|
# verifies that a profile
|
2016-04-13 11:47:33 +00:00
|
|
|
def self.exist?(config, profile)
|
2016-08-18 16:34:09 +00:00
|
|
|
_msg, profiles = Compliance::API.profiles(config)
|
2016-02-05 10:06:00 +00:00
|
|
|
if !profiles.empty?
|
|
|
|
index = profiles.index { |p| "#{p[:org]}/#{p[:name]}" == profile }
|
|
|
|
!index.nil? && index >= 0
|
|
|
|
else
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-13 11:47:33 +00:00
|
|
|
def self.upload(config, owner, profile_name, archive_path)
|
|
|
|
# upload the tar to Chef Compliance
|
2016-11-29 14:35:16 +00:00
|
|
|
config['server_type'] == 'automate' ? url = "#{config['server']}/#{config['user']}" : url = "#{config['server']}/owners/#{owner}/compliance/#{profile_name}/tar"
|
2016-11-30 14:30:11 +00:00
|
|
|
headers = get_headers(config)
|
|
|
|
res = Compliance::HTTP.post_file(url, headers, archive_path, config['insecure'])
|
2016-04-13 11:47:33 +00:00
|
|
|
[res.is_a?(Net::HTTPSuccess), res.body]
|
2016-02-05 07:38:45 +00:00
|
|
|
end
|
|
|
|
|
2016-09-19 14:00:35 +00:00
|
|
|
# Use username and refresh_toke to get an API access token
|
|
|
|
def self.get_token_via_refresh_token(url, refresh_token, insecure)
|
2016-04-13 11:47:33 +00:00
|
|
|
uri = URI.parse("#{url}/login")
|
2016-03-23 13:32:10 +00:00
|
|
|
req = Net::HTTP::Post.new(uri.path)
|
2016-09-19 14:00:35 +00:00
|
|
|
req.body = { token: refresh_token }.to_json
|
2016-04-13 11:47:33 +00:00
|
|
|
access_token = nil
|
|
|
|
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
|
|
data = response.body
|
2016-09-19 14:00:35 +00:00
|
|
|
if response.code == '200'
|
2016-03-23 13:32:10 +00:00
|
|
|
begin
|
|
|
|
tokendata = JSON.parse(data)
|
|
|
|
access_token = tokendata['access_token']
|
2016-09-19 14:00:35 +00:00
|
|
|
msg = 'Successfully fetched API access token'
|
2016-03-23 13:32:10 +00:00
|
|
|
success = true
|
|
|
|
rescue JSON::ParserError => e
|
|
|
|
success = false
|
|
|
|
msg = e.message
|
|
|
|
end
|
|
|
|
else
|
|
|
|
success = false
|
2016-09-19 14:00:35 +00:00
|
|
|
msg = "Failed to authenticate to #{url} \n\
|
|
|
|
Response code: #{response.code}\n Body: #{response.body}"
|
|
|
|
end
|
|
|
|
|
|
|
|
[success, msg, access_token]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Use username and password to get an API access token
|
|
|
|
def self.get_token_via_password(url, username, password, insecure)
|
|
|
|
uri = URI.parse("#{url}/login")
|
|
|
|
req = Net::HTTP::Post.new(uri.path)
|
|
|
|
req.body = { userid: username, password: password }.to_json
|
|
|
|
access_token = nil
|
|
|
|
response = Compliance::HTTP.send_request(uri, req, insecure)
|
|
|
|
data = response.body
|
|
|
|
if response.code == '200'
|
|
|
|
access_token = data
|
|
|
|
msg = 'Successfully fetched an API access token valid for 12 hours'
|
|
|
|
success = true
|
|
|
|
else
|
|
|
|
success = false
|
|
|
|
msg = "Failed to authenticate to #{url} \n\
|
|
|
|
Response code: #{response.code}\n Body: #{response.body}"
|
2016-03-23 13:32:10 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
[success, msg, access_token]
|
|
|
|
end
|
2016-11-30 14:30:11 +00:00
|
|
|
|
|
|
|
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
|
2017-01-05 11:37:43 +00:00
|
|
|
|
|
|
|
def self.target_url(config, profile)
|
|
|
|
if config['server_type'] == 'automate'
|
|
|
|
target = "#{config['server']}/#{profile}/tar"
|
|
|
|
else
|
|
|
|
owner, id = profile.split('/')
|
|
|
|
target = "#{config['server']}/owners/#{owner}/compliance/#{id}/tar"
|
|
|
|
end
|
|
|
|
target
|
|
|
|
end
|
2016-02-05 07:38:45 +00:00
|
|
|
end
|
|
|
|
end
|