16007: Validate group_class is set to 'project' or 'role'
[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 :active_trustedclient
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 :active_trustedclient
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: grp.uuid)
428     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
429     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
430
431     l1 = Link.create!(tail_uuid: users(:active).uuid,
432                  head_uuid: grp.uuid,
433                  link_class: 'permission',
434                  name: 'can_read')
435     l2 = Link.create!(tail_uuid: grp.uuid,
436                  head_uuid: users(:active).uuid,
437                  link_class: 'permission',
438                  name: 'can_read')
439
440     l3 = Link.create!(tail_uuid: users(:project_viewer).uuid,
441                  head_uuid: grp.uuid,
442                  link_class: 'permission',
443                  name: 'can_read')
444     l4 = Link.create!(tail_uuid: grp.uuid,
445                  head_uuid: users(:project_viewer).uuid,
446                  link_class: 'permission',
447                  name: 'can_read')
448
449     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
450     assert User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid).first
451
452     l1.destroy
453     l2.destroy
454
455     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
456     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
457
458   end
459
460
461   test "add user to group, then change permission level" do
462     set_user_from_auth :admin
463     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "role")
464     col = Collection.create!(owner_uuid: grp.uuid)
465     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
466     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
467
468     l1 = Link.create!(tail_uuid: users(:active).uuid,
469                  head_uuid: grp.uuid,
470                  link_class: 'permission',
471                  name: 'can_manage')
472     l2 = Link.create!(tail_uuid: grp.uuid,
473                  head_uuid: users(:active).uuid,
474                  link_class: 'permission',
475                  name: 'can_read')
476
477     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
478     assert users(:active).can?(read: col.uuid)
479     assert users(:active).can?(write: col.uuid)
480     assert users(:active).can?(manage: col.uuid)
481
482     l1.name = 'can_read'
483     l1.save!
484
485     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
486     assert users(:active).can?(read: col.uuid)
487     assert !users(:active).can?(write: col.uuid)
488     assert !users(:active).can?(manage: col.uuid)
489
490     l1.name = 'can_write'
491     l1.save!
492
493     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
494     assert users(:active).can?(read: col.uuid)
495     assert users(:active).can?(write: col.uuid)
496     assert !users(:active).can?(manage: col.uuid)
497   end
498
499
500   test "add user to group, then add overlapping permission link to group" do
501     set_user_from_auth :admin
502     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "role")
503     col = Collection.create!(owner_uuid: grp.uuid)
504     assert_empty Collection.readable_by(users(:active)).where(uuid: col.uuid)
505     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
506
507     l1 = Link.create!(tail_uuid: users(:active).uuid,
508                  head_uuid: grp.uuid,
509                  link_class: 'permission',
510                  name: 'can_manage')
511     l2 = Link.create!(tail_uuid: grp.uuid,
512                  head_uuid: users(:active).uuid,
513                  link_class: 'permission',
514                  name: 'can_read')
515
516     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
517     assert users(:active).can?(read: col.uuid)
518     assert users(:active).can?(write: col.uuid)
519     assert users(:active).can?(manage: col.uuid)
520
521     l3 = Link.create!(tail_uuid: users(:active).uuid,
522                  head_uuid: grp.uuid,
523                  link_class: 'permission',
524                  name: 'can_read')
525
526     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
527     assert users(:active).can?(read: col.uuid)
528     assert users(:active).can?(write: col.uuid)
529     assert users(:active).can?(manage: col.uuid)
530
531     l3.destroy!
532
533     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).first
534     assert users(:active).can?(read: col.uuid)
535     assert users(:active).can?(write: col.uuid)
536     assert users(:active).can?(manage: col.uuid)
537   end
538
539
540   test "add user to group, then add overlapping permission link to subproject" do
541     set_user_from_auth :admin
542     grp = Group.create!(owner_uuid: system_user_uuid, group_class: "project")
543     prj = Group.create!(owner_uuid: grp.uuid, group_class: "project")
544     assert_empty Group.readable_by(users(:active)).where(uuid: prj.uuid)
545     assert_empty User.readable_by(users(:active)).where(uuid: users(:project_viewer).uuid)
546
547     l1 = Link.create!(tail_uuid: users(:active).uuid,
548                  head_uuid: grp.uuid,
549                  link_class: 'permission',
550                  name: 'can_manage')
551     l2 = Link.create!(tail_uuid: grp.uuid,
552                  head_uuid: users(:active).uuid,
553                  link_class: 'permission',
554                  name: 'can_read')
555
556     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
557     assert users(:active).can?(read: prj.uuid)
558     assert users(:active).can?(write: prj.uuid)
559     assert users(:active).can?(manage: prj.uuid)
560
561     l3 = Link.create!(tail_uuid: grp.uuid,
562                  head_uuid: prj.uuid,
563                  link_class: 'permission',
564                  name: 'can_read')
565
566     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
567     assert users(:active).can?(read: prj.uuid)
568     assert users(:active).can?(write: prj.uuid)
569     assert users(:active).can?(manage: prj.uuid)
570
571     l3.destroy!
572
573     assert Group.readable_by(users(:active)).where(uuid: prj.uuid).first
574     assert users(:active).can?(read: prj.uuid)
575     assert users(:active).can?(write: prj.uuid)
576     assert users(:active).can?(manage: prj.uuid)
577   end
578 end