17217: Update RailsAPI tests to not expect signatures.
authorTom Clegg <tom@curii.com>
Thu, 26 Aug 2021 20:02:10 +0000 (16:02 -0400)
committerTom Clegg <tom@curii.com>
Fri, 27 Aug 2021 20:04:16 +0000 (16:04 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

services/api/app/controllers/arvados/v1/collections_controller.rb
services/api/app/models/collection.rb
services/api/test/functional/arvados/v1/collections_controller_test.rb
services/api/test/integration/collections_api_test.rb
services/api/test/unit/collection_performance_test.rb
services/api/test/unit/collection_test.rb

index 440ac640169404bc7a0fac4738f55febbd78c0cc..13b0261d94f2c309b9b6f279f74f5694e0fdcaef 100644 (file)
@@ -69,12 +69,12 @@ class Arvados::V1::CollectionsController < ApplicationController
         include_old_versions: params[:include_old_versions],
       }
 
-      # It matters which Collection object we pick because we use it to get signed_manifest_text,
-      # the value of which is affected by the value of trash_at.
+      # It matters which Collection object we pick because blob
+      # signatures depend on the value of trash_at.
       #
-      # From postgres doc: "By default, null values sort as if larger than any non-null
-      # value; that is, NULLS FIRST is the default for DESC order, and
-      # NULLS LAST otherwise."
+      # From postgres doc: "By default, null values sort as if larger
+      # than any non-null value; that is, NULLS FIRST is the default
+      # for DESC order, and NULLS LAST otherwise."
       #
       # "trash_at desc" sorts null first, then latest to earliest, so
       # it will select the Collection object with the longest
@@ -84,7 +84,7 @@ class Arvados::V1::CollectionsController < ApplicationController
         @object = {
           uuid: c.portable_data_hash,
           portable_data_hash: c.portable_data_hash,
-          manifest_text: c.signed_manifest_text,
+          manifest_text: c.manifest_text,
         }
       end
     else
index 2560ac2c98dcf454e715a1d85f1576087dcf21cc..a98cde4446d17e63e1e5e34db0bbc777f27f1903 100644 (file)
@@ -44,8 +44,8 @@ class Collection < ArvadosModel
     t.add :description
     t.add :properties
     t.add :portable_data_hash
-    t.add :signed_manifest_text, as: :manifest_text
     t.add :manifest_text, as: :unsigned_manifest_text
+    t.add :manifest_text, as: :manifest_text
     t.add :replication_desired
     t.add :replication_confirmed
     t.add :replication_confirmed_at
@@ -71,15 +71,11 @@ class Collection < ArvadosModel
 
   def self.attributes_required_columns
     super.merge(
-                # If we don't list manifest_text explicitly, the
-                # params[:select] code gets confused by the way we
-                # expose signed_manifest_text as manifest_text in the
-                # API response, and never let clients select the
-                # manifest_text column.
-                #
-                # We need trash_at and is_trashed to determine the
-                # correct timestamp in signed_manifest_text.
-                'manifest_text' => ['manifest_text', 'trash_at', 'is_trashed'],
+                # If we don't list unsigned_manifest_text explicitly,
+                # the params[:select] code gets confused by the way we
+                # expose manifest_text as unsigned_manifest_text in
+                # the API response, and never let clients select the
+                # unsigned_manifest_text column.
                 'unsigned_manifest_text' => ['manifest_text'],
                 'name' => ['name'],
                 )
@@ -406,7 +402,7 @@ class Collection < ArvadosModel
     end
   end
 
-  def signed_manifest_text
+  def signed_manifest_text_only_for_tests
     if !has_attribute? :manifest_text
       return nil
     elsif is_trashed
@@ -415,12 +411,22 @@ class Collection < ArvadosModel
       token = Thread.current[:token]
       exp = [db_current_time.to_i + Rails.configuration.Collections.BlobSigningTTL.to_i,
              trash_at].compact.map(&:to_i).min
-      self.class.sign_manifest manifest_text, token, exp
+      self.class.sign_manifest_only_for_tests manifest_text, token, exp
     end
   end
 
