aspera-cli 4.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3592 -0
  3. data/bin/ascli +7 -0
  4. data/bin/asession +89 -0
  5. data/docs/Makefile +59 -0
  6. data/docs/README.erb.md +3012 -0
  7. data/docs/README.md +13 -0
  8. data/docs/diagrams.txt +49 -0
  9. data/docs/secrets.make +38 -0
  10. data/docs/test_env.conf +117 -0
  11. data/docs/transfer_spec.html +99 -0
  12. data/examples/aoc.rb +17 -0
  13. data/examples/proxy.pac +60 -0
  14. data/examples/transfer.rb +115 -0
  15. data/lib/aspera/api_detector.rb +60 -0
  16. data/lib/aspera/ascmd.rb +151 -0
  17. data/lib/aspera/ats_api.rb +43 -0
  18. data/lib/aspera/cli/basic_auth_plugin.rb +38 -0
  19. data/lib/aspera/cli/extended_value.rb +88 -0
  20. data/lib/aspera/cli/formater.rb +238 -0
  21. data/lib/aspera/cli/listener/line_dump.rb +17 -0
  22. data/lib/aspera/cli/listener/logger.rb +20 -0
  23. data/lib/aspera/cli/listener/progress.rb +52 -0
  24. data/lib/aspera/cli/listener/progress_multi.rb +91 -0
  25. data/lib/aspera/cli/main.rb +304 -0
  26. data/lib/aspera/cli/manager.rb +440 -0
  27. data/lib/aspera/cli/plugin.rb +90 -0
  28. data/lib/aspera/cli/plugins/alee.rb +24 -0
  29. data/lib/aspera/cli/plugins/ats.rb +231 -0
  30. data/lib/aspera/cli/plugins/bss.rb +71 -0
  31. data/lib/aspera/cli/plugins/config.rb +806 -0
  32. data/lib/aspera/cli/plugins/console.rb +62 -0
  33. data/lib/aspera/cli/plugins/cos.rb +106 -0
  34. data/lib/aspera/cli/plugins/faspex.rb +377 -0
  35. data/lib/aspera/cli/plugins/faspex5.rb +93 -0
  36. data/lib/aspera/cli/plugins/node.rb +438 -0
  37. data/lib/aspera/cli/plugins/oncloud.rb +937 -0
  38. data/lib/aspera/cli/plugins/orchestrator.rb +169 -0
  39. data/lib/aspera/cli/plugins/preview.rb +464 -0
  40. data/lib/aspera/cli/plugins/server.rb +216 -0
  41. data/lib/aspera/cli/plugins/shares.rb +63 -0
  42. data/lib/aspera/cli/plugins/shares2.rb +114 -0
  43. data/lib/aspera/cli/plugins/sync.rb +65 -0
  44. data/lib/aspera/cli/plugins/xnode.rb +115 -0
  45. data/lib/aspera/cli/transfer_agent.rb +251 -0
  46. data/lib/aspera/cli/version.rb +5 -0
  47. data/lib/aspera/colors.rb +39 -0
  48. data/lib/aspera/command_line_builder.rb +137 -0
  49. data/lib/aspera/fasp/aoc.rb +24 -0
  50. data/lib/aspera/fasp/connect.rb +99 -0
  51. data/lib/aspera/fasp/error.rb +21 -0
  52. data/lib/aspera/fasp/error_info.rb +60 -0
  53. data/lib/aspera/fasp/http_gw.rb +81 -0
  54. data/lib/aspera/fasp/installation.rb +240 -0
  55. data/lib/aspera/fasp/listener.rb +11 -0
  56. data/lib/aspera/fasp/local.rb +377 -0
  57. data/lib/aspera/fasp/manager.rb +69 -0
  58. data/lib/aspera/fasp/node.rb +88 -0
  59. data/lib/aspera/fasp/parameters.rb +235 -0
  60. data/lib/aspera/fasp/resume_policy.rb +76 -0
  61. data/lib/aspera/fasp/uri.rb +51 -0
  62. data/lib/aspera/faspex_gw.rb +196 -0
  63. data/lib/aspera/hash_ext.rb +28 -0
  64. data/lib/aspera/log.rb +80 -0
  65. data/lib/aspera/nagios.rb +71 -0
  66. data/lib/aspera/node.rb +14 -0
  67. data/lib/aspera/oauth.rb +319 -0
  68. data/lib/aspera/on_cloud.rb +421 -0
  69. data/lib/aspera/open_application.rb +72 -0
  70. data/lib/aspera/persistency_action_once.rb +42 -0
  71. data/lib/aspera/persistency_folder.rb +91 -0
  72. data/lib/aspera/preview/file_types.rb +300 -0
  73. data/lib/aspera/preview/generator.rb +258 -0
  74. data/lib/aspera/preview/image_error.png +0 -0
  75. data/lib/aspera/preview/options.rb +35 -0
  76. data/lib/aspera/preview/utils.rb +131 -0
  77. data/lib/aspera/preview/video_error.png +0 -0
  78. data/lib/aspera/proxy_auto_config.erb.js +287 -0
  79. data/lib/aspera/proxy_auto_config.rb +34 -0
  80. data/lib/aspera/rest.rb +296 -0
  81. data/lib/aspera/rest_call_error.rb +13 -0
  82. data/lib/aspera/rest_error_analyzer.rb +98 -0
  83. data/lib/aspera/rest_errors_aspera.rb +58 -0
  84. data/lib/aspera/ssh.rb +53 -0
  85. data/lib/aspera/sync.rb +82 -0
  86. data/lib/aspera/temp_file_manager.rb +37 -0
  87. data/lib/aspera/uri_reader.rb +25 -0
  88. metadata +288 -0
