Merge branch '15877-accept-json-in-json'
[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   end
43
44   def activity
45     @breadcrumb_page_name = nil
46     @users = User.limit(params[:limit]).with_count("none")
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]]).with_count("none")
68       @activity[:jobs][span] = Job.select(%w(uuid modified_by_user_uuid)).
69         filter([[:created_at, '>=', threshold_start],
70                 [:created_at, '<', threshold_end]]).with_count("none")
71       @activity[:pipeline_instances][span] = PipelineInstance.select(%w(uuid modified_by_user_uuid)).
72         filter([[:created_at, '>=', threshold_start],
73                 [:created_at, '<', threshold_end]]).with_count("none")
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]).with_count("none")
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       %w(Admin) | super
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: "v2/#{resp[:uuid]}/#{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       with_count('none').
158       where(created_by: current_user.uuid)
159
160     @my_collections = Collection.
161       limit(10).
162       order('created_at desc').
163       with_count('none').
164       where(created_by: current_user.uuid)
165     collection_uuids = @my_collections.collect &:uuid
166
167     @persist_state = {}
168     collection_uuids.each do |uuid|
169       @persist_state[uuid] = 'cache'
170     end
171
172     Link.filter([['head_uuid', 'in', collection_uuids],
173                              ['link_class', 'in', ['tag', 'resources']]]).with_count("none")
174       each do |link|
175       case link.link_class
176       when 'tag'
177         (@my_tag_links[link.head_uuid] ||= []) << link
178       when 'resources'
179         if link.name == 'wants'
180           @persist_state[link.head_uuid] = 'persistent'
181         end
182       end
183     end
184
185     @my_pipelines = PipelineInstance.
186       limit(10).
187       order('created_at desc').
188       with_count('none').
189       where(created_by: current_user.uuid)
190
191     respond_to do |f|
192       f.js { render template: 'users/home.js' }
193       f.html { render template: 'users/home' }
194     end
195   end
196
197   def unsetup
198     if current_user.andand.is_admin
199       @object.unsetup
200     end
201     show
202   end
203
204   def setup
205     respond_to do |format|
206       if current_user.andand.is_admin
207         setup_params = {}
208         setup_params[:send_notification_email] = "#{Rails.configuration.Mail.SendUserSetupNotificationEmail}"
209         if params['user_uuid'] && params['user_uuid'].size>0
210           setup_params[:uuid] = params['user_uuid']
211         end
212         if params['email'] && params['email'].size>0
213           user = {email: params['email']}
214           setup_params[:user] = user
215         end
216         if params['openid_prefix'] && params['openid_prefix'].size>0
217           setup_params[:openid_prefix] = params['openid_prefix']
218         end
219         if params['vm_uuid'] && params['vm_uuid'].size>0
220           setup_params[:vm_uuid] = params['vm_uuid']
221         end
222
223         setup_resp = User.setup setup_params
224         if setup_resp
225           vm_link = nil
226           setup_resp[:items].each do |item|
227             if item[:head_kind] == "arvados#virtualMachine"
228               vm_link = item
229               break
230             end
231           end
232           if params[:groups]
233             new_groups = params[:groups].split(',').map(&:strip).select{|i| !i.empty?}
234             if vm_link and new_groups != vm_link[:properties][:groups]
235               vm_login_link = Link.where(uuid: vm_link[:uuid])
236               if vm_login_link.items_available > 0
237                 link = vm_login_link.results.first
238                 props = link.properties
239                 props[:groups] = new_groups
240                 link.save!
241               end
242             end
243           end
244
245           format.js
246         else
247           self.render_error status: 422
248         end
249       else
250         self.render_error status: 422
251       end
252     end
253   end
254
255   def setup_popup
256     @vms = VirtualMachine.all.results
257
258     @current_selections = find_current_links @object
259
260     respond_to do |format|
261       format.html
262       format.js
263     end
264   end
265
266   def virtual_machines
267     @my_vm_logins = {}
268     Link.where(tail_uuid: @object.uuid,
269                link_class: 'permission',
270                name: 'can_login').with_count("none").
271           each do |perm_link|
272             if perm_link.properties.andand[:username]
273               @my_vm_logins[perm_link.head_uuid] ||= []
274               @my_vm_logins[perm_link.head_uuid] << perm_link.properties[:username]
275             end
276           end
277     @my_virtual_machines = VirtualMachine.where(uuid: @my_vm_logins.keys).with_count("none")
278   end
279
280   def ssh_keys
281     @my_ssh_keys = AuthorizedKey.where(key_type: 'SSH', owner_uuid: @object.uuid)
282   end
283
284   def add_ssh_key_popup
285     respond_to do |format|
286       format.html
287       format.js
288     end
289   end
290
291   def add_ssh_key
292     respond_to do |format|
293       key_params = {'key_type' => 'SSH'}
294       key_params['authorized_user_uuid'] = current_user.uuid
295
296       if params['name'] && params['name'].size>0
297         key_params['name'] = params['name'].strip
298       end
299       if params['public_key'] && params['public_key'].size>0
300         key_params['public_key'] = params['public_key'].strip
301       end
302
303       if !key_params['name'] && params['public_key'].andand.size>0
304         split_key = key_params['public_key'].split
305         key_params['name'] = split_key[-1] if (split_key.size == 3)
306       end
307
308       new_key = AuthorizedKey.create! key_params
309       if new_key
310         format.js
311       else
312         self.render_error status: 422
313       end
314     end
315   end
316
317   def request_shell_access
318     logger.warn "request_access: #{params.inspect}"
319     params['request_url'] = request.url
320     RequestShellAccessReporter.send_request(current_user, params).deliver
321   end
322
323   def merge
324     User.merge params[:new_user_token], params[:direction]
325     redirect_to "/"
326   end
327
328   protected
329
330   def find_current_links user
331     current_selections = {}
332
333     if !user
334       return current_selections
335     end
336
337     # oid login perm
338     oid_login_perms = Link.where(tail_uuid: user.email,
339                                    head_kind: 'arvados#user',
340                                    link_class: 'permission',
341                                    name: 'can_login').with_count("none")
342
343     if oid_login_perms.any?
344       prefix_properties = oid_login_perms.first.properties
345       current_selections[:identity_url_prefix] = prefix_properties[:identity_url_prefix]
346     end
347
348     # repo perm
349     repo_perms = Link.where(tail_uuid: user.uuid,
350                             head_kind: 'arvados#repository',
351                             link_class: 'permission',
352                             name: 'can_write').with_count("none")
353     if repo_perms.any?
354       repo_uuid = repo_perms.first.head_uuid
355       repos = Repository.where(head_uuid: repo_uuid).with_count("none")
356       if repos.any?
357         repo_name = repos.first.name
358         current_selections[:repo_name] = repo_name
359       end
360     end
361
362     # vm login perm
363     vm_login_perms = Link.where(tail_uuid: user.uuid,
364                               head_kind: 'arvados#virtualMachine',
365                               link_class: 'permission',
366                               name: 'can_login').with_count("none")
367     if vm_login_perms.any?
368       vm_perm = vm_login_perms.first
369       vm_uuid = vm_perm.head_uuid
370       current_selections[:vm_uuid] = vm_uuid
371       current_selections[:groups] = vm_perm.properties[:groups].andand.join(', ')
372     end
373
374     return current_selections
375   end
376
377 end