Commit 92a6b48c authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '220322-refactor-target-type' into 'master'

Refactor target_type in AuditEvent

See merge request gitlab-org/gitlab!36198
parents a053bb48 f22f3327
...@@ -5,7 +5,7 @@ class AuditEvent < ApplicationRecord ...@@ -5,7 +5,7 @@ class AuditEvent < ApplicationRecord
include IgnorableColumns include IgnorableColumns
include BulkInsertSafe include BulkInsertSafe
PARALLEL_PERSISTENCE_COLUMNS = [:author_name, :entity_path, :target_details].freeze PARALLEL_PERSISTENCE_COLUMNS = [:author_name, :entity_path, :target_details, :target_type].freeze
ignore_column :updated_at, remove_with: '13.4', remove_after: '2020-09-22' ignore_column :updated_at, remove_with: '13.4', remove_after: '2020-09-22'
......
---
title: Add new target_type column on audit_events table
merge_request: 36198
author:
type: changed
# frozen_string_literal: true
class AddTargetTypeToAuditEvent < ActiveRecord::Migration[6.0]
include Gitlab::Database::SchemaHelpers
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
SOURCE_TABLE_NAME = 'audit_events'
PARTITIONED_TABLE_NAME = 'audit_events_part_5fc467ac26'
TRIGGER_FUNCTION_NAME = 'table_sync_function_2be879775d'
def up
with_lock_retries do
# rubocop:disable Migration/AddLimitToTextColumns
add_column('audit_events', :target_type, :text)
add_column('audit_events_part_5fc467ac26', :target_type, :text)
# rubocop:enable Migration/AddLimitToTextColumns
create_trigger_function(TRIGGER_FUNCTION_NAME, replace: true) do
<<~SQL
IF (TG_OP = 'DELETE') THEN
DELETE FROM #{PARTITIONED_TABLE_NAME} where id = OLD.id;
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE #{PARTITIONED_TABLE_NAME}
SET author_id = NEW.author_id,
type = NEW.type,
entity_id = NEW.entity_id,
entity_type = NEW.entity_type,
details = NEW.details,
ip_address = NEW.ip_address,
author_name = NEW.author_name,
entity_path = NEW.entity_path,
target_details = NEW.target_details,
target_type = NEW.target_type,
created_at = NEW.created_at
WHERE #{PARTITIONED_TABLE_NAME}.id = NEW.id;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO #{PARTITIONED_TABLE_NAME} (id,
author_id,
type,
entity_id,
entity_type,
details,
ip_address,
author_name,
entity_path,
target_details,
target_type,
created_at)
VALUES (NEW.id,
NEW.author_id,
NEW.type,
NEW.entity_id,
NEW.entity_type,
NEW.details,
NEW.ip_address,
NEW.author_name,
NEW.entity_path,
NEW.target_details,
NEW.target_type,
NEW.created_at);
END IF;
RETURN NULL;
SQL
end
end
end
def down
with_lock_retries do
remove_column SOURCE_TABLE_NAME, :target_type
create_trigger_function(TRIGGER_FUNCTION_NAME, replace: true) do
<<~SQL
IF (TG_OP = 'DELETE') THEN
DELETE FROM #{PARTITIONED_TABLE_NAME} where id = OLD.id;
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE #{PARTITIONED_TABLE_NAME}
SET author_id = NEW.author_id,
type = NEW.type,
entity_id = NEW.entity_id,
entity_type = NEW.entity_type,
details = NEW.details,
ip_address = NEW.ip_address,
author_name = NEW.author_name,
entity_path = NEW.entity_path,
target_details = NEW.target_details,
created_at = NEW.created_at
WHERE #{PARTITIONED_TABLE_NAME}.id = NEW.id;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO #{PARTITIONED_TABLE_NAME} (id,
author_id,
type,
entity_id,
entity_type,
details,
ip_address,
author_name,
entity_path,
target_details,
created_at)
VALUES (NEW.id,
NEW.author_id,
NEW.type,
NEW.entity_id,
NEW.entity_type,
NEW.details,
NEW.ip_address,
NEW.author_name,
NEW.entity_path,
NEW.target_details,
NEW.created_at);
END IF;
RETURN NULL;
SQL
end
remove_column PARTITIONED_TABLE_NAME, :target_type
end
end
end
# frozen_string_literal: true
class AddTextLimitToAuditEventTargetType < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
SOURCE_TABLE_NAME = 'audit_events'
PARTITIONED_TABLE_NAME = 'audit_events_part_5fc467ac26'
disable_ddl_transaction!
def up
add_text_limit(SOURCE_TABLE_NAME, :target_type, 255)
add_text_limit(PARTITIONED_TABLE_NAME, :target_type, 255)
end
def down
remove_text_limit(SOURCE_TABLE_NAME, :target_type)
remove_text_limit(PARTITIONED_TABLE_NAME, :target_type)
end
end
fe96df46a5f360cafe8f9816c6dfc2d00afdcf458fb38ace37ce59999dba2413
\ No newline at end of file
8bb03ea2ded957a41aa1efd60a9908a3c597aaaade9190f8f1515bfd2ab9a282
\ No newline at end of file
...@@ -29,6 +29,7 @@ ELSIF (TG_OP = 'UPDATE') THEN ...@@ -29,6 +29,7 @@ ELSIF (TG_OP = 'UPDATE') THEN
author_name = NEW.author_name, author_name = NEW.author_name,
entity_path = NEW.entity_path, entity_path = NEW.entity_path,
target_details = NEW.target_details, target_details = NEW.target_details,
target_type = NEW.target_type,
created_at = NEW.created_at created_at = NEW.created_at
WHERE audit_events_part_5fc467ac26.id = NEW.id; WHERE audit_events_part_5fc467ac26.id = NEW.id;
ELSIF (TG_OP = 'INSERT') THEN ELSIF (TG_OP = 'INSERT') THEN
...@@ -42,6 +43,7 @@ ELSIF (TG_OP = 'INSERT') THEN ...@@ -42,6 +43,7 @@ ELSIF (TG_OP = 'INSERT') THEN
author_name, author_name,
entity_path, entity_path,
target_details, target_details,
target_type,
created_at) created_at)
VALUES (NEW.id, VALUES (NEW.id,
NEW.author_id, NEW.author_id,
...@@ -53,6 +55,7 @@ ELSIF (TG_OP = 'INSERT') THEN ...@@ -53,6 +55,7 @@ ELSIF (TG_OP = 'INSERT') THEN
NEW.author_name, NEW.author_name,
NEW.entity_path, NEW.entity_path,
NEW.target_details, NEW.target_details,
NEW.target_type,
NEW.created_at); NEW.created_at);
END IF; END IF;
RETURN NULL; RETURN NULL;
...@@ -74,8 +77,10 @@ CREATE TABLE public.audit_events_part_5fc467ac26 ( ...@@ -74,8 +77,10 @@ CREATE TABLE public.audit_events_part_5fc467ac26 (
entity_path text, entity_path text,
target_details text, target_details text,
created_at timestamp without time zone NOT NULL, created_at timestamp without time zone NOT NULL,
target_type text,
CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)), CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)),
CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)), CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)),
CONSTRAINT check_97a8c868e7 CHECK ((char_length(target_type) <= 255)),
CONSTRAINT check_d493ec90b5 CHECK ((char_length(target_details) <= 5500)) CONSTRAINT check_d493ec90b5 CHECK ((char_length(target_details) <= 5500))
) )
PARTITION BY RANGE (created_at); PARTITION BY RANGE (created_at);
...@@ -9470,7 +9475,9 @@ CREATE TABLE public.audit_events ( ...@@ -9470,7 +9475,9 @@ CREATE TABLE public.audit_events (
author_name text, author_name text,
entity_path text, entity_path text,
target_details text, target_details text,
target_type text,
CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)), CONSTRAINT check_492aaa021d CHECK ((char_length(entity_path) <= 5500)),
CONSTRAINT check_82294106dd CHECK ((char_length(target_type) <= 255)),
CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)), CONSTRAINT check_83ff8406e2 CHECK ((char_length(author_name) <= 255)),
CONSTRAINT check_d493ec90b5 CHECK ((char_length(target_details) <= 5500)) CONSTRAINT check_d493ec90b5 CHECK ((char_length(target_details) <= 5500))
); );
......
...@@ -15,6 +15,7 @@ module EE ...@@ -15,6 +15,7 @@ module EE
old_access_level = @details[:old_access_level] old_access_level = @details[:old_access_level]
user_id = member.id user_id = member.id
user_name = member.user ? member.user.name : 'Deleted User' user_name = member.user ? member.user.name : 'Deleted User'
target_type = 'User'
@details = @details =
case action case action
...@@ -23,7 +24,7 @@ module EE ...@@ -23,7 +24,7 @@ module EE
remove: "user_access", remove: "user_access",
author_name: @author.name, author_name: @author.name,
target_id: user_id, target_id: user_id,
target_type: "User", target_type: target_type,
target_details: user_name target_details: user_name
} }
when :expired when :expired
...@@ -31,7 +32,7 @@ module EE ...@@ -31,7 +32,7 @@ module EE
remove: "user_access", remove: "user_access",
author_name: member.created_by ? member.created_by.name : 'Deleted User', author_name: member.created_by ? member.created_by.name : 'Deleted User',
target_id: user_id, target_id: user_id,
target_type: "User", target_type: target_type,
target_details: user_name, target_details: user_name,
system_event: true, system_event: true,
reason: "access expired on #{member.expires_at}" reason: "access expired on #{member.expires_at}"
...@@ -42,7 +43,7 @@ module EE ...@@ -42,7 +43,7 @@ module EE
as: ::Gitlab::Access.options_with_owner.key(member.access_level.to_i), as: ::Gitlab::Access.options_with_owner.key(member.access_level.to_i),
author_name: @author.name, author_name: @author.name,
target_id: user_id, target_id: user_id,
target_type: "User", target_type: target_type,
target_details: user_name target_details: user_name
} }
when :update, :override when :update, :override
...@@ -54,7 +55,7 @@ module EE ...@@ -54,7 +55,7 @@ module EE
expiry_to: member.expires_at, expiry_to: member.expires_at,
author_name: @author.name, author_name: @author.name,
target_id: user_id, target_id: user_id,
target_type: "User", target_type: target_type,
target_details: user_name target_details: user_name
} }
end end
......
...@@ -16,7 +16,7 @@ RSpec.describe AuditEvent, type: :model do ...@@ -16,7 +16,7 @@ RSpec.describe AuditEvent, type: :model do
describe 'callbacks' do describe 'callbacks' do
context 'parallel_persist' do context 'parallel_persist' do
let_it_be(:details) do let_it_be(:details) do
{ author_name: 'Kungfu Panda', entity_path: 'gitlab-org/gitlab', target_details: 'Project X' } { author_name: 'Kungfu Panda', entity_path: 'gitlab-org/gitlab', target_details: 'Project X', target_type: 'User' }
end end
let_it_be(:event) { create(:project_audit_event, details: details, entity_path: nil, target_details: nil) } let_it_be(:event) { create(:project_audit_event, details: details, entity_path: nil, target_details: nil) }
......
...@@ -42,6 +42,11 @@ RSpec.describe AuditEventService do ...@@ -42,6 +42,11 @@ RSpec.describe AuditEventService do
end end
end end
it 'generates a system event' do
expect(event[:details][:target_type]).to eq('User')
expect(event.target_type).to eq('User')
end
context 'updating membership' do context 'updating membership' do
let(:service) do let(:service) do
described_class.new(user, project, { described_class.new(user, project, {
...@@ -277,6 +282,22 @@ RSpec.describe AuditEventService do ...@@ -277,6 +282,22 @@ RSpec.describe AuditEventService do
end end
end end
describe '#for_project_group_link' do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:group) { create(:group) }
let_it_be(:link) { create(:project_group_link, group: group, project: project) }
let(:options) { { action: :create } }
subject(:event) { described_class.new(current_user, project, options).for_project_group_link(link).security_event }
it 'sets the target_type attribute' do
expect(event.details[:target_type]).to eq('Project')
expect(event.target_type).to eq('Project')
end
end
describe '#for_user' do describe '#for_user' do
let(:author_name) { 'Administrator' } let(:author_name) { 'Administrator' }
let(:current_user) { User.new(name: author_name) } let(:current_user) { User.new(name: author_name) }
...@@ -351,6 +372,55 @@ RSpec.describe AuditEventService do ...@@ -351,6 +372,55 @@ RSpec.describe AuditEventService do
end end
end end
describe '#for_project' do
let_it_be(:author_name) { 'Administrator' }
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:action) { :destroy }
let_it_be(:options) { { action: action } }
subject(:event) { described_class.new(current_user, project, options).for_project.security_event }
it 'sets the details attribute' do
expect(subject.details).to eq({
remove: 'project',
author_name: current_user.name,
target_id: project.full_path,
target_type: 'Project',
target_details: project.full_path
})
end
it 'sets the target_type column' do
expect(subject.target_type).to eq('Project')
end
end
describe '#for_group' do
let_it_be(:author_name) { 'Administrator' }
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:action) { :destroy }
let_it_be(:options) { { action: action } }
let_it_be(:service) { described_class.new(current_user, group, options).for_group }
subject(:event) { service.security_event }
it 'sets the details attribute' do
expect(event.details).to eq({
remove: 'group',
author_name: current_user.name,
target_id: group.full_path,
target_type: 'Group',
target_details: group.full_path
})
end
it 'stores target_type in a database column' do
expect(subject.target_type).to eq('Group')
end
end
describe 'license' do describe 'license' do
let(:event) { service.for_project.security_event } let(:event) { service.for_project.security_event }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment