Skip to content
Snippets Groups Projects
Verified Commit 915153bb authored by Jarka Košanová's avatar Jarka Košanová :three: Committed by GitLab
Browse files

Expose instance level custom roles

Changelog: added
EE: true
parent 7380cf70
No related branches found
No related tags found
1 merge request!137708Expose instance level custom roles
......@@ -577,6 +577,26 @@ This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, and `last: Int`.
 
### `Query.memberRoles`
Member roles available for the instance.
WARNING:
**Introduced** in 16.7.
This feature is an Experiment. It can be changed or removed at any time.
Returns [`MemberRoleConnection`](#memberroleconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, and `last: Int`.
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="querymemberrolesid"></a>`id` | [`MemberRoleID`](#memberroleid) | Global ID of the member role to look up. |
### `Query.mergeRequest`
 
Find a merge request.
......@@ -6,7 +6,7 @@ module MemberRoles
class RolesFinder
attr_reader :current_user, :params
VALID_PARAMS = [:parent, :id].freeze
VALID_PARAMS = [:parent, :id, :instance_roles].freeze
def initialize(current_user, params = {})
@current_user = current_user
......@@ -19,6 +19,7 @@ def execute
items = MemberRole.all
items = by_parent(items)
items = by_id(items)
items = for_instance(items)
items.ordered_by_name
end
......@@ -30,6 +31,7 @@ def validate_arguments!
end
def valid_params
params.delete(:instance_roles) unless can_read_instance_roles?
params.slice(*VALID_PARAMS)
end
......@@ -49,18 +51,29 @@ def by_id(items)
items.by_namespace(allowed_group_ids(items))
end
def for_instance(items)
return items if params[:instance_roles].blank?
items.by_namespace(nil)
end
def root_ancestor
params[:parent]&.root_ancestor
end
def can_read_instance_roles?
# for SaaS only group level roles are allowed
return false if Gitlab::Saas.feature_available?(:group_custom_roles)
Ability.allowed?(current_user, :admin_member_role)
end
def allowed_group_ids(items)
items.select { |item| allowed_read_member_role?(item.namespace) }.map(&:namespace_id)
end
def allowed_read_member_role?(group)
return false unless Ability.allowed?(current_user, :admin_group_member, group)
group.custom_roles_enabled?
Ability.allowed?(current_user, :admin_member_role, group)
end
end
end
......@@ -144,6 +144,10 @@ module QueryType
null: true,
description: 'Instance-level Amazon S3 configurations for audit events.',
resolver: ::Resolvers::AuditEvents::Instance::AmazonS3ConfigurationsResolver
field :member_roles, ::Types::MemberRoles::MemberRoleType.connection_type,
null: true, description: 'Member roles available for the instance.',
resolver: ::Resolvers::MemberRoles::RolesResolver,
alpha: { milestone: '16.7' }
end
def vulnerability(id:)
......
......@@ -11,8 +11,10 @@ class RolesResolver < BaseResolver
description: 'Global ID of the member role to look up.'
def resolve_with_lookahead(id: nil)
params = { parent: object }
params = {}
params = { parent: object } if object
params[:id] = id.model_id if id.present?
params[:instance_roles] = true if instance_roles?(params)
member_roles = ::MemberRoles::RolesFinder.new(current_user, params).execute
member_roles = member_roles.with_members_count if selects_field?(:members_count)
......@@ -29,6 +31,13 @@ def selected_fields
def selects_field?(name)
lookahead.selects?(:members_count) || selected_fields.include?(name)
end
def instance_roles?(params)
return false if params[:id]
return false if object
true
end
end
end
end
......@@ -112,6 +112,10 @@ module GlobalPolicy
enable :manage_subscription
end
rule { admin & custom_roles_allowed }.policy do
enable :admin_member_role
end
rule { admin & pages_size_limit_available }.enable :update_max_pages_size
rule { admin & runner_performance_insights_available }.enable :read_jobs_statistics
......
......@@ -8,14 +8,18 @@
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:member_role_1) { create(:member_role, name: 'Tester', namespace: group) }
let_it_be(:member_role_2) { create(:member_role, name: 'Manager', namespace: group) }
let_it_be(:group_2_member_role) { create(:member_role) }
let_it_be(:member_role_instance) { create(:member_role, :instance) }
let_it_be(:group_2_member_role) { create(:member_role, name: 'Another role') }
let_it_be(:active_group_iterations_cadence) do
create(:iterations_cadence, group: group, active: true, duration_in_weeks: 1, title: 'one week iterations')
end
subject(:find_member_roles) { described_class.new(user, params).execute }
let(:current_user) { user }
subject(:find_member_roles) { described_class.new(current_user, params).execute }
context 'without permissions' do
context 'when filtering by group' do
......@@ -53,9 +57,17 @@
stub_licensed_features(custom_roles: true)
end
context 'when filter param is missing' do
let(:params) { {} }
it 'raises an error' do
expect { find_member_roles }.to raise_error { ArgumentError }
end
end
context 'when filtering by group' do
it 'returns all member roles of the group' do
expect(find_member_roles).to contain_exactly(member_role_2, member_role_1)
expect(find_member_roles).to eq([member_role_2, member_role_1])
end
end
......@@ -63,7 +75,7 @@
let(:params) { { parent: project } }
it 'returns all member roles of the project root ancestor' do
expect(find_member_roles).to contain_exactly(member_role_2, member_role_1)
expect(find_member_roles).to eq([member_role_2, member_role_1])
end
end
......@@ -71,7 +83,7 @@
let(:params) { { id: member_role_2.id } }
it 'returns member role found by id' do
expect(find_member_roles).to contain_exactly(member_role_2)
expect(find_member_roles).to eq([member_role_2])
end
end
......@@ -79,7 +91,31 @@
let(:params) { { id: [member_role_1.id, member_role_2.id, group_2_member_role.id] } }
it 'returns only member roles a user can read' do
expect(find_member_roles).to contain_exactly(member_role_2, member_role_1)
expect(find_member_roles).to eq([member_role_2, member_role_1])
end
context 'when a user is an instance admin', :enable_admin_mode do
let(:current_user) { admin }
it 'returns all requested member roles for the instance admin' do
expect(find_member_roles).to eq([group_2_member_role, member_role_2, member_role_1])
end
end
end
context 'when requesting roles for the whole instance' do
let(:params) { { instance_roles: true } }
it 'raises an error' do
expect { find_member_roles }.to raise_error { ArgumentError }
end
context 'when a user is an instance admin', :enable_admin_mode do
let(:current_user) { admin }
it 'returns instance member roles for instance admin' do
expect(find_member_roles).to eq([member_role_instance])
end
end
end
end
......
......@@ -34,7 +34,8 @@
:instance_google_cloud_logging_configurations,
:audit_events_instance_amazon_s3_configurations,
:member_role,
:self_managed_add_on_eligible_users
:self_managed_add_on_eligible_users,
:member_roles
]
all_expected_fields = expected_foss_fields + expected_ee_fields
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Query.group_member_role', feature_category: :system_access do
include GraphqlHelpers
def member_roles_query
<<~QUERY
{
memberRoles {
nodes {
id
name
}
}
}
QUERY
end
let_it_be(:group_member_role) { create(:member_role, read_code: true) }
let_it_be(:instance_role) { create(:member_role, :instance, read_vulnerability: true) }
let_it_be(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
subject(:roles) do
graphql_data.dig('memberRoles', 'nodes')
end
before_all do
group_member_role.namespace.add_owner(user)
end
context 'with custom roles feature', :enable_admin_mode do
before do
stub_licensed_features(custom_roles: true)
end
context 'for an instance admin' do
before do
post_graphql(member_roles_query, current_user: admin)
end
context 'when running on gitlab.com', :saas do
it 'raises an error' do
expect { roles }.to raise_error { ArgumentError }
end
end
context 'on self-managed' do
it_behaves_like 'a working graphql query'
it 'returns instance roles' do
expected_result = [
{ 'id' => instance_role.to_global_id.to_s, 'name' => instance_role.name }
]
expect(roles).to match_array(expected_result)
end
end
end
context 'for a group owner' do
before do
post_graphql(member_roles_query, current_user: user)
end
it 'does not return any member roles' do
expect { roles }.to raise_error { ArgumentError }
end
end
end
context 'without custom roles feature', :enable_admin_mode do
before do
stub_licensed_features(custom_roles: false)
post_graphql(member_roles_query, current_user: admin)
end
it 'does not return any member roles' do
expect { roles }.to raise_error { ArgumentError }
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment