Merge branch '8784-dir-listings'
[arvados.git] / services / api / test / functional / arvados / v1 / links_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
7 class Arvados::V1::LinksControllerTest < ActionController::TestCase
8
9   ['link', 'link_json'].each do |formatted_link|
10     test "no symbol keys in serialized hash #{formatted_link}" do
11       link = {
12         properties: {username: 'testusername'},
13         link_class: 'test',
14         name: 'encoding',
15         tail_uuid: users(:admin).uuid,
16         head_uuid: virtual_machines(:testvm).uuid
17       }
18       authorize_with :admin
19       if formatted_link == 'link_json'
20         post :create, link: link.to_json
21       else
22         post :create, link: link
23       end
24       assert_response :success
25       assert_not_nil assigns(:object)
26       assert_equal 'testusername', assigns(:object).properties['username']
27       assert_equal false, assigns(:object).properties.has_key?(:username)
28     end
29   end
30
31   %w(created_at modified_at).each do |attr|
32     {nil: nil, bogus: 2.days.ago}.each do |bogustype, bogusvalue|
33       test "cannot set #{bogustype} #{attr} in create" do
34         authorize_with :active
35         post :create, {
36           link: {
37             properties: {},
38             link_class: 'test',
39             name: 'test',
40           }.merge(attr => bogusvalue)
41         }
42         assert_response :success
43         resp = JSON.parse @response.body
44         assert_in_delta Time.now, Time.parse(resp[attr]), 3.0
45       end
46       test "cannot set #{bogustype} #{attr} in update" do
47         really_created_at = links(:test_timestamps).created_at
48         authorize_with :active
49         put :update, {
50           id: links(:test_timestamps).uuid,
51           link: {
52             :properties => {test: 'test'},
53             attr => bogusvalue
54           }
55         }
56         assert_response :success
57         resp = JSON.parse @response.body
58         case attr
59         when 'created_at'
60           assert_in_delta really_created_at, Time.parse(resp[attr]), 0.001
61         else
62           assert_in_delta Time.now, Time.parse(resp[attr]), 3.0
63         end
64       end
65     end
66   end
67
68   test "head must exist" do
69     link = {
70       link_class: 'test',
71       name: 'stuff',
72       tail_uuid: users(:active).uuid,
73       head_uuid: 'zzzzz-tpzed-xyzxyzxerrrorxx'
74     }
75     authorize_with :admin
76     post :create, link: link
77     assert_response 422
78   end
79
80   test "tail must exist" do
81     link = {
82       link_class: 'test',
83       name: 'stuff',
84       head_uuid: users(:active).uuid,
85       tail_uuid: 'zzzzz-tpzed-xyzxyzxerrrorxx'
86     }
87     authorize_with :admin
88     post :create, link: link
89     assert_response 422
90   end
91
92   test "head and tail exist, head_kind and tail_kind are returned" do
93     link = {
94       link_class: 'test',
95       name: 'stuff',
96       head_uuid: users(:active).uuid,
97       tail_uuid: users(:spectator).uuid,
98     }
99     authorize_with :admin
100     post :create, link: link
101     assert_response :success
102     l = JSON.parse(@response.body)
103     assert 'arvados#user', l['head_kind']
104     assert 'arvados#user', l['tail_kind']
105   end
106
107   test "can supply head_kind and tail_kind without error" do
108     link = {
109       link_class: 'test',
110       name: 'stuff',
111       head_uuid: users(:active).uuid,
112       tail_uuid: users(:spectator).uuid,
113       head_kind: "arvados#user",
114       tail_kind: "arvados#user",
115     }
116     authorize_with :admin
117     post :create, link: link
118     assert_response :success
119     l = JSON.parse(@response.body)
120     assert 'arvados#user', l['head_kind']
121     assert 'arvados#user', l['tail_kind']
122   end
123
124   test "tail must be visible by user" do
125     link = {
126       link_class: 'test',
127       name: 'stuff',
128       head_uuid: users(:active).uuid,
129       tail_uuid: authorized_keys(:admin).uuid,
130     }
131     authorize_with :active
132     post :create, link: link
133     assert_response 422
134   end
135
136   test "filter links with 'is_a' operator" do
137     authorize_with :admin
138     get :index, {
139       filters: [ ['tail_uuid', 'is_a', 'arvados#user'] ]
140     }
141     assert_response :success
142     found = assigns(:objects)
143     assert_not_equal 0, found.count
144     assert_equal found.count, (found.select { |f| f.tail_uuid.match User.uuid_regex }).count
145   end
146
147   test "filter links with 'is_a' operator with more than one" do
148     authorize_with :admin
149     get :index, {
150       filters: [ ['tail_uuid', 'is_a', ['arvados#user', 'arvados#group'] ] ],
151     }
152     assert_response :success
153     found = assigns(:objects)
154     assert_not_equal 0, found.count
155     assert_equal found.count, (found.select { |f|
156                                  f.tail_uuid.match User.uuid_regex or
157                                  f.tail_uuid.match Group.uuid_regex
158                                }).count
159   end
160
161   test "filter links with 'is_a' operator with bogus type" do
162     authorize_with :admin
163     get :index, {
164       filters: [ ['tail_uuid', 'is_a', ['arvados#bogus'] ] ],
165     }
166     assert_response :success
167     found = assigns(:objects)
168     assert_equal 0, found.count
169   end
170
171   test "filter links with 'is_a' operator with collection" do
172     authorize_with :admin
173     get :index, {
174       filters: [ ['head_uuid', 'is_a', ['arvados#collection'] ] ],
175     }
176     assert_response :success
177     found = assigns(:objects)
178     assert_not_equal 0, found.count
179     assert_equal found.count, (found.select { |f| f.head_uuid.match Collection.uuid_regex}).count
180   end
181
182   test "test can still use where tail_kind" do
183     authorize_with :admin
184     get :index, {
185       where: { tail_kind: 'arvados#user' }
186     }
187     assert_response :success
188     found = assigns(:objects)
189     assert_not_equal 0, found.count
190     assert_equal found.count, (found.select { |f| f.tail_uuid.match User.uuid_regex }).count
191   end
192
193   test "test can still use where head_kind" do
194     authorize_with :admin
195     get :index, {
196       where: { head_kind: 'arvados#user' }
197     }
198     assert_response :success
199     found = assigns(:objects)
200     assert_not_equal 0, found.count
201     assert_equal found.count, (found.select { |f| f.head_uuid.match User.uuid_regex }).count
202   end
203
204   test "test can still use filter tail_kind" do
205     authorize_with :admin
206     get :index, {
207       filters: [ ['tail_kind', '=', 'arvados#user'] ]
208     }
209     assert_response :success
210     found = assigns(:objects)
211     assert_not_equal 0, found.count
212     assert_equal found.count, (found.select { |f| f.tail_uuid.match User.uuid_regex }).count
213   end
214
215   test "test can still use filter head_kind" do
216     authorize_with :admin
217     get :index, {
218       filters: [ ['head_kind', '=', 'arvados#user'] ]
219     }
220     assert_response :success
221     found = assigns(:objects)
222     assert_not_equal 0, found.count
223     assert_equal found.count, (found.select { |f| f.head_uuid.match User.uuid_regex }).count
224   end
225
226   test "head_kind matches head_uuid" do
227     link = {
228       link_class: 'test',
229       name: 'stuff',
230       head_uuid: groups(:public).uuid,
231       head_kind: "arvados#user",
232       tail_uuid: users(:spectator).uuid,
233       tail_kind: "arvados#user",
234     }
235     authorize_with :admin
236     post :create, link: link
237     assert_response 422
238   end
239
240   test "tail_kind matches tail_uuid" do
241     link = {
242       link_class: 'test',
243       name: 'stuff',
244       head_uuid: users(:active).uuid,
245       head_kind: "arvados#user",
246       tail_uuid: groups(:public).uuid,
247       tail_kind: "arvados#user",
248     }
249     authorize_with :admin
250     post :create, link: link
251     assert_response 422
252   end
253
254   test "test with virtual_machine" do
255     link = {
256       tail_kind: "arvados#user",
257       tail_uuid: users(:active).uuid,
258       head_kind: "arvados#virtual_machine",
259       head_uuid: virtual_machines(:testvm).uuid,
260       link_class: "permission",
261       name: "can_login",
262       properties: {username: "repo_and_user_name"}
263     }
264     authorize_with :admin
265     post :create, link: link
266     assert_response 422
267   end
268
269   test "test with virtualMachine" do
270     link = {
271       tail_kind: "arvados#user",
272       tail_uuid: users(:active).uuid,
273       head_kind: "arvados#virtualMachine",
274       head_uuid: virtual_machines(:testvm).uuid,
275       link_class: "permission",
276       name: "can_login",
277       properties: {username: "repo_and_user_name"}
278     }
279     authorize_with :admin
280     post :create, link: link
281     assert_response :success
282   end
283
284   test "project owner can show a project permission" do
285     uuid = links(:project_viewer_can_read_project).uuid
286     authorize_with :active
287     get :show, id: uuid
288     assert_response :success
289     assert_equal(uuid, assigns(:object).andand.uuid)
290   end
291
292   test "admin can show a project permission" do
293     uuid = links(:project_viewer_can_read_project).uuid
294     authorize_with :admin
295     get :show, id: uuid
296     assert_response :success
297     assert_equal(uuid, assigns(:object).andand.uuid)
298   end
299
300   test "project viewer can't show others' project permissions" do
301     authorize_with :project_viewer
302     get :show, id: links(:admin_can_write_aproject).uuid
303     assert_response 404
304   end
305
306   test "requesting a nonexistent link returns 404" do
307     authorize_with :active
308     get :show, id: 'zzzzz-zzzzz-zzzzzzzzzzzzzzz'
309     assert_response 404
310   end
311
312   # not implemented
313   skip "retrieve all permissions using generic links index api" do
314     # Links.readable_by() does not return the full set of permission
315     # links that are visible to a user (i.e., all permission links
316     # whose head_uuid references an object for which the user has
317     # ownership or can_manage permission). Therefore, neither does
318     # /arvados/v1/links.
319     #
320     # It is possible to retrieve the full set of permissions for a
321     # single object via /arvados/v1/permissions.
322     authorize_with :active
323     get :index, filters: [['link_class', '=', 'permission'],
324                           ['head_uuid', '=', groups(:aproject).uuid]]
325     assert_response :success
326     assert_not_nil assigns(:objects)
327     assert_includes(assigns(:objects).map(&:uuid),
328                     links(:project_viewer_can_read_project).uuid)
329   end
330
331   test "admin can index project permissions" do
332     authorize_with :admin
333     get :index, filters: [['link_class', '=', 'permission'],
334                           ['head_uuid', '=', groups(:aproject).uuid]]
335     assert_response :success
336     assert_not_nil assigns(:objects)
337     assert_includes(assigns(:objects).map(&:uuid),
338                     links(:project_viewer_can_read_project).uuid)
339   end
340
341   test "project viewer can't index others' project permissions" do
342     authorize_with :project_viewer
343     get :index, filters: [['link_class', '=', 'permission'],
344                           ['head_uuid', '=', groups(:aproject).uuid],
345                           ['tail_uuid', '!=', users(:project_viewer).uuid]]
346     assert_response :success
347     assert_not_nil assigns(:objects)
348     assert_empty assigns(:objects)
349   end
350
351   # Granting permissions.
352   test "grant can_read on project to other users in group" do
353     authorize_with :user_foo_in_sharing_group
354
355     refute users(:user_bar_in_sharing_group).can?(read: collections(:collection_owned_by_foo).uuid)
356
357     post :create, {
358       link: {
359         tail_uuid: users(:user_bar_in_sharing_group).uuid,
360         link_class: 'permission',
361         name: 'can_read',
362         head_uuid: collections(:collection_owned_by_foo).uuid,
363       }
364     }
365     assert_response :success
366     assert users(:user_bar_in_sharing_group).can?(read: collections(:collection_owned_by_foo).uuid)
367   end
368 end