aspera-cli 4.12.0 → 4.14.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +45 -5
  4. data/CONTRIBUTING.md +113 -22
  5. data/README.md +1289 -754
  6. data/bin/ascli +3 -3
  7. data/examples/dascli +1 -1
  8. data/examples/rubyc +24 -0
  9. data/lib/aspera/aoc.rb +63 -74
  10. data/lib/aspera/ascmd.rb +5 -3
  11. data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
  12. data/lib/aspera/cli/extended_value.rb +24 -37
  13. data/lib/aspera/cli/formatter.rb +23 -25
  14. data/lib/aspera/cli/info.rb +2 -4
  15. data/lib/aspera/cli/main.rb +27 -27
  16. data/lib/aspera/cli/manager.rb +143 -120
  17. data/lib/aspera/cli/plugin.rb +88 -43
  18. data/lib/aspera/cli/plugins/alee.rb +2 -2
  19. data/lib/aspera/cli/plugins/aoc.rb +235 -104
  20. data/lib/aspera/cli/plugins/ats.rb +16 -18
  21. data/lib/aspera/cli/plugins/bss.rb +3 -3
  22. data/lib/aspera/cli/plugins/config.rb +190 -373
  23. data/lib/aspera/cli/plugins/console.rb +4 -6
  24. data/lib/aspera/cli/plugins/cos.rb +12 -13
  25. data/lib/aspera/cli/plugins/faspex.rb +21 -21
  26. data/lib/aspera/cli/plugins/faspex5.rb +399 -150
  27. data/lib/aspera/cli/plugins/node.rb +260 -174
  28. data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
  29. data/lib/aspera/cli/plugins/preview.rb +40 -62
  30. data/lib/aspera/cli/plugins/server.rb +33 -16
  31. data/lib/aspera/cli/plugins/shares.rb +24 -33
  32. data/lib/aspera/cli/plugins/sync.rb +6 -6
  33. data/lib/aspera/cli/transfer_agent.rb +47 -30
  34. data/lib/aspera/cli/version.rb +2 -1
  35. data/lib/aspera/colors.rb +9 -7
  36. data/lib/aspera/command_line_builder.rb +2 -1
  37. data/lib/aspera/cos_node.rb +1 -1
  38. data/lib/aspera/data/6 +0 -0
  39. data/lib/aspera/environment.rb +7 -3
  40. data/lib/aspera/fasp/agent_connect.rb +6 -1
  41. data/lib/aspera/fasp/agent_direct.rb +17 -17
  42. data/lib/aspera/fasp/agent_httpgw.rb +138 -60
  43. data/lib/aspera/fasp/agent_node.rb +14 -4
  44. data/lib/aspera/fasp/agent_trsdk.rb +2 -0
  45. data/lib/aspera/fasp/error_info.rb +2 -0
  46. data/lib/aspera/fasp/installation.rb +19 -19
  47. data/lib/aspera/fasp/parameters.rb +29 -20
  48. data/lib/aspera/fasp/parameters.yaml +5 -2
  49. data/lib/aspera/fasp/resume_policy.rb +3 -3
  50. data/lib/aspera/fasp/transfer_spec.rb +8 -5
  51. data/lib/aspera/fasp/uri.rb +23 -21
  52. data/lib/aspera/faspex_gw.rb +1 -0
  53. data/lib/aspera/faspex_postproc.rb +3 -3
  54. data/lib/aspera/hash_ext.rb +12 -2
  55. data/lib/aspera/keychain/macos_security.rb +13 -13
  56. data/lib/aspera/log.rb +1 -0
  57. data/lib/aspera/node.rb +73 -84
  58. data/lib/aspera/oauth.rb +4 -3
  59. data/lib/aspera/persistency_action_once.rb +1 -1
  60. data/lib/aspera/preview/file_types.rb +8 -6
  61. data/lib/aspera/preview/generator.rb +23 -11
  62. data/lib/aspera/preview/options.rb +3 -2
  63. data/lib/aspera/preview/terminal.rb +80 -0
  64. data/lib/aspera/preview/utils.rb +11 -11
  65. data/lib/aspera/proxy_auto_config.js +2 -2
  66. data/lib/aspera/rest.rb +42 -4
  67. data/lib/aspera/rest_call_error.rb +3 -1
  68. data/lib/aspera/secret_hider.rb +10 -5
  69. data/lib/aspera/ssh.rb +1 -1
  70. data/lib/aspera/sync.rb +41 -33
  71. data/lib/aspera/web_server_simple.rb +22 -18
  72. data.tar.gz.sig +0 -0
  73. metadata +40 -48
  74. metadata.gz.sig +0 -0
  75. data/docs/test_env.conf +0 -179
  76. data/examples/aoc.rb +0 -30
  77. data/examples/faspex4.rb +0 -94
  78. data/examples/node.rb +0 -96
  79. data/examples/server.rb +0 -93
  80. data/lib/aspera/data/7 +0 -0
