Skip to content
Snippets Groups Projects
Commit 1e48ccdf authored by Sean Carroll's avatar Sean Carroll
Browse files

Deployer authorisation for protected environments

parent 9b967078
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -20,6 +20,11 @@ class BuildPolicy < CommitStatusPolicy
end
end
 
# overridden in EE
condition(:protected_environment_access) do
false
end
condition(:owner_of_job) do
@subject.triggered_by?(@user)
end
Loading
Loading
@@ -40,7 +45,7 @@ class BuildPolicy < CommitStatusPolicy
@subject.pipeline.webide?
end
 
rule { protected_ref | archived }.policy do
rule { ~protected_environment_access & (protected_ref | archived) }.policy do
prevent :update_build
prevent :update_commit_status
prevent :erase_build
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@ class ProtectedEnvironment::DeployAccessLevel < ApplicationRecord
ALLOWED_ACCESS_LEVELS = [
Gitlab::Access::MAINTAINER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::REPORTER,
Gitlab::Access::ADMIN
].freeze
 
Loading
Loading
@@ -15,9 +16,7 @@ class ProtectedEnvironment::DeployAccessLevel < ApplicationRecord
belongs_to :group
belongs_to :protected_environment
 
validates :access_level, presence: true, if: :role?, inclusion: {
in: ALLOWED_ACCESS_LEVELS
}
validates :access_level, presence: true, inclusion: { in: ALLOWED_ACCESS_LEVELS }
 
delegate :project, to: :protected_environment
 
Loading
Loading
Loading
Loading
@@ -7,10 +7,28 @@ module BuildPolicy
prepended do
condition(:deployable_by_user) { deployable_by_user? }
 
rule { ~deployable_by_user }.policy do
condition(:protected_environment_access) do
project = @subject.project
environment = @subject.environment
if environment && project.protected_environments_feature_available?
protected_environment = project.protected_environment_by_name(environment)
!!protected_environment&.accessible_to?(user)
else
false
end
end
rule { ~deployable_by_user & ~protected_environment_access}.policy do
prevent :update_build
end
 
rule { protected_environment_access }.policy do
enable :update_commit_status
enable :update_build
end
private
 
alias_method :current_user, :user
Loading
Loading
---
title: Deployer authorisation for protected environments
merge_request: 38188
author:
type: added
Loading
Loading
@@ -2,6 +2,10 @@
require 'spec_helper'
 
RSpec.describe ProtectedEnvironment::DeployAccessLevel do
let_it_be(:project) { create(:project) }
let_it_be(:protected_environment) { create(:protected_environment, project: project) }
let_it_be(:user) { create(:user) }
describe 'associations' do
it { is_expected.to belong_to(:protected_environment) }
it { is_expected.to belong_to(:user) }
Loading
Loading
@@ -10,13 +14,10 @@
 
describe 'validations' do
it { is_expected.to validate_presence_of(:access_level) }
it { is_expected.to validate_inclusion_of(:access_level).in_array([Gitlab::Access::REPORTER, Gitlab::Access::DEVELOPER, Gitlab::Access::MAINTAINER]) }
end
 
describe '#check_access' do
let(:project) { create(:project) }
let(:protected_environment) { create(:protected_environment, project: project) }
let(:user) { create(:user) }
subject { deploy_access_level.check_access(user) }
 
describe 'admin access' do
Loading
Loading
@@ -74,23 +75,37 @@
end
 
describe 'access level' do
let(:developer_access) { Gitlab::Access::DEVELOPER }
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, access_level: developer_access) }
context 'with a permitted access level' do
let(:developer_access) { Gitlab::Access::DEVELOPER }
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, access_level: developer_access) }
 
context 'when user is project member above the permitted access level' do
before do
project.add_developer(user)
context 'when user is project member above the permitted access level' do
before do
project.add_developer(user)
end
it { is_expected.to be_truthy }
end
 
it { is_expected.to be_truthy }
context 'when user is project member below the permitted access level' do
before do
project.add_reporter(user)
end
it { is_expected.to be_falsy }
end
end
 
context 'when user is project member below the permitted access level' do
context 'when the access level is not permitted' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, access_level: Gitlab::Access::GUEST) }
before do
project.add_reporter(user)
project.add_guest(user)
end
 
it { is_expected.to be_falsy }
it 'does not save the record' do
expect { deploy_access_level }.to raise_error ActiveRecord::RecordInvalid
end
end
end
end
Loading
Loading
Loading
Loading
@@ -14,6 +14,6 @@
 
subject { user.can?(:update_build, build) }
 
it_behaves_like 'protected environments access'
it_behaves_like 'protected environments access', direct_access: true
end
end
Loading
Loading
@@ -31,6 +31,6 @@
describe '#create_environment_terminal' do
subject { user.can?(:create_environment_terminal, environment) }
 
