diff --git a/doc/user/profile/service_accounts.md b/doc/user/profile/service_accounts.md index d09e1a819a4b400b1982bd07c9a6b0fec5b3d5ca..74450aee9c905e759d5a93932e660cc26ce9e266 100644 --- a/doc/user/profile/service_accounts.md +++ b/doc/user/profile/service_accounts.md @@ -40,7 +40,7 @@ Service accounts: - Do not receive notification emails without [adding a custom email address](../../api/service_accounts.md#create-an-instance-service-account). - Are not [billable users](../../subscriptions/manage_users_and_seats.md#billable-users) or [internal users](../../administration/internal_users.md). - Are available for [trial versions](https://gitlab.com/-/trial_registrations/new?glm_source=docs.gitlab.com&glm_content=free-user-limit-faq/ee/user/free_user_limit.html) - of GitLab.com after the Owner of the top-level group verifies their identity. Trial versions are limited to a maximum of 100 service accounts. + of GitLab.com after the Owner of the top-level group verifies their identity. - Can be used with trial versions of GitLab Self-Managed and GitLab Dedicated. You can also manage service accounts through the [service accounts API](../../api/service_accounts.md). @@ -108,8 +108,7 @@ to allow top-level group Owners to create group service accounts. The number of service accounts you can create is limited by your license: - On GitLab Free, you cannot create service accounts. -- On GitLab Premium, you can create one service account for every paid seat. -- On GitLab Ultimate, you can create an unlimited number of service accounts. +- On GitLab Ultimate and Premium, you can create an unlimited number of service accounts. To create a service account: diff --git a/ee/app/controllers/groups/settings/service_accounts_controller.rb b/ee/app/controllers/groups/settings/service_accounts_controller.rb index 8e04b79b68be8cee17cbff4e5668528341b96ad7..4a126966ead39dc729ea9830bcb39c7b41e87246 100644 --- a/ee/app/controllers/groups/settings/service_accounts_controller.rb +++ b/ee/app/controllers/groups/settings/service_accounts_controller.rb @@ -8,6 +8,7 @@ class ServiceAccountsController < Groups::ApplicationController feature_category :user_management before_action :ensure_root_group! + before_action :authorize_admin_service_accounts!, except: [:index, :show] before_action :authorize_read_service_accounts!, only: [:index, :show] @@ -17,6 +18,14 @@ class ServiceAccountsController < Groups::ApplicationController private + def ensure_root_group! + return if ::Feature.enabled?(:allow_subgroups_to_create_service_accounts, group) + + return if group.root? + + render_404 + end + def authorize_read_service_accounts! render_404 unless can?(current_user, :read_service_account, group) end @@ -25,10 +34,6 @@ def authorize_admin_service_accounts! render_404 unless can?(current_user, :create_service_account, group) && can?(current_user, :delete_service_account, group) end - - def ensure_root_group! - render_404 unless group.root? - end end end end diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb index 94719f5e4862a9681ef27ebefbbbbe4a4af90fe1..7629ecc5cfa8a82c51cbb271c18596392d599d23 100644 --- a/ee/app/policies/ee/group_policy.rb +++ b/ee/app/policies/ee/group_policy.rb @@ -214,6 +214,10 @@ module GroupPolicy end end + condition(:service_accounts_enabled_for_subgroups) do + ::Feature.enabled?(:allow_subgroups_to_create_service_accounts, @subject) + end + condition(:service_accounts_available) do if @subject.root_ancestor.trial_active? @subject.feature_available?(:service_accounts) @@ -631,7 +635,7 @@ module GroupPolicy prevent :read_service_account end - rule { has_parent }.policy do + rule { has_parent & ~service_accounts_enabled_for_subgroups }.policy do prevent :read_service_account prevent :create_service_account prevent :delete_service_account diff --git a/ee/app/services/namespaces/service_accounts/create_service.rb b/ee/app/services/namespaces/service_accounts/create_service.rb index b00e85e921219aa7df4c6df24c8ce1f874a4489f..017ed3f69995970e76bc33a5549042369704572f 100644 --- a/ee/app/services/namespaces/service_accounts/create_service.rb +++ b/ee/app/services/namespaces/service_accounts/create_service.rb @@ -72,27 +72,16 @@ def can_create_service_account? can?(current_user, :create_service_account, namespace) end - override :ultimate? - def ultimate? + override :active_subscription? + def active_subscription? return super unless saas? - return false if namespace.trial? # hosted plan can be ultimate even if a group is on trial - plan_name = namespace.actual_plan_name - [::Plan::GOLD, ::Plan::ULTIMATE].include?(plan_name) - end - - override :seats_available? - def seats_available? - return super unless saas? - return true if ultimate? + group_subscription = namespace.root_ancestor.gitlab_subscription - limit = if namespace.trial_active? - GitlabSubscription::SERVICE_ACCOUNT_LIMIT_FOR_TRIAL - else - namespace.gitlab_subscription.seats - end + return false unless group_subscription + return false if group_subscription.expired? - limit > namespace.provisioned_users.service_accounts_without_composite_identity.count + Plan::PAID_HOSTED_PLANS.include?(group_subscription.plan_name) || namespace.trial_active? end def username_unavailable?(username) diff --git a/ee/app/services/users/service_accounts/create_service.rb b/ee/app/services/users/service_accounts/create_service.rb index 1b477318782cff324688dfd171011767eba43ed6..344be2927881b7ba5e837a85caaa5f9525471e2f 100644 --- a/ee/app/services/users/service_accounts/create_service.rb +++ b/ee/app/services/users/service_accounts/create_service.rb @@ -15,7 +15,7 @@ def initialize(current_user, params = {}) def execute return error(error_messages[:no_permission], :forbidden) unless can_create_service_account? - return error(error_messages[:no_seats], :forbidden) unless ultimate? || seats_available? + return error(error_messages[:no_seats], :forbidden) unless active_subscription? create_user end @@ -98,14 +98,8 @@ def error(message, reason) ServiceResponse.error(message: message, reason: reason) end - def ultimate? - License.current.ultimate? - end - - def seats_available? - return true if ultimate? - - User.service_accounts_without_composite_identity.count < License.current.seats.to_i + def active_subscription? + License.current.ultimate? || License.current.premium? || License.current.seats.to_i > 0 end end end diff --git a/ee/config/feature_flags/gitlab_com_derisk/allow_subgroups_to_create_service_accounts.yml b/ee/config/feature_flags/gitlab_com_derisk/allow_subgroups_to_create_service_accounts.yml new file mode 100644 index 0000000000000000000000000000000000000000..87324bd4e729f5e618d044117a23ae1e87136520 --- /dev/null +++ b/ee/config/feature_flags/gitlab_com_derisk/allow_subgroups_to_create_service_accounts.yml @@ -0,0 +1,10 @@ +--- +name: allow_subgroups_to_create_service_accounts +description: Service accounts should be allowed to be created for subgroups +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/work_items/540777 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/214373 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/582313 +milestone: '18.8' +group: group::authentication +type: gitlab_com_derisk +default_enabled: false diff --git a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb index e84a62a4814718662b1621c5132e3fb260491a1e..b64addffcfe665ea95e68d0b8d328e7fb6a36144 100644 --- a/ee/lib/ee/sidebars/groups/menus/settings_menu.rb +++ b/ee/lib/ee/sidebars/groups/menus/settings_menu.rb @@ -74,8 +74,12 @@ def roles_and_permissions_menu_item end def service_accounts_available? - context.group.root? && + if ::Feature.enabled?(:allow_subgroups_to_create_service_accounts, context.group) can?(context.current_user, :read_service_account, context.group) + else + context.group.root? && + can?(context.current_user, :read_service_account, context.group) + end end def custom_roles_enabled? diff --git a/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb b/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb index 8b1f9e622854f54f47ef4aee7dd5970198762232..d77141346ee3c6865e57cdadaab80c89ea02f735 100644 --- a/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb +++ b/ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb @@ -46,7 +46,16 @@ let(:container) { subgroup } - it { is_expected.not_to be_present } + it { is_expected.to be_present } + + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + stub_feature_flags(allow_subgroups_to_create_service_accounts: false) + allow(::Feature).to receive(:enabled?).and_call_original + end + + it { is_expected.not_to be_present } + end end context 'when service accounts feature is not included in the license' do diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb index cd7bc6e36a822230328a8e4814e6f192fe4be4ba..41118bf4d0496eb1a5330308b79b42b81fe1a882 100644 --- a/ee/spec/policies/group_policy_spec.rb +++ b/ee/spec/policies/group_policy_spec.rb @@ -3695,50 +3695,53 @@ def expect_private_group_permissions_as_if_non_member 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) } - it { is_expected.to be_disallowed(:read_service_account) } - end - end - - context 'for subgroup' do - subject { described_class.new(current_user, subgroup) } + it { is_expected.to be_allowed(:create_service_account) } + it { is_expected.to be_allowed(:delete_service_account) } + it { is_expected.to be_allowed(:read_service_account) } - 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) } - it { is_expected.to be_disallowed(:read_service_account) } + context 'when a trial is active in GitLab.com', :saas do + let(:current_user) { owner } - context 'when a trial is active in GitLab.com', :saas do - let(:current_user) { owner } + before do + allow(subgroup.root_ancestor).to receive_messages(trial_active?: true) + end - before do - allow(subgroup.root_ancestor).to receive_messages(trial_active?: true) - end + context 'for owner with verified identity' do + before do + allow(owner).to receive(:identity_verified?).and_return(true) + end - context 'for owner with verified identity' do - before do - allow(owner).to receive(:identity_verified?).and_return(true) + 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) } + it { is_expected.to be_allowed(:read_service_account) } 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) } - it { is_expected.to be_disallowed(:read_service_account) } - end + context 'for owner without verified identity' do + before do + allow(owner).to receive(:identity_verified?).and_return(false) + end - context 'for owner without verified identity' do - before do - allow(owner).to receive(:identity_verified?).and_return(false) + 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) } + it { is_expected.to be_allowed(:read_service_account) } 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) } - it { is_expected.to be_disallowed(:read_service_account) } + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + allow(owner).to receive(:identity_verified?).and_return(true) + stub_feature_flags(allow_subgroups_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) } + it { is_expected.to be_disallowed(:read_service_account) } + end end end end @@ -3771,9 +3774,9 @@ def expect_private_group_permissions_as_if_non_member 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) } - it { is_expected.to be_disallowed(:read_service_account) } + it { is_expected.to be_allowed(:create_service_account) } + it { is_expected.to be_allowed(:delete_service_account) } + it { is_expected.to be_allowed(:read_service_account) } end end diff --git a/ee/spec/requests/api/group_service_accounts_spec.rb b/ee/spec/requests/api/group_service_accounts_spec.rb index c731f4ea31178cf074c8620524acc44ff3ec8c4f..1e56e1083d3c731e37a99a75b350799df48ebfc6 100644 --- a/ee/spec/requests/api/group_service_accounts_spec.rb +++ b/ee/spec/requests/api/group_service_accounts_spec.rb @@ -220,13 +220,30 @@ context 'for subgroup' do let(:group_id) { subgroup.id } - it 'returns error' do + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + stub_feature_flags(allow_subgroups_to_create_service_accounts: false) + end + + it 'returns error' do + perform_request + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to include( + s_('ServiceAccount|User does not have permission to create a service account in this namespace.') + ) + end + end + + it 'creates the user with the correct attributes' do perform_request - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['message']).to include( - s_('ServiceAccount|User does not have permission to create a service account in this namespace.') - ) + user = User.find(json_response['id']) + + expect(user.namespace.organization).to eq(current_organization) + expect(user.user_type).to eq('service_account') + expect(user.provisioned_by_group_id).to eq(group_id) + expect(user).to be_confirmed end end end diff --git a/ee/spec/requests/groups/settings/service_accounts_controller_spec.rb b/ee/spec/requests/groups/settings/service_accounts_controller_spec.rb index bec532620e922e053109e42db93e3a0e04b0dace..774bdbe4c85c521426d92229bfa17077de2d9039 100644 --- a/ee/spec/requests/groups/settings/service_accounts_controller_spec.rb +++ b/ee/spec/requests/groups/settings/service_accounts_controller_spec.rb @@ -50,7 +50,15 @@ context 'when accessing a subgroup' do let_it_be(:group) { create(:group, parent: group) } - it_behaves_like 'page is not found' + it_behaves_like 'page is found' + + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + stub_feature_flags(allow_subgroups_to_create_service_accounts: false) + end + + it_behaves_like 'page is not found' + end end context 'when license is disabled' do diff --git a/ee/spec/services/namespaces/service_accounts/create_service_spec.rb b/ee/spec/services/namespaces/service_accounts/create_service_spec.rb index a3782048b3ab724f961ac7763a86fd6006fdb67c..291c9eaa83856763aa874d0401566b7f304d0d6f 100644 --- a/ee/spec/services/namespaces/service_accounts/create_service_spec.rb +++ b/ee/spec/services/namespaces/service_accounts/create_service_spec.rb @@ -22,7 +22,13 @@ context 'when the group is subgroup' do let(:namespace_id) { subgroup.id } - it_behaves_like 'service account creation failure' + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + stub_feature_flags(allow_subgroups_to_create_service_accounts: false) + end + + it_behaves_like 'service account creation failure' + end end end @@ -153,57 +159,16 @@ context 'when subscription is of premium tier' do let(:license) { create(:license, plan: License::PREMIUM_PLAN) } - let_it_be(:service_account_without_composite) do - create(:user, :service_account, provisioned_by_group_id: group.id, composite_identity_enforced: false) - end - let_it_be(:service_account_without_composite2) do - create(:user, :service_account, provisioned_by_group_id: group.id, composite_identity_enforced: false) - end - - let_it_be(:service_account_with_composite) do - create(:user, :service_account, provisioned_by_group_id: group.id, composite_identity_enforced: true) - end - - context 'when premium seats are not available' do - before do - allow(license).to receive(:seats).and_return(1) - end - - it 'raises error' do - expect(result.status).to eq(:error) - expect(result.message).to include('No more seats are available to create Service Account User') - end + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{group.id}" } end - context 'when premium seats are available' do - before do - allow(license).to receive(:seats).and_return(User.service_accounts_without_composite_identity.count + 2) - end - - it_behaves_like 'service account creation success' do - let(:username_prefix) { "service_account_group_#{group.id}" } - end - - it 'sets provisioned by group' do - expect(result.payload[:user].provisioned_by_group_id).to eq(group.id) - end - - it_behaves_like 'invalid namespace scenarios' + it 'sets provisioned by group' do + expect(result.payload[:user].provisioned_by_group_id).to eq(group.id) end - context 'when service accounts with composite_identity_enforced exist' do - it 'does not count them toward the seat limit' do - # We have 2 service accounts with composite_identity_enforced: false - # and 1 service account with composite_identity_enforced: true (AI Agent) - # If we set seats to 3, we should be able to create 1 more (since only 2 without composite count) - allow(license).to receive(:seats).and_return(3) - - result = service.execute - - expect(result.status).to eq(:success) - end - end + it_behaves_like 'invalid namespace scenarios' end end @@ -245,7 +210,7 @@ end context 'when gitlab_com_subscriptions saas feature is available', :saas do - let_it_be(:group) { create(:group_with_plan, owners: current_user) } + let_it_be(:group) { create(:group_with_plan, plan: :premium_plan, owners: current_user) } before do stub_saas_features(gitlab_com_subscriptions: true) @@ -254,20 +219,30 @@ it_behaves_like 'service account creation success' do let(:username_prefix) { "service_account_group_#{group.id}" } end - end - # setting is only applicable for top level group - context 'when the group is subgroup' do - let(:namespace_id) { subgroup.id } + context 'when the group is subgroup' do + let_it_be(:subgroup) { create(:group, :private, parent: group) } + let_it_be(:namespace_id) { subgroup.id } - it_behaves_like 'service account creation failure' + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{namespace_id}" } + end - context 'when gitlab_com_subscriptions saas feature is available' do - before do - stub_saas_features(gitlab_com_subscriptions: true) + context 'when feature flag allow_subgroups_to_create_service_accounts is false' do + before do + stub_feature_flags(allow_subgroups_to_create_service_accounts: false) + end + + it_behaves_like 'service account creation failure' end + end + end + + context 'when the group is subgroup' do + let(:namespace_id) { subgroup.id } - it_behaves_like 'service account creation failure' + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{namespace_id}" } end end end @@ -289,7 +264,6 @@ let(:namespace_id) { group_with_gold.id } before do - # Reflecting real production data: Gold subscriptions have 0 seats create(:gitlab_subscription, :gold, namespace: group_with_gold, seats: 0) end @@ -322,6 +296,53 @@ it_behaves_like 'invalid namespace scenarios' end + + context 'when subscription is of premium tier' do + let_it_be(:group_with_premium) { create(:group) } + let(:namespace_id) { group_with_premium.id } + + before do + create(:gitlab_subscription, :premium, namespace: group_with_premium) + end + + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{group_with_premium.id}" } + end + + it 'sets provisioned by group' do + expect(result.payload[:user].provisioned_by_group_id).to eq(group_with_premium.id) + end + + it_behaves_like 'invalid namespace scenarios' + end + + context 'when subscription has expired' do + let_it_be(:group_with_expired) { create(:group) } + let(:namespace_id) { group_with_expired.id } + + before do + create(:gitlab_subscription, :ultimate, :expired, namespace: group_with_expired) + end + + it 'produces an error' do + expect(result.status).to eq(:error) + expect(result.message).to include('No more seats are available to create Service Account User') + end + end + + context 'when namespace has free plan' do + let_it_be(:group_with_free) { create(:group) } + let(:namespace_id) { group_with_free.id } + + before do + create(:gitlab_subscription, :free, namespace: group_with_free) + end + + it 'produces an error' do + expect(result.status).to eq(:error) + expect(result.message).to include('No more seats are available to create Service Account User') + end + end end context 'when current user is a group owner' do @@ -338,94 +359,53 @@ create(:gitlab_subscription, :active_trial, namespace: group_with_trial, hosted_plan: create(:ultimate_plan)) end - context 'when trial service account limit is not reached' do - before do - create_list(:user, 5, :service_account, provisioned_by_group_id: group_with_trial.id, - composite_identity_enforced: false) - create_list(:user, 5, :service_account, provisioned_by_group_id: group_with_trial.id, - composite_identity_enforced: true) - end - - it_behaves_like 'service account creation success' do - let(:username_prefix) { "service_account_group_#{group_with_trial.id}" } - end - - it 'sets provisioned by group' do - expect(result.payload[:user].provisioned_by_group_id).to eq(group_with_trial.id) - end - - it 'does not count service accounts with composite_identity_enforced toward limit' do - # We have 5 service accounts without composite and 5 with composite - # Only the 5 without composite should count toward the limit of 100 - expect(group_with_trial.provisioned_users.service_accounts_without_composite_identity.count).to eq(5) - end + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{group_with_trial.id}" } end - context 'when trial service account limit is reached' do - before do - stub_const('GitlabSubscription::SERVICE_ACCOUNT_LIMIT_FOR_TRIAL', 3) - - create_list(:user, GitlabSubscription::SERVICE_ACCOUNT_LIMIT_FOR_TRIAL, :service_account, - provisioned_by_group_id: group_with_trial.id, composite_identity_enforced: false) - create_list(:user, 5, :service_account, - provisioned_by_group_id: group_with_trial.id, composite_identity_enforced: true) - end + it 'sets provisioned by group' do + expect(result.payload[:user].provisioned_by_group_id).to eq(group_with_trial.id) + end + end - it 'produces an error' do - expect(result.status).to eq(:error) - expect(result.message).to include('No more seats are available to create Service Account User') - end + context 'when namespace does not have a gitlab_subscription' do + let_it_be(:group_without_subscription) { create(:group, owners: current_user) } + let(:namespace_id) { group_without_subscription.id } - it 'does not count service accounts with composite_identity_enforced toward limit' do - # We have 3 service accounts without composite (at limit) and 5 with composite - # Only the 3 without composite should count toward the limit - expect(group_with_trial.provisioned_users.service_accounts_without_composite_identity.count).to eq(3) - end + it 'produces an error' do + expect(result.status).to eq(:error) + expect(result.message).to include('No more seats are available to create Service Account User') end + end - context 'when subscription trial has expired' do - let_it_be(:group_with_expired_trial) { create(:group, owners: current_user) } - let(:namespace_id) { group_with_expired_trial.id } + context 'when subscription has expired' do + let_it_be(:group_with_expired_subscription) { create(:group, owners: current_user) } + let(:namespace_id) { group_with_expired_subscription.id } - before do - create(:gitlab_subscription, - namespace: group_with_expired_trial, - hosted_plan: create(:premium_plan), - trial: true, - trial_starts_on: 2.weeks.ago, - trial_ends_on: 1.day.ago, - seats: 5) - - create_list(:user, 2, :service_account, provisioned_by_group_id: group_with_expired_trial.id, - composite_identity_enforced: false) - create_list(:user, 1, :service_account, provisioned_by_group_id: group_with_expired_trial.id, - composite_identity_enforced: true) - end + before do + create(:gitlab_subscription, + namespace: group_with_expired_subscription, + hosted_plan: create(:premium_plan), + trial: false, + end_date: 1.day.ago) + end - context 'when regular subscription seats are available' do - it_behaves_like 'service account creation success' do - let(:username_prefix) { "service_account_group_#{group_with_expired_trial.id}" } - end + it 'produces an error' do + expect(result.status).to eq(:error) + expect(result.message).to include('No more seats are available to create Service Account User') + end + end - it 'does not count service accounts with composite_identity_enforced toward limit' do - # We have 2 service accounts without composite and 1 with composite - # Only the 2 without composite should count toward the limit - expect( - group_with_expired_trial.provisioned_users.service_accounts_without_composite_identity.count - ).to eq(2) - end - end + context 'when namespace has an active paid subscription' do + let_it_be(:group_with_paid) { create(:group, owners: current_user) } + let(:namespace_id) { group_with_paid.id } - context 'when regular subscription seats are not available' do - before do - group_with_expired_trial.gitlab_subscription.update!(seats: 2) - end + before do + create(:gitlab_subscription, :premium, namespace: group_with_paid) + end - it 'produces an error' do - expect(result.status).to eq(:error) - expect(result.message).to include('No more seats are available to create Service Account User') - end - end + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account_group_#{group_with_paid.id}" } end end end diff --git a/ee/spec/services/users/service_accounts/create_service_spec.rb b/ee/spec/services/users/service_accounts/create_service_spec.rb index fd4a7d6006e80c2cac9b5dda5f64c0f079ff37f9..7a4f0ecaf9634c5891bb23a0ec5e487cac00f661 100644 --- a/ee/spec/services/users/service_accounts/create_service_spec.rb +++ b/ee/spec/services/users/service_accounts/create_service_spec.rb @@ -61,58 +61,20 @@ context 'when subscription is of premium tier' do let(:license) { create(:license, plan: License::PREMIUM_PLAN) } - let!(:service_account_without_composite) { create(:user, :service_account, composite_identity_enforced: false) } - let!(:service_account_without_composite2) do - create(:user, :service_account, composite_identity_enforced: false) - end - - let!(:service_account_with_composite) { create(:user, :service_account, composite_identity_enforced: true) } - - context 'when premium seats are not available' do - before do - allow(license).to receive(:seats).and_return(1) - end - it 'raises error' do - result = service.execute - - expect(result.status).to eq(:error) - expect(result.message).to include('No more seats are available to create Service Account User') - end + it_behaves_like 'service account creation success' do + let(:username_prefix) { "service_account" } end - context 'when premium seats are available' do - before do - allow(license).to receive(:seats).and_return(User.service_accounts_without_composite_identity.count + 2) - end - - it_behaves_like 'service account creation success' do - let(:username_prefix) { "service_account" } - end - - it_behaves_like 'service account creation with customized params' - - it 'correctly returns active model errors' do - service.execute - - result = service.execute - - expect(result.status).to eq(:error) - expect(result.message).to eq('Email has already been taken and Username has already been taken') - end - end + it_behaves_like 'service account creation with customized params' - context 'when service accounts with composite_identity_enforced exist' do - it 'does not count them toward the seat limit' do - # We have 2 service accounts with composite_identity_enforced: false - # and 1 service account with composite_identity_enforced: true (AI Agent) - # If we set seats to 3, we should be able to create 1 more (since only 2 without composite count) - allow(license).to receive(:seats).and_return(3) + it 'correctly returns active model errors' do + service.execute - result = service.execute + result = service.execute - expect(result.status).to eq(:success) - end + expect(result.status).to eq(:error) + expect(result.message).to eq('Email has already been taken and Username has already been taken') end end end