@@ -23,7 +23,10 @@ module Aspera
23
23
  # either in standard domain, or product name in page
24
24
  if URI.parse(base_url).host.end_with?(Aspera::AoC::PROD_DOMAIN) ||
25
25
  api.call({operation: 'GET', redirect_max: 1, headers: {'Accept' => 'text/html'}})[:http].body.include?(Aspera::AoC::PRODUCT_NAME)
26
- return {product: :aoc, version: 'SaaS' }
26
+ return {
27
+ version: 'SaaS',
28
+ name: 'Aspera on Cloud'
29
+ }
27
30
  end
28
31
  return nil
29
32
  end
@@ -50,6 +53,7 @@ module Aspera
50
53
  client_registration_token
51
54
  client_access_key
52
55
  kms_profile].freeze
56
+ # TODO: remove this and use %name: instead
53
57
  ENTITY_NAME_SPECIFIER = 'name'
54
58
  PACKAGE_QUERY_DEFAULT = {'archived' => false, 'exclude_dropbox_packages' => true, 'has_content' => true, 'received' => true}.freeze
55
59
 
@@ -58,28 +62,23 @@ module Aspera
58
62
  @cache_workspace_info = nil
59
63
  @cache_home_node_file = nil
60
64
  @cache_api_aoc = nil
