Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions app/jobs/audits_cleanup_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class AuditsCleanupJob < ApplicationJob
DELETE_LIMIT = 10_000

queue_as :utilities

def self.perform
preserve_usernames = AdminSetting.current.preserve_audit_records_usernames&.split(/,\s*/) || []
preserve_user_ids = User.where(login: preserve_usernames).select(:id).map(&:id)

query = Audited.audit_class.where("0")

if ArchiveConfig.USER_KEEP_AUDIT_UPDATES_DAYS > -1
query = query.or(
Audited.audit_class.where(
auditable_type: "User",
action: "update",
created_at: ..ArchiveConfig.USER_KEEP_AUDIT_UPDATES_DAYS.days.ago
).and(
Audited.audit_class.where.not(
auditable_id: preserve_user_ids
)
)
)
end

if ArchiveConfig.USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS > -1
query = query.or(
Audited.audit_class.where(
auditable_type: "User",
action: %w[create destroy],
created_at: ..ArchiveConfig.USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS.days.ago
).and(
Audited.audit_class.where.not(
auditable_id: preserve_user_ids
)
)
)
end

query.limit(DELETE_LIMIT).delete_all
end
end
1 change: 1 addition & 0 deletions app/models/admin_setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class AdminSetting < ApplicationRecord
invite_from_queue_frequency: ArchiveConfig.INVITE_FROM_QUEUE_FREQUENCY,
account_creation_enabled?: ArchiveConfig.ACCOUNT_CREATION_ENABLED,
days_to_purge_unactivated: 2,
preserve_audit_records_usernames: nil,
suspend_filter_counts?: false,
enable_test_caching?: false,
cache_expiration: 10,
Expand Down
2 changes: 2 additions & 0 deletions app/policies/admin_setting_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class AdminSettingPolicy < ApplicationPolicy
# Define which roles can update which settings.
ALLOWED_SETTINGS_BY_ROLES = {
"policy_and_abuse" => %i[
preserve_audit_records_usernames
hide_spam
invite_from_queue_enabled
invite_from_queue_number
Expand All @@ -16,6 +17,7 @@ class AdminSettingPolicy < ApplicationPolicy
cache_expiration
creation_requires_invite
days_to_purge_unactivated
preserve_audit_records_usernames
disable_support_form
disabled_support_form_text
downloads_enabled
Expand Down
3 changes: 3 additions & 0 deletions app/views/admin/settings/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@

<dt><%= f.label :days_to_purge_unactivated, t(".fields.days_to_purge_unactivated") %></dt>
<dd><%= admin_setting_text_field(f, :days_to_purge_unactivated, size: "3") %></dd>

<dt><%= f.label :preserve_audit_records_usernames, t(".fields.preserve_audit_records_usernames") %></dt>
<dd><%= admin_setting_text_field(f, :preserve_audit_records_usernames, size: "20") %></dd>
</dl>
</fieldset>

Expand Down
6 changes: 6 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@ TAGGINGS_COUNT_REINDEX_LIMIT: 1000
# How many rows we should get from audits for backfilling user history tables
USER_HISTORIC_VALUES_LIMIT: 10_000

# How many days should we keep user update audit records. Set to -1 to keep forever
USER_KEEP_AUDIT_UPDATES_DAYS: 1460

# How many days should we keep user create and destroy audit records. Set to -1 to keep forever
USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS: -1

# how many signups in a challenge before we move to static summaries generated hourly
MAX_SIGNUPS_FOR_LIVE_SUMMARY: 20

Expand Down
1 change: 1 addition & 0 deletions config/locales/views/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ en:
invite_from_queue_enabled: Invite from queue enabled (People can add themselves to the queue and invitations are sent out automatically)
invite_from_queue_frequency: How often (in hours) should we invite people from the queue
invite_from_queue_number: Number of people to invite from the queue at once
preserve_audit_records_usernames: Preserve audit records for these usernames (comma separated)
request_invite_enabled: Users can request invitations
suspend_filter_counts: Suspend some filter tracking due to high posting volume
tag_wrangling_off: Turn off tag wrangling for non-admins
Expand Down
6 changes: 6 additions & 0 deletions config/resque_schedule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,9 @@ disable_admin_post_comments:
description: >-
Disables all comments on admin (news) posts older than the
configured window.

cleanup_audits:
cron: "0 0 1 * *"
class: AuditsCleanupJob
queue: utilities
description: Deletes user audit records older than the configured limit.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddPreserveAuditRecordsUsernamesAdminSetting < ActiveRecord::Migration[8.1]
def change
add_column :admin_settings, :preserve_audit_records_usernames, :string
end
end
77 changes: 77 additions & 0 deletions spec/jobs/audits_cleanup_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

require "spec_helper"

def create_audits(user)
user.audits.delete_all

user.audits.create!(action: "create", auditable: user, user: user, auditable_id: user.id,
auditable_type: "User", audited_changes: user.to_json,
created_at: (ArchiveConfig.USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS + 1).days.ago)

user.audits.create!(action: "update", auditable: user, user: user, auditable_id: user.id,
auditable_type: "User", audited_changes: { "sign_in_count" => [0, 1] },
created_at: (ArchiveConfig.USER_KEEP_AUDIT_UPDATES_DAYS + 1).days.ago)

user.audits.create!(action: "update", auditable: user, user: user, auditable_id: user.id,
auditable_type: "User", audited_changes: { "sign_in_count" => [1, 2] },
created_at: Time.now.utc)

user.audits.create!(action: "destroy", auditable: user, user: user, auditable_id: user.id,
auditable_type: "User", audited_changes: user.to_json,
created_at: Time.now.utc)
end

describe AuditsCleanupJob do
let(:existing_user) { create(:user) }
let(:no_cleanup_user) { create(:user) }

before do
ArchiveConfig.USER_KEEP_AUDIT_UPDATES_DAYS = 30
ArchiveConfig.USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS = 30
end

context "when old audits exist" do
before do
create_audits(existing_user)
end

it "deletes audit records older than the configured limits" do
AuditsCleanupJob.perform
expect(existing_user.audits.count).to eq(2)
end

it "doesn't delete 'update' audit records when configured limit is -1" do
ArchiveConfig.USER_KEEP_AUDIT_UPDATES_DAYS = -1
AuditsCleanupJob.perform
expect(existing_user.audits.where(action: "update").count).to eq(2)
end

it "doesn't delete 'create' or 'destroy' audit records when configured limit is -1" do
ArchiveConfig.USER_KEEP_AUDIT_CREATES_DESTROYS_DAYS = -1
AuditsCleanupJob.perform
expect(existing_user.audits.where(action: %w[create destroy]).count).to eq(2)
end
end

context "when audits exist for protected users" do
before do
admin_setting = AdminSetting.default
admin_setting.preserve_audit_records_usernames = [no_cleanup_user.login, "non_existing_user"].join(", ")
admin_setting.save(validate: false)

create_audits(existing_user)
create_audits(no_cleanup_user)
end

it "doesn't delete audit records for users whose usernames are in preserve_audit_records_usernames admin setting" do
AuditsCleanupJob.perform
expect(no_cleanup_user.audits.count).to eq(4)
end

it "does delete audit records for users whose username are not in preserve_audit_records_usernames admin setting" do
AuditsCleanupJob.perform
expect(existing_user.audits.count).to eq(2)
end
end
end
Loading