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