aspera-cli 4.0.0.pre2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +761 -210
  3. data/bin/ascli +2 -0
  4. data/bin/dascli +13 -0
  5. data/docs/Makefile +2 -1
  6. data/docs/README.erb.md +628 -160
  7. data/docs/test_env.conf +22 -10
  8. data/docs/transfer_spec.html +1 -1
  9. data/lib/aspera/aoc.rb +87 -108
  10. data/lib/aspera/cli/formater.rb +2 -0
  11. data/lib/aspera/cli/main.rb +48 -45
  12. data/lib/aspera/cli/manager.rb +19 -6
  13. data/lib/aspera/cli/plugin.rb +9 -4
  14. data/lib/aspera/cli/plugins/alee.rb +1 -1
  15. data/lib/aspera/cli/plugins/aoc.rb +208 -183
  16. data/lib/aspera/cli/plugins/ats.rb +2 -2
  17. data/lib/aspera/cli/plugins/config.rb +205 -125
  18. data/lib/aspera/cli/plugins/console.rb +2 -2
  19. data/lib/aspera/cli/plugins/faspex.rb +15 -8
  20. data/lib/aspera/cli/plugins/faspex5.rb +76 -37
  21. data/lib/aspera/cli/plugins/node.rb +3 -3
  22. data/lib/aspera/cli/plugins/preview.rb +35 -25
  23. data/lib/aspera/cli/plugins/server.rb +23 -8
  24. data/lib/aspera/cli/transfer_agent.rb +7 -6
  25. data/lib/aspera/cli/version.rb +1 -1
  26. data/lib/aspera/colors.rb +5 -1
  27. data/lib/aspera/cos_node.rb +33 -28
  28. data/lib/aspera/environment.rb +15 -4
  29. data/lib/aspera/fasp/connect.rb +28 -21
  30. data/lib/aspera/fasp/http_gw.rb +140 -28
  31. data/lib/aspera/fasp/installation.rb +119 -57
  32. data/lib/aspera/fasp/local.rb +174 -178
  33. data/lib/aspera/fasp/manager.rb +12 -0
  34. data/lib/aspera/fasp/node.rb +4 -4
  35. data/lib/aspera/fasp/parameters.rb +6 -18
  36. data/lib/aspera/fasp/resume_policy.rb +13 -12
  37. data/lib/aspera/log.rb +10 -2
  38. data/lib/aspera/node.rb +61 -1
  39. data/lib/aspera/oauth.rb +36 -13
  40. data/lib/aspera/persistency_folder.rb +9 -4
  41. data/lib/aspera/preview/file_types.rb +53 -21
  42. data/lib/aspera/preview/generator.rb +3 -3
  43. data/lib/aspera/rest.rb +29 -18
  44. data/lib/aspera/secrets.rb +20 -0
  45. data/lib/aspera/temp_file_manager.rb +19 -0
  46. metadata +40 -22
@@ -2,6 +2,7 @@ require 'aspera/colors'
2
2
  require 'aspera/log'
3
3
  require 'aspera/cli/extended_value'
4
4
  require 'optparse'
5
+ require 'io/console'
5
6
 
6
7
  module Aspera
7
8
  module Cli
@@ -135,6 +136,12 @@ module Aspera
135
136
  Log.log.debug("add_cmd_line_options:commands/args=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red)
136
137
  end
137
138
 
139
+ def prompt_user_input(prompt,sensitive)
140
+ return STDIN.getpass("#{prompt}> ") if sensitive
141
+ print "#{prompt}> "
142
+ return STDIN.gets.chomp
143
+ end
144
+
138
145
  def get_interactive(type,descr,expected=:single)
139
146
  if !@ask_missing_mandatory
140
147
  if expected.is_a?(Array)
@@ -143,23 +150,23 @@ module Aspera
143
150
  raise CliBadArgument,"missing argument (#{expected}): #{descr}"
144
151
  end
145
152
  result=nil
153
+ # Note: mandatory parenthesis here !
154
+ sensitive = (type.eql?(:option) and @declared_options[descr.to_sym][:sensitive].eql?(true))
155
+ default_prompt="#{type}: #{descr}"
146
156
  # ask interactively
147
157
  case expected
148
158
  when :multiple
149
159
  result=[]
150
160
  puts " (one per line, end with empty line)"
151
161
  loop do
152
- print "#{type}: #{descr}> "
153
- entry=STDIN.gets.chomp
162
+ entry=prompt_user_input(default_prompt,sensitive)
154
163
  break if entry.empty?
155
164
  result.push(ExtendedValue.instance.evaluate(entry))
156
165
  end
157
166
  when :single
158
- print "#{type}: #{descr}> "
159
- result=ExtendedValue.instance.evaluate(STDIN.gets.chomp)
167
+ result=ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive))
160
168
  else # one fixed
161
- print "#{expected.join(' ')}\n#{type}: #{descr}> "
162
- result=self.class.get_from_list(STDIN.gets.chomp,descr,expected)
169
+ result=self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected)
163
170
  end
164
171
  return result
165
172
  end
@@ -206,6 +213,12 @@ module Aspera
206
213
  return
207
214
  end
208
215
  @declared_options[option_symbol]={:type=>type}
