Class: Aspera::OAuth::Base
- Inherits:
-
Object
- Object
- Aspera::OAuth::Base
- Defined in:
- lib/aspera/oauth/base.rb
Overview
Implement OAuth 2 for the REST client and generate a bearer token bearer tokens are cached in memory and in a file cache for later re-use tools.ietf.org/html/rfc6749
Instance Attribute Summary collapse
-
#scope ⇒ Object
writeonly
scope can be modified after creation.
Instance Method Summary collapse
-
#create_token_call(creation_params) ⇒ Object
helper method to create token as per RFC.
-
#initialize(base_url:, auth: nil, client_id: nil, client_secret: nil, scope: nil, use_query: false, path_token: 'token', token_field: 'access_token') ⇒ Base
constructor
[M]=mandatory [D]=has default value [O]=Optional/nil.
-
#optional_scope_client_id(add_secret: false) ⇒ Object
Hash with optional general parameters.
-
#token(cache: true, refresh: false) ⇒ Object
get an OAuth v2 token (generated, cached, refreshed) call token() to get a token.
Constructor Details
#initialize(base_url:, auth: nil, client_id: nil, client_secret: nil, scope: nil, use_query: false, path_token: 'token', token_field: 'access_token') ⇒ Base
[M]=mandatory [D]=has default value [O]=Optional/nil
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/aspera/oauth/base.rb', line 26 def initialize( base_url:, auth: nil, client_id: nil, client_secret: nil, scope: nil, use_query: false, path_token: 'token', # default endpoint for /token to generate token token_field: 'access_token' # field with token in result of call to path_token ) Aspera.assert_type(base_url, String) Aspera.assert(respond_to?(:create_token), 'create_token method must be defined', exception_class: InternalError) @base_url = base_url @path_token = path_token @token_field = token_field @client_id = client_id @client_secret = client_secret @scope = scope @use_query = use_query @identifiers = [] @identifiers.push(auth[:username]) if auth.is_a?(Hash) && auth.key?(:username) # this is the OAuth API @api = Rest.new( base_url: @base_url, redirect_max: 2, auth: auth) end |
Instance Attribute Details
#scope=(value) ⇒ Object (writeonly)
scope can be modified after creation
16 17 18 |
# File 'lib/aspera/oauth/base.rb', line 16 def scope=(value) @scope = value end |
Instance Method Details
#create_token_call(creation_params) ⇒ Object
helper method to create token as per RFC
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/aspera/oauth/base.rb', line 55 def create_token_call(creation_params) Log.log.debug{'Generating a new token'.bg_green} payload = { body: creation_params, body_type: :www } if @use_query payload[:query] = creation_params payload[:body] = {} 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.
74 75 76 77 78 79 80 |
# File 'lib/aspera/oauth/base.rb', line 74 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 |
#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)
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/aspera/oauth/base.rb', line 87 def token(cache: true, refresh: false) # generate token unique identifier for persistency (memory/disk cache) token_id = IdGenerator.from_list(Factory.id( @base_url, Factory.class_to_id(self.class), @identifiers, @scope )) # get token_data from cache (or nil), token_data is what is returned by /token token_data = Factory.instance.persist_mgr.get(token_id) if cache token_data = JSON.parse(token_data) unless token_data.nil? # 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 if !refresh && !token_data.nil? decoded_token = OAuth::Factory.instance.decode_token(token_data[@token_field]) Log.log.debug{Log.dump('decoded_token', decoded_token)} unless decoded_token.nil? if decoded_token.is_a?(Hash) expires_at_sec = if decoded_token['expires_at'].is_a?(String) then DateTime.parse(decoded_token['expires_at']).to_time elsif decoded_token['exp'].is_a?(Integer) then Time.at(decoded_token['exp']) end # force refresh if we see a token too close from expiration refresh = true if expires_at_sec.is_a?(Time) && (expires_at_sec - Time.now) < OAuth::Factory.instance.parameters[:token_expiration_guard_sec] Log.log.debug{"Expiration: #{expires_at_sec} / #{refresh}"} end end # an API was already called, but failed, we need to regenerate or refresh if refresh if token_data.is_a?(Hash) && 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_id) token_data = nil # lets try the existing refresh token if !refresh_token.nil? Log.log.info{"refresh=[#{refresh_token}]".bg_green} # try to refresh # 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_id, json_data) else Log.log.debug{"refresh failed: #{resp[:http].body}".bg_red} 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_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 |