ee/spec/policies/group_policy_spec.rb (3,689 lines of code) (raw):
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GroupPolicy, feature_category: :groups_and_projects do
include AdminModeHelper
include LoginHelpers
using RSpec::Parameterized::TableSyntax
def stub_group_saml_config(enabled)
allow(::Gitlab::Auth::GroupSaml::Config).to receive_messages(enabled?: enabled)
end
include_context 'GroupPolicy context'
# Can't move to GroupPolicy context because auditor trait is not present
# outside of EE context and FOSS will fail on this
let_it_be(:auditor) { create(:user, :auditor) }
let(:epic_rules) do
%i[read_epic create_epic admin_epic destroy_epic read_confidential_epic
read_epic_board read_epic_board_list admin_epic_board
admin_epic_board_list]
end
let(:auditor_permissions) do
%i[
read_group
read_group_security_dashboard
read_cluster
read_group_runners
read_billing
read_container_image
read_confidential_issues
read_cycle_analytics
]
end
context 'when epics feature is disabled' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(*epic_rules) }
end
context 'when epics feature is enabled' do
before do
stub_licensed_features(epics: true)
end
context 'when user is owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(*epic_rules) }
end
context 'when user is admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(*epic_rules) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(*epic_rules) }
end
end
context 'when user is maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(*(epic_rules - [:destroy_epic])) }
it { is_expected.to be_disallowed(:destroy_epic) }
end
context 'when user is developer' do
let(:current_user) { developer }
it { is_expected.to be_allowed(*(epic_rules - [:destroy_epic])) }
it { is_expected.to be_disallowed(:destroy_epic) }
end
context 'when user is reporter' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(*(epic_rules - [:destroy_epic])) }
it { is_expected.to be_disallowed(:destroy_epic) }
end
context 'when user is planner' do
let(:current_user) { planner }
it { is_expected.to be_allowed(*epic_rules) }
end
context 'when user is guest' do
let(:current_user) { guest }
it { is_expected.to be_allowed(:read_epic, :read_epic_board, :list_subgroup_epics) }
it { is_expected.to be_disallowed(*(epic_rules - [:read_epic, :read_epic_board, :read_epic_board_list])) }
end
context 'when user is support bot' do
let_it_be(:current_user) { Users::Internal.support_bot }
before do
allow(::ServiceDesk).to receive(:supported?).and_return(true)
end
context 'when group has at least one project with service desk enabled' do
let_it_be(:project_with_service_desk) do
create(:project, group: group, service_desk_enabled: true)
end
it { is_expected.to be_allowed(:read_epic, :read_epic_iid) }
it { is_expected.to be_disallowed(*(epic_rules - [:read_epic, :read_epic_iid])) }
end
context 'when group does not have projects with service desk enabled' do
let_it_be(:project_without_service_desk) do
create(:project, group: group, service_desk_enabled: false)
end
it { is_expected.to be_disallowed(*epic_rules) }
end
end
context 'when user is not member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(*epic_rules) }
end
context 'when user is anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(*epic_rules) }
end
end
context 'when iterations feature is disabled' do
let(:current_user) { owner }
before do
stub_licensed_features(iterations: false)
end
it { is_expected.to be_disallowed(:read_iteration, :create_iteration, :admin_iteration, :create_iteration_cadence, :admin_iteration_cadence) }
end
context 'when iterations feature is enabled' do
let(:read_actions) { [:read_iteration, :read_iteration_cadence] }
let(:edit_actions) { [:create_iteration, :admin_iteration, :create_iteration_cadence, :admin_iteration_cadence] }
before do
stub_licensed_features(iterations: true)
end
where(:role, :actions, :allowed) do
:none | ref(:read_actions) | false
:none | ref(:edit_actions) | false
:guest | ref(:read_actions) | true
:guest | ref(:edit_actions) | false
:planner | ref(:read_actions) | true
:planner | ref(:edit_actions) | true
:reporter | ref(:read_actions) | true
:reporter | ref(:edit_actions) | true
end
with_them do
let(:current_user) { try(role) }
it { is_expected.to(allowed ? be_allowed(*actions) : be_disallowed(*actions)) }
end
context 'when project is public' do
let(:group) { create(:group, :public, :owner_subgroup_creation_only) }
context 'when user is logged out' do
let(:current_user) { nil }
it { is_expected.to be_allowed(:read_iteration, :read_iteration_cadence) }
it { is_expected.to be_disallowed(:create_iteration, :admin_iteration, :create_iteration_cadence, :admin_iteration_cadence) }
end
end
end
context 'when custom fields are available' do
before do
stub_licensed_features(custom_fields: true)
end
context 'when user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_allowed(:read_custom_field) }
it { is_expected.to be_disallowed(:admin_custom_field) }
end
context 'when user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:read_custom_field, :admin_custom_field) }
end
context 'when user is logged out' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:read_custom_field) }
end
end
context 'when custom fields are not available' do
let(:current_user) { guest }
before do
stub_licensed_features(custom_fields: false)
end
it { is_expected.to be_disallowed(:read_custom_field, :admin_custom_field) }
end
context 'when cluster deployments is available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(cluster_deployments: true)
end
it { is_expected.to be_allowed(:read_cluster_environments) }
end
context 'when cluster deployments is not available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(cluster_deployments: false)
end
it { is_expected.not_to be_allowed(:read_cluster_environments) }
end
describe 'invite_group_members policy' do
context 'when on saas', :saas do
let(:policy) { :invite_group_members }
let(:app_setting) { :disable_invite_members }
before do
stub_saas_features(group_disable_invite_members: true)
end
context 'with disable_invite_members is available in license' do
where(:role, :group_setting, :application_setting, :allowed) do
:guest | true | true | false
:planner | true | true | false
:reporter | true | true | false
:developer | true | true | false
:maintainer | false | true | false
:maintainer | true | true | false
:owner | false | true | true
:owner | false | false | true
:owner | true | true | false
:owner | true | false | false
:admin | false | true | true
:admin | false | false | true
:admin | false | true | true
:admin | false | false | true
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(disable_invite_members: true)
stub_application_setting(app_setting => application_setting)
allow(group).to receive(:disable_invite_members?).and_return(group_setting)
enable_admin_mode!(current_user) if role == :admin
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context 'with disable_invite_members not available in license' do
where(:role, :group_setting, :application_setting, :allowed) do
:guest | true | true | false
:planner | true | true | false
:reporter | true | true | false
:developer | true | true | false
:maintainer | false | true | false
:maintainer | true | true | false
:owner | false | true | true
:owner | false | false | true
:owner | true | false | true
:owner | true | true | true
:admin | false | true | true
:admin | true | false | true
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(disable_invite_members: false)
stub_application_setting(app_setting => application_setting)
allow(group).to receive(:disable_invite_members?).and_return(group_setting)
enable_admin_mode!(current_user) if role == :admin
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
context 'when self-managed' do
before do
allow(Gitlab).to receive(:com?).and_return(false)
end
let(:app_setting) { :disable_invite_members }
let(:policy) { :invite_group_members }
context 'with disable_invite_members available in license' do
where(:role, :setting, :admin_mode, :allowed) do
:guest | true | nil | false
:planner | true | nil | false
:reporter | true | nil | false
:developer | true | nil | false
:maintainer | false | nil | false
:maintainer | true | nil | false
:owner | false | nil | true
:owner | true | nil | false
:admin | false | false | false
:admin | false | true | true
:admin | true | false | false
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(disable_invite_members: true)
stub_application_setting(app_setting => setting)
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context 'with disable_invite_members not available in license' do
where(:role, :setting, :admin_mode, :allowed) do
:guest | true | nil | false
:planner | true | nil | false
:reporter | true | nil | false
:developer | true | nil | false
:maintainer | false | nil | false
:maintainer | true | nil | false
:owner | false | nil | true
:owner | true | nil | true
:admin | false | false | false
:admin | false | true | true
:admin | true | false | false
:admin | true | true | true
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(disable_invite_members: false)
stub_application_setting(app_setting => setting)
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
end
describe 'modify_value_stream_dashboard_settings policy' do
context 'when analytics dashboard is available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(group_level_analytics_dashboard: true)
end
it { is_expected.to be_allowed(:modify_value_stream_dashboard_settings) }
context 'when the current user is the owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:modify_value_stream_dashboard_settings) }
end
context 'when the current user is admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:modify_value_stream_dashboard_settings) }
end
context 'when a sub-group is given' do
let(:sub_group) { create(:group, :private, parent: group) }
subject { described_class.new(maintainer, sub_group) }
it { is_expected.not_to be_allowed(:modify_value_stream_dashboard_settings) }
end
context 'when the user is not a maintainer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:modify_value_stream_dashboard_settings) }
end
end
context 'when analytics dashboard is not available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(group_level_analytics_dashboard: false)
end
it { is_expected.not_to be_allowed(:modify_value_stream_dashboard_settings) }
end
end
context 'when contribution analytics is available' do
let(:current_user) { developer }
before do
stub_licensed_features(contribution_analytics: true)
end
context 'when signed in user is a member of the group' do
it { is_expected.to be_allowed(:read_group_contribution_analytics) }
end
describe 'when user is not a member of the group' do
let(:current_user) { non_group_member }
let(:private_group) { create(:group, :private) }
subject { described_class.new(non_group_member, private_group) }
context 'when user is not invited to any of the group projects' do
it { is_expected.not_to be_allowed(:read_group_contribution_analytics) }
end
context 'when user is invited to a group project, but not to the group' do
let(:private_project) { create(:project, :private, group: private_group) }
before do
private_project.add_guest(non_group_member)
end
it { is_expected.not_to be_allowed(:read_group_contribution_analytics) }
end
context 'when user has an auditor role' do
before do
allow(current_user).to receive(:auditor?).and_return(true)
end
it { is_expected.to be_allowed(:read_group_contribution_analytics) }
end
end
end
context 'when contribution analytics is not available' do
let(:current_user) { developer }
before do
stub_licensed_features(contribution_analytics: false)
end
it { is_expected.not_to be_allowed(:read_group_contribution_analytics) }
end
context 'when dora4 analytics is available' do
before do
stub_licensed_features(dora4_analytics: true)
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.to be_allowed(:read_dora4_analytics) }
end
context 'when the user is an auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_dora4_analytics) }
end
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:read_dora4_analytics) }
end
end
context 'when dora4 analytics is not available' do
let(:current_user) { developer }
before do
stub_licensed_features(dora4_analytics: false)
end
it { is_expected.not_to be_allowed(:read_dora4_analytics) }
end
describe ':read_product_analytics', :enable_admin_mode do
where(:role, :allowed) do
:guest | false
:planner | false
:reporter | true
:developer | true
:admin | true
end
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to(allowed ? be_allowed(:read_product_analytics) : be_disallowed(:read_product_analytics)) }
end
end
describe ':read_enterprise_ai_analytics' do
context 'when on SAAS', :saas do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
end
context 'when on self-managed' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :self_managed) }
it_behaves_like 'ai permission to', :read_enterprise_ai_analytics
end
end
describe ':read_pro_ai_analytics' do
context 'when on SAAS', :saas do
context 'with pro subscription' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_pro, namespace: group) }
it_behaves_like 'ai permission to', :read_pro_ai_analytics
end
context 'with enterprise subscription' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, namespace: group) }
it_behaves_like 'ai permission to', :read_pro_ai_analytics
end
end
context 'when on self-managed' do
context 'with pro subscription' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_pro, :self_managed) }
it_behaves_like 'ai permission to', :read_pro_ai_analytics
end
context 'with enterprise subscription' do
let(:subscription_purchase) { create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :self_managed) }
it_behaves_like 'ai permission to', :read_pro_ai_analytics
end
end
end
describe 'analytics value streams' do
context 'when feature is not available' do
context 'and user is admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.not_to be_allowed(:admin_value_stream) }
end
context 'and user is reporter' do
let(:current_user) { reporter }
it { is_expected.not_to be_allowed(:admin_value_stream) }
end
end
context 'when user feature is available' do
before do
stub_licensed_features(cycle_analytics_for_groups: true)
end
context 'and user is guest' do
let(:current_user) { guest }
it { is_expected.not_to be_allowed(:admin_value_stream) }
end
context 'and user is reporter' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:admin_value_stream) }
end
context 'and user is admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:admin_value_stream) }
end
end
end
context 'export group memberships' do
let(:current_user) { owner }
context 'when exporting user permissions is not available' do
before do
stub_licensed_features(export_user_permissions: false)
end
it { is_expected.not_to be_allowed(:export_group_memberships) }
end
context 'when exporting user permissions is available' do
before do
stub_licensed_features(export_user_permissions: true)
end
it { is_expected.to be_allowed(:export_group_memberships) }
end
end
context 'when group activity analytics is available' do
let(:current_user) { developer }
before do
stub_licensed_features(group_activity_analytics: true)
end
it { is_expected.to be_allowed(:read_group_activity_analytics) }
end
context 'when group activity analytics is not available' do
let(:current_user) { developer }
before do
stub_licensed_features(group_activity_analytics: false)
end
it { is_expected.not_to be_allowed(:read_group_activity_analytics) }
end
context 'group CI/CD analytics' do
context 'when group CI/CD analytics is available' do
before do
stub_licensed_features(group_ci_cd_analytics: true)
end
context 'when the user has at least reporter permissions' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:view_group_ci_cd_analytics) }
end
context 'when the user has less than reporter permissions' do
let(:current_user) { guest }
it { is_expected.not_to be_allowed(:view_group_ci_cd_analytics) }
end
context 'when the user has auditor permissions' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:view_group_ci_cd_analytics) }
end
end
context 'when group CI/CD analytics is not available' do
let(:current_user) { reporter }
before do
stub_licensed_features(group_ci_cd_analytics: false)
end
it { is_expected.not_to be_allowed(:view_group_ci_cd_analytics) }
end
end
context 'when group repository analytics is available' do
before do
stub_licensed_features(group_repository_analytics: true)
end
context 'for guests' do
let(:current_user) { guest }
it { is_expected.not_to be_allowed(:read_group_repository_analytics) }
end
context 'for reporter+' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:read_group_repository_analytics) }
end
context 'for auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_group_repository_analytics) }
end
end
context 'when group repository analytics is not available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(group_repository_analytics: false)
end
it { is_expected.not_to be_allowed(:read_group_repository_analytics) }
end
context 'when group cycle analytics is available' do
before do
stub_licensed_features(cycle_analytics_for_groups: true)
end
context 'for guests' do
let(:current_user) { guest }
it { is_expected.not_to be_allowed(:read_cycle_analytics) }
it { is_expected.not_to be_allowed(:read_group_stage) }
it { is_expected.not_to be_allowed(:view_type_of_work_charts) }
end
context 'for reporter+' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:read_cycle_analytics) }
it { is_expected.to be_allowed(:read_group_stage) }
it { is_expected.to be_allowed(:view_type_of_work_charts) }
end
context 'for auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_cycle_analytics) }
it { is_expected.to be_allowed(:read_group_stage) }
it { is_expected.to be_allowed(:view_type_of_work_charts) }
end
end
context 'when group cycle analytics is not available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(cycle_analytics_for_groups: false)
end
it { is_expected.not_to be_allowed(:read_cycle_analytics) }
end
context 'when group coverage reports is available' do
before do
stub_licensed_features(group_coverage_reports: true)
end
context 'for guests' do
let(:current_user) { guest }
it { is_expected.not_to be_allowed(:read_group_coverage_reports) }
end
context 'for reporter+' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:read_group_coverage_reports) }
end
end
context 'when group coverage reports is not available' do
let(:current_user) { maintainer }
before do
stub_licensed_features(group_coverage_reports: false)
end
it { is_expected.not_to be_allowed(:read_group_coverage_reports) }
end
describe 'per group SAML' do
context 'when group_saml is unavailable' do
let(:current_user) { owner }
context 'when group saml config is disabled' do
before do
stub_group_saml_config(false)
end
it { is_expected.to be_disallowed(:admin_group_saml) }
end
context 'when the group is a subgroup' do
let_it_be(:subgroup) { create(:group, :private, parent: group) }
before do
stub_group_saml_config(true)
end
subject { described_class.new(current_user, subgroup) }
it { is_expected.to be_disallowed(:admin_group_saml) }
end
context 'when the feature is not licensed' do
before do
stub_group_saml_config(true)
stub_licensed_features(group_saml: false)
end
it { is_expected.to be_disallowed(:admin_group_saml) }
end
end
context 'when group_saml is available' do
before do
stub_licensed_features(group_saml: true)
end
context 'when group_saml_group_sync is not licensed' do
context 'with an enabled SAML provider' do
let_it_be(:saml_provider) { create(:saml_provider, group: group, enabled: true) }
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'admin' do
let(:current_user) { admin }
it 'is disallowed even with admin mode', :enable_admin_mode do
is_expected.to be_disallowed(:admin_saml_group_links)
end
end
end
end
context 'when group_saml_group_sync is licensed', :saas do
before do
stub_group_saml_config(true)
stub_application_setting(check_namespace_plan: true)
end
before_all do
create(:license, plan: License::ULTIMATE_PLAN)
create(:gitlab_subscription, :premium, namespace: group)
end
context 'without an enabled SAML provider' do
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_group_saml) }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_group_saml) }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_group_saml) }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:admin_group_saml) }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
end
end
context 'with an enabled SAML provider' do
let_it_be(:saml_provider) { create(:saml_provider, group: group, enabled: true) }
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_saml_group_links) }
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_saml_group_links) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
end
context 'when the group is a subgroup' do
let_it_be(:subgroup) { create(:group, :private, parent: group) }
let(:current_user) { owner }
subject { described_class.new(current_user, subgroup) }
it { is_expected.to be_allowed(:admin_saml_group_links) }
end
end
end
context 'with SSO enforcement enabled' do
let(:current_user) { guest }
let_it_be(:saml_provider) { create(:saml_provider, group: group, enforced_sso: true) }
context 'when in context of the user web activity' do
around do |example|
session = {}
session['warden.user.user.key'] = [[current_user.id], current_user.authenticatable_salt]
Gitlab::Session.with_session(session) do
example.run
end
end
it 'prevents access without a SAML session' do
is_expected.not_to be_allowed(:read_group)
end
it 'allows access with a SAML session' do
Gitlab::Auth::GroupSaml::SsoEnforcer.new(saml_provider).update_session
is_expected.to be_allowed(:read_group)
end
end
context 'when there is no global session or sso state' do
it "allows access because we haven't yet restricted all use cases" do
is_expected.to be_allowed(:read_group)
end
context 'when the current user is a deploy token' do
let(:current_user) { create(:deploy_token, :group, groups: [group], read_package_registry: true) }
it 'allows access without a SAML session' do
is_expected.to allow_action(:read_group)
end
end
end
end
context 'without SSO enforcement enabled' do
let(:current_user) { guest }
let_it_be(:saml_provider) { create(:saml_provider, group: group, enforced_sso: false) }
context 'when in context of the user web activity' do
around do |example|
session = {}
session['warden.user.user.key'] = [[current_user.id], current_user.authenticatable_salt]
Gitlab::Session.with_session(session) do
example.run
end
end
it 'allows access when the user has no Group SAML identity' do
is_expected.to be_allowed(:read_group)
end
end
context 'when there is no global session or sso state' do
context 'when the current user is a deploy token' do
let(:current_user) { create(:deploy_token, :group, groups: [group], read_package_registry: true) }
it 'allows access without a SAML session' do
is_expected.to allow_action(:read_group)
end
end
end
end
end
context 'reading a group' do
context 'when SAML SSO is enabled for resource' do
let(:saml_provider) { create(:saml_provider, enabled: true, enforced_sso: false) }
let(:identity) { create(:group_saml_identity, saml_provider: saml_provider) }
let(:root_group) { saml_provider.group }
let(:subgroup) { create(:group, parent: root_group) }
let(:member_with_identity) { identity.user }
let(:member_without_identity) { create(:user) }
let(:non_member) { create(:user) }
let(:not_signed_in_user) { nil }
before do
stub_licensed_features(group_saml: true)
root_group.add_developer(member_with_identity)
root_group.add_developer(member_without_identity)
end
subject { described_class.new(current_user, resource) }
shared_examples 'does not allow read group' do
it 'does not allow read group' do
is_expected.not_to allow_action(:read_group)
end
end
shared_examples 'allows to read group' do
it 'allows read group' do
is_expected.to allow_action(:read_group)
end
end
shared_examples 'does not allow to read group due to its visibility level' do
it 'does not allow to read group due to its visibility level', :aggregate_failures do
expect(resource.root_ancestor.saml_provider.enforced_sso?).to eq(false)
is_expected.not_to allow_action(:read_group)
end
end
# See https://docs.gitlab.com/ee/user/group/saml_sso/#sso-enforcement
where(:resource, :resource_visibility_level, :enforced_sso?, :user, :user_is_resource_owner?, :user_with_saml_session?, :user_is_admin?, :enable_admin_mode?, :user_is_auditor?, :shared_examples) do
# Project/Group visibility: Private; Enforce SSO setting: Off
ref(:root_group) | 'private' | false | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'private' | false | ref(:member_with_identity) | true | false | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:root_group) | 'private' | false | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | true | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:member_without_identity) | false | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:member_without_identity) | false | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:non_member) | nil | nil | nil | nil | nil | 'does not allow to read group due to its visibility level'
ref(:root_group) | 'private' | false | ref(:non_member) | nil | nil | true | false | nil | 'does not allow to read group due to its visibility level'
ref(:root_group) | 'private' | false | ref(:non_member) | nil | nil | true | true | nil | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:non_member) | nil | nil | nil | nil | true | 'allows to read group'
ref(:root_group) | 'private' | false | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'does not allow to read group due to its visibility level'
ref(:subgroup) | 'private' | false | ref(:non_member) | nil | nil | nil | nil | nil | 'does not allow to read group due to its visibility level'
ref(:subgroup) | 'private' | false | ref(:non_member) | nil | nil | true | false | nil | 'does not allow to read group due to its visibility level'
ref(:subgroup) | 'private' | false | ref(:non_member) | nil | nil | true | true | nil | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:non_member) | nil | nil | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'private' | false | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'does not allow to read group due to its visibility level'
# Project/Group visibility: Private; Enforce SSO setting: On
ref(:root_group) | 'private' | true | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:member_with_identity) | true | false | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | true | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_without_identity) | false | nil | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:member_without_identity) | true | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_without_identity) | false | nil | true | false | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:member_without_identity) | false | nil | true | true | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:member_without_identity) | false | nil | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:member_without_identity) | false | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_without_identity) | true | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_without_identity) | false | nil | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:member_without_identity) | false | nil | true | true | nil | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:member_without_identity) | false | nil | nil | nil | true | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:non_member) | nil | nil | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:non_member) | nil | nil | true | false | nil | 'does not allow read group'
ref(:root_group) | 'private' | true | ref(:non_member) | nil | nil | true | true | nil | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:non_member) | nil | nil | nil | nil | true | 'allows to read group'
ref(:root_group) | 'private' | true | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:non_member) | nil | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:non_member) | nil | nil | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'private' | true | ref(:non_member) | nil | nil | true | true | nil | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:non_member) | nil | nil | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'private' | true | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'does not allow read group'
# Project/Group visibility: Public; Enforce SSO setting: Off
ref(:root_group) | 'public' | false | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'public' | false | ref(:member_with_identity) | true | false | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:root_group) | 'public' | false | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | true | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:member_without_identity) | false | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:member_without_identity) | false | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:non_member) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | false | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:non_member) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | false | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'allows to read group'
# Project/Group visibility: Public; Enforce SSO setting: On
ref(:root_group) | 'public' | true | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'public' | true | ref(:member_with_identity) | true | false | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:root_group) | 'public' | true | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | false | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | true | false | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | false | true | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | false | false | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | false | false | true | true | nil | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:member_with_identity) | false | false | nil | nil | true | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_without_identity) | false | nil | nil | nil | nil | 'does not allow read group'
ref(:root_group) | 'public' | true | ref(:member_without_identity) | true | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_without_identity) | false | nil | true | false | nil | 'does not allow read group'
ref(:root_group) | 'public' | true | ref(:member_without_identity) | false | nil | true | true | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:member_without_identity) | false | nil | nil | nil | true | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:member_without_identity) | false | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_without_identity) | true | nil | nil | nil | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_without_identity) | false | nil | true | false | nil | 'does not allow read group'
ref(:subgroup) | 'public' | true | ref(:member_without_identity) | false | nil | true | true | nil | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:member_without_identity) | false | nil | nil | nil | true | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:non_member) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:root_group) | 'public' | true | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:non_member) | nil | nil | nil | nil | nil | 'allows to read group'
ref(:subgroup) | 'public' | true | ref(:not_signed_in_user) | nil | nil | nil | nil | nil | 'allows to read group'
end
with_them do
context "when 'Enforce SSO-only authentication for web activity for this group' option is #{params[:enforced_sso?] ? 'enabled' : 'not enabled'}" do
around do |example|
session = {}
session['warden.user.user.key'] = [[user.id], user.authenticatable_salt] if user.is_a?(User)
Gitlab::Session.with_session(session) do
example.run
end
end
before do
saml_provider.update!(enforced_sso: enforced_sso?)
end
context "when resource is #{params[:resource_visibility_level]}" do
before do
resource.update!(visibility_level: Gitlab::VisibilityLevel.string_options[resource_visibility_level])
end
context 'for user', enable_admin_mode: params[:enable_admin_mode?] do
before do
if user_is_resource_owner?
resource.root_ancestor.member(user).update_column(:access_level, Gitlab::Access::OWNER)
end
Gitlab::Auth::GroupSaml::SsoEnforcer.new(saml_provider).update_session if user_with_saml_session?
user.update!(admin: true) if user_is_admin?
user.update!(auditor: true) if user_is_auditor?
end
let(:current_user) { user }
include_examples params[:shared_examples]
end
end
end
end
end
end
end
describe 'admin_saml_group_links for global SAML' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
context 'when global SAML is enabled' do
context 'when the groups attribute is not configured' do
before do
stub_basic_saml_config
end
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
context 'when the groups attribute is configured' do
before do
stub_omniauth_config(providers: [{ name: 'saml', groups_attribute: 'Groups', args: {} }])
end
it { is_expected.to be_disallowed(:admin_saml_group_links) }
context 'when saml_group_sync feature is licensed' do
before do
stub_licensed_features(saml_group_sync: true)
end
it { is_expected.to be_allowed(:admin_saml_group_links) }
context 'when the current user is not an admin or owner' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_saml_group_links) }
end
end
end
end
end
context 'with ip restriction' do
let(:current_user) { maintainer }
before do
allow(Gitlab::IpAddressState).to receive(:current).and_return('192.168.0.2')
stub_licensed_features(group_ip_restriction: true, epics: true)
stub_config(dependency_proxy: { enabled: true })
end
context 'without restriction' do
it { is_expected.to be_allowed(:read_group) }
it { is_expected.to be_allowed(:read_milestone) }
it { is_expected.to be_allowed(:read_package) }
it { is_expected.to be_allowed(:create_package) }
it { is_expected.to be_allowed(:destroy_package) }
it { is_expected.to be_allowed(:create_epic) }
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_package) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
context 'with restriction' do
before do
create(:ip_restriction, group: group, range: range)
end
context 'address is within the range' do
let(:range) { '192.168.0.0/24' }
it { is_expected.to be_allowed(:read_group) }
it { is_expected.to be_allowed(:read_milestone) }
it { is_expected.to be_allowed(:read_package) }
it { is_expected.to be_allowed(:create_package) }
it { is_expected.to be_allowed(:destroy_package) }
it { is_expected.to be_allowed(:create_epic) }
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_package) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
context 'address is outside the range' do
let(:range) { '10.0.0.0/8' }
context 'as maintainer' do
it { is_expected.to be_disallowed(:read_group) }
it { is_expected.to be_disallowed(:read_milestone) }
it { is_expected.to be_disallowed(:read_package) }
it { is_expected.to be_disallowed(:create_package) }
it { is_expected.to be_disallowed(:destroy_package) }
it { is_expected.to be_disallowed(:create_epic) }
it { is_expected.to be_disallowed(:admin_package) }
it { is_expected.to be_disallowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
context 'as owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:read_group) }
it { is_expected.to be_allowed(:read_milestone) }
it { is_expected.to be_allowed(:read_package) }
it { is_expected.to be_allowed(:create_package) }
it { is_expected.to be_allowed(:destroy_package) }
it { is_expected.to be_allowed(:admin_package) }
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_allowed(:admin_dependency_proxy) }
end
context 'as auditor' do
let(:current_user) { create(:user, :auditor) }
it { is_expected.to be_allowed(:read_group) }
it { is_expected.to be_allowed(:read_milestone) }
it { is_expected.to be_allowed(:read_group_audit_events) }
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
end
end
end
context 'when LDAP sync is not enabled' do
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
context 'does not allow group owners to manage ldap' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
end
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
end
context 'when memberships locked to SAML' do
let(:saml_with_group_sync) do
{
name: 'saml',
groups_attribute: 'groups',
external_groups: [],
args: {}
}
end
before do
stub_application_setting(lock_memberships_to_saml: true)
stub_licensed_features(saml_group_sync: true)
end
context 'when group is a root group' do
before do
stub_omniauth_config(providers: [saml_with_group_sync])
allow(Devise).to receive(:omniauth_providers).and_return([:saml])
end
context 'when SAML group link sync is enabled' do
before do
create(:saml_group_link, group: group)
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_group_member) }
end
context 'when admin mode is disabled' do
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'when no SAML sync is enabled' do
before do
allow(group).to receive(:saml_group_links_exists?).and_return(false)
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_group_member) }
end
end
end
context 'when group is not a root group' do
let(:parent_group) { create(:group) }
let(:group) { create(:group, :private, parent: parent_group) }
before do
stub_omniauth_config(providers: [saml_with_group_sync])
allow(Devise).to receive(:omniauth_providers).and_return([:saml])
group.add_owner(owner)
parent_group.add_owner(owner)
end
context 'when SAML group link sync is enabled' do
before do
create(:saml_group_link, group: parent_group)
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_group_member) }
end
context 'when admin mode is disabled' do
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'when child group has different owner than parent group' do
let(:sub_group_owner) { create(:user) }
let(:current_user) { sub_group_owner }
before do
group.add_owner(sub_group_owner)
end
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'when no SAML group link sync is enabled' do
before do
allow(group).to receive(:saml_group_links_exists?).and_return(false)
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(:admin_group_member) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_group_member) }
end
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_group_member) }
end
end
end
context 'when group is synced via GroupSaml', :saas do
before do
stub_licensed_features(group_saml: true, saml_group_sync: true)
stub_group_saml_config(true)
create(:saml_provider, group: group.root_ancestor, enabled: true)
end
context 'when SAML group link is configured' do
before do
create(:saml_group_link, group: group)
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_group_member) }
end
context 'when admin mode is disabled' do
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
end
context 'when SAML group link is not configured' do
before do
allow(group).to receive(:saml_group_links_exists?).and_return(false)
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.not_to be_allowed(:admin_group_member) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_group_member) }
end
end
end
end
context 'when LDAP sync is enabled' do
before do
allow(group).to receive(:ldap_synced?).and_return(true)
end
context 'with no user' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'guests' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'planners' do
let(:current_user) { planner }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'owner' do
let(:current_user) { owner }
context 'allow group owners to manage ldap' do
it { is_expected.to be_allowed(:override_group_member) }
end
context 'does not allow group owners to manage ldap' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
end
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
context 'when memberships locked to LDAP' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: true)
stub_application_setting(lock_memberships_to_ldap: true)
end
context 'admin' do
let(:current_user) { admin }
context 'when admin mode enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:override_group_member) }
it { is_expected.to be_allowed(:update_group_member) }
end
context 'when admin mode disabled' do
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:update_group_member) }
end
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:admin_group_member) }
it { is_expected.not_to be_allowed(:override_group_member) }
it { is_expected.not_to be_allowed(:update_group_member) }
context 'and service_accounts feature is enabled' do
before do
stub_licensed_features(service_accounts: true)
end
it { is_expected.to be_allowed(:admin_service_account_member) }
end
end
end
end
describe 'read_group_credentials_inventory' do
using RSpec::Parameterized::TableSyntax
let(:non_member) { create(:user) }
let_it_be(:read_policy) { :read_group_credentials_inventory }
let_it_be(:admin_policy) { :admin_group_credentials_inventory }
where(:user, :admin_mode?, :saas?, :licensed?, :allowed) do
ref(:admin) | false | false | false | false
ref(:admin) | true | false | false | false
ref(:admin) | true | true | false | false
ref(:admin) | false | false | true | false
ref(:admin) | false | true | true | false
ref(:admin) | true | false | true | false
ref(:admin) | false | true | false | false
ref(:admin) | true | true | true | true
ref(:owner) | nil | false | false | false
ref(:owner) | nil | true | false | false
ref(:owner) | nil | false | true | false
ref(:owner) | nil | true | false | false
ref(:owner) | nil | true | true | true
ref(:maintainer) | nil | true | true | false
ref(:developer) | nil | true | true | false
ref(:reporter) | nil | true | true | false
ref(:guest) | nil | true | true | false
ref(:non_member) | nil | true | true | false
nil | nil | true | true | false
end
with_them do
let(:current_user) { user }
before do
stub_licensed_features(credentials_inventory: licensed?)
end
context 'for user', saas: params[:saas?], enable_admin_mode: params[:admin_mode?] do
it { is_expected.to(allowed ? be_allowed(read_policy) : be_disallowed(read_policy)) }
it { is_expected.to(allowed ? be_allowed(admin_policy) : be_disallowed(admin_policy)) }
end
end
context 'subgroup', :saas do
let(:current_user) { owner }
let(:subgroup) { create(:group, :private, parent: group) }
subject { described_class.new(current_user, subgroup) }
before do
stub_licensed_features(credentials_inventory: true)
end
it { is_expected.to be_disallowed(read_policy) }
it { is_expected.to be_disallowed(admin_policy) }
end
end
describe 'change_prevent_group_forking' do
context 'when feature is disabled' do
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
context 'when feature is enabled' do
before do
stub_licensed_features(group_forking_protection: true)
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:change_prevent_group_forking) }
context 'when group has parent' do
let(:group) { create(:group, :private, parent: create(:group)) }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_prevent_group_forking) }
end
end
end
describe 'security orchestration policies' do
before do
stub_licensed_features(security_orchestration_policies: true)
end
context 'with developer or maintainer role' do
where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:read_security_orchestration_policies) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:read_security_orchestration_policies) }
end
end
context 'with auditor role' do
where(role: %w[auditor])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:read_security_orchestration_policies) }
end
end
end
describe "security dashboard policies" do
where(:policy, :role, :admin_mode, :allowed) do
:admin_vulnerability | :admin | false | false
:admin_vulnerability | :admin | true | true
:admin_vulnerability | :auditor | nil | false
:admin_vulnerability | :developer | nil | false
:admin_vulnerability | :guest | nil | false
:admin_vulnerability | :planner | nil | false
:admin_vulnerability | :maintainer | nil | true
:admin_vulnerability | :owner | nil | true
:admin_vulnerability | :reporter | nil | false
:read_dependency | :admin | false | false
:read_dependency | :admin | true | true
:read_dependency | :auditor | nil | true
:read_dependency | :developer | nil | true
:read_dependency | :guest | nil | false
:read_dependency | :planner | nil | false
:read_dependency | :maintainer | nil | true
:read_dependency | :owner | nil | true
:read_dependency | :reporter | nil | false
:read_group_security_dashboard | :admin | false | false
:read_group_security_dashboard | :admin | true | true
:read_group_security_dashboard | :auditor | nil | true
:read_group_security_dashboard | :developer | nil | true
:read_group_security_dashboard | :guest | nil | false
:read_group_security_dashboard | :planner | nil | false
:read_group_security_dashboard | :maintainer | nil | true
:read_group_security_dashboard | :owner | nil | true
:read_group_security_dashboard | :reporter | nil | false
:read_licenses | :admin | false | false
:read_licenses | :admin | true | true
:read_licenses | :auditor | nil | true
:read_licenses | :developer | nil | true
:read_licenses | :guest | nil | false
:read_licenses | :planner | nil | false
:read_licenses | :maintainer | nil | true
:read_licenses | :owner | nil | true
:read_licenses | :reporter | nil | false
:read_vulnerability | :admin | false | false
:read_vulnerability | :admin | true | true
:read_vulnerability | :auditor | nil | true
:read_vulnerability | :developer | nil | true
:read_vulnerability | :guest | nil | false
:read_vulnerability | :planner | nil | false
:read_vulnerability | :maintainer | nil | true
:read_vulnerability | :owner | nil | true
:read_vulnerability | :reporter | nil | false
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if admin_mode
end
context "with security_dashboard enabled" do
before do
stub_licensed_features(security_dashboard: true)
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
context "with security_dashboard disabled" do
before do
stub_licensed_features(security_dashboard: false)
end
it { is_expected.to be_disallowed(policy) }
end
end
end
describe 'resolve_vulnerability_with_ai' do
before do
stub_licensed_features(
security_dashboard: true,
ai_features: true
)
allow(current_user).to receive(:allowed_to_use?).and_return(true)
end
context 'when user cannot :read_security_resource' do
let(:current_user) { guest }
where(:duo_features_enabled, :cs_matcher) do
true | be_disallowed(:resolve_vulnerability_with_ai)
false | be_disallowed(:resolve_vulnerability_with_ai)
end
with_them do
before do
group.namespace_settings.update!(duo_features_enabled: duo_features_enabled)
end
it { is_expected.to cs_matcher }
end
end
context 'when user can?(:read_security_resource)' do
let(:current_user) { developer }
where(:duo_features_enabled, :cs_matcher) do
true | be_allowed(:resolve_vulnerability_with_ai)
false | be_disallowed(:resolve_vulnerability_with_ai)
end
with_them do
before do
group.namespace_settings.update!(duo_features_enabled: duo_features_enabled)
end
it { is_expected.to cs_matcher }
end
end
end
describe 'admin_vulnerability' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'with developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:admin_vulnerability) }
end
context 'with auditor' do
let(:current_user) { auditor }
context "when auditor is not a group member" do
it { is_expected.to be_disallowed(:admin_vulnerability) }
end
context "when developer doesn't have developer-level access to a group" do
before do
group.add_reporter(auditor)
end
it { is_expected.to be_disallowed(:admin_vulnerability) }
end
context 'when auditor has developer-level access to a group' do
before do
group.add_developer(auditor)
end
it { is_expected.to be_disallowed(:admin_vulnerability) }
end
end
end
describe 'read_group_security_dashboard & create_vulnerability_export' do
let(:abilities) do
%i[read_group_security_dashboard create_vulnerability_export read_security_resource read_dependency read_licenses]
end
before do
stub_licensed_features(security_dashboard: true)
end
context 'with admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(*abilities) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(*abilities) }
end
end
context 'with owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(*abilities) }
end
context 'with maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(*abilities) }
end
context 'with developer' do
let(:current_user) { developer }
it { is_expected.to be_allowed(*abilities) }
context 'when security dashboard features is not available' do
before do
stub_licensed_features(security_dashboard: false)
end
it { is_expected.to be_disallowed(*abilities) }
end
end
context 'with reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(*abilities) }
end
context 'with planner' do
let(:current_user) { planner }
it { is_expected.to be_disallowed(*abilities) }
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(*abilities) }
end
context 'with non member' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(*abilities) }
end
context 'with anonymous' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(*abilities) }
end
end
describe 'private nested group use the highest access level from the group and inherited permissions' do
let(:nested_group) { create(:group, :private, parent: group) }
before do
nested_group.add_guest(guest)
nested_group.add_guest(reporter)
nested_group.add_guest(developer)
nested_group.add_guest(maintainer)
group.members.all_owners.destroy_all # rubocop: disable Cop/DestroyAll
group.add_guest(owner)
nested_group.add_owner(owner)
end
subject { described_class.new(current_user, nested_group) }
context 'auditor' do
let(:current_user) { create(:user, :auditor) }
before do
stub_licensed_features(security_dashboard: true)
end
specify do
expect_allowed(*auditor_permissions)
expect_disallowed(*(reporter_permissions - auditor_permissions))
expect_disallowed(*(developer_permissions - auditor_permissions))
expect_disallowed(*(maintainer_permissions - auditor_permissions))
expect_disallowed(*(owner_permissions - auditor_permissions))
end
end
end
context 'when push_rules is not enabled by the current license' do
before do
stub_licensed_features(push_rules: false)
end
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_push_rules) }
end
context 'when push_rules is enabled by the current license' do
before do
stub_licensed_features(push_rules: true)
end
let(:current_user) { maintainer }
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_push_rules) }
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_push_rules) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:change_push_rules) }
end
end
context 'when commit_committer_check is not enabled by the current license' do
before do
stub_licensed_features(commit_committer_check: false)
end
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
end
context 'when commit_committer_check is enabled by the current license' do
before do
stub_licensed_features(commit_committer_check: true)
end
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_commit_committer_check) }
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_commit_committer_check) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
end
end
context 'when commit_committer_name_check is not enabled by the current license' do
before do
stub_licensed_features(commit_committer_name_check: false)
end
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_commit_committer_name_check) }
end
context 'when commit_committer_name_check is enabled by the current license' do
before do
stub_licensed_features(commit_committer_name_check: true)
end
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_commit_committer_name_check) }
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_commit_committer_name_check) }
end
context 'the user is a developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:change_commit_committer_name_check) }
end
end
context 'when reject_unsigned_commits is not enabled by the current license' do
before do
stub_licensed_features(reject_unsigned_commits: false)
end
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
end
context 'when reject_unsigned_commits is enabled by the current license' do
before do
stub_licensed_features(reject_unsigned_commits: true)
end
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
end
end
context 'when reject_non_dco_commits is not enabled by the current license' do
before do
stub_licensed_features(reject_non_dco_commits: false)
end
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:change_reject_non_dco_commits) }
end
context 'when reject_non_dco_commits is enabled by the current license' do
before do
stub_licensed_features(reject_non_dco_commits: true)
end
context 'when the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_reject_non_dco_commits) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:change_reject_non_dco_commits) }
end
end
shared_examples 'analytics policy' do |action|
shared_examples 'policy by role' do |role|
context role do
let(:current_user) { public_send(role) }
it 'is allowed' do
is_expected.to be_allowed(action)
end
end
end
%w[owner maintainer developer reporter].each do |role|
include_examples 'policy by role', role
end
context 'admin' do
let(:current_user) { admin }
it 'is allowed when admin mode is enabled', :enable_admin_mode do
is_expected.to be_allowed(action)
end
it 'is not allowed when admin mode is disabled' do
is_expected.to be_disallowed(action)
end
end
context 'guest' do
let(:current_user) { guest }
it 'is not allowed' do
is_expected.to be_disallowed(action)
end
end
context 'planner' do
let(:current_user) { planner }
it 'is not allowed' do
is_expected.to be_disallowed(action)
end
end
end
describe 'view_productivity_analytics' do
include_examples 'analytics policy', :view_productivity_analytics
end
describe '#read_group_saml_identity' do
let_it_be(:saml_provider) { create(:saml_provider, group: group, enabled: true) }
context 'for owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:read_group_saml_identity) }
context 'without Group SAML enabled' do
before do
saml_provider.update!(enabled: false)
end
it { is_expected.to be_disallowed(:read_group_saml_identity) }
end
end
%w[maintainer developer reporter guest].each do |role|
context "for #{role}" do
let(:current_user) { public_send(role) }
it { is_expected.to be_disallowed(:read_group_saml_identity) }
end
end
end
describe 'update_default_branch_protection' do
context 'for an admin' do
let(:current_user) { admin }
context 'when the `default_branch_protection_restriction_in_groups` feature is available' do
before do
stub_licensed_features(default_branch_protection_restriction_in_groups: true)
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is enabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: true)
end
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_default_branch_protection) }
end
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is disabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: false)
end
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_default_branch_protection) }
end
end
end
context 'when the `default_branch_protection_restriction_in_groups` feature is not available' do
before do
stub_licensed_features(default_branch_protection_restriction_in_groups: false)
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is enabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: true)
end
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_default_branch_protection) }
end
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is disabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: false)
end
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when admin mode is disabled' do
it { is_expected.to be_disallowed(:update_default_branch_protection) }
end
end
end
end
context 'for an owner' do
let(:current_user) { owner }
context 'when the `default_branch_protection_restriction_in_groups` feature is available' do
before do
stub_licensed_features(default_branch_protection_restriction_in_groups: true)
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is enabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: true)
end
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is disabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: false)
end
it { is_expected.to be_disallowed(:update_default_branch_protection) }
end
end
context 'when the `default_branch_protection_restriction_in_groups` feature is not available' do
before do
stub_licensed_features(default_branch_protection_restriction_in_groups: false)
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is enabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: true)
end
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
context 'when the setting `group_owners_can_manage_default_branch_protection` is disabled' do
before do
stub_ee_application_setting(group_owners_can_manage_default_branch_protection: false)
end
it { is_expected.to be_allowed(:update_default_branch_protection) }
end
end
end
end
describe ':admin_ci_minutes' do
let(:policy) { :admin_ci_minutes }
where(:role, :admin_mode, :allowed) do
:guest | nil | false
:planner | nil | false
:reporter | nil | false
:developer | nil | false
:maintainer | nil | false
:owner | nil | true
:admin | true | true
:admin | false | false
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
describe ':read_group_audit_events' do
let(:policy) { :read_group_audit_events }
where(:role, :admin_mode, :allowed) do
:guest | nil | false
:planner | nil | false
:reporter | nil | false
:developer | nil | true
:maintainer | nil | true
:owner | nil | true
:admin | true | true
:admin | false | false
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context 'when group is read only' do
let(:current_user) { owner }
let(:policies) do
%i[create_epic update_epic admin_pipeline register_group_runners add_cluster create_cluster
update_cluster admin_cluster create_deploy_token create_subgroup create_package]
end
before do
allow(group).to receive(:read_only?).and_return(read_only)
stub_licensed_features(epics: true)
end
context 'when the group is read only' do
let(:read_only) { true }
it { is_expected.to(be_disallowed(*policies)) }
it { is_expected.to(be_allowed(:read_billable_member)) }
end
context 'when the group is not read only' do
let(:read_only) { false }
it { is_expected.to(be_allowed(*policies)) }
end
end
context 'under .com', :saas do
it_behaves_like 'model with wiki policies' do
let_it_be_with_refind(:container) { create(:group_with_plan, plan: :premium_plan) }
let_it_be(:user) { owner }
before_all do
create(:license, plan: License::PREMIUM_PLAN)
end
before do
enable_namespace_license_check!
end
def set_access_level(access_level)
container.group_feature.update_attribute(:wiki_access_level, access_level)
end
context 'when the feature is not licensed on this group' do
let_it_be(:container) { create(:group_with_plan, plan: :bronze_plan) }
it 'does not include the wiki permissions' do
expect_disallowed(*wiki_permissions[:all])
end
end
end
end
it_behaves_like 'update namespace limit policy'
context 'group access tokens', :saas do
context 'GitLab.com Core resource access tokens', :saas do
before do
stub_ee_application_setting(should_check_namespace_plan: true)
end
context 'with owner access' do
let(:current_user) { owner }
it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
it { is_expected.not_to be_allowed(:admin_setting_to_allow_resource_access_token_creation) }
it { is_expected.to be_allowed(:read_resource_access_tokens) }
it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
end
end
context 'on GitLab.com paid' do
let_it_be(:group) { create(:group_with_plan, plan: :bronze_plan) }
context 'with owner' do
let(:current_user) { owner }
before do
group.add_owner(owner)
end
it_behaves_like 'GitLab.com Paid plan resource access tokens'
context 'create resource access tokens' do
it { is_expected.to be_allowed(:create_resource_access_tokens) }
context 'when resource access token creation is not allowed' do
before do
group.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
end
it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
end
context 'when parent group has resource access token creation disabled' do
let(:namespace_settings) { create(:namespace_settings, resource_access_token_creation_allowed: false) }
let(:parent) { create(:group_with_plan, plan: :bronze_plan, namespace_settings: namespace_settings) }
let(:group) { create(:group, parent: parent) }
context 'cannot create resource access tokens' do
it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
end
context 'can render admin settings for resource access token' do
it { is_expected.to be_allowed(:admin_setting_to_allow_resource_access_token_creation) }
end
end
end
context 'read resource access tokens' do
it { is_expected.to be_allowed(:read_resource_access_tokens) }
end
context 'destroy resource access tokens' do
it { is_expected.to be_allowed(:destroy_resource_access_tokens) }
end
context 'admin settings `allow resource access token` is allowed' do
it { is_expected.to be_allowed(:admin_setting_to_allow_resource_access_token_creation) }
end
end
context 'with developer' do
let(:current_user) { developer }
before do
group.add_developer(developer)
end
context 'create resource access tokens' do
it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
end
context 'read resource access tokens' do
it { is_expected.not_to be_allowed(:read_resource_access_tokens) }
end
context 'destroy resource access tokens' do
it { is_expected.not_to be_allowed(:destroy_resource_access_tokens) }
end
end
end
end
describe ':read_group_release_stats' do
shared_examples 'read_group_release_stats permissions' do
context 'when user is logged out' do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:read_group_release_stats) }
end
context 'when user is not a member of the group' do
let(:current_user) { create(:user) }
it { is_expected.to be_disallowed(:read_group_release_stats) }
end
context 'when user is guest' do
let(:current_user) { guest }
it { is_expected.to be_allowed(:read_group_release_stats) }
end
end
context 'when group is private' do
it_behaves_like 'read_group_release_stats permissions'
end
context 'when group is public' do
let(:group) { create(:group, :public) }
before do
group.add_guest(guest)
end
it_behaves_like 'read_group_release_stats permissions'
end
describe ':admin_merge_request_approval_settings' do
let(:policy) { :admin_merge_request_approval_settings }
where(:role, :licensed, :admin_mode, :root_group, :allowed) do
:guest | true | nil | true | false
:guest | false | nil | true | false
:planner | true | nil | true | false
:planner | false | nil | true | false
:reporter | true | nil | true | false
:reporter | false | nil | true | false
:developer | true | nil | true | false
:developer | false | nil | true | false
:maintainer | true | nil | true | false
:maintainer | false | nil | true | false
:owner | true | nil | true | true
:owner | true | nil | false | false
:owner | false | nil | true | false
:admin | true | true | true | true
:admin | true | true | false | false
:admin | false | true | true | false
:admin | true | false | true | false
:admin | false | false | true | false
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(merge_request_approvers: licensed)
enable_admin_mode!(current_user) if admin_mode
group.parent = build(:group) unless root_group
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
describe 'custom roles' do
let(:license) { :custom_roles }
context 'when on self-managed' do
let(:current_user) { owner }
before do
stub_licensed_features(license => true)
end
it { is_expected.to be_disallowed(:admin_member_role) }
end
describe ':admin_member_role', :saas do
using RSpec::Parameterized::TableSyntax
where(:role, :allowed) do
:guest | false
:planner | false
:reporter | false
:developer | false
:maintainer | false
:auditor | false
:owner | true
:admin | true
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if role == :admin
end
context 'custom roles license' do
let(:permissions) { [:admin_member_role, :view_member_roles] }
context 'when licensed feature is enabled' do
before do
stub_licensed_features(license => true)
end
it { is_expected.to(allowed ? be_allowed(*permissions) : be_disallowed(*permissions)) }
context 'when memberships are locked to LDAP' do
before do
allow(group).to receive(:ldap_synced?).and_return(true)
stub_application_setting(allow_group_owners_to_manage_ldap: true)
stub_application_setting(lock_memberships_to_ldap: true)
end
it { is_expected.to(allowed ? be_allowed(*permissions) : be_disallowed(*permissions)) }
end
end
context 'when licensed feature is disabled' do
before do
stub_licensed_features(license => false)
end
it { is_expected.to be_disallowed(*permissions) }
end
end
context 'default roles assignees license' do
let(:license) { :default_roles_assignees }
let(:permissions) { [:view_member_roles] }
context 'when licensed feature is enabled' do
before do
stub_licensed_features(license => true)
end
it { is_expected.to(allowed ? be_allowed(*permissions) : be_disallowed(*permissions)) }
end
context 'when licensed feature is disabled' do
before do
stub_licensed_features(license => false)
end
it { is_expected.to be_disallowed(*permissions) }
end
end
end
end
describe ':read_member_role' do
using RSpec::Parameterized::TableSyntax
let(:permissions) { [:read_member_role] }
where(:role, :allowed) do
:guest | true
:planner | true
:reporter | true
:developer | true
:maintainer | true
:auditor | false
:owner | true
:admin | true
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if role == :admin
end
context 'when custom_roles feature is enabled' do
before do
stub_licensed_features(custom_roles: true)
end
it { is_expected.to(allowed ? be_allowed(*permissions) : be_disallowed(*permissions)) }
context 'when memberships are locked to LDAP' do
before do
allow(group).to receive(:ldap_synced?).and_return(true)
stub_application_setting(allow_group_owners_to_manage_ldap: true)
stub_application_setting(lock_memberships_to_ldap: true)
end
it { is_expected.to(allowed ? be_allowed(*permissions) : be_disallowed(*permissions)) }
end
end
context 'when custom_roles feature is disabled' do
before do
stub_licensed_features(custom_roles: false)
end
it { is_expected.to be_disallowed(*permissions) }
end
end
end
end
describe ':start_trial' do
let(:policy) { :start_trial }
where(:role, :eligible_for_trial, :admin_mode, :allowed) do
:guest | true | nil | false
:guest | false | nil | false
:planner | true | nil | false
:planner | false | nil | false
:reporter | true | nil | false
:reporter | false | nil | false
:developer | true | nil | false
:developer | false | nil | false
:maintainer | true | nil | true
:maintainer | false | nil | false
:owner | true | nil | true
:owner | false | nil | false
:admin | true | true | true
:admin | false | true | false
:admin | true | false | false
:admin | false | false | false
end
with_them do
let(:current_user) { public_send(role) }
before do
allow(group).to receive(:eligible_for_trial?).and_return(eligible_for_trial)
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
describe 'compliance framework permissions' do
shared_examples 'compliance framework permissions' do
where(:role, :licensed, :admin_mode, :allowed) do
:owner | true | nil | true
:owner | false | nil | false
:admin | true | true | true
:admin | true | false | false
:maintainer | true | nil | false
:developer | true | nil | false
:reporter | true | nil | false
:planner | true | nil | false
:guest | true | nil | false
end
with_them do
let(:current_user) { public_send(role) }
before do
stub_licensed_features(licensed_feature => licensed)
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
context ':admin_compliance_framework' do
let(:policy) { :admin_compliance_framework }
let(:licensed_feature) { :custom_compliance_frameworks }
let(:feature_flag_name) { nil }
include_examples 'compliance framework permissions'
end
context ':admin_compliance_pipeline_configuration' do
let(:policy) { :admin_compliance_pipeline_configuration }
let(:licensed_feature) { :evaluate_group_level_compliance_pipeline }
include_examples 'compliance framework permissions'
end
end
describe 'view_devops_adoption' do
let(:current_user) { owner }
let(:policy) { :view_group_devops_adoption }
context 'when license does not include the feature' do
let(:current_user) { admin }
before do
stub_licensed_features(group_level_devops_adoption: false)
enable_admin_mode!(current_user)
end
it { is_expected.to be_disallowed(policy) }
end
context 'when license includes the feature' do
where(:role, :allowed) do
:admin | true
:owner | true
:maintainer | true
:developer | true
:reporter | true
:planner | false
:guest | false
:non_group_member | false
:auditor | true
end
before do
stub_licensed_features(group_level_devops_adoption: true)
enable_admin_mode!(current_user) if current_user.admin?
end
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
describe 'manage_devops_adoption_namespaces' do
let(:current_user) { owner }
let(:policy) { :manage_devops_adoption_namespaces }
context 'when license does not include the feature' do
let(:current_user) { admin }
before do
stub_licensed_features(group_level_devops_adoption: false)
enable_admin_mode!(current_user)
end
it { is_expected.to be_disallowed(policy) }
end
context 'when license includes the feature' do
where(:role, :allowed) do
:admin | true
:owner | true
:maintainer | true
:developer | true
:reporter | true
:planner | false
:guest | false
:non_group_member | false
end
before do
stub_licensed_features(group_level_devops_adoption: true)
enable_admin_mode!(current_user) if current_user.admin?
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 license plan does not include the feature' do
where(:role, :allowed) do
:admin | true
:owner | false
:maintainer | false
:developer | false
:reporter | false
:planner | false
:guest | false
:non_group_member | false
end
before do
stub_licensed_features(group_level_devops_adoption: true)
allow(group).to receive(:feature_available?).with(:group_level_devops_adoption).and_return(false)
enable_admin_mode!(current_user) if current_user.admin?
end
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
context 'external audit events' do
let(:current_user) { owner }
context 'when license is disabled' do
before do
stub_licensed_features(external_audit_events: false)
end
it { is_expected.to(be_disallowed(:admin_external_audit_events)) }
end
context 'when license is enabled' do
before do
stub_licensed_features(external_audit_events: true)
end
it { is_expected.to(be_allowed(:admin_external_audit_events)) }
end
context 'when user is not an owner' do
let(:current_user) { build_stubbed(:user, :auditor) }
it { is_expected.to(be_disallowed(:admin_external_audit_events)) }
end
end
describe 'a pending membership' do
let_it_be(:user) { create(:user) }
context 'with a private group' do
let_it_be(:private_group) { create(:group, :private) }
subject { described_class.new(user, private_group) }
where(:role) do
Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
end
with_them do
it 'has permission identical to a private group in which the user is not a member' do
create(:group_member, :awaiting, role, source: private_group, user: user)
expect_private_group_permissions_as_if_non_member
end
end
context 'with a project in the group' do
let_it_be(:project) { create(:project, :private, namespace: private_group) }
where(:role) do
Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
end
with_them do
it 'has permission identical to a private group in which the user is not a member' do
create(:group_member, :awaiting, role, source: private_group, user: user)
expect_private_group_permissions_as_if_non_member
end
end
end
end
context 'with a public group' do
let_it_be(:public_group) { create(:group, :public) }
subject { described_class.new(user, public_group) }
where(:role) do
Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
end
with_them do
it 'has permission identical to a public group in which the user is not a member' do
create(:group_member, :awaiting, role, source: public_group, user: user)
expect_allowed(*public_permissions)
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
expect_disallowed(:read_namespace_via_membership)
end
end
end
context 'with a group invited to another group' do
let_it_be(:group) { create(:group, :public) }
let_it_be(:other_group) { create(:group, :private) }
subject { described_class.new(user, other_group) }
before_all do
create(:group_group_link, { shared_with_group: group, shared_group: other_group })
end
where(:role) do
Gitlab::Access.sym_options_with_owner.keys.map(&:to_sym)
end
with_them do
it 'has permission to the other group as if the user is not a member' do
create(:group_member, :awaiting, role, source: group, user: user)
expect_private_group_permissions_as_if_non_member
end
end
end
def expect_private_group_permissions_as_if_non_member
expect_disallowed(*public_permissions)
expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*developer_permissions)
expect_disallowed(*maintainer_permissions)
expect_disallowed(*owner_permissions)
end
end
describe 'security complience policy' do
context 'when licensed feature is available' do
before do
stub_licensed_features(security_orchestration_policies: false)
end
context 'with developer or maintainer role' do
where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_disallowed(:read_security_orchestration_policies) }
it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_disallowed(:read_security_orchestration_policies) }
it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
it { is_expected.to be_disallowed(:modify_security_policy) }
end
end
end
context 'when licensed feature is available' do
before do
stub_licensed_features(security_orchestration_policies: true)
end
context 'when security_orchestration_policy_configuration is not present' do
context 'with developer or maintainer role' do
where(role: %w[maintainer developer])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:read_security_orchestration_policies) }
it { is_expected.to be_disallowed(:update_security_orchestration_policy_project) }
end
end
context 'with owner role' do
where(role: %w[owner])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:read_security_orchestration_policies) }
it { is_expected.to be_allowed(:update_security_orchestration_policy_project) }
it { is_expected.to be_allowed(:modify_security_policy) }
context 'when security_orchestration_policy_configuration is present' do
let_it_be(:security_policy_management_project) { create(:project) }
before do
create(:security_orchestration_policy_configuration, project: nil, namespace: group, security_policy_management_project: security_policy_management_project)
end
it { is_expected.to be_disallowed(:modify_security_policy) }
end
end
end
end
context 'when security_orchestration_policy_configuration is present' do
let_it_be(:security_policy_management_project) { create(:project) }
let(:current_user) { developer }
before do
create(:security_orchestration_policy_configuration, project: nil, namespace: group, security_policy_management_project: security_policy_management_project)
end
context 'when current_user is developer of security_policy_management_project' do
before do
security_policy_management_project.add_developer(developer)
end
it { is_expected.to be_allowed(:modify_security_policy) }
end
context 'when current_user is not developer of security_policy_management_project' do
it { is_expected.to be_disallowed(:modify_security_policy) }
end
end
end
end
describe 'read_usage_quotas policy' do
context 'reading usage quotas' do
let(:policy) { :read_usage_quotas }
where(:role, :admin_mode, :allowed) do
:owner | nil | true
:admin | true | true
:admin | false | false
:maintainer | nil | false
:developer | nil | false
:reporter | nil | false
:planner | nil | false
:guest | nil | false
end
with_them do
let(:current_user) { public_send(role) }
before do
enable_admin_mode!(current_user) if admin_mode
end
it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) }
end
end
end
describe 'dependency proxy' do
context 'feature enabled' do
before do
stub_config(dependency_proxy: { enabled: true })
end
context 'auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
end
end
describe 'read wiki' do
context 'feature enabled' do
before do
stub_licensed_features(group_wikis: true)
end
context 'auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_wiki) }
it { is_expected.to be_disallowed(:admin_wiki) }
end
end
context 'feature disabled' do
before do
stub_licensed_features(group_wikis: false)
end
context 'auditor' do
let(:current_user) { auditor }
it { is_expected.to be_disallowed(:read_wiki) }
it { is_expected.to be_disallowed(:admin_wiki) }
end
end
end
describe 'group level compliance features' do
shared_examples 'group level compliance feature' do |feature, permission|
context 'when enabled' do
before do
stub_licensed_features({ feature => true })
end
context 'when user is eligible for access' do
where(role: %w[owner auditor])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(permission) }
end
end
context 'allows admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(permission) }
end
end
context 'when disabled' do
before do
stub_licensed_features({ feature => false })
end
context 'when user is eligible for access' do
where(role: %w[owner auditor])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_disallowed(permission) }
end
end
context 'disallows admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_disallowed(permission) }
end
end
end
describe 'group level compliance dashboard' do
it_behaves_like 'group level compliance feature', :group_level_compliance_dashboard, :read_compliance_dashboard
end
describe 'group level compliance adherence report' do
it_behaves_like 'group level compliance feature', :group_level_compliance_adherence_report, :read_compliance_adherence_report
end
describe 'group level compliance violations report' do
it_behaves_like 'group level compliance feature', :group_level_compliance_violations_report, :read_compliance_violations_report
end
end
describe 'user banned from namespace' do
let_it_be_with_reload(:current_user) { create(:user) }
let_it_be(:group) { create(:group, :private) }
subject { described_class.new(current_user, group) }
before do
stub_licensed_features(unique_project_download_limit: true)
group.add_developer(current_user)
end
context 'when user is not banned' do
it { is_expected.to be_allowed(:read_group) }
end
context 'when user is banned' do
before do
create(:namespace_ban, user: current_user, namespace: group.root_ancestor)
end
it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
context 'inside a subgroup' do
let_it_be(:group) { create(:group, :private, :nested) }
it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
context 'as an owner of the subgroup' do
before do
group.add_owner(current_user)
end
it { is_expected.to be_disallowed(*described_class.own_ability_map.map.keys) }
end
end
context 'as an admin' do
let_it_be(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:read_group) }
end
end
context 'when group is public' do
let_it_be(:group) { create(:group, :public) }
it { is_expected.to be_disallowed(:read_group) }
end
context 'when licensed feature unique_project_download_limit is not available' do
before do
stub_licensed_features(unique_project_download_limit: false)
end
it { is_expected.to be_allowed(:read_group) }
end
end
end
describe 'ban_group_member' do
let_it_be(:user) { create(:user) }
let(:group) { create(:group) }
subject(:policy) { described_class.new(user, group) }
where(:unique_project_download_limit_enabled, :is_owner, :enabled) do
false | false | false
false | true | false
true | false | false
true | true | true
end
with_them do
before do
allow(group).to receive(:unique_project_download_limit_enabled?)
.and_return(unique_project_download_limit_enabled)
group.add_owner(user) if is_owner
end
it 'has the correct value' do
if enabled
expect(policy).to be_allowed(:ban_group_member)
else
expect(policy).to be_disallowed(:ban_group_member)
end
end
end
end
describe 'group cicd runners' do
context 'auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_group_runners) }
it { is_expected.to be_allowed(:read_group_all_available_runners) }
it { is_expected.to be_disallowed(:register_group_runners) }
it { is_expected.to be_disallowed(:create_runner) }
end
end
describe 'group container registry' do
context 'auditor' do
let(:current_user) { auditor }
it { is_expected.to be_allowed(:read_container_image) }
it { is_expected.to be_disallowed(:admin_container_image) }
end
end
describe 'admin_service_accounts' do
context 'when the feature is not enabled' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:admin_service_accounts) }
it { is_expected.to be_disallowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
context 'when feature is enabled' do
before do
stub_licensed_features(service_accounts: true)
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_service_accounts) }
it { is_expected.to be_disallowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
context 'when the user is an owner' do
let(:current_user) { owner }
context 'when application setting allow_top_level_group_owners_to_create_service_accounts is disabled' do
before do
stub_ee_application_setting(allow_top_level_group_owners_to_create_service_accounts: false)
end
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
context 'when saas', :saas do
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
context 'when application setting allow_top_level_group_owners_to_create_service_accounts is enabled ' do
before do
stub_ee_application_setting(allow_top_level_group_owners_to_create_service_accounts: true)
end
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_allowed(:create_service_account) }
it { is_expected.to be_allowed(:delete_service_account) }
context 'when saas', :saas do
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_allowed(:create_service_account) }
it { is_expected.to be_allowed(:delete_service_account) }
context 'when trial is active' do
before do
allow(group).to receive_messages(trial_active?: true)
end
it { is_expected.to be_disallowed(:admin_service_accounts) }
it { is_expected.to be_disallowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
context 'when a trial is active' do
before do
allow(group).to receive_messages(gitlab_subscription: nil)
end
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_allowed(:create_service_account) }
it { is_expected.to be_allowed(:delete_service_account) }
end
context 'for subgroup' do
let_it_be(:subgroup) { create(:group, :private, parent: group) }
subject { described_class.new(current_user, subgroup) }
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
context 'for subgroup' do
let_it_be(:subgroup) { create(:group, :private, parent: group) }
subject { described_class.new(current_user, subgroup) }
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
context 'when a trial is active in GitLab.com', :saas do
before do
allow(subgroup.root_ancestor).to receive_messages(trial_active?: true)
end
it { is_expected.to be_disallowed(:admin_service_accounts) }
it { is_expected.to be_disallowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
end
context 'when the user is an instance admin' do
let(:current_user) { admin }
context 'when admin mode is enabled', :enable_admin_mode do
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_allowed(:create_service_account) }
it { is_expected.to be_allowed(:delete_service_account) }
context 'when a trial is active' do
before do
allow(group).to receive_messages(gitlab_subscription: nil)
end
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_allowed(:create_service_account) }
it { is_expected.to be_allowed(:delete_service_account) }
end
context 'for subgroup' do
let_it_be(:subgroup) { create(:group, :private, parent: group) }
subject { described_class.new(current_user, subgroup) }
it { is_expected.to be_allowed(:admin_service_accounts) }
it { is_expected.to be_allowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
context 'when admin mode is not enabled' do
it { is_expected.to be_disallowed(:admin_service_accounts) }
it { is_expected.to be_disallowed(:admin_service_account_member) }
it { is_expected.to be_disallowed(:create_service_account) }
it { is_expected.to be_disallowed(:delete_service_account) }
end
end
end
end
describe 'access_duo_chat' do
let_it_be(:current_user) { create(:user) }
subject { described_class.new(current_user, group) }
context 'when on SaaS instance', :saas do
let_it_be_with_reload(:group) { create(:group_with_plan, plan: :premium_plan) }
context 'when container is a group with AI enabled' do
include_context 'with duo features enabled and ai chat available for group on SaaS'
context 'when user is a member of the group' do
before do
group.add_guest(current_user)
end
it { is_expected.to be_allowed(:access_duo_chat) }
context 'when the group does not have an Premium SaaS license' do
let_it_be(:group) { create(:group) }
it { is_expected.to be_disallowed(:access_duo_chat) }
end
end
context 'when the user is not a member but has AI enabled via another group' do
context 'user can view group' do
it 'is allowed' do
is_expected.to be_allowed(:access_duo_chat)
end
end
context 'user cannot view group' do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
it 'is not allowed' do
is_expected.to be_disallowed(:access_duo_chat)
end
end
end
end
end
context 'for self-managed', :with_cloud_connector do
let_it_be_with_reload(:group) { create(:group) }
let(:policy) { :access_duo_chat }
context 'when not on .org or .com' do
where(:enabled_for_user, :duo_features_enabled, :cs_matcher) do
true | false | be_disallowed(policy)
true | true | be_allowed(policy)
false | false | be_disallowed(policy)
false | true | be_disallowed(policy)
end
with_them do
before do
allow(::Gitlab).to receive(:org_or_com?).and_return(false)
stub_ee_application_setting(duo_features_enabled: duo_features_enabled, lock_duo_features_enabled: true)
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(current_user, :access_duo_chat).and_return(enabled_for_user)
end
it { is_expected.to cs_matcher }
end
end
end
end
context 'access_duo_features' do
where(:current_user, :duo_features_enabled, :cs_matcher) do
ref(:guest) | true | be_allowed(:access_duo_features)
ref(:guest) | false | be_disallowed(:access_duo_features)
nil | true | be_disallowed(:access_duo_features)
nil | false | be_disallowed(:access_duo_features)
end
with_them do
before do
group.namespace_settings.update!(duo_features_enabled: duo_features_enabled)
end
it do
is_expected.to cs_matcher
end
end
context 'when the group is not yet persisted' do
subject { described_class.new(admin, build(:group)) }
it { is_expected.to be_disallowed(:access_duo_features) }
end
end
describe 'access to group for duo workflow' do
let_it_be_with_reload(:group) { create(:group, :public) }
where(:current_user, :token_info, :duo_features_enabled, :cs_matcher) do
ref(:guest) | nil | true | be_allowed(:read_group)
ref(:guest) | { token_scopes: [:ai_workflows] } | true | be_allowed(:read_group)
ref(:guest) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_group, :admin_group)
ref(:guest) | { token_scopes: [:other_scope] } | true | be_allowed(:read_group)
ref(:maintainer) | { token_scopes: [:ai_workflows] } | false | be_disallowed(:read_group, :admin_group)
end
with_them do
before do
group.namespace_settings.update!(duo_features_enabled: duo_features_enabled)
::Current.token_info = token_info
end
it { is_expected.to cs_matcher }
end
end
describe 'access_duo_core_features' do
let_it_be(:current_user) { create(:user) }
context 'when on GitLab.com', :saas do
before do
stub_ee_application_setting(should_check_namespace_plan: true)
end
subject { described_class.new(current_user, group) }
context 'with a group with Duo Core enabled' do
before do
group.namespace_settings.reload.update!(duo_core_features_enabled: true)
end
context 'with duo core addon' do
include_context 'with duo core addon'
context 'when the group is not yet persisted' do
subject { described_class.new(admin, build(:group)) }
it { is_expected.to be_disallowed(:access_duo_core_features) }
end
context 'when user is a guest' do
before do
group.add_guest(current_user)
end
it { is_expected.to be_disallowed(:access_duo_core_features) }
end
context 'when user is a member of the group' do
where(:access_level) { %i[reporter developer maintainer owner] }
with_them do
before do
group.add_member(current_user, access_level)
end
it { is_expected.to be_allowed(:access_duo_core_features) }
end
end
context 'when user is a member of a subgroup' do
let(:subgroup) { create(:group, :private, parent: group) }
subject { described_class.new(current_user, subgroup) }
before do
subgroup.add_reporter(current_user)
end
it { is_expected.to be_allowed(:access_duo_core_features) }
end
end
['with duo pro addon', 'with duo enterprise addon'].each do |context|
context context do
include_context context
it { is_expected.to be_disallowed(:access_duo_core_features) }
end
end
end
context 'with a group with Duo Core disabled' do
before do
group.namespace_settings.reload.update!(duo_core_features_enabled: false)
group.add_member(current_user, :owner)
end
['with duo core addon', 'with duo pro addon', 'with duo enterprise addon'].each do |context|
context context do
include_context context
it { is_expected.to be_disallowed(:access_duo_core_features) }
end
end
end
end
end
describe ':read_saml_user' do
let_it_be(:user) { non_group_member }
let_it_be(:subgroup) { create(:group, :private, parent: group) }
subject(:policy) { described_class.new(user, the_group) }
context 'when a SAML provider does not exist' do
let_it_be(:the_group) { subgroup }
before do
stub_licensed_features(group_saml: true)
the_group.add_member(user, Gitlab::Access::OWNER)
end
it { is_expected.to be_disallowed(:read_saml_user) }
end
context 'when a SAML provider exists' do
before_all do
create(:saml_provider, group: group)
end
where(:the_group, :licensed, :saml_enabled, :sso_enforced, :role, :allowed) do
ref(:group) | false | false | false | Gitlab::Access::OWNER | false
ref(:group) | false | false | false | Gitlab::Access::MAINTAINER | false
ref(:group) | true | false | false | Gitlab::Access::OWNER | false
ref(:group) | true | false | false | Gitlab::Access::MAINTAINER | false
ref(:group) | true | true | false | Gitlab::Access::OWNER | false
ref(:group) | true | true | false | Gitlab::Access::MAINTAINER | false
ref(:group) | true | true | true | Gitlab::Access::OWNER | true
ref(:group) | true | true | true | Gitlab::Access::MAINTAINER | false
ref(:subgroup) | false | false | false | Gitlab::Access::OWNER | false
ref(:subgroup) | false | false | false | Gitlab::Access::MAINTAINER | false
ref(:subgroup) | true | false | false | Gitlab::Access::OWNER | false
ref(:subgroup) | true | false | false | Gitlab::Access::MAINTAINER | false
ref(:subgroup) | true | true | false | Gitlab::Access::OWNER | false
ref(:subgroup) | true | true | false | Gitlab::Access::MAINTAINER | false
ref(:subgroup) | true | true | true | Gitlab::Access::OWNER | true
ref(:subgroup) | true | true | true | Gitlab::Access::MAINTAINER | false
end
with_them do
before do
stub_licensed_features(group_saml: licensed)
the_group.add_member(user, role)
the_group.root_ancestor.saml_provider.update!(enabled: saml_enabled)
the_group.root_ancestor.saml_provider.update!(enforced_sso: sso_enforced)
end
it { expect(policy.allowed?(:read_saml_user)).to eq(allowed) }
end
end
end
context 'custom role' do
let_it_be(:guest) { create(:user) }
let_it_be(:parent_group) { create(:group) }
let_it_be(:group) { create(:group, parent: parent_group) }
let_it_be(:parent_group_member_guest) do
create(
:group_member,
user: guest,
source: parent_group,
access_level: Gitlab::Access::GUEST
)
end
let_it_be(:group_member_guest) do
create(
:group_member,
user: guest,
source: group,
access_level: Gitlab::Access::GUEST
)
end
let(:member_role_abilities) { {} }
let(:allowed_abilities) { [] }
let(:disallowed_abilities) { [] }
let(:licensed_features) { {} }
let(:current_user) { guest }
def create_member_role(member, abilities = member_role_abilities)
params = abilities.merge(namespace: parent_group)
create(:member_role, :guest, params).tap do |role|
role.members << member
end
end
shared_examples 'custom roles abilities' do
subject { described_class.new(current_user, group) }
context 'without custom_roles license disabled' do
before do
create_member_role(group_member_guest)
stub_licensed_features(licensed_features.merge(custom_roles: false))
end
it { expect_disallowed(*allowed_abilities) }
end
context 'with custom_roles license enabled' do
before do
stub_licensed_features(licensed_features.merge(custom_roles: true))
end
context 'custom role for parent group membership' do
context 'when a role enables the abilities' do
before do
create_member_role(parent_group_member_guest)
end
it { expect_allowed(*allowed_abilities) }
it { expect_disallowed(*disallowed_abilities) }
end
context 'when a role does not enable the abilities' do
it { expect_disallowed(*allowed_abilities) }
end
end
context 'custom role on group membership' do
context 'when a role enables the abilities' do
before do
create_member_role(group_member_guest)
end
it { expect_allowed(*allowed_abilities) }
it { expect_disallowed(*disallowed_abilities) }
end
context 'when a role does not enable the abilities' do
it { expect_disallowed(*allowed_abilities) }
end
end
end
end
context 'for a member role with read_vulnerability true' do
let(:member_role_abilities) { { read_vulnerability: true } }
let(:allowed_abilities) { [:read_group_security_dashboard] }
it_behaves_like 'custom roles abilities'
it 'does not enable to admin_vulnerability' do
expect(subject).to be_disallowed(:admin_vulnerability)
end
it { is_expected.to be_disallowed(:read_dependency) }
end
context 'for a member role with admin_vulnerability true' do
let(:member_role_abilities) { { read_vulnerability: true, admin_vulnerability: true } }
let(:allowed_abilities) { [:read_group_security_dashboard, :read_vulnerability, :admin_vulnerability] }
it_behaves_like 'custom roles abilities'
end
context 'for a member role with read_dependency true' do
let(:member_role_abilities) { { read_dependency: true } }
let(:allowed_abilities) { [:read_dependency, :read_licenses] }
it_behaves_like 'custom roles abilities'
end
context 'for a member role with admin_group_member true' do
let(:member_role_abilities) { { admin_group_member: true } }
let(:allowed_abilities) { [:admin_group_member] }
let(:disallowed_abilities) { [:activate_group_member] }
it_behaves_like 'custom roles abilities'
context 'admin_service_account_member' do
let_it_be(:guest) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:group_member_guest) do
create(
:group_member,
user: guest,
source: group,
access_level: Gitlab::Access::GUEST
)
end
let(:role) do
create(
:member_role,
:guest,
namespace: group,
admin_group_member: true
)
end
before do
role.members << group_member_guest
stub_licensed_features(custom_roles: true)
end
it { is_expected.to be_disallowed(:admin_service_account_member) }
context 'when service accounts feature enabled' do
before do
stub_licensed_features(custom_roles: true, service_accounts: true)
end
it { is_expected.to be_allowed(:admin_service_account_member) }
end
end
end
context 'for a member role with manage_group_access_tokens true' do
let(:member_role_abilities) { { manage_group_access_tokens: true } }
let(:allowed_abilities) do
[:read_resource_access_tokens, :destroy_resource_access_tokens,
:create_resource_access_tokens, :manage_resource_access_tokens]
end
it_behaves_like 'custom roles abilities'
context 'when resource access token creation is not allowed' do
before do
create_member_role(group_member_guest)
stub_licensed_features(custom_roles: true)
group.root_ancestor.namespace_settings.update_column(:resource_access_token_creation_allowed, false)
end
it { is_expected.to be_allowed(:read_resource_access_tokens, :destroy_resource_access_tokens) }
it { is_expected.to be_disallowed(:create_resource_access_tokens, :manage_resource_access_tokens) }
end
context 'when resource access tokens feature is unavailable' do
before do
create_member_role(group_member_guest)
stub_licensed_features(custom_roles: true)
stub_ee_application_setting(personal_access_tokens_disabled?: true)
end
it { is_expected.to be_disallowed(*allowed_abilities) }
end
end
context 'for a custom role with the `admin_cicd_variables` ability' do
let(:member_role_abilities) { { admin_cicd_variables: true } }
let(:allowed_abilities) { [:admin_cicd_variables] }
it_behaves_like 'custom roles abilities'
end
context 'for a custom role with the `admin_protected_environments` ability' do
let(:member_role_abilities) { { admin_protected_environments: true } }
let(:allowed_abilities) { [:admin_protected_environments] }
it_behaves_like 'custom roles abilities'
end
context 'for a member role with admin_compliance_framework true' do
let(:member_role_abilities) { { read_compliance_dashboard: true, admin_compliance_framework: true } }
let(:allowed_abilities) do
[
:admin_compliance_framework,
:admin_compliance_pipeline_configuration,
:read_compliance_dashboard,
:read_compliance_adherence_report,
:read_compliance_violations_report
]
end
context 'when compliance framework feature is available' do
let(:licensed_features) do
{
compliance_framework: true,
custom_compliance_frameworks: true,
evaluate_group_level_compliance_pipeline: true,
group_level_compliance_dashboard: true,
group_level_compliance_adherence_report: true,
group_level_compliance_violations_report: true
}
end
it_behaves_like 'custom roles abilities'
end
context 'when compliance framework features are unavailable' do
before do
create_member_role(group_member_guest)
stub_licensed_features(
custom_roles: true,
custom_compliance_frameworks: false,
evaluate_group_level_compliance_pipeline: false,
group_level_compliance_dashboard: false,
group_level_compliance_adherence_report: false,
group_level_compliance_violations_report: false
)
end
it { is_expected.to be_disallowed(*allowed_abilities) }
end
end
context 'for a member role with read_compliance_dashboard true' do
let(:member_role_abilities) { { read_compliance_dashboard: true } }
let(:allowed_abilities) do
[
:read_compliance_dashboard,
:read_compliance_adherence_report,
:read_compliance_violations_report
]
end
context 'when compliance framework feature is available' do
let(:licensed_features) do
{
group_level_compliance_dashboard: true,
group_level_compliance_adherence_report: true,
group_level_compliance_violations_report: true
}
end
it_behaves_like 'custom roles abilities'
end
context 'for a custom role with the `admin_security_testing` ability' do
let(:member_role_abilities) { { admin_security_testing: true } }
let(:licensed_features) do
{ security_dashboard: true,
secret_push_protection: true,
group_level_compliance_dashboard: true }
end
let(:allowed_abilities) do
[
:access_security_and_compliance,
:read_security_configuration,
:read_group_security_dashboard,
:read_security_resource,
:enable_secret_push_protection
]
end
it_behaves_like 'custom roles abilities'
end
context 'when compliance framework feature is unavailable' do
before do
create_member_role(group_member_guest)
stub_licensed_features(
custom_roles: true,
group_level_compliance_dashboard: false,
group_level_compliance_adherence_report: false,
group_level_compliance_violations_report: false
)
end
it { is_expected.to be_disallowed(*allowed_abilities) }
end
end
context 'for a custom role with the `remove_group` ability' do
let(:member_role_abilities) { { remove_group: true } }
let(:allowed_abilities) { [:remove_group, :view_edit_page] }
it_behaves_like 'custom roles abilities'
context 'when the group is a top level group' do
before do
create_member_role(parent_group_member_guest)
stub_licensed_features(custom_roles: true)
end
subject { described_class.new(current_user, parent_group) }
it { is_expected.to be_disallowed(*allowed_abilities) }
end
end
context 'for a custom role with the `admin_push_rules` ability' do
let(:member_role_abilities) { { admin_push_rules: true } }
let(:allowed_abilities) { [:admin_push_rules] }
it_behaves_like 'custom roles abilities'
context 'when push rules feature is enabled' do
before do
stub_licensed_features(
custom_roles: true,
push_rules: true,
commit_committer_check: true,
commit_committer_name_check: true,
reject_unsigned_commits: true,
reject_non_dco_commits: true
)
create_member_role(group_member_guest)
end
it do
is_expected.to be_allowed(
:change_push_rules,
:change_commit_committer_check,
:change_commit_committer_name_check,
:change_reject_unsigned_commits,
:change_reject_non_dco_commits
)
end
end
end
context 'for a custom role with the `manage_security_policy_link` ability' do
let(:member_role_abilities) { { manage_security_policy_link: true } }
let(:licensed_features) { { security_orchestration_policies: true } }
let(:allowed_abilities) do
[:read_security_orchestration_policies, :read_security_orchestration_policy_project,
:update_security_orchestration_policy_project]
end
let(:disallowed_abilities) do
[:modify_security_policy]
end
it_behaves_like 'custom roles abilities'
end
context 'for a member role with admin_web_hook true' do
let(:member_role_abilities) { { admin_web_hook: true } }
let(:allowed_abilities) { [:admin_web_hook, :read_web_hook] }
it_behaves_like 'custom roles abilities'
end
context 'for a custom role with the `manage_deploy_tokens` permission' do
let(:member_role_abilities) { { manage_deploy_tokens: true } }
let(:allowed_abilities) do
[:manage_deploy_tokens, :read_deploy_token, :create_deploy_token, :destroy_deploy_token, :view_edit_page]
end
it_behaves_like 'custom roles abilities'
end
context 'for a custom role with the `manage_merge_request_settings` ability' do
let(:member_role_abilities) { { read_code: true, manage_merge_request_settings: true } }
let(:allowed_abilities) { [:manage_merge_request_settings, :view_edit_page] }
it_behaves_like 'custom roles abilities'
context 'when the group is a top level group and the `merge_request_approvers` feature is available' do
before do
create_member_role(parent_group_member_guest)
stub_licensed_features(custom_roles: true, merge_request_approvers: true)
end
subject { described_class.new(current_user, parent_group) }
it { is_expected.to be_allowed(:admin_merge_request_approval_settings) }
end
end
context 'for a member role with `admin_runners` true' do
let(:member_role_abilities) { { admin_runners: true } }
let(:allowed_abilities) do
[
:admin_runner,
:create_runner,
:read_group_all_available_runners,
:read_group_runners
]
end
it_behaves_like 'custom roles abilities'
end
context 'for a custom role with the `admin_integrations` permission' do
let(:member_role_abilities) { { admin_integrations: true } }
let(:allowed_abilities) do
[:admin_integrations]
end
it_behaves_like 'custom roles abilities'
end
context 'for a member role with read_crm_contact true' do
let(:member_role_abilities) { { read_crm_contact: true } }
let(:allowed_abilities) { [:read_crm_contact] }
it_behaves_like 'custom roles abilities'
end
context 'for a member role with read_runners true' do
let(:member_role_abilities) { { read_runners: true } }
let(:allowed_abilities) { [:read_group_runners] }
it_behaves_like 'custom roles abilities'
end
end
describe ':destroy_group policy' do
context 'when default_project_deletion_protection is set to true' do
before do
stub_application_setting(default_project_deletion_protection: true)
stub_licensed_features(custom_roles: true)
end
context 'with admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:remove_group) }
end
context 'with owner' do
let(:current_user) { owner }
context 'when group is empty' do
it { is_expected.to be_allowed(:remove_group) }
end
context 'when group has only inactive project' do
let_it_be(:project_marked_for_deletion) do
create(:project, group: group, marked_for_deletion_at: Time.current)
end
let_it_be(:project_archived) do
create(:project, group: group, archived: true)
end
it { is_expected.to be_allowed(:remove_group) }
end
context 'when group has at least one active project' do
let_it_be(:project) do
create(:project, group: group)
end
it { is_expected.to be_disallowed(:remove_group) }
end
end
end
end
context 'for :read_limit_alert' do
context 'when the user is a guest member of the group' do
let(:current_user) { guest }
it { is_expected.to be_allowed(:read_limit_alert) }
end
context 'when the user is not a member of the group' do
let(:current_user) { non_group_member }
it { is_expected.to be_disallowed(:read_limit_alert) }
end
end
context 'saved replies permissions' do
let(:current_user) { owner }
context 'when no license is present' do
before do
stub_licensed_features(group_saved_replies: false)
end
it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
context 'with correct license' do
before do
stub_licensed_features(group_saved_replies: true)
end
it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
context 'when the user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_allowed(:read_saved_replies) }
it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
context 'when the user is a reporter' do
let(:current_user) { reporter }
it { is_expected.to be_allowed(:read_saved_replies) }
it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
context 'when the user is a planner' do
let(:current_user) { planner }
it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
context 'when the user is a guest member of the group' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:create_saved_replies, :update_saved_replies, :destroy_saved_replies) }
end
end
end
describe 'read_runner_cloud_provisioning_info policy' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
context 'when SaaS-only feature is available' do
before do
stub_saas_features(google_cloud_support: true)
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:read_runner_cloud_provisioning_info) }
end
context 'the user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:read_runner_cloud_provisioning_info) }
end
end
end
describe 'read_runner_gke_provisioning_info policy' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
context 'when SaaS-only feature is available' do
before do
stub_saas_features(google_cloud_support: true)
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:read_runner_gke_provisioning_info) }
end
context 'the user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:read_runner_gke_provisioning_info) }
end
end
end
describe 'provision_cloud_runner policy' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:provision_cloud_runner) }
context 'when SaaS-only feature is available' do
before do
stub_saas_features(google_cloud_support: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:provision_cloud_runner) }
end
context 'the user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:provision_cloud_runner) }
end
end
end
describe 'provision_gke_runner policy' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:provision_gke_runner) }
context 'when SaaS-only feature is available' do
before do
stub_saas_features(google_cloud_support: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:provision_gke_runner) }
end
context 'the user is a guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:provision_gke_runner) }
end
end
end
describe 'read_jobs_statistics' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:read_jobs_statistics) }
context 'when runner performance insights feature is available' do
before do
stub_licensed_features(runner_performance_insights_for_namespace: true)
end
it { is_expected.to be_disallowed(:read_jobs_statistics) }
context 'when user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:read_jobs_statistics) }
end
end
end
describe 'read_runner_usage' do
where(:licensed, :current_user, :enable_admin_mode, :clickhouse_configured, :expected) do
true | ref(:admin) | true | true | true
false | ref(:maintainer) | false | true | false
true | ref(:maintainer) | false | false | false
true | ref(:maintainer) | false | true | true
true | ref(:auditor) | false | true | false
true | ref(:developer) | false | true | false
end
with_them do
before do
stub_licensed_features(runner_performance_insights_for_namespace: licensed)
enable_admin_mode!(admin) if enable_admin_mode
allow(::Gitlab::ClickHouse).to receive(:configured?).and_return(clickhouse_configured)
end
it 'matches expectation' do
if expected
is_expected.to be_allowed(:read_runner_usage)
else
is_expected.to be_disallowed(:read_runner_usage)
end
end
end
end
describe 'web_hooks' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:read_web_hook, :admin_web_hook) }
context 'when user is an owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:read_web_hook, :admin_web_hook) }
end
end
describe 'enable_secret_push_protection' do
using RSpec::Parameterized::TableSyntax
where(:current_user, :licensed, :match_expected_result) do
ref(:owner) | true | be_allowed(:enable_secret_push_protection)
ref(:maintainer) | true | be_allowed(:enable_secret_push_protection)
ref(:developer) | true | be_disallowed(:enable_secret_push_protection)
ref(:owner) | false | be_disallowed(:enable_secret_push_protection)
ref(:maintainer) | false | be_disallowed(:enable_secret_push_protection)
ref(:developer) | false | be_disallowed(:enable_secret_push_protection)
end
with_them do
before do
stub_licensed_features(secret_push_protection: licensed)
end
it { is_expected.to match_expected_result }
end
describe 'when the group does not have the correct license' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:enable_secret_push_protection) }
end
end
describe 'admin_licensed_seat' do
context 'when user is an owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:admin_licensed_seat) }
end
context 'when user is not an owner' do
let(:current_user) { maintainer }
it { is_expected.to be_disallowed(:admin_licensed_seat) }
end
end
describe 'bulk_admin_epic' do
context 'when bulk_edit_feature_available is true' do
before do
stub_licensed_features(epics: true, group_bulk_edit: true)
end
context 'when user is planner or reporter' do
where(role: %w[planner reporter])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_allowed(:bulk_admin_epic) }
end
end
context 'when user is not reporter or better' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:bulk_admin_epic) }
end
end
context 'when bulk_edit_feature_available is false' do
before do
stub_licensed_features(epics: true, group_bulk_edit: false)
end
context 'when user is guest, planner or reporter' do
where(role: %w[guest planner reporter])
with_them do
let(:current_user) { public_send(role) }
it { is_expected.to be_disallowed(:bulk_admin_epic) }
end
end
end
end
describe 'generate_description' do
context "when feature is authorized" do
before do
stub_licensed_features(epics: true)
allow_next_instance_of(::Gitlab::Llm::FeatureAuthorizer) do |instance|
allow(instance).to receive(:allowed?).and_return(true)
end
end
context 'with planner+' do
let(:current_user) { planner }
context 'when user can create issue' do
it { is_expected.to be_allowed(:generate_description) }
end
end
context 'with guest' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:generate_description) }
end
end
context "when feature is not authorized" do
let(:current_user) { owner }
before do
allow_next_instance_of(::Gitlab::Llm::FeatureAuthorizer) do |instance|
allow(instance).to receive(:allowed?).and_return(false)
end
end
it { is_expected.to be_disallowed(:generate_description) }
end
end
describe 'access_ai_review_mr' do
let(:current_user) { owner }
where(:duo_features_enabled, :allowed_to_use, :enabled_for_user) do
false | false | be_disallowed(:access_ai_review_mr)
true | false | be_disallowed(:access_ai_review_mr)
false | true | be_disallowed(:access_ai_review_mr)
true | true | be_allowed(:access_ai_review_mr)
end
with_them do
before do
allow(group).to receive(:duo_features_enabled).and_return(duo_features_enabled)
allow(current_user).to receive(:allowed_to_use?)
.with(:review_merge_request, licensed_feature: :review_merge_request).and_return(allowed_to_use)
end
it { is_expected.to enabled_for_user }
end
end
describe 'admin custom roles', :enable_admin_mode do
let_it_be(:group) { create(:group, :private) }
let_it_be(:user) { create(:user) }
subject { described_class.new(user, group) }
before do
create(:admin_member_role, :read_admin_cicd, user: user)
end
context 'when user can read_admin_cicd' do
context 'when custom roles feature is unavailable' do
before do
stub_licensed_features(custom_roles: false)
end
it { is_expected.to be_disallowed(:read_group_metadata) }
end
context 'when custom roles feature is available' do
before do
stub_licensed_features(custom_roles: true)
end
it { is_expected.to be_allowed(:read_group_metadata) }
end
end
end
end