summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabio Pitino <fpitino@gitlab.com>2019-06-25 12:41:11 +0100
committerFabio Pitino <fpitino@gitlab.com>2019-07-15 15:05:22 +0200
commit42d99460d3bbae88fdd9d8415970ecbfffc85a01 (patch)
tree0539b3150651f2a18567a58cf9b7a7aa87abf46d
parent0e8af2525f16d871510c24e6e15f1bc80f133edd (diff)
downloadgitlab-ce-42d99460d3bbae88fdd9d8415970ecbfffc85a01.tar.gz
Allow use of legacy triggers with feature flag
Keep feature flag disabled by default and turn off all functionality related to legacy triggers. * Block legacy triggers from creating pipeline * Highlight legacy triggers to be invalid via the UI * Make legacy triggers invalid in the model
-rw-r--r--app/models/ci/trigger.rb8
-rw-r--r--app/policies/ci/trigger_policy.rb2
-rw-r--r--app/views/projects/triggers/_content.html.haml17
-rw-r--r--app/views/projects/triggers/_trigger.html.haml7
-rw-r--r--changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml5
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb6
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/abilities.rb4
-rw-r--r--spec/features/triggers_spec.rb78
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb44
-rw-r--r--spec/models/ci/trigger_spec.rb20
-rw-r--r--spec/policies/ci/trigger_policy_spec.rb94
11 files changed, 216 insertions, 69 deletions
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 8927bb9bc18..dc9965ce08c 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -14,6 +14,7 @@ module Ci
has_many :trigger_requests
validates :token, presence: true, uniqueness: true
+ validates :owner, presence: true, unless: :supports_legacy_tokens?
before_validation :set_default_values
@@ -37,8 +38,13 @@ module Ci
self.owner_id.blank?
end
+ def supports_legacy_tokens?
+ Feature.enabled?(:use_legacy_pipeline_triggers, project)
+ end
+
def can_access_project?
- self.owner_id.blank? || Ability.allowed?(self.owner, :create_build, project)
+ supports_legacy_tokens? && legacy? ||
+ Ability.allowed?(self.owner, :create_build, project)
end
end
end
diff --git a/app/policies/ci/trigger_policy.rb b/app/policies/ci/trigger_policy.rb
index 209db44539c..578301d7f7e 100644
--- a/app/policies/ci/trigger_policy.rb
+++ b/app/policies/ci/trigger_policy.rb
@@ -5,7 +5,7 @@ module Ci
delegate { @subject.project }
with_options scope: :subject, score: 0
- condition(:legacy) { @subject.legacy? }
+ condition(:legacy) { @subject.supports_legacy_tokens? && @subject.legacy? }
with_score 0
condition(:is_owner) { @user && @subject.owner_id == @user.id }
diff --git a/app/views/projects/triggers/_content.html.haml b/app/views/projects/triggers/_content.html.haml
index 96a41aa066c..e686068657c 100644
--- a/app/views/projects/triggers/_content.html.haml
+++ b/app/views/projects/triggers/_content.html.haml
@@ -1,8 +1,9 @@
-%p.append-bottom-default
- Triggers with the
- %span.badge.badge-primary legacy
- label do not have an associated user and only have access to the current project.
- %br
- = succeed '.' do
- Learn more in the
- = link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
+- if Feature.enabled?(:use_legacy_pipeline_triggers, @project)
+ %p.append-bottom-default
+ Triggers with the
+ %span.badge.badge-primary legacy
+ label do not have an associated user and only have access to the current project.
+ %br
+ = succeed '.' do
+ Learn more in the
+ = link_to 'triggers documentation', help_page_path('ci/triggers/README'), target: '_blank'
diff --git a/app/views/projects/triggers/_trigger.html.haml b/app/views/projects/triggers/_trigger.html.haml
index 6f6f1e5e0c5..31a598ccd5e 100644
--- a/app/views/projects/triggers/_trigger.html.haml
+++ b/app/views/projects/triggers/_trigger.html.haml
@@ -8,8 +8,11 @@
.label-container
- if trigger.legacy?
- %span.badge.badge-primary.has-tooltip{ title: "Trigger makes use of deprecated functionality" } legacy
- - if !trigger.can_access_project?
+ - if trigger.supports_legacy_tokens?
+ %span.badge.badge-primary.has-tooltip{ title: "Trigger makes use of deprecated functionality" } legacy
+ - else
+ %span.badge.badge-danger.has-tooltip{ title: "Trigger is invalid due to being a legacy trigger. We recommend replacing it with a new trigger" } invalid
+ - elsif !trigger.can_access_project?
%span.badge.badge-danger.has-tooltip{ title: "Trigger user has insufficient permissions to project" } invalid
%td
diff --git a/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml b/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml
new file mode 100644
index 00000000000..3f4d4bbd432
--- /dev/null
+++ b/changelogs/unreleased/remove-support-for-legacy-pipeline-triggers.yml
@@ -0,0 +1,5 @@
+---
+title: Remove support for legacy pipeline triggers
+merge_request: 30133
+author:
+type: removed
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index c911bfa7ff6..afad391e8e0 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -20,6 +20,12 @@ module Gitlab
end
end
+ def uses_unsupported_legacy_trigger?
+ trigger_request.present? &&
+ trigger_request.trigger.legacy? &&
+ !trigger_request.trigger.supports_legacy_tokens?
+ end
+
def branch_exists?
strong_memoize(:is_branch) do
project.repository.branch_exists?(ref)
diff --git a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
index aaa3daddcc5..357a1d55b3b 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
@@ -14,6 +14,10 @@ module Gitlab
return error('Pipelines are disabled!')
end
+ if @command.uses_unsupported_legacy_trigger?
+ return error('Trigger token is invalid because is not owned by any user')
+ end
+
unless allowed_to_trigger_pipeline?
if can?(current_user, :create_pipeline, project)
return error("Insufficient permissions for protected ref '#{command.ref}'")
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 919859c145a..7c44680e9f7 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -66,7 +66,7 @@ describe 'Triggers', :js do
it 'edit "legacy" trigger and save' do
# Create new trigger without owner association, i.e. Legacy trigger
- create(:ci_trigger, owner: nil, project: @project)
+ create(:ci_trigger, owner: user, project: @project).update_attribute(:owner, nil)
visit project_settings_ci_cd_path(@project)
# See if the trigger can be edited and description is blank
@@ -127,17 +127,19 @@ describe 'Triggers', :js do
end
describe 'show triggers workflow' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: false)
+ end
+
it 'contains trigger description placeholder' do
expect(page.find('#trigger_description')['placeholder']).to eq 'Trigger description'
end
- it 'show "legacy" badge for legacy trigger' do
- create(:ci_trigger, owner: nil, project: @project)
+ it 'show "invalid" badge for legacy trigger' do
+ create(:ci_trigger, owner: user, project: @project).update_attribute(:owner, nil)
visit project_settings_ci_cd_path(@project)
- # See if trigger without owner (i.e. legacy) shows "legacy" badge and is editable
- expect(page.find('.triggers-list')).to have_content 'legacy'
- expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
+ expect(page.find('.triggers-list')).to have_content 'invalid'
end
it 'show "invalid" badge for trigger with owner having insufficient permissions' do
@@ -149,6 +151,19 @@ describe 'Triggers', :js do
expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
end
+ it 'do not show "Edit" or full token for legacy trigger' do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ .update_attribute(:owner, nil)
+ visit project_settings_ci_cd_path(@project)
+
+ # See if trigger not owned shows only first few token chars and doesn't have copy-to-clipboard button
+ expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3])
+ expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard')
+
+ # See if trigger is non-editable
+ expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ end
+
it 'do not show "Edit" or full token for not owned trigger' do
# Create trigger with user different from current_user
create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
@@ -175,5 +190,56 @@ describe 'Triggers', :js do
expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
end
+
+ context 'when :use_legacy_pipeline_triggers feature flag is enabled' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: true)
+ end
+
+ it 'show "legacy" badge for legacy trigger' do
+ create(:ci_trigger, owner: nil, project: @project)
+ visit project_settings_ci_cd_path(@project)
+
+ # See if trigger without owner (i.e. legacy) shows "legacy" badge and is editable
+ expect(page.find('.triggers-list')).to have_content 'legacy'
+ expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
+ end
+
+ it 'show "invalid" badge for trigger with owner having insufficient permissions' do
+ create(:ci_trigger, owner: guest_user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ # See if trigger without owner (i.e. legacy) shows "legacy" badge and is non-editable
+ expect(page.find('.triggers-list')).to have_content 'invalid'
+ expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ end
+
+ it 'do not show "Edit" or full token for not owned trigger' do
+ # Create trigger with user different from current_user
+ create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ # See if trigger not owned by current_user shows only first few token chars and doesn't have copy-to-clipboard button
+ expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3])
+ expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard')
+
+ # See if trigger owner name doesn't match with current_user and trigger is non-editable
+ expect(page.find('.triggers-list .trigger-owner')).not_to have_content user.name
+ expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
+ end
+
+ it 'show "Edit" and full token for owned trigger' do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ # See if trigger shows full token and has copy-to-clipboard button
+ expect(page.find('.triggers-list')).to have_content @project.triggers.first.token
+ expect(page.find('.triggers-list')).to have_selector('button.btn-clipboard')
+
+ # See if trigger owner name matches with current_user and is editable
+ expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
+ expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index 7d750877d09..b3e58c3dfdb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -10,7 +10,11 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
- project: project, current_user: user, origin_ref: origin_ref, merge_request: merge_request)
+ project: project,
+ current_user: user,
+ origin_ref: origin_ref,
+ merge_request: merge_request,
+ trigger_request: trigger_request)
end
let(:step) { described_class.new(pipeline, command) }
@@ -18,6 +22,7 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
let(:ref) { 'master' }
let(:origin_ref) { ref }
let(:merge_request) { nil }
+ let(:trigger_request) { nil }
shared_context 'detached merge request pipeline' do
let(:merge_request) do
@@ -69,6 +74,43 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
end
end
+ context 'when pipeline triggered by legacy trigger' do
+ let(:user) { nil }
+ let(:trigger_request) do
+ build_stubbed(:ci_trigger_request, trigger: build_stubbed(:ci_trigger, owner: nil))
+ end
+
+ context 'when :use_legacy_pipeline_triggers feature flag is enabled' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: true)
+ step.perform!
+ end
+
+ it 'allows legacy triggers to create a pipeline' do
+ expect(pipeline).to be_valid
+ end
+
+ it 'does not break the chain' do
+ expect(step.break?).to eq false
+ end
+ end
+
+ context 'when :use_legacy_pipeline_triggers feature flag is disabled' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: false)
+ step.perform!
+ end
+
+ it 'prevents legacy triggers from creating a pipeline' do
+ expect(pipeline.errors.to_a).to include /Trigger token is invalid/
+ end
+
+ it 'breaks the pipeline builder chain' do
+ expect(step.break?).to eq true
+ end
+ end
+ end
+
describe '#allowed_to_create?' do
subject { step.allowed_to_create? }
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index fde8375f2a5..5b5d6f51b33 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -54,19 +54,31 @@ describe Ci::Trigger do
end
describe '#can_access_project?' do
+ let(:owner) { create(:user) }
let(:trigger) { create(:ci_trigger, owner: owner, project: project) }
context 'when owner is blank' do
- let(:owner) { nil }
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: false)
+ trigger.update_attribute(:owner, nil)
+ end
subject { trigger.can_access_project? }
- it { is_expected.to eq(true) }
+ it { is_expected.to eq(false) }
+
+ context 'when :use_legacy_pipeline_triggers feature flag is enabled' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: true)
+ end
+
+ subject { trigger.can_access_project? }
+
+ it { is_expected.to eq(true) }
+ end
end
context 'when owner is set' do
- let(:owner) { create(:user) }
-
subject { trigger.can_access_project? }
context 'and is member of the project' do
diff --git a/spec/policies/ci/trigger_policy_spec.rb b/spec/policies/ci/trigger_policy_spec.rb
index d8a63066265..e9a85890082 100644
--- a/spec/policies/ci/trigger_policy_spec.rb
+++ b/spec/policies/ci/trigger_policy_spec.rb
@@ -3,52 +3,24 @@ require 'spec_helper'
describe Ci::TriggerPolicy do
let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:trigger) { create(:ci_trigger, project: project, owner: owner) }
+ let(:trigger) { create(:ci_trigger, project: project, owner: create(:user)) }
- let(:policies) do
- described_class.new(user, trigger)
- end
-
- shared_examples 'allows to admin and manage trigger' do
- it 'does include ability to admin trigger' do
- expect(policies).to be_allowed :admin_trigger
- end
-
- it 'does include ability to manage trigger' do
- expect(policies).to be_allowed :manage_trigger
- end
- end
-
- shared_examples 'allows to manage trigger' do
- it 'does not include ability to admin trigger' do
- expect(policies).not_to be_allowed :admin_trigger
- end
-
- it 'does include ability to manage trigger' do
- expect(policies).to be_allowed :manage_trigger
- end
- end
-
- shared_examples 'disallows to admin and manage trigger' do
- it 'does not include ability to admin trigger' do
- expect(policies).not_to be_allowed :admin_trigger
- end
-
- it 'does not include ability to manage trigger' do
- expect(policies).not_to be_allowed :manage_trigger
- end
- end
+ subject { described_class.new(user, trigger) }
describe '#rules' do
context 'when owner is undefined' do
- let(:owner) { nil }
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: false)
+ trigger.update_attribute(:owner, nil)
+ end
context 'when user is maintainer of the project' do
before do
project.add_maintainer(user)
end
- it_behaves_like 'allows to admin and manage trigger'
+ it { is_expected.to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
end
context 'when user is developer of the project' do
@@ -56,35 +28,63 @@ describe Ci::TriggerPolicy do
project.add_developer(user)
end
- it_behaves_like 'disallows to admin and manage trigger'
+ it { is_expected.not_to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
end
- context 'when user is not member of the project' do
- it_behaves_like 'disallows to admin and manage trigger'
+ context 'when :use_legacy_pipeline_triggers feature flag is enabled' do
+ before do
+ stub_feature_flags(use_legacy_pipeline_triggers: true)
+ end
+
+ context 'when user is maintainer of the project' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { is_expected.to be_allowed(:manage_trigger) }
+ it { is_expected.to be_allowed(:admin_trigger) }
+ end
+
+ context 'when user is developer of the project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.not_to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
+ end
+
+ context 'when user is not member of the project' do
+ it { is_expected.not_to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
+ end
end
end
context 'when owner is an user' do
- let(:owner) { user }
+ before do
+ trigger.update!(owner: user)
+ end
context 'when user is maintainer of the project' do
before do
project.add_maintainer(user)
end
- it_behaves_like 'allows to admin and manage trigger'
+ it { is_expected.to be_allowed(:manage_trigger) }
+ it { is_expected.to be_allowed(:admin_trigger) }
end
end
context 'when owner is another user' do
- let(:owner) { create(:user) }
-
context 'when user is maintainer of the project' do
before do
project.add_maintainer(user)
end
- it_behaves_like 'allows to manage trigger'
+ it { is_expected.to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
end
context 'when user is developer of the project' do
@@ -92,11 +92,13 @@ describe Ci::TriggerPolicy do
project.add_developer(user)
end
- it_behaves_like 'disallows to admin and manage trigger'
+ it { is_expected.not_to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
end
context 'when user is not member of the project' do
- it_behaves_like 'disallows to admin and manage trigger'
+ it { is_expected.not_to be_allowed(:manage_trigger) }
+ it { is_expected.not_to be_allowed(:admin_trigger) }
end
end
end