3 class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
5 # StoppedClock.now always returns the same timestamp.
6 # Set the Blob permission signing clock to ensure that
7 # all permission hints use consistent timestamps for testing.
10 @@cached_timestamp = Time.now
12 return @@cached_timestamp
17 Blob.set_clock(StoppedClock)
24 test "should get index" do
25 authorize_with :active
27 assert_response :success
28 assert_not_nil assigns(:objects)
31 [0,1,2].each do |limit|
32 test "get index with limit=#{limit}" do
33 authorize_with :active
34 get :index, limit: limit
35 assert_response :success
36 assert_equal limit, assigns(:objects).count
37 resp = JSON.parse(@response.body)
38 assert_equal limit, resp['limit']
42 test "items.count == items_available" do
43 authorize_with :active
44 get :index, limit: 100000
45 assert_response :success
46 resp = JSON.parse(@response.body)
47 assert_equal resp['items_available'], assigns(:objects).length
48 assert_equal resp['items_available'], resp['items'].count
49 unique_uuids = resp['items'].collect { |i| i['uuid'] }.compact.uniq
50 assert_equal unique_uuids.count, resp['items'].count
53 test "get index with limit=2 offset=99999" do
54 # Assume there are not that many test fixtures.
55 authorize_with :active
56 get :index, limit: 2, offset: 99999
57 assert_response :success
58 assert_equal 0, assigns(:objects).count
59 resp = JSON.parse(@response.body)
60 assert_equal 2, resp['limit']
61 assert_equal 99999, resp['offset']
64 test "should create" do
65 authorize_with :active
68 . d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo.txt
69 . acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
70 . acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
71 ./baz acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
74 test_collection[:uuid] =
75 Digest::MD5.hexdigest(test_collection[:manifest_text]) +
77 test_collection[:manifest_text].length.to_s
79 collection: test_collection
81 assert_response :success
82 assert_nil assigns(:objects)
85 id: test_collection[:uuid]
87 assert_response :success
88 assert_not_nil assigns(:object)
89 resp = JSON.parse(@response.body)
90 assert_equal test_collection[:uuid], resp['uuid']
91 assert_equal test_collection[:manifest_text], resp['manifest_text']
92 assert_equal 9, resp['data_size']
93 assert_equal [['.', 'foo.txt', 0],
95 ['./baz', 'bar.txt', 3]], resp['files']
98 test "list of files is correct for empty manifest" do
99 authorize_with :active
102 uuid: "d41d8cd98f00b204e9800998ecf8427e+0"
105 collection: test_collection
107 assert_response :success
110 id: "d41d8cd98f00b204e9800998ecf8427e+0"
112 assert_response :success
113 resp = JSON.parse(@response.body)
114 assert_equal [], resp['files']
117 test "create with owner_uuid set to owned group" do
118 authorize_with :active
119 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
122 owner_uuid: 'zzzzz-j7d0g-rew6elm53kancon',
123 manifest_text: manifest_text,
124 uuid: "d30fe8ae534397864cb96c544f4cf102"
127 assert_response :success
128 resp = JSON.parse(@response.body)
129 assert_equal 'zzzzz-tpzed-000000000000000', resp['owner_uuid']
132 test "create with owner_uuid set to group i can_manage" do
133 authorize_with :active
134 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
137 owner_uuid: 'zzzzz-j7d0g-8ulrifv67tve5sx',
138 manifest_text: manifest_text,
139 uuid: "d30fe8ae534397864cb96c544f4cf102"
142 assert_response :success
143 resp = JSON.parse(@response.body)
144 assert_equal 'zzzzz-tpzed-000000000000000', resp['owner_uuid']
147 test "create with owner_uuid set to group with no can_manage permission" do
148 authorize_with :active
149 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
152 owner_uuid: 'zzzzz-j7d0g-it30l961gq3t0oi',
153 manifest_text: manifest_text,
154 uuid: "d30fe8ae534397864cb96c544f4cf102"
160 test "admin create with owner_uuid set to group with no permission" do
161 authorize_with :admin
162 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
165 owner_uuid: 'zzzzz-j7d0g-it30l961gq3t0oi',
166 manifest_text: manifest_text,
167 uuid: "d30fe8ae534397864cb96c544f4cf102"
170 assert_response :success
173 test "should create with collection passed as json" do
174 authorize_with :active
178 "manifest_text":". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",\
179 "uuid":"d30fe8ae534397864cb96c544f4cf102"\
183 assert_response :success
186 test "should fail to create with checksum mismatch" do
187 authorize_with :active
191 "manifest_text":". d41d8cd98f00b204e9800998ecf8427e 0:0:bar.txt\n",\
192 "uuid":"d30fe8ae534397864cb96c544f4cf102"\
199 test "collection UUID is normalized when created" do
200 authorize_with :active
203 manifest_text: ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",
204 uuid: "d30fe8ae534397864cb96c544f4cf102+47+Khint+Xhint+Zhint"
207 assert_response :success
208 assert_not_nil assigns(:object)
209 resp = JSON.parse(@response.body)
210 assert_equal "d30fe8ae534397864cb96c544f4cf102+47", resp['uuid']
213 test "get full provenance for baz file" do
214 authorize_with :active
215 get :provenance, id: 'ea10d51bcf88862dbcc36eb292017dfd+45'
216 assert_response :success
217 resp = JSON.parse(@response.body)
218 assert_not_nil resp['ea10d51bcf88862dbcc36eb292017dfd+45'] # baz
219 assert_not_nil resp['fa7aeb5140e2848d39b416daeef4ffc5+45'] # bar
220 assert_not_nil resp['1f4b0bc7583c2a7f9102c395f4ffc5e3+45'] # foo
221 assert_not_nil resp['zzzzz-8i9sb-cjs4pklxxjykyuq'] # bar->baz
222 assert_not_nil resp['zzzzz-8i9sb-aceg2bnq7jt7kon'] # foo->bar
225 test "get no provenance for foo file" do
226 # spectator user cannot even see baz collection
227 authorize_with :spectator
228 get :provenance, id: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
232 test "get partial provenance for baz file" do
233 # spectator user can see bar->baz job, but not foo->bar job
234 authorize_with :spectator
235 get :provenance, id: 'ea10d51bcf88862dbcc36eb292017dfd+45'
236 assert_response :success
237 resp = JSON.parse(@response.body)
238 assert_not_nil resp['ea10d51bcf88862dbcc36eb292017dfd+45'] # baz
239 assert_not_nil resp['fa7aeb5140e2848d39b416daeef4ffc5+45'] # bar
240 assert_not_nil resp['zzzzz-8i9sb-cjs4pklxxjykyuq'] # bar->baz
241 assert_nil resp['zzzzz-8i9sb-aceg2bnq7jt7kon'] # foo->bar
242 assert_nil resp['1f4b0bc7583c2a7f9102c395f4ffc5e3+45'] # foo
245 test "search collections with 'any' operator" do
246 authorize_with :active
248 where: { any: ['contains', '7f9102c395f4ffc5e3'] }
250 assert_response :success
251 found = assigns(:objects).collect(&:uuid)
252 assert_equal 1, found.count
253 assert_equal true, !!found.index('1f4b0bc7583c2a7f9102c395f4ffc5e3+45')
256 test "create collection with signed manifest" do
257 authorize_with :active
259 d41d8cd98f00b204e9800998ecf8427e+0
260 acbd18db4cc2f85cedef654fccc4a4d8+3
261 ea10d51bcf88862dbcc36eb292017dfd+45)
263 unsigned_manifest = locators.map { |loc|
264 ". " + loc + " 0:0:foo.txt\n"
266 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
268 unsigned_manifest.length.to_s
270 # build a manifest with both signed and unsigned locators.
271 # TODO(twp): in phase 4, all locators will need to be signed, so
272 # this test should break and will need to be rewritten. Issue #2755.
274 key: Rails.configuration.blob_signing_key,
275 api_token: api_token(:active),
278 ". " + locators[0] + " 0:0:foo.txt\n" +
279 ". " + Blob.sign_locator(locators[1], signing_opts) + " 0:0:foo.txt\n" +
280 ". " + Blob.sign_locator(locators[2], signing_opts) + " 0:0:foo.txt\n"
284 manifest_text: signed_manifest,
288 assert_response :success
289 assert_not_nil assigns(:object)
290 resp = JSON.parse(@response.body)
291 assert_equal manifest_uuid, resp['uuid']
292 assert_equal 48, resp['data_size']
293 # All of the locators in the output must be signed.
294 resp['manifest_text'].lines.each do |entry|
295 m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
297 assert Blob.verify_signature m[0], signing_opts
302 test "create collection with signed manifest and explicit TTL" do
303 authorize_with :active
305 d41d8cd98f00b204e9800998ecf8427e+0
306 acbd18db4cc2f85cedef654fccc4a4d8+3
307 ea10d51bcf88862dbcc36eb292017dfd+45)
309 unsigned_manifest = locators.map { |loc|
310 ". " + loc + " 0:0:foo.txt\n"
312 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
314 unsigned_manifest.length.to_s
316 # build a manifest with both signed and unsigned locators.
317 # TODO(twp): in phase 4, all locators will need to be signed, so
318 # this test should break and will need to be rewritten. Issue #2755.
320 key: Rails.configuration.blob_signing_key,
321 api_token: api_token(:active),
325 ". " + locators[0] + " 0:0:foo.txt\n" +
326 ". " + Blob.sign_locator(locators[1], signing_opts) + " 0:0:foo.txt\n" +
327 ". " + Blob.sign_locator(locators[2], signing_opts) + " 0:0:foo.txt\n"
331 manifest_text: signed_manifest,
335 assert_response :success
336 assert_not_nil assigns(:object)
337 resp = JSON.parse(@response.body)
338 assert_equal manifest_uuid, resp['uuid']
339 assert_equal 48, resp['data_size']
340 # All of the locators in the output must be signed.
341 resp['manifest_text'].lines.each do |entry|
342 m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
344 assert Blob.verify_signature m[0], signing_opts
349 test "create fails with invalid signature" do
350 authorize_with :active
352 key: Rails.configuration.blob_signing_key,
353 api_token: api_token(:active),
356 # Generate a locator with a bad signature.
357 unsigned_locator = "d41d8cd98f00b204e9800998ecf8427e+0"
358 bad_locator = unsigned_locator + "+Affffffff@ffffffff"
359 assert !Blob.verify_signature(bad_locator, signing_opts)
361 # Creating a collection with this locator should
362 # produce 403 Permission denied.
363 unsigned_manifest = ". #{unsigned_locator} 0:0:foo.txt\n"
364 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
366 unsigned_manifest.length.to_s
368 bad_manifest = ". #{bad_locator} 0:0:foo.txt\n"
371 manifest_text: bad_manifest,
379 test "create fails with uuid of signed manifest" do
380 authorize_with :active
382 key: Rails.configuration.blob_signing_key,
383 api_token: api_token(:active),
386 unsigned_locator = "d41d8cd98f00b204e9800998ecf8427e+0"
387 signed_locator = Blob.sign_locator(unsigned_locator, signing_opts)
388 signed_manifest = ". #{signed_locator} 0:0:foo.txt\n"
389 manifest_uuid = Digest::MD5.hexdigest(signed_manifest) +
391 signed_manifest.length.to_s
395 manifest_text: signed_manifest,
403 test "multiple locators per line" do
404 authorize_with :active
406 d41d8cd98f00b204e9800998ecf8427e+0
407 acbd18db4cc2f85cedef654fccc4a4d8+3
408 ea10d51bcf88862dbcc36eb292017dfd+45)
410 manifest_text = [".", *locators, "0:0:foo.txt\n"].join(" ")
411 manifest_uuid = Digest::MD5.hexdigest(manifest_text) +
413 manifest_text.length.to_s
417 manifest_text: manifest_text,
421 assert_response :success
422 assert_not_nil assigns(:object)
423 resp = JSON.parse(@response.body)
424 assert_equal manifest_uuid, resp['uuid']
425 assert_equal 48, resp['data_size']
426 assert_equal resp['manifest_text'], manifest_text
429 test "multiple signed locators per line" do
430 authorize_with :active
432 d41d8cd98f00b204e9800998ecf8427e+0
433 acbd18db4cc2f85cedef654fccc4a4d8+3
434 ea10d51bcf88862dbcc36eb292017dfd+45)
437 key: Rails.configuration.blob_signing_key,
438 api_token: api_token(:active),
441 unsigned_manifest = [".", *locators, "0:0:foo.txt\n"].join(" ")
442 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
444 unsigned_manifest.length.to_s
446 signed_locators = locators.map { |loc| Blob.sign_locator loc, signing_opts }
447 signed_manifest = [".", *signed_locators, "0:0:foo.txt\n"].join(" ")
451 manifest_text: signed_manifest,
455 assert_response :success
456 assert_not_nil assigns(:object)
457 resp = JSON.parse(@response.body)
458 assert_equal manifest_uuid, resp['uuid']
459 assert_equal 48, resp['data_size']
460 # All of the locators in the output must be signed.
461 # Each line is of the form "path locator locator ... 0:0:file.txt"
462 # entry.split[1..-2] will yield just the tokens in the middle of the line
463 returned_locator_count = 0
464 resp['manifest_text'].lines.each do |entry|
465 entry.split[1..-2].each do |tok|
466 returned_locator_count += 1
467 assert Blob.verify_signature tok, signing_opts
470 assert_equal locators.count, returned_locator_count