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