ae7b21dec83e556f8321ee7290e5680824be5881
[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_read',
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_read',
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_read',
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_read',
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_read',
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_read',
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}, 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   end
678
679   test "non-admin user gets only safe attributes from users#show" do
680     g = act_as_system_user do
681       create :group, group_class: "role"
682     end
683     users = create_list :active_user, 2, join_groups: [g]
684     token = create :token, user: users[0]
685     authorize_with_token token
686     get :show, params: {id: users[1].uuid}
687     check_non_admin_show
688   end
689
690   [2, 4].each do |limit|
691     test "non-admin user can limit index to #{limit}" do
692       g = act_as_system_user do
693         create :group, group_class: "role"
694       end
695       users = create_list :active_user, 4, join_groups: [g]
696       token = create :token, user: users[0]
697
698       authorize_with_token token
699       get(:index, params: {limit: limit})
700       check_non_admin_index
701       assert_equal(limit, json_response["items"].size,
702                    "non-admin index limit was ineffective")
703     end
704   end
705
706   test "admin has full index powers" do
707     authorize_with :admin
708     check_inactive_user_findable
709   end
710
711   test "reader token can grant admin index powers" do
712     authorize_with :spectator
713     check_inactive_user_findable(reader_tokens: [api_token(:admin)])
714   end
715
716   test "admin can filter on user.is_active" do
717     authorize_with :admin
718     get(:index, params: {filters: [["is_active", "=", "true"]]})
719     assert_response :success
720     check_readable_users_index [:active, :spectator], [:inactive]
721   end
722
723   test "admin can search where user.is_active" do
724     authorize_with :admin
725     get(:index, params: {where: {is_active: true}})
726     assert_response :success
727     check_readable_users_index [:active, :spectator], [:inactive]
728   end
729
730   test "update active_no_prefs user profile and expect notification email" do
731     authorize_with :admin
732
733     put :update, params: {
734       id: users(:active_no_prefs).uuid,
735       user: {
736         prefs: {:profile => {'organization' => 'example.com'}}
737       }
738     }
739     assert_response :success
740
741     found_email = false
742     ActionMailer::Base.deliveries.andand.each do |email|
743       if email.subject == "Profile created by #{users(:active_no_prefs).email}"
744         found_email = true
745         break
746       end
747     end
748     assert_equal true, found_email, 'Expected email after creating profile'
749   end
750
751   test "update active_no_prefs_profile user profile and expect notification email" do
752     authorize_with :admin
753
754     user = {}
755     user[:prefs] = users(:active_no_prefs_profile_no_getting_started_shown).prefs
756     user[:prefs][:profile] = {:profile => {'organization' => 'example.com'}}
757     put :update, params: {
758       id: users(:active_no_prefs_profile_no_getting_started_shown).uuid,
759       user: user
760     }
761     assert_response :success
762
763     found_email = false
764     ActionMailer::Base.deliveries.andand.each do |email|
765       if email.subject == "Profile created by #{users(:active_no_prefs_profile_no_getting_started_shown).email}"
766         found_email = true
767         break
768       end
769     end
770     assert_equal true, found_email, 'Expected email after creating profile'
771   end
772
773   test "update active user profile and expect no notification email" do
774     authorize_with :admin
775
776     put :update, params: {
777       id: users(:active).uuid,
778       user: {
779         prefs: {:profile => {'organization' => 'anotherexample.com'}}
780       }
781     }
782     assert_response :success
783
784     found_email = false
785     ActionMailer::Base.deliveries.andand.each do |email|
786       if email.subject == "Profile created by #{users(:active).email}"
787         found_email = true
788         break
789       end
790     end
791     assert_equal false, found_email, 'Expected no email after updating profile'
792   end
793
794   test "user API response includes writable_by" do
795     authorize_with :active
796     get :current
797     assert_response :success
798     assert_includes(json_response["writable_by"], users(:active).uuid,
799                     "user's writable_by should include self")
800     assert_includes(json_response["writable_by"], users(:active).owner_uuid,
801                     "user's writable_by should include its owner_uuid")
802   end
803
804   test "merge with redirect_to_user_uuid=false" do
805     authorize_with :project_viewer_trustedclient
806     tok = api_client_authorizations(:project_viewer).api_token
807     post :merge, params: {
808            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
809            new_owner_uuid: users(:active).uuid,
810            redirect_to_new_user: false,
811          }
812     assert_response(:success)
813     assert_nil(User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
814
815     # because redirect_to_new_user=false, token owned by
816     # project_viewer should be deleted
817     auth = ApiClientAuthorization.validate(token: tok)
818     assert_nil(auth)
819   end
820
821   test "merge remote to local as admin" do
822     authorize_with :admin
823
824     remoteuser = User.create!(uuid: "zbbbb-tpzed-remotremotremot")
825     tok = ApiClientAuthorization.create!(user: remoteuser, api_client: api_clients(:untrusted)).api_token
826
827     auth = ApiClientAuthorization.validate(token: tok)
828     assert_not_nil(auth)
829     assert_nil(remoteuser.redirect_to_user_uuid)
830
831     post :merge, params: {
832            new_user_uuid: users(:active).uuid,
833            old_user_uuid: remoteuser.uuid,
834            new_owner_uuid: users(:active).uuid,
835            redirect_to_new_user: true,
836          }
837     assert_response(:success)
838     remoteuser.reload
839     assert_equal(users(:active).uuid, remoteuser.redirect_to_user_uuid)
840
841     # token owned by remoteuser should be deleted
842     auth = ApiClientAuthorization.validate(token: tok)
843     assert_nil(auth)
844   end
845
846   test "refuse to merge user into self" do
847     authorize_with(:active_trustedclient)
848     post(:merge, params: {
849            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
850            new_owner_uuid: users(:active).uuid,
851            redirect_to_new_user: true,
852          })
853     assert_response(422)
854   end
855
856   [[:active, :project_viewer_trustedclient],
857    [:active_trustedclient, :project_viewer]].each do |src, dst|
858     test "refuse to merge with untrusted token (#{src} -> #{dst})" do
859       authorize_with(src)
860       post(:merge, params: {
861              new_user_token: api_client_authorizations(dst).api_token,
862              new_owner_uuid: api_client_authorizations(dst).user.uuid,
863              redirect_to_new_user: true,
864            })
865       assert_response(403)
866     end
867   end
868
869   [[:expired_trustedclient, :project_viewer_trustedclient],
870    [:project_viewer_trustedclient, :expired_trustedclient]].each do |src, dst|
871     test "refuse to merge with expired token (#{src} -> #{dst})" do
872       authorize_with(src)
873       post(:merge, params: {
874              new_user_token: api_client_authorizations(dst).api_token,
875              new_owner_uuid: api_client_authorizations(dst).user.uuid,
876              redirect_to_new_user: true,
877            })
878       assert_response(401)
879     end
880   end
881
882   [['src', :active_trustedclient],
883    ['dst', :project_viewer_trustedclient]].each do |which_scoped, auth|
884     test "refuse to merge with scoped #{which_scoped} token" do
885       act_as_system_user do
886         api_client_authorizations(auth).update_attributes(scopes: ["GET /", "POST /", "PUT /"])
887       end
888       authorize_with(:active_trustedclient)
889       post(:merge, params: {
890              new_user_token: api_client_authorizations(:project_viewer_trustedclient).api_token,
891              new_owner_uuid: users(:project_viewer).uuid,
892              redirect_to_new_user: true,
893            })
894       assert_response(403)
895     end
896   end
897
898   test "refuse to merge if new_owner_uuid is not writable" do
899     authorize_with(:project_viewer_trustedclient)
900     post(:merge, params: {
901            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
902            new_owner_uuid: groups(:anonymously_accessible_project).uuid,
903            redirect_to_new_user: true,
904          })
905     assert_response(403)
906   end
907
908   test "refuse to merge if new_owner_uuid is empty" do
909     authorize_with(:project_viewer_trustedclient)
910     post(:merge, params: {
911            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
912            new_owner_uuid: "",
913            redirect_to_new_user: true,
914          })
915     assert_response(422)
916   end
917
918   test "refuse to merge if new_owner_uuid is not provided" do
919     authorize_with(:project_viewer_trustedclient)
920     post(:merge, params: {
921            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
922            redirect_to_new_user: true,
923          })
924     assert_response(422)
925   end
926
927   test "refuse to update redirect_to_user_uuid directly" do
928     authorize_with(:active_trustedclient)
929     patch(:update, params: {
930             id: users(:active).uuid,
931             user: {
932               redirect_to_user_uuid: users(:active).uuid,
933             },
934           })
935     assert_response(403)
936   end
937
938   test "merge 'project_viewer' account into 'active' account" do
939     authorize_with(:project_viewer_trustedclient)
940     post(:merge, params: {
941            new_user_token: api_client_authorizations(:active_trustedclient).api_token,
942            new_owner_uuid: users(:active).uuid,
943            redirect_to_new_user: true,
944          })
945     assert_response(:success)
946     assert_equal(users(:active).uuid, User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
947
948     auth = ApiClientAuthorization.validate(token: api_client_authorizations(:project_viewer).api_token)
949     assert_not_nil(auth)
950     assert_not_nil(auth.user)
951     assert_equal(users(:active).uuid, auth.user.uuid)
952   end
953
954
955   test "merge 'project_viewer' account into 'active' account using uuids" do
956     authorize_with(:admin)
957     post(:merge, params: {
958            old_user_uuid: users(:project_viewer).uuid,
959            new_user_uuid: users(:active).uuid,
960            new_owner_uuid: users(:active).uuid,
961            redirect_to_new_user: true,
962          })
963     assert_response(:success)
964     assert_equal(users(:active).uuid, User.unscoped.find_by_uuid(users(:project_viewer).uuid).redirect_to_user_uuid)
965
966     auth = ApiClientAuthorization.validate(token: api_client_authorizations(:project_viewer).api_token)
967     assert_not_nil(auth)
968     assert_not_nil(auth.user)
969     assert_equal(users(:active).uuid, auth.user.uuid)
970   end
971
972   test "merge 'project_viewer' account into 'active' account using uuids denied for non-admin" do
973     authorize_with(:active)
974     post(:merge, params: {
975            old_user_uuid: users(:project_viewer).uuid,
976            new_user_uuid: users(:active).uuid,
977            new_owner_uuid: users(:active).uuid,
978            redirect_to_new_user: true,
979          })
980     assert_response(403)
981     assert_nil(users(:project_viewer).redirect_to_user_uuid)
982   end
983
984   test "merge 'project_viewer' account into 'active' account using uuids denied missing old_user_uuid" do
985     authorize_with(:admin)
986     post(:merge, params: {
987            new_user_uuid: users(:active).uuid,
988            new_owner_uuid: users(:active).uuid,
989            redirect_to_new_user: true,
990          })
991     assert_response(422)
992     assert_nil(users(:project_viewer).redirect_to_user_uuid)
993   end
994
995   test "merge 'project_viewer' account into 'active' account using uuids denied missing new_user_uuid" do
996     authorize_with(:admin)
997     post(:merge, params: {
998            old_user_uuid: users(:project_viewer).uuid,
999            new_owner_uuid: users(:active).uuid,
1000            redirect_to_new_user: true,
1001          })
1002     assert_response(422)
1003     assert_nil(users(:project_viewer).redirect_to_user_uuid)
1004   end
1005
1006   test "merge 'project_viewer' account into 'active' account using uuids denied bogus old_user_uuid" do
1007     authorize_with(:admin)
1008     post(:merge, params: {
1009            old_user_uuid: "zzzzz-tpzed-bogusbogusbogus",
1010            new_user_uuid: users(:active).uuid,
1011            new_owner_uuid: users(:active).uuid,
1012            redirect_to_new_user: true,
1013          })
1014     assert_response(422)
1015     assert_nil(users(:project_viewer).redirect_to_user_uuid)
1016   end
1017
1018   test "merge 'project_viewer' account into 'active' account using uuids denied bogus new_user_uuid" do
1019     authorize_with(:admin)
1020     post(:merge, params: {
1021            old_user_uuid: users(:project_viewer).uuid,
1022            new_user_uuid: "zzzzz-tpzed-bogusbogusbogus",
1023            new_owner_uuid: users(:active).uuid,
1024            redirect_to_new_user: true,
1025          })
1026     assert_response(422)
1027     assert_nil(users(:project_viewer).redirect_to_user_uuid)
1028   end
1029
1030   test "batch update fails for non-admin" do
1031     authorize_with(:active)
1032     patch(:batch_update, params: {updates: {}})
1033     assert_response(403)
1034   end
1035
1036   test "batch update" do
1037     existinguuid = 'remot-tpzed-foobarbazwazqux'
1038     newuuid = 'remot-tpzed-newnarnazwazqux'
1039     unchanginguuid = 'remot-tpzed-nochangingattrs'
1040     act_as_system_user do
1041       User.create!(uuid: existinguuid, email: 'root@existing.example.com')
1042       User.create!(uuid: unchanginguuid, email: 'root@unchanging.example.com', prefs: {'foo' => {'bar' => 'baz'}})
1043     end
1044     assert_equal(1, Log.where(object_uuid: unchanginguuid).count)
1045
1046     authorize_with(:admin)
1047     patch(:batch_update,
1048           params: {
1049             updates: {
1050               existinguuid => {
1051                 'first_name' => 'root',
1052                 'email' => 'root@remot.example.com',
1053                 'is_active' => true,
1054                 'is_admin' => true,
1055                 'prefs' => {'foo' => 'bar'},
1056               },
1057               newuuid => {
1058                 'first_name' => 'noot',
1059                 'email' => 'root@remot.example.com',
1060                 'username' => '',
1061               },
1062               unchanginguuid => {
1063                 'email' => 'root@unchanging.example.com',
1064                 'prefs' => {'foo' => {'bar' => 'baz'}},
1065               },
1066             }})
1067     assert_response(:success)
1068
1069     assert_equal('root', User.find_by_uuid(existinguuid).first_name)
1070     assert_equal('root@remot.example.com', User.find_by_uuid(existinguuid).email)
1071     assert_equal(true, User.find_by_uuid(existinguuid).is_active)
1072     assert_equal(true, User.find_by_uuid(existinguuid).is_admin)
1073     assert_equal({'foo' => 'bar'}, User.find_by_uuid(existinguuid).prefs)
1074
1075     assert_equal('noot', User.find_by_uuid(newuuid).first_name)
1076     assert_equal('root@remot.example.com', User.find_by_uuid(newuuid).email)
1077
1078     assert_equal(1, Log.where(object_uuid: unchanginguuid).count)
1079   end
1080
1081   NON_ADMIN_USER_DATA = ["uuid", "kind", "is_active", "email", "first_name",
1082                          "last_name", "username"].sort
1083
1084   def check_non_admin_index
1085     assert_response :success
1086     response_items = json_response["items"]
1087     assert_not_nil response_items
1088     response_items.each do |user_data|
1089       check_non_admin_item user_data
1090       assert(user_data["is_active"], "non-admin index returned inactive user")
1091     end
1092   end
1093
1094   def check_non_admin_show
1095     assert_response :success
1096     check_non_admin_item json_response
1097   end
1098
1099   def check_non_admin_item user_data
1100     assert_equal(NON_ADMIN_USER_DATA, user_data.keys.sort,
1101                  "data in response had missing or extra attributes")
1102     assert_equal("arvados#user", user_data["kind"])
1103   end
1104
1105
1106   def check_readable_users_index expect_present, expect_missing
1107     response_uuids = json_response["items"].map { |u| u["uuid"] }
1108     expect_present.each do |user_key|
1109       assert_includes(response_uuids, users(user_key).uuid,
1110                       "#{user_key} missing from index")
1111     end
1112     expect_missing.each do |user_key|
1113       refute_includes(response_uuids, users(user_key).uuid,
1114                       "#{user_key} included in index")
1115     end
1116   end
1117
1118   def check_inactive_user_findable(params={})
1119     inactive_user = users(:inactive)
1120     get(:index, params: params.merge(filters: [["email", "=", inactive_user.email]]))
1121     assert_response :success
1122     user_list = json_response["items"]
1123     assert_equal(1, user_list.andand.count)
1124     # This test needs to check a column non-admins have no access to,
1125     # to ensure that admins see all user information.
1126     assert_equal(inactive_user.identity_url, user_list.first["identity_url"],
1127                  "admin's filtered index did not return inactive user")
1128   end
1129
1130   def verify_links_added more
1131     assert_equal @initial_link_count+more, Link.count,
1132         "Started with #{@initial_link_count} links, expected #{more} more"
1133   end
1134
1135   def find_obj_in_resp (response_items, object_type, head_kind=nil)
1136     return_obj = nil
1137     response_items.each { |x|
1138       if !x
1139         next
1140       end
1141
1142       if object_type == 'User'
1143         if ArvadosModel::resource_class_for_uuid(x['uuid']) == User
1144           return_obj = x
1145           break
1146         end
1147       else  # looking for a link
1148         if x['head_uuid'] and ArvadosModel::resource_class_for_uuid(x['head_uuid']).kind == head_kind
1149           return_obj = x
1150           break
1151         end
1152       end
1153     }
1154     return return_obj
1155   end
1156 end