aspera-cli 4.13.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 (64) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +28 -5
  4. data/CONTRIBUTING.md +17 -1
  5. data/README.md +782 -401
  6. data/examples/dascli +1 -1
  7. data/examples/rubyc +24 -0
  8. data/lib/aspera/aoc.rb +21 -32
  9. data/lib/aspera/ascmd.rb +1 -0
  10. data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
  11. data/lib/aspera/cli/formatter.rb +17 -25
  12. data/lib/aspera/cli/main.rb +21 -27
  13. data/lib/aspera/cli/manager.rb +128 -114
  14. data/lib/aspera/cli/plugin.rb +87 -38
  15. data/lib/aspera/cli/plugins/alee.rb +2 -2
  16. data/lib/aspera/cli/plugins/aoc.rb +216 -102
  17. data/lib/aspera/cli/plugins/ats.rb +16 -18
  18. data/lib/aspera/cli/plugins/bss.rb +3 -3
  19. data/lib/aspera/cli/plugins/config.rb +177 -367
  20. data/lib/aspera/cli/plugins/console.rb +4 -6
  21. data/lib/aspera/cli/plugins/cos.rb +12 -13
  22. data/lib/aspera/cli/plugins/faspex.rb +17 -18
  23. data/lib/aspera/cli/plugins/faspex5.rb +332 -216
  24. data/lib/aspera/cli/plugins/node.rb +171 -142
  25. data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
  26. data/lib/aspera/cli/plugins/preview.rb +38 -60
  27. data/lib/aspera/cli/plugins/server.rb +22 -15
  28. data/lib/aspera/cli/plugins/shares.rb +24 -33
  29. data/lib/aspera/cli/plugins/sync.rb +3 -3
  30. data/lib/aspera/cli/transfer_agent.rb +29 -26
  31. data/lib/aspera/cli/version.rb +1 -1
  32. data/lib/aspera/colors.rb +9 -7
  33. data/lib/aspera/data/6 +0 -0
  34. data/lib/aspera/environment.rb +7 -3
  35. data/lib/aspera/fasp/agent_connect.rb +5 -0
  36. data/lib/aspera/fasp/agent_direct.rb +5 -5
  37. data/lib/aspera/fasp/agent_httpgw.rb +138 -60
  38. data/lib/aspera/fasp/agent_trsdk.rb +2 -0
  39. data/lib/aspera/fasp/error_info.rb +2 -0
  40. data/lib/aspera/fasp/installation.rb +18 -19
  41. data/lib/aspera/fasp/parameters.rb +18 -17
  42. data/lib/aspera/fasp/parameters.yaml +2 -1
  43. data/lib/aspera/fasp/resume_policy.rb +3 -3
  44. data/lib/aspera/fasp/transfer_spec.rb +6 -5
  45. data/lib/aspera/fasp/uri.rb +23 -21
  46. data/lib/aspera/faspex_postproc.rb +1 -1
  47. data/lib/aspera/hash_ext.rb +12 -2
  48. data/lib/aspera/keychain/macos_security.rb +13 -13
  49. data/lib/aspera/log.rb +1 -0
  50. data/lib/aspera/node.rb +62 -80
  51. data/lib/aspera/oauth.rb +1 -1
  52. data/lib/aspera/persistency_action_once.rb +1 -1
  53. data/lib/aspera/preview/terminal.rb +61 -15
  54. data/lib/aspera/preview/utils.rb +3 -3
  55. data/lib/aspera/proxy_auto_config.js +2 -2
  56. data/lib/aspera/rest.rb +37 -0
  57. data/lib/aspera/secret_hider.rb +6 -1
  58. data/lib/aspera/ssh.rb +1 -1
  59. data/lib/aspera/sync.rb +2 -0
  60. data.tar.gz.sig +0 -0
  61. metadata +3 -4
  62. metadata.gz.sig +0 -0
  63. data/docs/test_env.conf +0 -186
  64. 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
@@ -164,11 +163,11 @@ module Aspera
164
163
  l_res_name = options.get_option(:name)
165
164
  raise 'Provide either option id or name, not both' unless l_res_id.nil? || l_res_name.nil?
166
165
  # try to find item by name (single partial match or exact match)
167
- 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?
168
167
  # if no name or id option, taken on command line (after command)
169
168
  if l_res_id.nil?
170
169
  l_res_id = options.get_next_argument('identifier')
171
- 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)
172
171
  end
173
172
  return l_res_id
174
173
  end
@@ -240,15 +239,15 @@ module Aspera
240
239
  # server side is protocol server
241
240
  # in same workspace
242
241
  # default is push
243
- case options.get_option(:operation, is_type: :mandatory)
242
+ case options.get_option(:operation, mandatory: true)
244
243
  when :push
245
244
  client_direction = Fasp::TransferSpec::DIRECTION_SEND
