Commit ca85f477 authored by Etienne Baqué's avatar Etienne Baqué

Merge branch 'vij-user-cap-restrictions' into 'master'

Add more verification for namespace UserCap availability

See merge request gitlab-org/gitlab!76332
parents 554b740b c4fa6214
...@@ -19,7 +19,7 @@ class Groups::UsageQuotasController < Groups::ApplicationController ...@@ -19,7 +19,7 @@ class Groups::UsageQuotasController < Groups::ApplicationController
end end
def pending_members def pending_members
render_404 unless ::Feature.enabled?(:saas_user_caps, @group.root_ancestor, default_enabled: :yaml) render_404 unless @group.user_cap_available?
@hide_search_settings = true @hide_search_settings = true
end end
......
...@@ -4,10 +4,8 @@ module EE ...@@ -4,10 +4,8 @@ module EE
module NamespaceUserCapReachedAlertHelper module NamespaceUserCapReachedAlertHelper
def display_namespace_user_cap_reached_alert?(namespace) def display_namespace_user_cap_reached_alert?(namespace)
root_namespace = namespace.root_ancestor root_namespace = namespace.root_ancestor
return false unless ::Feature.enabled?(:saas_user_caps, root_namespace, default_enabled: :yaml)
return false if root_namespace.user_namespace?
return false unless root_namespace.user_cap_available?
return false if alert_has_been_dismissed?(root_namespace) return false if alert_has_been_dismissed?(root_namespace)
can?(current_user, :admin_namespace, root_namespace) && root_namespace.user_cap_reached?(use_cache: true) can?(current_user, :admin_namespace, root_namespace) && root_namespace.user_cap_reached?(use_cache: true)
......
...@@ -512,7 +512,7 @@ module EE ...@@ -512,7 +512,7 @@ module EE
end end
def user_cap_reached?(use_cache: false) def user_cap_reached?(use_cache: false)
return false unless ::Feature.enabled?(:saas_user_caps, root_ancestor, default_enabled: :yaml) return false unless user_cap_available?
user_cap = root_ancestor.namespace_settings&.new_user_signups_cap user_cap = root_ancestor.namespace_settings&.new_user_signups_cap
return false unless user_cap return false unless user_cap
......
...@@ -447,6 +447,13 @@ module EE ...@@ -447,6 +447,13 @@ module EE
klass.new(self) klass.new(self)
end end
def user_cap_available?
return false unless group_namespace?
return false unless ::Gitlab.com?
::Feature.enabled?(:saas_user_caps, root_ancestor, default_enabled: :yaml)
end
private private
def any_project_with_shared_runners_enabled_with_cte? def any_project_with_shared_runners_enabled_with_cte?
......
- return unless Feature.enabled?(:saas_user_caps, group.root_ancestor) - return unless group.user_cap_available?
.form-group .form-group
= f.label :new_user_signups_cap, _('User cap'), class: 'gl-font-weight-bold' = f.label :new_user_signups_cap, _('User cap'), class: 'gl-font-weight-bold'
......
- page_title s_("UsageQuota|Usage") - page_title s_("UsageQuota|Usage")
- url_to_purchase_storage = buy_storage_path(@group) if purchase_storage_link_enabled?(@group) - url_to_purchase_storage = buy_storage_path(@group) if purchase_storage_link_enabled?(@group)
- pending_members_page_path = pending_members_group_usage_quotas_path(@group) if Feature.enabled?(:saas_user_caps, @group.root_ancestor) - pending_members_page_path = pending_members_group_usage_quotas_path(@group) if @group.user_cap_available?
- pending_members_count = Member.in_hierarchy(@group).with_state("awaiting").count - pending_members_count = Member.in_hierarchy(@group).with_state("awaiting").count
- if show_product_purchase_success_alert? - if show_product_purchase_success_alert?
......
...@@ -37,4 +37,30 @@ RSpec.describe Groups::UsageQuotasController do ...@@ -37,4 +37,30 @@ RSpec.describe Groups::UsageQuotasController do
end end
end end
end end
describe 'GET #pending_members' do
let(:feature_available) { true }
before do
allow_next_found_instance_of(Group) do |group|
allow(group).to receive(:user_cap_available?).and_return(feature_available)
end
end
it 'renders the pending members index' do
get :pending_members, params: { group_id: group }
expect(response).to render_template 'groups/usage_quotas/pending_members'
end
context 'when user cap feature is unavailable' do
let(:feature_available) { false }
it 'returns 404' do
get :pending_members, params: { group_id: group }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end end
...@@ -409,9 +409,18 @@ RSpec.describe 'Edit group settings' do ...@@ -409,9 +409,18 @@ RSpec.describe 'Edit group settings' do
end end
describe 'user caps settings' do describe 'user caps settings' do
context 'when :saas_user_caps feature flag is off' do let(:user_cap_available) { true }
before do
allow_next_found_instance_of(Group) do |instance|
allow(instance).to receive(:user_cap_available?).and_return user_cap_available
end
end
context 'when user cap feature is unavailable' do
let(:user_cap_available) { false }
before do before do
stub_feature_flags(saas_user_caps: false)
visit edit_group_path(group) visit edit_group_path(group)
end end
...@@ -420,11 +429,10 @@ RSpec.describe 'Edit group settings' do ...@@ -420,11 +429,10 @@ RSpec.describe 'Edit group settings' do
end end
end end
context 'when :saas_user_caps feature flag is on', :js do context 'when user cap feature is available', :js do
let(:user_caps_selector) { '[name="group[new_user_signups_cap]"]' } let(:user_caps_selector) { '[name="group[new_user_signups_cap]"]' }
before do before do
stub_feature_flags(saas_user_caps: true)
visit edit_group_path(group) visit edit_group_path(group)
end end
...@@ -479,7 +487,6 @@ RSpec.describe 'Edit group settings' do ...@@ -479,7 +487,6 @@ RSpec.describe 'Edit group settings' do
end end
before do before do
stub_feature_flags(saas_user_caps: true)
group.namespace_settings.update!(new_user_signups_cap: group.group_members.count) group.namespace_settings.update!(new_user_signups_cap: group.group_members.count)
end end
......
...@@ -24,6 +24,8 @@ RSpec.describe 'Namespace user cap reached alert', :feature, :js, :use_clean_rai ...@@ -24,6 +24,8 @@ RSpec.describe 'Namespace user cap reached alert', :feature, :js, :use_clean_rai
context 'with an exceeded user cap' do context 'with an exceeded user cap' do
before do before do
allow(Gitlab).to receive(:com?).and_return(true)
stub_cache(group) stub_cache(group)
end end
......
...@@ -23,19 +23,23 @@ RSpec.describe EE::NamespaceUserCapReachedAlertHelper, :use_clean_rails_memory_s ...@@ -23,19 +23,23 @@ RSpec.describe EE::NamespaceUserCapReachedAlertHelper, :use_clean_rails_memory_s
before do before do
allow(helper).to receive(:can?).with(owner, :admin_namespace, group).and_return(true) allow(helper).to receive(:can?).with(owner, :admin_namespace, group).and_return(true)
allow(helper).to receive(:can?).with(developer, :admin_namespace, group).and_return(false) allow(helper).to receive(:can?).with(developer, :admin_namespace, group).and_return(false)
allow(group).to receive(:user_cap_available?).and_return(true)
stub_cache(group) stub_cache(group)
end end
subject(:display_alert?) { helper.display_namespace_user_cap_reached_alert?(group) }
it 'returns true when the user cap is reached for a user who can admin the namespace' do it 'returns true when the user cap is reached for a user who can admin the namespace' do
sign_in(owner) sign_in(owner)
expect(helper.display_namespace_user_cap_reached_alert?(group)).to be true expect(display_alert?).to be true
end end
it 'returns false when the user cap is reached for a user who cannot admin the namespace' do it 'returns false when the user cap is reached for a user who cannot admin the namespace' do
sign_in(developer) sign_in(developer)
expect(helper.display_namespace_user_cap_reached_alert?(group)).to be false expect(display_alert?).to be false
end end
it 'does not trigger reactive caching if there is no user cap set' do it 'does not trigger reactive caching if there is no user cap set' do
...@@ -44,7 +48,15 @@ RSpec.describe EE::NamespaceUserCapReachedAlertHelper, :use_clean_rails_memory_s ...@@ -44,7 +48,15 @@ RSpec.describe EE::NamespaceUserCapReachedAlertHelper, :use_clean_rails_memory_s
sign_in(owner) sign_in(owner)
expect(group).not_to receive(:with_reactive_cache) expect(group).not_to receive(:with_reactive_cache)
expect(helper.display_namespace_user_cap_reached_alert?(group)).to be false expect(display_alert?).to be false
end
it 'returns false when the user cap feature is unavailable' do
allow(group).to receive(:user_cap_available?).and_return(false)
sign_in(owner)
expect(display_alert?).to be false
end end
def sign_in(user) def sign_in(user)
......
...@@ -1495,63 +1495,77 @@ RSpec.describe Group do ...@@ -1495,63 +1495,77 @@ RSpec.describe Group do
describe '#user_cap_reached?' do describe '#user_cap_reached?' do
subject(:user_cap_reached_for_group?) { group.user_cap_reached? } subject(:user_cap_reached_for_group?) { group.user_cap_reached? }
context 'when the :saas_user_caps feature flag is not enabled' do context 'when user cap feature is not available' do
before do
allow(group).to receive(:user_cap_available?).and_return(false)
end
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context 'when the :saas_user_caps feature flag is enabled' do context 'when user cap feature is available' do
before do before do
stub_feature_flags(saas_user_caps: true) allow(group).to receive(:user_cap_available?).and_return(true)
end end
let(:new_user_signups_cap) { nil } context 'when the :saas_user_caps feature flag is not enabled' do
it { is_expected.to be_falsey }
end
shared_context 'returning the right value for user_cap_reached?' do context 'when the :saas_user_caps feature flag is enabled' do
before do before do
root_group.namespace_settings.update!(new_user_signups_cap: new_user_signups_cap) stub_feature_flags(saas_user_caps: true)
end end
context 'when no user cap has been set to that root ancestor' do let(:new_user_signups_cap) { nil }
it { is_expected.to be_falsey }
end
context 'when a user cap has been set to that root ancestor' do
let(:new_user_signups_cap) { 100 }
shared_examples 'returning the right value for user_cap_reached?' do
before do before do
allow(root_group).to receive(:billable_members_count).and_return(billable_members_count) root_group.namespace_settings.update!(new_user_signups_cap: new_user_signups_cap)
allow(group).to receive(:root_ancestor).and_return(root_group)
end end
context 'when this cap is higher than the number of billable members' do context 'when no user cap has been set to that root ancestor' do
let(:billable_members_count) { new_user_signups_cap - 10 }
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context 'when this cap is the same as the number of billable members' do context 'when a user cap has been set to that root ancestor' do
let(:billable_members_count) { new_user_signups_cap } let(:new_user_signups_cap) { 100 }
it { is_expected.to be_truthy } before do
end allow(root_group).to receive(:billable_members_count).and_return(billable_members_count)
allow(group).to receive(:root_ancestor).and_return(root_group)
end
context 'when this cap is lower than the number of billable members' do context 'when this cap is higher than the number of billable members' do
let(:billable_members_count) { new_user_signups_cap + 10 } let(:billable_members_count) { new_user_signups_cap - 10 }
it { is_expected.to be_truthy } it { is_expected.to be_falsey }
end
context 'when this cap is the same as the number of billable members' do
let(:billable_members_count) { new_user_signups_cap }
it { is_expected.to be_truthy }
end
context 'when this cap is lower than the number of billable members' do
let(:billable_members_count) { new_user_signups_cap + 10 }
it { is_expected.to be_truthy }
end
end end
end end
end
context 'when this group has no root ancestor' do context 'when this group has no root ancestor' do
it_behaves_like 'returning the right value for user_cap_reached?' do it_behaves_like 'returning the right value for user_cap_reached?' do
let(:root_group) { group } let(:root_group) { group }
end
end end
end
context 'when this group has a root ancestor' do context 'when this group has a root ancestor' do
it_behaves_like 'returning the right value for user_cap_reached?' do it_behaves_like 'returning the right value for user_cap_reached?' do
let(:root_group) { create(:group, children: [group]) } let(:root_group) { create(:group, children: [group]) }
end
end end
end end
end end
......
...@@ -1960,6 +1960,48 @@ RSpec.describe Namespace do ...@@ -1960,6 +1960,48 @@ RSpec.describe Namespace do
end end
end end
describe '#user_cap_available?' do
let_it_be(:namespace) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: namespace) }
let(:gitlab_com?) { true }
subject(:user_cap_available?) { namespace.user_cap_available? }
before do
allow(::Gitlab).to receive(:com?).and_return(gitlab_com?)
end
context 'when not on Gitlab.com' do
let(:gitlab_com?) { false }
it { is_expected.to be false }
end
context 'when :saas_user_caps is disabled' do
before do
stub_feature_flags(saas_user_caps: false)
end
it { is_expected.to be false }
end
context 'when :saas_user_caps is enabled' do
before do
stub_feature_flags(saas_user_caps: true)
end
it { is_expected.to be true }
context 'when the namespace is not a group' do
let(:user) { create(:user) }
let(:namespace) { user.namespace }
it { is_expected.to be false }
end
end
end
def create_project(repository_size:, lfs_objects_size:, repository_size_limit:) def create_project(repository_size:, lfs_objects_size:, repository_size_limit:)
create(:project, namespace: namespace, repository_size_limit: repository_size_limit).tap do |project| create(:project, namespace: namespace, repository_size_limit: repository_size_limit).tap do |project|
create(:project_statistics, project: project, repository_size: repository_size, lfs_objects_size: lfs_objects_size) create(:project_statistics, project: project, repository_size: repository_size, lfs_objects_size: lfs_objects_size)
......
...@@ -20,6 +20,7 @@ RSpec.describe 'shared/namespace_user_cap_reached_alert', :use_clean_rails_memor ...@@ -20,6 +20,7 @@ RSpec.describe 'shared/namespace_user_cap_reached_alert', :use_clean_rails_memor
before do before do
allow(view).to receive(:current_user).and_return(owner) allow(view).to receive(:current_user).and_return(owner)
allow(Gitlab).to receive(:com?).and_return(true)
stub_cache(group) stub_cache(group)
stub_cache(other_group) stub_cache(other_group)
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment