class Mongo::Crypt::KMS::Azure::CredentialsRetriever

This class retrieves Azure credentials using Azure metadata host. This should be used when the driver is used on the Azure environment.

@api private

Constants

DEFAULT_HOST

Default host to obtain Azure metadata.

Public Class Methods

fetch_access_token(extra_headers: {}, metadata_host: nil) click to toggle source

Fetches Azure credentials from Azure metadata host.

@param [Hash] extra_headers Extra headers to be passed to the

request. This is used for testing.

@param [String | nil] metadata_host Azure metadata host. This

is used for testing.

@return [ KMS::Azure::AccessToken ] Azure access token.

@raise [KMS::CredentialsNotFound] If credentials could not be found.

# File lib/mongo/crypt/kms/azure/credentials_retriever.rb, line 40
def self.fetch_access_token(extra_headers: {}, metadata_host: nil)
  uri, req = prepare_request(extra_headers, metadata_host)
  parsed_response = fetch_response(uri, req)
  Azure::AccessToken.new(
    parsed_response.fetch('access_token'),
    Integer(parsed_response.fetch('expires_in'))
  )
rescue KeyError, ArgumentError => e
  raise KMS::CredentialsNotFound,
        "Azure metadata response is invalid: '#{parsed_response}'; #{e.class}: #{e.message}"
end

Private Class Methods

do_request(uri, req) click to toggle source

Performs a request to Azure metadata host.

@param [URI] uri URI to Azure metadata host. @param [Net::HTTP::Get] req Request object.

@return [Net::HTTPResponse] Response object.

@raise [KMS::CredentialsNotFound] If cannot execute request.

# File lib/mongo/crypt/kms/azure/credentials_retriever.rb, line 106
def self.do_request(uri, req)
  ::Timeout.timeout(10) do
    Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
      http.request(req)
    end
  end
rescue ::Timeout::Error, IOError, SystemCallError, SocketError => e
  raise KMS::CredentialsNotFound,
        "Could not receive Azure metadata response; #{e.class}: #{e.message}"
end
fetch_response(uri, req) click to toggle source

Fetches response from Azure metadata host.

@param [URI] uri URI to Azure metadata host. @param [Net::HTTP::Get] req Request object.

@return [Hash] Parsed response.

@raise [KMS::CredentialsNotFound] If cannot fetch response or

response is invalid.
# File lib/mongo/crypt/kms/azure/credentials_retriever.rb, line 85
def self.fetch_response(uri, req)
  resp = do_request(uri, req)
  if resp.code != '200'
    raise KMS::CredentialsNotFound,
          "Azure metadata host responded with code #{resp.code}"
  end
  JSON.parse(resp.body)
rescue JSON::ParserError => e
  raise KMS::CredentialsNotFound,
        "Azure metadata response is invalid: '#{resp.body}'; #{e.class}: #{e.message}"
end
prepare_request(extra_headers, metadata_host) click to toggle source

Prepares a request to Azure metadata host.

@param [Hash] extra_headers Extra headers to be passed to the

request. This is used for testing.

@param [String | nil] metadata_host Azure metadata host. This

is used for testing.

@return [Array<URI, Net::HTTP::Get>] URI and request object.

# File lib/mongo/crypt/kms/azure/credentials_retriever.rb, line 60
def self.prepare_request(extra_headers, metadata_host)
  host = metadata_host || DEFAULT_HOST
  host = DEFAULT_HOST if host.empty?
  uri = URI("http://#{host}/metadata/identity/oauth2/token")
  uri.query = ::URI.encode_www_form(
    'api-version' => '2018-02-01',
    'resource' => 'https://vault.azure.net'
  )
  req = Net::HTTP::Get.new(uri)
  req['Metadata'] = 'true'
  req['Accept'] = 'application/json'
  extra_headers.each { |k, v| req[k] = v }
  [uri, req]
end