Commit 3f2e821d authored by Tan Le's avatar Tan Le Committed by Mayra Cabrera

Extend ability to read audit events to more roles

This commit will extend the ability to query project and group audit
events via API to more roles.

Group-level audit events
- Group Owner and above: read all group-level audit events
- Developer to Maintainer: read their own group-level audit events

Project-level audit events
- Project Maintainer and above: read all project-level audit events
- Developer: read their own project-level audit events
parent 920f4430
......@@ -132,7 +132,8 @@ Example response:
The Group Audit Events API allows you to retrieve [group audit events](../administration/audit_events.md#group-events).
To retrieve group audit events using the API, you must [authenticate yourself](README.md#authentication) as an Administrator or an owner of the group.
A user with a Owner role (or above) can retrieve group audit events of all users.
A user with a Developer or Maintainer role is limited to group audit events based on their individual actions.
### Retrieve all group audit events
......@@ -238,7 +239,8 @@ Example response:
The Project Audit Events API allows you to retrieve [project audit events](../administration/audit_events.md#project-events).
To retrieve project audit events using the API, you must [authenticate yourself](README.md#authentication) as a Maintainer or an Owner of the project.
A user with a Maintainer role (or above) can retrieve project audit events of all users.
A user with a Developer role is limited to project audit events based on their individual actions.
### Retrieve all project audit events
......
......@@ -233,6 +233,7 @@ module EE
enable :create_wiki
enable :admin_merge_request
enable :read_ci_minutes_quota
enable :read_group_audit_events
end
rule { security_dashboard_enabled & developer }.enable :read_group_security_dashboard
......
......@@ -195,6 +195,7 @@ module EE
enable :update_vulnerability_feedback
enable :read_ci_minutes_quota
enable :admin_feature_flags_issue_links
enable :read_project_audit_events
end
rule { can?(:developer_access) & iterations_available }.policy do
......
---
title: Extend ability to read audit events to more roles
merge_request: 49106
author:
type: added
......@@ -54,7 +54,13 @@ module EE
end
def audit_log_finder_params
params.slice(:created_after, :created_before)
params
.slice(:created_after, :created_before)
.then { |params| filter_by_author(params) }
end
def filter_by_author(params)
can?(current_user, :admin_group, user_group) ? params : params.merge(author_id: current_user.id)
end
override :delete_group
......@@ -90,7 +96,7 @@ module EE
segment ':id/audit_events' do
before do
authorize! :admin_group, user_group
authorize! :read_group_audit_events, user_group
check_audit_events_available!(user_group)
increment_unique_values('a_compliance_audit_events_api', current_user.id)
end
......
......@@ -23,7 +23,7 @@ module EE
end
segment ':id/audit_events', feature_category: :audit_events do
before do
authorize! :admin_project, user_project
authorize! :read_project_audit_events, user_project
check_audit_events_available!(user_project)
increment_unique_values('a_compliance_audit_events_api', current_user.id)
end
......@@ -97,7 +97,13 @@ module EE
end
def audit_log_finder_params
params.slice(:created_after, :created_before)
params
.slice(:created_after, :created_before)
.then { |params| filter_by_author(params) }
end
def filter_by_author(params)
can?(current_user, :admin_project, user_project) ? params : params.merge(author_id: current_user.id)
end
override :delete_project
......
......@@ -1159,6 +1159,27 @@ RSpec.describe GroupPolicy do
end
end
describe ':read_group_audit_events' do
using RSpec::Parameterized::TableSyntax
let(:policy) { :read_group_audit_events }
where(:role, :allowed) do
:guest | false
:reporter | false
:developer | true
:maintainer | true
:owner | true
:admin | true
end
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context 'when group is locked because storage usage limit exceeded' do
let(:current_user) { owner }
let(:policies) do
......
......@@ -22,7 +22,7 @@ RSpec.describe ProjectPolicy do
let(:additional_developer_permissions) do
%i[
admin_vulnerability_feedback read_project_security_dashboard
admin_vulnerability_feedback read_project_audit_events read_project_security_dashboard
read_vulnerability read_vulnerability_scanner create_vulnerability create_vulnerability_export admin_vulnerability
admin_vulnerability_issue_link read_merge_train
]
......
......@@ -17,6 +17,46 @@ RSpec.describe API::Groups do
group.ldap_group_links.create cn: 'ldap-group', group_access: Gitlab::Access::MAINTAINER, provider: 'ldap'
end
shared_examples 'inaccessable by reporter role and lower' do
context 'for reporter' do
before do
reporter = create(:user)
group.add_reporter(reporter)
get api(path, reporter)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'for guest' do
before do
guest = create(:user)
group.add_guest(guest)
get api(path, guest)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'for anonymous' do
before do
anonymous = create(:user)
get api(path, anonymous)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
describe "GET /groups" do
context "when authenticated as user" do
it "returns ldap details" do
......@@ -507,9 +547,21 @@ RSpec.describe API::Groups do
describe 'GET group/:id/audit_events' do
let(:path) { "/groups/#{group.id}/audit_events" }
context 'when authenticated, as a user' do
it_behaves_like '403 response' do
let(:request) { get api(path, create(:user)) }
it_behaves_like 'inaccessable by reporter role and lower'
context 'when authenticated, as a member' do
before do
stub_licensed_features(audit_events: true)
group.add_developer(user)
end
it 'returns only events authored by current user' do
group_audit_event = create(:group_audit_event, entity_id: group.id, author_id: user.id)
create(:group_audit_event, entity_id: group.id, author_id: another_user.id)
get api(path, user)
expect_response_contain_exactly(group_audit_event.id)
end
end
......@@ -602,9 +654,33 @@ RSpec.describe API::Groups do
let_it_be(:group_audit_event) { create(:group_audit_event, created_at: Date.new(2000, 1, 10), entity_id: group.id) }
context 'when authenticated, as a user' do
it_behaves_like '403 response' do
let(:request) { get api(path, create(:user)) }
it_behaves_like 'inaccessable by reporter role and lower'
context 'when authenticated, as a member' do
let_it_be(:developer) { create(:user) }
before do
stub_licensed_features(audit_events: true)
group.add_developer(developer)
end
it 'returns 200 response' do
audit_event = create(:group_audit_event, entity_id: group.id, author_id: developer.id)
path = "/groups/#{group.id}/audit_events/#{audit_event.id}"
get api(path, developer)
expect(response).to have_gitlab_http_status(:ok)
end
context 'existing audit event of a different user' do
let_it_be(:audit_event) { create(:group_audit_event, entity_id: group.id, author_id: another_user.id) }
let(:path) { "/groups/#{group.id}/audit_events/#{audit_event.id}" }
it_behaves_like '404 response' do
let(:request) { get api(path, developer) }
end
end
end
......
......@@ -6,8 +6,49 @@ RSpec.describe API::Projects do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let_it_be(:another_user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
shared_examples 'inaccessable by reporter role and lower' do
context 'for reporter' do
before do
reporter = create(:user)
project.add_reporter(reporter)
get api(path, reporter)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'for guest' do
before do
guest = create(:user)
project.add_guest(guest)
get api(path, guest)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'for anonymous' do
before do
anonymous = create(:user)
get api(path, anonymous)
end
it 'returns 403 response' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
describe 'GET /projects' do
it 'does not break on license checks' do
enable_namespace_license_check!
......@@ -515,13 +556,31 @@ RSpec.describe API::Projects do
let_it_be(:project) { create(:project, :public, namespace: user.namespace) }
let(:path) { "/projects/#{project.id}/audit_events" }
context 'when authenticated, as a user' do
it_behaves_like '403 response' do
let(:request) { get api(path, create(:user)) }
it_behaves_like 'inaccessable by reporter role and lower'
context 'when authenticated, as a member' do
let_it_be(:developer) { create(:user) }
before do
stub_licensed_features(audit_events: true)
project.add_developer(developer)
end
it 'returns only events authored by current user' do
project_audit_event_1 = create(:project_audit_event, entity_id: project.id, author_id: developer.id)
create(:project_audit_event, entity_id: project.id, author_id: 666)
get api(path, developer)
expect_response_contain_exactly(project_audit_event_1.id)
end
end
context 'when authenticated, as a project owner' do
before do
project.add_maintainer(user)
end
context 'audit events feature is not available' do
before do
stub_licensed_features(audit_events: false)
......@@ -612,9 +671,46 @@ RSpec.describe API::Projects do
let_it_be(:project_audit_event) { create(:project_audit_event, created_at: Date.new(2000, 1, 10), entity_id: project.id) }
context 'when authenticated, as a user' do
it_behaves_like 'inaccessable by reporter role and lower'
context 'when authenticated, as a guest' do
let_it_be(:guest) { create(:user) }
before do
stub_licensed_features(audit_events: true)
project.add_guest(guest)
end
it_behaves_like '403 response' do
let(:request) { get api(path, create(:user)) }
let(:request) { get api(path, guest) }
end
end
context 'when authenticated, as a member' do
let_it_be(:developer) { create(:user) }
before do
stub_licensed_features(audit_events: true)
project.add_developer(developer)
end
it 'returns 200 response' do
audit_event = create(:project_audit_event, entity_id: project.id, author_id: developer.id)
path = "/projects/#{project.id}/audit_events/#{audit_event.id}"
get api(path, developer)
expect(response).to have_gitlab_http_status(:ok)
end
context 'existing audit event of a different user' do
let_it_be(:audit_event) { create(:project_audit_event, entity_id: project.id, author_id: another_user.id) }
let(:path) { "/projects/#{project.id}/audit_events/#{audit_event.id}" }
it_behaves_like '404 response' do
let(:request) { get api(path, developer) }
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