4019: Improve error checking, add tests for some error cases
[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 = Specimen.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 = Specimen.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 = Specimen.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 = Specimen.create!
52     Link.create!(tail_uuid: users(:active).uuid,
53                  head_uuid: ob.uuid,
54                  link_class: 'permission',
55                  name: 'can_read')
56     assert Specimen.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 = Specimen.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 "writable_by reports requesting user's own uuid for a writable project" do
71     invited_to_write = users(:project_viewer)
72     group = groups(:asubproject)
73
74     # project_view can read, but cannot see write or see writers list
75     set_user_from_auth :project_viewer
76     assert_equal([group.owner_uuid],
77                  group.writable_by,
78                  "writers list should just have owner_uuid")
79
80     # allow project_viewer to write for the remainder of the test
81     set_user_from_auth :admin
82     Link.create!(tail_uuid: invited_to_write.uuid,
83                  head_uuid: group.uuid,
84                  link_class: 'permission',
85                  name: 'can_write')
86     group.permissions.reload
87
88     # project_viewer should see self in writers list (but not all writers)
89     set_user_from_auth :project_viewer
90     assert_not_nil(group.writable_by,
91                     "can write but cannot see writers list")
92     assert_includes(group.writable_by, invited_to_write.uuid,
93                     "self missing from writers list")
94     assert_includes(group.writable_by, group.owner_uuid,
95                     "project owner missing from writers list")
96     refute_includes(group.writable_by, users(:active).uuid,
97                     "saw :active user in writers list")
98
99     # active user should see full writers list
100     set_user_from_auth :active
101     assert_includes(group.writable_by, invited_to_write.uuid,
102                     "permission just added, but missing from writers list")
103
104     # allow project_viewer to manage for the remainder of the test
105     set_user_from_auth :admin
106     Link.create!(tail_uuid: invited_to_write.uuid,
107                  head_uuid: group.uuid,
108                  link_class: 'permission',
109                  name: 'can_manage')
110     # invite another writer we can test for
111     Link.create!(tail_uuid: users(:spectator).uuid,
112                  head_uuid: group.uuid,
113                  link_class: 'permission',
114                  name: 'can_write')
115     group.permissions.reload
116
117     set_user_from_auth :project_viewer
118     assert_not_nil(group.writable_by,
119                     "can manage but cannot see writers list")
120     assert_includes(group.writable_by, users(:spectator).uuid,
121                     ":spectator missing from writers list")
122   end
123
124   test "user owns group, group can_manage object's group, user can add permissions" do
125     set_user_from_auth :admin
126
127     owner_grp = Group.create!(owner_uuid: users(:active).uuid)
128
129     sp_grp = Group.create!
130     sp = Specimen.create!(owner_uuid: sp_grp.uuid)
131
132     Link.create!(link_class: 'permission',
133                  name: 'can_manage',
134                  tail_uuid: owner_grp.uuid,
135                  head_uuid: sp_grp.uuid)
136
137     # active user owns owner_grp, which has can_manage permission on sp_grp
138     # user should be able to add permissions on sp.
139     set_user_from_auth :active_trustedclient
140     test_perm = Link.create(tail_uuid: users(:active).uuid,
141                             head_uuid: sp.uuid,
142                             link_class: 'permission',
143                             name: 'can_write')
144     assert test_perm.save, "could not save new permission on target object"
145     assert test_perm.destroy, "could not delete new permission on target object"
146   end
147
148   # bug #3091
149   skip "can_manage permission on a non-group object" do
150     set_user_from_auth :admin
151
152     ob = Specimen.create!
153     # grant can_manage permission to active
154     perm_link = Link.create!(tail_uuid: users(:active).uuid,
155                              head_uuid: ob.uuid,
156                              link_class: 'permission',
157                              name: 'can_manage')
158     # ob is owned by :admin, the link is owned by root
159     assert_equal users(:admin).uuid, ob.owner_uuid
160     assert_equal system_user_uuid, perm_link.owner_uuid
161
162     # user "active" can modify the permission link
163     set_user_from_auth :active_trustedclient
164     perm_link.properties["foo"] = 'bar'
165     assert perm_link.save, "could not save modified link"
166
167     assert_equal 'bar', perm_link.properties['foo'], "link properties do not include foo = bar"
168   end
169
170   test "user without can_manage permission may not modify permission link" do
171     set_user_from_auth :admin
172
173     ob = Specimen.create!
174     # grant can_manage permission to active
175     perm_link = Link.create!(tail_uuid: users(:active).uuid,
176                              head_uuid: ob.uuid,
177                              link_class: 'permission',
178                              name: 'can_read')
179     # ob is owned by :admin, the link is owned by root
180     assert_equal ob.owner_uuid, users(:admin).uuid
181     assert_equal perm_link.owner_uuid, system_user_uuid
182
183     # user "active" may not modify the permission link
184     set_user_from_auth :active_trustedclient
185     perm_link.name = 'can_manage'
186     assert_raises ArvadosModel::PermissionDeniedError do
187       perm_link.save
188     end
189   end
190
191   test "manager user gets permission to minions' articles via can_manage link" do
192     manager = create :active_user, first_name: "Manage", last_name: "Er"
193     minion = create :active_user, first_name: "Min", last_name: "Ion"
194     minions_specimen = act_as_user minion do
195       Specimen.create!
196     end
197     # Manager creates a group. (Make sure it doesn't magically give
198     # anyone any additional permissions.)
199     g = nil
200     act_as_user manager do
201       g = create :group, name: "NoBigSecret Lab"
202       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
203                    "saw a user I shouldn't see")
204       assert_raises(ArvadosModel::PermissionDeniedError,
205                     ActiveRecord::RecordInvalid,
206                     "gave can_read permission to a user I shouldn't see") do
207         create(:permission_link,
208                name: 'can_read', tail_uuid: minion.uuid, head_uuid: g.uuid)
209       end
210       %w(can_manage can_write can_read).each do |perm_type|
211         assert_raises(ArvadosModel::PermissionDeniedError,
212                       ActiveRecord::RecordInvalid,
213                       "escalated privileges") do
214           create(:permission_link,
215                  name: perm_type, tail_uuid: g.uuid, head_uuid: minion.uuid)
216         end
217       end
218       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
219                    "manager saw minion too soon")
220       assert_empty(User.readable_by(minion).where(uuid: manager.uuid),
221                    "minion saw manager too soon")
222       assert_empty(Group.readable_by(minion).where(uuid: g.uuid),
223                    "minion saw manager's new NoBigSecret Lab group too soon")
224
225       # Manager declares everybody on the system should be able to see
226       # the NoBigSecret Lab group.
227       create(:permission_link,
228              name: 'can_read',
229              tail_uuid: 'zzzzz-j7d0g-fffffffffffffff',
230              head_uuid: g.uuid)
231       # ...but nobody has joined the group yet. Manager still can't see
232       # minion.
233       assert_empty(User.readable_by(manager).where(uuid: minion.uuid),
234                    "manager saw minion too soon")
235     end
236
237     act_as_user minion do
238       # Minion can see the group.
239       assert_not_empty(Group.readable_by(minion).where(uuid: g.uuid),
240                        "minion could not see the NoBigSecret Lab group")
241       # Minion joins the group.
242       create(:permission_link,
243              name: 'can_read',
244              tail_uuid: g.uuid,
245              head_uuid: minion.uuid)
246     end
247
248     act_as_user manager do
249       # Now, manager can see minion.
250       assert_not_empty(User.readable_by(manager).where(uuid: minion.uuid),
251                        "manager could not see minion")
252       # But cannot obtain further privileges this way.
253       assert_raises(ArvadosModel::PermissionDeniedError,
254                     "escalated privileges") do
255         create(:permission_link,
256                name: 'can_manage', tail_uuid: manager.uuid, head_uuid: minion.uuid)
257       end
258       assert_empty(Specimen
259                      .readable_by(manager)
260                      .where(uuid: minions_specimen.uuid),
261                    "manager saw the minion's private stuff")
262       assert_raises(ArvadosModel::PermissionDeniedError,
263                    "manager could update minion's private stuff") do
264         minions_specimen.update_attributes(properties: {'x' => 'y'})
265       end
266     end
267
268     act_as_system_user do
269       # Root can give Manager more privileges over Minion.
270       create(:permission_link,
271              name: 'can_manage', tail_uuid: g.uuid, head_uuid: minion.uuid)
272     end
273
274     act_as_user manager do
275       # Now, manager can read and write Minion's stuff.
276       assert_not_empty(Specimen
277                          .readable_by(manager)
278                          .where(uuid: minions_specimen.uuid),
279                        "manager could not find minion's specimen by uuid")
280       assert_equal(true,
281                    minions_specimen.update_attributes(properties: {'x' => 'y'}),
282                    "manager could not update minion's specimen object")
283     end
284   end
285
286   test "users with bidirectional read permission in group can see each other, but cannot see each other's private articles" do
287     a = create :active_user, first_name: "A"
288     b = create :active_user, first_name: "B"
289     other = create :active_user, first_name: "OTHER"
290     act_as_system_user do
291       g = create :group
292       [a,b].each do |u|
293         create(:permission_link,
294                name: 'can_read', tail_uuid: u.uuid, head_uuid: g.uuid)
295         create(:permission_link,
296                name: 'can_read', head_uuid: u.uuid, tail_uuid: g.uuid)
297       end
298     end
299     a_specimen = act_as_user a do
300       Specimen.create!
301     end
302     assert_not_empty(Specimen.readable_by(a).where(uuid: a_specimen.uuid),
303                      "A cannot read own Specimen, following test probably useless.")
304     assert_empty(Specimen.readable_by(b).where(uuid: a_specimen.uuid),
305                  "B can read A's Specimen")
306     [a,b].each do |u|
307       assert_empty(User.readable_by(u).where(uuid: other.uuid),
308                    "#{u.first_name} can see OTHER in the user list")
309       assert_empty(User.readable_by(other).where(uuid: u.uuid),
310                    "OTHER can see #{u.first_name} in the user list")
311       act_as_user u do
312         assert_raises ArvadosModel::PermissionDeniedError, "wrote without perm" do
313           other.update_attributes!(prefs: {'pwned' => true})
314         end
315         assert_equal(true, u.update_attributes!(prefs: {'thisisme' => true}),
316                      "#{u.first_name} can't update its own prefs")
317       end
318       act_as_user other do
319         assert_raises(ArvadosModel::PermissionDeniedError,
320                         "OTHER wrote #{u.first_name} without perm") do
321           u.update_attributes!(prefs: {'pwned' => true})
322         end
323         assert_equal(true, other.update_attributes!(prefs: {'thisisme' => true}),
324                      "OTHER can't update its own prefs")
325       end
326     end
327   end
328
329   test "cannot create with owner = unwritable user" do
330     set_user_from_auth :rominiadmin
331     assert_raises ArvadosModel::PermissionDeniedError, "created with owner = unwritable user" do
332       Specimen.create!(owner_uuid: users(:active).uuid)
333     end
334   end
335
336   test "cannot change owner to unwritable user" do
337     set_user_from_auth :rominiadmin
338     ob = Specimen.create!
339     assert_raises ArvadosModel::PermissionDeniedError, "changed owner to unwritable user" do
340       ob.update_attributes!(owner_uuid: users(:active).uuid)
341     end
342   end
343
344   test "cannot create with owner = unwritable group" do
345     set_user_from_auth :rominiadmin
346     assert_raises ArvadosModel::PermissionDeniedError, "created with owner = unwritable group" do
347       Specimen.create!(owner_uuid: groups(:aproject).uuid)
348     end
349   end
350
351   test "cannot change owner to unwritable group" do
352     set_user_from_auth :rominiadmin
353     ob = Specimen.create!
354     assert_raises ArvadosModel::PermissionDeniedError, "changed owner to unwritable group" do
355       ob.update_attributes!(owner_uuid: groups(:aproject).uuid)
356     end
357   end
358
359   def container_logs(container, user)
360     Log.readable_by(users(user)).
361       where(object_uuid: containers(container).uuid, event_type: "test")
362   end
363
364   test "container logs created by dispatch are visible to container requestor" do
365     set_user_from_auth :dispatch1
366     Log.create!(object_uuid: containers(:running).uuid,
367                 event_type: "test")
368
369     assert_not_empty container_logs(:running, :admin)
370     assert_not_empty container_logs(:running, :active)
371     assert_empty container_logs(:running, :spectator)
372   end
373
374   test "container logs created by dispatch are public if container request is public" do
375     set_user_from_auth :dispatch1
376     Log.create!(object_uuid: containers(:running_older).uuid,
377                 event_type: "test")
378
379     assert_not_empty container_logs(:running_older, :anonymous)
380   end
381 end