216
+ # by default passwords and secrets are sensitive, else specify when declaring the option
217
+ set_is_sensitive(option_symbol) if !%w{password secret key}.select{|i| option_symbol.to_s.end_with?(i)}.empty?
218
+ end
219
+
220
+ def set_is_sensitive(option_symbol)
221
+ @declared_options[option_symbol][:sensitive]=true
209
222
  end
210
223
 
211
224
  # define option with handler
@@ -67,7 +67,10 @@ module Aspera
67
67
  when :delete
68
68
  rest_api.delete(one_res_path)
69
69
  return Main.result_status("deleted")
70
+ else
71
+ raise "unknown action: #{command}"
70
72
  end
73
+ raise "internal error should not reach here"
71
74
  end
72
75
 
73
76
  # implement generic rest operations on given resource path
@@ -77,13 +80,15 @@ module Aspera
77
80
  return entity_command(command,rest_api,res_class_path,display_fields,id_symb,id_default,subkey)
78
81
  end
79
82
 
80
- def options;@agents[:options];end
83
+ def options; return @agents[:options];end
81
84
 
82
- def transfer;@agents[:transfer];end
85
+ def transfer; return @agents[:transfer];end
83
86
 
84
- def config;return @agents[:config];end
87
+ def config; return @agents[:config];end
85
88
 
86
- def format;return @agents[:formater];end
89
+ def format; return @agents[:formater];end
90
+
91
+ def persistency; return @agents[:persistency];end
87
92
 
88
93
  end # Plugin
89
94
  end # Cli
@@ -12,7 +12,7 @@ module Aspera
12
12
  command=self.options.get_next_command(ACTIONS)
13
13
  case command
14
14
  when :entitlement
15
- entitlement_id = self.options.get_option(:username,:mandatory),
15
+ entitlement_id = self.options.get_option(:username,:mandatory)
16
16
  customer_id = self.options.get_option(:password,:mandatory)
17
17
  api_metering=AoC.metering_api(entitlement_id,customer_id)
18
18
  return {:type=>:single_object, :data=>api_metering.read('entitlement')[:data]}
@@ -3,6 +3,7 @@ require 'aspera/cli/plugins/ats'
3
3
  require 'aspera/cli/basic_auth_plugin'
4
4
  require 'aspera/cli/transfer_agent'
5
5
  require 'aspera/aoc'
6
+ require 'aspera/node'
6
7
  require 'aspera/persistency_action_once'
7
8
  require 'securerandom'
8
9
  require 'resolv'
@@ -11,10 +12,9 @@ require 'date'
11
12
  module Aspera
12
13
  module Cli
13
14
  module Plugins
14
- class Oncloud < BasicAuthPlugin
15
+ class Aoc < BasicAuthPlugin
16
+ # special value for package id
15
17
  VAL_ALL='ALL'
16
- private_constant :VAL_ALL
17
- attr_reader :api_aoc
18
18
  def initialize(env)
19
19
  super(env)
20
20
  @default_workspace_id=nil
@@ -25,23 +25,23 @@ module Aspera
25
25
  @api_aoc=nil
26
26
  @url_token_data=nil
27
27
  @user_info=nil
28
- @ats=Ats.new(@agents)
29
- self.options.add_opt_list(:auth,Oauth.auth_types,"type of Oauth authentication")
30
- self.options.add_opt_list(:operation,[:push,:pull],"client operation for transfers")
31
- self.options.add_opt_simple(:client_id,"API client identifier in application")
32
- self.options.add_opt_simple(:client_secret,"API client passcode")
33
- self.options.add_opt_simple(:redirect_uri,"API client redirect URI")
34
- self.options.add_opt_simple(:private_key,"RSA private key PEM value for JWT (prefix file path with @val:@file:)")
35
- self.options.add_opt_simple(:workspace,"name of workspace")
36
- self.options.add_opt_simple(:eid,"identifier") # used ?
37
- self.options.add_opt_simple(:name,"resource name")
38
- self.options.add_opt_simple(:link,"public link to shared resource")
39
- self.options.add_opt_simple(:new_user_option,"new user creation option")
40
- self.options.add_opt_simple(:from_folder,"share to share source folder")
41
- self.options.add_opt_simple(:scope,"scope for AoC API calls")
42
- self.options.add_opt_simple(:notify,"notify users that file was received")
43
- self.options.add_opt_boolean(:bulk,"bulk operation")
44
- self.options.add_opt_boolean(:default_ports,"use standard FASP ports or get from node api")
28
+ @api_aoc=nil
29
+ self.options.add_opt_list(:auth,Oauth.auth_types,'OAuth type of authentication')
30
+ self.options.add_opt_list(:operation,[:push,:pull],'client operation for transfers')
31
+ self.options.add_opt_simple(:client_id,'OAuth API client identifier in application')
32
+ self.options.add_opt_simple(:client_secret,'OAuth API client passcode')
33
+ self.options.add_opt_simple(:redirect_uri,'OAuth API client redirect URI')
34
+ self.options.add_opt_simple(:private_key,'OAuth JWT RSA private key PEM value (prefix file path with @val:@file:)')
35
+ self.options.add_opt_simple(:workspace,'name of workspace')
36
+ self.options.add_opt_simple(:name,'resource name')
37
+ self.options.add_opt_simple(:path,'file or folder path')
38
+ self.options.add_opt_simple(:link,'public link to shared resource')
39
+ self.options.add_opt_simple(:new_user_option,'new user creation option')
40
+ self.options.add_opt_simple(:from_folder,'share to share source folder')
41
+ self.options.add_opt_simple(:scope,'OAuth scope for AoC API calls')
42
+ self.options.add_opt_simple(:notify,'notify users that file was received')
43
+ self.options.add_opt_boolean(:bulk,'bulk operation')
44
+ self.options.add_opt_boolean(:default_ports,'use standard FASP ports or get from node api')
45
45
  self.options.set_option(:bulk,:no)
