aspera-cli 4.21.1 → 4.21.2
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 +4 -2
- data/CHANGELOG.md +22 -10
- data/CONTRIBUTING.md +68 -143
- data/README.md +175 -145
- data/bin/ascli +5 -14
- data/bin/asession +1 -3
- data/examples/get_proto_file.rb +4 -3
- data/examples/proxy.pac +20 -20
- data/lib/aspera/agent/base.rb +2 -0
- data/lib/aspera/agent/{alpha.rb → desktop.rb} +7 -7
- data/lib/aspera/agent/direct.rb +9 -1
- data/lib/aspera/agent/transferd.rb +24 -17
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/cos_node.rb +1 -1
- data/lib/aspera/api/node.rb +4 -4
- data/lib/aspera/ascp/installation.rb +40 -58
- data/lib/aspera/cli/extended_value.rb +9 -3
- data/lib/aspera/cli/formatter.rb +8 -1
- data/lib/aspera/cli/info.rb +1 -0
- data/lib/aspera/cli/main.rb +2 -2
- data/lib/aspera/cli/manager.rb +3 -3
- data/lib/aspera/cli/plugins/aoc.rb +106 -55
- data/lib/aspera/cli/plugins/config.rb +10 -31
- data/lib/aspera/cli/plugins/faspex5.rb +8 -5
- data/lib/aspera/cli/plugins/node.rb +6 -3
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/coverage.rb +5 -3
- data/lib/aspera/environment.rb +20 -11
- data/lib/aspera/faspex_postproc.rb +3 -5
- data/lib/aspera/hash_ext.rb +2 -12
- data/lib/aspera/oauth/base.rb +6 -1
- data/lib/aspera/preview/generator.rb +12 -9
- data/lib/aspera/preview/options.rb +2 -2
- data/lib/aspera/preview/terminal.rb +1 -1
- data/lib/aspera/preview/utils.rb +4 -4
- data/lib/aspera/products/connect.rb +34 -0
- data/lib/aspera/products/{alpha.rb → desktop.rb} +2 -2
- data/lib/aspera/products/transferd.rb +8 -1
- data/lib/aspera/rest.rb +4 -4
- data/lib/aspera/secret_hider.rb +7 -0
- data/lib/aspera/temp_file_manager.rb +5 -4
- data/lib/aspera/uri_reader.rb +18 -1
- data.tar.gz.sig +0 -0
- metadata +4 -174
- metadata.gz.sig +0 -0
- data/examples/build_exec +0 -74
- data/examples/build_exec_rubyc +0 -40
@@ -348,7 +348,7 @@ module Aspera
|
|
348
348
|
transfer.agent_instance = Agent::Node.new(
|
349
349
|
url: client_apfid[:api].base_url,
|
350
350
|
username: client_apfid[:api].app_info[:node_info]['access_key'],
|
351
|
-
password: client_apfid[:api].oauth.
|
351
|
+
password: client_apfid[:api].oauth.authorization,
|
352
352
|
root_id: client_apfid[:file_id]
|
353
353
|
)
|
354
354
|
# additional node to node TS info
|
@@ -473,60 +473,111 @@ module Aspera
|
|
473
473
|
when :subscription
|
474
474
|
org = aoc_api.read('organization')
|
475
475
|
bss_graphql = api_from_options('bss/platform/graphql')
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
476
|
+
command_subscription = options.get_next_command(%i[account usage])
|
477
|
+
case command_subscription
|
478
|
+
when :account
|
479
|
+
# cspell:disable
|
480
|
+
graphql_query = <<-GRAPHQL
|
481
|
+
query ($organization_id: ID!) {
|
482
|
+
aoc (organization_id: $organization_id) {
|
483
|
+
bssSubscription {
|
484
|
+
aocVersion
|
485
|
+
endDate
|
486
|
+
startDate
|
487
|
+
termMonths
|
488
|
+
plan
|
489
|
+
trial
|
490
|
+
termType
|
491
|
+
aocOrganizations {
|
492
|
+
id
|
493
|
+
}
|
494
|
+
additionalStorageVolumeGb
|
495
|
+
additionalEgressVolumeGb
|
496
|
+
term {
|
497
|
+
startDate
|
498
|
+
endDate
|
499
|
+
transferVolumeGb
|
500
|
+
egressVolumeGb
|
501
|
+
storageVolumeGb
|
502
|
+
transferVolumeOffsetGb
|
503
|
+
}
|
504
|
+
paygoRate {
|
505
|
+
transferRate
|
506
|
+
storageRate
|
507
|
+
currency
|
508
|
+
}
|
509
|
+
aocPlanData {
|
510
|
+
tier
|
511
|
+
trial
|
512
|
+
workspaces { max }
|
513
|
+
users {
|
514
|
+
planAmount
|
515
|
+
max
|
516
|
+
}
|
517
|
+
samlIntegration
|
518
|
+
activity
|
519
|
+
sharedInboxes
|
520
|
+
uniqueUrls
|
521
|
+
support
|
522
|
+
watermarking
|
523
|
+
byok
|
524
|
+
automation { planAmount, max }
|
525
|
+
}
|
526
|
+
}
|
527
|
+
}
|
513
528
|
}
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
529
|
+
GRAPHQL
|
530
|
+
# cspell:enable
|
531
|
+
result = bss_graphql.create(nil, {query: graphql_query, variables: {organization_id: org['id']}})['data']
|
532
|
+
return {type: :single_object, data: result['aoc']['bssSubscription']}
|
533
|
+
when :usage
|
534
|
+
# cspell:disable
|
535
|
+
graphql_query = <<-GRAPHQL
|
536
|
+
query ($organization_id: ID!, $startDate: Date!, $endDate: Date!, $aggregate: TransferUsageAggregateOption!) {
|
537
|
+
aoc (organization_id: $organization_id) {
|
538
|
+
bssSubscription {
|
539
|
+
aocOrganizations { id }
|
540
|
+
additionalStorageVolumeGb
|
541
|
+
additionalEgressVolumeGb
|
542
|
+
aocPlanData {
|
543
|
+
tier
|
544
|
+
trial
|
545
|
+
}
|
546
|
+
term {
|
547
|
+
transferVolumeGb
|
548
|
+
egressVolumeGb
|
549
|
+
storageVolumeGb
|
550
|
+
startDate
|
551
|
+
endDate
|
552
|
+
transferVolumeOffsetGb
|
553
|
+
}
|
554
|
+
termMonths
|
555
|
+
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
556
|
+
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
557
|
+
}
|
558
|
+
subscriptionEntitlements {
|
559
|
+
id
|
560
|
+
transferUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { mbTotal }
|
561
|
+
egressUsages (startDate: $startDate, endDate: $endDate, aggregate: $aggregate) { usageMb }
|
562
|
+
}
|
563
|
+
}
|
564
|
+
}
|
565
|
+
GRAPHQL
|
566
|
+
aggregate = options.get_next_argument('aggregation', accept_list: %i[ALL MONTHLY], default: :ALL)
|
567
|
+
today = Date.today
|
568
|
+
start_date = options.get_next_argument('start date', mandatory: false, default: today.prev_year.strftime('%Y-%m-%d'))
|
569
|
+
end_date = options.get_next_argument('end date', mandatory: false, default: today.strftime('%Y-%m-%d'))
|
570
|
+
# cspell:enable
|
571
|
+
result = bss_graphql.create(
|
572
|
+
nil,
|
573
|
+
{query: graphql_query,
|
574
|
+
variables: {
|
575
|
+
organization_id: org['id'],
|
576
|
+
aggregate: aggregate,
|
577
|
+
startDate: start_date,
|
578
|
+
endDate: end_date}})['data']
|
579
|
+
return {type: :single_object, data: result['aoc']}
|
580
|
+
end
|
530
581
|
when :ats
|
531
582
|
ats_api = Rest.new(**aoc_api.params.deep_merge({
|
532
583
|
base_url: "#{aoc_api.base_url}/admin/ats/pub/v1",
|
@@ -690,7 +741,7 @@ module Aspera
|
|
690
741
|
when :servers
|
691
742
|
return {type: :object_list, data: Rest.new(base_url: "#{Api::AoC.api_base_url}/#{Api::AoC::API_V1}").read('servers')}
|
692
743
|
when :bearer_token
|
693
|
-
return {type: :text, data: aoc_api.oauth.
|
744
|
+
return {type: :text, data: aoc_api.oauth.authorization}
|
694
745
|
when :organization
|
695
746
|
return { type: :single_object, data: aoc_api.read('organization') }
|
696
747
|
when :tier_restrictions
|
@@ -54,8 +54,6 @@ module Aspera
|
|
54
54
|
ASPERA = 'aspera'
|
55
55
|
SERVER_COMMAND = 'server'
|
56
56
|
DIR_SDK = 'sdk'
|
57
|
-
CONNECT_WEB_URL = 'https://d3gcli72yxqn2z.cloudfront.net/connect'
|
58
|
-
CONNECT_VERSIONS = 'connectversions.js' # cspell: disable-line
|
59
57
|
DEMO_SERVER = 'demo'
|
60
58
|
DEMO_PRESET = 'demoserver' # cspell: disable-line
|
61
59
|
EMAIL_TEST_TEMPLATE = <<~END_OF_TEMPLATE
|
@@ -150,7 +148,6 @@ module Aspera
|
|
150
148
|
@use_plugin_defaults = true
|
151
149
|
@config_presets = nil
|
152
150
|
@config_checksum_on_disk = nil
|
153
|
-
@connect_versions = nil
|
154
151
|
@vault = nil
|
155
152
|
@pac_exec = nil
|
156
153
|
@sdk_default_location = false
|
@@ -213,7 +210,8 @@ module Aspera
|
|
213
210
|
# Transfer SDK options
|
214
211
|
options.declare(:ascp_path, 'Path to ascp', handler: {o: Ascp::Installation.instance, m: :ascp_path})
|
215
212
|
options.declare(:use_product, 'Use ascp from specified product', handler: {o: self, m: :option_use_product})
|
216
|
-
options.declare(:sdk_url, 'URL to get
|
213
|
+
options.declare(:sdk_url, 'URL to get Aspera Transfer Daemon', default: SpecialValues::DEF)
|
214
|
+
options.declare(:locations_url, 'URL to get locations of Aspera Transfer Daemon', handler: {o: Ascp::Installation.instance, m: :transferd_urls})
|
217
215
|
options.declare(:sdk_folder, 'SDK folder path', handler: {o: Products::Transferd, m: :sdk_directory})
|
218
216
|
options.declare(:progress_bar, 'Display progress bar', values: :bool, default: Environment.terminal?)
|
219
217
|
# email options
|
@@ -439,23 +437,6 @@ module Aspera
|
|
439
437
|
end if check_data[:need_update]
|
440
438
|
end
|
441
439
|
|
442
|
-
# retrieve structure from cloud (CDN) with all versions available
|
443
|
-
def connect_versions
|
444
|
-
if @connect_versions.nil?
|
445
|
-
api_connect_cdn = Rest.new(base_url: CONNECT_WEB_URL)
|
446
|
-
javascript = api_connect_cdn.call(operation: 'GET', subpath: CONNECT_VERSIONS)
|
447
|
-
# get result on one line
|
448
|
-
connect_versions_javascript = javascript[:http].body.gsub(/\r?\n\s*/, '')
|
449
|
-
Log.log.debug{"javascript=[\n#{connect_versions_javascript}\n]"}
|
450
|
-
# get javascript object only
|
451
|
-
found = connect_versions_javascript.match(/^.*? = (.*);/)
|
452
|
-
raise Cli::Error, 'Problem when getting connect versions from internet' if found.nil?
|
453
|
-
all_data = JSON.parse(found[1])
|
454
|
-
@connect_versions = all_data['entries']
|
455
|
-
end
|
456
|
-
return @connect_versions
|
457
|
-
end
|
458
|
-
|
459
440
|
# loads default parameters of plugin if no -P parameter
|
460
441
|
# and if there is a section defined for the plugin in the "default" section
|
461
442
|
# try to find: conf[conf["default"][plugin_str]]
|
@@ -659,12 +640,12 @@ module Aspera
|
|
659
640
|
command = options.get_next_command(%i[list info version])
|
660
641
|
if %i[info version].include?(command)
|
661
642
|
connect_id = options.get_next_argument('id or title')
|
662
|
-
one_res =
|
643
|
+
one_res = Products::Connect.instance.versions.find{|i|i['id'].eql?(connect_id) || i['title'].eql?(connect_id)}
|
663
644
|
raise Cli::NoSuchIdentifier.new(:connect, connect_id) if one_res.nil?
|
664
645
|
end
|
665
646
|
case command
|
666
647
|
when :list
|
667
|
-
return Main.result_object_list(
|
648
|
+
return Main.result_object_list(Products::Connect.instance.versions, fields: %w[id title version])
|
668
649
|
when :info
|
669
650
|
one_res.delete('links')
|
670
651
|
return Main.result_single_object(one_res)
|
@@ -674,18 +655,16 @@ module Aspera
|
|
674
655
|
if %i[download open].include?(command)
|
675
656
|
link_title = options.get_next_argument('title or rel')
|
676
657
|
one_link = all_links.find {|i| i['title'].eql?(link_title) || i['rel'].eql?(link_title)}
|
677
|
-
raise
|
658
|
+
raise "no such value: #{link_title}" if one_link.nil?
|
678
659
|
end
|
679
660
|
case command
|
680
661
|
when :list
|
681
662
|
return Main.result_object_list(all_links)
|
682
663
|
when :download
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
api_connect_cdn.call(operation: 'GET', subpath: file_url, save_to_file: File.join(folder_dest, filename))
|
688
|
-
return Main.result_status("Downloaded: #{filename}")
|
664
|
+
archive_path = one_link['href']
|
665
|
+
save_to_path = File.join(transfer.destination_folder(Transfer::Spec::DIRECTION_RECEIVE), archive_path.gsub(%r{.*/}, ''))
|
666
|
+
Products::Connect.instance.cdn_api.call(operation: 'GET', subpath: archive_path, save_to_file: save_to_path)
|
667
|
+
return Main.result_status("Downloaded: #{save_to_path}")
|
689
668
|
when :open
|
690
669
|
Environment.instance.open_uri(one_link['href'])
|
691
670
|
return Main.result_status("Opened: #{one_link['href']}")
|
@@ -759,7 +738,7 @@ module Aspera
|
|
759
738
|
n, v = Ascp::Installation.instance.install_sdk(url: options.get_option(:sdk_url, mandatory: true), version: version)
|
760
739
|
return Main.result_status("Installed #{n} version #{v}")
|
761
740
|
when :list
|
762
|
-
sdk_list = Ascp::Installation.sdk_locations
|
741
|
+
sdk_list = Ascp::Installation.instance.sdk_locations
|
763
742
|
return Main.result_object_list(
|
764
743
|
sdk_list,
|
765
744
|
fields: sdk_list.first.keys - ['url']
|
@@ -228,10 +228,8 @@ module Aspera
|
|
228
228
|
config.progress_bar&.event(:transfer, session_id: id, info: status['bytes_written'].to_i)
|
229
229
|
end
|
230
230
|
if status_list.include?(status['upload_status'])
|
231
|
-
# if status['upload_status'].eql?('completed')
|
232
231
|
config.progress_bar&.event(:end, session_id: id)
|
233
232
|
return status
|
234
|
-
# end
|
235
233
|
end
|
236
234
|
sleep(1.0)
|
237
235
|
end
|
@@ -467,7 +465,8 @@ module Aspera
|
|
467
465
|
end
|
468
466
|
return browse_folder("packages/#{package_id}/files/#{location}")
|
469
467
|
when :status
|
470
|
-
|
468
|
+
status_list = options.get_next_argument('list of states, or nothing', mandatory: false, validation: Array)
|
469
|
+
status = wait_package_status(package_id, status_list: status_list)
|
471
470
|
return {type: :single_object, data: status}
|
472
471
|
when :delete
|
473
472
|
ids = package_id
|
@@ -600,7 +599,11 @@ module Aspera
|
|
600
599
|
|
601
600
|
end
|
602
601
|
when :browse
|
603
|
-
|
602
|
+
node_id = instance_identifier do |field, value|
|
603
|
+
lookup_entity_by_field(
|
604
|
+
type: res_type, value: value, field: field, real_path: res_path, item_list_key: list_key, query: res_id_query)['id']
|
605
|
+
end
|
606
|
+
return browse_folder("#{res_path}/#{node_id}/browse")
|
604
607
|
when :invite_external_collaborator
|
605
608
|
shared_inbox_id = instance_identifier { |field, value| lookup_entity_by_field(type: res_type.to_s, field: field, value: value, query: res_id_query)['id']}
|
606
609
|
creation_payload = value_create_modify(command: res_command, type: [Hash, String])
|
@@ -744,7 +747,7 @@ fields: %w[event_type created_at application user.name]}
|
|
744
747
|
end
|
745
748
|
end
|
746
749
|
when :bearer_token
|
747
|
-
return {type: :text, data: @api_v5.oauth.
|
750
|
+
return {type: :text, data: @api_v5.oauth.authorization}
|
748
751
|
when :packages
|
749
752
|
return package_action
|
750
753
|
when :shared_folders
|
@@ -477,7 +477,7 @@ module Aspera
|
|
477
477
|
result[:password] = apifid[:api].auth_params[:password]
|
478
478
|
when :oauth2
|
479
479
|
result[:username] = apifid[:api].params[:headers][Api::Node::HEADER_X_ASPERA_ACCESS_KEY]
|
480
|
-
result[:password] = apifid[:api].oauth.
|
480
|
+
result[:password] = apifid[:api].oauth.authorization
|
481
481
|
else Aspera.error_unreachable_line
|
482
482
|
end
|
483
483
|
return {type: :single_object, data: result} if command_repo.eql?(:node_info)
|
@@ -601,7 +601,7 @@ module Aspera
|
|
601
601
|
return Main.result_image(result[:http].body, formatter: formatter)
|
602
602
|
when :permission
|
603
603
|
apifid = apifid_from_next_arg(top_file_id)
|
604
|
-
command_perm = options.get_next_command(%i[list create delete])
|
604
|
+
command_perm = options.get_next_command(%i[list show create delete])
|
605
605
|
case command_perm
|
606
606
|
when :list
|
607
607
|
list_query = query_read_delete(default: {'include' => Rest.array_params(%w[access_level permission_count])})
|
@@ -611,6 +611,9 @@ module Aspera
|
|
611
611
|
# NOTE: supports per_page and page and header X-Total-Count
|
612
612
|
items = apifid[:api].read('permissions', list_query)
|
613
613
|
return {type: :object_list, data: items}
|
614
|
+
when :show
|
615
|
+
perm_id = instance_identifier
|
616
|
+
return Main.result_single_object(apifid[:api].read("permissions/#{perm_id}"))
|
614
617
|
when :delete
|
615
618
|
return do_bulk_operation(command: command_perm, descr: 'identifier', values: :identifier) do |one_id|
|
616
619
|
apifid[:api].delete("permissions/#{one_id}")
|
@@ -1013,7 +1016,7 @@ module Aspera
|
|
1013
1016
|
Environment.instance.open_uri("#{options.get_option(:asperabrowserurl)}?goto=#{encoded_params}")
|
1014
1017
|
return Main.result_status('done')
|
1015
1018
|
when :basic_token
|
1016
|
-
return Main.result_status(Rest.
|
1019
|
+
return Main.result_status(Rest.basic_authorization(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
|
1017
1020
|
when :bearer_token
|
1018
1021
|
private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', validation: String))
|
1019
1022
|
token_info = options.get_next_argument('user and group identification', validation: Hash)
|
data/lib/aspera/cli/version.rb
CHANGED
data/lib/aspera/coverage.rb
CHANGED
@@ -20,14 +20,16 @@ if ENV.key?('ENABLE_COVERAGE')
|
|
20
20
|
# lines with those words are ignored from coverage
|
21
21
|
no_cov_functions = %w[error_unreachable_line error_unexpected_value Log.log.trace].freeze
|
22
22
|
SimpleCov.start do
|
23
|
-
|
24
|
-
add_filter 'lib/aspera/node_simulator.rb'
|
25
|
-
add_filter 'lib/aspera/keychain/macos_security.rb'
|
23
|
+
# assert usually do not trigger
|
26
24
|
add_filter do |source_file|
|
27
25
|
source_file.lines.each do |line|
|
28
26
|
line.skipped! if no_cov_functions.any?{|i|line.src.include?(i)}
|
29
27
|
end
|
30
28
|
false
|
31
29
|
end
|
30
|
+
# no coverage test in those
|
31
|
+
add_filter 'lib/aspera/cli/plugins/faspex.rb'
|
32
|
+
add_filter 'lib/aspera/node_simulator.rb'
|
33
|
+
add_filter 'lib/aspera/keychain/macos_security.rb'
|
32
34
|
end
|
33
35
|
end
|
data/lib/aspera/environment.rb
CHANGED
@@ -109,23 +109,28 @@ module Aspera
|
|
109
109
|
].compact.flatten.join(' ')
|
110
110
|
end
|
111
111
|
|
112
|
-
#
|
112
|
+
# Start process in background
|
113
113
|
# caller can call Process.wait on returned value
|
114
|
-
# @param
|
115
|
-
# @param
|
116
|
-
# @param
|
117
|
-
# @
|
118
|
-
|
114
|
+
# @param exec [String] path to executable
|
115
|
+
# @param args [Array, nil] arguments for executable
|
116
|
+
# @param env [Hash, nil] environment variables
|
117
|
+
# @param options [Hash, nil] spawn options
|
118
|
+
# @return [String] PID of process
|
119
|
+
# @raise [Exception] if problem
|
120
|
+
def secure_spawn(exec:, args: nil, env: nil, **options)
|
119
121
|
Aspera.assert_type(exec, String)
|
120
122
|
Aspera.assert_type(args, Array) unless args.nil?
|
121
123
|
Aspera.assert_type(env, Hash) unless env.nil?
|
124
|
+
Aspera.assert_type(options, Hash) unless options.nil?
|
122
125
|
Log.log.debug {log_spawn(exec: exec, args: args, env: env)}
|
123
126
|
# start ascp in separate process
|
124
127
|
spawn_args = []
|
125
128
|
spawn_args.push(env) unless env.nil?
|
126
129
|
spawn_args.push([exec, exec])
|
127
130
|
spawn_args.concat(args) unless args.nil?
|
128
|
-
|
131
|
+
opts = {close_others: true}
|
132
|
+
opts.merge!(options) unless options.nil?
|
133
|
+
ascp_pid = Process.spawn(*spawn_args, **opts)
|
129
134
|
Log.log.debug{"pid: #{ascp_pid}"}
|
130
135
|
return ascp_pid
|
131
136
|
end
|
@@ -135,24 +140,28 @@ module Aspera
|
|
135
140
|
# @param exec [String] path to executable
|
136
141
|
# @param args [Array, nil] arguments
|
137
142
|
# @return [String] PID of process
|
138
|
-
def secure_execute(exec:, args: nil, env: nil)
|
143
|
+
def secure_execute(exec:, args: nil, env: nil, **system_args)
|
139
144
|
Aspera.assert_type(exec, String)
|
140
145
|
Aspera.assert_type(args, Array) unless args.nil?
|
141
146
|
Aspera.assert_type(env, Hash) unless env.nil?
|
142
147
|
Log.log.debug {log_spawn(exec: exec, args: args, env: env)}
|
143
|
-
# start
|
148
|
+
# start in separate process
|
144
149
|
spawn_args = []
|
145
150
|
spawn_args.push(env) unless env.nil?
|
151
|
+
# ensure no shell expansion
|
146
152
|
spawn_args.push([exec, exec])
|
147
153
|
spawn_args.concat(args) unless args.nil?
|
148
|
-
|
154
|
+
kwargs = {exception: true}
|
155
|
+
kwargs.merge!(system_args)
|
156
|
+
Kernel.system(*spawn_args, **kwargs)
|
149
157
|
nil
|
150
158
|
end
|
151
159
|
|
160
|
+
# Execute process and capture stdout
|
152
161
|
# @param exec [String] path to executable
|
153
162
|
# @param args [Array] arguments to executable
|
154
163
|
# @param opts [Hash] options to capture3
|
155
|
-
# @return stdout of executable or raise
|
164
|
+
# @return stdout of executable or raise exception
|
156
165
|
def secure_capture(exec:, args: [], **opts)
|
157
166
|
Aspera.assert_type(exec, String)
|
158
167
|
Aspera.assert_type(args, Array)
|
@@ -39,7 +39,7 @@ module Aspera
|
|
39
39
|
response.body = {status: 'error', message: 'Empty request'}.to_json
|
40
40
|
return
|
41
41
|
end
|
42
|
-
# build script path by removing domain
|
42
|
+
# build script path by removing domain and adding script folder
|
43
43
|
script_file = request.path[@parameters[:root].size..]
|
44
44
|
Log.log.debug{"script file=#{script_file}"}
|
45
45
|
script_path = File.join(@parameters[:script_folder], script_file)
|
@@ -48,11 +48,9 @@ module Aspera
|
|
48
48
|
Log.log.debug{Log.dump(:webhook_parameters, webhook_parameters)}
|
49
49
|
# env expects only strings
|
50
50
|
environment = webhook_parameters.each_with_object({}) { |(k, v), h| h[k] = v.to_s }
|
51
|
-
post_proc_pid =
|
52
|
-
Log.log.debug{"pid=#{post_proc_pid}"}
|
53
|
-
raise 'no pid' if post_proc_pid.nil?
|
54
|
-
# "wait" for process to avoid zombie
|
51
|
+
post_proc_pid = Environment.secure_spawn(env: environment, exec: script_path)
|
55
52
|
Timeout.timeout(@parameters[:timeout_seconds]) do
|
53
|
+
# "wait" for process to avoid zombie
|
56
54
|
Process.wait(post_proc_pid)
|
57
55
|
post_proc_pid = nil
|
58
56
|
end
|
data/lib/aspera/hash_ext.rb
CHANGED
@@ -20,17 +20,7 @@ class ::Hash
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
# in
|
24
|
-
unless Hash.method_defined?(:transform_keys)
|
25
|
-
class Hash
|
26
|
-
def transform_keys
|
27
|
-
raise 'missing block' unless block_given?
|
28
|
-
return each_with_object({}){|(k, v), memo|memo[yield(k)] = v}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# rails
|
23
|
+
# Exists in Rails
|
34
24
|
unless Hash.method_defined?(:symbolize_keys)
|
35
25
|
class Hash
|
36
26
|
def symbolize_keys
|
@@ -39,7 +29,7 @@ unless Hash.method_defined?(:symbolize_keys)
|
|
39
29
|
end
|
40
30
|
end
|
41
31
|
|
42
|
-
#
|
32
|
+
# Exists in Rails
|
43
33
|
unless Hash.method_defined?(:stringify_keys)
|
44
34
|
class Hash
|
45
35
|
def stringify_keys
|
data/lib/aspera/oauth/base.rb
CHANGED
@@ -79,6 +79,11 @@ module Aspera
|
|
79
79
|
return call_params
|
80
80
|
end
|
81
81
|
|
82
|
+
# @return value suitable for Authorization header
|
83
|
+
def authorization(**kwargs)
|
84
|
+
return OAuth::Factory.bearer_build(token(**kwargs))
|
85
|
+
end
|
86
|
+
|
82
87
|
# get an OAuth v2 token (generated, cached, refreshed)
|
83
88
|
# call token() to get a token.
|
84
89
|
# if a token is expired (api returns 4xx), call again token(refresh: true)
|
@@ -129,7 +134,7 @@ module Aspera
|
|
129
134
|
end
|
130
135
|
Aspera.assert(token_data.key?(@token_field)){"API error: No such field in answer: #{@token_field}"}
|
131
136
|
# ok we shall have a token here
|
132
|
-
return
|
137
|
+
return token_data[@token_field]
|
133
138
|
end
|
134
139
|
end
|
135
140
|
end
|
@@ -4,7 +4,6 @@
|
|
4
4
|
# spellchecker:ignore pauseframes libx264 trunc bufsize muxer apng libmp3lame maxrate posterize movflags faststart
|
5
5
|
# spellchecker:ignore palettegen paletteuse pointsize bordercolor repage lanczos unoconv optipng reencode conv transframes
|
6
6
|
|
7
|
-
require 'open3'
|
8
7
|
require 'aspera/preview/options'
|
9
8
|
require 'aspera/preview/utils'
|
10
9
|
require 'aspera/preview/file_types'
|
@@ -24,17 +23,18 @@ module Aspera
|
|
24
23
|
# one of CONVERSION_TYPES
|
25
24
|
attr_reader :conversion_type
|
26
25
|
|
27
|
-
# @param src source file path
|
28
|
-
# @param dst destination file path
|
29
|
-
# @param api_mime_type optional mime type as provided by node api (or nil)
|
30
26
|
# node API mime types are from: http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
31
|
-
# supported preview type is one of Preview::PREVIEW_FORMATS
|
32
27
|
# the resulting preview file type is taken from destination file extension.
|
33
28
|
# conversion methods are provided by private methods: convert_<conversion_type>_to_<preview_format>
|
34
29
|
# -> conversion_type is one of FileTypes::CONVERSION_TYPES
|
35
30
|
# -> preview_format is one of Generator::PREVIEW_FORMATS
|
36
31
|
# the conversion video->mp4 is implemented in methods: convert_video_to_mp4_using_<video_conversion>
|
37
32
|
# -> conversion method is one of Generator::VIDEO_CONVERSION_METHODS
|
33
|
+
# @param src [String] source file path
|
34
|
+
# @param dst [String] destination file path
|
35
|
+
# @param options [Options] All conversion options
|
36
|
+
# @param main_temp_dir [String] Main temp folder, sub folder will be created for generation
|
37
|
+
# @param api_mime_type [String,nil] Optional mime type as provided by node api (or nil)
|
38
38
|
def initialize(src, dst, options, main_temp_dir, api_mime_type)
|
39
39
|
@source_file_path = src
|
40
40
|
@destination_file_path = dst
|
@@ -54,7 +54,7 @@ module Aspera
|
|
54
54
|
end
|
55
55
|
@processing_method = @processing_method.to_sym
|
56
56
|
Log.log.debug{"method: #{@processing_method}"}
|
57
|
-
Aspera.assert(respond_to?(@processing_method, true)){"no processing
|
57
|
+
Aspera.assert(respond_to?(@processing_method, true)){"no processing known for #{conversion_type} -> #{@preview_format_sym}"}
|
58
58
|
end
|
59
59
|
|
60
60
|
# create preview as specified in constructor
|
@@ -215,7 +215,8 @@ module Aspera
|
|
215
215
|
|
216
216
|
def convert_pdf_to_png(source_file_path=nil)
|
217
217
|
source_file_path ||= @source_file_path
|
218
|
-
Utils.external_command(:
|
218
|
+
Utils.external_command(:magick, [
|
219
|
+
'convert',
|
219
220
|
'-size', "x#{@options.thumb_img_size}",
|
220
221
|
'-background', 'white',
|
221
222
|
'-flatten',
|
@@ -224,7 +225,8 @@ module Aspera
|
|
224
225
|
end
|
225
226
|
|
226
227
|
def convert_image_to_png
|
227
|
-
Utils.external_command(:
|
228
|
+
Utils.external_command(:magick, [
|
229
|
+
'convert',
|
228
230
|
'-auto-orient',
|
229
231
|
'-thumbnail', "#{@options.thumb_img_size}x#{@options.thumb_img_size}>",
|
230
232
|
'-quality', 95,
|
@@ -239,7 +241,8 @@ module Aspera
|
|
239
241
|
def convert_plaintext_to_png
|
240
242
|
# get 100 first lines of text file
|
241
243
|
first_lines = File.open(@source_file_path){|f|Array.new(100){f.readline rescue ''}.join}
|
242
|
-
Utils.external_command(:
|
244
|
+
Utils.external_command(:magick, [
|
245
|
+
'convert',
|
243
246
|
'-size', "#{@options.thumb_img_size}x#{@options.thumb_img_size}",
|
244
247
|
'xc:white', # define canvas with background color (xc, or canvas) of preceding size
|
245
248
|
'-font', @options.thumb_text_font,
|
@@ -19,10 +19,10 @@ module Aspera
|
|
19
19
|
{ name: :thumb_vid_scale, default: "-1:'min(ih,100)'", description: 'png: video: size (ffmpeg scale argument)' },
|
20
20
|
{ name: :thumb_vid_fraction, default: 0.1, description: 'png: video: time percent position of snapshot' },
|
21
21
|
{ name: :thumb_img_size, default: 800, description: 'png: non-video: height (and width)' },
|
22
|
-
{ name: :thumb_text_font, default: 'Courier', description: 'png: plaintext: font
|
22
|
+
{ name: :thumb_text_font, default: 'Courier', description: 'png: plaintext: font for text rendering: `magick identify -list font`'},
|
23
23
|
{ name: :video_conversion, default: :reencode, description: 'mp4: method for preview generation', values: VIDEO_CONVERSION_METHODS },
|
24
24
|
{ name: :video_png_conv, default: :fixed, description: 'mp4: method for thumbnail generation', values: VIDEO_THUMBNAIL_METHODS },
|
25
|
-
{ name: :video_scale, default: "'min(iw,360)':-2", description: 'mp4: all: video scale (ffmpeg)' },
|
25
|
+
{ name: :video_scale, default: "'min(iw,360)':-2", description: 'mp4: all: video scale (ffmpeg scale argument)' },
|
26
26
|
{ name: :video_start_sec, default: 10, description: 'mp4: all: start offset (seconds) of video preview' },
|
27
27
|
{ name: :reencode_ffmpeg, default: {}, description: 'mp4: reencode: options to ffmpeg' },
|
28
28
|
{ name: :blend_keyframes, default: 30, description: 'mp4: blend: # key frames' },
|
@@ -44,7 +44,7 @@ module Aspera
|
|
44
44
|
fit_term_ratio = [term_rows.to_f * font_ratio / image.rows.to_f, term_columns.to_f / image.columns.to_f].min
|
45
45
|
height_ratio = double ? 2.0 : 1.0
|
46
46
|
image = image.scale((image.columns * fit_term_ratio).to_i, (image.rows * fit_term_ratio * height_ratio / font_ratio).to_i)
|
47
|
-
# quantum depth is 8 or 16, see: `
|
47
|
+
# quantum depth is 8 or 16, see: `magick xc: -format "%q" info:`
|
48
48
|
shift_for_8_bit = Magick::MAGICKCORE_QUANTUM_DEPTH - 8
|
49
49
|
# get all pixel colors, adjusted for Rainbow
|
50
50
|
pixel_colors = []
|