Commit 42d99460 authored by Fabio Pitino's avatar Fabio Pitino

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
parent 0e8af252
......@@ -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
......@@ -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 }
......
%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'
......@@ -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
......
---
title: Remove support for legacy pipeline triggers
merge_request: 30133
author:
type: removed
......@@ -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)
......
......@@ -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}'")
......
......@@ -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
......@@ -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? }
......
......@@ -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
......
......@@ -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
......
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