46
46
  self.options.set_option(:default_ports,:yes)
47
47
  self.options.set_option(:new_user_option,{'package_contact'=>true})
@@ -52,20 +52,19 @@ module Aspera
52
52
  self.options.parse_options!
53
53
  AoC.set_use_default_ports(self.options.get_option(:default_ports))
54
54
  return if env[:man_only]
55
- update_aoc_api
56
55
  end
57
56
 
58
- # call this to populate single AK secret in AoC API object, from options
59
- # make sure secret is available
60
- def find_ak_secret(ak,mandatory=true)
61
- # secret hash is already provisioned
62
- # optionally override with specific secret
63
- @api_aoc.add_secrets({ak=>self.config.get_secret(ak,mandatory)})
64
- # check that secret was provided as single value or dictionary
65
- raise CliBadArgument,"Please provide option secret or entry in option secrets for: #{ak}" unless @api_aoc.has_secret(ak) or !mandatory
57
+ def get_api
58
+ if @api_aoc.nil?
59
+ @api_aoc=AoC.new(aoc_params(AoC::API_V1))
60
+ # add keychain for access key secrets
61
+ @api_aoc.key_chain=@agents[:secret]
62
+ end
63
+ return @api_aoc
66
64
  end
67
65
 
68
- def user_info
66
+ # cached user information
67
+ def c_user_info
69
68
  if @user_info.nil?
70
69
  # get our user's default information
71
70
  # self?embed[]=default_workspace&embed[]=organization
@@ -80,16 +79,11 @@ module Aspera
80
79
  # starts transfer using transfer agent
81
80
  def transfer_start(app,direction,node_file,ts_add)
82
81
  ts_add.deep_merge!(AoC.analytics_ts(app,direction,@workspace_id,@workspace_name))
83
- ts_add.deep_merge!(AoC.console_ts(app,user_info['name'],user_info['email']))
82
+ ts_add.deep_merge!(AoC.console_ts(app,c_user_info['name'],c_user_info['email']))
84
83
  return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
85
84
  end
86
85
 
87
- NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node, :permissions ]
88
-
89
- def node_gen4_execute_action(top_node_file)
90
- command_repo=self.options.get_next_command(NODE4_COMMANDS)
91
- return execute_node_gen4_command(command_repo,top_node_file)
92
- end
86
+ NODE4_COMMANDS=[ :browse, :find, :mkdir, :rename, :delete, :upload, :download, :transfer, :http_node_download, :v3, :file, :bearer_token_node ]
93
87
 
94
88
  def execute_node_gen4_command(command_repo,top_node_file)
95
89
  case command_repo
@@ -113,14 +107,8 @@ module Aspera
113
107
  return {:type=>:object_list,:data=>items,:fields=>['name','type','recursive_size','size','modified_time','access_level']}
114
108
  when :find
115
109
  thepath=self.options.get_next_argument('path')
116
- exec_prefix='exec:'
117
- expression=self.options.get_option(:value,:optional)||"#{exec_prefix}true"
118
110
  node_file=@api_aoc.resolve_node_file(top_node_file,thepath)
119
- if expression.start_with?(exec_prefix)
120
- test_block=eval "lambda{|f|#{expression[exec_prefix.length..-1]}}"
121
- else
122
- test_block=lambda{|f|f['name'].match(/#{expression}/)}
123
- end
111
+ test_block=Aspera::Node.file_matcher(self.options.get_option(:value,:optional))
124
112
  return {:type=>:object_list,:data=>@api_aoc.find_files(node_file,test_block),:fields=>['path']}
125
113
  when :mkdir
126
114
  thepath=self.options.get_next_argument('path')
@@ -139,12 +127,12 @@ module Aspera
139
127
  return Main.result_status("renamed #{thepath} to #{newname}")
140
128
  when :delete
141
129
  thepath=self.options.get_next_argument('path')
142
- return do_bulk_operation(thepath,'deleted','path') do |thepath|
143
- raise "expecting String (path), got #{thepath.class.name} (#{thepath})" unless thepath.is_a?(String)
144
- node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
130
+ return do_bulk_operation(thepath,'deleted','path') do |l_path|
131
+ raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
132
+ node_file = @api_aoc.resolve_node_file(top_node_file,l_path)
145
133
  node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
146
134
  result=node_api.delete("files/#{node_file[:file_id]}")[:data]
147
- {'path'=>thepath}
135
+ {'path'=>l_path}
148
136
  end
149
137
  when :transfer
150
138
  # client side is agent
@@ -213,69 +201,79 @@ module Aspera
213
201
  node_api=@api_aoc.get_node_api(top_node_file[:node_info],AoC::SCOPE_NODE_USER)
214
202
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
215
203
  when :file
216
- fileid=self.options.get_next_argument('file id')
217
- node_file = @api_aoc.resolve_node_file(top_node_file)
218
- node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
219
- items=node_api.read("files/#{fileid}")[:data]
220
- return {:type=>:single_object,:data=>items}
221
- when :permissions
222
- fileid=self.options.get_next_argument('file id')
223
- node_file = @api_aoc.resolve_node_file(top_node_file)
204
+ file_path=self.options.get_option(:path,:optional)
205
+ node_file = if !file_path.nil?
206
+ @api_aoc.resolve_node_file(top_node_file,file_path) # TODO: allow follow link ?
207
+ else
208
+ {node_info: top_node_file[:node_info],file_id: self.options.get_option(:id,:mandatory)}
209
+ end
224
210
  node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
225
- command_perms=self.options.get_next_command([:show,:create])
226
- case command_perms
211
+ command_node_file=self.options.get_next_command([:show,:permission,:modify])
212
+ case command_node_file
227
213
  when :show
228
- items=node_api.read('permissions',{'include'=>['[]','access_level','permission_count'],'file_id'=>fileid,'inherited'=>false})[:data]
229
- return {:type=>:object_list,:data=>items}
230
- when :create
231
- #value=self.options.get_next_argument('creation value')
232
- set_workspace_info
233
- access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
234
- node_file[:node_info]
235
- params={
236
- "file_id"=>fileid,
237
- "access_type"=>"user",
238
- "access_id"=>access_id,
239
- "access_levels"=>["list","read","write","delete","mkdir","rename","preview"],
240
- "tags"=>{
241
- "aspera"=>{
242
- "files"=>{
243
- "workspace"=>{
244
- "id"=>@workspace_id,
245
- "workspace_name"=>@workspace_name,
246
- "user_name"=>user_info['name'],
247
- "shared_by_user_id"=>user_info['id'],
248
- "shared_by_name"=>user_info['name'],
249
- "shared_by_email"=>user_info['email'],
250
- "shared_with_name"=>access_id,
251
- "access_key"=>node_file[:node_info]['access_key'],
252
- "node"=>node_file[:node_info]['name']}}}}}
253
- item=node_api.create('permissions',params)[:data]
254
- return {:type=>:single_object,:data=>item}
255
- else raise "error"
214
+ items=node_api.read("files/#{node_file[:file_id]}")[:data]
215
+ return {:type=>:single_object,:data=>items}
216
+ when :modify
217
+ update_param=self.options.get_next_argument("update data (Hash)")
218
+ res=node_api.update("files/#{node_file[:file_id]}",update_param)[:data]
219
+ return {:type=>:single_object,:data=>res}
220
+ when :permission
221
+ command_perm=self.options.get_next_command([:list,:create])
222
+ case command_perm
223
+ when :list
224
+ # generic options : TODO: as arg ?
225
+ list_options||={'include'=>['[]','access_level','permission_count']}
226
+ # special value: ALL will show all permissions
227
+ if !VAL_ALL.eql?(node_file[:file_id])
228
+ # add which one to get
229
+ list_options['file_id']=node_file[:file_id]
230
+ list_options['inherited']||=false
231
+ end
232
+ #option_url_query
233
+ items=node_api.read('permissions',list_options)[:data]
234
+ return {:type=>:object_list,:data=>items}
235
+ when :create
236
+ #create_param=self.options.get_next_argument("creation data (Hash)")
237
+ set_workspace_info
238
+ access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
239
+ node_file[:node_info]
240
+ params={
241
+ 'file_id' =>node_file[:file_id], # mandatory
242
+ 'access_type' =>'user', # mandatory: user or group
243
+ 'access_id' =>access_id, # id of user or group
244
+ 'access_levels'=>Aspera::Node::ACCESS_LEVELS,
245
+ 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
246
+ 'id' =>@workspace_id,
247
+ 'workspace_name' =>@workspace_name,
248
+ 'user_name' =>c_user_info['name'],
249
+ 'shared_by_user_id'=>c_user_info['id'],
250
+ 'shared_by_name' =>c_user_info['name'],
251
+ 'shared_by_email' =>c_user_info['email'],
252
+ 'shared_with_name' =>access_id,
253
+ 'access_key' =>node_file[:node_info]['access_key'],
254
+ 'node' =>node_file[:node_info]['name']}}}}}
255
+ item=node_api.create('permissions',params)[:data]
256
+ return {:type=>:single_object,:data=>item}
257
+ else raise "internal error:shall not reach here (#{command_perm})"
258
+ end
259
+ raise "internal error:shall not reach here"
260
+ else raise "internal error:shall not reach here (#{command_node_file})"
256
261
  end
262
+ raise "internal error:shall not reach here"
263
+ when :permissions
264
+
257
265
  end # command_repo
258
- throw "ERR"
266
+ raise "ERR"
259
267
  end # execute_node_gen4_command
260
268
 
261
269
  # build constructor option list for AoC based on options of CLI
262
270
  def aoc_params(subpath)
263
271
  # copy command line options to args