@@ -0,0 +1,937 @@
1
+ require 'aspera/cli/plugins/node'
2
+ require 'aspera/cli/plugins/ats'
3
+ require 'aspera/cli/basic_auth_plugin'
4
+ require 'aspera/cli/transfer_agent'
5
+ require 'aspera/on_cloud'
6
+ require 'aspera/persistency_action_once'
7
+ require 'securerandom'
8
+ require 'resolv'
9
+ require 'date'
10
+
11
+ module Aspera
12
+ module Cli
13
+ module Plugins
14
+ class Oncloud < BasicAuthPlugin
15
+ VAL_ALL='ALL'
16
+ private_constant :VAL_ALL
17
+ attr_reader :api_aoc
18
+ def initialize(env)
19
+ super(env)
20
+ @default_workspace_id=nil
21
+ @workspace_name=nil
22
+ @workspace_id=nil
23
+ @persist_ids=nil
24
+ @home_node_file=nil
25
+ @api_aoc=nil
26
+ @url_token_data=nil
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")
45
+ self.options.set_option(:bulk,:no)
46
+ self.options.set_option(:default_ports,:yes)
47
+ self.options.set_option(:new_user_option,{'package_contact'=>true})
48
+ self.options.set_option(:operation,:push)
49
+ self.options.set_option(:auth,:jwt)
50
+ self.options.set_option(:scope,OnCloud::SCOPE_FILES_USER)
51
+ self.options.set_option(:private_key,'@file:'+env[:private_key_path]) if env[:private_key_path].is_a?(String)
52
+ self.options.parse_options!
53
+ OnCloud.set_use_default_ports(self.options.get_option(:default_ports))
54
+ return if env[:man_only]
55
+ update_aoc_api
56
+ end
57
+
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
66
+ end
67
+
68
+ def user_info
69
+ if @user_info.nil?
70
+ # get our user's default information
71
+ # self?embed[]=default_workspace&embed[]=organization
72
+ @user_info=@api_aoc.read('self')[:data] rescue {
73
+ 'name' => 'unknown',
74
+ 'email' => 'unknown',
75
+ }
76
+ end
77
+ return @user_info
78
+ end
79
+
80
+ # starts transfer using transfer agent
81
+ def transfer_start(app,direction,node_file,ts_add)
82
+ ts_add.deep_merge!(OnCloud.analytics_ts(app,direction,@workspace_id,@workspace_name))
83
+ ts_add.deep_merge!(OnCloud.console_ts(app,user_info['name'],user_info['email']))
84
+ return self.transfer.start(*@api_aoc.tr_spec(app,direction,node_file,ts_add))
85
+ end
86
+
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
93
+
94
+ def execute_node_gen4_command(command_repo,top_node_file)
95
+ case command_repo
96
+ when :bearer_token_node
97
+ thepath=self.options.get_next_argument('path')
98
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
99
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
100
+ return Main.result_status(node_api.oauth_token)
101
+ when :browse
102
+ thepath=self.options.get_next_argument('path')
103
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
104
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
105
+ file_info = node_api.read("files/#{node_file[:file_id]}")[:data]
106
+ if file_info['type'].eql?('folder')
107
+ result=node_api.read("files/#{node_file[:file_id]}/files",self.options.get_option(:value,:optional))
108
+ items=result[:data]
109
+ self.format.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
110
+ else
111
+ items=[file_info]
112
+ end
113
+ return {:type=>:object_list,:data=>items,:fields=>['name','type','recursive_size','size','modified_time','access_level']}
114
+ when :find
115
+ thepath=self.options.get_next_argument('path')
116
+ exec_prefix='exec:'
117
+ expression=self.options.get_option(:value,:optional)||"#{exec_prefix}true"
118
+ 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
124
+ return {:type=>:object_list,:data=>@api_aoc.find_files(node_file,test_block),:fields=>['path']}
125
+ when :mkdir
126
+ thepath=self.options.get_next_argument('path')
127
+ containing_folder_path = thepath.split(OnCloud::PATH_SEPARATOR)
128
+ new_folder=containing_folder_path.pop
129
+ node_file = @api_aoc.resolve_node_file(top_node_file,containing_folder_path.join(OnCloud::PATH_SEPARATOR))
130
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
131
+ result=node_api.create("files/#{node_file[:file_id]}/files",{:name=>new_folder,:type=>:folder})[:data]
132
+ return Main.result_status("created: #{result['name']} (id=#{result['id']})")
133
+ when :rename
134
+ thepath=self.options.get_next_argument('source path')
135
+ newname=self.options.get_next_argument('new name')
136
+ node_file = @api_aoc.resolve_node_file(top_node_file,thepath)
137
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
138
+ result=node_api.update("files/#{node_file[:file_id]}",{:name=>newname})[:data]
139
+ return Main.result_status("renamed #{thepath} to #{newname}")
140
+ when :delete
141
+ 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)
145
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
146
+ result=node_api.delete("files/#{node_file[:file_id]}")[:data]
147
+ {'path'=>thepath}
148
+ end
149
+ when :transfer
150
+ # client side is agent
151
+ # server side is protocol server
152
+ # in same workspace
153
+ server_home_node_file=client_home_node_file=top_node_file
154
+ # default is push
155
+ case self.options.get_option(:operation,:mandatory)
156
+ when :push
157
+ client_tr_oper='send'
158
+ client_folder=self.options.get_option(:from_folder,:mandatory)
159
+ server_folder=self.transfer.destination_folder(client_tr_oper)
160
+ when :pull
161
+ client_tr_oper='receive'
162
+ client_folder=self.transfer.destination_folder(client_tr_oper)
163
+ server_folder=self.options.get_option(:from_folder,:mandatory)
164
+ end
165
+ client_node_file = @api_aoc.resolve_node_file(client_home_node_file,client_folder)
166
+ server_node_file = @api_aoc.resolve_node_file(server_home_node_file,server_folder)
167
+ # force node as transfer agent
168
+ @agents[:transfer].set_agent_instance(Fasp::Node.new(@api_aoc.get_node_api(client_node_file[:node_info],OnCloud::SCOPE_NODE_USER)))
169
+ # additional node to node TS info
170
+ add_ts={
171
+ 'remote_access_key' => server_node_file[:node_info]['access_key'],
172
+ 'destination_root_id' => server_node_file[:file_id],
173
+ 'source_root_id' => client_node_file[:file_id]
174
+ }
175
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,client_tr_oper,server_node_file,add_ts))
176
+ when :upload
177
+ node_file = @api_aoc.resolve_node_file(top_node_file,self.transfer.destination_folder('send'))
178
+ add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
179
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,'send',node_file,add_ts))
180
+ when :download
181
+ source_paths=self.transfer.ts_source_paths
182
+ # special case for AoC : all files must be in same folder
183
+ source_folder=source_paths.shift['source']
184
+ # if a single file: split into folder and path
185
+ if source_paths.empty?
186
+ source_folder=source_folder.split(OnCloud::PATH_SEPARATOR)
187
+ source_paths=[{'source'=>source_folder.pop}]
188
+ source_folder=source_folder.join(OnCloud::PATH_SEPARATOR)
189
+ end
190
+ node_file = @api_aoc.resolve_node_file(top_node_file,source_folder)
191
+ # override paths with just filename
192
+ add_ts={'tags'=>{'aspera'=>{'files'=>{'parentCwd'=>"#{node_file[:node_info]['id']}:#{node_file[:file_id]}"}}}}
193
+ add_ts.merge!({'paths'=>source_paths})
194
+ return Main.result_transfer(transfer_start(OnCloud::FILES_APP,'receive',node_file,add_ts))
195
+ when :http_node_download
196
+ source_paths=self.transfer.ts_source_paths
197
+ source_folder=source_paths.shift['source']
198
+ if source_paths.empty?
199
+ source_folder=source_folder.split(OnCloud::PATH_SEPARATOR)
200
+ source_paths=[{'source'=>source_folder.pop}]
201
+ source_folder=source_folder.join(OnCloud::PATH_SEPARATOR)
202
+ end
203
+ raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
204
+ file_name = source_paths.first['source']
205
+ node_file = @api_aoc.resolve_node_file(top_node_file,File.join(source_folder,file_name))
206
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
207
+ node_api.call({:operation=>'GET',:subpath=>"files/#{node_file[:file_id]}/content",:save_to_file=>File.join(self.transfer.destination_folder('receive'),file_name)})
208
+ return Main.result_status("downloaded: #{file_name}")
209
+ when :v3
210
+ # Note: other "common" actions are unauthorized with user scope
211
+ command_legacy=self.options.get_next_command(Node::SIMPLE_ACTIONS)
212
+ # TODO: shall we support all methods here ? what if there is a link ?
213
+ node_api=@api_aoc.get_node_api(top_node_file[:node_info],OnCloud::SCOPE_NODE_USER)
214
+ return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: node_api)).execute_action(command_legacy)
215
+ 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],OnCloud::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)
224
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
225
+ command_perms=self.options.get_next_command([:show,:create])
226
+ case command_perms
227
+ 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"
256
+ end
257
+ end # command_repo
258
+ throw "ERR"
259
+ end # execute_node_gen4_command
260
+
261
+ # build constructor option list for OnCloud based on options of CLI
262
+ def oncloud_params(subpath)
263
+ # 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}
265
+ opt[:subpath]=subpath
266
+ return opt
267
+ end
268
+
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=OnCloud.new(oncloud_params('api/v1'))
274
+ # add access key secrets
275
+ @api_aoc.add_secrets(self.config.get_secrets)
276
+ return nil
277
+ end
278
+
279
+ # initialize apis and authentication
280
+ # set:
281
+ # @default_workspace_id
282
+ # @workspace_name
283
+ # @workspace_id
284
+ # @persist_ids
285
+ # returns nil
286
+ def set_workspace_info
287
+ if @api_aoc.params[:auth].has_key?(:url_token)
288
+ # TODO: can there be several in list ?
289
+ @url_token_data=@api_aoc.read('url_tokens')[:data].first
290
+ @default_workspace_id=@url_token_data['data']['workspace_id']
291
+ @persist_ids=[] # TODO : @url_token_data['id'] ?
292
+ else
293
+ @default_workspace_id=user_info['default_workspace_id']
294
+ @persist_ids=[user_info['id']]
295
+ end
296
+
297
+ ws_name=self.options.get_option(:workspace,:optional)
298
+ if ws_name.nil?
299
+ Log.log.debug("using default workspace".green)
300
+ if @default_workspace_id.eql?(nil)
301
+ raise CliError,"no default workspace defined for user, please specify workspace"
302
+ end
303
+ # get default workspace
304
+ @workspace_id=@default_workspace_id
305
+ else
306
+ # lookup another workspace
307
+ wss=@api_aoc.read("workspaces",{'q'=>ws_name})[:data]
308
+ wss=wss.select { |i| i['name'].eql?(ws_name) }
309
+ case wss.length
310
+ when 0
311
+ raise CliBadArgument,"no such workspace: #{ws_name}"
312
+ when 1
313
+ @workspace_id=wss.first['id']
314
+ else
315
+ raise "unexpected case"
316
+ end
317
+ end
318
+ @workspace_data=@api_aoc.read("workspaces/#{@workspace_id}")[:data]
319
+ Log.log.debug("workspace_id=#{@workspace_id},@workspace_data=#{@workspace_data}".red)
320
+
321
+ @workspace_name||=@workspace_data['name']
322
+ Log.log.info("current workspace is "+@workspace_name.red)
323
+
324
+ # display workspace
325
+ self.format.display_status("Current Workspace: #{@workspace_name.red}#{@workspace_id == @default_workspace_id ? ' (default)' : ''}")
326
+ return nil
327
+ end
328
+
329
+ # @home_node_file (hash with :node_info and :file_id)
330
+ def set_home_node_file
331
+ if !@url_token_data.nil?
332
+ assert_public_link_types(['view_shared_file'])
333
+ home_node_id=@url_token_data['data']['node_id']
334
+ home_file_id=@url_token_data['data']['file_id']
335
+ end
336
+ home_node_id||=@workspace_data['home_node_id']||@workspace_data['node_id']
337
+ home_file_id||=@workspace_data['home_file_id']
338
+ raise "node_id must be defined" if home_node_id.to_s.empty?
339
+ @home_node_file={
340
+ node_info: @api_aoc.read("nodes/#{home_node_id}")[:data],
341
+ file_id: home_file_id
342
+ }
343
+ @api_aoc.check_get_node_file(@home_node_file)
344
+
345
+ return nil
346
+ end
347
+
348
+ def do_bulk_operation(ids_or_one,success_msg,id_result='id',&do_action)
349
+ ids_or_one=[ids_or_one] unless self.options.get_option(:bulk)
350
+ raise "expecting Array" unless ids_or_one.is_a?(Array)
351
+ result_list=[]
352
+ ids_or_one.each do |id|
353
+ one={id_result=>id}
354
+ begin
355
+ res=do_action.call(id)
356
+ one=res if id.is_a?(Hash) # if block returns a has, let's use this
357
+ one['status']=success_msg
358
+ rescue => e
359
+ one['status']=e.to_s
360
+ end
361
+ result_list.push(one)
362
+ end
363
+ return {:type=>:object_list,:data=>result_list,:fields=>[id_result,'status']}
364
+ end
365
+
366
+ # package creation params can give just email, and full hash is created
367
+ def resolve_package_recipients(package_creation,recipient_list_field)
368
+ return unless package_creation.has_key?(recipient_list_field)
369
+ raise CliBadArgument,"#{recipient_list_field} must be an Array" unless package_creation[recipient_list_field].is_a?(Array)
370
+ new_user_option=self.options.get_option(:new_user_option,:mandatory)
371
+ resolved_list=[]
372
+ package_creation[recipient_list_field].each do |recipient_email_or_info|
373
+ case recipient_email_or_info
374
+ when Hash
375
+ raise 'recipient element hash shall have field id and type' unless recipient_email_or_info.has_key?('id') and recipient_email_or_info.has_key?('type')
376
+ # already provided all information ?
377
+ resolved_list.push(recipient_email_or_info)
378
+ when String
379
+ if recipient_email_or_info.include?('@')
380
+ # or need to resolve email
381
+ item_lookup=@api_aoc.read('contacts',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
382
+ case item_lookup.length
383
+ when 1; recipient_user_id=item_lookup.first
384
+ when 0; recipient_user_id=@api_aoc.create('contacts',{'current_workspace_id'=>@workspace_id,'email'=>recipient_email_or_info}.merge(new_user_option))[:data]
385
+ else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
386
+ end
387
+ resolved_list.push({'id'=>recipient_user_id['source_id'],'type'=>recipient_user_id['source_type']})
388
+ else
389
+ item_lookup=@api_aoc.read('dropboxes',{'current_workspace_id'=>@workspace_id,'q'=>recipient_email_or_info})[:data]
390
+ case item_lookup.length
391
+ when 1; recipient_user_id=item_lookup.first
392
+ when 0; raise "no such shared inbox in workspace #{@workspace_name}"
393
+ else raise CliBadArgument,"multiple match for: #{recipient_email_or_info}"
394
+ end
395
+ resolved_list.push({'id'=>recipient_user_id['id'],'type'=>'dropbox'})
396
+ end
397
+ else
398
+ raise "recipient item must be a String (email, shared inboc) or hash (id,type)"
399
+ end
400
+ end
401
+ package_creation[recipient_list_field]=resolved_list
402
+ end
403
+
404
+ def url_query(default)
405
+ query=self.options.get_option(:query,:optional)||default
406
+ Log.log.debug("Query=#{query}".bg_red)
407
+ begin
408
+ # check it is suitable
409
+ URI.encode_www_form(query) unless query.nil?
410
+ rescue => e
411
+ raise CliBadArgument,"query must be an extended value which can be encoded with URI.encode_www_form. Refer to manual. (#{e.message})"
412
+ end
413
+ return query
414
+ end
415
+
416
+ def assert_public_link_types(expected)
417
+ if !expected.include?(@url_token_data['purpose'])
418
+ raise CliBadArgument,"public link type is #{@url_token_data['purpose']} but action requires one of #{expected.join(',')}"
419
+ end
420
+ end
421
+
422
+ def execute_admin_action
423
+ self.options.set_option(:scope,OnCloud::SCOPE_FILES_ADMIN)
424
+ update_aoc_api
425
+ command_admin=self.options.get_next_command([ :ats, :resource, :usage_reports, :analytics, :subscription, :auth_providers ])
426
+ case command_admin
427
+ when :auth_providers
428
+ command_auth_prov=self.options.get_next_command([ :list, :update ])
429
+ case command_auth_prov
430
+ when :list
431
+ providers=@api_aoc.read('admin/auth_providers')[:data]
432
+ return {:type=>:object_list,:data=>providers}
433
+ when :update
434
+ end
435
+ when :subscription
436
+ org=@api_aoc.read('organization')[:data]
437
+ bss_api=OnCloud.new(oncloud_params('bss/platform'))
438
+ graphql_query="
439
+ query ($organization_id: ID!) {
440
+ aoc (organization_id: $organization_id) {
441
+ bssSubscription {
442
+ endDate
443
+ startDate
444
+ termMonths
445
+ plan
446
+ trial
447
+ termType
448
+ instances {
449
+ id
450
+ entitlements {
451
+ maxUsageMb
452
+ }
453
+ }
454
+ additionalStorageVolumeGb
455
+ additionalEgressVolumeGb
456
+ additionalUsers
457
+ term {
458
+ startDate
459
+ endDate
460
+ transferVolumeGb
461
+ egressVolumeGb
462
+ storageVolumeGb
463
+ }
464
+ paygoRate {
465
+ rate
466
+ currency
467
+ }
468
+ aocPlanData {
469
+ tier
470
+ trial
471
+ workspaces { max }
472
+ users {
473
+ planAmount
474
+ max
475
+ }
476
+ samlIntegration
477
+ activity
478
+ sharedInboxes
479
+ uniqueUrls
480
+ support
481
+ }
482
+ }
483
+ }
484
+ }
485
+ "
486
+ result=bss_api.create('graphql',{'variables'=>{'organization_id'=>org['id']},'query'=>graphql_query})[:data]['data']
487
+ return {:type=>:single_object,:data=>result['aoc']['bssSubscription']}
488
+ when :ats
489
+ ats_api = Rest.new(@api_aoc.params.deep_merge({
490
+ :base_url => @api_aoc.params[:base_url]+'/admin/ats/pub/v1',
491
+ :auth => {:scope => OnCloud::SCOPE_FILES_ADMIN_USER}
492
+ }))
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']}
507
+ when :analytics
508
+ analytics_api = Rest.new(@api_aoc.params.deep_merge({
509
+ :base_url => @api_aoc.params[:base_url].gsub('/api/v1','')+'/analytics/v2',
510
+ :auth => {:scope => OnCloud::SCOPE_FILES_ADMIN_USER}
511
+ }))
512
+ command_analytics=self.options.get_next_command([ :application_events, :transfers ])
513
+ case command_analytics
514
+ when :application_events
515
+ event_type=command_analytics.to_s
516
+ events=analytics_api.read("organizations/#{user_info['organization_id']}/#{event_type}")[:data][event_type]
517
+ return {:type=>:object_list,:data=>events}
518
+ when :transfers
519
+ event_type=command_analytics.to_s
520
+ filter_resource=self.options.get_option(:name,:optional) || 'organizations'
521
+ 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']
525
+ else raise "organizations or users for option --name"
526
+ end
527
+ #
528
+ filter=self.options.get_option(:query,:optional) || {}
529
+ filter['limit']||=100
530
+ if self.options.get_option(:once_only,:mandatory)
531
+ saved_date=[]
532
+ startdate_persistency=PersistencyActionOnce.new(
533
+ manager: @agents[:persistency],
534
+ data: saved_date,
535
+ ids: ['aoc_ana_date',self.options.get_option(:url,:mandatory),@workspace_name].push(filter_resource,filter_id))
536
+ start_datetime=saved_date.first
537
+ stop_datetime=Time.now.utc.strftime('%FT%T.%LZ')
538
+ #Log.log().error("start: #{start_datetime}")
539
+ #Log.log().error("end: #{stop_datetime}")
540
+ saved_date[0]=stop_datetime
541
+ filter['start_time'] = start_datetime unless start_datetime.nil?
542
+ filter['stop_time'] = stop_datetime
543
+ end
544
+ notification=self.options.get_option(:notify,:optional)
545
+ events=analytics_api.read("#{filter_resource}/#{filter_id}/#{event_type}",url_query(filter))[:data][event_type]
546
+ startdate_persistency.save unless startdate_persistency.nil?
547
+ if !notification.nil?
548
+ require 'erb'
549
+ events.each do |transfer|
550
+ email_to_send={}
551
+ notification.each do |k,v|
552
+ email_to_send[k.to_sym]=ERB.new(v).result(binding)
553
+ end
554
+ Log.log().error("send email: #{email_to_send}")
555
+ self.config.send_email(email_to_send)
556
+ end
557
+ end
558
+ return {:type=>:object_list,:data=>events}
559
+ end
560
+ 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
563
+ singleton_object=[:self,:organization].include?(resource_type)
564
+ global_operations=[:create,:list]
565
+ supported_operations=[:show,:modify]
566
+ supported_operations.push(:delete,*global_operations) unless singleton_object
567
+ supported_operations.push(:v4,:v3) if resource_type.eql?(:node)
568
+ supported_operations.push(:set_pub_key) if resource_type.eql?(:client)
569
+ supported_operations.push(:shared_folders) if [:node,:workspace].include?(resource_type)
570
+ command=self.options.get_next_command(supported_operations)
571
+
572
+ # require identifier for non global commands
573
+ if !singleton_object and !global_operations.include?(command)
574
+ res_id=self.options.get_option(:id)
575
+ res_name=self.options.get_option(:name)
576
+ if res_id.nil? and res_name.nil? and resource_type.eql?(:node)
577
+ set_workspace_info
578
+ set_home_node_file
579
+ res_id=@home_node_file[:node_info]['id']
580
+ end
581
+ if !res_name.nil?
582
+ Log.log.warn("name overrides id") unless res_id.nil?
583
+ matching=@api_aoc.read(resource_class_path,{:q=>res_name})[:data]
584
+ raise CliError,"no resource match name" if matching.empty?
585
+ raise CliError,"several resources match name (#{matching.join(',')})" unless matching.length.eql?(1)
586
+ res_id=matching.first['id']
587
+ end
588
+ raise CliBadArgument,"provide either id or name" if res_id.nil?
589
+ resource_instance_path="#{resource_class_path}/#{res_id}"
590
+ end
591
+ resource_instance_path=resource_class_path if singleton_object
592
+ case command
593
+ when :create
594
+ id_result='id'
595
+ id_result='token' if resource_class_path.eql?('admin/client_registration_tokens')
596
+ # TODO: report inconsistency: creation url is !=, and does not return id.
597
+ resource_class_path='admin/client_registration/token' if resource_class_path.eql?('admin/client_registration_tokens')
598
+ list_or_one=self.options.get_next_argument("creation data (Hash)")
599
+ return do_bulk_operation(list_or_one,'created',id_result)do|params|
600
+ raise "expecting Hash" unless params.is_a?(Hash)
601
+ @api_aoc.create(resource_class_path,params)[:data]
602
+ end
603
+ when :list
604
+ default_fields=['id','name']
605
+ list_query=nil
606
+ case resource_type
607
+ when :node; default_fields.push('host','access_key')
608
+ when :operation; default_fields=nil
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']
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']}")
615
+ return {:type=>:object_list,:data=>result[:data],:fields=>default_fields}
616
+ when :show
617
+ object=@api_aoc.read(resource_instance_path)[:data]
618
+ fields=object.keys.select{|k|!k.eql?('certificate')}
619
+ return { :type=>:single_object, :data =>object, :fields=>fields }
620
+ when :modify
621
+ changes=self.options.get_next_argument('modified parameters (hash)')
622
+ @api_aoc.update(resource_instance_path,changes)
623
+ return Main.result_status('modified')
624
+ when :delete
625
+ return do_bulk_operation(res_id,'deleted')do|one_id|
626
+ @api_aoc.delete("#{resource_class_path}/#{one_id.to_s}")
627
+ {'id'=>one_id}
628
+ end
629
+ when :set_pub_key
630
+ # special : reads private and generate public
631
+ the_private_key=self.options.get_next_argument('private_key')
632
+ the_public_key=OpenSSL::PKey::RSA.new(the_private_key).public_key.to_s
633
+ @api_aoc.update(resource_instance_path,{:jwt_grant_enabled=>true, :public_key=>the_public_key})
634
+ return Main.result_success
635
+ when :v3,:v4
636
+ res_data=@api_aoc.read(resource_instance_path)[:data]
637
+ find_ak_secret(res_data['access_key'])
638
+ api_node=@api_aoc.get_node_api(res_data)
639
+ return Node.new(@agents.merge(skip_basic_auth_options: true, node_api: api_node)).execute_action if command.eql?(:v3)
640
+ 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}
654
+ when :shared_folders
655
+ read_params = case resource_type
656
+ when :workspace;{'access_id'=>"ASPERA_ACCESS_KEY_ADMIN_WS_#{res_id}",'access_type'=>'user'}
657
+ when :node;{'include'=>['[]','access_level','permission_count'],'created_by_id'=>"ASPERA_ACCESS_KEY_ADMIN"}
658
+ else raise "error"
659
+ end
660
+ res_data=@api_aoc.read("#{resource_class_path}/#{res_id}/permissions",read_params)[:data]
661
+ fields=case resource_type
662
+ when :node;['id','file_id','file.path','access_type']
663
+ when :workspace;['id','node_id','file_id','node_name','file.path','tags.aspera.files.workspace.share_as']
664
+ else raise "error"
665
+ end
666
+ return { :type=>:object_list, :data =>res_data , :fields=>fields}
667
+ else raise :ERROR
668
+ end
669
+ when :usage_reports
670
+ return {:type=>:object_list,:data=>@api_aoc.read("usage_reports",{:workspace_id=>@workspace_id})[:data]}
671
+ end
672
+ end
673
+
674
+ ACTIONS=[ :apiinfo, :bearer_token, :organization, :tier_restrictions, :user, :workspace, :packages, :files, :gateway, :admin, :automation, :servers]
675
+
676
+ def execute_action
677
+ command=self.options.get_next_command(ACTIONS)
678
+ 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}
684
+ when :bearer_token
685
+ return {:type=>:text,:data=>@api_aoc.oauth_token}
686
+ when :organization
687
+ return { :type=>:single_object, :data =>@api_aoc.read('organization')[:data] }
688
+ when :tier_restrictions
689
+ return { :type=>:single_object, :data =>@api_aoc.read('tier_restrictions')[:data] }
690
+ when :user
691
+ command=self.options.get_next_command([ :workspaces,:info,:shared_inboxes ])
692
+ case command
693
+ when :workspaces
694
+ return {:type=>:object_list,:data=>@api_aoc.read("workspaces")[:data],:fields=>['id','name']}
695
+ # when :settings
696
+ # return {:type=>:object_list,:data=>@api_aoc.read("client_settings/")[:data]}
697
+ when :shared_inboxes
698
+ query=url_query(nil)
699
+ if query.nil?
700
+ set_workspace_info
701
+ query={'embed[]'=>'dropbox','workspace_id'=>@workspace_id,'aggregate_permissions_by_dropbox'=>true,'sort'=>'dropbox_name'}
702
+ end
703
+ return {:type=>:object_list,:data=>@api_aoc.read("dropbox_memberships",query)[:data],:fields=>['dropbox_id','dropbox.name']}
704
+ when :info
705
+ command=self.options.get_next_command([ :show,:modify ])
706
+ case command
707
+ when :show
708
+ return { :type=>:single_object, :data =>user_info }
709
+ when :modify
710
+ @api_aoc.update("users/#{user_info['id']}",self.options.get_next_argument('modified parameters (hash)'))
711
+ return Main.result_status('modified')
712
+ end
713
+ end
714
+ when :workspace # show current workspace parameters
715
+ set_workspace_info
716
+ return { :type=>:single_object, :data =>@workspace_data }
717
+ when :packages
718
+ set_workspace_info if @url_token_data.nil?
719
+ command_pkg=self.options.get_next_command([ :send, :recv, :list, :show, :delete ])
720
+ case command_pkg
721
+ when :send
722
+ package_creation=self.options.get_option(:value,:mandatory)
723
+ raise CliBadArgument,"value must be hash, refer to doc" unless package_creation.is_a?(Hash)
724
+
725
+ if !@url_token_data.nil?
726
+ assert_public_link_types(['send_package_to_user','send_package_to_dropbox'])
727
+ box_type=@url_token_data['purpose'].split('_').last
728
+ package_creation['recipients']=[{'id'=>@url_token_data['data']["#{box_type}_id"],'type'=>box_type}]
729
+ @workspace_id=@url_token_data['data']['workspace_id']
730
+ end
731
+
732
+ package_creation['workspace_id']=@workspace_id
733
+
734
+ # list of files to include in package, optional
735
+ #package_creation['file_names']=self.transfer.ts_source_paths.map{|i|File.basename(i['source'])}
736
+
737
+ # lookup users
738
+ resolve_package_recipients(package_creation,'recipients')
739
+ resolve_package_recipients(package_creation,'bcc_recipients')
740
+
741
+ # create a new package with one file
742
+ package_info=@api_aoc.create('packages',package_creation)[:data]
743
+
744
+ # get node information for the node on which package must be created
745
+ node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
746
+
747
+ # tell Aspera what to expect in package: 1 transfer (can also be done after transfer)
748
+ @api_aoc.update("packages/#{package_info['id']}",{'sent'=>true,'transfers_expected'=>1})[:data]
749
+
750
+ # execute transfer
751
+ node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
752
+ # raise esception if at least one error
753
+ Main.result_transfer(transfer_start(OnCloud::PACKAGES_APP,'send',node_file,OnCloud.package_tags(package_info,'upload')))
754
+ # return all info on package
755
+ return { :type=>:single_object, :data =>package_info}
756
+ when :recv
757
+ if !@url_token_data.nil?
758
+ assert_public_link_types(['view_received_package'])
759
+ self.options.set_option(:id,@url_token_data['data']['package_id'])
760
+ end
761
+ # scalar here
762
+ ids_to_download=self.options.get_option(:id,:mandatory)
763
+ skip_ids_data=[]
764
+ skip_ids_persistency=nil
765
+ if self.options.get_option(:once_only,:mandatory)
766
+ skip_ids_persistency=PersistencyActionOnce.new(
767
+ manager: @agents[:persistency],
768
+ data: skip_ids_data,
769
+ ids: ['aoc_recv',self.options.get_option(:url,:mandatory),@workspace_id].push(*@persist_ids))
770
+ end
771
+ if ids_to_download.eql?(VAL_ALL)
772
+ # get list of packages in inbox
773
+ package_info=@api_aoc.read('packages',{'archived'=>false,'exclude_dropbox_packages'=>true,'has_content'=>true,'received'=>true,'workspace_id'=>@workspace_id})[:data]
774
+ # remove from list the ones already downloaded
775
+ ids_to_download=package_info.map{|e|e['id']}
776
+ # array here
777
+ ids_to_download.select!{|id|!skip_ids_data.include?(id)}
778
+ end # ALL
779
+ # list here
780
+ ids_to_download = [ids_to_download] unless ids_to_download.is_a?(Array)
781
+ result_transfer=[]
782
+ self.format.display_status("found #{ids_to_download.length} package(s).")
783
+ ids_to_download.each do |package_id|
784
+ package_info=@api_aoc.read("packages/#{package_id}")[:data]
785
+ node_info=@api_aoc.read("nodes/#{package_info['node_id']}")[:data]
786
+ self.format.display_status("downloading package: #{package_info['name']}")
787
+ add_ts={'paths'=>[{'source'=>'.'}]}
788
+ node_file = {node_info: node_info, file_id: package_info['contents_file_id']}
789
+ statuses=transfer_start(OnCloud::PACKAGES_APP,'receive',node_file,OnCloud.package_tags(package_info,'download').merge(add_ts))
790
+ result_transfer.push({'package'=>package_id,'status'=>statuses.map{|i|i.to_s}.join(',')})
791
+ # update skip list only if all transfer sessions completed
792
+ if TransferAgent.session_status(statuses).eql?(:success)
793
+ skip_ids_data.push(package_id)
794
+ skip_ids_persistency.save unless skip_ids_persistency.nil?
795
+ end
796
+ end
797
+ return {:type=>:object_list,:data=>result_transfer}
798
+ when :show
799
+ package_id=self.options.get_next_argument('package ID')
800
+ package_info=@api_aoc.read("packages/#{package_id}")[:data]
801
+ return { :type=>:single_object, :data =>package_info }
802
+ when :list
803
+ # 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]
805
+ return {:type=>:object_list,:data=>packages,:fields=>['id','name','bytes_transferred']}
806
+ when :delete
807
+ list_or_one=self.options.get_option(:id,:mandatory)
808
+ 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)
810
+ @api_aoc.delete("packages/#{id}")[:data]
811
+ end
812
+ end
813
+ when :files
814
+ # get workspace related information
815
+ set_workspace_info
816
+ set_home_node_file
817
+ find_ak_secret(@home_node_file[:node_info]['access_key'],false)
818
+ command_repo=self.options.get_next_command(NODE4_COMMANDS.clone.concat([:short_link]))
819
+ case command_repo
820
+ when *NODE4_COMMANDS; return execute_node_gen4_command(command_repo,@home_node_file)
821
+ when :short_link
822
+ folder_dest=self.options.get_option(:to_folder,:optional)
823
+ value_option=self.options.get_option(:value,:optional)
824
+ case value_option
825
+ when 'public'
826
+ value_option={'purpose'=>'token_auth_redirection'}
827
+ when 'private'
828
+ value_option={'purpose'=>'shared_folder_auth_link'}
829
+ when NilClass,Hash
830
+ else raise "value must be either: public, private, Hash or nil"
831
+ end
832
+ create_params=nil
833
+ node_file=nil
834
+ if !folder_dest.nil?
835
+ node_file = @api_aoc.resolve_node_file(@home_node_file,folder_dest)
836
+ create_params={
837
+ file_id: node_file[:file_id],
838
+ node_id: node_file[:node_info]['id'],
839
+ workspace_id: @workspace_id
840
+ }
841
+ end
842
+ if !value_option.nil? and !create_params.nil?
843
+ case value_option['purpose']
844
+ when 'shared_folder_auth_link'
845
+ value_option['data']=create_params
846
+ value_option['user_selected_name']=nil
847
+ when 'token_auth_redirection'
848
+ create_params['name']=''
849
+ value_option['data']={
850
+ aoc: true,
851
+ url_token_data: {
852
+ data: create_params,
853
+ purpose: 'view_shared_file'
854
+ }
855
+ }
856
+ value_option['user_selected_name']=nil
857
+ else
858
+ raise "purpose must be one of: token_auth_redirection or shared_folder_auth_link"
859
+ end
860
+ self.options.set_option(:value,value_option)
861
+ end
862
+ result=self.entity_action(@api_aoc,'short_links',nil,:id,'self')
863
+ if result[:data].is_a?(Hash) and result[:data].has_key?('created_at') and result[:data]['resource_type'].eql?('UrlToken')
864
+ node_api=@api_aoc.get_node_api(node_file[:node_info],OnCloud::SCOPE_NODE_USER)
865
+ 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']
879
+ }
880
+ }
881
+ node_api.create("permissions?file_id=#{node_file[:file_id]}",perm_data)
882
+ end
883
+ return result
884
+ end # files command
885
+ throw "Error: shall not reach this line"
886
+ when :automation
887
+ Log.log.warn("BETA: work under progress")
888
+ # automation api is not in the same place
889
+ automation_rest_params=@api_aoc.params.clone
890
+ automation_rest_params[:base_url].gsub!('/api/','/automation/')
891
+ automation_api=Rest.new(automation_rest_params)
892
+ command_automation=self.options.get_next_command([ :workflows, :instances ])
893
+ case command_automation
894
+ when :instances
895
+ return self.entity_action(@api_aoc,'workflow_instances',nil,:id,nil)
896
+ when :workflows
897
+ wF_COMMANDS=Plugin::ALL_OPS.clone.push(:action,:launch)
898
+ wf_command=self.options.get_next_command(wF_COMMANDS)
899
+ case wf_command
900
+ when *Plugin::ALL_OPS
901
+ return self.entity_command(wf_command,automation_api,'workflows',nil,:id)
902
+ when :launch
903
+ wf_id=self.options.get_option(:id,:mandatory)
904
+ data=automation_api.create("workflows/#{wf_id}/launch",{})[:data]
905
+ return {:type=>:single_object,:data=>data}
906
+ when :action
907
+ wf_command=self.options.get_next_command([:list,:create,:show])
908
+ wf_id=self.options.get_option(:id,:mandatory)
909
+ step=automation_api.create('steps',{'workflow_id'=>wf_id})[:data]
910
+ automation_api.update("workflows/#{wf_id}",{'step_order'=>[step["id"]]})
911
+ action=automation_api.create('actions',{'step_id'=>step["id"],'type'=>'manual'})[:data]
912
+ automation_api.update("steps/#{step["id"]}",{'action_order'=>[action["id"]]})
913
+ wf=automation_api.read("workflows/#{wf_id}")[:data]
914
+ return {:type=>:single_object,:data=>wf}
915
+ end
916
+ end
917
+ when :gateway
918
+ set_workspace_info
919
+ require 'aspera/faspex_gw'
920
+ FaspexGW.new(@api_aoc,@workspace_id).start_server
921
+ when :admin
922
+ return execute_admin_action
923
+ 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}
929
+ else
930
+ raise "internal error: #{command}"
931
+ end # action
932
+ raise RuntimeError, "internal error: command shall return"
933
+ end
934
+ end # Aspera
935
+ end # Plugins
936
+ end # Cli
937
+ end # Aspera