aspera-cli 4.0.0.pre2 → 4.2.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
- data/README.md +761 -210
- data/bin/ascli +2 -0
- data/bin/dascli +13 -0
- data/docs/Makefile +2 -1
- data/docs/README.erb.md +628 -160
- data/docs/test_env.conf +22 -10
- data/docs/transfer_spec.html +1 -1
- data/lib/aspera/aoc.rb +87 -108
- data/lib/aspera/cli/formater.rb +2 -0
- data/lib/aspera/cli/main.rb +48 -45
- data/lib/aspera/cli/manager.rb +19 -6
- data/lib/aspera/cli/plugin.rb +9 -4
- data/lib/aspera/cli/plugins/alee.rb +1 -1
- data/lib/aspera/cli/plugins/aoc.rb +208 -183
- data/lib/aspera/cli/plugins/ats.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +205 -125
- data/lib/aspera/cli/plugins/console.rb +2 -2
- data/lib/aspera/cli/plugins/faspex.rb +15 -8
- data/lib/aspera/cli/plugins/faspex5.rb +76 -37
- data/lib/aspera/cli/plugins/node.rb +3 -3
- data/lib/aspera/cli/plugins/preview.rb +35 -25
- data/lib/aspera/cli/plugins/server.rb +23 -8
- data/lib/aspera/cli/transfer_agent.rb +7 -6
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +5 -1
- data/lib/aspera/cos_node.rb +33 -28
- data/lib/aspera/environment.rb +15 -4
- data/lib/aspera/fasp/connect.rb +28 -21
- data/lib/aspera/fasp/http_gw.rb +140 -28
- data/lib/aspera/fasp/installation.rb +119 -57
- data/lib/aspera/fasp/local.rb +174 -178
- data/lib/aspera/fasp/manager.rb +12 -0
- data/lib/aspera/fasp/node.rb +4 -4
- data/lib/aspera/fasp/parameters.rb +6 -18
- data/lib/aspera/fasp/resume_policy.rb +13 -12
- data/lib/aspera/log.rb +10 -2
- data/lib/aspera/node.rb +61 -1
- data/lib/aspera/oauth.rb +36 -13
- data/lib/aspera/persistency_folder.rb +9 -4
- data/lib/aspera/preview/file_types.rb +53 -21
- data/lib/aspera/preview/generator.rb +3 -3
- data/lib/aspera/rest.rb +29 -18
- data/lib/aspera/secrets.rb +20 -0
- data/lib/aspera/temp_file_manager.rb +19 -0
- metadata +40 -22
data/lib/aspera/fasp/manager.rb
CHANGED
|
@@ -45,6 +45,18 @@ module Aspera
|
|
|
45
45
|
end
|
|
46
46
|
end # notify_listeners
|
|
47
47
|
|
|
48
|
+
def notify_begin(id,size)
|
|
49
|
+
notify_listeners('emulated',{LISTENER_SESSION_ID_B=>id,'Type'=>'NOTIFICATION','PreTransferBytes'=>size})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def notify_progress(id,size)
|
|
53
|
+
notify_listeners('emulated',{LISTENER_SESSION_ID_B=>id,'Type'=>'STATS','Bytescont'=>size})
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def notify_end(id)
|
|
57
|
+
notify_listeners('emulated',{LISTENER_SESSION_ID_B=>id,'Type'=>'DONE'})
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
public
|
|
49
61
|
LISTENER_SESSION_ID_B='ListenerSessionId'
|
|
50
62
|
LISTENER_SESSION_ID_S='listener_session_id'
|
data/lib/aspera/fasp/node.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Aspera
|
|
|
55
55
|
trdata=node_api_.read("ops/transfers/#{@transfer_id}")[:data] || {"status"=>"unknown"} rescue {"status"=>"waiting(read error)"}
|
|
56
56
|
case trdata['status']
|
|
57
57
|
when 'completed'
|
|
58
|
-
|
|
58
|
+
notify_end(@transfer_id)
|
|
59
59
|
break
|
|
60
60
|
when 'waiting','partially_completed','unknown','waiting(read error)'
|
|
61
61
|
if spinner.nil?
|
|
@@ -69,10 +69,10 @@ module Aspera
|
|
|
69
69
|
#puts "running: sessions:#{trdata["sessions"].length}, #{trdata["sessions"].map{|i| i['bytes_transferred']}.join(',')}"
|
|
70
70
|
if !started and trdata['precalc'].is_a?(Hash) and
|
|
71
71
|
trdata['precalc']['status'].eql?('ready')
|
|
72
|
-
|
|
72
|
+
notify_begin(@transfer_id,trdata['precalc']['bytes_expected'])
|
|
73
73
|
started=true
|
|
74
74
|
else
|
|
75
|
-
|
|
75
|
+
notify_progress(@transfer_id,trdata['bytes_transferred'])
|
|
76
76
|
end
|
|
77
77
|
else
|
|
78
78
|
Log.log.warn("trdata -> #{trdata}")
|
|
@@ -81,7 +81,7 @@ module Aspera
|
|
|
81
81
|
sleep 1
|
|
82
82
|
end
|
|
83
83
|
#TODO get status of sessions
|
|
84
|
-
return []
|
|
84
|
+
return []
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
end
|
|
@@ -16,10 +16,6 @@ module Aspera
|
|
|
16
16
|
# because of garbage collection takes any file there
|
|
17
17
|
# this could be refined, as , for instance, on macos, temp folder is already user specific
|
|
18
18
|
@@file_list_folder=TempFileManager.instance.new_file_path_global('asession_filelists')
|
|
19
|
-
SEC_IN_DAY=86400
|
|
20
|
-
# assume no transfer last longer than this
|
|
21
|
-
# (garbage collect file list which were not deleted after transfer)
|
|
22
|
-
FILE_LIST_AGE_MAX_SEC=5*SEC_IN_DAY
|
|
23
19
|
PARAM_DEFINITION={
|
|
24
20
|
# parameters with env vars
|
|
25
21
|
'remote_password' => { :type => :envvar, :variable=>'ASPERA_SCP_PASS'},
|
|
@@ -63,7 +59,7 @@ module Aspera
|
|
|
63
59
|
'exclude_older_than' => { :type => :opt_with_arg, :accepted_types=>Integer},
|
|
64
60
|
'preserve_acls' => { :type => :opt_with_arg, :accepted_types=>String},
|
|
65
61
|
'move_after_transfer' => { :type => :opt_with_arg, :accepted_types=>String},
|
|
66
|
-
'multi_session_threshold' => { :type => :opt_with_arg, :accepted_types=>
|
|
62
|
+
'multi_session_threshold' => { :type => :opt_with_arg, :accepted_types=>Integer},
|
|
67
63
|
# non standard parameters
|
|
68
64
|
'EX_fasp_proxy_url' => { :type => :opt_with_arg, :option_switch=>'--proxy',:accepted_types=>String},
|
|
69
65
|
'EX_http_proxy_url' => { :type => :opt_with_arg, :option_switch=>'-x',:accepted_types=>String},
|
|
@@ -82,11 +78,12 @@ module Aspera
|
|
|
82
78
|
'lock_rate_policy' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
|
|
83
79
|
'lock_min_rate' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
|
|
84
80
|
'lock_target_rate' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
|
|
85
|
-
|
|
81
|
+
'authentication' => { :type => :ignore, :accepted_types=>String}, # value = token
|
|
86
82
|
'https_fallback_port' => { :type => :ignore, :accepted_types=>Integer}, # same as http fallback, option -t ?
|
|
87
83
|
'content_protection' => { :type => :ignore, :accepted_types=>String},
|
|
88
84
|
'cipher_allowed' => { :type => :ignore, :accepted_types=>String},
|
|
89
85
|
'multi_session' => { :type => :ignore, :accepted_types=>Integer}, # managed
|
|
86
|
+
'obfuscate_file_names' => { :type => :ignore, :accepted_types=>Aspera::CommandLineBuilder::BOOLEAN_CLASSES},
|
|
90
87
|
# optional tags ( additional option to generate: {:space=>' ',:object_nl=>' ',:space_before=>'+',:array_nl=>'1'} )
|
|
91
88
|
'tags' => { :type => :opt_with_arg, :option_switch=>'--tags64',:accepted_types=>Hash,:encode=>lambda{|tags|Base64.strict_encode64(JSON.generate(tags))}},
|
|
92
89
|
# special processing @builder.process_param( called individually
|
|
@@ -100,7 +97,7 @@ module Aspera
|
|
|
100
97
|
'wss_port' => { :type => :defer, :accepted_types=>Integer},
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
private_constant :
|
|
100
|
+
private_constant :PARAM_DEFINITION
|
|
104
101
|
|
|
105
102
|
def initialize(job_spec,options)
|
|
106
103
|
@job_spec=job_spec
|
|
@@ -207,18 +204,9 @@ module Aspera
|
|
|
207
204
|
# temp file list files are created here
|
|
208
205
|
def self.file_list_folder=(v)
|
|
209
206
|
@@file_list_folder=v
|
|
210
|
-
|
|
207
|
+
if !@@file_list_folder.nil?
|
|
211
208
|
FileUtils.mkdir_p(@@file_list_folder)
|
|
212
|
-
|
|
213
|
-
Dir.entries(@@file_list_folder).each do |name|
|
|
214
|
-
file_path=File.join(@@file_list_folder,name)
|
|
215
|
-
age_sec=(Time.now - File.stat(file_path).mtime).to_i
|
|
216
|
-
# check age of file, delete too old
|
|
217
|
-
if File.file?(file_path) and age_sec > FILE_LIST_AGE_MAX_SEC
|
|
218
|
-
Log.log.debug("garbage collecting #{name}")
|
|
219
|
-
File.delete(file_path)
|
|
220
|
-
end
|
|
221
|
-
end
|
|
209
|
+
TempFileManager.instance.cleanup_expired(@@file_list_folder)
|
|
222
210
|
end
|
|
223
211
|
end
|
|
224
212
|
|
|
@@ -14,17 +14,21 @@ module Aspera
|
|
|
14
14
|
:sleep_max => 60
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
# @param params see DEFAULTS
|
|
18
|
+
def initialize(params=nil)
|
|
18
19
|
@parameters=DEFAULTS.clone
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
if !params.nil?
|
|
21
|
+
raise "expecting Hash (or nil), but have #{params.class}" unless params.is_a?(Hash)
|
|
22
|
+
params.each do |k,v|
|
|
23
|
+
if DEFAULTS.has_key?(k)
|
|
24
|
+
raise "#{k} must be Integer" unless v.is_a?(Integer)
|
|
25
|
+
@parameters[k]=v
|
|
26
|
+
else
|
|
27
|
+
raise "unknown resume parameter: #{k}, expect one of #{DEFAULTS.keys.map{|i|i.to_s}.join(",")}"
|
|
28
|
+
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
31
|
+
Log.log.debug("resume params=#{@parameters}")
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
# calls block a number of times (resumes) until success or limit reached
|
|
@@ -45,10 +49,7 @@ module Aspera
|
|
|
45
49
|
# failure in ascp
|
|
46
50
|
if e.retryable? then
|
|
47
51
|
# exit if we exceed the max number of retry
|
|
48
|
-
|
|
49
|
-
Log.log.error "Maximum number of retry reached"
|
|
50
|
-
raise Fasp::Error,"max retry after: [#{status[:message]}]"
|
|
51
|
-
end
|
|
52
|
+
raise Fasp::Error,'Maximum number of retry reached' if remaining_resumes <= 0
|
|
52
53
|
else
|
|
53
54
|
# give one chance only to non retryable errors
|
|
54
55
|
unless remaining_resumes.eql?(@parameters[:iter_max])
|
data/lib/aspera/log.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Aspera
|
|
|
14
14
|
attr_reader :logger
|
|
15
15
|
attr_reader :logger_type
|
|
16
16
|
# levels are :debug,:info,:warn,:error,fatal,:unknown
|
|
17
|
-
def self.levels; Logger::Severity.constants.map{|c|
|
|
17
|
+
def self.levels; Logger::Severity.constants.sort{|a,b|Logger::Severity.const_get(a)<=>Logger::Severity.const_get(b)}.map{|c|c.downcase.to_sym};end
|
|
18
18
|
|
|
19
19
|
# where logs are sent to
|
|
20
20
|
def self.logtypes; [:stderr,:stdout,:syslog];end
|
|
@@ -49,7 +49,15 @@ module Aspera
|
|
|
49
49
|
|
|
50
50
|
# change underlying logger, but keep log level
|
|
51
51
|
def logger_type=(new_logtype)
|
|
52
|
-
current_severity_integer
|
|
52
|
+
current_severity_integer=if @logger.nil?
|
|
53
|
+
if ENV.has_key?('AS_LOG_LEVEL')
|
|
54
|
+
ENV['AS_LOG_LEVEL']
|
|
55
|
+
else
|
|
56
|
+
Logger::Severity::WARN
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
@logger.level
|
|
60
|
+
end
|
|
53
61
|
case new_logtype
|
|
54
62
|
when :stderr
|
|
55
63
|
@logger = Logger.new(STDERR)
|
data/lib/aspera/node.rb
CHANGED
|
@@ -1,14 +1,74 @@
|
|
|
1
|
+
require 'aspera/rest'
|
|
2
|
+
require 'aspera/log'
|
|
1
3
|
require 'zlib'
|
|
2
4
|
require 'base64'
|
|
5
|
+
|
|
3
6
|
module Aspera
|
|
4
7
|
# Provides additional functions using node API.
|
|
5
8
|
class Node < Rest
|
|
9
|
+
# permissions
|
|
10
|
+
ACCESS_LEVELS=['delete','list','mkdir','preview','read','rename','write']
|
|
11
|
+
MATCH_EXEC_PREFIX='exec:'
|
|
12
|
+
|
|
13
|
+
# for information only
|
|
6
14
|
def self.decode_bearer_token(token)
|
|
7
15
|
return JSON.parse(Zlib::Inflate.inflate(Base64.decode64(token)).partition('==SIGNATURE==').first)
|
|
8
16
|
end
|
|
17
|
+
|
|
18
|
+
# for access keys: provide expression to match entry in folder
|
|
19
|
+
# if no prefix: regex
|
|
20
|
+
# if prefix: ruby code
|
|
21
|
+
# if filder is nil, then always match
|
|
22
|
+
def self.file_matcher(match_expression)
|
|
23
|
+
match_expression||="#{MATCH_EXEC_PREFIX}true"
|
|
24
|
+
if match_expression.start_with?(MATCH_EXEC_PREFIX)
|
|
25
|
+
return eval "lambda{|f|#{match_expression[MATCH_EXEC_PREFIX.length..-1]}}"
|
|
26
|
+
end
|
|
27
|
+
return lambda{|f|f['name'].match(/#{match_expression}/)}
|
|
28
|
+
end
|
|
29
|
+
|
|
9
30
|
def initialize(rest_params)
|
|
10
31
|
super(rest_params)
|
|
11
|
-
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# recursively crawl in a folder.
|
|
35
|
+
# subfolders a processed if the processing method returns true
|
|
36
|
+
# @param processor must provide a method to process each entry
|
|
37
|
+
# @param opt options
|
|
38
|
+
# - top_file_id file id to start at (default = access key root file id)
|
|
39
|
+
# - top_file_path path of top folder (default = /)
|
|
40
|
+
# - method processing method (default= process_entry)
|
|
41
|
+
def crawl(processor,opt={})
|
|
42
|
+
Log.log.debug("crawl1 #{opt}")
|
|
43
|
+
# not possible with bearer token
|
|
44
|
+
opt[:top_file_id] ||= read('access_keys/self')[:data]['root_file_id']
|
|
45
|
+
opt[:top_file_path] ||= '/'
|
|
46
|
+
opt[:method] ||= :process_entry
|
|
47
|
+
raise "processor must have #{opt[:method]}" unless processor.respond_to?(opt[:method])
|
|
48
|
+
Log.log.debug("crawl #{opt}")
|
|
49
|
+
#top_info=read("files/#{opt[:top_file_id]}")[:data]
|
|
50
|
+
folders_to_explore=[{id: opt[:top_file_id], relpath: opt[:top_file_path]}]
|
|
51
|
+
Log.dump(:folders_to_explore,folders_to_explore)
|
|
52
|
+
while !folders_to_explore.empty? do
|
|
53
|
+
current_item = folders_to_explore.shift
|
|
54
|
+
Log.log.debug("searching #{current_item[:relpath]}".bg_green)
|
|
55
|
+
# get folder content
|
|
56
|
+
folder_contents = begin
|
|
57
|
+
read("files/#{current_item[:id]}/files")[:data]
|
|
58
|
+
rescue => e
|
|
59
|
+
Log.log.warn("#{current_item[:relpath]}: #{e.class} #{e.message}")
|
|
60
|
+
[]
|
|
61
|
+
end
|
|
62
|
+
Log.dump(:folder_contents,folder_contents)
|
|
63
|
+
folder_contents.each do |entry|
|
|
64
|
+
relative_path=File.join(current_item[:relpath],entry['name'])
|
|
65
|
+
Log.log.debug("looking #{relative_path}".bg_green)
|
|
66
|
+
# entry type is file, folder or link
|
|
67
|
+
if processor.send(opt[:method],entry,relative_path) and entry['type'].eql?('folder')
|
|
68
|
+
folders_to_explore.push({:id=>entry['id'],:relpath=>relative_path})
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
12
72
|
end
|
|
13
73
|
end
|
|
14
74
|
end
|
data/lib/aspera/oauth.rb
CHANGED
|
@@ -16,7 +16,10 @@ module Aspera
|
|
|
16
16
|
# one hour validity (TODO: configurable?)
|
|
17
17
|
JWT_EXPIRY_OFFSET=3600
|
|
18
18
|
PERSIST_CATEGORY_TOKEN='token'
|
|
19
|
-
|
|
19
|
+
# tokens older than 30 minutes will be discarded
|
|
20
|
+
TOKEN_EXPIRY_SECONDS=1800
|
|
21
|
+
THANK_YOU_HTML = "<html><head><title>Ok</title></head><body><h1>Thank you !</h1><p>You can close this window.</p></body></html>"
|
|
22
|
+
private_constant :JWT_NOTBEFORE_OFFSET,:JWT_EXPIRY_OFFSET,:PERSIST_CATEGORY_TOKEN,:TOKEN_EXPIRY_SECONDS,:THANK_YOU_HTML
|
|
20
23
|
class << self
|
|
21
24
|
# OAuth methods supported
|
|
22
25
|
def auth_types
|
|
@@ -28,12 +31,18 @@ module Aspera
|
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
def persist_mgr
|
|
31
|
-
|
|
34
|
+
if @persist.nil?
|
|
35
|
+
Log.log.warn('Not using persistency (use Aspera::Oauth.persist_mgr=Aspera::PersistencyFolder.new)')
|
|
36
|
+
# create NULL persistency class
|
|
37
|
+
@persist=Class.new do
|
|
38
|
+
def get(x);nil;end;def delete(x);nil;end;def put(x,y);nil;end;def garbage_collect(x,y);nil;end
|
|
39
|
+
end.new
|
|
40
|
+
end
|
|
32
41
|
return @persist
|
|
33
42
|
end
|
|
34
43
|
|
|
35
44
|
def flush_tokens
|
|
36
|
-
persist_mgr.
|
|
45
|
+
persist_mgr.garbage_collect(PERSIST_CATEGORY_TOKEN,nil)
|
|
37
46
|
end
|
|
38
47
|
end
|
|
39
48
|
|
|
@@ -79,10 +88,10 @@ module Aspera
|
|
|
79
88
|
raise "redirect_uri must have a port" if uri.port.nil?
|
|
80
89
|
# we could check that host is localhost or local address
|
|
81
90
|
end
|
|
91
|
+
# cleanup expired tokens
|
|
92
|
+
self.class.persist_mgr.garbage_collect(PERSIST_CATEGORY_TOKEN,TOKEN_EXPIRY_SECONDS)
|
|
82
93
|
end
|
|
83
94
|
|
|
84
|
-
THANK_YOU_HTML = "<html><head><title>Ok</title></head><body><h1>Thank you !</h1><p>You can close this window.</p></body></html>"
|
|
85
|
-
|
|
86
95
|
# open the login page, wait for code and check_code, then return code
|
|
87
96
|
def goto_page_and_get_code(login_page_url,check_code)
|
|
88
97
|
request_params=self.class.goto_page_and_get_request(@params[:redirect_uri],login_page_url)
|
|
@@ -117,6 +126,9 @@ module Aspera
|
|
|
117
126
|
|
|
118
127
|
public
|
|
119
128
|
|
|
129
|
+
# used to change parameter, such as scope
|
|
130
|
+
attr_reader :params
|
|
131
|
+
|
|
120
132
|
# @param options : :scope and :refresh
|
|
121
133
|
def get_authorization(options={})
|
|
122
134
|
# api scope can be overriden to get auth for other scope
|
|
@@ -184,14 +196,13 @@ module Aspera
|
|
|
184
196
|
when :web
|
|
185
197
|
# AoC Web based Auth
|
|
186
198
|
check_code=SecureRandom.uuid
|
|
187
|
-
|
|
188
|
-
"#{@params[:base_url]}/#{@params[:path_authorize]}",
|
|
189
|
-
p_client_id_and_scope.merge({
|
|
199
|
+
auth_params=p_client_id_and_scope.merge({
|
|
190
200
|
:response_type => 'code',
|
|
191
201
|
:redirect_uri => @params[:redirect_uri],
|
|
192
|
-
:client_secret => @params[:client_secret],
|
|
193
202
|
:state => check_code
|
|
194
|
-
})
|
|
203
|
+
})
|
|
204
|
+
auth_params[:client_secret]=@params[:client_secret] if @params.has_key?(:client_secret)
|
|
205
|
+
login_page_url=Rest.build_uri("#{@params[:base_url]}/#{@params[:path_authorize]}",auth_params)
|
|
195
206
|
# here, we need a human to authorize on a web page
|
|
196
207
|
code=goto_page_and_get_code(login_page_url,check_code)
|
|
197
208
|
# exchange code for token
|
|
@@ -214,6 +225,16 @@ module Aspera
|
|
|
214
225
|
:nbf => seconds_since_epoch-JWT_NOTBEFORE_OFFSET, # not before
|
|
215
226
|
:exp => seconds_since_epoch+JWT_EXPIRY_OFFSET # expiration
|
|
216
227
|
}
|
|
228
|
+
# Hum.. compliant ? TODO: remove when Faspex5 API is clarified
|
|
229
|
+
if @params[:jwt_is_f5]
|
|
230
|
+
payload[:jti] = SecureRandom.uuid
|
|
231
|
+
payload[:iat] = seconds_since_epoch
|
|
232
|
+
payload.delete(:nbf)
|
|
233
|
+
p_scope[:redirect_uri]="https://127.0.0.1:5000/token"
|
|
234
|
+
p_scope[:state]=SecureRandom.uuid
|
|
235
|
+
p_scope[:client_id]=@params[:client_id]
|
|
236
|
+
@token_auth_api.params[:auth]={:type=>:none}
|
|
237
|
+
end
|
|
217
238
|
|
|
218
239
|
# non standard, only for global ids
|
|
219
240
|
payload.merge!(@params[:jwt_add]) if @params.has_key?(:jwt_add)
|
|
@@ -222,8 +243,8 @@ module Aspera
|
|
|
222
243
|
|
|
223
244
|
Log.log.debug("private=[#{rsa_private}]")
|
|
224
245
|
|
|
225
|
-
Log.log.debug("JWT
|
|
226
|
-
assertion = JWT.encode(payload, rsa_private, 'RS256')
|
|
246
|
+
Log.log.debug("JWT payload=[#{payload}]")
|
|
247
|
+
assertion = JWT.encode(payload, rsa_private, 'RS256',@params[:jwt_headers]||{})
|
|
227
248
|
|
|
228
249
|
Log.log.debug("assertion=[#{assertion}]")
|
|
229
250
|
|
|
@@ -233,8 +254,10 @@ module Aspera
|
|
|
233
254
|
}))
|
|
234
255
|
when :url_token
|
|
235
256
|
# AoC Public Link
|
|
257
|
+
params={:url_token=>@params[:url_token]}
|
|
258
|
+
params[:password]=@params[:password] if @params.has_key?(:password)
|
|
236
259
|
resp=create_token_advanced({
|
|
237
|
-
:json_params =>
|
|
260
|
+
:json_params => params,
|
|
238
261
|
:url_params => p_scope.merge({
|
|
239
262
|
:grant_type => 'url_token'
|
|
240
263
|
})})
|
|
@@ -55,12 +55,17 @@ module Aspera
|
|
|
55
55
|
@cache.delete(object_id)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
def garbage_collect(persist_category,max_age_seconds=nil)
|
|
59
|
+
garbage_files=Dir[File.join(@folder,persist_category+'*'+FILE_SUFFIX)]
|
|
60
|
+
if !max_age_seconds.nil?
|
|
61
|
+
current_time = Time.now
|
|
62
|
+
garbage_files.select! { |filepath| (current_time - File.stat(filepath).mtime).to_i > max_age_seconds}
|
|
63
|
+
end
|
|
64
|
+
garbage_files.each do |filepath|
|
|
61
65
|
File.delete(filepath)
|
|
66
|
+
Log.log.debug("Deleted expired: #{filepath}")
|
|
62
67
|
end
|
|
63
|
-
return
|
|
68
|
+
return garbage_files
|
|
64
69
|
end
|
|
65
70
|
|
|
66
71
|
private
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
1
|
+
require 'aspera/log'
|
|
2
|
+
require 'singleton'
|
|
3
3
|
|
|
4
4
|
module Aspera
|
|
5
5
|
module Preview
|
|
6
|
+
# function conversion_type returns one of the types: CONVERSION_TYPES
|
|
6
7
|
class FileTypes
|
|
8
|
+
include Singleton
|
|
7
9
|
# values for conversion_type : input format
|
|
8
10
|
CONVERSION_TYPES=[
|
|
9
11
|
:image,
|
|
@@ -148,6 +150,7 @@ module Aspera
|
|
|
148
150
|
'dif' => :office,
|
|
149
151
|
'divx' => :video,
|
|
150
152
|
'dng' => :image,
|
|
153
|
+
'docx' => :office,
|
|
151
154
|
'dpx' => :image,
|
|
152
155
|
'epdf' => :image,
|
|
153
156
|
'epi' => :image,
|
|
@@ -204,6 +207,7 @@ module Aspera
|
|
|
204
207
|
'pam' => :image,
|
|
205
208
|
'pcd' => :image,
|
|
206
209
|
'pcds' => :image,
|
|
210
|
+
'pdf' => :pdf,
|
|
207
211
|
'pef' => :image,
|
|
208
212
|
'picon' => :image,
|
|
209
213
|
'pict' => :image,
|
|
@@ -261,39 +265,67 @@ module Aspera
|
|
|
261
265
|
'x3f' => :image,
|
|
262
266
|
'xcf' => :image,
|
|
263
267
|
'xlk' => :office,
|
|
268
|
+
'xlsx' => :office,
|
|
269
|
+
'xls' => :office,
|
|
264
270
|
'ycbcr' => :image,
|
|
265
271
|
'ycbcra' => :image,
|
|
266
272
|
'yuv' => :image,
|
|
267
273
|
'zabw' => :office}
|
|
268
274
|
|
|
269
|
-
|
|
275
|
+
private_constant :SUPPORTED_MIME_TYPES, :SUPPORTED_EXTENSIONS
|
|
270
276
|
|
|
271
|
-
|
|
277
|
+
# @attr use_mimemagic [bool] true to use mimemagic to determine real mime type based on file content
|
|
278
|
+
attr_accessor :use_mimemagic
|
|
279
|
+
|
|
280
|
+
def initialize
|
|
281
|
+
@use_mimemagic=false
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# use mime magic to find mime type based on file content (magic numbers)
|
|
285
|
+
def mime_from_file(filepath)
|
|
286
|
+
# moved here, as mimemagic can cause installation issues
|
|
287
|
+
require 'mimemagic'
|
|
288
|
+
require 'mimemagic/version'
|
|
289
|
+
require 'mimemagic/overlay' if MimeMagic::VERSION.start_with?('0.3.')
|
|
290
|
+
# check magic number inside file (empty string if not found)
|
|
272
291
|
detected_mime=MimeMagic.by_magic(File.open(filepath)).to_s
|
|
273
|
-
|
|
292
|
+
# check extension only
|
|
293
|
+
if !SUPPORTED_MIME_TYPES.has_key?(detected_mime)
|
|
294
|
+
Log.log.debug("no conversion for #{detected_mime}, trying extension")
|
|
295
|
+
detected_mime=MimeMagic.by_extension(File.extname(filepath)).to_s
|
|
296
|
+
end
|
|
274
297
|
detected_mime=nil if detected_mime.empty?
|
|
298
|
+
Log.log.debug("mimemagic: #{detected_mime.class.name} [#{detected_mime}]")
|
|
275
299
|
return detected_mime
|
|
276
300
|
end
|
|
277
301
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
302
|
+
# return file type, one of enum CONVERSION_TYPES
|
|
303
|
+
# @param filepath [String] full path to file
|
|
304
|
+
# @param mimetype [String] provided by node api
|
|
305
|
+
def conversion_type(filepath,mimetype)
|
|
306
|
+
Log.log.debug("conversion_type(#{filepath},m=#{mimetype},t=#{@use_mimemagic})")
|
|
307
|
+
# 1- get type from provided mime type, using local mapping
|
|
308
|
+
conv_type=SUPPORTED_MIME_TYPES[mimetype] if ! mimetype.nil?
|
|
309
|
+
# 2- else, from computed mime type (if available)
|
|
310
|
+
if conv_type.nil? and @use_mimemagic
|
|
281
311
|
detected_mime=mime_from_file(filepath)
|
|
282
|
-
if
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
312
|
+
if ! detected_mime.nil?
|
|
313
|
+
conv_type=SUPPORTED_MIME_TYPES[detected_mime]
|
|
314
|
+
if ! mimetype.nil?
|
|
315
|
+
if mimetype.eql?(detected_mime)
|
|
316
|
+
Log.log.debug("matching mime type per magic number")
|
|
317
|
+
else
|
|
318
|
+
# note: detected can be nil
|
|
319
|
+
Log.log.debug("non matching mime types: node=[#{mimetype}], magic=[#{detected_mime}]")
|
|
320
|
+
end
|
|
321
|
+
end
|
|
287
322
|
end
|
|
288
323
|
end
|
|
289
|
-
#
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# 3- else, from local extensions
|
|
295
|
-
result||=FileTypes::SUPPORTED_EXTENSIONS[extension]
|
|
296
|
-
return result
|
|
324
|
+
# 3- else, from extensions, using local mapping
|
|
325
|
+
extension = File.extname(filepath.downcase)[1..-1]
|
|
326
|
+
conv_type=SUPPORTED_EXTENSIONS[extension] if conv_type.nil?
|
|
327
|
+
Log.log.debug("conversion_type(#{extension}): #{conv_type.class.name} [#{conv_type}]")
|
|
328
|
+
return conv_type
|
|
297
329
|
end
|
|
298
330
|
end
|
|
299
331
|
end
|