it_behaves_like 'protected environments access', false
it_behaves_like 'protected environments access', developer_access: false
end
end
Loading
Loading
@@ -19,7 +19,7 @@
 
subject { entity.as_json[:playable] }
 
it_behaves_like 'protected environments access'
it_behaves_like 'protected environments access', direct_access: true
end
 
describe '#retryable?' do
Loading
Loading
@@ -27,6 +27,6 @@
 
subject { entity.as_json.include?(:retry_path) }
 
it_behaves_like 'protected environments access'
it_behaves_like 'protected environments access', direct_access: true
end
end
Loading
Loading
@@ -66,7 +66,7 @@
 
subject { entity.as_json.include?(:terminal_path) }
 
it_behaves_like 'protected environments access', false
it_behaves_like 'protected environments access', developer_access: false
end
end
end
Loading
Loading
# frozen_string_literal: true
 
RSpec.shared_examples 'protected environments access' do |developer_access = true|
RSpec.shared_examples 'protected environments access' do |developer_access: true, direct_access: false|
using RSpec::Parameterized::TableSyntax
 
include AdminModeHelper
Loading
Loading
@@ -13,20 +13,19 @@
context 'when Protected Environments feature is not available in the project' do
let(:feature_available) { false }
 
where(:access_level, :admin_mode, :result) do
:guest | nil | false
:reporter | nil | false
:developer | nil | developer_access
:maintainer | nil | true
:admin | false | false
:admin | true | true
where(:access_level, :result) do
:guest | false
:reporter | false
:developer | developer_access
:maintainer | true
:admin | true
end
 
with_them do
before do
environment
 
update_user_access(access_level, admin_mode, user, project)
update_user_access(access_level, user, project)
end
 
it { is_expected.to eq(result) }
Loading
Loading
@@ -40,20 +39,19 @@
let(:protected_environment) { create(:protected_environment, name: environment.name, project: project) }
 
context 'when user does not have access to the environment' do
where(:access_level, :admin_mode, :result) do
:guest | nil | false
:reporter | nil | false
:developer | nil | false
:maintainer | nil | false
:admin | false | false
:admin | true | true
where(:access_level, :result) do
:guest | false
:reporter | false
:developer | false
:maintainer | false
:admin | true
end
 
with_them do
before do
protected_environment
 
update_user_access(access_level, admin_mode, user, project)
update_user_access(access_level, user, project)
end
 
it { is_expected.to eq(result) }
Loading
Loading
@@ -61,40 +59,49 @@
end
 
context 'when user has access to the environment' do
where(:access_level, :admin_mode, :result) do
:guest | nil | false
:reporter | nil | false
:developer | nil | developer_access
:maintainer | nil | true
:admin | false | false
:admin | true | true
where(:access_level, :result) do
:reporter | direct_access
:developer | developer_access
:maintainer | true
:admin | true
end
 
with_them do
before do
protected_environment.deploy_access_levels.create(user: user)
protected_environment.deploy_access_levels.create!(user: user, access_level: deploy_access_level(access_level))
 
update_user_access(access_level, admin_mode, user, project)
update_user_access(access_level, user, project)
end
it { is_expected.to eq(result) }
end
end
context 'when the user has access via a group' do
let(:group) { create(:group) }
before do
project.add_reporter(user)
group.add_reporter(user)
protected_environment.deploy_access_levels.create!(group: group, access_level: Gitlab::Access::REPORTER)
end
it { is_expected.to eq(direct_access) }
end
end
 
context 'when environment is not protected' do
where(:access_level, :admin_mode, :result) do
:guest | nil | false
:reporter | nil | false
:developer | nil | developer_access
:maintainer | nil | true
:admin | false | false
:admin | true | true
where(:access_level, :result) do
:guest | false
:reporter | false
:developer | developer_access
:maintainer | true
:admin | true
end
 
with_them do
before do
update_user_access(access_level, admin_mode, user, project)
update_user_access(access_level, user, project)
end
 
it { is_expected.to eq(result) }
Loading
Loading
@@ -102,12 +109,27 @@
end
end
 
def update_user_access(access_level, admin_mode, user, project)
def update_user_access(access_level, user, project)
if access_level == :admin
user.update_attribute(:admin, true)
enable_admin_mode!(user) if admin_mode
enable_admin_mode!(user)
elsif access_level.present?
project.add_user(user, access_level)
end
end
def deploy_access_level(access_level)
case access_level
when :guest
Gitlab::Access::GUEST
when :reporter
Gitlab::Access::REPORTER
when :developer
Gitlab::Access::DEVELOPER
when :maintainer
Gitlab::Access::MAINTAINER
when :admin
Gitlab::Access::MAINTAINER
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment