class Aptible::Auth::Token

Public Class Methods

create(options) click to toggle source
# File lib/aptible/auth/token.rb, line 26
def self.create(options)
  # For backwards compatibility: we used to throw in .create (which isn't
  # consistent with other resources), and we probably need to continue
  # doing this. We also need to continue throwing a OAuth2::Error.
  token = new
  token.process_options(options)
  token
end
create!(options) click to toggle source
# File lib/aptible/auth/token.rb, line 35
def self.create!(options)
  Token.create(options)
rescue OAuth2::Error => e
  # Rethrow OAuth2::Error as HyperResource::ResponseError for
  # aptible-resource to handle
  raise HyperResource::ResponseError.new(e.code, response: e.response,
                                                 cause: e)
end

Public Instance Methods

authenticate_client(id, secret, subject, options = {}) click to toggle source
# File lib/aptible/auth/token.rb, line 50
def authenticate_client(id, secret, subject, options = {})
  options[:scope] ||= 'manage'
  # Unlike other methods, the assertion token grant requirs an "exp"
  # parameter rather than expires_in, but since we'd like to expose a
  # consistent API to consumers, we override it here
  expires_in = options.delete(:expires_in)
  options[:exp] = Time.now.utc.to_i + expires_in if expires_in
  oauth_token = oauth.assertion.get_token({
    iss: id,
    sub: subject
  }.merge(signing_params_from_secret(secret).merge(options)))
  apply_oauth_response(oauth_token)
end
authenticate_impersonate(subject_token, subject_token_type, options) click to toggle source
# File lib/aptible/auth/token.rb, line 64
def authenticate_impersonate(subject_token, subject_token_type, options)
  actor_token = token_as_string(options.delete(:token)) || bearer_token

  # TODO: Do we want to check whether the token is non-nil at this stage?
  options[:scope] ||= 'manage'
  oauth_token = oauth.token_exchange.get_token(
    actor_token, 'urn:ietf:params:oauth:token-type:jwt',
    subject_token, subject_token_type, options
  )

  apply_oauth_response(oauth_token)
end
authenticate_user(email, password, options = {}) click to toggle source
# File lib/aptible/auth/token.rb, line 44
def authenticate_user(email, password, options = {})
  options[:scope] ||= 'manage'
  oauth_token = oauth.password.get_token(email, password, options)
  apply_oauth_response(oauth_token)
end
expires_at() click to toggle source
# File lib/aptible/auth/token.rb, line 130
def expires_at
  # The Auth API returns the expiry as a timestamp (i.e. an Integer), but
  # our API client knows only to handle times as strings. This overrides
  # the field method for expires_at to return a Time despite the
  # underlying API field being an Integer.
  Time.at(attributes[:expires_at])
end
oauth() click to toggle source
# File lib/aptible/auth/token.rb, line 77
def oauth
  options = {
    site: root_url,
    token_url: '/tokens',
    connection_opts: {
      headers: {
        'User-Agent' => Aptible::Resource.configuration.user_agent
      }
    }
  }
  @oauth ||= OAuth2::Client.new(nil, nil, options)
end
process_options(options) click to toggle source
# File lib/aptible/auth/token.rb, line 90
def process_options(options)
  options = options.dup
  if (email = options.delete(:email)) &&
     (password = options.delete(:password))
    authenticate_user(email, password, options)
  elsif (client_id = options.delete(:client_id)) &&
        (client_secret = options.delete(:client_secret)) &&
        (subject = options.delete(:subject))
    authenticate_client(client_id, client_secret, subject, options)
  elsif (href = options.delete(:user_href))
    authenticate_impersonate(href, 'aptible:user:href', options)
  elsif (href = options.delete(:organization_href))
    authenticate_impersonate(href, 'aptible:organization:href', options)
  elsif (email = options.delete(:user_email))
    authenticate_impersonate(email, 'aptible:user:email', options)
  elsif (user_token = options.delete(:user_token))
    authenticate_impersonate(
      token_as_string(user_token), 'aptible:token', options
    )
  elsif (href = options.delete(:ssh_key_pre_authorization_href))
    authenticate_impersonate(
      href,
      'aptible:ssh_key_pre_authorization:href',
      options
    )
  else
    raise 'Unrecognized options'
  end
end
token() click to toggle source
# File lib/aptible/auth/token.rb, line 120
def token
  # If the user set an arbitrary token, then we'll return that one,
  # otherwise we'll fall back to the Token itself, which makes it
  # possible to create a token and immediately access it #user or #actor
  # methods.
  # NOTE: Setting the token after the fact probably doesn't work anyway,
  # since the Authorization header won't be updated.
  @token || access_token
end

Private Instance Methods

apply_oauth_response(oauth_token) click to toggle source
# File lib/aptible/auth/token.rb, line 140
def apply_oauth_response(oauth_token)
  # apply() + loaded is what HyperResource normally does after
  # deserializing a response back from the API. On top of that, we need
  # to set the Authorization header so that the token can be used to make
  # further API requests (e.g. accessing token#user or token#actor).
  adapter.apply(oauth_token.to_hash, self)
  self.loaded = true
  headers['Authorization'] = "Bearer #{bearer_token}"
  self
end
key_length(private_key) click to toggle source
# File lib/aptible/auth/token.rb, line 172
def key_length(private_key)
  # http://stackoverflow.com/questions/13747212
  private_key.n.num_bytes * 8
end
parse_private_key(string) click to toggle source
# File lib/aptible/auth/token.rb, line 159
      def parse_private_key(string)
        if string.start_with?('-----')
          OpenSSL::PKey::RSA.new(string)
        else
          formatted_string = <<-PRIVATE_KEY.gsub(/^\s+/, '')
            -----BEGIN RSA PRIVATE KEY-----
            #{string.scan(/.{1,64}/).join("\n")}
            -----END RSA PRIVATE KEY-----
          PRIVATE_KEY
          OpenSSL::PKey::RSA.new(formatted_string)
        end
      end
signing_params_from_secret(secret) click to toggle source
# File lib/aptible/auth/token.rb, line 151
def signing_params_from_secret(secret)
  private_key = parse_private_key(secret)
  {
    private_key: private_key,
    algorithm: "RS#{key_length(private_key) / 2}"
  }
end
token_as_string(tok) click to toggle source
# File lib/aptible/auth/token.rb, line 177
def token_as_string(tok)
  # TODO: This duplicates aptible-resource, is it worth extracting?
  return nil if tok.nil?

  case tok
  when Aptible::Resource::Base then tok.access_token
  when Fridge::AccessToken then tok.to_s
  when String then tok
  else
    raise "Unrecognized token: #{tok.class}: #{tok}"
  end
end