61
- options.add_opt_list(:auth, Oauth::STD_AUTH_TYPES, 'OAuth type of authentication')
62
- options.add_opt_list(:operation, %i[push pull], 'client operation for transfers')
63
- options.add_opt_simple(:client_id, 'OAuth API client identifier')
64
- options.add_opt_simple(:client_secret, 'OAuth API client secret')
65
- options.add_opt_simple(:redirect_uri, 'OAuth API client redirect URI')
66
- options.add_opt_simple(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
67
- options.add_opt_simple(:scope, 'OAuth scope for AoC API calls')
68
- options.add_opt_simple(:passphrase, 'RSA private key passphrase')
69
- options.add_opt_simple(:workspace, 'Name of workspace')
70
- options.add_opt_simple(:name, "Resource name (prefer to use keyword #{ENTITY_NAME_SPECIFIER})")
71
- options.add_opt_simple(:link, 'Public link to shared resource')
72
- options.add_opt_simple(:new_user_option, 'New user creation option for unknown package recipients')
73
- options.add_opt_simple(:from_folder, 'Source folder for Folder-to-Folder transfer')
74
- options.add_opt_boolean(:validate_metadata, 'Validate shared inbox metadata')
75
- options.set_option(:validate_metadata, :yes)
76
- options.set_option(:operation, :push)
77
- options.set_option(:auth, :jwt)
78
- options.set_option(:scope, AoC::SCOPE_FILES_USER)
79
- options.set_option(:private_key, '@file:' + env[:private_key_path]) if env[:private_key_path].is_a?(String)
80
- options.set_option(:workspace, :default)
65
+ options.declare(:auth, 'OAuth type of authentication', values: Oauth::STD_AUTH_TYPES, default: :jwt)
66
+ options.declare(:operation, 'Client operation for transfers', values: %i[push pull], default: :push)
67
+ options.declare(:client_id, 'OAuth API client identifier')
68
+ options.declare(:client_secret, 'OAuth API client secret')
69
+ options.declare(:redirect_uri, 'OAuth API client redirect URI')
70
+ options.declare(:private_key, 'OAuth JWT RSA private key PEM value (prefix file path with @file:)')
71
+ options.declare(:scope, 'OAuth scope for AoC API calls', default: AoC::SCOPE_FILES_USER)
72
+ options.declare(:passphrase, 'RSA private key passphrase')
73
+ options.declare(:workspace, 'Name of workspace', default: :default)
74
+ # TODO: remove this and use %name: instead
75
+ options.declare(:name, "Resource name (prefer to use keyword #{ENTITY_NAME_SPECIFIER})")
76
+ options.declare(:link, 'Public link to shared resource')
77
+ options.declare(:new_user_option, 'New user creation option for unknown package recipients')
78
+ options.declare(:from_folder, 'Source folder for Folder-to-Folder transfer')
79
+ options.declare(:validate_metadata, 'Validate shared inbox metadata', values: :bool, default: true)
81
80
  options.parse_options!
82
- # add node plugin options
81
+ # add node plugin options (TODO: check needed ? if yes, tell why)
83
82
  Node.new(env.merge({man_only: true, skip_basic_auth_options: true}))
84
83
  end
85
84
 
@@ -114,7 +113,7 @@ module Aspera
114
113
  Log.log.debug('Using default workspace'.green)
115
114
  raise CliError, 'No default workspace defined for user, please specify workspace' if default_workspace_id.nil?
116
115
  default_workspace_id
117
- when String then aoc_api.lookup_entity_by_name('workspaces', ws_name)['id']
116
+ when String then aoc_api.lookup_by_name('workspaces', ws_name)['id']
118
117
  when NilClass then nil
119
118
  else raise CliError, 'unexpected value type for workspace'
120
119
  end
@@ -142,6 +141,13 @@ module Aspera
142
141
  end
143
142
  home_node_id ||= current_workspace_info['home_node_id'] || current_workspace_info['node_id']
144
143
  home_file_id ||= current_workspace_info['home_file_id']
144
+ if home_node_id.to_s.empty?
145
+ # not part of any workspace, but has some folder shared
146
+ user_info = aoc_api.current_user_info(exception: true)
147
+ home_node_id = user_info['read_only_home_node_id']
148
+ home_file_id = user_info['read_only_home_file_id']
149
+ end
150
+
145
151
  raise "Cannot get user's home node id, check your default workspace or specify one" if home_node_id.to_s.empty?
146
152
  @cache_home_node_file = {
147
153
  node_id: home_node_id,
@@ -157,11 +163,11 @@ module Aspera
157
163
  l_res_name = options.get_option(:name)
158
164
  raise 'Provide either option id or name, not both' unless l_res_id.nil? || l_res_name.nil?
159
165
  # try to find item by name (single partial match or exact match)
160
- l_res_id = aoc_api.lookup_entity_by_name(resource_class_path, l_res_name)['id'] unless l_res_name.nil?
166
+ l_res_id = aoc_api.lookup_by_name(resource_class_path, l_res_name)['id'] unless l_res_name.nil?
161
167
  # if no name or id option, taken on command line (after command)
162
168
  if l_res_id.nil?
163
169
  l_res_id = options.get_next_argument('identifier')
164
- l_res_id = aoc_api.lookup_entity_by_name(resource_class_path, options.get_next_argument('identifier'))['id'] if l_res_id.eql?(ENTITY_NAME_SPECIFIER)
170
+ l_res_id = aoc_api.lookup_by_name(resource_class_path, options.get_next_argument('identifier'))['id'] if l_res_id.eql?(ENTITY_NAME_SPECIFIER)
165
171
  end
166
172
  return l_res_id
167
173
  end
@@ -214,7 +220,12 @@ module Aspera
214
220
  # @param file_id [String] root file id for the operation (can be AK root, or other, e.g. package, or link)
215
221
  # @param scope [String] node scope, or nil (admin)
216
222
  def execute_nodegen4_command(command_repo, node_id, file_id: nil, scope: nil)
217
- top_node_api = aoc_api.node_api_from(node_id: node_id, workspace_info: current_workspace_info, scope: scope)
223
+ top_node_api = aoc_api.node_api_from(
224
+ node_id: node_id,
225
+ workspace_id: current_workspace_info['id'],
226
+ workspace_name: current_workspace_info['name'],
227
+ scope: scope
228
+ )
218
229
  file_id = top_node_api.read("access_keys/#{top_node_api.app_info[:node_info]['access_key']}")[:data]['root_file_id'] if file_id.nil?
219
230
  node_plugin = Node.new(@agents.merge(
220
231
  skip_basic_auth_options: true,
@@ -228,15 +239,15 @@ module Aspera
228
239
  # server side is protocol server
229
240
  # in same workspace
230
241
  # default is push
231
- case options.get_option(:operation, is_type: :mandatory)
242
+ case options.get_option(:operation, mandatory: true)
232
243
  when :push
233
244
  client_direction = Fasp::TransferSpec::DIRECTION_SEND
234
- client_folder = options.get_option(:from_folder, is_type: :mandatory)
245
+ client_folder = options.get_option(:from_folder, mandatory: true)
235
246
  server_folder = transfer.destination_folder(client_direction)
236
247
  when :pull
237
248
  client_direction = Fasp::TransferSpec::DIRECTION_RECEIVE
238
249
  client_folder = transfer.destination_folder(client_direction)
239
- server_folder = options.get_option(:from_folder, is_type: :mandatory)
250
+ server_folder = options.get_option(:from_folder, mandatory: true)
240
251
  end
241
252
  client_apfid = top_node_api.resolve_api_fid(file_id, client_folder)
242
253
  server_apfid = top_node_api.resolve_api_fid(file_id, server_folder)
@@ -359,12 +370,12 @@ module Aspera
359
370
  filter = options.get_option(:query) || {}
360
371
  raise 'query must be Hash' unless filter.is_a?(Hash)
361
372
  filter['limit'] ||= 100
362
- if options.get_option(:once_only, is_type: :mandatory)
373
+ if options.get_option(:once_only, mandatory: true)
363
374
  saved_date = []
364
375
  start_date_persistency = PersistencyActionOnce.new(
365
376
  manager: @agents[:persistency],
366
377
  data: saved_date,
367
- ids: IdGenerator.from_list(['aoc_ana_date', options.get_option(:url, is_type: :mandatory), current_workspace_info['name']].push(
378
+ ids: IdGenerator.from_list(['aoc_ana_date', options.get_option(:url, mandatory: true), current_workspace_info['name']].push(
368
379
  filter_resource,
369
380
  filter_id)))
370
381
  start_date_time = saved_date.first
@@ -375,7 +386,7 @@ module Aspera
375
386
  filter['start_time'] = start_date_time unless start_date_time.nil?
376
387
  filter['stop_time'] = stop_date_time
377
388
  end
378
- events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", option_url_query(filter))[:data][event_type]
389
+ events = analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}", query_read_delete(default: filter))[:data][event_type]
379
390
  start_date_persistency&.save
380
391
  if !options.get_option(:notif_to).nil?
381
392
  events.each do |tr_event|
@@ -439,10 +450,8 @@ module Aspera
439
450
  when :group_membership then default_fields.push(*%w[group_id member_type member_id])
440
451
  when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
441
452
  end
442
- items = read_with_paging(resource_class_path, option_url_query(default_query))
443
- count_msg = "Items: #{items[:list].length}/#{items[:total]}"
444
- count_msg = count_msg.bg_red unless items[:list].length.eql?(items[:total].to_i)
445
- formatter.display_status(count_msg)
453
+ items = read_with_paging(resource_class_path, query_read_delete(default: default_query))
454
+ formatter.display_item_count(items[:list].length, items[:total])
446
455
  return {type: :object_list, data: items[:list], fields: default_fields}
447
456
  when :show
448
457
  object = aoc_api.read(resource_instance_path)[:data]
@@ -481,7 +490,7 @@ module Aspera
481
490
  case command
482
491
  when :reminder
483
492
  # send an email reminder with list of orgs
484
- user_email = options.get_option(:username, is_type: :mandatory)
493
+ user_email = options.get_option(:username, mandatory: true)
485
494
  Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
486
495
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
487
496
  when :servers
@@ -518,7 +527,7 @@ module Aspera
518
527
  when :shared_inboxes
519
528
  case options.get_next_command(%i[list show])
520
529
  when :list
521
- query = option_url_query(nil)
530
+ query = query_read_delete
522
531
  if query.nil?
523
532
  query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
524
533
  query['workspace_id'] = current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
@@ -528,8 +537,7 @@ module Aspera
528
537
  return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'), query)[:data]}
529
538
  end
530
539
  when :send
531
- package_data = options.get_option(:value, is_type: :mandatory)
532
- raise CliBadArgument, 'value must be hash, refer to doc' unless package_data.is_a?(Hash)
540
+ package_data = value_create_modify(command: package_command, type: Hash)
533
541
  new_user_option = options.get_option(:new_user_option)
534
542
  option_validate = options.get_option(:validate_metadata)
535
543
  # works for both normal usr auth and link auth
@@ -557,20 +565,20 @@ module Aspera
557
565
  ids_to_download = instance_identifier
558
566
  skip_ids_data = []
559
567
  skip_ids_persistency = nil
560
- if options.get_option(:once_only, is_type: :mandatory)
568
+ if options.get_option(:once_only, mandatory: true)
561
569
  skip_ids_persistency = PersistencyActionOnce.new(
562
570
  manager: @agents[:persistency],
563
571
  data: skip_ids_data,
564
- id: IdGenerator.from_list(['aoc_recv', options.get_option(:url, is_type: :mandatory),
572
+ id: IdGenerator.from_list(['aoc_recv', options.get_option(:url, mandatory: true),
565
573
  current_workspace_info['id']].concat(aoc_api.additional_persistence_ids)))
566
574
  end
567
575
  if VAL_ALL.eql?(ids_to_download)
568
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
576
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
569
577
  raise 'option query must be Hash' unless query.is_a?(Hash)
570
578
  if query.key?('dropbox_name')
571
579
  # convenience: specify name instead of id
572
580
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
573
- query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes', query['dropbox_name'])['id']
581
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
574
582
  query.delete('dropbox_name')
575
583
  end
576
584
  query['workspace_id'] ||= current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
@@ -588,7 +596,11 @@ module Aspera
588
596
  ids_to_download.each do |package_id|
589
597
  package_info = aoc_api.read("packages/#{package_id}")[:data]
590
598
  formatter.display_status("downloading package: #{package_info['name']}")
591
- package_node_api = aoc_api.node_api_from(package_info: package_info, scope: AoC::SCOPE_NODE_USER)
599
+ package_node_api = aoc_api.node_api_from(
600
+ node_id: package_info['node_id'],
601
+ workspace_id: current_workspace_info['id'],
602
+ workspace_name: current_workspace_info['name'],
603
+ package_info: package_info)
592
604
  statuses = transfer.start(
593
605
  package_node_api.transfer_spec_gen4(
594
606
  package_info['contents_file_id'],
@@ -609,12 +621,12 @@ module Aspera
609
621
  return { type: :single_object, data: package_info }
610
622
  when :list
611
623
  display_fields = %w[id name bytes_transferred]
612
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
624
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
613
625
  raise 'option query must be Hash' unless query.is_a?(Hash)
614
626
  if query.key?('dropbox_name')
615
627
  # convenience: specify name instead of id
616
628
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
617
- query['dropbox_id'] = aoc_api.lookup_entity_by_name('dropboxes', query['dropbox_name'])['id']
629
+ query['dropbox_id'] = aoc_api.lookup_by_name('dropboxes', query['dropbox_name'])['id']
618
630
  query.delete('dropbox_name')
619
631
  end
620
632
  if current_workspace_info['id'].eql?(:undefined)
@@ -641,72 +653,111 @@ module Aspera
641
653
  when *NODE4_EXT_COMMANDS
642
654
  return execute_nodegen4_command(command_repo, home_info[:node_id], file_id: home_info[:file_id], scope: AoC::SCOPE_NODE_USER)
643
655
  when :short_link
644
- # TODO: move to permissions ?
645
- folder_dest = options.get_option(:to_folder)
646
- value_option = options.get_option(:value)
647
- case value_option
648
- when 'public' then value_option = {'purpose' => 'token_auth_redirection'}
649
- when 'private' then value_option = {'purpose' => 'shared_folder_auth_link'}
650
- when NilClass, Hash then nil # keep value
651
- else raise 'value must be either: public, private, Hash or nil'
656
+ # execute action on AoC API
657
+ short_link_command = options.get_next_command(%i[create delete list])
658
+ folder_dest = options.get_next_argument('path')
659
+ link_type = options.get_next_argument('link type', expected: %i[public private])
660
+ home_node_api = aoc_api.node_api_from(
661
+ node_id: home_info[:node_id],
662
+ workspace_id: current_workspace_info['id'],
663
+ workspace_name: current_workspace_info['name'])
664
+ shared_apfid = home_node_api.resolve_api_fid(home_info[:file_id], folder_dest)
665
+ folder_info = {
666
+ node_id: shared_apfid[:api].app_info[:node_info]['id'],
667
+ file_id: shared_apfid[:file_id],
668
+ workspace_id: current_workspace_info['id']
669
+ }
670
+ purpose = case link_type
671
+ when :public then 'token_auth_redirection'
672
+ when :private then 'shared_folder_auth_link'
673
+ else raise 'internal error'
652
674
  end
653
- create_params = nil
654
- shared_apfid = nil
655
- if !folder_dest.nil?
656
- home_node_api = aoc_api.node_api_from(node_id: home_info[:node_id], workspace_info: current_workspace_info, scope: AoC::SCOPE_NODE_USER)
657
- shared_apfid = home_node_api.resolve_api_fid(home_info[:file_id], folder_dest)
658
- create_params = {
659
- file_id: shared_apfid[:file_id],
660
- node_id: shared_apfid[:api].app_info[:node_info]['id'],
661
- workspace_id: current_workspace_info['id']
675
+ case short_link_command
676
+ when :delete
677
+ one_id = instance_identifier
678
+ folder_info.delete(:workspace_id)
679
+ delete_params = {
680
+ edit_access: true,
681
+ json_query: folder_info.to_json
662
682
  }
663
- end
664
- if !value_option.nil? && !create_params.nil?
665
- case value_option['purpose']
666
- when 'shared_folder_auth_link'
667
- value_option['data'] = create_params
668
- value_option['user_selected_name'] = nil
669
- when 'token_auth_redirection'
670
- create_params['name'] = ''
671
- value_option['data'] = {
683
+ aoc_api.delete("short_links/#{one_id}", delete_params)
684
+ if link_type.eql?(:public)
685
+ # TODO: get permission id..
686
+ # shared_apfid[:api].delete('permissions', {ids: })[:data]
687
+ end
688
+ return Main.result_status('deleted')
689
+ when :list
690
+ query = if link_type.eql?(:private)
691
+ folder_info
692
+ else
693
+ {
694
+ url_token_data: {
695
+ data: folder_info,
696
+ purpose: 'view_shared_file'
697
+ }
698
+ }
699
+ end
700
+ list_params = {
701
+ json_query: query.to_json,
702
+ edit_access: true,
703
+ purpose: purpose,
704
+ # embed: 'updated_by_user',
705
+ sort: '-created_at'
706
+ }
707
+ result = aoc_api.read('short_links', list_params)[:data]
708
+ result.each{|i|i.delete('data')}
709
+ return {type: :object_list, data: result}
710
+ when :create
711
+ creation_params = {
712
+ purpose: purpose,
713
+ user_selected_name: nil
714
+ }
715
+ case link_type
716
+ when :private
717
+ creation_params[:data] = folder_info
718
+ when :public
719
+ creation_params[:expires_at] = nil
720
+ creation_params[:password_enabled] = false
721
+ folder_info[:name] = ''
722
+ creation_params[:data] = {
672
723
  aoc: true,
673
724
  url_token_data: {
674
- data: create_params,
725
+ data: folder_info,
675
726
  purpose: 'view_shared_file'
676
727
  }
677
728
  }
678
- value_option['user_selected_name'] = nil
679
- else
680
- raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
681
729
  end
682
- options.set_option(:value, value_option)
683
- end
684
- result = entity_action(aoc_api, 'short_links', id_default: 'self')
685
- if result[:data].is_a?(Hash) && result[:data].key?('created_at') && result[:data]['resource_type'].eql?('UrlToken')
686
- # TODO: access level as arg
687
- access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
688
- perm_data = {
689
- 'file_id' => shared_apfid[:file_id],
690
- 'access_type' => 'user',
691
- 'access_id' => result[:data]['resource_id'],
692
- 'access_levels' => access_levels,
693
- 'tags' => {
694
- 'url_token' => true,
695
- 'workspace_id' => current_workspace_info['id'],
696
- 'workspace_name' => current_workspace_info['name'],
697
- 'folder_name' => 'my folder',
698
- 'created_by_name' => aoc_api.current_user_info['name'],
699
- 'created_by_email' => aoc_api.current_user_info['email'],
700
- 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
701
- 'node' => shared_apfid[:api].app_info[:node_info]['host']
730
+ result_create_short_link = aoc_api.create('short_links', creation_params)[:data]
731
+ # public: Creation: permission on node
732
+ if link_type.eql?(:public)
733
+ # TODO: merge with node permissions ?
734
+ # TODO: access level as arg
735
+ access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
736
+ folder_name = File.basename(folder_dest)
737
+ perm_data = {
738
+ 'file_id' => shared_apfid[:file_id],
739
+ 'access_id' => result_create_short_link['resource_id'],
740
+ 'access_type' => 'user',
741
+ 'access_levels' => access_levels,
742
+ 'tags' => {
743
+ 'url_token' => true,
744
+ 'workspace_id' => current_workspace_info['id'],
745
+ 'workspace_name' => current_workspace_info['name'],
746
+ 'folder_name' => folder_name,
747
+ 'created_by_name' => aoc_api.current_user_info['name'],
748
+ 'created_by_email' => aoc_api.current_user_info['email'],
749
+ 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
750
+ 'node' => shared_apfid[:api].app_info[:node_info]['host']
751
+ }
702
752
  }
703
- }
704
- shared_apfid[:api].create("permissions?file_id=#{shared_apfid[:file_id]}", perm_data)
705
- # TODO: event ?
706
- end
707
- return result
753
+ created_data = shared_apfid[:api].create('permissions', perm_data)[:data]
754
+ aoc_api.permissions_send_event(created_data: created_data, app_info: shared_apfid[:api].app_info)
755
+ # TODO: event ?
756
+ end
757
+ return {type: :single_object, data: result_create_short_link}
758
+ end # short_link command
708
759
  end # files command
709
- throw('Error: shall not reach this line')
760
+ raise 'Error: shall not reach this line'
710
761
  when :automation
711
762
  Log.log.warn('BETA: work under progress')
712
763
  # automation api is not in the same place
@@ -743,7 +794,7 @@ module Aspera
743
794
  return execute_admin_action
744
795
  when :gateway
745
796
  require 'aspera/faspex_gw'
746
- url = options.get_option(:value, is_type: :mandatory)
797
+ url = value_create_modify(type: String)
747
798
  uri = URI.parse(url)
748
799
  server = WebServerSimple.new(uri)
749
800
  server.mount(uri.path, Faspex4GWServlet, aoc_api, current_workspace_info['id'])
@@ -759,6 +810,86 @@ module Aspera
759
810
  raise 'internal error: command shall return'
760
811
  end
761
812
 
813
+ # @param [Hash] params : plugin_sym, instance_url
814
+ # @return [Hash] :preset_value, :test_args
815
+ def wizard(params)
816
+ if params[:prepare]
817
+ organization = AoC.parse_url(params[:instance_url]).first
818
+ # if not defined by user, generate name
819
+ params[:preset_name] ||= [params[:plugin_sym], organization].join('_')
820
+ params[:need_private_key] = true
821
+ return
822
+ end
823
+ options.set_option(:private_key, '@file:' + params[:private_key_path])
824
+ # make username mandatory for jwt, this triggers interactive input
825
+ options.get_option(:username, mandatory: true)
826
+ auto_set_pub_key = false
827
+ auto_set_jwt = false
828
+ use_browser_authentication = false
829
+ if options.get_option(:use_generic_client)
830
+ formatter.display_status('Using global client_id.')
831
+ formatter.display_status('Please Login to your Aspera on Cloud instance.'.red)
832
+ formatter.display_status('Navigate to your "Account Settings"'.red)
833
+ formatter.display_status('Check or update the value of "Public Key" to be:'.red.blink)
834
+ formatter.display_status(params[:pub_key_pem])
835
+ if !options.get_option(:test_mode)
836
+ formatter.display_status('Once updated or validated, press enter.')
837
+ OpenApplication.instance.uri(params[:instance_url])
838
+ $stdin.gets
839
+ end
840
+ else
841
+ formatter.display_status('Using organization specific client_id.')
842
+ if options.get_option(:client_id).nil? || options.get_option(:client_secret).nil?
843
+ formatter.display_status('Please login to your Aspera on Cloud instance.'.red)
844
+ formatter.display_status('Go to: Apps->Admin->Organization->Integrations')
845
+ formatter.display_status('Create or check if there is an existing integration named:')
846
+ formatter.display_status("- name: #{@info[:name]}")
847
+ formatter.display_status("- redirect uri: #{DEFAULT_REDIRECT}")
848
+ formatter.display_status('- origin: localhost')
849
+ formatter.display_status('Once created or identified,')
850
+ formatter.display_status('Please enter:'.red)
851
+ end
852
+ OpenApplication.instance.uri("#{params[:instance_url]}/#{AOC_PATH_API_CLIENTS}")
853
+ options.get_option(:client_id, mandatory: true)
854
+ options.get_option(:client_secret, mandatory: true)
855
+ use_browser_authentication = true
856
+ end
857
+ if use_browser_authentication
858
+ formatter.display_status('We will use web authentication to bootstrap.')
859
+ auto_set_pub_key = true
860
+ auto_set_jwt = true
861
+ aoc_api.oauth.generic_parameters[:grant_method] = :web
862
+ aoc_api.oauth.generic_parameters[:scope] = AoC::SCOPE_FILES_ADMIN
863
+ aoc_api.oauth.specific_parameters[:redirect_uri] = DEFAULT_REDIRECT
864
+ end
865
+ myself = aoc_api.read('self')[:data]
866
+ if auto_set_pub_key
867
+ raise CliError, 'Public key is already set in profile (use --override=yes)' unless myself['public_key'].empty? || option_override
868
+ formatter.display_status('Updating profile with new key')
869
+ aoc_api.update("users/#{myself['id']}", {'public_key' => params[:pub_key_pem]})
870
+ end
871
+ if auto_set_jwt
872
+ formatter.display_status('Enabling JWT for client')
873
+ aoc_api.update("clients/#{options.get_option(:client_id)}", {'jwt_grant_enabled' => true, 'explicit_authorization_required' => false})
874
+ end
875
+ formatter.display_status("Creating new config preset: #{params[:preset_name]}")
876
+ preset_result = {
877
+ url: params[:instance_url],
878
+ username: myself['email'],
879
+ auth: :jwt.to_s,
880
+ private_key: '@file:' + params[:private_key_path]
881
+ }.stringify_keys
882
+ # set only if non nil
883
+ %i[client_id client_secret].each do |s|
884
+ o = options.get_option(s)
885
+ preset_result[s.to_s] = o unless o.nil?
886
+ end
887
+ return {
888
+ preset_value: preset_result,
889
+ test_args: "#{params[:plugin_sym]} user profile show"
890
+ }
891
+ end
892
+
762
893
  private :aoc_params,
763
894
  :home_info,
764
895
  :assert_public_link_types,
@@ -7,25 +7,24 @@ module Aspera
7
7
  module Cli
8
8
  module Plugins
9
9
  # Access Aspera Transfer Service
10
- # https://52.44.83.163/docs/
11
10
  # https://developer.ibm.com/aspera/docs/ats-api-reference/creating-ats-api-keys/
12
11
  class Ats < Aspera::Cli::Plugin
13
12
  def initialize(env)
14
13
  super(env)
15
- options.add_opt_simple(:ibm_api_key, 'IBM API key, see https://cloud.ibm.com/iam/apikeys')
16
- options.add_opt_simple(:instance, 'ATS instance in ibm cloud')
17
- options.add_opt_simple(:ats_key, 'ATS key identifier (ats_xxx)')
18
- options.add_opt_simple(:ats_secret, 'ATS key secret')
19
- options.add_opt_simple(:params, 'Parameters access key creation (@json:)')
20
- options.add_opt_simple(:cloud, 'Cloud provider')
21
- options.add_opt_simple(:region, 'Cloud region')
14
+ options.declare(:ibm_api_key, 'IBM API key, see https://cloud.ibm.com/iam/apikeys')
15
+ options.declare(:instance, 'ATS instance in ibm cloud')
16
+ options.declare(:ats_key, 'ATS key identifier (ats_xxx)')
17
+ options.declare(:ats_secret, 'ATS key secret')
18
+ options.declare(:params, 'Parameters access key creation (@json:)')
19
+ options.declare(:cloud, 'Cloud provider')
20
+ options.declare(:region, 'Cloud region')
22
21
  options.parse_options!
23
22
  end
24
23
 
25
24
  def server_by_cloud_region
26
25
  # TODO: provide list ?
27
- cloud = options.get_option(:cloud, is_type: :mandatory).upcase
28
- region = options.get_option(:region, is_type: :mandatory)
26
+ cloud = options.get_option(:cloud, mandatory: true).upcase
27
+ region = options.get_option(:region, mandatory: true)
29
28
  return @ats_api_pub.read("servers/#{cloud}/#{region}")[:data]
30
29
  end
31
30
 
@@ -36,8 +35,8 @@ module Aspera
36
35
  base_url: AtsApi.base_url + '/pub/v1',
37
36
  auth: {
38
37
  type: :basic,
39
- username: options.get_option(:ats_key, is_type: :mandatory),
40
- password: options.get_option(:ats_secret, is_type: :mandatory)}
38
+ username: options.get_option(:ats_key, mandatory: true),
39
+ password: options.get_option(:ats_secret, mandatory: true)}
41
40
  })
42
41
  end
43
42
 
@@ -90,7 +89,7 @@ module Aspera
90
89
  res = ats_api_pub_v1.read("access_keys/#{access_key_id}")
91
90
  return {type: :single_object, data: res[:data]}
92
91
  when :modify
93
- params = options.get_option(:value, is_type: :mandatory)
92
+ params = value_create_modify(command: command, type: Hash)
94
93
  params['id'] = access_key_id
95
94
  ats_api_pub_v1.update("access_keys/#{access_key_id}", params)
96
95
  return Main.result_status('modified')
@@ -138,7 +137,7 @@ module Aspera
138
137
  when :list
139
138
  return {type: :object_list, data: @ats_api_pub.all_servers, fields: %w[id cloud region]}
140
139
  when :show
141
- if options.get_option(:cloud) || options.get_option(:region, is_type: :optional)
140
+ if options.get_option(:cloud) || options.get_option(:region)
142
141
  server_data = server_by_cloud_region
143
142
  else
144
143
  server_id = instance_identifier
@@ -161,7 +160,7 @@ module Aspera
161
160
  generic: {
162
161
  grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
163
162
  response_type: 'cloud_iam',
164
- apikey: options.get_option(:ibm_api_key, is_type: :mandatory)
163
+ apikey: options.get_option(:ibm_api_key, mandatory: true)
165
164
  }}})
166
165
  end
167
166
 
@@ -187,8 +186,7 @@ module Aspera
187
186
  Log.log.warn{"more instances remaining: #{instances['remaining']}"} unless instances['remaining'].to_i.eql?(0)
188
187
  return {type: :value_list, data: instances['data'], name: 'instance'}
189
188
  when :create
190
- create_value = options.get_option(:value) || {}
191
- created_key = ats_ibm_api.create('api_keys', create_value)[:data]
189
+ created_key = ats_ibm_api.create('api_keys', value_create_modify(command: command, default: {}, type: Hash))[:data]
192
190
  return {type: :single_object, data: created_key}
193
191
  when :list # list known api keys in ATS (this require an api_key ...)
194
192
  res = ats_ibm_api.read('api_keys', {'offset' => 0, 'max_results' => 1000})
@@ -221,7 +219,7 @@ module Aspera
221
219
  when :api_key # manage credential to access ATS API
222
220
  return execute_action_api_key
223
221
  when :aws_trust_policy
224
- res = ats_api_pub_v1.read('aws/trustpolicy', {region: options.get_option(:region, is_type: :mandatory)})[:data]
222
+ res = ats_api_pub_v1.read('aws/trustpolicy', {region: options.get_option(:region, mandatory: true)})[:data]
225
223
  return {type: :single_object, data: res}
226
224
  else raise 'ERROR'
227
225
  end
@@ -23,7 +23,7 @@ module Aspera
23
23
 
24
24
  def execute_action
25
25
  if @api_bss.nil?
26
- key = options.get_option(:password, is_type: :mandatory)
26
+ key = options.get_option(:password, mandatory: true)
27
27
  @api_bss = Rest.new(
28
28
  base_url: 'https://dashboard.bss.asperasoft.com/platform',
29
29
  headers: {cookie: "_dashboard_key=#{key}"})
@@ -35,8 +35,8 @@ module Aspera
35
35
  object = 'bssSubscriptions'
36
36
  case command
37
37
  when :find
38
- query = options.get_option(:query, is_type: :mandatory) # AOC_ORGANIZATION_QUERY AOC_USER_EMAIL
39
- value = options.get_option(:value, is_type: :mandatory)
38
+ query = options.get_option(:query, mandatory: true) # AOC_ORGANIZATION_QUERY AOC_USER_EMAIL
39
+ value = value_create_modify(command: command)
40
40
  request = {
41
41
  'variables' => {'filter' => {'key' => query, 'value' => value}},
42
42
  'query' => "query($filter: BssSubscriptionFilter!) {#{object}(filter: $filter) { #{all_fields('bssSubscriptions')} } }"