Merge branch '13338-wb-run-wf-proj' refs #13338
[arvados.git] / apps / workbench / app / controllers / users_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class UsersController < ApplicationController
6   skip_around_filter :require_thread_api_token, only: :welcome
7   skip_before_filter :check_user_agreements, only: [:welcome, :inactive]
8   skip_before_filter :check_user_profile, only: [:welcome, :inactive, :profile]
9   skip_before_filter :find_object_by_uuid, only: [:welcome, :activity, :storage]
10   before_filter :ensure_current_user_is_admin, only: [:sudo, :unsetup, :setup]
11
12   def show
13     if params[:uuid] == current_user.uuid
14       respond_to do |f|
15         f.html do
16           if request.url.include?("/users/#{current_user.uuid}")
17             super
18           else
19             redirect_to(params[:return_to] || project_path(params[:uuid]))
20           end
21         end
22       end
23     else
24       super
25     end
26   end
27
28   def welcome
29     if current_user
30       redirect_to (params[:return_to] || '/')
31     end
32   end
33
34   def inactive
35     if current_user.andand.is_invited
36       redirect_to (params[:return_to] || '/')
37     end
38   end
39
40   def profile
41     params[:offer_return_to] ||= params[:return_to]
42   end
43
44   def activity
45     @breadcrumb_page_name = nil
46     @users = User.limit(params[:limit])
47     @user_activity = {}
48     @activity = {
49       logins: {},
50       jobs: {},
51       pipeline_instances: {}
52     }
53     @total_activity = {}
54     @spans = [['This week', Time.now.beginning_of_week, Time.now],
55               ['Last week',
56                Time.now.beginning_of_week.advance(weeks:-1),
57                Time.now.beginning_of_week],
58               ['This month', Time.now.beginning_of_month, Time.now],
59               ['Last month',
60                1.month.ago.beginning_of_month,
61                Time.now.beginning_of_month]]
62     @spans.each do |span, threshold_start, threshold_end|
63       @activity[:logins][span] = Log.select(%w(uuid modified_by_user_uuid)).
64         filter([[:event_type, '=', 'login'],
65                 [:object_kind, '=', 'arvados#user'],
66                 [:created_at, '>=', threshold_start],
67                 [:created_at, '<', threshold_end]])
68       @activity[:jobs][span] = Job.select(%w(uuid modified_by_user_uuid)).
69         filter([[:created_at, '>=', threshold_start],
70                 [:created_at, '<', threshold_end]])
71       @activity[:pipeline_instances][span] = PipelineInstance.select(%w(uuid modified_by_user_uuid)).
72         filter([[:created_at, '>=', threshold_start],
73                 [:created_at, '<', threshold_end]])
74       @activity.each do |type, act|
75         records = act[span]
76         @users.each do |u|
77           @user_activity[u.uuid] ||= {}
78           @user_activity[u.uuid][span + ' ' + type.to_s] ||= 0
79         end
80         records.each do |record|
81           @user_activity[record.modified_by_user_uuid] ||= {}
82           @user_activity[record.modified_by_user_uuid][span + ' ' + type.to_s] ||= 0
83           @user_activity[record.modified_by_user_uuid][span + ' ' + type.to_s] += 1
84           @total_activity[span + ' ' + type.to_s] ||= 0
85           @total_activity[span + ' ' + type.to_s] += 1
86         end
87       end
88     end
89     @users = @users.sort_by do |a|
90       [-@user_activity[a.uuid].values.inject(:+), a.full_name]
91     end
92     # Prepend a "Total" pseudo-user to the sorted list
93     @user_activity[nil] = @total_activity
94     @users = [OpenStruct.new(uuid: nil)] + @users
95   end
96
97   def storage
98     @breadcrumb_page_name = nil
99     @users = User.limit(params[:limit])
100     @user_storage = {}
101     total_storage = {}
102     @log_date = {}
103     @users.each do |u|
104       @user_storage[u.uuid] ||= {}
105       storage_log = Log.
106         filter([[:object_uuid, '=', u.uuid],
107                 [:event_type, '=', 'user-storage-report']]).
108         order(:created_at => :desc).
109         with_count('none').
110         limit(1)
111       storage_log.each do |log_entry|
112         # We expect this block to only execute once since we specified limit(1)
113         @user_storage[u.uuid] = log_entry['properties']
114         @log_date[u.uuid] = log_entry['event_at']
115       end
116       total_storage.merge!(@user_storage[u.uuid]) { |k,v1,v2| v1 + v2 }
117     end
118     @users = @users.sort_by { |u|
119       [-@user_storage[u.uuid].values.push(0).inject(:+), u.full_name]}
120     # Prepend a "Total" pseudo-user to the sorted list
121     @users = [OpenStruct.new(uuid: nil)] + @users
122     @user_storage[nil] = total_storage
123   end
124
125   def show_pane_list
126     if current_user.andand.is_admin
127       super | %w(Admin)
128     else
129       super
130     end
131   end
132
133   def index_pane_list
134     if current_user.andand.is_admin
135       super | %w(Activity)
136     else
137       super
138     end
139   end
140
141   def sudo
142     resp = arvados_api_client.api(ApiClientAuthorization, '', {
143                                     api_client_authorization: {
144                                       owner_uuid: @object.uuid
145                                     }
146                                   })
147     redirect_to root_url(api_token: resp[:api_token])
148   end
149
150   def home
151     @my_ssh_keys = AuthorizedKey.where(authorized_user_uuid: current_user.uuid)
152     @my_tag_links = {}
153
154     @my_jobs = Job.
155       limit(10).
156       order('created_at desc').
157       where(created_by: current_user.uuid)
158
159     @my_collections = Collection.
160       limit(10).
161       order('created_at desc').
162       where(created_by: current_user.uuid)
163     collection_uuids = @my_collections.collect &:uuid
164
165     @persist_state = {}
166     collection_uuids.each do |uuid|
167       @persist_state[uuid] = 'cache'
168     end
169
170     Link.filter([['head_uuid', 'in', collection_uuids],
171                              ['link_class', 'in', ['tag', 'resources']]]).
172       each do |link|
173       case link.link_class
174       when 'tag'
175         (@my_tag_links[link.head_uuid] ||= []) << link
176       when 'resources'
177         if link.name == 'wants'
178           @persist_state[link.head_uuid] = 'persistent'
179         end
180       end
181     end
182
183     @my_pipelines = PipelineInstance.
184       limit(10).
185       order('created_at desc').
186       where(created_by: current_user.uuid)
187
188     respond_to do |f|
189       f.js { render template: 'users/home.js' }
190       f.html { render template: 'users/home' }
191     end
192   end
193
194   def unsetup
195     if current_user.andand.is_admin
196       @object.unsetup
197     end
198     show
199   end
200
201   def setup
202     respond_to do |format|
203       if current_user.andand.is_admin
204         setup_params = {}
205         setup_params[:send_notification_email] = "#{Rails.configuration.send_user_setup_notification_email}"
206         if params['user_uuid'] && params['user_uuid'].size>0
207           setup_params[:uuid] = params['user_uuid']
208         end
209         if params['email'] && params['email'].size>0
210           user = {email: params['email']}
211           setup_params[:user] = user
212         end
213         if params['openid_prefix'] && params['openid_prefix'].size>0
214           setup_params[:openid_prefix] = params['openid_prefix']
215         end
216         if params['vm_uuid'] && params['vm_uuid'].size>0
217           setup_params[:vm_uuid] = params['vm_uuid']
218         end
219
220         setup_resp = User.setup setup_params
221         if setup_resp
222           vm_link = nil
223           setup_resp[:items].each do |item|
224             if item[:head_kind] == "arvados#virtualMachine"
225               vm_link = item
226               break
227             end
228           end
229           if params[:groups]
230             new_groups = params[:groups].split(',').map(&:strip).select{|i| !i.empty?}
231             if vm_link and new_groups != vm_link[:properties][:groups]
232               vm_login_link = Link.where(uuid: vm_link[:uuid])
233               if vm_login_link.items_available > 0
234                 link = vm_login_link.results.first
235                 props = link.properties
236                 props[:groups] = new_groups
237                 link.save!
238               end
239             end
240           end
241
242           format.js
243         else
244           self.render_error status: 422
245         end
246       else
247         self.render_error status: 422
248       end
249     end
250   end
251
252   def setup_popup
253     @vms = VirtualMachine.all.results
254
255     @current_selections = find_current_links @object
256
257     respond_to do |format|
258       format.html
259       format.js
260     end
261   end
262
263   def virtual_machines
264     @my_vm_logins = {}
265     Link.where(tail_uuid: @object.uuid,
266                link_class: 'permission',
267                name: 'can_login').
268           each do |perm_link|
269             if perm_link.properties.andand[:username]
270               @my_vm_logins[perm_link.head_uuid] ||= []
271               @my_vm_logins[perm_link.head_uuid] << perm_link.properties[:username]
272             end
273           end
274     @my_virtual_machines = VirtualMachine.where(uuid: @my_vm_logins.keys)
275   end
276
277   def ssh_keys
278     @my_ssh_keys = AuthorizedKey.where(key_type: 'SSH', owner_uuid: @object.uuid)
279   end
280
281   def add_ssh_key_popup
282     respond_to do |format|
283       format.html
284       format.js
285     end
286   end
287
288   def add_ssh_key
289     respond_to do |format|
290       key_params = {'key_type' => 'SSH'}
291       key_params['authorized_user_uuid'] = current_user.uuid
292
293       if params['name'] && params['name'].size>0
294         key_params['name'] = params['name'].strip
295       end
296       if params['public_key'] && params['public_key'].size>0
297         key_params['public_key'] = params['public_key'].strip
298       end
299
300       if !key_params['name'] && params['public_key'].andand.size>0
301         split_key = key_params['public_key'].split
302         key_params['name'] = split_key[-1] if (split_key.size == 3)
303       end
304
305       new_key = AuthorizedKey.create! key_params
306       if new_key
307         format.js
308       else
309         self.render_error status: 422
310       end
311     end
312   end
313
314   def request_shell_access
315     logger.warn "request_access: #{params.inspect}"
316     params['request_url'] = request.url
317     RequestShellAccessReporter.send_request(current_user, params).deliver
318   end
319
320   protected
321
322   def find_current_links user
323     current_selections = {}
324
325     if !user
326       return current_selections
327     end
328
329     # oid login perm
330     oid_login_perms = Link.where(tail_uuid: user.email,
331                                    head_kind: 'arvados#user',
332                                    link_class: 'permission',
333                                    name: 'can_login')
334
335     if oid_login_perms.any?
336       prefix_properties = oid_login_perms.first.properties
337       current_selections[:identity_url_prefix] = prefix_properties[:identity_url_prefix]
338     end
339
340     # repo perm
341     repo_perms = Link.where(tail_uuid: user.uuid,
342                             head_kind: 'arvados#repository',
343                             link_class: 'permission',
344                             name: 'can_write')
345     if repo_perms.any?
346       repo_uuid = repo_perms.first.head_uuid
347       repos = Repository.where(head_uuid: repo_uuid)
348       if repos.any?
349         repo_name = repos.first.name
350         current_selections[:repo_name] = repo_name
351       end
352     end
353
354     # vm login perm
355     vm_login_perms = Link.where(tail_uuid: user.uuid,
356                               head_kind: 'arvados#virtualMachine',
357                               link_class: 'permission',
358                               name: 'can_login')
359     if vm_login_perms.any?
360       vm_perm = vm_login_perms.first
361       vm_uuid = vm_perm.head_uuid
362       current_selections[:vm_uuid] = vm_uuid
363       current_selections[:groups] = vm_perm.properties[:groups].andand.join(', ')
364     end
365
366     return current_selections
367   end
368
369 end