Commit c9a3e0d4 authored by Subashis's avatar Subashis

Auditor can see and add projects

- Add the ability to pass membership param from frontend
- Adds new policy for add project to accomodate auditor
- Adds and update specs
- Adds change log
parent 42c050db
...@@ -19,6 +19,12 @@ export default { ...@@ -19,6 +19,12 @@ export default {
ProjectList, ProjectList,
ProjectSelector, ProjectSelector,
}, },
props: {
isAuditor: {
type: Boolean,
required: true,
},
},
data() { data() {
return { return {
searchQuery: '', searchQuery: '',
...@@ -217,6 +223,7 @@ export default { ...@@ -217,6 +223,7 @@ export default {
after: pageInfo.endCursor, after: pageInfo.endCursor,
searchNamespaces: true, searchNamespaces: true,
sort: 'similarity', sort: 'similarity',
membership: !this.isAuditor,
}, },
}); });
}, },
......
...@@ -7,12 +7,13 @@ query getProjects( ...@@ -7,12 +7,13 @@ query getProjects(
$first: Int! $first: Int!
$searchNamespaces: Boolean = false $searchNamespaces: Boolean = false
$sort: String $sort: String
$membership: Boolean = true
) { ) {
projects( projects(
search: $search search: $search
after: $after after: $after
first: $first first: $first
membership: true membership: $membership
searchNamespaces: $searchNamespaces searchNamespaces: $searchNamespaces
sort: $sort sort: $sort
) { ) {
......
import Vue from 'vue'; import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import ProjectManager from './components/first_class_project_manager/project_manager.vue'; import ProjectManager from './components/first_class_project_manager/project_manager.vue';
import apolloProvider from './graphql/provider'; import apolloProvider from './graphql/provider';
...@@ -7,11 +8,17 @@ export default (el) => { ...@@ -7,11 +8,17 @@ export default (el) => {
return null; return null;
} }
const { isAuditor } = el.dataset;
return new Vue({ return new Vue({
el, el,
apolloProvider, apolloProvider,
render(createElement) { render(createElement) {
return createElement(ProjectManager); return createElement(ProjectManager, {
props: {
isAuditor: parseBoolean(isAuditor),
},
});
}, },
}); });
}; };
...@@ -5,7 +5,7 @@ module Mutations ...@@ -5,7 +5,7 @@ module Mutations
class AddProject < BaseMutation class AddProject < BaseMutation
graphql_name 'AddProjectToSecurityDashboard' graphql_name 'AddProjectToSecurityDashboard'
authorize :developer_access authorize :add_project_to_instance_security_dashboard
field :project, Types::ProjectType, field :project, Types::ProjectType,
null: true, null: true,
......
...@@ -23,4 +23,10 @@ module SecurityHelper ...@@ -23,4 +23,10 @@ module SecurityHelper
is_unavailable: "true" is_unavailable: "true"
} }
end end
def instance_security_settings_data
{
is_auditor: current_user.auditor?.to_s
}
end
end end
...@@ -370,6 +370,8 @@ module EE ...@@ -370,6 +370,8 @@ module EE
prevent(*create_update_admin(feature)) prevent(*create_update_admin(feature))
end end
end end
rule { auditor | can?(:developer_access) }.enable :add_project_to_instance_security_dashboard
end end
override :lookup_access_level! override :lookup_access_level!
......
- page_title _('Settings') - page_title _('Settings')
#js-security #js-security{ data: instance_security_settings_data }
---
title: "“Security Center project selector returns projects and gives the ability to
add project for Auditor user”"
merge_request: 58841
author:
type: added
...@@ -32,6 +32,10 @@ describe('Project Manager component', () => { ...@@ -32,6 +32,10 @@ describe('Project Manager component', () => {
}, },
}; };
const defaultProps = {
isAuditor: false,
};
const createWrapper = ({ data = {}, mocks = {}, props = {} }) => { const createWrapper = ({ data = {}, mocks = {}, props = {} }) => {
spyQuery = defaultMocks.$apollo.query; spyQuery = defaultMocks.$apollo.query;
spyMutate = defaultMocks.$apollo.mutate; spyMutate = defaultMocks.$apollo.mutate;
...@@ -40,7 +44,7 @@ describe('Project Manager component', () => { ...@@ -40,7 +44,7 @@ describe('Project Manager component', () => {
return { ...data }; return { ...data };
}, },
mocks: { ...defaultMocks, ...mocks }, mocks: { ...defaultMocks, ...mocks },
propsData: props, propsData: { ...defaultProps, ...props },
}); });
}; };
...@@ -83,6 +87,7 @@ describe('Project Manager component', () => { ...@@ -83,6 +87,7 @@ describe('Project Manager component', () => {
after: '', after: '',
searchNamespaces: true, searchNamespaces: true,
sort: 'similarity', sort: 'similarity',
membership: true,
}, },
}); });
}); });
...@@ -217,4 +222,18 @@ describe('Project Manager component', () => { ...@@ -217,4 +222,18 @@ describe('Project Manager component', () => {
expect(spyQuery).not.toHaveBeenCalled(); expect(spyQuery).not.toHaveBeenCalled();
}); });
}); });
describe('membership prop', () => {
it.each([true, false])('calls the expected query when membership prop is $s', (isAuditor) => {
createWrapper({ props: { isAuditor } });
findProjectSelector().vm.$emit('searched', 'test');
expect(spyQuery).toHaveBeenCalledTimes(1);
expect(spyQuery).toHaveBeenCalledWith(
expect.objectContaining({
variables: expect.objectContaining({ membership: !isAuditor }),
}),
);
});
});
}); });
...@@ -64,7 +64,18 @@ RSpec.describe Mutations::InstanceSecurityDashboard::AddProject do ...@@ -64,7 +64,18 @@ RSpec.describe Mutations::InstanceSecurityDashboard::AddProject do
end end
end end
context 'when project is not available to the user' do context 'when user is auditor and project is not available to the user explicitly' do
let(:selected_project) { project }
let(:current_user) { create(:user, :auditor) }
it 'adds project to the security dashboard', :aggregate_failures do
expect(subject[:project]).to eq(project)
expect(subject[:errors]).to be_empty
expect(current_user.security_dashboard_projects).to include(project)
end
end
context 'when project is not available to the user and user is not auditor' do
let(:selected_project) { project } let(:selected_project) { project }
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable error' do it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable error' do
......
...@@ -23,4 +23,20 @@ RSpec.describe SecurityHelper do ...@@ -23,4 +23,20 @@ RSpec.describe SecurityHelper do
}) })
end end
end end
describe '#instance_security_settings_data' do
subject { instance_security_settings_data }
context 'when user is not auditor' do
let_it_be(:current_user) { create(:user) }
it { is_expected.to eq({ is_auditor: "false" }) }
end
context 'when user is auditor' do
let_it_be(:current_user) { create(:user, :auditor) }
it { is_expected.to eq({ is_auditor: "true" }) }
end
end
end end
...@@ -1102,6 +1102,30 @@ RSpec.describe ProjectPolicy do ...@@ -1102,6 +1102,30 @@ RSpec.describe ProjectPolicy do
end end
end end
describe 'add_project_to_instance_security_dashboard' do
let(:policy) { :add_project_to_instance_security_dashboard }
context 'when user is auditor' do
let(:current_user) { create(:user, :auditor) }
it { is_expected.to be_allowed(policy) }
end
context 'when user is not auditor' do
context 'with developer access' do
let(:current_user) { developer }
it { is_expected.to be_allowed(policy) }
end
context 'without developer access' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(policy) }
end
end
end
context 'visual review bot' do context 'visual review bot' do
let(:current_user) { User.visual_review_bot } let(:current_user) { User.visual_review_bot }
......
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