Commit 958afbb9 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '257514-audit-events-api-for-non-admin' into 'master'

Extend ability to read audit events via API to more roles

See merge request gitlab-org/gitlab!49106
parents 2701cc2e 3f2e821d
......@@ -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