246
- client_folder = options.get_option(:from_folder, is_type: :mandatory)
245
+ client_folder = options.get_option(:from_folder, mandatory: true)
247
246
  server_folder = transfer.destination_folder(client_direction)
248
247
  when :pull
249
248
  client_direction = Fasp::TransferSpec::DIRECTION_RECEIVE
250
249
  client_folder = transfer.destination_folder(client_direction)
251
- server_folder = options.get_option(:from_folder, is_type: :mandatory)
250
+ server_folder = options.get_option(:from_folder, mandatory: true)
252
251
  end
253
252
  client_apfid = top_node_api.resolve_api_fid(file_id, client_folder)
254
253
  server_apfid = top_node_api.resolve_api_fid(file_id, server_folder)
@@ -371,12 +370,12 @@ module Aspera
371
370
  filter = options.get_option(:query) || {}
372
371
  raise 'query must be Hash' unless filter.is_a?(Hash)
373
372
  filter['limit'] ||= 100
374
- if options.get_option(:once_only, is_type: :mandatory)
373
+ if options.get_option(:once_only, mandatory: true)
375
374
  saved_date = []
376
375
  start_date_persistency = PersistencyActionOnce.new(
377
376
  manager: @agents[:persistency],
378
377
  data: saved_date,
379
- 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(
380
379
  filter_resource,
381
380
  filter_id)))
382
381
  start_date_time = saved_date.first
@@ -387,7 +386,7 @@ module Aspera
387
386
  filter['start_time'] = start_date_time unless start_date_time.nil?
388
387
  filter['stop_time'] = stop_date_time
389
388
  end
390
- 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]
391
390
  start_date_persistency&.save
392
391
  if !options.get_option(:notif_to).nil?
393
392
  events.each do |tr_event|
@@ -451,7 +450,7 @@ module Aspera
451
450
  when :group_membership then default_fields.push(*%w[group_id member_type member_id])
452
451
  when :workspace_membership then default_fields.push(*%w[workspace_id member_type member_id])
453
452
  end
454
- items = read_with_paging(resource_class_path, option_url_query(default_query))
453
+ items = read_with_paging(resource_class_path, query_read_delete(default: default_query))
455
454
  formatter.display_item_count(items[:list].length, items[:total])
456
455
  return {type: :object_list, data: items[:list], fields: default_fields}
457
456
  when :show
@@ -491,7 +490,7 @@ module Aspera
491
490
  case command
492
491
  when :reminder
493
492
  # send an email reminder with list of orgs
494
- user_email = options.get_option(:username, is_type: :mandatory)
493
+ user_email = options.get_option(:username, mandatory: true)
495
494
  Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders', {email: user_email})[:data]
496
495
  return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
497
496
  when :servers
@@ -528,7 +527,7 @@ module Aspera
528
527
  when :shared_inboxes
529
528
  case options.get_next_command(%i[list show])
530
529
  when :list
531
- query = option_url_query(nil)
530
+ query = query_read_delete
532
531
  if query.nil?
533
532
  query = {'embed[]' => 'dropbox', 'aggregate_permissions_by_dropbox' => true, 'sort' => 'dropbox_name'}
534
533
  query['workspace_id'] = current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
@@ -538,8 +537,7 @@ module Aspera
538
537
  return {type: :single_object, data: aoc_api.read(get_resource_path_from_args('dropboxes'), query)[:data]}
539
538
  end
540
539
  when :send
541
- package_data = options.get_option(:value, is_type: :mandatory)
542
- 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)
543
541
  new_user_option = options.get_option(:new_user_option)
544
542
  option_validate = options.get_option(:validate_metadata)
545
543
  # works for both normal usr auth and link auth
@@ -567,20 +565,20 @@ module Aspera
567
565
  ids_to_download = instance_identifier
568
566
  skip_ids_data = []
569
567
  skip_ids_persistency = nil
570
- if options.get_option(:once_only, is_type: :mandatory)
568
+ if options.get_option(:once_only, mandatory: true)
571
569
  skip_ids_persistency = PersistencyActionOnce.new(
572
570
  manager: @agents[:persistency],
573
571
  data: skip_ids_data,
574
- 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),
575
573
  current_workspace_info['id']].concat(aoc_api.additional_persistence_ids)))
576
574
  end
577
575
  if VAL_ALL.eql?(ids_to_download)
578
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
576
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
579
577
  raise 'option query must be Hash' unless query.is_a?(Hash)
580
578
  if query.key?('dropbox_name')
581
579
  # convenience: specify name instead of id
582
580
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
583
- 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']
584
582
  query.delete('dropbox_name')
585
583
  end
586
584
  query['workspace_id'] ||= current_workspace_info['id'] unless current_workspace_info['id'].eql?(:undefined)
@@ -623,12 +621,12 @@ module Aspera
623
621
  return { type: :single_object, data: package_info }
624
622
  when :list
