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