-  def self.sign_manifest manifest, token, exp=nil
-    return manifest
+  def self.sign_manifest_only_for_tests manifest, token, exp=nil
+    if exp.nil?
+      exp = db_current_time.to_i + Rails.configuration.Collections.BlobSigningTTL.to_i
+    end
+    signing_opts = {
+      api_token: token,
+      expire: exp,
+    }
+    m = munge_manifest_locators(manifest) do |match|
+      Blob.sign_locator(match[0], signing_opts)
+    end
+    return m
   end
 
   def self.munge_manifest_locators manifest
index 1ca2dd1dc109857e6987fdaa32caad2b04a52f8b..2c9470d9729455b347cb40438792640ae944b784 100644 (file)
@@ -32,8 +32,7 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
     end
   end
 
-  def assert_unsigned_manifest resp, label=''
-    txt = resp['unsigned_manifest_text']
+  def assert_unsigned_manifest txt, label=''
     assert_not_nil(txt, "#{label} unsigned_manifest_text was nil")
     locs = 0
     txt.scan(/ [[:xdigit:]]{32}\S*/) do |tok|
@@ -66,24 +65,16 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
            "past version not included on index")
   end
 
-  test "collections.get returns signed locators, and no unsigned_manifest_text" do
+  test "collections.get returns unsigned locators, and no unsigned_manifest_text" do
     permit_unsigned_manifests
     authorize_with :active
     get :show, params: {id: collections(:foo_file).uuid}
     assert_response :success
-    assert_signed_manifest json_response['manifest_text'], 'foo_file'
+    assert_unsigned_manifest json_response["manifest_text"], 'foo_file'
     refute_includes json_response, 'unsigned_manifest_text'
   end
 
   ['v1token', 'v2token'].each do |token_method|
-    test "correct signatures are given for #{token_method}" do
-      token = api_client_authorizations(:active).send(token_method)
-      authorize_with_token token
-      get :show, params: {id: collections(:foo_file).uuid}
-      assert_response :success
-      assert_signed_manifest json_response['manifest_text'], 'foo_file', token: token
-    end
-
     test "signatures with #{token_method} are accepted" do
       token = api_client_authorizations(:active).send(token_method)
       signed = Blob.sign_locator(
@@ -98,11 +89,11 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
             },
           }
       assert_response :success
-      assert_signed_manifest json_response['manifest_text'], 'updated', token: token
+      assert_unsigned_manifest json_response['manifest_text'], 'updated'
     end
   end
 
-  test "index with manifest_text selected returns signed locators" do
+  test "index with manifest_text selected returns unsigned locators" do
     columns = %w(uuid owner_uuid manifest_text)
     authorize_with :active
     get :index, params: {select: columns}
@@ -112,7 +103,7 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
     json_response["items"].each do |coll|
       assert_equal(coll.keys - ['kind'], columns,
                    "Collections index did not respect selected columns")
-      assert_signed_manifest coll['manifest_text'], coll['uuid']
+      assert_unsigned_manifest coll['manifest_text'], coll['uuid']
     end
   end
 
@@ -125,7 +116,8 @@ class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
     json_response["items"].each do |coll|
       assert_equal(coll.keys - ['kind'], ['unsigned_manifest_text'],
                    "Collections index did not respect selected columns")
-      locs += assert_unsigned_manifest coll, coll['uuid']
+      assert_nil coll['manifest_text']
+      locs += assert_unsigned_manifest coll['unsigned_manifest_text'], coll['uuid']
     end
     assert_operator locs, :>, 0, "no locators found in any manifests"
   end
@@ -338,7 +330,7 @@ EOS
       assert_not_nil assigns(:object)
       resp = assigns(:object)
       assert_equal foo_collection[:portable_data_hash], resp[:portable_data_hash]
-      assert_signed_manifest resp[:manifest_text]
+      assert_unsigned_manifest resp[:manifest_text]
 
       # The manifest in the response will have had permission hints added.
       # Remove any permission hints in the response before comparing it to the source.
@@ -388,7 +380,7 @@ EOS
       authorize_with :active
       manifest_text = ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:0:foo.txt\n"
       if !unsigned