625
623
  display_fields = %w[id name bytes_transferred]
626
- query = option_url_query(PACKAGE_QUERY_DEFAULT)
624
+ query = query_read_delete(default: PACKAGE_QUERY_DEFAULT)
627
625
  raise 'option query must be Hash' unless query.is_a?(Hash)
628
626
  if query.key?('dropbox_name')
629
627
  # convenience: specify name instead of id
630
628
  raise 'not both dropbox_name and dropbox_id' if query.key?('dropbox_id')
631
- 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']
632
630
  query.delete('dropbox_name')
633
631
  end
634
632
  if current_workspace_info['id'].eql?(:undefined)
@@ -655,75 +653,111 @@ module Aspera
655
653
  when *NODE4_EXT_COMMANDS
656
654
  return execute_nodegen4_command(command_repo, home_info[:node_id], file_id: home_info[:file_id], scope: AoC::SCOPE_NODE_USER)
657
655
  when :short_link
658
- # TODO: move to permissions ?
659
- folder_dest = options.get_option(:to_folder)
660
- value_option = options.get_option(:value)
661
- case value_option
662
- when 'public' then value_option = {'purpose' => 'token_auth_redirection'}
663
- when 'private' then value_option = {'purpose' => 'shared_folder_auth_link'}
664
- when NilClass, Hash then nil # keep value
665
- 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'
666
674
  end
667
- create_params = nil
668
- shared_apfid = nil
669
- if !folder_dest.nil?
670
- home_node_api = aoc_api.node_api_from(
671
- node_id: home_info[:node_id],
672
- workspace_id: current_workspace_info['id'],
673
- workspace_name: current_workspace_info['name'])
674
- shared_apfid = home_node_api.resolve_api_fid(home_info[:file_id], folder_dest)
675
- create_params = {
676
- file_id: shared_apfid[:file_id],
677
- node_id: shared_apfid[:api].app_info[:node_info]['id'],
678
- 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
679
682
  }
680
- end
681
- if !value_option.nil? && !create_params.nil?
682
- case value_option['purpose']
683
- when 'shared_folder_auth_link'
684
- value_option['data'] = create_params
685
- value_option['user_selected_name'] = nil
686
- when 'token_auth_redirection'
687
- create_params['name'] = ''
688
- 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] = {
689
723
  aoc: true,
690
724
  url_token_data: {
691
- data: create_params,
725
+ data: folder_info,
692
726
  purpose: 'view_shared_file'
693
727
  }
694
728
  }
695
- value_option['user_selected_name'] = nil
696
- else
697
- raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
698
729
  end
699
- options.set_option(:value, value_option)
700
- end
701
- result = entity_action(aoc_api, 'short_links', id_default: 'self')
702
- if result[:data].is_a?(Hash) && result[:data].key?('created_at') && result[:data]['resource_type'].eql?('UrlToken')
703
- # TODO: access level as arg
704
- access_levels = Aspera::Node::ACCESS_LEVELS # ['delete','list','mkdir','preview','read','rename','write']
705
- perm_data = {
706
- 'file_id' => shared_apfid[:file_id],
707
- 'access_type' => 'user',
708
- 'access_id' => result[:data]['resource_id'],
709
- 'access_levels' => access_levels,
710
- 'tags' => {
711
- 'url_token' => true,
712
- 'workspace_id' => current_workspace_info['id'],
713
- 'workspace_name' => current_workspace_info['name'],
714
- 'folder_name' => 'my folder',
715
- 'created_by_name' => aoc_api.current_user_info['name'],
716
- 'created_by_email' => aoc_api.current_user_info['email'],
717
- 'access_key' => shared_apfid[:api].app_info[:node_info]['access_key'],
718
- '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
+ }
719
752
  }
720
- }
721
- shared_apfid[:api].create("permissions?file_id=#{shared_apfid[:file_id]}", perm_data)
722
- # TODO: event ?
723
- end
724
- 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
725
759
  end # files command
726
- throw('Error: shall not reach this line')
760
+ raise 'Error: shall not reach this line'
727
761
  when :automation
728
762
  Log.log.warn('BETA: work under progress')
729
763
  # automation api is not in the same place
@@ -760,7 +794,7 @@ module Aspera
760
794
  return execute_admin_action
761
795
  when :gateway
762
796
  require 'aspera/faspex_gw'
763
- url = options.get_option(:value, is_type: :mandatory)
797
+ url = value_create_modify(type: String)
764
798
  uri = URI.parse(url)
765
799
  server = WebServerSimple.new(uri)
766
800
  server.mount(uri.path, Faspex4GWServlet, aoc_api, current_workspace_info['id'])
@@ -776,6 +810,86 @@ module Aspera
776
810
  raise 'internal error: command shall return'
777
811
  end
778
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
+
779
893
  private :aoc_params,
780
894
  :home_info,
781
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')} } }"