264
- opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
272
+ opt=[:link,:url,:auth,:client_id,:client_secret,:scope,:redirect_uri,:private_key,:username,:password].inject({}){|m,i|m[i]=self.options.get_option(i,:optional);m}
265
273
  opt[:subpath]=subpath
266
274
  return opt
267
275
  end
268
276
 
269
- # Create a new AoC API REST object and set @api_aoc.
270
- # Parameters based on command line options
271
- # @return nil
272
- def update_aoc_api
273
- @api_aoc=AoC.new(aoc_params('api/v1'))
274
- # add access key secrets
275
- @api_aoc.add_secrets(self.config.get_secrets)
276
- return nil
277
- end
278
-
279
277
  # initialize apis and authentication
280
278
  # set:
281
279
  # @default_workspace_id
@@ -290,8 +288,8 @@ module Aspera
290
288
  @default_workspace_id=@url_token_data['data']['workspace_id']
291
289
  @persist_ids=[] # TODO : @url_token_data['id'] ?
292
290
  else
293
- @default_workspace_id=user_info['default_workspace_id']
294
- @persist_ids=[user_info['id']]
291
+ @default_workspace_id=c_user_info['default_workspace_id']
292
+ @persist_ids=[c_user_info['id']]
295
293
  end
296
294
 
297
295
  ws_name=self.options.get_option(:workspace,:optional)
@@ -401,7 +399,8 @@ module Aspera
401
399
  package_creation[recipient_list_field]=resolved_list
402
400
  end
403
401
 
404
- def url_query(default)
402
+ # private
403
+ def option_url_query(default)
405
404
  query=self.options.get_option(:query,:optional)||default
406
405
  Log.log.debug("Query=#{query}".bg_red)
407
406
  begin
@@ -420,8 +419,7 @@ module Aspera
420
419
  end
421
420
 
422
421
  def execute_admin_action
423
- self.options.set_option(:scope,AoC::SCOPE_FILES_ADMIN)
424
- update_aoc_api
422
+ @api_aoc.oauth.params[:scope]=AoC::SCOPE_FILES_ADMIN
425
423
  command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
426
424
  case command_admin
427
425
  when :auth_providers
@@ -490,20 +488,7 @@ module Aspera
490
488
  :base_url => @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
491
489
  :auth => {:scope => AoC::SCOPE_FILES_ADMIN_USER}
492
490
  }))
493
- return @ats.execute_action_gen(ats_api)
494
- # when :search_nodes
495
- # query=self.options.get_option(:query,:optional) || '*'
496
- # nodes=@api_aoc.read("search_nodes",{'q'=>query})[:data]
497
- # # simplify output
498
- # nodes=nodes.map do |i|
499
- # item=i['_source']
500
- # item['score']=i['_score']
501
- # nodedata=item['access_key_recursive_counts'].first
502
- # item.delete('access_key_recursive_counts')
503
- # item['node']=nodedata
504
- # item
505
- # end
506
- # return {:type=>:object_list,:data=>nodes,:fields=>['host_name','node_status.cluster_id','node_status.node_id']}
491
+ return Ats.new(@agents).execute_action_gen(ats_api)
507
492
  when :analytics