-        manifest_text = Collection.sign_manifest manifest_text, api_token(:active)
+        manifest_text = Collection.sign_manifest_only_for_tests manifest_text, api_token(:active)
       end
       post :create, params: {
         collection: {
@@ -594,10 +586,10 @@ EOS
       assert_not_nil assigns(:object)
       resp = JSON.parse(@response.body)
       assert_equal manifest_uuid, resp['portable_data_hash']
-      # All of the locators in the output must be signed.
+      # All of the signatures in the output must be valid.
       resp['manifest_text'].lines.each do |entry|
         m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
-        if m
+        if m && m[0].index('+A')
           assert Blob.verify_signature m[0], signing_opts
         end
       end
@@ -641,10 +633,10 @@ EOS
     assert_not_nil assigns(:object)
     resp = JSON.parse(@response.body)
     assert_equal manifest_uuid, resp['portable_data_hash']
-    # All of the locators in the output must be signed.
+    # All of the signatures in the output must be valid.
     resp['manifest_text'].lines.each do |entry|
       m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
-      if m
+      if m && m[0].index('+A')
         assert Blob.verify_signature m[0], signing_opts
       end
     end
@@ -746,50 +738,6 @@ EOS
     assert_equal manifest_text, stripped_manifest
   end
 
-  test "multiple signed locators per line" do
-    permit_unsigned_manifests
-    authorize_with :active
-    locators = %w(
-      d41d8cd98f00b204e9800998ecf8427e+0
-      acbd18db4cc2f85cedef654fccc4a4d8+3
-      ea10d51bcf88862dbcc36eb292017dfd+45)
-
-    signing_opts = {
-      key: Rails.configuration.Collections.BlobSigningKey,
-      api_token: api_token(:active),
-    }
-
-    unsigned_manifest = [".", *locators, "0:0:foo.txt\n"].join(" ")
-    manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
-      '+' +
-      unsigned_manifest.length.to_s
-
-    signed_locators = locators.map { |loc| Blob.sign_locator loc, signing_opts }
-    signed_manifest = [".", *signed_locators, "0:0:foo.txt\n"].join(" ")
-
-    post :create, params: {
-      collection: {
-        manifest_text: signed_manifest,
-        portable_data_hash: manifest_uuid,
-      }
-    }
-    assert_response :success
-    assert_not_nil assigns(:object)
-    resp = JSON.parse(@response.body)
-    assert_equal manifest_uuid, resp['portable_data_hash']
-    # All of the locators in the output must be signed.
-    # Each line is of the form "path locator locator ... 0:0:file.txt"
-    # entry.split[1..-2] will yield just the tokens in the middle of the line
-    returned_locator_count = 0
-    resp['manifest_text'].lines.each do |entry|
-      entry.split[1..-2].each do |tok|
-        returned_locator_count += 1
-        assert Blob.verify_signature tok, signing_opts
-      end
-    end
-    assert_equal locators.count, returned_locator_count
-  end
-
   test 'Reject manifest with unsigned blob' do
     permit_unsigned_manifests false
     authorize_with :active
index 070e964e538c6d0f23992b5d1426be7f88f7146d..baca6e2781f5b6be4fb7d1022b355d7eef0832ad 100644 (file)
@@ -224,7 +224,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
 
   test "create collection, update manifest, and search with filename" do
     # create collection
-    signed_manifest = Collection.sign_manifest(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
+    signed_manifest = Collection.sign_manifest_only_for_tests(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
     post "/arvados/v1/collections",
       params: {
         format: :json,
@@ -241,7 +241,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
     search_using_filter 'my_test_file.txt', 1
 
     # update the collection's manifest text
-    signed_manifest = Collection.sign_manifest(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_updated_test_file.txt\n", api_token(:active))
+    signed_manifest = Collection.sign_manifest_only_for_tests(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_updated_test_file.txt\n", api_token(:active))
     put "/arvados/v1/collections/#{created['uuid']}",
       params: {
         format: :json,
@@ -375,7 +375,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
 
   test "create and get collection with properties" do
     # create collection to be searched for
-    signed_manifest = Collection.sign_manifest(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
+    signed_manifest = Collection.sign_manifest_only_for_tests(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
     post "/arvados/v1/collections",
       params: {
         format: :json,
@@ -401,7 +401,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
 
   test "create collection and update it with json encoded hash properties" do
     # create collection to be searched for
-    signed_manifest = Collection.sign_manifest(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
+    signed_manifest = Collection.sign_manifest_only_for_tests(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
     post "/arvados/v1/collections",
       params: {
         format: :json,
@@ -431,7 +431,7 @@ class CollectionsApiTest < ActionDispatch::IntegrationTest
     Rails.configuration.Collections.CollectionVersioning = true
     Rails.configuration.Collections.PreserveVersionIfIdle = -1 # Disable auto versioning
 
-    signed_manifest = Collection.sign_manifest(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
+    signed_manifest = Collection.sign_manifest_only_for_tests(". bad42fa702ae3ea7d888fef11b46f450+44 0:44:my_test_file.txt\n", api_token(:active))
     post "/arvados/v1/collections",
       params: {
         format: :json,
index 4efc947dd251d131b6746ca0059be95ac459db8a..bbae49f4a22bda7e277b7ecc67cbaf28cc941b7b 100644 (file)
@@ -42,10 +42,7 @@ class CollectionModelPerformanceTest < ActiveSupport::TestCase
       c = time_block 'read' do
         Collection.find_by_uuid(c.uuid)
       end
-      time_block 'sign' do
-        c.signed_manifest_text
-      end
-      time_block 'sign + render' do
+      time_block 'render' do
         c.as_api_response(nil)
       end
       loc = Blob.sign_locator(Digest::MD5.hexdigest('foo') + '+3',
index 8b8edbc15319fe7cc9f9aee0c21d46aa3cb23506..de0f1d360cb8509a5aea5bea31bbd763eba46609 100644 (file)
@@ -856,7 +856,7 @@ class CollectionTest < ActiveSupport::TestCase
   test "clear replication_confirmed* when introducing a new block in manifest" do
     c = collections(:replication_desired_2_confirmed_2)
     act_as_user users(:active) do
-      assert c.update_attributes(manifest_text: collections(:user_agreement).signed_manifest_text)
+      assert c.update_attributes(manifest_text: collections(:user_agreement).signed_manifest_text_only_for_tests)
       assert_nil c.replication_confirmed
       assert_nil c.replication_confirmed_at
     end
@@ -865,7 +865,7 @@ class CollectionTest < ActiveSupport::TestCase
   test "don't clear replication_confirmed* when just renaming a file" do
     c = collections(:replication_desired_2_confirmed_2)
     act_as_user users(:active) do
-      new_manifest = c.signed_manifest_text.sub(':bar', ':foo')
+      new_manifest = c.signed_manifest_text_only_for_tests.sub(':bar', ':foo')
       assert c.update_attributes(manifest_text: new_manifest)
       assert_equal 2, c.replication_confirmed
       assert_not_nil c.replication_confirmed_at
@@ -875,13 +875,13 @@ class CollectionTest < ActiveSupport::TestCase
   test "don't clear replication_confirmed* when just deleting a data block" do
     c = collections(:replication_desired_2_confirmed_2)
     act_as_user users(:active) do
-      new_manifest = c.signed_manifest_text
+      new_manifest = c.signed_manifest_text_only_for_tests
       new_manifest.sub!(/ \S+:bar/, '')
       new_manifest.sub!(/ acbd\S+/, '')
 
       # Confirm that we did just remove a block from the manifest (if
       # not, this test would pass without testing the relevant case):
-      assert_operator new_manifest.length+40, :<, c.signed_manifest_text.length
+      assert_operator new_manifest.length+40, :<, c.signed_manifest_text_only_for_tests.length
 
       assert c.update_attributes(manifest_text: new_manifest)
       assert_equal 2, c.replication_confirmed
@@ -895,7 +895,7 @@ class CollectionTest < ActiveSupport::TestCase
       c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n", name: 'foo')
       c.update_attributes! trash_at: (t0 + 1.hours)
       c.reload
-      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
+      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text_only_for_tests)[1].to_i
       assert_operator sig_exp.to_i, :<=, (t0 + 1.hours).to_i
     end
   end
@@ -905,7 +905,7 @@ class CollectionTest < ActiveSupport::TestCase
       c = Collection.create!(manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:x\n",
                              name: 'foo',
                              trash_at: db_current_time + 1.years)
-      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text)[1].to_i
+      sig_exp = /\+A[0-9a-f]{40}\@([0-9]+)/.match(c.signed_manifest_text_only_for_tests)[1].to_i
       expect_max_sig_exp = db_current_time.to_i + Rails.configuration.Collections.BlobSigningTTL.to_i
       assert_operator c.trash_at.to_i, :>, expect_max_sig_exp
       assert_operator sig_exp.to_i, :<=, expect_max_sig_exp