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 tools.ietf.org/html/rfc6749

Direct Known Subclasses

Generic, Jwt, UrlJson, Web

Instance Method Summary collapse

Constructor Details

#initialize(client_id: nil, client_secret: nil, scope: nil, use_query: false, path_token: 'token', token_field: Factory::TOKEN_FIELD, cache_ids: nil, **rest_params) ⇒ Base

Returns a new instance of Base.

Parameters:

  • **

    Parameters for REST

  • client_id (String, nil) (defaults to: nil)
  • client_secret (String, nil) (defaults to: nil)
  • scope (String, nil) (defaults to: nil)
  • use_query (bool) (defaults to: false)

    Provide parameters in query instead of body

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

    API end point to create a token

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

    Field in result that contains the token

  • cache_ids (Array, nil) (defaults to: nil)

    List of unique identifiers for cache id generation



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/aspera/oauth/base.rb', line 23

def initialize(
  client_id: nil,
  client_secret: nil,
  scope: nil,
  use_query: false,
  path_token:  'token',
  token_field: Factory::TOKEN_FIELD,
  cache_ids: nil,
  **rest_params
)
  Aspera.assert(respond_to?(:create_token), 'create_token method must be defined', exception_class: InternalError)
  # this is the OAuth API
  @api = Rest.new(**rest_params)
  @path_token = path_token
  @token_field = token_field
  @client_id = client_id
  @client_secret = client_secret
  @use_query = use_query
  @base_cache_ids = cache_ids.clone
  @base_cache_ids = [] if @base_cache_ids.nil?
  Aspera.assert_type(@base_cache_ids, Array)
  if @api.auth_params.key?(:username)
    cache_ids.push(@api.auth_params[:username])
  end
  @base_cache_ids.freeze
  self.scope = scope
end

Instance Method Details

#create_token_call(creation_params) ⇒ Object

helper method to create token as per RFC



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/aspera/oauth/base.rb', line 59

def create_token_call(creation_params)
  Log.log.debug{'Generating a new token'.bg_green}
  payload = { body_type: :www }
  if @use_query
    payload[:query] = creation_params
  else
    payload[:body] = creation_params
  end
  return @api.call(
    operation: 'POST',
    subpath:   @path_token,
    headers:   {'Accept' => 'application/json'},
    **payload
  )
end

#optional_scope_client_id(add_secret: false) ⇒ Object

Returns Hash with optional general parameters.

Returns:

  • Hash with optional general parameters



76
77
78
79
80
81
82
# File 'lib/aspera/oauth/base.rb', line 76

def optional_scope_client_id(add_secret: false)
  call_params = {}
  call_params[:scope] = @scope unless @scope.nil?
  call_params[:client_id] = @client_id unless @client_id.nil?
  call_params[:client_secret] = @client_secret if add_secret && !@client_id.nil?
  return call_params
end

#scope=(scope) ⇒ Object

Scope can be modified after creation, then update identifier for cache



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

def scope=(scope)
  @scope = scope
  # generate token unique identifier for persistency (memory/disk cache)
  @token_cache_id = Factory.cache_id(@api.base_url, self.class, @base_cache_ids, @scope)
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)



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
129
130
131
132
133
134
135
# File 'lib/aspera/oauth/base.rb', line 89

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 node 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]
      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
      if !refresh_token.nil?
        Log.log.info{"refresh=[#{refresh_token}]".bg_green}
        # NOTE: AoC admin token has no refresh, and lives by default 1800secs
        resp = create_token_call(optional_scope_client_id.merge(grant_type: 'refresh_token', refresh_token: refresh_token))
        if resp[:http].code.start_with?('2')
          # save only if success
          json_data = resp[:http].body
          token_data = JSON.parse(json_data)
          Factory.instance.persist_mgr.put(@token_cache_id, json_data)
        else
          Log.log.debug{"refresh failed: #{resp[:http].body}".bg_red}
        end
      end
    end
  end

  # no cache, nor refresh: generate a token
  if token_data.nil?
    resp = create_token
    json_data = resp[:http].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 OAuth::Factory.bearer_build(token_data[@token_field])
end