Commit 3f34e45e authored by Stan Hu's avatar Stan Hu

Audit log: Add audit events for group setting changes

This adds audit logging for permission changes (e.g. who can create
projects, 2FA enforcement, etc.)

Closes https://gitlab.com/gitlab-org/gitlab-ee/issues/8051
parent 21bfce0c
......@@ -41,6 +41,8 @@ You need Owner [permissions] to view the group Audit Events page.
To view a group's audit events, navigate to **Group > Settings > Audit Events**.
From there, you can see the following actions:
- Group name/path changed
- Group repository size limit changed
- Group created/deleted
- Group changed visibility
- User was added to group and with which [permissions]
......@@ -51,6 +53,12 @@ From there, you can see the following actions:
- [Project shared with group](../user/project/members/share_project_with_groups.md)
and with which [permissions]
- Removal of a previously shared group with a project
- LFS enabled/disabled
- Shared runners minutes limit changed
- Membership lock enabled/disabled
- Request access enabled/disabled
- 2FA enforcement/grace period changed
- Roles allowed to create project changed
### Project events
......
---
title: 'Audit log: Add audit events for group setting changes'
merge_request: 7987
author:
type: added
module EE
module Audit
class GroupChangesAuditor < ProjectChangesAuditor
class GroupChangesAuditor < BaseChangesAuditor
COLUMNS = %i(name path repository_size_limit visibility_level
request_access_enabled membership_lock lfs_enabled
shared_runners_minutes_limit
require_two_factor_authentication
two_factor_grace_period plan_id
project_creation_level).freeze
COLUMN_HUMAN_NAME = {
plan_id: 'plan',
visibility_level: 'visibility'
}.freeze
def execute
audit_changes(:visibility_level, as: 'visibility', model: model)
COLUMNS.each do |column|
audit_changes(column, as: column_human_name(column), model: model)
end
end
def attributes_from_auditable_model(column)
old = model.previous_changes[column].first
new = model.previous_changes[column].last
case column
when :visibility_level
{
from: ::Gitlab::VisibilityLevel.level_name(old),
to: ::Gitlab::VisibilityLevel.level_name(new)
}
when :project_creation_level
{
from: ::EE::Gitlab::Access.level_name(old),
to: ::EE::Gitlab::Access.level_name(new)
}
when :plan_id
{
from: plan_name(old),
to: plan_name(new)
}
else
{
from: old,
to: new
}
end
end
private
def plan_name(plan_id)
return 'none' unless plan_id.present?
Plan.find_by_id(plan_id.to_i)&.name || 'unknown'
end
def column_human_name(column)
COLUMN_HUMAN_NAME.fetch(column, column.to_s)
end
end
end
......
......@@ -20,6 +20,10 @@ module EE
s_('ProjectCreationLevel|Developers + Maintainers') => DEVELOPER_MAINTAINER_PROJECT_ACCESS
}
end
def level_name(name)
project_creation_options.key(name)
end
end
end
end
......@@ -12,7 +12,7 @@ describe EE::Audit::GroupChangesAuditor do
describe 'non audit changes' do
it 'does not call the audit event service' do
group.update!(name: 'new name')
group.update!(runners_token: 'new token')
expect { foo_instance.execute }.not_to change { SecurityEvent.count }
end
......@@ -25,6 +25,58 @@ describe EE::Audit::GroupChangesAuditor do
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
expect(SecurityEvent.last.details[:change]).to eq 'visibility'
end
it 'creates an event for project creation level change' do
group.update!(project_creation_level: 0)
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
event = SecurityEvent.last
expect(event.details[:from]).to eq 'Maintainers'
expect(event.details[:to]).to eq 'No one'
expect(event.details[:change]).to eq 'project_creation_level'
end
it 'creates an event when the group plan changes' do
new_plan = create(:free_plan, name: 'plan-1')
group.update!(plan_id: new_plan.id)
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
event = SecurityEvent.last
expect(event.details[:from]).to eq 'none'
expect(event.details[:to]).to eq 'plan-1'
expect(event.details[:change]).to eq 'plan'
end
it 'creates an event when attributes change' do
# Exclude special cases covered from above
columns = described_class::COLUMNS - described_class::COLUMN_HUMAN_NAME.keys - [:project_creation_level]
columns.each do |column|
data = group.attributes[column.to_s]
value =
case Group.type_for_attribute(column.to_s).type
when :integer
data.present? ? data + 1 : 0
when :boolean
!data
else
"#{data}-next"
end
group.update_attribute(column, value)
expect { foo_instance.execute }.to change { SecurityEvent.count }.by(1)
event = SecurityEvent.last
expect(event.details[:from]).to eq data
expect(event.details[:to]).to eq value
expect(event.details[:change]).to eq column.to_s
end
end
end
end
end
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