Merge branch '16265-security-updates' into dependabot/bundler/apps/workbench/loofah...
[arvados.git] / services / api / test / functional / arvados / v1 / users_controller_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6 require 'helpers/users_test_helper'
7
8 class Arvados::V1::UsersControllerTest < ActionController::TestCase
9   include CurrentApiClient
10   include UsersTestHelper
11
12   setup do
13     @initial_link_count = Link.count
14     @vm_uuid = virtual_machines(:testvm).uuid
15     ActionMailer::Base.deliveries = []
16   end
17
18   test "activate a user after signing UA" do
19     authorize_with :inactive_but_signed_user_agreement
20     post :activate, params: {id: users(:inactive_but_signed_user_agreement).uuid}
21     assert_response :success
22     assert_not_nil assigns(:object)
23     me = JSON.parse(@response.body)
24     assert_equal true, me['is_active']
25   end
26
27   test "refuse to activate a user before signing UA" do
28     act_as_system_user do
29     required_uuids = Link.where("owner_uuid = ? and link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
30                                 system_user_uuid,
31                                 'signature',
32                                 'require',
33                                 system_user_uuid,
34                                 Collection.uuid_like_pattern).
35       collect(&:head_uuid)
36
37       assert required_uuids.length > 0
38
39       signed_uuids = Link.where(owner_uuid: system_user_uuid,
40                                 link_class: 'signature',
41                                 name: 'click',
42                                 tail_uuid: users(:inactive).uuid,
43                                 head_uuid: required_uuids).
44                           collect(&:head_uuid)
45
46       assert_equal 0, signed_uuids.length
47     end
48
49     authorize_with :inactive
50     assert_equal false, users(:inactive).is_active
51
52     post :activate, params: {id: users(:inactive).uuid}
53     assert_response 403
54
55     resp = json_response
56     assert resp['errors'].first.include? 'Cannot activate without user agreements'
57     assert_nil resp['is_active']
58   end
59
60   test "activate an already-active user" do
61     authorize_with :active
62     post :activate, params: {id: users(:active).uuid}
63     assert_response :success
64     me = JSON.parse(@response.body)
65     assert_equal true, me['is_active']
66   end
67
68   test "respond 401 if given token exists but user record is missing" do
69     authorize_with :valid_token_deleted_user
70     get :current, {format: :json}
71     assert_response 401
72   end
73
74   test "create new user with user as input" do
75     authorize_with :admin
76     post :create, params: {
77       user: {
78         first_name: "test_first_name",
79         last_name: "test_last_name",
80         email: "foo@example.com"
81       }
82     }
83     assert_response :success
84     created = JSON.parse(@response.body)
85     assert_equal 'test_first_name', created['first_name']
86     assert_not_nil created['uuid'], 'expected uuid for the newly created user'
87     assert_not_nil created['email'], 'expected non-nil email'
88     assert_nil created['identity_url'], 'expected no identity_url'
89   end
90
91   test "create user with user, vm and repo as input" do
92     authorize_with :admin
93     repo_name = 'usertestrepo'
94
95     post :setup, params: {
96       repo_name: repo_name,
97       user: {
98         uuid: 'zzzzz-tpzed-abcdefghijklmno',
99         first_name: "in_create_test_first_name",
100         last_name: "test_last_name",
101         email: "foo@example.com"
102       }
103     }
104     assert_response :success
105     response_items = JSON.parse(@response.body)['items']
106
107     created = find_obj_in_resp response_items, 'User', nil
108
109     assert_equal 'in_create_test_first_name', created['first_name']
110     assert_not_nil created['uuid'], 'expected non-null uuid for the new user'
111     assert_equal 'zzzzz-tpzed-abcdefghijklmno', created['uuid']
112     assert_not_nil created['email'], 'expected non-nil email'
113     assert_nil created['identity_url'], 'expected no identity_url'
114
115     # repo link and link add user to 'All users' group
116     verify_links_added 3
117
118     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
119         "foo/#{repo_name}", created['uuid'], 'arvados#repository', true, 'Repository'
120
121     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
122         'All users', created['uuid'], 'arvados#group', true, 'Group'
123
124     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
125         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
126
127     verify_system_group_permission_link_for created['uuid']
128   end
129
130   test "setup user with bogus uuid and expect error" do
131     authorize_with :admin
132
133     post :setup, params: {
134       uuid: 'bogus_uuid',
135       repo_name: 'usertestrepo',
136       vm_uuid: @vm_uuid
137     }
138     response_body = JSON.parse(@response.body)
139     response_errors = response_body['errors']
140     assert_not_nil response_errors, 'Expected error in response'
141     assert (response_errors.first.include? 'Path not found'), 'Expected 404'
142   end
143
144   test "setup user with bogus uuid in user and expect error" do
145     authorize_with :admin
146
147     post :setup, params: {
148       user: {uuid: 'bogus_uuid'},
149       repo_name: 'usertestrepo',
150       vm_uuid: @vm_uuid,
151     }
152     response_body = JSON.parse(@response.body)
153     response_errors = response_body['errors']
154     assert_not_nil response_errors, 'Expected error in response'
155     assert (response_errors.first.include? 'ArgumentError: Require user email'),
156       'Expected RuntimeError'
157   end
158
159   test "setup user with no uuid and user, expect error" do
160     authorize_with :admin
161
162     post :setup, params: {
163       repo_name: 'usertestrepo',
164       vm_uuid: @vm_uuid,
165     }
166     response_body = JSON.parse(@response.body)
167     response_errors = response_body['errors']
168     assert_not_nil response_errors, 'Expected error in response'
169     assert (response_errors.first.include? 'Required uuid or user'),
170         'Expected ArgumentError'
171   end
172
173   test "setup user with no uuid and email, expect error" do
174     authorize_with :admin
175
176     post :setup, params: {
177       user: {},
178       repo_name: 'usertestrepo',
179       vm_uuid: @vm_uuid,
180     }
181     response_body = JSON.parse(@response.body)
182     response_errors = response_body['errors']
183     assert_not_nil response_errors, 'Expected error in response'
184     assert (response_errors.first.include? '<ArgumentError: Require user email'),
185         'Expected ArgumentError'
186   end
187
188   test "invoke setup with existing uuid, vm and repo and verify links" do
189     authorize_with :admin
190     inactive_user = users(:inactive)
191
192     post :setup, params: {
193       uuid: users(:inactive).uuid,
194       repo_name: 'usertestrepo',
195       vm_uuid: @vm_uuid
196     }
197
198     assert_response :success
199
200     response_items = JSON.parse(@response.body)['items']
201     resp_obj = find_obj_in_resp response_items, 'User', nil
202
203     assert_not_nil resp_obj['uuid'], 'expected uuid for the new user'
204     assert_equal inactive_user['uuid'], resp_obj['uuid']
205     assert_equal inactive_user['email'], resp_obj['email'],
206         'expecting inactive user email'
207
208     # expect repo and vm links
209     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
210         'inactiveuser/usertestrepo', resp_obj['uuid'], 'arvados#repository', true, 'Repository'
211
212     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
213         @vm_uuid, resp_obj['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
214   end
215
216   test "invoke setup with existing uuid but different email, expect original email" do
217     authorize_with :admin
218     inactive_user = users(:inactive)
219
220     post :setup, params: {
221       uuid: inactive_user['uuid'],
222       user: {email: 'junk_email'}
223     }
224
225     assert_response :success
226
227     response_items = JSON.parse(@response.body)['items']
228     resp_obj = find_obj_in_resp response_items, 'User', nil
229
230     assert_not_nil resp_obj['uuid'], 'expected uuid for the new user'
231     assert_equal inactive_user['uuid'], resp_obj['uuid']
232     assert_equal inactive_user['email'], resp_obj['email'],
233         'expecting inactive user email'
234   end
235
236   test "setup user with valid email and repo as input" do
237     authorize_with :admin
238
239     post :setup, params: {
240       repo_name: 'usertestrepo',
241       user: {email: 'foo@example.com'},
242     }
243
244     assert_response :success
245     response_items = JSON.parse(@response.body)['items']
246     response_object = find_obj_in_resp response_items, 'User', nil
247     assert_not_nil response_object['uuid'], 'expected uuid for the new user'
248     assert_equal response_object['email'], 'foo@example.com', 'expected given email'
249
250     # three extra links; system_group, group and repo perms
251     verify_links_added 3
252   end
253
254   test "setup user with fake vm and expect error" do
255     authorize_with :admin
256
257     post :setup, params: {
258       repo_name: 'usertestrepo',
259       vm_uuid: 'no_such_vm',
260       user: {email: 'foo@example.com'},
261     }
262
263     response_body = JSON.parse(@response.body)
264     response_errors = response_body['errors']
265     assert_not_nil response_errors, 'Expected error in response'
266     assert (response_errors.first.include? "No vm found for no_such_vm"),
267           'Expected RuntimeError: No vm found for no_such_vm'
268   end
269
270   test "setup user with valid email, repo and real vm as input" do
271     authorize_with :admin
272
273     post :setup, params: {
274       repo_name: 'usertestrepo',
275       vm_uuid: @vm_uuid,
276       user: {email: 'foo@example.com'}
277     }
278
279     assert_response :success
280     response_items = JSON.parse(@response.body)['items']
281     response_object = find_obj_in_resp response_items, 'User', nil
282     assert_not_nil response_object['uuid'], 'expected uuid for the new user'
283     assert_equal response_object['email'], 'foo@example.com', 'expected given email'
284
285     # four extra links; system_group, group, vm, repo
286     verify_links_added 4
287   end
288
289   test "setup user with valid email, no vm and no repo as input" do
290     authorize_with :admin
291
292     post :setup, params: {
293       user: {email: 'foo@example.com'},
294     }
295
296     assert_response :success
297     response_items = JSON.parse(@response.body)['items']
298     response_object = find_obj_in_resp response_items, 'User', nil
299     assert_not_nil response_object['uuid'], 'expected uuid for new user'
300     assert_equal response_object['email'], 'foo@example.com', 'expected given email'
301
302     # two extra links; system_group, and group
303     verify_links_added 2
304
305     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
306         'All users', response_object['uuid'], 'arvados#group', true, 'Group'
307
308     verify_link response_items, 'arvados#repository', false, 'permission', 'can_manage',
309         'foo/usertestrepo', response_object['uuid'], 'arvados#repository', true, 'Repository'
310
311     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
312         nil, response_object['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
313   end
314
315   test "setup user with email, first name, repo name and vm uuid" do
316     authorize_with :admin
317
318     post :setup, params: {
319       repo_name: 'usertestrepo',
320       vm_uuid: @vm_uuid,
321       user: {
322         first_name: 'test_first_name',
323         email: 'foo@example.com'
324       }
325     }
326
327     assert_response :success
328     response_items = JSON.parse(@response.body)['items']
329     response_object = find_obj_in_resp response_items, 'User', nil
330     assert_not_nil response_object['uuid'], 'expected uuid for new user'
331     assert_equal response_object['email'], 'foo@example.com', 'expected given email'
332     assert_equal 'test_first_name', response_object['first_name'],
333         'expecting first name'
334
335     # four extra links; system_group, group, repo and vm
336     verify_links_added 4
337   end
338
339   test "setup user with an existing user email and check different object is created" do
340     authorize_with :admin
341     inactive_user = users(:inactive)
342
343     post :setup, params: {
344       repo_name: 'usertestrepo',
345       user: {
346         email: inactive_user['email']
347       }
348     }
349
350     assert_response :success
351     response_items = JSON.parse(@response.body)['items']
352     response_object = find_obj_in_resp response_items, 'User', nil
353     assert_not_nil response_object['uuid'], 'expected uuid for new user'
354     assert_not_equal response_object['uuid'], inactive_user['uuid'],
355         'expected different uuid after create operation'
356     assert_equal inactive_user['email'], response_object['email'], 'expected given email'
357     # system_group, group, and repo. No vm link.
358     verify_links_added 3
359   end
360
361   test "setup user with openid prefix" do
362     authorize_with :admin
363
364     post :setup, params: {
365       repo_name: 'usertestrepo',
366       user: {
367         first_name: "in_create_test_first_name",
368         last_name: "test_last_name",
369         email: "foo@example.com"
370       }
371     }
372
373     assert_response :success
374
375     response_items = JSON.parse(@response.body)['items']
376     created = find_obj_in_resp response_items, 'User', nil
377
378     assert_equal 'in_create_test_first_name', created['first_name']
379     assert_not_nil created['uuid'], 'expected uuid for new user'
380     assert_not_nil created['email'], 'expected non-nil email'
381     assert_nil created['identity_url'], 'expected no identity_url'
382
383     # verify links
384     # three new links: system_group, repo, and 'All users' group.
385     verify_links_added 3
386
387     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
388         'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
389
390     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
391         'All users', created['uuid'], 'arvados#group', true, 'Group'
392
393     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
394         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
395   end
396
397   test "setup user with user, vm and repo and verify links" do
398     authorize_with :admin
399
400     post :setup, params: {
401       user: {
402         first_name: "in_create_test_first_name",
403         last_name: "test_last_name",
404         email: "foo@example.com"
405       },
406       vm_uuid: @vm_uuid,
407       repo_name: 'usertestrepo',
408     }
409
410     assert_response :success
411
412     response_items = JSON.parse(@response.body)['items']
413     created = find_obj_in_resp response_items, 'User', nil
414
415     assert_equal 'in_create_test_first_name', created['first_name']
416     assert_not_nil created['uuid'], 'expected uuid for new user'
417     assert_not_nil created['email'], 'expected non-nil email'
418     assert_nil created['identity_url'], 'expected no identity_url'
419
420     # four new links: system_group, repo, vm and 'All users' group link
421     verify_links_added 4
422
423     # system_group isn't part of the response.  See User#add_system_group_permission_link
424
425     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
426         'foo/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
427
428     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
429         'All users', created['uuid'], 'arvados#group', true, 'Group'
430
431     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
432         @vm_uuid, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
433   end
434
435   test "create user as non admin user and expect error" do
436     authorize_with :active
437
438     post :create, params: {
439       user: {email: 'foo@example.com'}
440     }
441
442     response_body = JSON.parse(@response.body)
443     response_errors = response_body['errors']
444     assert_not_nil response_errors, 'Expected error in response'
445     assert (response_errors.first.include? 'PermissionDenied'),
446           'Expected PermissionDeniedError'
447   end
448
449   test "setup user as non admin user and expect error" do
450     authorize_with :active
451
452     post :setup, params: {
453       user: {email: 'foo@example.com'}
454     }
455
456     response_body = JSON.parse(@response.body)
457     response_errors = response_body['errors']
458     assert_not_nil response_errors, 'Expected error in response'
459     assert (response_errors.first.include? 'Forbidden'),
460           'Expected Forbidden error'
461   end
462
463   test "setup active user with repo and no vm" do
464     authorize_with :admin
465     active_user = users(:active)
466
467     # invoke setup with a repository
468     post :setup, params: {
469       repo_name: 'usertestrepo',
470       uuid: active_user['uuid']
471     }
472
473     assert_response :success
474
475     response_items = JSON.parse(@response.body)['items']
476     created = find_obj_in_resp response_items, 'User', nil
477
478     assert_equal active_user[:email], created['email'], 'expected input email'
479
480      # verify links
481     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
482         'All users', created['uuid'], 'arvados#group', true, 'Group'
483
484     verify_link response_items, 'arvados#repository', true, 'permission', 'can_manage',
485         'active/usertestrepo', created['uuid'], 'arvados#repository', true, 'Repository'
486
487     verify_link response_items, 'arvados#virtualMachine', false, 'permission', 'can_login',
488         nil, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
489   end
490
491   test "setup active user with vm and no repo" do
492     authorize_with :admin
493     active_user = users(:active)
494     repos_query = Repository.where(owner_uuid: active_user.uuid)
495     repo_link_query = Link.where(tail_uuid: active_user.uuid,
496                                  link_class: "permission", name: "can_manage")
497     repos_count = repos_query.count
498     repo_link_count = repo_link_query.count
499
500     # invoke setup with a repository
501     post :setup, params: {
502       vm_uuid: @vm_uuid,
503       uuid: active_user['uuid'],
504       email: 'junk_email'
505     }
506
507     assert_response :success
508
509     response_items = JSON.parse(@response.body)['items']
510     created = find_obj_in_resp response_items, 'User', nil
511
512     assert_equal active_user['email'], created['email'], 'expected original email'
513
514     # verify links
515     verify_link response_items, 'arvados#group', true, 'permission', 'can_read',
516         'All users', created['uuid'], 'arvados#group', true, 'Group'
517
518     assert_equal(repos_count, repos_query.count)
519     assert_equal(repo_link_count, repo_link_query.count)
520
521     verify_link response_items, 'arvados#virtualMachine', true, 'permission', 'can_login',
522         @vm_uuid, created['uuid'], 'arvados#virtualMachine', false, 'VirtualMachine'
523   end
524
525   test "unsetup active user" do
526     active_user = users(:active)
527     assert_not_nil active_user['uuid'], 'expected uuid for the active user'
528     assert active_user['is_active'], 'expected is_active for active user'
529
530     verify_link_existence active_user['uuid'], active_user['email'],
531           false, true, true, true, true
532
533     authorize_with :admin
534
535     # now unsetup this user
536     post :unsetup, params: {id: active_user['uuid']}
537     assert_response :success
538
539     response_user = JSON.parse(@response.body)
540     assert_not_nil response_user['uuid'], 'expected uuid for the upsetup user'
541     assert_equal active_user['uuid'], response_user['uuid'], 'expected uuid not found'
542     assert !response_user['is_active'], 'expected user to be inactive'
543     assert !response_user['is_invited'], 'expected user to be uninvited'
544
545     verify_link_existence response_user['uuid'], response_user['email'],
546           false, false, false, false, false
547
548     active_user = User.find_by_uuid(users(:active).uuid)
549     readable_groups = active_user.groups_i_can(:read)
550     all_users_group = Group.all.collect(&:uuid).select { |g| g.match(/-f+$/) }
551     refute_includes(readable_groups, all_users_group,
552                     "active user can read All Users group after being deactivated")
553     assert_equal(false, active_user.is_invited,
554                  "active user is_invited after being deactivated & reloaded")
555   end
556
557   test "setup user with send notification param false and verify no email" do
558     authorize_with :admin
559
560     post :setup, params: {
561       send_notification_email: 'false',
562       user: {
563         email: "foo@example.com"
564       }
565     }
566
567     assert_response :success
568     response_items = JSON.parse(@response.body)['items']
569     created = find_obj_in_resp response_items, 'User', nil
570     assert_not_nil created['uuid'], 'expected uuid for the new user'
571     assert_equal created['email'], 'foo@example.com', 'expected given email'
572
573     setup_email = ActionMailer::Base.deliveries.last
574     assert_nil setup_email, 'expected no setup email'
575   end
576
577   test "setup user with send notification param true and verify email" do
578     authorize_with :admin
579
580     post :setup, params: {
581       send_notification_email: 'true',
582       user: {
583         email: "foo@example.com"
584       }
585     }
586
587     assert_response :success
588     response_items = JSON.parse(@response.body)['items']
589     created = find_obj_in_resp response_items, 'User', nil
590     assert_not_nil created['uuid'], 'expected uuid for the new user'
591     assert_equal created['email'], 'foo@example.com', 'expected given email'
592
593     setup_email = ActionMailer::Base.deliveries.last
594     assert_not_nil setup_email, 'Expected email after setup'
595
596     assert_equal Rails.configuration.Users.UserNotifierEmailFrom, setup_email.from[0]
597     assert_equal 'foo@example.com', setup_email.to[0]
598     assert_equal 'Welcome to Arvados - account enabled', setup_email.subject
599     assert (setup_email.body.to_s.include? 'Your Arvados shell account has been set up'),
600         'Expected Your Arvados shell account has been set up in email body'
601     assert (setup_email.body.to_s.include? "#{Rails.configuration.Services.Workbench1.ExternalURL}users/#{created['uuid']}/virtual_machines"), 'Expected virtual machines url in email body'
602   end
603
604   test "setup inactive user by changing is_active to true" do
605     authorize_with :admin
606     active_user = users(:active)
607
608     # invoke setup with a repository
609     put :update, params: {
610           id: active_user['uuid'],
611           user: {
612             is_active: true,
613           }
614         }
615     assert_response :success
616     assert_equal active_user['uuid'], json_response['uuid']
617     updated = User.where(uuid: active_user['uuid']).first
618     assert_equal(true, updated.is_active)
619     assert_equal({read: true}, updated.group_permissions[all_users_group_uuid])
620   end
621
622   test "non-admin user can get basic information about readable users" do
623     authorize_with :spectator
624     get(:index)
625     check_non_admin_index
626     check_readable_users_index [:spectator], [:inactive, :active]
627   end
628
629   test "non-admin user gets only safe attributes from users#show" do
630     g = act_as_system_user do
631       create :group
632     end
633     users = create_list :active_user, 2, join_groups: [g]
634     token = create :token, user: users[0]
635     authorize_with_token token
636     get :show, params: {id: users[1].uuid}
637     check_non_admin_show
638   end
639
640   [2, 4].each do |limit|
641     test "non-admin user can limit index to #{limit}" do
642       g = act_as_system_user do
643         create :group
644       end
645       users = create_list :active_user, 4, join_groups: [g]
646       token = create :token, user: users[0]
647
648       authorize_with_token token
649       get(:index, params: {limit: limit})
650       check_non_admin_index
651       assert_equal(limit, json_response["items"].size,
652                    "non-admin index limit was ineffective")
653     end
654   end
655
656   test "admin has full index powers" do
657     authorize_with :admin
658     check_inactive_user_findable
659   end
660
661   test "reader token can grant admin index powers" do
662     authorize_with :spectator
663     check_inactive_user_findable(reader_tokens: [api_token(:admin)])
664   end
665
666   test "admin can filter on user.is_active" do
667     authorize_with :admin
668     get(:index, params: {filters: [["is_active", "=", "true"]]})
669     assert_response :success
670     check_readable_users_index [:active, :spectator], [:inactive]
671   end
672
673   test "admin can search where user.is_active" do
674     authorize_with :admin
675     get(:index, params: {where: {is_active: true}})
676     assert_response :success
677     check_readable_users_index [:active, :spectator], [:inactive]
678   end
679
680   test "update active_no_prefs user profile and expect notification email" do
681     authorize_with :admin
682
683     put :update, params: {
684       id: users(:active_no_prefs).uuid,
685       user: {
686         prefs: {:profile => {'organization' => 'example.com'}}
687       }
688     }
689     assert_response :success
690
691     found_email = false
692     ActionMailer::Base.deliveries.andand.each do |email|
693       if email.subject == "Profile created by #{users(:active_no_prefs).email}"
694         found_email = true
695         break
696       end
697     end
698     assert_equal true, found_email, 'Expected email after creating profile'
699   end
700
701   test "update active_no_prefs_profile user profile and expect notification email" do
702     authorize_with :admin
703
704     user = {}
705     user[:prefs] = users(:active_no_prefs_profile_no_getting_started_shown).prefs
706     user[:prefs][:profile] = {:profile => {'organization' => 'example.com'}}
707     put :update, params: {
708       id: users(:active_no_prefs_profile_no_getting_started_shown).uuid,
709       user: user
710     }
711     assert_response :success
712
713     found_email = false
714     ActionMailer::Base.deliveries.andand.each do |email|
715       if email.subject == "Profile created by #{users(:active_no_prefs_profile_no_getting_started_shown).email}"
716         found_email = true
717         break
718       end
719     end
720     assert_equal true, found_email, 'Expected email after creating profile'
721   end
722
723   test "update active user profile and expect no notification email" do
724     authorize_with :admin
725
726     put :update, params: {
727       id: users(:active).uuid,
728       user: {
729         prefs: {:profile => {'organization' => 'anotherexample.com'}}
730       }
731     }
732     assert_response :success
733
734     found_email = false
735     ActionMailer::Base.deliveries.andand.each do |email|
736       if email.subject == "Profile created by #{users(:active).email}"
737         found_email = true
738         break
739       end
740     end
741     assert_equal false, found_email, 'Expected no email after updating profile'
742   end
743
744   test "user API response includes writable_by" do
745     authorize_with :active
746     get :current
747     assert_response :success
748     assert_includes(json_response["writable_by"], users(:active).uuid,
749                     "user's writable_by should include self")
750     assert_includes(json_response["writable_by"], users(:active).owner_uuid,
751                     "user's writable_by should include its owner_uuid")
752   end
753
754   [
755     [:admin, true],
756     [:active, false],
757   ].each do |auth_user, expect_success|
758     test "update_uuid as #{auth_user}" do
759       authorize_with auth_user
760       orig_uuid = users(:active).uuid
761       post :update_uuid, params: {
762              id: orig_uuid,
763              new_uuid: 'zbbbb-tpzed-abcde12345abcde',
764            }
765       if expect_success
766         assert_response :success
767         assert_empty User.where(uuid: orig_uuid)
768       else
769         assert_response 403
770         assert_not_empty User.where(uuid: orig_uuid)
771       end
772     end
773   end
774
775   test "merge with redirect_to_user_uuid=false" do
776     authorize_with :project_viewer_trustedclient
777     tok = api_client_authorizations(:project_viewer).api_token
778     post :merge, params: {
779            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
780            new_owner_uuid: users(:active).uuid,
781            redirect_to_new_user: false,
782          }
783     assert_response(:success)
784     assert_nil(User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
785
786     # because redirect_to_new_user=false, token owned by
787     # project_viewer should be deleted
788     auth = ApiClientAuthorization.validate(token: tok)
789     assert_nil(auth)
790   end
791
792   test "merge remote to local as admin" do
793     authorize_with :admin
794
795     remoteuser = User.create!(uuid: "zbbbb-tpzed-remotremotremot")
796     tok = ApiClientAuthorization.create!(user: remoteuser, api_client: api_clients(:untrusted)).api_token
797
798     auth = ApiClientAuthorization.validate(token: tok)
799     assert_not_nil(auth)
800     assert_nil(remoteuser.redirect_to_user_uuid)
801
802     post :merge, params: {
803            new_user_uuid: users(:active).uuid,
804            old_user_uuid: remoteuser.uuid,
805            new_owner_uuid: users(:active).uuid,
806            redirect_to_new_user: true,
807          }
808     assert_response(:success)
809     remoteuser.reload
810     assert_equal(users(:active).uuid, remoteuser.redirect_to_user_uuid)
811
812     # token owned by remoteuser should be deleted
813     auth = ApiClientAuthorization.validate(token: tok)
814     assert_nil(auth)
815   end
816
817   test "refuse to merge user into self" do
818     authorize_with(:active_trustedclient)
819     post(:merge, params: {
820            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
821            new_owner_uuid: users(:active).uuid,
822            redirect_to_new_user: true,
823          })
824     assert_response(422)
825   end
826
827   [[:active, :project_viewer_trustedclient],
828    [:active_trustedclient, :project_viewer]].each do |src, dst|
829     test "refuse to merge with untrusted token (#{src} -> #{dst})" do
830       authorize_with(src)
831       post(:merge, params: {
832              new_user_token: api_client_authorizations(dst).api_token,
833              new_owner_uuid: api_client_authorizations(dst).user.uuid,
834              redirect_to_new_user: true,
835            })
836       assert_response(403)
837     end
838   end
839
840   [[:expired_trustedclient, :project_viewer_trustedclient],
841    [:project_viewer_trustedclient, :expired_trustedclient]].each do |src, dst|
842     test "refuse to merge with expired token (#{src} -> #{dst})" do
843       authorize_with(src)
844       post(:merge, params: {
845              new_user_token: api_client_authorizations(dst).api_token,
846              new_owner_uuid: api_client_authorizations(dst).user.uuid,
847              redirect_to_new_user: true,
848            })
849       assert_response(401)
850     end
851   end
852
853   [['src', :active_trustedclient],
854    ['dst', :project_viewer_trustedclient]].each do |which_scoped, auth|
855     test "refuse to merge with scoped #{which_scoped} token" do
856       act_as_system_user do
857         api_client_authorizations(auth).update_attributes(scopes: ["GET /", "POST /", "PUT /"])
858       end
859       authorize_with(:active_trustedclient)
860       post(:merge, params: {
861              new_user_token: api_client_authorizations(:project_viewer_trustedclient).api_token,
862              new_owner_uuid: users(:project_viewer).uuid,
863              redirect_to_new_user: true,
864            })
865       assert_response(403)
866     end
867   end
868
869   test "refuse to merge if new_owner_uuid is not writable" do
870     authorize_with(:project_viewer_trustedclient)
871     post(:merge, params: {
872            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
873            new_owner_uuid: groups(:anonymously_accessible_project).uuid,
874            redirect_to_new_user: true,
875          })
876     assert_response(403)
877   end
878
879   test "refuse to merge if new_owner_uuid is empty" do
880     authorize_with(:project_viewer_trustedclient)
881     post(:merge, params: {
882            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
883            new_owner_uuid: "",
884            redirect_to_new_user: true,
885          })
886     assert_response(422)
887   end
888
889   test "refuse to merge if new_owner_uuid is not provided" do
890     authorize_with(:project_viewer_trustedclient)
891     post(:merge, params: {
892            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
893            redirect_to_new_user: true,
894          })
895     assert_response(422)
896   end
897
898   test "refuse to update redirect_to_user_uuid directly" do
899     authorize_with(:active_trustedclient)
900     patch(:update, params: {
901             id: users(:active).uuid,
902             user: {
903               redirect_to_user_uuid: users(:active).uuid,
904             },
905           })
906     assert_response(403)
907   end
908
909   test "merge 'project_viewer' account into 'active' account" do
910     authorize_with(:project_viewer_trustedclient)
911     post(:merge, params: {
912            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
913            new_owner_uuid: users(:active).uuid,
914            redirect_to_new_user: true,
915          })
916     assert_response(:success)
917     assert_equal(users(:active).uuid, User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
918
919     auth = ApiClientAuthorization.validate(token: api_client_authorizations(:project_viewer).api_token)
920     assert_not_nil(auth)
921     assert_not_nil(auth.user)
922     assert_equal(users(:active).uuid, auth.user.uuid)
923   end
924
925
926   test "merge 'project_viewer' account into 'active' account using uuids" do
927     authorize_with(:admin)
928     post(:merge, params: {
929            old_user_uuid: users(:project_viewer).uuid,
930            new_user_uuid: users(:active).uuid,
931            new_owner_uuid: users(:active).uuid,
932            redirect_to_new_user: true,
933          })
934     assert_response(:success)
935     assert_equal(users(:active).uuid, User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
936
937     auth = ApiClientAuthorization.validate(token: api_client_authorizations(:project_viewer).api_token)
938     assert_not_nil(auth)
939     assert_not_nil(auth.user)
940     assert_equal(users(:active).uuid, auth.user.uuid)
941   end
942
943   test "merge 'project_viewer' account into 'active' account using uuids denied for non-admin" do
944     authorize_with(:active)
945     post(:merge, params: {
946            old_user_uuid: users(:project_viewer).uuid,
947            new_user_uuid: users(:active).uuid,
948            new_owner_uuid: users(:active).uuid,
949            redirect_to_new_user: true,
950          })
951     assert_response(403)
952     assert_nil(users(:project_viewer).redirect_to_user_uuid)
953   end
954
955   test "merge 'project_viewer' account into 'active' account using uuids denied missing old_user_uuid" do
956     authorize_with(:admin)
957     post(:merge, params: {
958            new_user_uuid: users(:active).uuid,
959            new_owner_uuid: users(:active).uuid,
960            redirect_to_new_user: true,
961          })
962     assert_response(422)
963     assert_nil(users(:project_viewer).redirect_to_user_uuid)
964   end
965
966   test "merge 'project_viewer' account into 'active' account using uuids denied missing new_user_uuid" do
967     authorize_with(:admin)
968     post(:merge, params: {
969            old_user_uuid: users(:project_viewer).uuid,
970            new_owner_uuid: users(:active).uuid,
971            redirect_to_new_user: true,
972          })
973     assert_response(422)
974     assert_nil(users(:project_viewer).redirect_to_user_uuid)
975   end
976
977   test "merge 'project_viewer' account into 'active' account using uuids denied bogus old_user_uuid" do
978     authorize_with(:admin)
979     post(:merge, params: {
980            old_user_uuid: "zzzzz-tpzed-bogusbogusbogus",
981            new_user_uuid: users(:active).uuid,
982            new_owner_uuid: users(:active).uuid,
983            redirect_to_new_user: true,
984          })
985     assert_response(422)
986     assert_nil(users(:project_viewer).redirect_to_user_uuid)
987   end
988
989   test "merge 'project_viewer' account into 'active' account using uuids denied bogus new_user_uuid" do
990     authorize_with(:admin)
991     post(:merge, params: {
992            old_user_uuid: users(:project_viewer).uuid,
993            new_user_uuid: "zzzzz-tpzed-bogusbogusbogus",
994            new_owner_uuid: users(:active).uuid,
995            redirect_to_new_user: true,
996          })
997     assert_response(422)
998     assert_nil(users(:project_viewer).redirect_to_user_uuid)
999   end
1000
1001   test "batch update fails for non-admin" do
1002     authorize_with(:active)
1003     patch(:batch_update, params: {updates: {}})
1004     assert_response(403)
1005   end
1006
1007   test "batch update" do
1008     existinguuid = 'remot-tpzed-foobarbazwazqux'
1009     newuuid = 'remot-tpzed-newnarnazwazqux'
1010     act_as_system_user do
1011       User.create!(uuid: existinguuid, email: 'root@existing.example.com')
1012     end
1013
1014     authorize_with(:admin)
1015     patch(:batch_update,
1016           params: {
1017             updates: {
1018               existinguuid => {
1019                 'first_name' => 'root',
1020                 'email' => 'root@remot.example.com',
1021                 'is_active' => true,
1022                 'is_admin' => true,
1023                 'prefs' => {'foo' => 'bar'},
1024               },
1025               newuuid => {
1026                 'first_name' => 'noot',
1027                 'email' => 'root@remot.example.com',
1028               },
1029             }})
1030     assert_response(:success)
1031
1032     assert_equal('root', User.find_by_uuid(existinguuid).first_name)
1033     assert_equal('root@remot.example.com', User.find_by_uuid(existinguuid).email)
1034     assert_equal(true, User.find_by_uuid(existinguuid).is_active)
1035     assert_equal(true, User.find_by_uuid(existinguuid).is_admin)
1036     assert_equal({'foo' => 'bar'}, User.find_by_uuid(existinguuid).prefs)
1037
1038     assert_equal('noot', User.find_by_uuid(newuuid).first_name)
1039     assert_equal('root@remot.example.com', User.find_by_uuid(newuuid).email)
1040   end
1041
1042   NON_ADMIN_USER_DATA = ["uuid", "kind", "is_active", "email", "first_name",
1043                          "last_name", "username"].sort
1044
1045   def check_non_admin_index
1046     assert_response :success
1047     response_items = json_response["items"]
1048     assert_not_nil response_items
1049     response_items.each do |user_data|
1050       check_non_admin_item user_data
1051       assert(user_data["is_active"], "non-admin index returned inactive user")
1052     end
1053   end
1054
1055   def check_non_admin_show
1056     assert_response :success
1057     check_non_admin_item json_response
1058   end
1059
1060   def check_non_admin_item user_data
1061     assert_equal(NON_ADMIN_USER_DATA, user_data.keys.sort,
1062                  "data in response had missing or extra attributes")
1063     assert_equal("arvados#user", user_data["kind"])
1064   end
1065
1066
1067   def check_readable_users_index expect_present, expect_missing
1068     response_uuids = json_response["items"].map { |u| u["uuid"] }
1069     expect_present.each do |user_key|
1070       assert_includes(response_uuids, users(user_key).uuid,
1071                       "#{user_key} missing from index")
1072     end
1073     expect_missing.each do |user_key|
1074       refute_includes(response_uuids, users(user_key).uuid,
1075                       "#{user_key} included in index")
1076     end
1077   end
1078
1079   def check_inactive_user_findable(params={})
1080     inactive_user = users(:inactive)
1081     get(:index, params: params.merge(filters: [["email", "=", inactive_user.email]]))
1082     assert_response :success
1083     user_list = json_response["items"]
1084     assert_equal(1, user_list.andand.count)
1085     # This test needs to check a column non-admins have no access to,
1086     # to ensure that admins see all user information.
1087     assert_equal(inactive_user.identity_url, user_list.first["identity_url"],
1088                  "admin's filtered index did not return inactive user")
1089   end
1090
1091   def verify_links_added more
1092     assert_equal @initial_link_count+more, Link.count,
1093         "Started with #{@initial_link_count} links, expected #{more} more"
1094   end
1095
1096   def find_obj_in_resp (response_items, object_type, head_kind=nil)
1097     return_obj = nil
1098     response_items.each { |x|
1099       if !x
1100         next
1101       end
1102
1103       if object_type == 'User'
1104         if ArvadosModel::resource_class_for_uuid(x['uuid']) == User
1105           return_obj = x
1106           break
1107         end
1108       else  # looking for a link
1109         if x['head_uuid'] and ArvadosModel::resource_class_for_uuid(x['head_uuid']).kind == head_kind
1110           return_obj = x
1111           break
1112         end
1113       end
1114     }
1115     return return_obj
1116   end
1117 end