Commit 3f856b60 authored by Fabio Pitino's avatar Fabio Pitino

Prevent creating pipeline on free and trial plans without CC info

Return an error if user is not validated via add credit card info and
tries to create a pipeline on Free or trial plan.
parent 6cadce10
...@@ -10,6 +10,7 @@ module Enums ...@@ -10,6 +10,7 @@ module Enums
unknown_failure: 0, unknown_failure: 0,
config_error: 1, config_error: 1,
external_validation_failure: 2, external_validation_failure: 2,
user_not_verified: 3,
activity_limit_exceeded: 20, activity_limit_exceeded: 20,
size_limit_exceeded: 21, size_limit_exceeded: 21,
job_activity_limit_exceeded: 22, job_activity_limit_exceeded: 22,
......
...@@ -11,6 +11,7 @@ module Ci ...@@ -11,6 +11,7 @@ module Ci
{ unknown_failure: 'The reason for the pipeline failure is unknown.', { unknown_failure: 'The reason for the pipeline failure is unknown.',
config_error: 'The pipeline failed due to an error on the CI/CD configuration file.', config_error: 'The pipeline failed due to an error on the CI/CD configuration file.',
external_validation_failure: 'The external pipeline validation failed.', external_validation_failure: 'The external pipeline validation failed.',
user_not_verified: 'The pipeline failed due to the user not being verified',
activity_limit_exceeded: 'The pipeline activity limit was exceeded.', activity_limit_exceeded: 'The pipeline activity limit was exceeded.',
size_limit_exceeded: 'The pipeline size limit was exceeded.', size_limit_exceeded: 'The pipeline size limit was exceeded.',
job_activity_limit_exceeded: 'The pipeline job activity limit was exceeded.', job_activity_limit_exceeded: 'The pipeline job activity limit was exceeded.',
......
---
name: ci_require_credit_card_on_free_plan
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61152
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330104
milestone: '13.12'
type: development
group: group::continuous integration
default_enabled: false
---
name: ci_require_credit_card_on_trial_plan
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61152
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330105
milestone: '13.12'
type: development
group: group::continuous integration
default_enabled: false
...@@ -396,6 +396,27 @@ module EE ...@@ -396,6 +396,27 @@ module EE
!password_automatically_set? !password_automatically_set?
end end
def has_valid_credit_card?
credit_card_validated_at.present?
end
def requires_credit_card_to_run_pipelines?(project)
return false unless ::Gitlab.com?
root_namespace = project.root_namespace
if root_namespace.free_plan?
::Feature.enabled?(:ci_require_credit_card_on_free_plan, project, default_enabled: :yaml)
elsif root_namespace.trial?
::Feature.enabled?(:ci_require_credit_card_on_trial_plan, project, default_enabled: :yaml)
else
false
end
end
def has_required_credit_card_to_run_pipelines?(project)
has_valid_credit_card? || !requires_credit_card_to_run_pipelines?(project)
end
protected protected
override :password_required? override :password_required?
......
...@@ -18,6 +18,10 @@ module EE ...@@ -18,6 +18,10 @@ module EE
return error('Pipeline is disabled for mirror updates') return error('Pipeline is disabled for mirror updates')
end end
if current_user && !current_user.has_required_credit_card_to_run_pipelines?(project)
return error('Credit card required to be on file in order to create a pipeline', drop_reason: :user_not_verified)
end
super super
end end
end end
......
...@@ -19,10 +19,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do ...@@ -19,10 +19,12 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
let(:ref) { 'master' } let(:ref) { 'master' }
describe '#perform!' do describe '#perform!' do
context 'when triggering builds for project mirrors is disabled' do before do
it 'returns an error' do
project.add_developer(user) project.add_developer(user)
end
context 'when triggering builds for project mirrors is disabled' do
it 'returns an error' do
allow(command) allow(command)
.to receive(:allow_mirror_update) .to receive(:allow_mirror_update)
.and_return(true) .and_return(true)
...@@ -37,5 +39,40 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do ...@@ -37,5 +39,40 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
.to include('Pipeline is disabled for mirror updates') .to include('Pipeline is disabled for mirror updates')
end end
end end
describe 'credit card requirement' do
context 'when user does not have credit card for pipelines in project' do
before do
allow(user)
.to receive(:has_required_credit_card_to_run_pipelines?)
.with(project)
.and_return(false)
end
it 'breaks the chain with an error' do
step.perform!
expect(step.break?).to be_truthy
expect(pipeline.errors.to_a)
.to include('Credit card required to be on file in order to create a pipeline')
end
end
context 'when user has credit card for pipelines in project' do
before do
allow(user)
.to receive(:has_required_credit_card_to_run_pipelines?)
.with(project)
.and_return(true)
end
it 'succeeds the step' do
step.perform!
expect(step.break?).to be_falsey
expect(pipeline.errors).to be_empty
end
end
end
end end
end end
...@@ -1674,4 +1674,41 @@ RSpec.describe User do ...@@ -1674,4 +1674,41 @@ RSpec.describe User do
end end
end end
end end
describe '#has_required_credit_card_to_run_pipelines?' do
let_it_be(:project) { create(:project) }
subject { user.has_required_credit_card_to_run_pipelines?(project) }
using RSpec::Parameterized::TableSyntax
where(:is_saas, :cc_present, :is_free, :is_trial, :free_ff_enabled, :trial_ff_enabled, :result) do
# self-hosted
false | false | false | false | true | true | true # paid plan
false | false | false | true | true | true | true # missing CC on trial plan
# saas
true | false | false | false | true | true | true # missing CC on paid plan
true | false | true | false | true | true | false # missing CC on free plan
true | false | true | false | false | true | true # missing CC on free plan - FF off
true | false | false | true | true | true | false # missing CC on trial plan
true | false | false | true | true | false | true # missing CC on trial plan - FF off
true | true | true | false | true | true | true # present CC on free plan
true | true | false | true | true | true | true # present CC on trial plan
end
with_them do
before do
allow(::Gitlab).to receive(:com?).and_return(is_saas)
allow(user).to receive(:credit_card_validated_at).and_return(Time.current) if cc_present
allow(project.namespace).to receive(:free_plan?).and_return(is_free)
allow(project.namespace).to receive(:trial?).and_return(is_trial)
stub_feature_flags(
ci_require_credit_card_on_free_plan: free_ff_enabled,
ci_require_credit_card_on_trial_plan: trial_ff_enabled)
end
it { is_expected.to eq(result) }
end
end
end end
...@@ -173,6 +173,54 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do ...@@ -173,6 +173,54 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
end end
end end
describe 'credit card requirement' do
shared_examples 'creates a successful pipeline' do
it 'creates a successful pipeline' do
pipeline = create_pipeline!
expect(pipeline).to be_created_successfully
end
end
context 'when credit card is required' do
context 'when project is on free plan' do
before do
allow(::Gitlab).to receive(:com?).and_return(true)
namespace.gitlab_subscription.update!(hosted_plan: create(:free_plan))
end
context 'when user has credit card' do
before do
allow(user).to receive(:credit_card_validated_at).and_return(Time.current)
end
it_behaves_like 'creates a successful pipeline'
end
context 'when user does not have credit card' do
it 'creates a pipeline with errors', :aggregate_failures do
pipeline = create_pipeline!
expect(pipeline).not_to be_created_successfully
expect(pipeline.failure_reason).to eq('user_not_verified')
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(ci_require_credit_card_on_free_plan: false)
end
it_behaves_like 'creates a successful pipeline'
end
end
end
end
context 'when credit card is not required' do
it_behaves_like 'creates a successful pipeline'
end
end
def create_pipeline! def create_pipeline!
service.execute(:push) service.execute(:push)
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