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