508
493
  analytics_api = Rest.new(@api_aoc.params.deep_merge({
509
494
  :base_url => @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
@@ -513,19 +498,20 @@ module Aspera
513
498
  case command_analytics
514
499
  when :application_events
515
500
  event_type=command_analytics.to_s
516
- events=analytics_api.read("organizations/#{user_info['organization_id']}/#{event_type}")[:data][event_type]
501
+ events=analytics_api.read("organizations/#{c_user_info['organization_id']}/#{event_type}")[:data][event_type]
517
502
  return {:type=>:object_list,:data=>events}
518
503
  when :transfers
519
504
  event_type=command_analytics.to_s
520
505
  filter_resource=self.options.get_option(:name,:optional) || 'organizations'
521
506
  filter_id=self.options.get_option(:id,:optional) || case filter_resource
522
- when 'organizations'; user_info['organization_id']
523
- when 'users'; user_info['id']
524
- when 'nodes'; user_info['id']
507
+ when 'organizations'; c_user_info['organization_id']
508
+ when 'users'; c_user_info['id']
509
+ when 'nodes'; c_user_info['id']
525
510
  else raise "organizations or users for option --name"
526
511
  end
527
512
  #
528
513
  filter=self.options.get_option(:query,:optional) || {}
514
+ raise "query must be Hash" unless filter.is_a?(Hash)
529
515
  filter['limit']||=100
530
516
  if self.options.get_option(:once_only,:mandatory)
531
517
  saved_date=[]
@@ -542,7 +528,7 @@ module Aspera
542
528
  filter['stop_time'] = stop_datetime
543
529
  end
544
530
  notification=self.options.get_option(:notify,:optional)
545
- events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",url_query(filter))[:data][event_type]
531
+ events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",option_url_query(filter))[:data][event_type]
546
532
  startdate_persistency.save unless startdate_persistency.nil?
547
533
  if !notification.nil?
548
534
  require 'erb'
@@ -558,17 +544,31 @@ module Aspera
558
544
  return {:type=>:object_list,:data=>events}
559
545
  end
560
546
  when :resource
561
- resource_type=self.options.get_next_argument('resource',[:self,:organization,:user,:group,:client,:contact,:dropbox,:node,:operation,:package,:saml_configuration, :workspace, :dropbox_membership,:short_link,:workspace_membership,'admin/apps_new'.to_sym,'admin/client_registration_token'.to_sym,'integrations/kms_profile'.to_sym])
562
- resource_class_path=resource_type.to_s+case resource_type;when :dropbox;'es';when :self,:organization,'admin/apps_new'.to_sym;'';else; 's';end
547
+ resource_type=self.options.get_next_argument('resource',[:self,:organization,:user,:group,:client,:contact,:dropbox,:node,:operation,:package,:saml_configuration, :workspace, :dropbox_membership,:short_link,:workspace_membership,:apps_new,:client_registration_token,:client_access_key,:kms_profile])
548
+ # get path on API
549
+ resource_class_path=case resource_type
550
+ when :self,:organization
551
+ "#{resource_type}"
552
+ when :apps_new
553
+ "admin/#{resource_type}"
554
+ when :dropbox
555
+ resource_type.to_s+'es'
556
+ when :client_registration_token,:client_access_key
557
+ "admin/#{resource_type}s"
558
+ when :kms_profile
559
+ "integrations/#{resource_type}s"
560
+ else
561
+ resource_type.to_s+'s'
562
+ end
563
+ # build list of supported operations
563
564
  singleton_object=[:self,:organization].include?(resource_type)
564
565
  global_operations=[:create,:list]
565
566
  supported_operations=[:show,:modify]
566
567
  supported_operations.push(:delete,*global_operations) unless singleton_object
567
568
  supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
568
569
  supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
569
- supported_operations.push(:shared_folders) if [:node,:workspace].include?(resource_type)
570
+ supported_operations.push(:shared_folders,:shared_create) if [:node,:workspace].include?(resource_type)
570
571
  command=self.options.get_next_command(supported_operations)
571
-
572
572
  # require identifier for non global commands
573
573
  if !singleton_object and !global_operations.include?(command)
574
574
  res_id=self.options.get_option(:id)
@@ -607,11 +607,13 @@ module Aspera
607
607
  when :node; default_fields.push('host','access_key')
608
608
  when :operation; default_fields=nil
609
609
  when :contact; default_fields=["email","name","source_id","source_type"]
610
- when 'admin/apps_new'.to_sym; list_query={:organization_apps=>true};default_fields=['app_type','available']
611
- when 'admin/client_registration_token'.to_sym; default_fields=['id','value','data.client_subject_scopes','created_at']
610
+ when :apps_new; list_query={:organization_apps=>true};default_fields=['app_type','available']
611
+ when :client_registration_token; default_fields=['id','value','data.client_subject_scopes','created_at']
612
612
  end
613
- result=@api_aoc.read(resource_class_path,url_query(list_query))
614
- self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
613
+ result=@api_aoc.read(resource_class_path,option_url_query(list_query))
614
+ count_msg="Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}"
615
+ count_msg=count_msg.bg_red unless result[:data].length.eql?(result[:http]['X-Total-Count'].to_i)
616
+ self.format.display_status(count_msg)
615
617
  return {:type=>:object_list,:data=>result[:data],:fields=>default_fields}
616
618
  when :show
617
619
  object=@api_aoc.read(resource_instance_path)[:data]
@@ -634,23 +636,11 @@ module Aspera
634
636
  return Main.result_success
635
637
  when :v3,:v4
636
638
  res_data=@api_aoc.read(resource_instance_path)[:data]
637
- find_ak_secret(res_data['access_key'])
638
639
  api_node=@api_aoc.get_node_api(res_data)
639
640
  return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
640
641
  ak_data=api_node.call({:operation=>'GET',:subpath=>"access_keys/#{res_data['access_key']}",:headers=>{'Accept'=>'application/json'}})[:data]
641
- return node_gen4_execute_action({node_info: res_data, file_id: ak_data['root_file_id']})
642
- # when :info
643
- # object=@api_aoc.read(resource_instance_path)[:data]
644
- # access_key=object['access_key']
645
- # match_list=@api_aoc.read('admin/search_nodes',{:q=>"access_key:\"#{access_key}\""})[:data]
646
- # result=match_list.select{|i|i["_source"]["access_key_recursive_counts"].first["access_key"].eql?(access_key)}
647
- # return Main.result_status('Private node') if result.empty?
648
- # raise CliError,"more than one match" unless result.length.eql?(1)
649
- # result=result.first["_source"]
650
- # result.merge!(result['access_key_recursive_counts'].first)
651
- # result.delete('access_key_recursive_counts')
652
- # result.delete('token')
653
- # return { :type=>:single_object, :data =>result}
642
+ command_repo=self.options.get_next_command(NODE4_COMMANDS)
643
+ return execute_node_gen4_command(command_repo,{node_info: res_data, file_id: ak_data['root_file_id']})
654
644
  when :shared_folders
655
645
  read_params = case resource_type
656
646
  when :workspace;{'access_id'=>"ASPERA_ACCESS_KEY_ADMIN_WS_#{res_id}",'access_type'=>'user'}
@@ -661,26 +651,59 @@ module Aspera
661
651
  fields=case resource_type
662
652
  when :node;['id','file_id','file.path','access_type']
663
653
  when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
664
- else raise "error"
654
+ else raise "unexpected resource type #{resource_type}"
665
655
  end
666
656
  return { :type=>:object_list, :data =>res_data , :fields=>fields}
667
- else raise :ERROR
657
+ when :shared_create
658
+ # TODO
659
+ folder_path=self.options.get_next_argument('folder path in node')
660
+ user_create_data=self.options.get_next_argument('creation data (Hash)')
661
+ res_data=@api_aoc.read(resource_instance_path)[:data]
662
+ node_file = @api_aoc.resolve_node_file({node_info: res_data, file_id: ak_data['root_file_id']},folder_path)
663
+
664
+ #node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
665
+ #file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
666
+
667
+ access_id="ASPERA_ACCESS_KEY_ADMIN_WS_#{@workspace_id}"
668
+ create_data={
669
+ 'file_id' =>node_file[:file_id],
670
+ 'access_type' =>'user',
671
+ 'access_id' =>access_id,
672
+ 'access_levels'=>['list','read','write','delete','mkdir','rename','preview'],
673
+ 'tags' =>{'aspera'=>{'files'=>{'workspace'=>{
674
+ 'id' =>@workspace_id,
675
+ 'workspace_name' =>@workspace_name,
676
+ 'share_as' =>File.basename(folder_path),
677
+ 'user_name' =>c_user_info['name'],
678
+ 'shared_by_user_id'=>c_user_info['id'],
679
+ 'shared_by_name' =>c_user_info['name'],
680
+ 'shared_by_email' =>c_user_info['email'],
681
+ 'shared_with_name' =>access_id,
682
+ 'access_key' =>node_file[:node_info]['access_key'],
683
+ 'node' =>node_file[:node_info]['name']}
684
+ }}}}
685
+ create_data.deep_merge!(user_create_data)
686
+ Log.dump(:data,create_data)
687
+ raise :ERROR
688
+ else raise "unknown command"
668
689
  end
669
690
  when :usage_reports
670
- return {:type=>:object_list,:data=>@api_aoc.read("usage_reports",{:workspace_id=>@workspace_id})[:data]}
691
+ return {:type=>:object_list,:data=>@api_aoc.read('usage_reports',{:workspace_id=>@workspace_id})[:data]}
671
692
  end
672
693
  end
673
694
 
674
- ACTIONS=[ :apiinfo, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
695
+ # must be public
696
+ ACTIONS=[ :reminder, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
675
697
 
676
698
  def execute_action
677
699
  command=self.options.get_next_command(ACTIONS)
700
+ get_api unless [:reminder,:servers].include?(command)
678
701
  case command
679
- when :apiinfo
680
- api_info={}
681
- num=1
682
- Resolv::DNS.open{|dns|dns.each_address('api.ibmaspera.com'){|a| api_info["api.#{num}"]=a;num+=1}}
683
- return {:type=>:single_object,:data=>api_info}
702
+ when :reminder
703
+ # send an email reminder with list of orgs
704
+ user_email=options.get_option(:username,:mandatory)
705
+ Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").create('organization_reminders',{email: user_email})[:data]
706
+ return Main.result_status("List of organizations user is member of, has been sent by e-mail to #{user_email}")
684
707
  when :bearer_token
685
708
  return {:type=>:text,:data=>@api_aoc.oauth_token}
686
709
  when :organization
@@ -691,23 +714,23 @@ module Aspera
691
714
  command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
692
715
  case command
693
716
  when :workspaces
694
- return {:type=>:object_list,:data=>@api_aoc.read("workspaces")[:data],:fields=>['id','name']}
717
+ return {:type=>:object_list,:data=>@api_aoc.read('workspaces')[:data],:fields=>['id','name']}
695
718
  # when :settings
696
719
  # return {:type=>:object_list,:data=>@api_aoc.read("client_settings/")[:data]}
697
720
  when :shared_inboxes
698
- query=url_query(nil)
721
+ query=option_url_query(nil)
699
722
  if query.nil?
700
723
  set_workspace_info
701
724
  query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
702
725
  end
703
- return {:type=>:object_list,:data=>@api_aoc.read("dropbox_memberships",query)[:data],:fields=>['dropbox_id','dropbox.name']}
726
+ return {:type=>:object_list,:data=>@api_aoc.read('dropbox_memberships',query)[:data],:fields=>['dropbox_id','dropbox.name']}
704
727
  when :info
705
728
  command=self.options.get_next_command([ :show,:modify ])
706
729
  case command
707
730
  when :show
708
- return { :type=>:single_object, :data =>user_info }
731
+ return { :type=>:single_object, :data =>c_user_info }
709
732
  when :modify
710
- @api_aoc.update("users/#{user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
733
+ @api_aoc.update("users/#{c_user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
711
734
  return Main.result_status('modified')
712
735
  end
713
736
  end
@@ -720,7 +743,7 @@ module Aspera
720
743
  case command_pkg
721
744
  when :send
722
745
  package_creation=self.options.get_option(:value,:mandatory)
723
- raise CliBadArgument,"value must be hash, refer to doc" unless package_creation.is_a?(Hash)
746
+ raise CliBadArgument,'value must be hash, refer to doc' unless package_creation.is_a?(Hash)
724
747
 
725
748
  if !@url_token_data.nil?
726
749
  assert_public_link_types(['send_package_to_user','send_package_to_dropbox'])
@@ -801,12 +824,12 @@ module Aspera
801
824
  return { :type=>:single_object, :data =>package_info }
802
825
  when :list
803
826
  # list all packages ('page'=>1,'per_page'=>10,)'sort'=>'-sent_at',
804
- packages=@api_aoc.read("packages",{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
827
+ packages=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
805
828
  return {:type=>:object_list,:data=>packages,:fields=>['id','name','bytes_transferred']}
806
829
  when :delete
807
830
  list_or_one=self.options.get_option(:id,:mandatory)
808
831
  return do_bulk_operation(list_or_one,'deleted')do|id|
809
- raise "expecting String identifier" unless id.is_a?(String) or id.is_a?(Integer)
832
+ raise 'expecting String identifier' unless id.is_a?(String) or id.is_a?(Integer)
810
833
  @api_aoc.delete("packages/#{id}")[:data]
811
834
  end
812
835
  end
@@ -814,7 +837,6 @@ module Aspera
814
837
  # get workspace related information
815
838
  set_workspace_info
816
839
  set_home_node_file
817
- find_ak_secret(@home_node_file[:node_info]['access_key'],false)
818
840
  command_repo=self.options.get_next_command(NODE4_COMMANDS.clone.concat([:short_link]))
819
841
  case command_repo
820
842
  when *NODE4_COMMANDS; return execute_node_gen4_command(command_repo,@home_node_file)
@@ -827,7 +849,7 @@ module Aspera
827
849
  when 'private'
828
850
  value_option={'purpose'=>'shared_folder_auth_link'}
829
851
  when NilClass,Hash
830
- else raise "value must be either: public, private, Hash or nil"
852
+ else raise 'value must be either: public, private, Hash or nil'
831
853
  end
832
854
  create_params=nil
833
855
  node_file=nil
@@ -855,30 +877,33 @@ module Aspera
855
877
  }
856
878
  value_option['user_selected_name']=nil
857
879
  else
858
- raise "purpose must be one of: token_auth_redirection or shared_folder_auth_link"
880
+ raise 'purpose must be one of: token_auth_redirection or shared_folder_auth_link'
859
881
  end
860
882
  self.options.set_option(:value,value_option)
861
883
  end
862
884
  result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
863
885
  if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
864
886
  node_api=@api_aoc.get_node_api(node_file[:node_info],AoC::SCOPE_NODE_USER)
887
+ # TODO: access level as arg
888
+ access_levels=Aspera::Node::ACCESS_LEVELS #['delete','list','mkdir','preview','read','rename','write']
865
889
  perm_data={
866
- "file_id" =>node_file[:file_id],
867
- "access_type" =>"user",
868
- "access_id" =>result[:data]['resource_id'],
869
- "access_levels"=>["delete","list","mkdir","preview","read","rename","write"],
870
- "tags" =>{
871
- "url_token" =>true,
872
- "workspace_id" =>@workspace_id,
873
- "workspace_name" =>@workspace_name,
874
- "folder_name" =>"my folder",
875
- "created_by_name" =>user_info['name'],
876
- "created_by_email"=>user_info['email'],
877
- "access_key" =>node_file[:node_info]['access_key'],
878
- "node" =>node_file[:node_info]['host']
890
+ 'file_id' =>node_file[:file_id],
891
+ 'access_type' =>'user',
892
+ 'access_id' =>result[:data]['resource_id'],
893
+ 'access_levels'=>access_levels,
894
+ 'tags' =>{
895
+ 'url_token' =>true,
896
+ 'workspace_id' =>@workspace_id,
897
+ 'workspace_name' =>@workspace_name,
898
+ 'folder_name' =>'my folder',
899
+ 'created_by_name' =>c_user_info['name'],
900
+ 'created_by_email'=>c_user_info['email'],
901
+ 'access_key' =>node_file[:node_info]['access_key'],
902
+ 'node' =>node_file[:node_info]['host']
879
903
  }
880
904
  }
881
905
  node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
906
+ # TODO: event ?
882
907
  end
883
908
  return result
884
909
  end # files command
@@ -921,17 +946,17 @@ module Aspera
921
946
  when :admin
922
947
  return execute_admin_action
923
948
  when :servers
924
- self.format.display_status("Beta feature")
925
- server_api=Rest.new(base_url: 'https://eudemo.asperademo.com')
926
- require 'json'
927
- servers=JSON.parse(server_api.read('servers')[:data])
928
- return {:type=>:object_list,:data=>servers}
949
+ return {:type=>:object_list,:data=>Rest.new(base_url: "#{AoC.api_base_url}/#{AoC::API_V1}").read('servers')[:data]}
929
950
  else
930
951
  raise "internal error: #{command}"
931
952
  end # action
932
953
  raise RuntimeError, "internal error: command shall return"
933
954
  end
934
- end # Aspera
955
+
956
+ private :c_user_info,:aoc_params,:set_workspace_info,:set_home_node_file,:do_bulk_operation,:resolve_package_recipients,:option_url_query,:assert_public_link_types,:execute_admin_action
957
+ private_constant :VAL_ALL,:NODE4_COMMANDS
958
+
959
+ end # AoC
935
960
  end # Plugins
936
961
  end # Cli
937
962
  end # Aspera