9278: Ensure locator signatures expire no later than expires_at.
[arvados.git] / services / api / test / unit / collection_test.rb
index 7ce73eb1d5bc039e47f8d84de1af3b33e25297ae..91568927ae37654117da4dec7c811882818d0add 100644 (file)
@@ -1,6 +1,8 @@
 require 'test_helper'
 
 class CollectionTest < ActiveSupport::TestCase
+  include DbCurrentTime
+
   def create_collection name, enc=nil
     txt = ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:#{name}.txt\n"
     txt.force_encoding(enc) if enc
@@ -39,6 +41,66 @@ class CollectionTest < ActiveSupport::TestCase
     end
   end
 
+  [
+    ". 0:0:foo.txt",
+    ". d41d8cd98f00b204e9800998ecf8427e foo.txt",
+    "d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
+    ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
+  ].each do |manifest_text|
+    test "create collection with invalid manifest text #{manifest_text} and expect error" do
+      act_as_system_user do
+        c = Collection.create(manifest_text: manifest_text)
+        assert !c.valid?
+      end
+    end
+  end
+
+  [
+    nil,
+    "",
+    ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",
+  ].each do |manifest_text|
+    test "create collection with valid manifest text #{manifest_text.inspect} and expect success" do
+      act_as_system_user do
+        c = Collection.create(manifest_text: manifest_text)
+        assert c.valid?
+      end
+    end
+  end
+
+  [
+    ". 0:0:foo.txt",
+    ". d41d8cd98f00b204e9800998ecf8427e foo.txt",
+    "d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
+    ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
+  ].each do |manifest_text|
+    test "update collection with invalid manifest text #{manifest_text} and expect error" do
+      act_as_system_user do
+        c = create_collection 'foo', Encoding::US_ASCII
+        assert c.valid?
+
+        c.update_attribute 'manifest_text', manifest_text
+        assert !c.valid?
+      end
+    end
+  end
+
+  [
+    nil,
+    "",
+    ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",
+  ].each do |manifest_text|
+    test "update collection with valid manifest text #{manifest_text.inspect} and expect success" do
+      act_as_system_user do
+        c = create_collection 'foo', Encoding::US_ASCII
+        assert c.valid?
+
+        c.update_attribute 'manifest_text', manifest_text
+        assert c.valid?
+      end
+    end
+  end
+
   test 'create and update collection and verify file_names' do
     act_as_system_user do
       c = create_collection 'foo', Encoding::US_ASCII
@@ -56,27 +118,27 @@ class CollectionTest < ActiveSupport::TestCase
   [
     [2**8, false],
     [2**18, true],
-  ].each do |manifest_size, gets_truncated|
-    test "create collection with manifest size #{manifest_size} which gets truncated #{gets_truncated},
+  ].each do |manifest_size, allow_truncate|
+    test "create collection with manifest size #{manifest_size} with allow_truncate=#{allow_truncate},
           and not expect exceptions even on very large manifest texts" do
       # file_names has a max size, hence there will be no errors even on large manifests
       act_as_system_user do
-        manifest_text = './blurfl d41d8cd98f00b204e9800998ecf8427e+0'
+        manifest_text = ''
         index = 0
         while manifest_text.length < manifest_size
-          manifest_text += './subdir1 d41d8cd98f00b204e9800998ecf8427e+0' if index > 0
-          manifest_text += ' ' + "0:0:veryverylongfilename000000000000#{index}.txt\n"
+          manifest_text += "./blurfl d41d8cd98f00b204e9800998ecf8427e+0 0:0:veryverylongfilename000000000000#{index}.txt\n"
           index += 1
         end
+        manifest_text += "./laststreamname d41d8cd98f00b204e9800998ecf8427e+0 0:0:veryverylastfilename.txt\n"
         c = Collection.create(manifest_text: manifest_text)
 
         assert c.valid?
         assert c.file_names
         assert_match /veryverylongfilename0000000000001.txt/, c.file_names
         assert_match /veryverylongfilename0000000000002.txt/, c.file_names
-        if !gets_truncated
-          assert_match /blurfl/, c.file_names
-          assert_match /subdir1/, c.file_names
+        if not allow_truncate
+          assert_match /veryverylastfilename/, c.file_names
+          assert_match /laststreamname/, c.file_names
         end
       end
     end
@@ -245,6 +307,29 @@ class CollectionTest < ActiveSupport::TestCase
     end
   end
 
+  test 'signature expiry does not exceed expires_at' do
+    act_as_user users(:active) do
+      t0 = db_current_time
+      c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n", name: 'foo')
+      c.update_attributes! expires_at: (t0 + 1.hours)
+      c.reload
+      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
+      assert_operator sig_exp.to_i, :<=, (t0 + 1.hours).to_i
+    end
+  end
+
+  test 'far-future expiry date cannot be used to circumvent configured permission ttl' do
+    act_as_user users(:active) do
+      c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n",
+                             name: 'foo',
+                             expires_at: db_current_time + 1.years)
+      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
+      expect_max_sig_exp = db_current_time.to_i + Rails.configuration.blob_signature_ttl
+      assert_operator c.expires_at.to_i, :>, expect_max_sig_exp
+      assert_operator sig_exp.to_i, :<=, expect_max_sig_exp
+    end
+  end
+
   test "create collection with properties" do
     act_as_system_user do
       c = Collection.create(manifest_text: ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo\n",
@@ -280,4 +365,14 @@ class CollectionTest < ActiveSupport::TestCase
     coll_uuids = coll_list.map(&:uuid)
     assert_includes(coll_uuids, collections(:docker_image).uuid)
   end
+
+  test 'expires_at cannot be set too far in the past' do
+    act_as_user users(:active) do
+      t0 = db_current_time
+      c = Collection.create!(manifest_text: '', name: 'foo')
+      c.update_attributes! expires_at: (t0 - 2.weeks)
+      c.reload
+      assert_operator c.expires_at, :>, t0
+    end
+  end
 end