X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/0f361d9a0748e2b470ed5bc73351cb2eaf3e02e3..HEAD:/services/api/test/unit/log_test.rb diff --git a/services/api/test/unit/log_test.rb b/services/api/test/unit/log_test.rb index 92976e0053..e69f0517e4 100644 --- a/services/api/test/unit/log_test.rb +++ b/services/api/test/unit/log_test.rb @@ -1,4 +1,9 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 + require 'test_helper' +require 'audit_logs' class LogTest < ActiveSupport::TestCase include CurrentApiClient @@ -27,6 +32,10 @@ class LogTest < ActiveSupport::TestCase Log.where(object_uuid: thing.uuid).order("created_at ASC").all end + def clear_logs_about(thing) + Log.where(object_uuid: thing.uuid).delete_all + end + def assert_logged(thing, event_type) logs = get_logs_about(thing) assert_equal(@log_count, logs.size, "log count mismatch") @@ -51,6 +60,10 @@ class LogTest < ActiveSupport::TestCase 'old_etag', 'old_attributes') assert_properties(new_props_test, event_type, props, 'new_etag', 'new_attributes') + ['old_attributes', 'new_attributes'].each do |logattr| + next if !props[logattr] + assert_match /"created_at":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z"/, Oj.dump(props, mode: :compat) + end yield props if block_given? end @@ -97,10 +110,11 @@ class LogTest < ActiveSupport::TestCase test "old_attributes preserves values deep inside a hash" do set_user_from_auth :active - it = specimens(:owned_by_active_user) + it = collections(:collection_owned_by_active) + clear_logs_about it it.properties = {'foo' => {'bar' => ['baz', 'qux', {'quux' => 'bleat'}]}} it.save! - @log_count += 1 + assert_logged it, :update it.properties['foo']['bar'][2]['quux'] = 'blert' it.save! assert_logged it, :update do |props| @@ -219,6 +233,21 @@ class LogTest < ActiveSupport::TestCase assert_logged(auth, :update) end + test "don't log changes only to Collection.preserve_version" do + set_user_from_auth :admin_trustedclient + col = collections(:collection_owned_by_active) + clear_logs_about col + start_log_count = get_logs_about(col).size + assert_equal false, col.preserve_version + col.preserve_version = true + col.save! + assert_equal(start_log_count, get_logs_about(col).size, + "log count changed after updating Collection.preserve_version") + col.name = 'updated by admin' + col.save! + assert_logged(col, :update) + end + test "token isn't included in ApiClientAuthorization logs" do set_user_from_auth :admin_trustedclient auth = ApiClientAuthorization.new @@ -235,27 +264,29 @@ class LogTest < ActiveSupport::TestCase test "use ownership and permission links to determine which logs a user can see" do known_logs = [:noop, - :admin_changes_repository2, - :admin_changes_specimen, + :admin_changes_collection_owned_by_active, + :admin_changes_collection_owned_by_foo, :system_adds_foo_file, :system_adds_baz, :log_owned_by_active, - :crunchstat_for_running_job] + :crunchstat_for_running_container] c = Log.readable_by(users(:admin)).order("id asc").each.to_a assert_log_result c, known_logs, known_logs c = Log.readable_by(users(:active)).order("id asc").each.to_a - assert_log_result c, known_logs, [:admin_changes_repository2, # owned by active - :system_adds_foo_file, # readable via link - :system_adds_baz, # readable via 'all users' group - :log_owned_by_active, # log owned by active - :crunchstat_for_running_job] # log & job owned by active + assert_log_result c, known_logs, [:admin_changes_collection_owned_by_active, + :system_adds_foo_file, # readable via link + :system_adds_baz, # readable via 'all users' group + :log_owned_by_active, # log owned by active + :crunchstat_for_running_container] # log & job owned by active c = Log.readable_by(users(:spectator)).order("id asc").each.to_a - assert_log_result c, known_logs, [:noop, # object_uuid is spectator - :admin_changes_specimen, # object_uuid is a specimen owned by spectator - :system_adds_baz] # readable via 'all users' group + assert_log_result c, known_logs, [:noop, # object_uuid is spectator + :system_adds_baz] # readable via 'all users' group + + c = Log.readable_by(users(:user_foo_in_sharing_group)).order("id asc").each.to_a + assert_log_result c, known_logs, [:admin_changes_collection_owned_by_foo] # collection's parent is readable via role group end def assert_log_result result, known_logs, expected_logs @@ -273,7 +304,7 @@ class LogTest < ActiveSupport::TestCase end test "non-empty configuration.unlogged_attributes" do - Rails.configuration.unlogged_attributes = ["manifest_text"] + Rails.configuration.AuditLogs.UnloggedAttributes = ConfigLoader.to_OrderedOptions({"manifest_text"=>{}}) txt = ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo\n" act_as_system_user do @@ -288,7 +319,7 @@ class LogTest < ActiveSupport::TestCase end test "empty configuration.unlogged_attributes" do - Rails.configuration.unlogged_attributes = [] + Rails.configuration.AuditLogs.UnloggedAttributes = ConfigLoader.to_OrderedOptions({}) txt = ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo\n" act_as_system_user do @@ -296,7 +327,7 @@ class LogTest < ActiveSupport::TestCase assert_logged(coll, :create) do |props| assert_equal(txt, props['new_attributes']['manifest_text']) end - coll.update_attributes!(name: "testing") + coll.update!(name: "testing") assert_logged(coll, :update) do |props| assert_equal(txt, props['old_attributes']['manifest_text']) assert_equal(txt, props['new_attributes']['manifest_text']) @@ -307,4 +338,69 @@ class LogTest < ActiveSupport::TestCase end end end + + def assert_no_logs_deleted + logs_before = Log.unscoped.all.count + assert logs_before > 0 + yield + assert_equal logs_before, Log.unscoped.all.count + end + + def remaining_audit_logs + Log.unscoped.where('event_type in (?)', %w(create update destroy delete)) + end + + # Default settings should not delete anything -- some sites rely on + # the original "keep everything forever" behavior. + test 'retain old audit logs with default settings' do + assert_no_logs_deleted do + AuditLogs.delete_old( + max_age: Rails.configuration.AuditLogs.MaxAge, + max_batch: Rails.configuration.AuditLogs.MaxDeleteBatch) + end + end + + # Batch size 0 should retain all logs -- even if max_age is very + # short, and even if the default settings (and associated test) have + # changed. + test 'retain old audit logs with max_audit_log_delete_batch=0' do + assert_no_logs_deleted do + AuditLogs.delete_old(max_age: 1, max_batch: 0) + end + end + + # We recommend a more conservative age of 5 minutes for production, + # but 3 minutes suits our test data better (and is test-worthy in + # that it's expected to work correctly in production). + test 'delete old audit logs with production settings' do + initial_log_count = remaining_audit_logs.count + assert initial_log_count > 0 + AuditLogs.delete_old(max_age: 180, max_batch: 100000) + assert_operator remaining_audit_logs.count, :<, initial_log_count + end + + test 'delete all audit logs in multiple batches' do + assert remaining_audit_logs.count > 2 + AuditLogs.delete_old(max_age: 0.00001, max_batch: 2) + assert_equal [], remaining_audit_logs.collect(&:uuid) + end + + test 'delete old audit logs in thread' do + Rails.configuration.AuditLogs.MaxAge = 20 + Rails.configuration.AuditLogs.MaxDeleteBatch = 100000 + Rails.cache.delete 'AuditLogs' + initial_audit_log_count = remaining_audit_logs.count + assert initial_audit_log_count > 0 + act_as_system_user do + Log.create!() + end + deadline = Time.now + 10 + while remaining_audit_logs.count == initial_audit_log_count + if Time.now > deadline + raise "timed out" + end + sleep 0.1 + end + assert_operator remaining_audit_logs.count, :<, initial_audit_log_count + end end