From 9ac5e75f81e62e9da40793a34d655c8021ccaa8c Mon Sep 17 00:00:00 2001 From: smriti Date: Wed, 30 Apr 2025 23:44:56 +0530 Subject: [PATCH 1/6] View changes for group setting disable invite members --- .../groups/settings/_permissions.html.haml | 5 + doc/user/group/manage.md | 23 ++ .../helpers/ee/groups/group_members_helper.rb | 9 + .../ee/projects/project_members_helper.rb | 9 + ee/app/policies/ee/group_policy.rb | 9 +- ee/app/policies/ee/project_policy.rb | 9 +- .../_disable_invite_members.html.haml | 11 + .../group_disable_invite_members.yml | 5 + ee/lib/ee/gitlab/saas.rb | 1 + .../ee/groups/group_members_helper_spec.rb | 35 ++- .../projects/project_members_helper_spec.rb | 45 +++- ee/spec/policies/group_policy_spec.rb | 167 +++++++++++---- ee/spec/policies/project_policy_spec.rb | 199 ++++++++++++------ ee/spec/requests/api/invitations_spec.rb | 124 +++++++++-- locale/gitlab.pot | 9 + .../groups/group_members_helper_spec.rb | 7 + .../projects/project_members_helper_spec.rb | 1 + 17 files changed, 518 insertions(+), 150 deletions(-) create mode 100644 ee/app/views/groups/settings/_disable_invite_members.html.haml create mode 100644 ee/config/saas_features/group_disable_invite_members.yml diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index f1a91ea1d7f119..01c1028076cfeb 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -43,6 +43,11 @@ = render 'groups/settings/two_factor_auth', f: f, group: @group = render 'groups/settings/membership', f: f, group: @group = render_if_exists 'groups/settings/remove_dormant_members', f: f, group: @group +<<<<<<< HEAD +======= + = render_if_exists 'groups/settings/personal_access_tokens', f: f, group: @group + = render_if_exists 'groups/settings/disable_invite_members', f: f, group: @group +>>>>>>> 04c07db65349 (View changes for group setting disable invite members) = render_if_exists 'groups/settings/extensions_marketplace', f: f, group: @group = render_if_exists 'groups/settings/pages_access_control', f: f, group: @group diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index 19a3ab593d092b..21bb5fe54ad679 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -250,6 +250,29 @@ To disable group mentions: 1. Select **Group mentions are disabled**. 1. Select **Save changes**. +## Disable user invitations to group + +{{< history >}} + +- [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189898) in GitLab 18.0. Disabled by default. + +{{< /history >}} + +You can disable the ability for any user to invite users to groups or projects. After +you configure this setting, only instance administrators can invite users to groups or related subgroups and projects. This setting can only be configured on top level group + +Prerequisites: + +- You must be an administrator. + +To disable user invitations: + +1. On the left sidebar, select **Search or go to** and find your group. +1. Select **Settings > General**. +1. Expand the **Permissions and group features** section. +1. Select **Disable Group/Project members invitation**. +1. Select **Save changes**. + ## Export members as CSV {{< details >}} diff --git a/ee/app/helpers/ee/groups/group_members_helper.rb b/ee/app/helpers/ee/groups/group_members_helper.rb index 2ee025301714fb..fdac8e361541fe 100644 --- a/ee/app/helpers/ee/groups/group_members_helper.rb +++ b/ee/app/helpers/ee/groups/group_members_helper.rb @@ -39,6 +39,15 @@ def group_members_app_data( # rubocop:enable Metrics/ParameterLists def group_member_header_subtext(group) + unless current_user && can?(current_user, :invite_group_members, group) + if Gitlab::Saas.feature_available?(:group_disable_invite_members) + return "You cannot invite a new member to #{group.name} since its disabled by group owner." + end + + return "You cannot invite a new member to #{group.name} since its disabled by administrator." + + end + if ::Namespaces::FreeUserCap::Enforcement.new(group.root_ancestor).enforce_cap? && can?(current_user, :admin_group_member, group.root_ancestor) super + member_header_manage_namespace_members_text(group.root_ancestor) diff --git a/ee/app/helpers/ee/projects/project_members_helper.rb b/ee/app/helpers/ee/projects/project_members_helper.rb index 1d7bcf7192a70c..ed10094f4ba40f 100644 --- a/ee/app/helpers/ee/projects/project_members_helper.rb +++ b/ee/app/helpers/ee/projects/project_members_helper.rb @@ -25,6 +25,15 @@ def can_approve_access_requests(project) end def project_member_header_subtext(project) + unless can?(current_user, :invite_project_members, project) + if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) + return "You cannot invite a new member to #{project.name} since its disabled by group owner." + end + + return "You cannot invite a new member to #{project.name} since its disabled by administrator." + + end + if project.group && ::Namespaces::FreeUserCap::Enforcement.new(project.root_ancestor).enforce_cap? && can?(current_user, :admin_group_member, project.root_ancestor) diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb index e9e0b7065b4544..0364adec8aa3b4 100644 --- a/ee/app/policies/ee/group_policy.rb +++ b/ee/app/policies/ee/group_policy.rb @@ -974,8 +974,13 @@ module GroupPolicy end condition(:disable_invite_members, scope: :global) do - ::License.feature_available?(:disable_invite_members) && - ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? + if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) + @subject.licensed_feature_available?(:disable_invite_members) && + @subject.root_ancestor.disable_invite_members? + else + ::License.feature_available?(:disable_invite_members) && + ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? + end end rule { can?(:admin_epic) & bulk_edit_feature_available }.policy do diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb index e7a30106adad9c..ad983c90cf57d6 100644 --- a/ee/app/policies/ee/project_policy.rb +++ b/ee/app/policies/ee/project_policy.rb @@ -74,8 +74,13 @@ module ProjectPolicy with_scope :global condition(:disable_invite_members) do - License.feature_available?(:disable_invite_members) && - ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? + if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) + @subject.group&.root_ancestor&.licensed_feature_available?(:disable_invite_members) && + @subject.group&.root_ancestor.disable_invite_members? + else + License.feature_available?(:disable_invite_members) && + ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? + end end condition(:group_merge_request_approval_settings_enabled) do diff --git a/ee/app/views/groups/settings/_disable_invite_members.html.haml b/ee/app/views/groups/settings/_disable_invite_members.html.haml new file mode 100644 index 00000000000000..18d46e7bb47770 --- /dev/null +++ b/ee/app/views/groups/settings/_disable_invite_members.html.haml @@ -0,0 +1,11 @@ +- return unless group.root? && group.licensed_feature_available?(:disable_invite_members) + +%h5= _('Group/Project Invite members restrictions') + +.form-group.gl-mb-3 + = f.gitlab_ui_checkbox_component :disable_invite_members, checkbox_options: { checked: group.disable_invite_members? } do |c| + - c.with_label do + = s_('GroupSettings|Disable inviting members to group/projects within %{group}').html_safe % { group: link_to_group(group) } + - c.with_help_text do + - learn_more_link = link_to(_('Learn more'), help_page_path('user/group/manage.md', anchor: 'disable-user-invitations-to-group')) + = s_("GroupSettings|If enabled, group/project owners and project maintainers will not be able to invite users to group/project. %{learn_more_link}.").html_safe % { learn_more_link: learn_more_link } diff --git a/ee/config/saas_features/group_disable_invite_members.yml b/ee/config/saas_features/group_disable_invite_members.yml new file mode 100644 index 00000000000000..dcf86eff77dee0 --- /dev/null +++ b/ee/config/saas_features/group_disable_invite_members.yml @@ -0,0 +1,5 @@ +--- +name: group_disable_invite_members +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/189898 +milestone: '18.0' +group: group::authentication diff --git a/ee/lib/ee/gitlab/saas.rb b/ee/lib/ee/gitlab/saas.rb index af164cb3af6937..9e9ba46faf6af2 100644 --- a/ee/lib/ee/gitlab/saas.rb +++ b/ee/lib/ee/gitlab/saas.rb @@ -40,6 +40,7 @@ module Saas instance_push_limit hide_project_instance_tab cloud_connector_self_signed_tokens + group_disable_invite_members ].freeze CONFIG_FILE_ROOT = 'ee/config/saas_features' diff --git a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb index 15407734c554d7..a6d66ea59b65e1 100644 --- a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb +++ b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe Groups::GroupMembersHelper do +RSpec.describe Groups::GroupMembersHelper, feature_category: :groups_and_projects do include MembersPresentation using RSpec::Parameterized::TableSyntax @@ -206,18 +206,39 @@ describe '#group_member_header_subtext' do let(:base_subtext) { "You're viewing members of #{group.name}." } + let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{group.name} since its disabled by group owner." } + let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{group.name} since its disabled by administrator." } + let(:standard_subtext) { "^#{base_subtext}$" } let(:enforcement_subtext) { "^#{base_subtext}
To manage seats for all members" } - where(:can_admin_member, :enforce_free_user_cap, :subtext) do - true | true | ref(:enforcement_subtext) - true | false | ref(:standard_subtext) - false | true | ref(:standard_subtext) - false | false | ref(:standard_subtext) + where(:com, :can_invite_member, :can_admin_member, :enforce_free_user_cap, :subtext) do + true | true | true | true | ref(:enforcement_subtext) + true | false | true | true | ref(:cannot_invite_subtext_for_com) + false | false | true | true | ref(:cannot_invite_subtext_for_self_managed) + + true | true | true | false | ref(:standard_subtext) + true | false | true | false | ref(:cannot_invite_subtext_for_com) + false | false | true | false | ref(:cannot_invite_subtext_for_self_managed) + + true | true | false | true | ref(:standard_subtext) + true | false | false | true | ref(:cannot_invite_subtext_for_com) + false | false | false | true | ref(:cannot_invite_subtext_for_self_managed) + + true | true | false | false | ref(:standard_subtext) + true | false | false | false | ref(:cannot_invite_subtext_for_com) + false | false | false | false | ref(:cannot_invite_subtext_for_self_managed) end before do - allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(can_admin_member) + allow(helper).to receive(:can?).with(current_user, :invite_group_members, group) + .and_return(can_invite_member) + allow(Gitlab::Saas).to receive(:feature_available?).with(:group_disable_invite_members).and_return(com) + + if can_invite_member + allow(helper).to receive(:can?).with(current_user, :admin_group_member, group).and_return(can_admin_member) + end + allow_next_instance_of(::Namespaces::FreeUserCap::Enforcement, group) do |instance| allow(instance).to receive(:enforce_cap?).and_return(enforce_free_user_cap) end diff --git a/ee/spec/helpers/projects/project_members_helper_spec.rb b/ee/spec/helpers/projects/project_members_helper_spec.rb index 260f2f8180802e..4d0db398ef442e 100644 --- a/ee/spec/helpers/projects/project_members_helper_spec.rb +++ b/ee/spec/helpers/projects/project_members_helper_spec.rb @@ -165,26 +165,51 @@ def initialize(user) end end - describe '#project_member_header_subtext' do + describe '#project_member_header_subtext', feature_category: :groups_and_projects do let(:base_subtext) { "You can invite a new member to #{current_project.name} or invite another group." } + let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{current_project.name} since its disabled by group owner." } + let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{current_project.name} since its disabled by administrator." } let(:standard_subtext) { "^#{base_subtext}$" } let(:enforcement_subtext) { "^#{base_subtext}
To manage seats for all members" } let_it_be(:project_with_group) { create(:project, group: create(:group)) } - where(:can_admin_member, :enforce_free_user_cap, :subtext, :current_project) do - true | true | ref(:standard_subtext) | ref(:project) - true | true | ref(:enforcement_subtext) | ref(:project_with_group) - true | false | ref(:standard_subtext) | ref(:project_with_group) - false | true | ref(:standard_subtext) | ref(:project_with_group) - false | false | ref(:standard_subtext) | ref(:project_with_group) + where(:com, :can_invite_member, :can_admin_member, :enforce_free_user_cap, :subtext, :current_project) do + true | true | true | true | ref(:standard_subtext) | ref(:project) + false | false | true | true | ref(:cannot_invite_subtext_for_self_managed) | ref(:project) + true | false | true | true | ref(:cannot_invite_subtext_for_com) | ref(:project) + + true | true | true | true | ref(:enforcement_subtext) | ref(:project_with_group) + false | false | true | true | ref(:cannot_invite_subtext_for_self_managed) | ref(:project_with_group) + true | false | true | true | ref(:cannot_invite_subtext_for_com) | ref(:project_with_group) + + true | true | true | false | ref(:standard_subtext) | ref(:project_with_group) + false | false | true | false | ref(:cannot_invite_subtext_for_self_managed) | ref(:project_with_group) + true | false | true | false | ref(:cannot_invite_subtext_for_com) | ref(:project_with_group) + + true | true | false | true | ref(:standard_subtext) | ref(:project_with_group) + false | false | false | true | ref(:cannot_invite_subtext_for_self_managed) | ref(:project_with_group) + true | false | false | true | ref(:cannot_invite_subtext_for_com) | ref(:project_with_group) + + true | true | false | false | ref(:standard_subtext) | ref(:project_with_group) + false | false | false | false | ref(:cannot_invite_subtext_for_self_managed) | ref(:project_with_group) + true | false | false | false | ref(:cannot_invite_subtext_for_com) | ref(:project_with_group) end before do assign(:project, current_project) - allow(helper).to receive(:can?).with(current_user, :admin_project_member, current_project).and_return(true) - allow(helper).to receive(:can?).with(current_user, :admin_group_member, current_project.root_ancestor) - .and_return(can_admin_member) + + allow(helper).to receive(:can?).with(current_user, :invite_project_members, current_project) + .and_return(can_invite_member) + allow(::Gitlab::Saas).to receive(:feature_available?).with(:group_disable_invite_members).and_return(com) + + if can_invite_member + allow(helper).to receive(:can?).with(current_user, :admin_group_member, current_project.root_ancestor) + .and_return(can_admin_member) + allow(helper).to receive(:can?).with(current_user, :admin_project_member, current_project) + .and_return(can_invite_member) + end + allow_next_instance_of(::Namespaces::FreeUserCap::Enforcement, current_project.root_ancestor) do |instance| allow(instance).to receive(:enforce_cap?).and_return(enforce_free_user_cap) end diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb index 0fe3eee42112b4..7199dcdfc89daf 100644 --- a/ee/spec/policies/group_policy_spec.rb +++ b/ee/spec/policies/group_policy_spec.rb @@ -240,63 +240,140 @@ def stub_group_saml_config(enabled) end describe 'invite_group_members policy' do - 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 + context 'when on saas', :saas do + let(:policy) { :invite_group_members } + let(:app_setting) { :disable_invite_members } - with_them do - let(:current_user) { public_send(role) } + 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 - before do - stub_licensed_features(disable_invite_members: true) - stub_application_setting(app_setting => setting) - enable_admin_mode!(current_user) if admin_mode + 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 - it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) } + 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 '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 + context 'when self-managed' do + before do + allow(Gitlab).to receive(:com?).and_return(false) end - with_them do - let(:current_user) { public_send(role) } + let(:app_setting) { :disable_invite_members } + let(:policy) { :invite_group_members } - before do - stub_licensed_features(disable_invite_members: false) - stub_application_setting(app_setting => setting) - enable_admin_mode!(current_user) if admin_mode + 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 - it { is_expected.to(allowed ? be_allowed(policy) : be_disallowed(policy)) } + 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 diff --git a/ee/spec/policies/project_policy_spec.rb b/ee/spec/policies/project_policy_spec.rb index b44adbad068619..ac0c2291781bff 100644 --- a/ee/spec/policies/project_policy_spec.rb +++ b/ee/spec/policies/project_policy_spec.rb @@ -2101,68 +2101,6 @@ end end - describe 'invite_group_members policy' do - let(:app_setting) { :disable_invite_members } - let(:policy) { :invite_project_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 | true - :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 | true - :maintainer | true | nil | true - :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 - context 'when project is read only on the namespace' do let(:project) { public_project_in_group } let(:current_user) { maintainer } @@ -4945,6 +4883,143 @@ def create_member_role(member, abilities = member_role_abilities) end end + describe 'invite_group_members policy' do + let(:app_setting) { :disable_invite_members } + let(:policy) { :invite_project_members } + let(:group) { create(:group) } + + context 'when on saas', :saas do + before do + allow(project).to receive(:group).and_return(group) + + stub_saas_features(group_disable_invite_members: true) + end + + context 'with disable_invite_members is available in license' do + where(:role, :parent_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 | true + :maintainer | false | false | true + :maintainer | true | true | false + :maintainer | true | false | 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(project.group).to receive(:disable_invite_members?).and_return(parent_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, :parent_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 | true + :maintainer | false | false | true + :maintainer | true | true | true + :maintainer | true | false | true + :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(project.group).to receive(:disable_invite_members?).and_return(parent_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 '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 | true + :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 | true + :maintainer | true | nil | true + :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 + describe 'pages_multiple_versions_available' do let(:current_user) { maintainer } diff --git a/ee/spec/requests/api/invitations_spec.rb b/ee/spec/requests/api/invitations_spec.rb index 8137cb2901aafe..cc6d3f01c15074 100644 --- a/ee/spec/requests/api/invitations_spec.rb +++ b/ee/spec/requests/api/invitations_spec.rb @@ -469,17 +469,15 @@ context 'when licensed feature for disable_invite_members is available' do let(:email) { 'example1@example.com' } let(:maintainer) { create(:user) } + let(:owner) { create(:user) } before do project.add_maintainer(maintainer) + project.add_maintainer(owner) stub_licensed_features(disable_invite_members: true) end - context 'when setting to disable_group_invite_member is ON' do - before do - stub_application_setting(disable_invite_members: true) - end - + shared_examples "user is not allowed to invite members" do context 'when user is maintainer/owner' do it 'returns 403' do post api(url, maintainer), @@ -510,22 +508,85 @@ end end - context 'when setting to disable_group_invite_member is OFF' do - let_it_be(:owner) { create(:user) } - - before do - project.add_owner(owner) - stub_application_setting(disable_invite_members: false) + shared_examples "user is allowed to invite members" do + it 'adds a new member by email for owner role' do + expect do + post api(url, owner), + params: { email: email, access_level: Member::MAINTAINER } + expect(response).to have_gitlab_http_status(:created) + end.to change { project.members.invite.count }.by(1) end - it 'adds a new member by email for owner/maintainer role' do + it 'adds a new member by email for maintainer role' do expect do - post api(url, owner), + post api(url, maintainer), params: { email: email, access_level: Member::MAINTAINER } expect(response).to have_gitlab_http_status(:created) end.to change { project.members.invite.count }.by(1) end end + + context 'when .com', :saas do + let_it_be(:group, refind: true) { create(:group_with_plan, plan: :premium_plan) } + let_it_be(:project, refind: true) { create(:project, namespace: group) } + + context 'when saas feature is available' do + before do + stub_saas_features(group_disable_invite_members: true) + end + + context 'when setting to disable_invite_member is ON' do + before do + group.update!(disable_invite_members: true) + end + + it_behaves_like "user is not allowed to invite members" + + context 'when disable_invite_members application setting is OFF' do + before do + stub_application_setting(disable_invite_members: false) + end + + it_behaves_like "user is not allowed to invite members" + end + end + + context 'when setting to disable_invite_member is OFF' do + before do + group.update!(disable_invite_members: false) + end + + it_behaves_like "user is allowed to invite members" + end + end + + context 'when saas feature is not available' do + before do + stub_saas_features(group_disable_invite_members: false) + group.update!(disable_invite_members: true) + end + + it_behaves_like "user is allowed to invite members" + end + end + + context 'when self-managed' do + context 'when setting to disable_invite_member is ON' do + before do + stub_application_setting(disable_invite_members: true) + end + + it_behaves_like "user is not allowed to invite members" + end + + context 'when setting to disable_invite_member is OFF' do + before do + stub_application_setting(disable_invite_members: false) + end + + it_behaves_like "user is allowed to invite members" + end + end end context 'when licensed feature disable_invite_members is not available' do @@ -537,17 +598,36 @@ stub_licensed_features(disable_invite_members: false) end - context 'setting to disable_invite_members is ON' do - before do - stub_application_setting(disable_invite_members: true) + context 'when .com', :saas do + context 'setting to disable_invite_members is ON' do + before do + stub_saas_features(group_disable_invite_members: true) + project.group.update!(disable_invite_members: true) + end + + it "does not make any difference to invitation of new member" do + expect do + post api(url, maintainer), + params: { email: email, access_level: Member::MAINTAINER } + expect(response).to have_gitlab_http_status(:created) + end.to change { project.members.invite.count }.by(1) + end end + end - it "does not make any difference to invitation of new member" do - expect do - post api(url, maintainer), - params: { email: email, access_level: Member::MAINTAINER } - expect(response).to have_gitlab_http_status(:created) - end.to change { project.members.invite.count }.by(1) + context 'when self-managed' do + context 'setting to disable_invite_members is ON' do + before do + stub_application_setting(disable_invite_members: true) + end + + it "does not make any difference to invitation of new member" do + expect do + post api(url, maintainer), + params: { email: email, access_level: Member::MAINTAINER } + expect(response).to have_gitlab_http_status(:created) + end.to change { project.members.invite.count }.by(1) + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b98a755c41cfc3..ea6fe6666ed9de 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -29691,6 +29691,9 @@ msgstr "" msgid "Group-level wiki is disabled." msgstr "" +msgid "Group/Project Invite members restrictions" +msgstr "" + msgid "Group: %{group_name}" msgstr "" @@ -30075,6 +30078,9 @@ msgstr "" msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group" msgstr "" +msgid "GroupSettings|Disable inviting members to group/projects within %{group}" +msgstr "" + msgid "GroupSettings|Disable personal access tokens for enterprise users" msgstr "" @@ -30144,6 +30150,9 @@ msgstr "" msgid "GroupSettings|If enabled, group access tokens expiry webhooks execute 60, 30, and 7 days before the token expires. If disabled, these webhooks only execute 7 days before the token expires." msgstr "" +msgid "GroupSettings|If enabled, group/project owners and project maintainers will not be able to invite users to group/project. %{learn_more_link}." +msgstr "" + msgid "GroupSettings|If enabled, individual user accounts will be able to use only issued SSH certificates for Git access. It doesn't apply to service accounts, deploy keys, and other types of internal accounts." msgstr "" diff --git a/spec/helpers/groups/group_members_helper_spec.rb b/spec/helpers/groups/group_members_helper_spec.rb index ff5e188a348dd0..025ffb4ff30de2 100644 --- a/spec/helpers/groups/group_members_helper_spec.rb +++ b/spec/helpers/groups/group_members_helper_spec.rb @@ -211,6 +211,13 @@ end describe '#group_member_header_subtext' do + let(:current_user) { create(:user) } + + before do + allow(helper).to receive(:current_user).and_return(current_user) + allow(helper).to receive(:can?).with(current_user, :invite_group_members, group).and_return(true) + end + it 'contains expected text with group name' do expect(helper.group_member_header_subtext(group)).to match("You're viewing members of .*#{group.name}") end diff --git a/spec/helpers/projects/project_members_helper_spec.rb b/spec/helpers/projects/project_members_helper_spec.rb index 6ade90c3273a60..0f1f7f869b4ba9 100644 --- a/spec/helpers/projects/project_members_helper_spec.rb +++ b/spec/helpers/projects/project_members_helper_spec.rb @@ -167,6 +167,7 @@ describe '#project_member_header_subtext' do before do + allow(helper).to receive(:can?).with(current_user, :invite_project_members, project).and_return(true) allow(helper).to receive(:can?).with(current_user, :admin_project_member, project).and_return(can_admin_member) end -- GitLab From 5c4476cfaf72a480377b4466e8d25f8fe0e7c6ea Mon Sep 17 00:00:00 2001 From: smriti Date: Wed, 30 Apr 2025 23:44:56 +0530 Subject: [PATCH 2/6] View changes for group setting disable invite members Spec changes finalized Spec changes finalized Spec changes finalized Spec changes finalized Spec changes finalized -- GitLab From d4228e306b2f875b677b5697b085dec0c71a2498 Mon Sep 17 00:00:00 2001 From: smriti Date: Fri, 9 May 2025 13:31:33 +0530 Subject: [PATCH 3/6] Review suggestions for policy --- .../groups/settings/_permissions.html.haml | 4 ---- ee/app/policies/ee/group_policy.rb | 17 +++++++++-------- ee/app/policies/ee/project_policy.rb | 19 +++++++++++-------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml index 01c1028076cfeb..dfc244825a2137 100644 --- a/app/views/groups/settings/_permissions.html.haml +++ b/app/views/groups/settings/_permissions.html.haml @@ -43,11 +43,7 @@ = render 'groups/settings/two_factor_auth', f: f, group: @group = render 'groups/settings/membership', f: f, group: @group = render_if_exists 'groups/settings/remove_dormant_members', f: f, group: @group -<<<<<<< HEAD -======= - = render_if_exists 'groups/settings/personal_access_tokens', f: f, group: @group = render_if_exists 'groups/settings/disable_invite_members', f: f, group: @group ->>>>>>> 04c07db65349 (View changes for group setting disable invite members) = render_if_exists 'groups/settings/extensions_marketplace', f: f, group: @group = render_if_exists 'groups/settings/pages_access_control', f: f, group: @group diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb index 0364adec8aa3b4..da33504e954f98 100644 --- a/ee/app/policies/ee/group_policy.rb +++ b/ee/app/policies/ee/group_policy.rb @@ -973,21 +973,22 @@ module GroupPolicy @subject.licensed_feature_available?(:group_bulk_edit) end - condition(:disable_invite_members, scope: :global) do - if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) + condition(:disable_invite_members_for_group, scope: :subject) do + ::Gitlab::Saas.feature_available?(:group_disable_invite_members) && @subject.licensed_feature_available?(:disable_invite_members) && - @subject.root_ancestor.disable_invite_members? - else - ::License.feature_available?(:disable_invite_members) && - ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? - end + @subject.root_ancestor.disable_invite_members? + end + + condition(:disable_invite_members, scope: :global) do + ::License.feature_available?(:disable_invite_members) && + ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? end rule { can?(:admin_epic) & bulk_edit_feature_available }.policy do enable :bulk_admin_epic end - rule { ~admin & disable_invite_members }.policy do + rule { ~admin & ((~is_gitlab_com & disable_invite_members) | disable_invite_members_for_group) }.policy do prevent :invite_group_members end diff --git a/ee/app/policies/ee/project_policy.rb b/ee/app/policies/ee/project_policy.rb index ad983c90cf57d6..1c7ad156f2bf16 100644 --- a/ee/app/policies/ee/project_policy.rb +++ b/ee/app/policies/ee/project_policy.rb @@ -72,15 +72,18 @@ module ProjectPolicy ::Gitlab::CurrentSettings.disable_overriding_approvers_per_merge_request end + with_scope :subject + condition(:disable_invite_members_for_group) do + ::Gitlab::Saas.feature_available?(:group_disable_invite_members) && + @subject.group && + @subject.group.root_ancestor.licensed_feature_available?(:disable_invite_members) && + @subject.group.root_ancestor.disable_invite_members? + end + with_scope :global condition(:disable_invite_members) do - if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) - @subject.group&.root_ancestor&.licensed_feature_available?(:disable_invite_members) && - @subject.group&.root_ancestor.disable_invite_members? - else - License.feature_available?(:disable_invite_members) && - ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? - end + License.feature_available?(:disable_invite_members) && + ::Gitlab::CurrentSettings.current_application_settings.disable_invite_members? end condition(:group_merge_request_approval_settings_enabled) do @@ -413,7 +416,7 @@ module ProjectPolicy prevent(:read_issue_analytics) end - rule { ~admin & disable_invite_members }.policy do + rule { ~admin & ((~is_gitlab_com & disable_invite_members) | disable_invite_members_for_group) }.policy do prevent :invite_project_members end -- GitLab From fb0c6d25702a583a46d7439b191cc22d70dfba26 Mon Sep 17 00:00:00 2001 From: smriti Date: Fri, 9 May 2025 22:48:16 +0530 Subject: [PATCH 4/6] Review suggestions for internationalization Review suggestions for internationalization --- ee/app/helpers/ee/groups/group_members_helper.rb | 12 ++++++++++-- .../helpers/ee/groups/group_members_helper_spec.rb | 4 ++-- locale/gitlab.pot | 3 +++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ee/app/helpers/ee/groups/group_members_helper.rb b/ee/app/helpers/ee/groups/group_members_helper.rb index fdac8e361541fe..0a55fb856e7c7d 100644 --- a/ee/app/helpers/ee/groups/group_members_helper.rb +++ b/ee/app/helpers/ee/groups/group_members_helper.rb @@ -41,10 +41,10 @@ def group_members_app_data( def group_member_header_subtext(group) unless current_user && can?(current_user, :invite_group_members, group) if Gitlab::Saas.feature_available?(:group_disable_invite_members) - return "You cannot invite a new member to #{group.name} since its disabled by group owner." + return cannot_invite_member_subtext(group.name, "group owner") end - return "You cannot invite a new member to #{group.name} since its disabled by administrator." + return cannot_invite_member_subtext(group.name, "administrator") end @@ -65,4 +65,12 @@ def available_group_roles(group) super + custom_role_options end + + private + + def cannot_invite_member_subtext(group_name, actor) + safe_format( + _("You cannot invite a new member to %{strong_start}%{group_name}%{strong_end} since its disabled by %{actor}."), + tag_pair(tag.strong, :strong_start, :strong_end), group_name: group_name, actor: actor) + end end diff --git a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb index a6d66ea59b65e1..4df54392d2dd70 100644 --- a/ee/spec/helpers/ee/groups/group_members_helper_spec.rb +++ b/ee/spec/helpers/ee/groups/group_members_helper_spec.rb @@ -206,8 +206,8 @@ describe '#group_member_header_subtext' do let(:base_subtext) { "You're viewing members of #{group.name}." } - let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{group.name} since its disabled by group owner." } - let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{group.name} since its disabled by administrator." } + let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{group.name} since its disabled by group owner." } + let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{group.name} since its disabled by administrator." } let(:standard_subtext) { "^#{base_subtext}$" } let(:enforcement_subtext) { "^#{base_subtext}
To manage seats for all members" } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ea6fe6666ed9de..bb984b9bdf6264 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -70296,6 +70296,9 @@ msgstr "" msgid "You cannot impersonate an internal user" msgstr "" +msgid "You cannot invite a new member to %{strong_start}%{group_name}%{strong_end} since its disabled by %{actor}." +msgstr "" + msgid "You cannot play this scheduled pipeline at the moment. Please wait a minute." msgstr "" -- GitLab From 590ff74c9e4e7e0506153c3b51bb0a474524e151 Mon Sep 17 00:00:00 2001 From: Smriti Garg Date: Mon, 12 May 2025 09:19:31 +0000 Subject: [PATCH 5/6] Docs suggestions Docs suggestions --- doc/user/group/manage.md | 9 +++++---- ee/app/helpers/ee/projects/project_members_helper.rb | 4 ++-- .../groups/settings/_disable_invite_members.html.haml | 6 +++--- ee/spec/helpers/projects/project_members_helper_spec.rb | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md index 21bb5fe54ad679..5e87ed2e24dbc0 100644 --- a/doc/user/group/manage.md +++ b/doc/user/group/manage.md @@ -250,7 +250,7 @@ To disable group mentions: 1. Select **Group mentions are disabled**. 1. Select **Save changes**. -## Disable user invitations to group +## Disable user invitations to a group {{< history >}} @@ -258,12 +258,13 @@ To disable group mentions: {{< /history >}} -You can disable the ability for any user to invite users to groups or projects. After -you configure this setting, only instance administrators can invite users to groups or related subgroups and projects. This setting can only be configured on top level group +You can disable the ability for users to invite new members to sub-groups or projects in a top-level +group. This also stops group Owners from sending invites. You must disable this setting before you +can invite users again. Prerequisites: -- You must be an administrator. +- You must have the Owner role for the group. To disable user invitations: diff --git a/ee/app/helpers/ee/projects/project_members_helper.rb b/ee/app/helpers/ee/projects/project_members_helper.rb index ed10094f4ba40f..b2e4e29272a273 100644 --- a/ee/app/helpers/ee/projects/project_members_helper.rb +++ b/ee/app/helpers/ee/projects/project_members_helper.rb @@ -27,10 +27,10 @@ def can_approve_access_requests(project) def project_member_header_subtext(project) unless can?(current_user, :invite_project_members, project) if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) - return "You cannot invite a new member to #{project.name} since its disabled by group owner." + return "You cannot invite a new member to #{project.name}. User invitations are disabled by the group owner." end - return "You cannot invite a new member to #{project.name} since its disabled by administrator." + return "You cannot invite a new member to #{project.name}. User invitations are disabled by the instance administrator." end diff --git a/ee/app/views/groups/settings/_disable_invite_members.html.haml b/ee/app/views/groups/settings/_disable_invite_members.html.haml index 18d46e7bb47770..6c0bb49e4a37a8 100644 --- a/ee/app/views/groups/settings/_disable_invite_members.html.haml +++ b/ee/app/views/groups/settings/_disable_invite_members.html.haml @@ -1,11 +1,11 @@ - return unless group.root? && group.licensed_feature_available?(:disable_invite_members) -%h5= _('Group/Project Invite members restrictions') +%h5= _('User invitation restrictions') .form-group.gl-mb-3 = f.gitlab_ui_checkbox_component :disable_invite_members, checkbox_options: { checked: group.disable_invite_members? } do |c| - c.with_label do - = s_('GroupSettings|Disable inviting members to group/projects within %{group}').html_safe % { group: link_to_group(group) } + = s_('GroupSettings|Disable user invitations to groups and projects within %{group}').html_safe % { group: link_to_group(group) } - c.with_help_text do - learn_more_link = link_to(_('Learn more'), help_page_path('user/group/manage.md', anchor: 'disable-user-invitations-to-group')) - = s_("GroupSettings|If enabled, group/project owners and project maintainers will not be able to invite users to group/project. %{learn_more_link}.").html_safe % { learn_more_link: learn_more_link } + = s_("GroupSettings|If enabled, users can no longer invite members to groups or projects in the top-level group. %{learn_more_link}.").html_safe % { learn_more_link: learn_more_link } diff --git a/ee/spec/helpers/projects/project_members_helper_spec.rb b/ee/spec/helpers/projects/project_members_helper_spec.rb index 4d0db398ef442e..2f9b294744229b 100644 --- a/ee/spec/helpers/projects/project_members_helper_spec.rb +++ b/ee/spec/helpers/projects/project_members_helper_spec.rb @@ -167,8 +167,8 @@ def initialize(user) describe '#project_member_header_subtext', feature_category: :groups_and_projects do let(:base_subtext) { "You can invite a new member to #{current_project.name} or invite another group." } - let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{current_project.name} since its disabled by group owner." } - let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{current_project.name} since its disabled by administrator." } + let(:cannot_invite_subtext_for_com) { "You cannot invite a new member to #{current_project.name}. User invitations are disabled by the group owner." } + let(:cannot_invite_subtext_for_self_managed) { "You cannot invite a new member to #{current_project.name}. User invitations are disabled by the instance administrator." } let(:standard_subtext) { "^#{base_subtext}$" } let(:enforcement_subtext) { "^#{base_subtext}
To manage seats for all members" } -- GitLab From b4af631d32499385225f73e7cb0cc421ae31b8b4 Mon Sep 17 00:00:00 2001 From: smriti Date: Mon, 12 May 2025 16:56:39 +0530 Subject: [PATCH 6/6] Spec failure resolved Spec failure resolved Spec failure resolved --- .../helpers/ee/projects/project_members_helper.rb | 6 ++++-- .../settings/_disable_invite_members.html.haml | 2 +- .../groups/group_members/index.html.haml_spec.rb | 1 + locale/gitlab.pot | 14 +++++++------- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ee/app/helpers/ee/projects/project_members_helper.rb b/ee/app/helpers/ee/projects/project_members_helper.rb index b2e4e29272a273..971b471c8467f0 100644 --- a/ee/app/helpers/ee/projects/project_members_helper.rb +++ b/ee/app/helpers/ee/projects/project_members_helper.rb @@ -27,10 +27,12 @@ def can_approve_access_requests(project) def project_member_header_subtext(project) unless can?(current_user, :invite_project_members, project) if ::Gitlab::Saas.feature_available?(:group_disable_invite_members) - return "You cannot invite a new member to #{project.name}. User invitations are disabled by the group owner." + return "You cannot invite a new member to #{project.name}. " \ + "User invitations are disabled by the group owner." end - return "You cannot invite a new member to #{project.name}. User invitations are disabled by the instance administrator." + return "You cannot invite a new member to #{project.name}. " \ + "User invitations are disabled by the instance administrator." end diff --git a/ee/app/views/groups/settings/_disable_invite_members.html.haml b/ee/app/views/groups/settings/_disable_invite_members.html.haml index 6c0bb49e4a37a8..9374936e459b88 100644 --- a/ee/app/views/groups/settings/_disable_invite_members.html.haml +++ b/ee/app/views/groups/settings/_disable_invite_members.html.haml @@ -7,5 +7,5 @@ - c.with_label do = s_('GroupSettings|Disable user invitations to groups and projects within %{group}').html_safe % { group: link_to_group(group) } - c.with_help_text do - - learn_more_link = link_to(_('Learn more'), help_page_path('user/group/manage.md', anchor: 'disable-user-invitations-to-group')) + - learn_more_link = link_to(_('Learn more'), help_page_path('user/group/manage.md', anchor: 'disable-user-invitations-to-a-group')) = s_("GroupSettings|If enabled, users can no longer invite members to groups or projects in the top-level group. %{learn_more_link}.").html_safe % { learn_more_link: learn_more_link } diff --git a/ee/spec/views/groups/group_members/index.html.haml_spec.rb b/ee/spec/views/groups/group_members/index.html.haml_spec.rb index 5d4ece1d86d482..8d8947b3c3c092 100644 --- a/ee/spec/views/groups/group_members/index.html.haml_spec.rb +++ b/ee/spec/views/groups/group_members/index.html.haml_spec.rb @@ -24,6 +24,7 @@ before do allow(view).to receive(:can_admin_group_member?).with(group).and_return(true) allow(view).to receive(:can?).with(user, :admin_group_member, group.root_ancestor).and_return(true) + allow(view).to receive(:can?).with(user, :invite_group_members, group.root_ancestor).and_return(true) allow_next_instance_of(::Namespaces::FreeUserCap::Enforcement, group.root_ancestor) do |instance| allow(instance).to receive(:enforce_cap?).and_return(true) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bb984b9bdf6264..ed67e96357e812 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -29691,9 +29691,6 @@ msgstr "" msgid "Group-level wiki is disabled." msgstr "" -msgid "Group/Project Invite members restrictions" -msgstr "" - msgid "Group: %{group_name}" msgstr "" @@ -30078,10 +30075,10 @@ msgstr "" msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group" msgstr "" -msgid "GroupSettings|Disable inviting members to group/projects within %{group}" +msgid "GroupSettings|Disable personal access tokens for enterprise users" msgstr "" -msgid "GroupSettings|Disable personal access tokens for enterprise users" +msgid "GroupSettings|Disable user invitations to groups and projects within %{group}" msgstr "" msgid "GroupSettings|Emails are not encrypted. Concerned administrators may want to disable diff previews." @@ -30150,10 +30147,10 @@ msgstr "" msgid "GroupSettings|If enabled, group access tokens expiry webhooks execute 60, 30, and 7 days before the token expires. If disabled, these webhooks only execute 7 days before the token expires." msgstr "" -msgid "GroupSettings|If enabled, group/project owners and project maintainers will not be able to invite users to group/project. %{learn_more_link}." +msgid "GroupSettings|If enabled, individual user accounts will be able to use only issued SSH certificates for Git access. It doesn't apply to service accounts, deploy keys, and other types of internal accounts." msgstr "" -msgid "GroupSettings|If enabled, individual user accounts will be able to use only issued SSH certificates for Git access. It doesn't apply to service accounts, deploy keys, and other types of internal accounts." +msgid "GroupSettings|If enabled, users can no longer invite members to groups or projects in the top-level group. %{learn_more_link}." msgstr "" msgid "GroupSettings|If not specified at the group or instance level, the default is %{default_initial_branch_name}. Does not affect existing repositories." @@ -65614,6 +65611,9 @@ msgid_plural "Users in subscription" msgstr[0] "" msgstr[1] "" +msgid "User invitation restrictions" +msgstr "" + msgid "User is blocked" msgstr "" -- GitLab