Commit e1242733 authored by charlie ablett's avatar charlie ablett

Merge branch 'dep-approval-multi-access-levels' into 'master'

Database Modeling for Multi Access Levels in Deployment Approval

See merge request gitlab-org/gitlab!82800
parents 2a7c0a5a 61b1b9ea
# frozen_string_literal: true
class CreateProtectedEnvironmentApprovalRules < Gitlab::Database::Migration[1.0]
def up
create_table :protected_environment_approval_rules do |t|
t.references :protected_environment,
index: { name: :index_approval_rule_on_protected_environment_id },
foreign_key: { to_table: :protected_environments, on_delete: :cascade },
null: false
t.bigint :user_id
t.bigint :group_id
t.timestamps_with_timezone null: false
t.integer :access_level, limit: 2
t.integer :required_approvals, null: false, limit: 2
t.index :user_id
t.index :group_id
t.check_constraint "((access_level IS NOT NULL) AND (group_id IS NULL) AND (user_id IS NULL)) OR " \
"((user_id IS NOT NULL) AND (access_level IS NULL) AND (group_id IS NULL)) OR " \
"((group_id IS NOT NULL) AND (user_id IS NULL) AND (access_level IS NULL))"
t.check_constraint "required_approvals > 0"
end
end
def down
drop_table :protected_environment_approval_rules
end
end
# frozen_string_literal: true
class AddUserFkToProtectedEnvironmentApprovalRules < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :protected_environment_approval_rules, :users, column: :user_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key_if_exists :protected_environment_approval_rules, column: :user_id
end
end
end
# frozen_string_literal: true
class AddGroupFkToProtectedEnvironmentApprovalRules < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_concurrent_foreign_key :protected_environment_approval_rules, :namespaces, column: :group_id, on_delete: :cascade
end
def down
with_lock_retries do
remove_foreign_key_if_exists :protected_environment_approval_rules, column: :group_id
end
end
end
# frozen_string_literal: true
class AddApprovalRuleIdToDeploymentApprovals < Gitlab::Database::Migration[1.0]
def change
add_column :deployment_approvals, :approval_rule_id, :bigint
end
end
# frozen_string_literal: true
class AddApprovalRuleFkToDeploymentApprovals < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
INDEX_NAME = 'index_deployment_approvals_on_approval_rule_id'
def up
add_concurrent_index :deployment_approvals, :approval_rule_id, name: INDEX_NAME
add_concurrent_foreign_key :deployment_approvals, :protected_environment_approval_rules, column: :approval_rule_id, on_delete: :nullify
end
def down
with_lock_retries do
remove_foreign_key_if_exists :deployment_approvals, column: :approval_rule_id
end
remove_concurrent_index_by_name :deployment_approvals, INDEX_NAME
end
end
258c7a3409aea1c713c2ddd6679de586e7548ce4d7c0811db1d4903f2794c660
\ No newline at end of file
85be80bb8c929d017fedfe66c1f18e4a0dbd7dd8f3b683ded60213e621ec06f4
\ No newline at end of file
41e7a36164fe3b1b582149d9cfbefc6ee2ce804ac85207f918c064b0ef738b53
\ No newline at end of file
e58b89906cd09577c1a773768e4cf3f97b870744e4ee6a323e0276895dff0de5
\ No newline at end of file
e2fa0265f3c14c8e6f08a4ffc4b682d8805fa634bac66c578684faaee97541cf
\ No newline at end of file
...@@ -14270,6 +14270,7 @@ CREATE TABLE deployment_approvals ( ...@@ -14270,6 +14270,7 @@ CREATE TABLE deployment_approvals (
updated_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL,
status smallint NOT NULL, status smallint NOT NULL,
comment text, comment text,
approval_rule_id bigint,
CONSTRAINT check_e2eb6a17d8 CHECK ((char_length(comment) <= 255)) CONSTRAINT check_e2eb6a17d8 CHECK ((char_length(comment) <= 255))
); );
...@@ -19681,6 +19682,28 @@ CREATE SEQUENCE protected_branches_id_seq ...@@ -19681,6 +19682,28 @@ CREATE SEQUENCE protected_branches_id_seq
ALTER SEQUENCE protected_branches_id_seq OWNED BY protected_branches.id; ALTER SEQUENCE protected_branches_id_seq OWNED BY protected_branches.id;
CREATE TABLE protected_environment_approval_rules (
id bigint NOT NULL,
protected_environment_id bigint NOT NULL,
user_id bigint,
group_id bigint,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
access_level smallint,
required_approvals smallint NOT NULL,
CONSTRAINT chk_rails_bed75249bc CHECK ((((access_level IS NOT NULL) AND (group_id IS NULL) AND (user_id IS NULL)) OR ((user_id IS NOT NULL) AND (access_level IS NULL) AND (group_id IS NULL)) OR ((group_id IS NOT NULL) AND (user_id IS NULL) AND (access_level IS NULL)))),
CONSTRAINT chk_rails_cfa90ae3b5 CHECK ((required_approvals > 0))
);
CREATE SEQUENCE protected_environment_approval_rules_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE protected_environment_approval_rules_id_seq OWNED BY protected_environment_approval_rules.id;
CREATE TABLE protected_environment_deploy_access_levels ( CREATE TABLE protected_environment_deploy_access_levels (
id integer NOT NULL, id integer NOT NULL,
created_at timestamp with time zone NOT NULL, created_at timestamp with time zone NOT NULL,
...@@ -22950,6 +22973,8 @@ ALTER TABLE ONLY protected_branch_unprotect_access_levels ALTER COLUMN id SET DE ...@@ -22950,6 +22973,8 @@ ALTER TABLE ONLY protected_branch_unprotect_access_levels ALTER COLUMN id SET DE
ALTER TABLE ONLY protected_branches ALTER COLUMN id SET DEFAULT nextval('protected_branches_id_seq'::regclass); ALTER TABLE ONLY protected_branches ALTER COLUMN id SET DEFAULT nextval('protected_branches_id_seq'::regclass);
ALTER TABLE ONLY protected_environment_approval_rules ALTER COLUMN id SET DEFAULT nextval('protected_environment_approval_rules_id_seq'::regclass);
ALTER TABLE ONLY protected_environment_deploy_access_levels ALTER COLUMN id SET DEFAULT nextval('protected_environment_deploy_access_levels_id_seq'::regclass); ALTER TABLE ONLY protected_environment_deploy_access_levels ALTER COLUMN id SET DEFAULT nextval('protected_environment_deploy_access_levels_id_seq'::regclass);
ALTER TABLE ONLY protected_environments ALTER COLUMN id SET DEFAULT nextval('protected_environments_id_seq'::regclass); ALTER TABLE ONLY protected_environments ALTER COLUMN id SET DEFAULT nextval('protected_environments_id_seq'::regclass);
...@@ -25066,6 +25091,9 @@ ALTER TABLE ONLY protected_branch_unprotect_access_levels ...@@ -25066,6 +25091,9 @@ ALTER TABLE ONLY protected_branch_unprotect_access_levels
ALTER TABLE ONLY protected_branches ALTER TABLE ONLY protected_branches
ADD CONSTRAINT protected_branches_pkey PRIMARY KEY (id); ADD CONSTRAINT protected_branches_pkey PRIMARY KEY (id);
ALTER TABLE ONLY protected_environment_approval_rules
ADD CONSTRAINT protected_environment_approval_rules_pkey PRIMARY KEY (id);
ALTER TABLE ONLY protected_environment_deploy_access_levels ALTER TABLE ONLY protected_environment_deploy_access_levels
ADD CONSTRAINT protected_environment_deploy_access_levels_pkey PRIMARY KEY (id); ADD CONSTRAINT protected_environment_deploy_access_levels_pkey PRIMARY KEY (id);
...@@ -26707,6 +26735,8 @@ CREATE UNIQUE INDEX index_approval_rule_name_for_code_owners_rule_type ON approv ...@@ -26707,6 +26735,8 @@ CREATE UNIQUE INDEX index_approval_rule_name_for_code_owners_rule_type ON approv
CREATE UNIQUE INDEX index_approval_rule_name_for_sectional_code_owners_rule_type ON approval_merge_request_rules USING btree (merge_request_id, name, section) WHERE (rule_type = 2); CREATE UNIQUE INDEX index_approval_rule_name_for_sectional_code_owners_rule_type ON approval_merge_request_rules USING btree (merge_request_id, name, section) WHERE (rule_type = 2);
CREATE INDEX index_approval_rule_on_protected_environment_id ON protected_environment_approval_rules USING btree (protected_environment_id);
CREATE INDEX index_approval_rules_code_owners_rule_type ON approval_merge_request_rules USING btree (merge_request_id) WHERE (rule_type = 2); CREATE INDEX index_approval_rules_code_owners_rule_type ON approval_merge_request_rules USING btree (merge_request_id) WHERE (rule_type = 2);
CREATE INDEX index_approvals_on_merge_request_id ON approvals USING btree (merge_request_id); CREATE INDEX index_approvals_on_merge_request_id ON approvals USING btree (merge_request_id);
...@@ -27343,6 +27373,8 @@ CREATE INDEX index_deploy_tokens_on_token_and_expires_at_and_id ON deploy_tokens ...@@ -27343,6 +27373,8 @@ CREATE INDEX index_deploy_tokens_on_token_and_expires_at_and_id ON deploy_tokens
CREATE UNIQUE INDEX index_deploy_tokens_on_token_encrypted ON deploy_tokens USING btree (token_encrypted); CREATE UNIQUE INDEX index_deploy_tokens_on_token_encrypted ON deploy_tokens USING btree (token_encrypted);
CREATE INDEX index_deployment_approvals_on_approval_rule_id ON deployment_approvals USING btree (approval_rule_id);
CREATE UNIQUE INDEX index_deployment_approvals_on_deployment_id_and_user_id ON deployment_approvals USING btree (deployment_id, user_id); CREATE UNIQUE INDEX index_deployment_approvals_on_deployment_id_and_user_id ON deployment_approvals USING btree (deployment_id, user_id);
CREATE INDEX index_deployment_approvals_on_user_id ON deployment_approvals USING btree (user_id); CREATE INDEX index_deployment_approvals_on_user_id ON deployment_approvals USING btree (user_id);
...@@ -28787,6 +28819,10 @@ CREATE INDEX index_protected_branch_unprotect_access_levels_on_user_id ON protec ...@@ -28787,6 +28819,10 @@ CREATE INDEX index_protected_branch_unprotect_access_levels_on_user_id ON protec
CREATE INDEX index_protected_branches_on_project_id ON protected_branches USING btree (project_id); CREATE INDEX index_protected_branches_on_project_id ON protected_branches USING btree (project_id);
CREATE INDEX index_protected_environment_approval_rules_on_group_id ON protected_environment_approval_rules USING btree (group_id);
CREATE INDEX index_protected_environment_approval_rules_on_user_id ON protected_environment_approval_rules USING btree (user_id);
CREATE INDEX index_protected_environment_deploy_access ON protected_environment_deploy_access_levels USING btree (protected_environment_id); CREATE INDEX index_protected_environment_deploy_access ON protected_environment_deploy_access_levels USING btree (protected_environment_id);
CREATE INDEX index_protected_environment_deploy_access_levels_on_group_id ON protected_environment_deploy_access_levels USING btree (group_id); CREATE INDEX index_protected_environment_deploy_access_levels_on_group_id ON protected_environment_deploy_access_levels USING btree (group_id);
...@@ -31172,6 +31208,9 @@ ALTER TABLE ONLY ci_pipelines ...@@ -31172,6 +31208,9 @@ ALTER TABLE ONLY ci_pipelines
ALTER TABLE ONLY merge_request_reviewers ALTER TABLE ONLY merge_request_reviewers
ADD CONSTRAINT fk_3d674b9f23 FOREIGN KEY (updated_state_by_user_id) REFERENCES users(id) ON DELETE SET NULL; ADD CONSTRAINT fk_3d674b9f23 FOREIGN KEY (updated_state_by_user_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY protected_environment_approval_rules
ADD CONSTRAINT fk_405568b491 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_pipeline_schedule_variables ALTER TABLE ONLY ci_pipeline_schedule_variables
ADD CONSTRAINT fk_41c35fda51 FOREIGN KEY (pipeline_schedule_id) REFERENCES ci_pipeline_schedules(id) ON DELETE CASCADE; ADD CONSTRAINT fk_41c35fda51 FOREIGN KEY (pipeline_schedule_id) REFERENCES ci_pipeline_schedules(id) ON DELETE CASCADE;
...@@ -31235,6 +31274,9 @@ ALTER TABLE ONLY project_access_tokens ...@@ -31235,6 +31274,9 @@ ALTER TABLE ONLY project_access_tokens
ALTER TABLE ONLY merge_requests ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_6149611a04 FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL; ADD CONSTRAINT fk_6149611a04 FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY deployment_approvals
ADD CONSTRAINT fk_61cdbdc5b9 FOREIGN KEY (approval_rule_id) REFERENCES protected_environment_approval_rules(id) ON DELETE SET NULL;
ALTER TABLE ONLY dast_profile_schedules ALTER TABLE ONLY dast_profile_schedules
ADD CONSTRAINT fk_61d52aa0e7 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE; ADD CONSTRAINT fk_61d52aa0e7 FOREIGN KEY (dast_profile_id) REFERENCES dast_profiles(id) ON DELETE CASCADE;
...@@ -31274,6 +31316,9 @@ ALTER TABLE ONLY projects ...@@ -31274,6 +31316,9 @@ ALTER TABLE ONLY projects
ALTER TABLE ONLY terraform_state_versions ALTER TABLE ONLY terraform_state_versions
ADD CONSTRAINT fk_6e81384d7f FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL; ADD CONSTRAINT fk_6e81384d7f FOREIGN KEY (created_by_user_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY protected_environment_approval_rules
ADD CONSTRAINT fk_6ee8249821 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY protected_branch_push_access_levels ALTER TABLE ONLY protected_branch_push_access_levels
ADD CONSTRAINT fk_7111b68cdb FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_7111b68cdb FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
...@@ -32285,6 +32330,9 @@ ALTER TABLE ONLY scim_identities ...@@ -32285,6 +32330,9 @@ ALTER TABLE ONLY scim_identities
ALTER TABLE ONLY snippet_user_mentions ALTER TABLE ONLY snippet_user_mentions
ADD CONSTRAINT fk_rails_4d3f96b2cb FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_4d3f96b2cb FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE;
ALTER TABLE ONLY protected_environment_approval_rules
ADD CONSTRAINT fk_rails_4e554f96f5 FOREIGN KEY (protected_environment_id) REFERENCES protected_environments(id) ON DELETE CASCADE;
ALTER TABLE ONLY deployment_clusters ALTER TABLE ONLY deployment_clusters
ADD CONSTRAINT fk_rails_4e6243e120 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_4e6243e120 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
...@@ -7,6 +7,11 @@ module Deployments ...@@ -7,6 +7,11 @@ module Deployments
belongs_to :deployment belongs_to :deployment
belongs_to :user belongs_to :user
belongs_to :approval_rule,
class_name: 'ProtectedEnvironments::ApprovalRule',
foreign_key: :approval_rule_id,
inverse_of: :deployment_approvals
validates :user, presence: true, uniqueness: { scope: :deployment_id } validates :user, presence: true, uniqueness: { scope: :deployment_id }
validates :deployment, presence: true validates :deployment, presence: true
validates :status, presence: true validates :status, presence: true
......
...@@ -6,6 +6,7 @@ class ProtectedEnvironment < ApplicationRecord ...@@ -6,6 +6,7 @@ class ProtectedEnvironment < ApplicationRecord
belongs_to :project belongs_to :project
belongs_to :group, inverse_of: :protected_environments belongs_to :group, inverse_of: :protected_environments
has_many :deploy_access_levels, inverse_of: :protected_environment has_many :deploy_access_levels, inverse_of: :protected_environment
has_many :approval_rules, class_name: 'ProtectedEnvironments::ApprovalRule', inverse_of: :protected_environment
accepts_nested_attributes_for :deploy_access_levels, allow_destroy: true accepts_nested_attributes_for :deploy_access_levels, allow_destroy: true
......
# frozen_string_literal: true # frozen_string_literal: true
class ProtectedEnvironment::DeployAccessLevel < ApplicationRecord class ProtectedEnvironment::DeployAccessLevel < ApplicationRecord
ALLOWED_ACCESS_LEVELS = [ include ProtectedEnvironments::Authorizable
Gitlab::Access::MAINTAINER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::REPORTER,
Gitlab::Access::ADMIN
].freeze
HUMAN_ACCESS_LEVELS = {
Gitlab::Access::MAINTAINER => 'Maintainers',
Gitlab::Access::DEVELOPER => 'Developers + Maintainers'
}.freeze
belongs_to :user
belongs_to :group
belongs_to :protected_environment, inverse_of: :deploy_access_levels belongs_to :protected_environment, inverse_of: :deploy_access_levels
validates :access_level, presence: true, inclusion: { in: ALLOWED_ACCESS_LEVELS } validates :access_level, presence: true, inclusion: { in: ALLOWED_ACCESS_LEVELS }
def check_access(user)
return false unless user
return true if user.admin?
return user.id == user_id if user_type?
return group.member?(user) if group_type?
protected_environment.container_access_level(user) >= access_level
end
def user_type?
user_id.present?
end
def group_type?
group_id.present?
end
def type
if user_type?
:user
elsif group_type?
:group
else
:role
end
end
def role?
type == :role
end
def humanize
return user.name if user_type?
return group.name if group_type?
HUMAN_ACCESS_LEVELS[access_level]
end
end end
# frozen_string_literal: true
module ProtectedEnvironments
class ApprovalRule < ApplicationRecord
include Authorizable
self.table_name = 'protected_environment_approval_rules'
belongs_to :protected_environment, inverse_of: :approval_rules
has_many :deployment_approvals, class_name: 'Deployments::Approval', inverse_of: :approval_rule
validates :access_level, allow_blank: true, inclusion: { in: ALLOWED_ACCESS_LEVELS }
end
end
# frozen_string_literal: true
module ProtectedEnvironments
module Authorizable
extend ActiveSupport::Concern
included do
belongs_to :user
belongs_to :group
end
ALLOWED_ACCESS_LEVELS = [
Gitlab::Access::MAINTAINER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::REPORTER,
Gitlab::Access::ADMIN
].freeze
HUMAN_ACCESS_LEVELS = {
Gitlab::Access::MAINTAINER => 'Maintainers',
Gitlab::Access::DEVELOPER => 'Developers + Maintainers'
}.freeze
def check_access(user)
return false unless user
return true if user.admin? # rubocop: disable Cop/UserAdmin
return user.id == user_id if user_type?
return group.member?(user) if group_type?
protected_environment.container_access_level(user) >= access_level
end
def user_type?
user_id.present?
end
def group_type?
group_id.present?
end
def type
if user_type?
:user
elsif group_type?
:group
else
:role
end
end
def role?
type == :role
end
def humanize
return user.name if user_type?
return group.name if group_type?
HUMAN_ACCESS_LEVELS[access_level]
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :protected_environment_approval_rule, class: 'ProtectedEnvironments::ApprovalRule' do
protected_environment
required_approvals { 1 }
trait :maintainer_access do
access_level { Gitlab::Access::MAINTAINER }
end
end
end
...@@ -6,6 +6,7 @@ RSpec.describe Deployments::Approval do ...@@ -6,6 +6,7 @@ RSpec.describe Deployments::Approval do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:deployment) } it { is_expected.to belong_to(:deployment) }
it { is_expected.to belong_to(:approval_rule).class_name('ProtectedEnvironments::ApprovalRule').with_foreign_key(:approval_rule_id).inverse_of(:deployment_approvals) }
end end
describe 'validations' do describe 'validations' do
......
...@@ -2,167 +2,10 @@ ...@@ -2,167 +2,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe ProtectedEnvironment::DeployAccessLevel do RSpec.describe ProtectedEnvironment::DeployAccessLevel do
let_it_be_with_reload(: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) }
it { is_expected.to belong_to(:group) }
end
describe 'validations' do describe 'validations' do
it { is_expected.to validate_presence_of(:access_level) } 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
subject { deploy_access_level.check_access(user) }
context 'anonymous access' do
let(:user) { nil }
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment) }
it { is_expected.to be_falsy }
end
describe 'admin access' do
let_it_be(:user) { create(:user, :admin) }
context 'when admin user does have specific access' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, user: user) }
it { is_expected.to be_truthy }
end
context 'when admin user does not have specific access' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment) }
it { is_expected.to be_truthy }
end
end
describe 'user access' do
context 'when specific access has been assigned to a user' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, user: user) }
it { is_expected.to be_truthy }
end
context 'when no permissions have been given to a user' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment) }
it { is_expected.to be_falsy }
end
end
describe 'group access' do
let_it_be(:group) { create(:group, projects: [project]) }
context 'when specific access has been assigned to a group' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, group: group) }
before do
group.add_reporter(user)
end
it { is_expected.to be_truthy }
end
context 'when there is an inherited member of a group' do
let_it_be(:parent_group) { create(:group) }
let_it_be(:child_group) { create(:group, parent: parent_group, projects: [project])}
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, group: child_group) }
before do
parent_group.add_reporter(user)
end
it { is_expected.to be_truthy }
end
context 'when no permissions have been given to a group' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment) }
before do
group.add_reporter(user)
end end
it { is_expected.to be_falsy } it_behaves_like 'authorizable for protected environments',
end factory_name: :protected_environment_deploy_access_level
end
describe 'access level' do
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)
end
it { is_expected.to be_truthy }
end
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 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_guest(user)
end
it 'does not save the record' do
expect { deploy_access_level }.to raise_error ActiveRecord::RecordInvalid
end
end
end
end
describe '#humanize' do
let_it_be(:protected_environment) { create(:protected_environment) }
subject { deploy_access_level.humanize }
context 'when is related to a user' do
let(:user) { create(:user) }
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, user: user) }
it { is_expected.to eq(user.name) }
end
context 'when is related to a group' do
let(:group) { create(:group) }
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, group: group) }
it { is_expected.to eq(group.name) }
end
context 'when is set to have a role' do
let(:deploy_access_level) { create(:protected_environment_deploy_access_level, protected_environment: protected_environment, access_level: access_level) }
context 'for developer access' do
let(:access_level) { Gitlab::Access::DEVELOPER }
it { is_expected.to eq('Developers + Maintainers') }
end
context 'for maintainer access' do
let(:access_level) { Gitlab::Access::MAINTAINER }
it { is_expected.to eq('Maintainers') }
end
end
end
end end
...@@ -5,6 +5,7 @@ RSpec.describe ProtectedEnvironment do ...@@ -5,6 +5,7 @@ RSpec.describe ProtectedEnvironment do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:deploy_access_levels) } it { is_expected.to have_many(:deploy_access_levels) }
it { is_expected.to have_many(:approval_rules).class_name('ProtectedEnvironments::ApprovalRule').inverse_of(:protected_environment) }
end end
describe 'validation' do describe 'validation' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ProtectedEnvironments::ApprovalRule do
describe 'associations' do
it { is_expected.to have_many(:deployment_approvals).class_name('Deployments::Approval').inverse_of(:approval_rule) }
end
it_behaves_like 'authorizable for protected environments',
factory_name: :protected_environment_approval_rule
end
# frozen_string_literal: true
RSpec.shared_examples 'authorizable for protected environments' do |factory_name:|
let_it_be_with_reload(: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) }
it { is_expected.to belong_to(:group) }
end
describe 'validations' do
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
subject { authorizable.check_access(user) }
context 'anonymous access' do
let(:user) { nil }
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
it { is_expected.to be_falsy }
end
describe 'admin access' do
let_it_be(:user) { create(:user, :admin) }
context 'when admin user does have specific access' do
let(:authorizable) { create(factory_name, protected_environment: protected_environment, user: user) }
it { is_expected.to be_truthy }
end
context 'when admin user does not have specific access' do
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
it { is_expected.to be_truthy }
end
end
describe 'user access' do
context 'when specific access has been assigned to a user' do
let(:authorizable) { create(factory_name, protected_environment: protected_environment, user: user) }
it { is_expected.to be_truthy }
end
context 'when no permissions have been given to a user' do
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
it { is_expected.to be_falsy }
end
end
describe 'group access' do
let_it_be(:group) { create(:group, projects: [project]) }
context 'when specific access has been assigned to a group' do
let(:authorizable) { create(factory_name, protected_environment: protected_environment, group: group) }
before do
group.add_reporter(user)
end
it { is_expected.to be_truthy }
end
context 'when there is an inherited member of a group' do
let_it_be(:parent_group) { create(:group) }
let_it_be(:child_group) { create(:group, parent: parent_group, projects: [project])}
let(:authorizable) { create(factory_name, protected_environment: protected_environment, group: child_group) }
before do
parent_group.add_reporter(user)
end
it { is_expected.to be_truthy }
end
context 'when no permissions have been given to a group' do
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
before do
group.add_reporter(user)
end
it { is_expected.to be_falsy }
end
end
describe 'access level' do
context 'with a permitted access level' do
let(:developer_access) { Gitlab::Access::DEVELOPER }
let(:authorizable) { create(factory_name, 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)
end
it { is_expected.to be_truthy }
end
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 the access level is not permitted' do
let(:authorizable) { create(factory_name, protected_environment: protected_environment, access_level: Gitlab::Access::GUEST) }
before do
project.add_guest(user)
end
it 'does not save the record' do
expect { authorizable }.to raise_error ActiveRecord::RecordInvalid
end
end
end
end
describe '#humanize' do
let_it_be(:protected_environment) { create(:protected_environment) }
subject { authorizable.humanize }
context 'when is related to a user' do
let(:user) { create(:user) }
let(:authorizable) { create(factory_name, protected_environment: protected_environment, user: user) }
it { is_expected.to eq(user.name) }
end
context 'when is related to a group' do
let(:group) { create(:group) }
let(:authorizable) { create(factory_name, protected_environment: protected_environment, group: group) }
it { is_expected.to eq(group.name) }
end
context 'when is set to have a role' do
let(:authorizable) { create(factory_name, protected_environment: protected_environment, access_level: access_level) }
context 'for developer access' do
let(:access_level) { Gitlab::Access::DEVELOPER }
it { is_expected.to eq('Developers + Maintainers') }
end
context 'for maintainer access' do
let(:access_level) { Gitlab::Access::MAINTAINER }
it { is_expected.to eq('Maintainers') }
end
end
end
describe '#type' do
subject { authorizable.type }
context 'with role type' do
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
it { is_expected.to eq(:role) }
end
context 'with user type' do
let(:user) { create(:user) }
let(:authorizable) { create(factory_name, protected_environment: protected_environment, user: user) }
it { is_expected.to eq(:user) }
end
context 'with group type' do
let(:group) { create(:group) }
let(:authorizable) { create(factory_name, protected_environment: protected_environment, group: group) }
it { is_expected.to eq(:group) }
end
end
describe '#role?' do
subject { authorizable.role? }
context 'with role type' do
let(:authorizable) { create(factory_name, :maintainer_access, protected_environment: protected_environment) }
it { is_expected.to eq(true) }
end
context 'with user type' do
let(:user) { create(:user) }
let(:authorizable) { create(factory_name, protected_environment: protected_environment, user: user) }
it { is_expected.to eq(false) }
end
end
end
...@@ -435,6 +435,7 @@ protected_branches: :gitlab_main ...@@ -435,6 +435,7 @@ protected_branches: :gitlab_main
protected_branch_merge_access_levels: :gitlab_main protected_branch_merge_access_levels: :gitlab_main
protected_branch_push_access_levels: :gitlab_main protected_branch_push_access_levels: :gitlab_main
protected_branch_unprotect_access_levels: :gitlab_main protected_branch_unprotect_access_levels: :gitlab_main
protected_environment_approval_rules: :gitlab_main
protected_environment_deploy_access_levels: :gitlab_main protected_environment_deploy_access_levels: :gitlab_main
protected_environments: :gitlab_main protected_environments: :gitlab_main
protected_tag_create_access_levels: :gitlab_main protected_tag_create_access_levels: :gitlab_main
......
...@@ -665,6 +665,7 @@ protected_environments: ...@@ -665,6 +665,7 @@ protected_environments:
- project - project
- group - group
- deploy_access_levels - deploy_access_levels
- approval_rules
deploy_access_levels: deploy_access_levels:
- protected_environment - protected_environment
- user - user
......
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