aspera-cli 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +1 -0
- data/README.md +844 -861
- data/bin/ascli +20 -1
- data/bin/asession +37 -34
- data/docs/test_env.conf +11 -3
- data/examples/aoc.rb +13 -12
- data/examples/dascli +26 -0
- data/examples/faspex4.rb +34 -29
- data/examples/transfer.rb +30 -29
- data/lib/aspera/aoc.rb +151 -143
- data/lib/aspera/ascmd.rb +56 -45
- data/lib/aspera/ats_api.rb +6 -5
- data/lib/aspera/cli/basic_auth_plugin.rb +18 -16
- data/lib/aspera/cli/extended_value.rb +32 -30
- data/lib/aspera/cli/formater.rb +103 -111
- data/lib/aspera/cli/info.rb +2 -1
- data/lib/aspera/cli/listener/line_dump.rb +1 -0
- data/lib/aspera/cli/listener/logger.rb +1 -0
- data/lib/aspera/cli/listener/progress.rb +13 -12
- data/lib/aspera/cli/listener/progress_multi.rb +21 -20
- data/lib/aspera/cli/main.rb +106 -89
- data/lib/aspera/cli/manager.rb +96 -85
- data/lib/aspera/cli/plugin.rb +50 -32
- data/lib/aspera/cli/plugins/alee.rb +6 -5
- data/lib/aspera/cli/plugins/aoc.rb +521 -426
- data/lib/aspera/cli/plugins/ats.rb +84 -83
- data/lib/aspera/cli/plugins/bss.rb +30 -27
- data/lib/aspera/cli/plugins/config.rb +483 -397
- data/lib/aspera/cli/plugins/console.rb +17 -15
- data/lib/aspera/cli/plugins/cos.rb +26 -35
- data/lib/aspera/cli/plugins/faspex.rb +201 -168
- data/lib/aspera/cli/plugins/faspex5.rb +109 -74
- data/lib/aspera/cli/plugins/node.rb +378 -189
- data/lib/aspera/cli/plugins/orchestrator.rb +71 -65
- data/lib/aspera/cli/plugins/preview.rb +131 -122
- data/lib/aspera/cli/plugins/server.rb +94 -93
- data/lib/aspera/cli/plugins/shares.rb +42 -28
- data/lib/aspera/cli/plugins/sync.rb +15 -14
- data/lib/aspera/cli/transfer_agent.rb +56 -52
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +29 -28
- data/lib/aspera/command_line_builder.rb +50 -43
- data/lib/aspera/cos_node.rb +64 -38
- data/lib/aspera/data_repository.rb +1 -0
- data/lib/aspera/environment.rb +18 -8
- data/lib/aspera/fasp/agent_base.rb +26 -23
- data/lib/aspera/fasp/agent_connect.rb +35 -30
- data/lib/aspera/fasp/agent_direct.rb +68 -60
- data/lib/aspera/fasp/agent_httpgw.rb +71 -64
- data/lib/aspera/fasp/agent_node.rb +24 -23
- data/lib/aspera/fasp/agent_trsdk.rb +19 -20
- data/lib/aspera/fasp/error.rb +2 -1
- data/lib/aspera/fasp/error_info.rb +79 -68
- data/lib/aspera/fasp/installation.rb +122 -114
- data/lib/aspera/fasp/listener.rb +1 -0
- data/lib/aspera/fasp/parameters.rb +44 -41
- data/lib/aspera/fasp/resume_policy.rb +14 -11
- data/lib/aspera/fasp/transfer_spec.rb +6 -5
- data/lib/aspera/fasp/uri.rb +25 -24
- data/lib/aspera/faspex_gw.rb +83 -72
- data/lib/aspera/hash_ext.rb +10 -12
- data/lib/aspera/id_generator.rb +8 -7
- data/lib/aspera/keychain/encrypted_hash.rb +60 -45
- data/lib/aspera/keychain/macos_security.rb +26 -24
- data/lib/aspera/log.rb +34 -38
- data/lib/aspera/nagios.rb +14 -13
- data/lib/aspera/node.rb +19 -19
- data/lib/aspera/oauth.rb +121 -101
- data/lib/aspera/open_application.rb +6 -5
- data/lib/aspera/persistency_action_once.rb +9 -8
- data/lib/aspera/persistency_folder.rb +10 -9
- data/lib/aspera/preview/file_types.rb +261 -266
- data/lib/aspera/preview/generator.rb +74 -73
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +7 -6
- data/lib/aspera/preview/utils.rb +30 -33
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/proxy_auto_config.rb +25 -23
- data/lib/aspera/rest.rb +73 -74
- data/lib/aspera/rest_call_error.rb +1 -0
- data/lib/aspera/rest_error_analyzer.rb +11 -9
- data/lib/aspera/rest_errors_aspera.rb +5 -4
- data/lib/aspera/secret_hider.rb +68 -0
- data/lib/aspera/ssh.rb +12 -10
- data/lib/aspera/sync.rb +49 -47
- data/lib/aspera/temp_file_manager.rb +7 -5
- data/lib/aspera/timer_limiter.rb +9 -8
- data/lib/aspera/uri_reader.rb +11 -14
- data/lib/aspera/web_auth.rb +17 -15
- data.tar.gz.sig +0 -0
- metadata +117 -34
- metadata.gz.sig +2 -0
- data/bin/dascli +0 -13
data/lib/aspera/oauth.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/open_application'
|
3
4
|
require 'aspera/web_auth'
|
4
5
|
require 'aspera/id_generator'
|
@@ -14,46 +15,38 @@ module Aspera
|
|
14
15
|
# if a token is expired (api returns 4xx), call again get_authorization({refresh: true})
|
15
16
|
# https://tools.ietf.org/html/rfc6749
|
16
17
|
class Oauth
|
17
|
-
DEFAULT_CREATE_PARAMS={
|
18
|
-
|
19
|
-
|
20
|
-
web:
|
18
|
+
DEFAULT_CREATE_PARAMS = {
|
19
|
+
path_token: 'token', # default endpoint for /token to generate token
|
20
|
+
token_field: 'access_token', # field with token in result of call to path_token
|
21
|
+
web: {path_authorize: 'authorize'} # default endpoint for /authorize, used for code exchange
|
21
22
|
}.freeze
|
22
23
|
|
23
24
|
# OAuth methods supported by default
|
24
|
-
STD_AUTH_TYPES=[
|
25
|
+
STD_AUTH_TYPES = %i[web jwt].freeze
|
25
26
|
|
26
27
|
# remove 5 minutes to account for time offset (TODO: configurable?)
|
27
|
-
JWT_NOTBEFORE_OFFSET_SEC=300
|
28
|
+
JWT_NOTBEFORE_OFFSET_SEC = 300
|
28
29
|
# one hour validity (TODO: configurable?)
|
29
|
-
JWT_EXPIRY_OFFSET_SEC=3600
|
30
|
+
JWT_EXPIRY_OFFSET_SEC = 3600
|
30
31
|
# tokens older than 30 minutes will be discarded from cache
|
31
|
-
TOKEN_CACHE_EXPIRY_SEC=1800
|
32
|
-
#
|
33
|
-
|
34
|
-
|
32
|
+
TOKEN_CACHE_EXPIRY_SEC = 1800
|
33
|
+
# tokens valid for less than this duration will be regenerated
|
34
|
+
TOKEN_EXPIRATION_GUARD_SEC = 120
|
35
|
+
# a prefix for persistency of tokens (simplify garbage collect)
|
36
|
+
PERSIST_CATEGORY_TOKEN = 'token'
|
35
37
|
|
36
38
|
private_constant :JWT_NOTBEFORE_OFFSET_SEC,:JWT_EXPIRY_OFFSET_SEC,:TOKEN_CACHE_EXPIRY_SEC,:PERSIST_CATEGORY_TOKEN,:TOKEN_EXPIRATION_GUARD_SEC
|
37
39
|
|
38
40
|
# persistency manager
|
39
|
-
@persist=nil
|
41
|
+
@persist = nil
|
40
42
|
# token creation methods
|
41
|
-
@
|
43
|
+
@create_handlers = {}
|
42
44
|
# token unique identifiers from oauth parameters
|
43
|
-
@
|
44
|
-
[:crtype],
|
45
|
-
[:generic,:grant_type],
|
46
|
-
[:jwt,:payload,:sub],
|
47
|
-
[:auth,:username],
|
48
|
-
[:aoc_pub_link,:json,:url_token],
|
49
|
-
[:generic,:apikey],
|
50
|
-
[:scope],
|
51
|
-
[:generic,:response_type]
|
52
|
-
]
|
45
|
+
@id_handlers = {}
|
53
46
|
|
54
47
|
class << self
|
55
48
|
def persist_mgr=(manager)
|
56
|
-
@persist=manager
|
49
|
+
@persist = manager
|
57
50
|
# cleanup expired tokens
|
58
51
|
@persist.garbage_collect(PERSIST_CATEGORY_TOKEN,TOKEN_CACHE_EXPIRY_SEC)
|
59
52
|
end
|
@@ -62,8 +55,8 @@ module Aspera
|
|
62
55
|
if @persist.nil?
|
63
56
|
Log.log.debug('Not using persistency') # (use Aspera::Oauth.persist_mgr=Aspera::PersistencyFolder.new)
|
64
57
|
# create NULL persistency class
|
65
|
-
@persist=Class.new do
|
66
|
-
def get(_x);nil;end;def delete(_x);nil;end;def put(_x,_y);nil;end;def garbage_collect(_x,_y);nil;end
|
58
|
+
@persist = Class.new do
|
59
|
+
def get(_x);nil;end;def delete(_x);nil;end;def put(_x,_y);nil;end;def garbage_collect(_x,_y);nil;end
|
67
60
|
end.new
|
68
61
|
end
|
69
62
|
return @persist
|
@@ -76,60 +69,77 @@ module Aspera
|
|
76
69
|
|
77
70
|
# register a bearer token decoder, mainly to inspect expiry date
|
78
71
|
def register_decoder(method)
|
79
|
-
@decoders||=[]
|
72
|
+
@decoders ||= []
|
80
73
|
@decoders.push(method)
|
81
74
|
end
|
82
75
|
|
83
76
|
# decode token using all registered decoders
|
84
77
|
def decode_token(token)
|
85
78
|
@decoders.each do |decoder|
|
86
|
-
result=decoder.call(token) rescue nil
|
79
|
+
result = decoder.call(token) rescue nil
|
87
80
|
return result unless result.nil?
|
88
81
|
end
|
89
82
|
return nil
|
90
83
|
end
|
91
84
|
|
92
|
-
# register a token creation method
|
93
|
-
|
94
|
-
|
95
|
-
|
85
|
+
# register a token creation method
|
86
|
+
# @param id creation type from field :crtype in constructor
|
87
|
+
# @param lambda_create called to create token
|
88
|
+
# @param id_create called to generate unique id for token, for cache
|
89
|
+
def register_token_creator(id, lambda_create, id_create)
|
90
|
+
raise 'ERROR: requites Symbol and 2 lambdas' unless id.is_a?(Symbol) && lambda_create.is_a?(Proc) && id_create.is_a?(Proc)
|
91
|
+
@create_handlers[id] = lambda_create
|
92
|
+
@id_handlers[id] = id_create
|
96
93
|
end
|
97
94
|
|
98
95
|
# @return one of the registered creators for the given create type
|
99
96
|
def token_creator(id)
|
100
|
-
raise "token
|
101
|
-
@
|
97
|
+
raise "token creator type unknown: #{id}/#{id.class}" unless @create_handlers.has_key?(id)
|
98
|
+
@create_handlers[id]
|
102
99
|
end
|
103
100
|
|
104
101
|
# list of identifiers foundn in creation parameters that can be used to uniquely identify the token
|
105
|
-
def
|
106
|
-
|
102
|
+
def id_creator(id)
|
103
|
+
raise "id creator type unknown: #{id}/#{id.class}" unless @id_handlers.has_key?(id)
|
104
|
+
@id_handlers[id]
|
107
105
|
end
|
108
106
|
end # self
|
109
107
|
|
110
108
|
# JSON Web Signature (JWS) compact serialization: https://datatracker.ietf.org/doc/html/rfc7515
|
111
|
-
register_decoder lambda { |token| parts=token.split('.'); raise 'not aoc token' unless parts.length.eql?(3); JSON.parse(Base64.decode64(parts[1]))}
|
109
|
+
register_decoder lambda { |token| parts = token.split('.'); raise 'not aoc token' unless parts.length.eql?(3); JSON.parse(Base64.decode64(parts[1]))}
|
112
110
|
|
113
111
|
# generic token creation, parameters are provided in :generic
|
114
112
|
register_token_creator :generic,lambda { |oauth|
|
115
|
-
return oauth.create_token(oauth.
|
113
|
+
return oauth.create_token(oauth.sparams)
|
114
|
+
},lambda { |oauth|
|
115
|
+
return [
|
116
|
+
oauth.sparams[:grant_type]&.split(':')&.last,
|
117
|
+
oauth.sparams[:apikey],
|
118
|
+
oauth.sparams[:response_type]
|
119
|
+
]
|
116
120
|
}
|
117
121
|
|
118
122
|
# Authentication using Web browser
|
119
123
|
register_token_creator :web,lambda { |oauth|
|
120
|
-
|
121
|
-
login_page_url=Rest.build_uri("#{oauth.
|
124
|
+
verification_code = SecureRandom.uuid # used to check later
|
125
|
+
login_page_url = Rest.build_uri("#{oauth.api[:base_url]}/#{oauth.sparams[:path_authorize]}",
|
126
|
+
oauth.optional_scope_client_id.merge(response_type: 'code', redirect_uri: oauth.sparams[:redirect_uri], state: verification_code))
|
122
127
|
# here, we need a human to authorize on a web page
|
123
128
|
Log.log.info("login_page_url=#{login_page_url}".bg_red.gray)
|
124
129
|
# start a web server to receive request code
|
125
|
-
webserver=WebAuth.new(oauth.
|
130
|
+
webserver = WebAuth.new(oauth.sparams[:redirect_uri])
|
126
131
|
# start browser on login page
|
127
132
|
OpenApplication.instance.uri(login_page_url)
|
128
133
|
# wait for code in request
|
129
|
-
received_params=webserver.received_request
|
130
|
-
raise 'state does not match'
|
134
|
+
received_params = webserver.received_request
|
135
|
+
raise 'state does not match' unless verification_code.eql?(received_params['state'])
|
131
136
|
# exchange code for token
|
132
|
-
return oauth.create_token(oauth.optional_scope_client_id(
|
137
|
+
return oauth.create_token(oauth.optional_scope_client_id(add_secret: true).merge(
|
138
|
+
grant_type: 'authorization_code',
|
139
|
+
code: received_params['code'],
|
140
|
+
redirect_uri: oauth.sparams[:redirect_uri]))
|
141
|
+
},lambda { |_oauth|
|
142
|
+
return []
|
133
143
|
}
|
134
144
|
|
135
145
|
# Authentication using private key
|
@@ -137,24 +147,26 @@ module Aspera
|
|
137
147
|
# https://tools.ietf.org/html/rfc7523
|
138
148
|
# https://tools.ietf.org/html/rfc7519
|
139
149
|
require 'jwt'
|
140
|
-
seconds_since_epoch=Time.new.to_i
|
150
|
+
seconds_since_epoch = Time.new.to_i
|
141
151
|
Log.log.info("seconds=#{seconds_since_epoch}")
|
142
|
-
raise 'missing JWT payload' unless oauth.
|
152
|
+
raise 'missing JWT payload' unless oauth.sparams[:payload].is_a?(Hash)
|
143
153
|
jwt_payload = {
|
144
|
-
exp: seconds_since_epoch+JWT_EXPIRY_OFFSET_SEC, # expiration time
|
145
|
-
nbf: seconds_since_epoch-JWT_NOTBEFORE_OFFSET_SEC, # not before
|
154
|
+
exp: seconds_since_epoch + JWT_EXPIRY_OFFSET_SEC, # expiration time
|
155
|
+
nbf: seconds_since_epoch - JWT_NOTBEFORE_OFFSET_SEC, # not before
|
146
156
|
iat: seconds_since_epoch, # issued at
|
147
157
|
jti: SecureRandom.uuid # JWT id
|
148
|
-
}.merge(oauth.
|
158
|
+
}.merge(oauth.sparams[:payload])
|
149
159
|
Log.log.debug("JWT jwt_payload=[#{jwt_payload}]")
|
150
|
-
rsa_private=oauth.
|
160
|
+
rsa_private = oauth.sparams[:private_key_obj] # type: OpenSSL::PKey::RSA
|
151
161
|
Log.log.debug("private=[#{rsa_private}]")
|
152
|
-
assertion = JWT.encode(jwt_payload, rsa_private, 'RS256', oauth.
|
162
|
+
assertion = JWT.encode(jwt_payload, rsa_private, 'RS256', oauth.sparams[:headers] || {})
|
153
163
|
Log.log.debug("assertion=[#{assertion}]")
|
154
|
-
return oauth.create_token(oauth.optional_scope_client_id(
|
164
|
+
return oauth.create_token(oauth.optional_scope_client_id.merge(grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: assertion))
|
165
|
+
},lambda { |oauth|
|
166
|
+
return [oauth.sparams.dig(:payload,:sub)]
|
155
167
|
}
|
156
168
|
|
157
|
-
attr_reader :
|
169
|
+
attr_reader :gparams, :sparams, :api
|
158
170
|
|
159
171
|
private
|
160
172
|
|
@@ -176,72 +188,80 @@ module Aspera
|
|
176
188
|
def initialize(a_params)
|
177
189
|
Log.log.debug("auth=#{a_params}")
|
178
190
|
# replace default values
|
179
|
-
@
|
180
|
-
|
181
|
-
|
182
|
-
|
191
|
+
@gparams = DEFAULT_CREATE_PARAMS.deep_merge(a_params)
|
192
|
+
# check that type is known
|
193
|
+
self.class.token_creator(@gparams[:crtype])
|
194
|
+
# specific parameters for the creation type
|
195
|
+
@sparams=@gparams[@gparams[:crtype]]
|
196
|
+
if @gparams[:crtype].eql?(:web) && @sparams.has_key?(:redirect_uri)
|
197
|
+
uri = URI.parse(@sparams[:redirect_uri])
|
198
|
+
raise 'redirect_uri scheme must be http or https' unless %w[http https].include?(uri.scheme)
|
183
199
|
raise 'redirect_uri must have a port' if uri.port.nil?
|
184
200
|
# TODO: we could check that host is localhost or local address
|
185
201
|
end
|
186
|
-
rest_params={
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
identifier=identifier.split(':').last if identifier.is_a?(String) && p.last.eql?(:grant_type)
|
199
|
-
parts.push(identifier) unless identifier.nil?
|
200
|
-
end
|
201
|
-
return IdGenerator.from_list(parts)
|
202
|
+
rest_params = {
|
203
|
+
base_url: @gparams[:base_url],
|
204
|
+
redirect_max: 2
|
205
|
+
}
|
206
|
+
rest_params[:auth] = a_params[:auth] if a_params.has_key?(:auth)
|
207
|
+
@api = Rest.new(rest_params)
|
208
|
+
# if needed use from api
|
209
|
+
@gparams.delete(:base_url)
|
210
|
+
@gparams.delete(:auth)
|
211
|
+
@gparams.delete(@gparams[:crtype])
|
212
|
+
Log.dump(:gparams,@gparams)
|
213
|
+
Log.dump(:sparams,@sparams)
|
202
214
|
end
|
203
215
|
|
204
216
|
public
|
205
217
|
|
206
218
|
# helper method to create token as per RFC
|
207
219
|
def create_token(www_params)
|
208
|
-
return @
|
220
|
+
return @api.call({
|
209
221
|
operation: 'POST',
|
210
|
-
subpath: @
|
211
|
-
headers: {'Accept'=>'application/json'},
|
222
|
+
subpath: @gparams[:path_token],
|
223
|
+
headers: {'Accept' => 'application/json'},
|
212
224
|
www_body_params: www_params})
|
213
225
|
end
|
214
226
|
|
215
|
-
#
|
216
|
-
def optional_scope_client_id(
|
217
|
-
call_params
|
218
|
-
call_params[:
|
219
|
-
call_params[:
|
227
|
+
# @return Hash with optional general parameters
|
228
|
+
def optional_scope_client_id(add_secret: false)
|
229
|
+
call_params={}
|
230
|
+
call_params[:scope] = @gparams[:scope] unless @gparams[:scope].nil?
|
231
|
+
call_params[:client_id] = @gparams[:client_id] unless @gparams[:client_id].nil?
|
232
|
+
call_params[:client_secret] = @gparams[:client_secret] if add_secret && !@gparams[:client_id].nil?
|
220
233
|
return call_params
|
221
234
|
end
|
222
235
|
|
223
236
|
# Oauth v2 token generation
|
224
237
|
# @param use_refresh_token set to true to force refresh or re-generation (if previous failed)
|
225
|
-
def get_authorization(use_refresh_token: false)
|
238
|
+
def get_authorization(use_refresh_token: false, use_cache: true)
|
226
239
|
# generate token unique identifier for persistency (memory/disk cache)
|
227
|
-
token_id=
|
240
|
+
token_id = IdGenerator.from_list([
|
241
|
+
PERSIST_CATEGORY_TOKEN,
|
242
|
+
@api.params[:base_url],
|
243
|
+
@gparams[:crtype],
|
244
|
+
self.class.id_creator(@gparams[:crtype]).call(self), # array, so we flatten later
|
245
|
+
@gparams[:scope],
|
246
|
+
@api.params.dig(%i[auth username])
|
247
|
+
].flatten)
|
228
248
|
|
229
249
|
# get token_data from cache (or nil), token_data is what is returned by /token
|
230
|
-
token_data=self.class.persist_mgr.get(token_id)
|
231
|
-
token_data=JSON.parse(token_data) unless token_data.nil?
|
250
|
+
token_data = self.class.persist_mgr.get(token_id) if use_cache
|
251
|
+
token_data = JSON.parse(token_data) unless token_data.nil?
|
232
252
|
# Optional optimization: check if node token is expired basd on decoded content then force refresh if close enough
|
233
253
|
# might help in case the transfer agent cannot refresh himself
|
234
254
|
# `direct` agent is equipped with refresh code
|
235
255
|
if !use_refresh_token && !token_data.nil?
|
236
|
-
decoded_token = self.class.decode_token(token_data[@
|
256
|
+
decoded_token = self.class.decode_token(token_data[@gparams[:token_field]])
|
237
257
|
Log.dump('decoded_token',decoded_token) unless decoded_token.nil?
|
238
258
|
if decoded_token.is_a?(Hash)
|
239
|
-
expires_at_sec=
|
240
|
-
|
241
|
-
|
242
|
-
|
259
|
+
expires_at_sec =
|
260
|
+
if decoded_token['expires_at'].is_a?(String) then DateTime.parse(decoded_token['expires_at']).to_time
|
261
|
+
elsif decoded_token['exp'].is_a?(Integer) then Time.at(decoded_token['exp'])
|
262
|
+
end
|
243
263
|
# force refresh if we see a token too close from expiration
|
244
|
-
use_refresh_token=true if expires_at_sec.is_a?(Time) && (expires_at_sec-Time.now) < TOKEN_EXPIRATION_GUARD_SEC
|
264
|
+
use_refresh_token = true if expires_at_sec.is_a?(Time) && (expires_at_sec - Time.now) < TOKEN_EXPIRATION_GUARD_SEC
|
245
265
|
Log.log.debug("Expiration: #{expires_at_sec} / #{use_refresh_token}")
|
246
266
|
end
|
247
267
|
end
|
@@ -250,21 +270,21 @@ module Aspera
|
|
250
270
|
if use_refresh_token
|
251
271
|
if token_data.is_a?(Hash) && token_data.has_key?('refresh_token')
|
252
272
|
# save possible refresh token, before deleting the cache
|
253
|
-
refresh_token=token_data['refresh_token']
|
273
|
+
refresh_token = token_data['refresh_token']
|
254
274
|
end
|
255
275
|
# delete cache
|
256
276
|
self.class.persist_mgr.delete(token_id)
|
257
|
-
token_data=nil
|
277
|
+
token_data = nil
|
258
278
|
# lets try the existing refresh token
|
259
279
|
if !refresh_token.nil?
|
260
280
|
Log.log.info("refresh=[#{refresh_token}]".bg_green)
|
261
281
|
# try to refresh
|
262
282
|
# note: AoC admin token has no refresh, and lives by default 1800secs
|
263
|
-
resp=create_token(optional_scope_client_id(
|
283
|
+
resp = create_token(optional_scope_client_id.merge(grant_type: 'refresh_token',refresh_token: refresh_token))
|
264
284
|
if resp[:http].code.start_with?('2')
|
265
285
|
# save only if success
|
266
|
-
json_data=resp[:http].body
|
267
|
-
token_data=JSON.parse(json_data)
|
286
|
+
json_data = resp[:http].body
|
287
|
+
token_data = JSON.parse(json_data)
|
268
288
|
self.class.persist_mgr.put(token_id,json_data)
|
269
289
|
else
|
270
290
|
Log.log.debug("refresh failed: #{resp[:http].body}".bg_red)
|
@@ -274,14 +294,14 @@ module Aspera
|
|
274
294
|
|
275
295
|
# no cache, nor refresh: generate a token
|
276
296
|
if token_data.nil?
|
277
|
-
resp=self.class.token_creator(@
|
278
|
-
json_data=resp[:http].body
|
279
|
-
token_data=JSON.parse(json_data)
|
297
|
+
resp = self.class.token_creator(@gparams[:crtype]).call(self)
|
298
|
+
json_data = resp[:http].body
|
299
|
+
token_data = JSON.parse(json_data)
|
280
300
|
self.class.persist_mgr.put(token_id,json_data)
|
281
301
|
end # if ! in_cache
|
282
|
-
raise "API error: No such field in answer: #{@
|
302
|
+
raise "API error: No such field in answer: #{@gparams[:token_field]}" unless token_data.has_key?(@gparams[:token_field])
|
283
303
|
# ok we shall have a token here
|
284
|
-
return 'Bearer '+token_data[@
|
304
|
+
return 'Bearer ' + token_data[@gparams[:token_field]]
|
285
305
|
end
|
286
306
|
end # OAuth
|
287
307
|
end # Aspera
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'aspera/log'
|
3
4
|
require 'aspera/environment'
|
4
5
|
require 'rbconfig'
|
@@ -11,7 +12,7 @@ module Aspera
|
|
11
12
|
class OpenApplication
|
12
13
|
include Singleton
|
13
14
|
# User Interfaces
|
14
|
-
def self.user_interfaces; [
|
15
|
+
def self.user_interfaces; %i[text graphical]; end
|
15
16
|
|
16
17
|
def self.default_gui_mode
|
17
18
|
return :graphical if [Aspera::Environment::OS_WINDOWS,Aspera::Environment::OS_X].include?(Aspera::Environment.os)
|
@@ -26,7 +27,7 @@ module Aspera
|
|
26
27
|
when Aspera::Environment::OS_X
|
27
28
|
return system('open',uri.to_s)
|
28
29
|
when Aspera::Environment::OS_WINDOWS
|
29
|
-
return system('start explorer "'+uri.to_s+'"')
|
30
|
+
return system('start explorer "' + uri.to_s + '"')
|
30
31
|
when Aspera::Environment::OS_LINUX
|
31
32
|
return system("xdg-open '#{uri}'")
|
32
33
|
else
|
@@ -37,7 +38,7 @@ module Aspera
|
|
37
38
|
attr_accessor :url_method
|
38
39
|
|
39
40
|
def initialize
|
40
|
-
@url_method=self.class.default_gui_mode
|
41
|
+
@url_method = self.class.default_gui_mode
|
41
42
|
end
|
42
43
|
|
43
44
|
# this is non blocking
|
@@ -48,9 +49,9 @@ module Aspera
|
|
48
49
|
when :text
|
49
50
|
case the_url.to_s
|
50
51
|
when /^http/
|
51
|
-
puts "USER ACTION: please enter this url in a browser:\n"+the_url.to_s.red
|
52
|
+
puts "USER ACTION: please enter this url in a browser:\n" + the_url.to_s.red + "\n"
|
52
53
|
else
|
53
|
-
puts "USER ACTION: open this:\n"+the_url.to_s.red
|
54
|
+
puts "USER ACTION: open this:\n" + the_url.to_s.red + "\n"
|
54
55
|
end
|
55
56
|
else
|
56
57
|
raise StandardError,"unsupported url open method: #{@url_method}"
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'json'
|
3
4
|
require 'aspera/log'
|
4
5
|
|
@@ -19,15 +20,15 @@ module Aspera
|
|
19
20
|
raise 'mandatory :data' if options[:data].nil?
|
20
21
|
raise 'mandatory :id (String)' unless options[:id].is_a?(String)
|
21
22
|
raise 'mandatory 1 element in :id' unless options[:id].length >= 1
|
22
|
-
@manager=options[:manager]
|
23
|
-
@persisted_object=options[:data]
|
24
|
-
@object_id=options[:id]
|
23
|
+
@manager = options[:manager]
|
24
|
+
@persisted_object = options[:data]
|
25
|
+
@object_id = options[:id]
|
25
26
|
# by default , at save time, file is deleted if data is nil
|
26
|
-
@delete_condition=options[:delete] || lambda{|d|d.empty?}
|
27
|
-
@persist_format=options[:format] || lambda {|h| JSON.generate(h)}
|
28
|
-
persist_parse=options[:parse] || lambda {|t| JSON.parse(t)}
|
29
|
-
persist_merge=options[:merge] || lambda {|current,file| current.concat(file).uniq rescue current}
|
30
|
-
value
|
27
|
+
@delete_condition = options[:delete] || lambda{|d|d.empty?}
|
28
|
+
@persist_format = options[:format] || lambda {|h| JSON.generate(h)}
|
29
|
+
persist_parse = options[:parse] || lambda {|t| JSON.parse(t)}
|
30
|
+
persist_merge = options[:merge] || lambda {|current,file| current.concat(file).uniq rescue current}
|
31
|
+
value = @manager.get(@object_id)
|
31
32
|
persist_merge.call(@persisted_object,persist_parse.call(value)) unless value.nil?
|
32
33
|
end
|
33
34
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'fileutils'
|
3
4
|
require 'aspera/log'
|
4
5
|
|
@@ -7,11 +8,11 @@ require 'aspera/log'
|
|
7
8
|
module Aspera
|
8
9
|
# Persist data on file system
|
9
10
|
class PersistencyFolder
|
10
|
-
FILE_SUFFIX='.txt'
|
11
|
+
FILE_SUFFIX = '.txt'
|
11
12
|
private_constant :FILE_SUFFIX
|
12
13
|
def initialize(folder)
|
13
|
-
@cache={}
|
14
|
-
@folder=folder
|
14
|
+
@cache = {}
|
15
|
+
@folder = folder
|
15
16
|
Log.log.debug("persistency folder: #{@folder}")
|
16
17
|
end
|
17
18
|
|
@@ -21,11 +22,11 @@ module Aspera
|
|
21
22
|
if @cache.has_key?(object_id)
|
22
23
|
Log.log.debug('got from memory cache')
|
23
24
|
else
|
24
|
-
persist_filepath=id_to_filepath(object_id)
|
25
|
+
persist_filepath = id_to_filepath(object_id)
|
25
26
|
Log.log.debug("persistency = #{persist_filepath}")
|
26
27
|
if File.exist?(persist_filepath)
|
27
28
|
Log.log.debug('got from file cache')
|
28
|
-
@cache[object_id]=File.read(persist_filepath)
|
29
|
+
@cache[object_id] = File.read(persist_filepath)
|
29
30
|
end
|
30
31
|
end
|
31
32
|
return @cache[object_id]
|
@@ -33,21 +34,21 @@ module Aspera
|
|
33
34
|
|
34
35
|
def put(object_id,value)
|
35
36
|
raise 'value: only String supported' unless value.is_a?(String)
|
36
|
-
persist_filepath=id_to_filepath(object_id)
|
37
|
+
persist_filepath = id_to_filepath(object_id)
|
37
38
|
Log.log.debug("persistency saving: #{persist_filepath}")
|
38
39
|
File.write(persist_filepath,value)
|
39
|
-
@cache[object_id]=value
|
40
|
+
@cache[object_id] = value
|
40
41
|
end
|
41
42
|
|
42
43
|
def delete(object_id)
|
43
|
-
persist_filepath=id_to_filepath(object_id)
|
44
|
+
persist_filepath = id_to_filepath(object_id)
|
44
45
|
Log.log.debug("persistency deleting: #{persist_filepath}")
|
45
46
|
File.delete(persist_filepath) if File.exist?(persist_filepath)
|
46
47
|
@cache.delete(object_id)
|
47
48
|
end
|
48
49
|
|
49
50
|
def garbage_collect(persist_category,max_age_seconds=nil)
|
50
|
-
garbage_files=Dir[File.join(@folder,persist_category+'*'+FILE_SUFFIX)]
|
51
|
+
garbage_files = Dir[File.join(@folder,persist_category + '*' + FILE_SUFFIX)]
|
51
52
|
if !max_age_seconds.nil?
|
52
53
|
current_time = Time.now
|
53
54
|
garbage_files.select! { |filepath| (current_time - File.stat(filepath).mtime).to_i > max_age_seconds}
|