mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
424267321d
Passing in a refresh token to `Compute#new` will allow the token to be used to request new access tokens as the original authenticated user so the username and password do not have to be stored locally.
164 lines
4.8 KiB
Ruby
164 lines
4.8 KiB
Ruby
# This module covers Brightbox's partial implementation of OAuth 2.0
|
|
# and enables fog clients to implement several authentictication strategies
|
|
#
|
|
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10
|
|
#
|
|
module Fog::Brightbox::OAuth2
|
|
|
|
# This builds the simplest form of requesting an access token
|
|
# based on the arguments passed in
|
|
#
|
|
# @param [Fog::Connection] connection
|
|
# @param [CredentialSet] credentials
|
|
#
|
|
# @return [Excon::Response]
|
|
def request_access_token(connection, credentials)
|
|
token_strategy = credentials.best_grant_strategy
|
|
|
|
header_content = "#{credentials.client_id}:#{credentials.client_secret}"
|
|
encoded_credentials = Base64.encode64(header_content).chomp
|
|
|
|
connection.request({
|
|
:path => "/token",
|
|
:expects => 200,
|
|
:headers => {
|
|
'Authorization' => "Basic #{encoded_credentials}",
|
|
'Content-Type' => 'application/json'
|
|
},
|
|
:method => 'POST',
|
|
:body => Fog::JSON.encode(token_strategy.authorization_body_data)
|
|
})
|
|
end
|
|
|
|
# Encapsulates credentials required to request access tokens from the
|
|
# Brightbox authorisation servers
|
|
#
|
|
# @todo Interface to update certain credentials (after password change)
|
|
#
|
|
class CredentialSet
|
|
attr_reader :client_id, :client_secret, :username, :password
|
|
attr_reader :access_token, :refresh_token
|
|
#
|
|
# @param [String] client_id
|
|
# @param [String] client_secret
|
|
# @param [Hash] options
|
|
# @option options [String] :username
|
|
# @option options [String] :password
|
|
#
|
|
def initialize(client_id, client_secret, options = {})
|
|
@client_id = client_id
|
|
@client_secret = client_secret
|
|
@username = options[:username]
|
|
@password = options[:password]
|
|
@access_token = options[:access_token]
|
|
@refresh_token = options[:refresh_token]
|
|
end
|
|
|
|
# Returns true if user details are available
|
|
# @return [Boolean]
|
|
def user_details?
|
|
!!(@username && @password)
|
|
end
|
|
|
|
# Is an access token available for these credentials?
|
|
def access_token?
|
|
!!@access_token
|
|
end
|
|
|
|
# Is a refresh token available for these credentials?
|
|
def refresh_token?
|
|
!!@refresh_token
|
|
end
|
|
|
|
# Updates the credentials with newer tokens
|
|
def update_tokens(access_token, refresh_token = nil)
|
|
@access_token = access_token
|
|
@refresh_token = refresh_token
|
|
end
|
|
|
|
# Based on available credentials returns the best strategy
|
|
#
|
|
# @todo Add a means to dictate which should or shouldn't be used
|
|
#
|
|
def best_grant_strategy
|
|
if refresh_token?
|
|
RefreshTokenStrategy.new(self)
|
|
elsif user_details?
|
|
UserCredentialsStrategy.new(self)
|
|
else
|
|
ClientCredentialsStrategy.new(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
# This strategy class is the basis for OAuth2 grant types
|
|
#
|
|
# @abstract Need to implement {#authorization_body_data} to return a
|
|
# Hash matching the expected parameter form for the OAuth request
|
|
#
|
|
# @todo Strategies should be able to validate if credentials are suitable
|
|
# so just client credentials cannot be used with user strategies
|
|
#
|
|
class GrantTypeStrategy
|
|
def initialize(credentials)
|
|
@credentials = credentials
|
|
end
|
|
|
|
def authorization_body_data
|
|
raise "Not implemented"
|
|
end
|
|
end
|
|
|
|
# This implements client based authentication/authorization
|
|
# based on the existing trust relationship using the `none`
|
|
# grant type.
|
|
#
|
|
class ClientCredentialsStrategy < GrantTypeStrategy
|
|
def authorization_body_data
|
|
{
|
|
"grant_type" => "none",
|
|
"client_id" => @credentials.client_id
|
|
}
|
|
end
|
|
end
|
|
|
|
# This passes user details through so the returned token
|
|
# carries the privileges of the user not account limited
|
|
# by the client
|
|
#
|
|
class UserCredentialsStrategy < GrantTypeStrategy
|
|
def authorization_body_data
|
|
{
|
|
"grant_type" => "password",
|
|
"client_id" => @credentials.client_id,
|
|
"username" => @credentials.username,
|
|
"password" => @credentials.password
|
|
}
|
|
end
|
|
end
|
|
|
|
# This strategy attempts to use a refresh_token gained during an earlier
|
|
# request to reuse the credentials given originally
|
|
#
|
|
class RefreshTokenStrategy < GrantTypeStrategy
|
|
def authorization_body_data
|
|
{
|
|
"grant_type" => "refresh_token",
|
|
"client_id" => @credentials.client_id,
|
|
"refresh_token" => @credentials.refresh_token
|
|
}
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# This updates the current credentials if passed a valid response
|
|
#
|
|
# @param [CredentialSet] credentials Credentials to update
|
|
# @param [Excon::Response] response Response object to parse value from
|
|
#
|
|
def update_credentials_from_response(credentials, response)
|
|
response_data = Fog::JSON.decode(response.body)
|
|
credentials.update_tokens(response_data["access_token"], response_data["refresh_token"])
|
|
end
|
|
end
|