Merge branch 'patch-1' of https://github.com/mr-c/arvados into mr-c-patch-1
[arvados.git] / services / api / test / unit / permission_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
7 class PermissionTest < ActiveSupport::TestCase
8   include CurrentApiClient
9
10   test "Grant permissions on an object I own" do
11     set_user_from_auth :active_trustedclient
12
13     ob = Collection.create
14     assert ob.save
15
16     # Ensure I have permission to manage this group even when its owner changes
17     perm_link = Link.create(tail_uuid: users(:active).uuid,
18                             head_uuid: ob.uuid,
19                             link_class: 'permission',
20                             name: 'can_manage')
21     assert perm_link.save, "should give myself permission on my own object"
22   end
23
24   test "Delete permission links when deleting an object" do
25     set_user_from_auth :active_trustedclient
26
27     ob = Collection.create!
28     Link.create!(tail_uuid: users(:active).uuid,
29                  head_uuid: ob.uuid,
30                  link_class: 'permission',
31                  name: 'can_manage')
32     ob_uuid = ob.uuid
33     assert ob.destroy, "Could not destroy object with 1 permission link"
34     assert_empty(Link.where(head_uuid: ob_uuid),
35                  "Permission link was not deleted when object was deleted")
36   end
37
38   test "permission links owned by root" do
39     set_user_from_auth :active_trustedclient
40     ob = Collection.create!
41     perm_link = Link.create!(tail_uuid: users(:active).uuid,
42                              head_uuid: ob.uuid,
43                              link_class: 'permission',
44                              name: 'can_read')
45     assert_equal system_user_uuid, perm_link.owner_uuid
46   end
47
48   test "readable_by" do
49     set_user_from_auth :admin
50
51     ob = Collection.create!
52     Link.create!(tail_uuid: users(:active).uuid,
53                  head_uuid: ob.uuid,
54                  link_class: 'permission',
55                  name: 'can_read')
56     assert Collection.readable_by(users(:active)).where(uuid: ob.uuid).any?, "user does not have read permission"
57   end
58
59   test "writable_by" do
60     set_user_from_auth :admin
61
62     ob = Collection.create!
63     Link.create!(tail_uuid: users(:active).uuid,
64                  head_uuid: ob.uuid,
65                  link_class: 'permission',
66                  name: 'can_write')
67     assert ob.writable_by.include?(users(:active).uuid), "user does not have write permission"
68   end
69
70   test "update permission link" do
71     set_user_from_auth :admin
72
73     grp = Group.create! name: "blah project", group_class: "project"
74     ob = Collection.create! owner_uuid: grp.uuid
75
76     assert !users(:active).can?(write: ob)
77     assert !users(:active).can?(read: ob)
78
79     l1 = Link.create!(tail_uuid: users(:active).uuid,
80                  head_uuid: grp.uuid,
81                  link_class: 'permission',
82                  name: 'can_write')
83
84     assert users(:active).can?(write: ob)
85     assert users(:active).can?(read: ob)
86
87     l1.update_attributes!(name: 'can_read')
88
89     assert !users(:active).can?(write: ob)
90     assert users(:active).can?(read: ob)
91
92     l1.destroy
93
94     assert !users(:active).can?(write: ob)
95     assert !users(:active).can?(read: ob)
96   end
97
98   test "writable_by reports requesting user's own uuid for a writable project" do
99     invited_to_write = users(:project_viewer)
100     group = groups(:asubproject)
101
102     # project_view can read, but cannot see write or see writers list
103     set_user_from_auth :project_viewer
104     assert_equal([group.owner_uuid],
105                  group.writable_by,
106                  "writers list should just have owner_uuid")
107
108     # allow project_viewer to write for the remainder of the test
109     set_user_from_auth :admin
110     Link.create!(tail_uuid: invited_to_write.uuid,
111                  head_uuid: group.uuid,
112                  link_class: 'permission',
113                  name: 'can_write')
114     group.permissions.reload
115
116     # project_viewer should see self in writers list (but not all writers)
117     set_user_from_auth :project_viewer
118     assert_not_nil(group.writable_by,
119                     "can write but cannot see writers list")
120     assert_includes(group.writable_by, invited_to_write.uuid,
121                     "self missing from writers list")
122     assert_includes(group.writable_by, group.owner_uuid,
123                     "project owner missing from writers list")
124     refute_includes(group.writable_by, users(:active).uuid,
125                     "saw :active user in writers list")
126
127     # active user should see full writers list
128     set_user_from_auth :active
129     assert_includes(group.writable_by, invited_to_write.uuid,
130                     "permission just added, but missing from writers list")
131
132     # allow project_viewer to manage for the remainder of the test
133     set_user_from_auth :admin
134     Link.create!(tail_uuid: invited_to_write.uuid,
135                  head_uuid: group.uuid,
136                  link_class: 'permission',
137                  name: 'can_manage')
138     # invite another writer we can test for
139     Link.create!(tail_uuid: users(:spectator).uuid,
140                  head_uuid: group.uuid,
141                  link_class: 'permission',
142                  name: 'can_write')
143     group.permissions.reload
144
145     set_user_from_auth :project_viewer
146     assert_not_nil(group.writable_by,
147                     "can manage but cannot see writers list")
148     assert_includes(group.writable_by, users(:spectator).uuid,
149                     ":spectator missing from writers list")
150   end
151
152   test "user owns group, group can_manage object's group, user can add permissions" do
153     set_user_from_auth :admin
154
155     owner_grp = Group.create!(owner_uuid: users(:active).uuid, group_class: "role")
156
157     sp_grp = Group.create!(group_class: "project")
158
159     Link.create!(link_class: 'permission',
160                  name: 'can_manage',
161                  tail_uuid: owner_grp.uuid,
162                  head_uuid: sp_grp.uuid)
163
164     sp = Collection.create!(owner_uuid: sp_grp.uuid)
165
166     # active user owns owner_grp, which has can_manage permission on sp_grp
167     # user should be able to add permissions on sp.
168     set_user_from_auth :active_trustedclient
169     test_perm = Link.create(tail_uuid: users(:active).uuid,
170                             head_uuid: sp.uuid,
171                             link_class: 'permission',
172                             name: 'can_write')
173     assert test_perm.save, "could not save new permission on target object"
174     assert test_perm.destroy, "could not delete new permission on target object"
175   end
176
177   # bug #3091
178   skip "can_manage permission on a non-group object" do
179     set_user_from_auth :admin
180
181     ob = Collection.create!
182     # grant can_manage permission to active
183     perm_link = Link.create!(tail_uuid: users(:active).uuid,
184                              head_uuid: ob.uuid,
185                              link_class: 'permission',
186                              name: 'can_manage')
187     # ob is owned by :admin, the link is owned by root
188     assert_equal users(:admin).uuid, ob.owner_uuid
189     assert_equal system_user_uuid, perm_link.owner_uuid
190
191     # user "active" can modify the permission link
192     set_user_from_auth :active_trustedclient
193     perm_link.properties["foo"] = 'bar'
194     assert perm_link.save, "could not save modified link"
195
196     assert_equal 'bar', perm_link.properties['foo'], "link properties do not include foo = bar"
197   end
198
199   test "user without can_manage permission may not modify permission link" do
200     set_user_from_auth :admin
201
202     ob = Collection.create!
203     # grant can_manage permission to active
204     perm_link = Link.create!(tail_uuid: users(:active).uuid,
205                              head_uuid: ob.uuid,
206                              link_class: 'permission',
207                              name: 'can_read')
208     # ob is owned by :admin, the link is owned by root
209     assert_equal ob.owner_uuid, users(:admin).uuid
210     assert_equal perm_link.owner_uuid, system_user_uuid
211
212     # user "active" may not modify the permission link
213     set_user_from_auth :active_trustedclient
214     perm_link.name = 'can_manage'
215     assert_raises ArvadosModel::PermissionDeniedError do
216       perm_link.save
217     end
218   end
219
220   test "manager user gets permission to minions' articles via can_manage link" do
221     manager = create :active_user, first_name: "Manage", last_name: "Er"
222     minion = create :active_user, first_name: "Min", last_name: "Ion"
223     minions_specimen = act_as_user minion do
224       g = Group.create! name: "minon project", group_class: "project"
225       Collection.create! owner_uuid: g.uuid
226     end
227     # Manager creates a group. (Make sure it doesn't magically give
228     # anyone any additional permissions.)
229     g = nil
230     act_as_user manager do
231       g = create :group, name: "NoBigSecret Lab", group_class: "role"
232       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
233                    "saw a user I shouldn't see")
234       assert_raises(ArvadosModel::PermissionDeniedError,
235                     ActiveRecord::RecordInvalid,
236                     "gave can_read permission to a user I shouldn't see") do
237         create(:permission_link,
238                name: 'can_read', tail_uuid: minion.uuid, head_uuid: g.uuid)
239       end
240       %w(can_manage can_write can_read).each do |perm_type|
241         assert_raises(ArvadosModel::PermissionDeniedError,
242                       ActiveRecord::RecordInvalid,
243                       "escalated privileges") do
244           create(:permission_link,
245                  name: perm_type, tail_uuid: g.uuid, head_uuid: minion.uuid)
246         end
247       end
248       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
249                    "manager saw minion too soon")
250       assert_empty(User.readable_by(minion).where(uuid: manager.uuid),
251                    "minion saw manager too soon")
252       assert_empty(Group.readable_by(minion).where(uuid: g.uuid),
253                    "minion saw manager's new NoBigSecret Lab group too soon")
254
255       # Manager declares everybody on the system should be able to see
256       # the NoBigSecret Lab group.
257       create(:permission_link,
258              name: 'can_read',
259              tail_uuid: 'zzzzz-j7d0g-fffffffffffffff',
260              head_uuid: g.uuid)
261       # ...but nobody has joined the group yet. Manager still can't see
262       # minion.
263       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
264                    "manager saw minion too soon")
265     end
266
267     act_as_user minion do
268       # Minion can see the group.
269       assert_not_empty(Group.readable_by(minion).where(uuid: g.uuid),
270                        "minion could not see the NoBigSecret Lab group")
271       # Minion joins the group.
272       create(:permission_link,
273              name: 'can_read',
274              tail_uuid: g.uuid,
275              head_uuid: minion.uuid)
276     end
277
278     act_as_user manager do
279       # Now, manager can see minion.
280       assert_not_empty(User.readable_by(manager).where(uuid: minion.uuid),
281                        "manager could not see minion")
282       # But cannot obtain further privileges this way.
283       assert_raises(ArvadosModel::PermissionDeniedError,
284                     "escalated privileges") do
285         create(:permission_link,
286                name: 'can_manage', tail_uuid: manager.uuid, head_uuid: minion.uuid)
287       end
288       assert_empty(Collection
289                      .readable_by(manager)
290                      .where(uuid: minions_specimen.uuid),
291                    "manager saw the minion's private stuff")
292       assert_raises(ArvadosModel::PermissionDeniedError,
293                    "manager could update minion's private stuff") do
294         minions_specimen.update_attributes(properties: {'x' => 'y'})
295       end
296     end
297
298     act_as_system_user do
299       # Root can give Manager more privileges over Minion.
300       create(:permission_link,
301              name: 'can_manage', tail_uuid: g.uuid, head_uuid: minion.uuid)
302     end
303
304     act_as_user manager do
305       # Now, manager can read and write Minion's stuff.
306       assert_not_empty(Collection
307                          .readable_by(manager)
308                          .where(uuid: minions_specimen.uuid),
309                        "manager could not find minion's specimen by uuid")
310       assert_equal(true,
311                    minions_specimen.update_attributes(properties: {'x' => 'y'}),
312                    "manager could not update minion's specimen object")
313     end
314   end
315
316   test "users with bidirectional read permission in group can see each other, but cannot see each other's private articles" do
317     a = create :active_user, first_name: "A"
318     b = create :active_user, first_name: "B"
319     other = create :active_user, first_name: "OTHER"
320
321     assert_empty(User.readable_by(b).where(uuid: a.uuid),
322                      "#{b.first_name} should not be able to see 'a' in the user list")
323     assert_empty(User.readable_by(a).where(uuid: b.uuid),
324                      "#{a.first_name} should not be able to see 'b' in the user list")
325
326     act_as_system_user do
327       g = create :group, group_class: "role"
328       [a,b].each do |u|
329         create(:permission_link,
330                name: 'can_read', tail_uuid: u.uuid, head_uuid: g.uuid)
331         create(:permission_link,
332                name: 'can_read', head_uuid: u.uuid, tail_uuid: g.uuid)
333       end
334     end
335
336     assert_not_empty(User.readable_by(b).where(uuid: a.uuid),
337                      "#{b.first_name} should be able to see 'a' in the user list")
338     assert_not_empty(User.readable_by(a).where(uuid: b.uuid),
339                      "#{a.first_name} should be able to see 'b' in the user list")
340
341     a_specimen = act_as_user a do
342       Collection.create!
343     end
344     assert_not_empty(Collection.readable_by(a).where(uuid: a_specimen.uuid),
345                      "A cannot read own Collection, following test probably useless.")
346     assert_empty(Collection.readable_by(b).where(uuid: a_specimen.uuid),
347                  "B can read A's Collection")
348     [a,b].each do |u|
349       assert_empty(User.readable_by(u).where(uuid: other.uuid),
350                    "#{u.first_name} can see OTHER in the user list")
351       assert_empty(User.readable_by(other).where(uuid: u.uuid),
352                    "OTHER can see #{u.first_name} in the user list")
353       act_as_user u do
354         assert_raises ArvadosModel::PermissionDeniedError, "wrote without perm" do
355           other.update_attributes!(prefs: {'pwned' => true})
356         end
357         assert_equal(true, u.update_attributes!(prefs: {'thisisme' => true}),
358                      "#{u.first_name} can't update its own prefs")
359       end
360       act_as_user other do
361         assert_raises(ArvadosModel::PermissionDeniedError,
362                         "OTHER wrote #{u.first_name} without perm") do
363           u.update_attributes!(prefs: {'pwned' => true})
364         end
365         assert_equal(true, other.update_attributes!(prefs: {'thisisme' => true}),
366                      "OTHER can't update its own prefs")
367       end
368     end
369   end
370
371   test "cannot create with owner = unwritable user" do
372     set_user_from_auth :rominiadmin
373     assert_raises ArvadosModel::PermissionDeniedError, "created with owner = unwritable user" do
374       Collection.create!(owner_uuid: users(:active).uuid)
375     end
376   end
377
378   test "cannot change owner to unwritable user" do
379     set_user_from_auth :rominiadmin
380     ob = Collection.create!
381     assert_raises ArvadosModel::PermissionDeniedError, "changed owner to unwritable user" do
382       ob.update_attributes!(owner_uuid: users(:active).uuid)
383     end
384   end
385
386   test "cannot create with owner = unwritable group" do
387     set_user_from_auth :rominiadmin
388     assert_raises ArvadosModel::PermissionDeniedError, "created with owner = unwritable group" do
389       Collection.create!(owner_uuid: groups(:aproject).uuid)
390     end
391   end
392
393   test "cannot change owner to unwritable group" do
394     set_user_from_auth :rominiadmin
395     ob = Collection.create!
396     assert_raises ArvadosModel::PermissionDeniedError, "changed owner to unwritable group" do
397       ob.update_attributes!(owner_uuid: groups(:aproject).uuid)
398     end
399   end
400
401   def container_logs(container, user)
402     Log.readable_by(users(user)).
403       where(object_uuid: containers(container).uuid, event_type: "test")
404   end
405
406   test "container logs created by dispatch are visible to container requestor" do
407     set_user_from_auth :dispatch1
408     Log.create!(object_uuid: containers(:running).uuid,
409                 event_type: "test")
410
411     assert_not_empty container_logs(:running, :admin)
412     assert_not_empty container_logs(:running, :active)
413     assert_empty container_logs(:running, :spectator)
414   end
415
416   test "container logs created by dispatch are public if container request is public" do
417     set_user_from_auth :dispatch1
418     Log.create!(object_uuid: containers(:running_older).uuid,
419                 event_type: "test")
420
421     assert_not_empty container_logs(:running_older, :anonymous)
422   end
423
424   test "add user to group, then remove them" do
425     set_user_from_auth :admin
426     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "role")
427     col = Collection.create!(owner_uuid: system_user_uuid)
428
429     l0 = Link.create!(tail_uuid: grp.uuid,
430                  head_uuid: col.uuid,
431                  link_class: 'permission',
432                  name: 'can_read')
433
434     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
435     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
436
437     l1 = Link.create!(tail_uuid: users(:active).uuid,
438                  head_uuid: grp.uuid,
439                  link_class: 'permission',
440                  name: 'can_read')
441     l2 = Link.create!(tail_uuid: grp.uuid,
442                  head_uuid: users(:active).uuid,
443                  link_class: 'permission',
444                  name: 'can_read')
445
446     l3 = Link.create!(tail_uuid: users(:project_viewer).uuid,
447                  head_uuid: grp.uuid,
448                  link_class: 'permission',
449                  name: 'can_read')
450     l4 = Link.create!(tail_uuid: grp.uuid,
451                  head_uuid: users(:project_viewer).uuid,
452                  link_class: 'permission',
453                  name: 'can_read')
454
455     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
456     assert User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid).first
457
458     l1.destroy
459     l2.destroy
460
461     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
462     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
463
464   end
465
466
467   test "add user to group, then change permission level" do
468     set_user_from_auth :admin
469     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "project")
470     col = Collection.create!(owner_uuid: grp.uuid)
471     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
472     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
473
474     l1 = Link.create!(tail_uuid: users(:active).uuid,
475                  head_uuid: grp.uuid,
476                  link_class: 'permission',
477                  name: 'can_manage')
478
479     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
480     assert users(:active).can?(read: col.uuid)
481     assert users(:active).can?(write: col.uuid)
482     assert users(:active).can?(manage: col.uuid)
483
484     l1.name = 'can_read'
485     l1.save!
486
487     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
488     assert users(:active).can?(read: col.uuid)
489     assert !users(:active).can?(write: col.uuid)
490     assert !users(:active).can?(manage: col.uuid)
491
492     l1.name = 'can_write'
493     l1.save!
494
495     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
496     assert users(:active).can?(read: col.uuid)
497     assert users(:active).can?(write: col.uuid)
498     assert !users(:active).can?(manage: col.uuid)
499   end
500
501
502   test "add user to group, then add overlapping permission link to group" do
503     set_user_from_auth :admin
504     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "project")
505     col = Collection.create!(owner_uuid: grp.uuid)
506     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
507     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
508
509     l1 = Link.create!(tail_uuid: users(:active).uuid,
510                  head_uuid: grp.uuid,
511                  link_class: 'permission',
512                  name: 'can_manage')
513
514     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
515     assert users(:active).can?(read: col.uuid)
516     assert users(:active).can?(write: col.uuid)
517     assert users(:active).can?(manage: col.uuid)
518
519     l3 = Link.create!(tail_uuid: users(:active).uuid,
520                  head_uuid: grp.uuid,
521                  link_class: 'permission',
522                  name: 'can_read')
523
524     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
525     assert users(:active).can?(read: col.uuid)
526     assert users(:active).can?(write: col.uuid)
527     assert users(:active).can?(manage: col.uuid)
528
529     l3.destroy!
530
531     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
532     assert users(:active).can?(read: col.uuid)
533     assert users(:active).can?(write: col.uuid)
534     assert users(:active).can?(manage: col.uuid)
535   end
536
537
538   test "add user to group, then add overlapping permission link to subproject" do
539     set_user_from_auth :admin
540     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "role")
541     prj = Group.create!(owner_uuid: system_user_uuid, group_class: "project")
542
543     l0 = Link.create!(tail_uuid: grp.uuid,
544                  head_uuid: prj.uuid,
545                  link_class: 'permission',
546                  name: 'can_manage')
547
548     assert_empty Group.readable_by(users(:active)).where(uuid: prj.uuid)
549     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
550
551     l1 = Link.create!(tail_uuid: users(:active).uuid,
552                  head_uuid: grp.uuid,
553                  link_class: 'permission',
554                  name: 'can_manage')
555     l2 = Link.create!(tail_uuid: grp.uuid,
556                  head_uuid: users(:active).uuid,
557                  link_class: 'permission',
558                  name: 'can_read')
559
560     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
561     assert users(:active).can?(read: prj.uuid)
562     assert users(:active).can?(write: prj.uuid)
563     assert users(:active).can?(manage: prj.uuid)
564
565     l3 = Link.create!(tail_uuid: grp.uuid,
566                  head_uuid: prj.uuid,
567                  link_class: 'permission',
568                  name: 'can_read')
569
570     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
571     assert users(:active).can?(read: prj.uuid)
572     assert users(:active).can?(write: prj.uuid)
573     assert users(:active).can?(manage: prj.uuid)
574
575     l3.destroy!
576
577     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
578     assert users(:active).can?(read: prj.uuid)
579     assert users(:active).can?(write: prj.uuid)
580     assert users(:active).can?(manage: prj.uuid)
581   end
582 end