class InspecPlugins::Compliance::API

API Implementation does not hold any state by itself, everything will be stored in local Configuration store

Public Class Methods

authenticate_login_using_version_api(url, api_token, insecure) click to toggle source

Use API access token to validate login using version API

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 133
def self.authenticate_login_using_version_api(url, api_token, insecure)
  uri = URI.parse("#{url}/version")
  req = Net::HTTP::Get.new(uri.path)
  req["api-token"] = api_token
  response = InspecPlugins::Compliance::HTTP.send_request(uri, req, insecure)

  if response.code == "200"
    msg = "Successfully Logged In"
    success = true
  else
    success = false
    msg = "Failed to authenticate to #{url} \n\Response code: #{response.code}\nBody: #{response.body}"
  end
  [success, msg]
end
exist?(config, profile) click to toggle source

verifies that a profile exists

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 93
def self.exist?(config, profile)
  _msg, profiles = InspecPlugins::Compliance::API.profiles(config, profile)
  !profiles.empty?
end
get_headers(config) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 170
def self.get_headers(config)
  token = get_token(config)
  headers = { "chef-delivery-enterprise" => config["automate"]["ent"] }
  if config["automate"]["token_type"] == "dctoken"
    headers["x-data-collector-token"] = token
  else
    headers["chef-delivery-user"] = config["user"]
    headers["chef-delivery-token"] = token
  end
  headers
end
get_token(config) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 182
def self.get_token(config)
  return config["token"] unless config["refresh_token"]

  _success, _msg, token = get_token_via_refresh_token(config["server"], config["refresh_token"], config["insecure"])
  token
end
get_token_via_password(url, username, password, insecure) click to toggle source

Use username and password to get an API access token

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 150
  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 = InspecPlugins::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}"
    end

    [success, msg, access_token]
  end
get_token_via_refresh_token(url, refresh_token, insecure) click to toggle source

Use username and refresh_token to get an API access token

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 106
  def self.get_token_via_refresh_token(url, refresh_token, insecure)
    uri = URI.parse("#{url}/login")
    req = Net::HTTP::Post.new(uri.path)
    req.body = { token: refresh_token }.to_json
    access_token = nil
    response = InspecPlugins::Compliance::HTTP.send_request(uri, req, insecure)
    data = response.body
    if response.code == "200"
      begin
        tokendata = JSON.parse(data)
        access_token = tokendata["access_token"]
        msg = "Successfully fetched API access token"
        success = true
      rescue JSON::ParserError => e
        success = false
        msg = e.message
      end
    else
      success = false
      msg = "Failed to authenticate to #{url} \n\
Response code: #{response.code}\n  Body: #{response.body}"
    end

    [success, msg, access_token]
  end
is_automate2_server?(config) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 218
def self.is_automate2_server?(config)
  config["server_type"] == "automate2"
end
profile_split(profile) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 193
def self.profile_split(profile)
  owner, id = profile.split("/")
  id, version = id.split("#")
  [owner, id, version]
end
profiles(config, profile_filter = nil) click to toggle source

return all compliance profiles available for the user the user is either specified in the options hash or by default the username of the account is used that is logged in

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 25
def self.profiles(config, profile_filter = nil) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength
  owner = config["owner"] || config["user"]
  if is_automate2_server?(config)
    url = "#{config["server"]}/compliance/profiles/search"
  else
    raise ServerConfigurationMissing
  end

  headers = get_headers(config)
  if profile_filter
    _owner, id, ver = profile_split(profile_filter)
  else
    id, ver = nil
  end

  body = { owner: owner, name: id }.to_json
  response = InspecPlugins::Compliance::HTTP.post_with_headers(url, headers, body, config["insecure"])

  data = response.body
  response_code = response.code
  case response_code
  when "200"
    msg = "success"
    profiles = JSON.parse(data)
    # iterate over profiles

    mapped_profiles = []
    profiles["profiles"].each do |p|
      mapped_profiles << p
    end

    # filter by name and version if they were specified in profile_filter
    mapped_profiles.select! do |p|
      (!ver || p["version"] == ver) && (!id || p["name"] == id)
    end
    [msg, mapped_profiles]
  when "401"
    msg = "401 Unauthorized. Please check your token."
    [msg, []]
  else
    msg = "An unexpected error occurred (HTTP #{response_code}): #{response.message}"
    [msg, []]
  end
end
sanitize_profile_name(profile) click to toggle source

returns a parsed url for `admin/profile` or `compliance://admin/profile`

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 200
def self.sanitize_profile_name(profile)
  if URI(profile).scheme == "compliance"
    uri = URI(profile)
  else
    uri = URI("compliance://#{profile}")
  end
  uri.to_s.sub(%r{^compliance:\/\/}, "")
end
server_version_from_config(config) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 209
def self.server_version_from_config(config)
  # Automate versions 0.8.x and later will have a "version" key in the config
  # that looks like: "version":{"api":"compliance","version":"0.8.24"}
  return nil unless config.key?("version")
  return nil unless config["version"].is_a?(Hash)

  config["version"]["version"]
end
target_url(config, profile) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 189
def self.target_url(config, profile)
  "#{config["server"]}/compliance/profiles/tar"
end
upload(config, owner, profile_name, archive_path) click to toggle source
# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 98
def self.upload(config, owner, profile_name, archive_path)
  url = "#{config["server"]}/compliance/profiles?owner=#{owner}"
  headers = get_headers(config)
  res = InspecPlugins::Compliance::HTTP.post_multipart_file(url, headers, archive_path, config["insecure"])
  [res.is_a?(Net::HTTPSuccess), res.body]
end
version(config) click to toggle source

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)

# File lib/plugins/inspec-compliance/lib/inspec-compliance/api.rb, line 73
def self.version(config)
  url = config["server"]
  insecure = config["insecure"]

  raise ServerConfigurationMissing if url.nil?

  headers = get_headers(config)
  response = InspecPlugins::Compliance::HTTP.get(url + "/version", headers, insecure)
  return {} if response.code == "404"

  data = response.body
  return {} if data.nil? || data.empty?

  parsed = JSON.parse(data)
  return {} unless parsed.key?("version") && !parsed["version"].empty?

  parsed
end