Class: Aspera::OAuth::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/aspera/oauth/base.rb

Overview

OAuth 2 client for the REST client Generate bearer token Bearer tokens are cached in memory and in a file cache for later re-use OAuth 2.0 Authorization Framework: tools.ietf.org/html/rfc6749 Bearer Token Usage: tools.ietf.org/html/rfc6750

Direct Known Subclasses

FaspexPubLink, Generic, Jwt, UrlJson, Web

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params: {}, use_query: false, path_token: 'token', token_field: Factory::TOKEN_FIELD, cache_ids: [], **rest_params) ⇒ Base

Returns a new instance of Base.

Parameters:

  • params (Hash) (defaults to: {})

    Parameters for token creation (client_id, client_secret, scope, etc…)

  • use_query (Boolean) (defaults to: false)

    Provide parameters in query instead of body

  • path_token (String) (defaults to: 'token')

    API end point to create a token from base URL

  • token_field (String) (defaults to: Factory::TOKEN_FIELD)

    Field in result that contains the token

  • cache_ids (Array) (defaults to: [])

    List of unique identifiers for cache id generation

  • **rest_params (Hash)

    Parameters for REST



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/aspera/oauth/base.rb', line 23

def initialize(
  params: {},
  use_query: false,
  path_token:  'token',
  token_field: Factory::TOKEN_FIELD,
  cache_ids: [],
  **rest_params
)
  Aspera.assert_type(params, Hash)
  Aspera.assert_type(cache_ids, Array)
  # This is the OAuth API
  @api = Rest.new(**rest_params)
  @params = params.dup.freeze
  @path_token = path_token
  @token_field = token_field
  @use_query = use_query
  # TODO: :username and :scope shall be done in class, using cache_ids
  @token_cache_id = Factory.cache_id(@api.base_url, self.class, cache_ids, rest_params[:username], @params[:scope])
end

Instance Attribute Details

#apiObject (readonly)

The OAuth API Object



44
45
46
# File 'lib/aspera/oauth/base.rb', line 44

def api
  @api
end

#paramsObject (readonly)

Parameters to generate token



48
49
50
# File 'lib/aspera/oauth/base.rb', line 48

def params
  @params
end

#path_tokenObject (readonly)

Sub path to generate token



46
47
48
# File 'lib/aspera/oauth/base.rb', line 46

def path_token
  @path_token
end

Instance Method Details

#authorization(**kwargs) ⇒ String

Returns value suitable for Authorization header.

Returns:

  • (String)

    value suitable for Authorization header



69
70
71
# File 'lib/aspera/oauth/base.rb', line 69

def authorization(**kwargs)
  return OAuth::Factory.bearer_authorization(token(**kwargs))
end

#base_params(add_secret: false) ⇒ Hash

Create base parameters for token creation calls

Parameters:

  • add_secret (Boolean) (defaults to: false)

    Add secret in default call parameters

Returns:

  • (Hash)

    Optional general parameters



62
63
64
65
66
# File 'lib/aspera/oauth/base.rb', line 62

def base_params(add_secret: false)
  call_params = @params.dup
  call_params.delete(:client_secret) unless add_secret
  return call_params
end

#create_token_call(creation_params) ⇒ HTTPResponse

Helper method to create token as per RFC

Returns:

  • (HTTPResponse)

Raises:

  • RestError if not 2XX code



53
54
55
56
57
# File 'lib/aspera/oauth/base.rb', line 53

def create_token_call(creation_params)
  Log.log.debug{'Generating a new token'.bg_green}
  return @api.create(@path_token, nil, query: creation_params, ret: :resp) if @use_query
  return @api.create(@path_token, creation_params, content_type: Rest::MIME_WWW, ret: :resp)
end

#token(cache: true, refresh: false) ⇒ Object

get an OAuth v2 token (generated, cached, refreshed) call token() to get a token. if a token is expired (api returns 4xx), call again token(refresh: true)

Parameters:

  • cache (defaults to: true)

    set to false to disable cache

  • refresh (defaults to: false)

    set to true to force refresh or re-generation (if previous failed)



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/aspera/oauth/base.rb', line 78

def token(cache: true, refresh: false)
  # get token info from cache (or nil), decoded with date and expiration status
  token_info = Factory.instance.get_token_info(@token_cache_id) if cache
  token_data = nil
  unless token_info.nil?
    token_data = token_info[:data]
    # Optional optimization:
    # Check if token is expired based on decoded content then force refresh if close enough
    # might help in case the transfer agent cannot refresh himself
    # `direct` agent is equipped with refresh code
    # an API was already called, but failed, we need to regenerate or refresh
    if refresh || token_info[:expired]
      Log.log.trace1{"refresh: #{refresh} expired: #{token_info[:expired]}"}
      refresh_token = nil
      if token_data.key?('refresh_token') && !token_data['refresh_token'].eql?('not_supported')
        # save possible refresh token, before deleting the cache
        refresh_token = token_data['refresh_token']
      end
      # delete cache
      Factory.instance.persist_mgr.delete(@token_cache_id)
      token_data = nil
      # lets try the existing refresh token
      # NOTE: AoC admin token has no refresh, and lives by default 1800secs
      if !refresh_token.nil?
        Log.log.debug{"refresh token=[#{refresh_token}]"}
        begin
          http = create_token_call(base_params(add_secret: true).merge(grant_type: 'refresh_token', refresh_token: refresh_token))
          # Save only if success
          json_data = http.body
          token_data = JSON.parse(json_data)
          Factory.instance.persist_mgr.put(@token_cache_id, json_data)
        rescue => e
          # Refresh token can fail.
          Log.log.warn{"Refresh failed: #{e}"}
        end
      end
    end
  end

  # no cache, nor refresh: generate a token
  if token_data.nil?
    # Call the method-specific token creation
    # which returns the result of create_token_call
    json_data = create_token.body
    token_data = JSON.parse(json_data)
    Factory.instance.persist_mgr.put(@token_cache_id, json_data)
  end
  Aspera.assert(token_data.key?(@token_field)){"API error: No such field in answer: #{@token_field}"}
  # ok we shall have a token here
  return token_data[@token_field]
end