Commit 22570587 authored by Małgorzata Ksionek's avatar Małgorzata Ksionek

Add group push rules feature

Fix failing push_rules_controller spec

Add push rules to nav spec

Fix specs on project_push_rules

Add feature spec for groups

Add modified string file

Update changelog

Fix specs

Add additional check

Remove

bring back

FIX

Add cr remarks

FIX

Fix

Add cr remarks

Add cr remarks

Add cr remarks

Add cr remarks

Add cr remarks

Add cr remarks

Enable admin mode
parent 25f4c592
......@@ -102,6 +102,8 @@
= render_if_exists "layouts/nav/ee/security_link" # EE-specific
= render_if_exists "layouts/nav/ee/push_rules_link" # EE-specific
- if group_sidebar_link?(:kubernetes)
= nav_link(controller: [:clusters]) do
= link_to group_clusters_path(@group) do
......
# frozen_string_literal: true
class Groups::PushRulesController < Groups::ApplicationController
include Gitlab::Utils::StrongMemoize
include PushRulesHelper
layout 'group'
before_action :check_push_rules_available!
before_action :push_rule
respond_to :html
def edit
end
def update
if @push_rule.update(push_rule_params)
flash[:notice] = _('Push Rule updated successfully.')
else
flash[:alert] = @push_rule.errors.full_messages.join(', ').html_safe
end
redirect_to edit_group_push_rules_path(group)
end
private
def push_rule_params
allowed_fields = %i[deny_delete_tag delete_branch_regex commit_message_regex commit_message_negative_regex
branch_name_regex force_push_regex author_email_regex
member_check file_name_regex max_file_size prevent_secrets]
if can?(current_user, :change_reject_unsigned_commits, group)
allowed_fields << :reject_unsigned_commits
end
if can?(current_user, :change_commit_committer_check, group)
allowed_fields << :commit_committer_check
end
params.require(:push_rule).permit(allowed_fields)
end
def push_rule
strong_memoize(:push_rule) do
group.update(push_rule: PushRule.create!) unless group.push_rule
group.push_rule
end
end
def check_push_rules_available!
render_404 unless can_modify_group_push_rules?(current_user, group)
end
end
......@@ -137,10 +137,10 @@ module EE
::Gitlab.config.alternative_gitlab_kerberos_url?
end
def can_change_push_rule?(push_rule, rule)
def can_change_push_rule?(push_rule, rule, context)
return true if push_rule.global?
can?(current_user, :"change_#{rule}", @project)
can?(current_user, :"change_#{rule}", context)
end
def ci_cd_projects_available?
......
# frozen_string_literal: true
module PushRulesHelper
def can_modify_group_push_rules?(current_user, group)
can?(current_user, :change_push_rules, group)
end
def reject_unsigned_commits_description(push_rule)
message = s_("ProjectSettings|Only signed commits can be pushed to this repository.")
......
......@@ -351,9 +351,9 @@ module EE
def predefined_push_rule
strong_memoize(:predefined_push_rule) do
if push_rule.present?
push_rule
elsif has_parent?
next push_rule if push_rule
if has_parent?
parent.predefined_push_rule
else
PushRule.global
......
......@@ -21,7 +21,6 @@ class PushRule < ApplicationRecord
belongs_to :project
validates :project, presence: true, unless: :is_sample?
validates :max_file_size, numericality: { greater_than_or_equal_to: 0, only_integer: true }
validates(*REGEX_COLUMNS, untrusted_regexp: true)
......@@ -91,11 +90,12 @@ class PushRule < ApplicationRecord
is_sample?
end
def available?(feature_sym)
def available?(feature_sym, object: nil)
if global?
License.feature_available?(feature_sym)
else
project&.feature_available?(feature_sym)
object ||= project
object&.feature_available?(feature_sym)
end
end
......
......@@ -70,6 +70,28 @@ module EE
License.feature_available?(:cluster_health)
end
with_scope :global
condition(:commit_committer_check_disabled_globally) do
!PushRule.global&.commit_committer_check
end
with_scope :global
condition(:reject_unsigned_commits_disabled_globally) do
!PushRule.global&.reject_unsigned_commits
end
condition(:commit_committer_check_available) do
@subject.feature_available?(:commit_committer_check)
end
condition(:reject_unsigned_commits_available) do
@subject.feature_available?(:reject_unsigned_commits)
end
condition(:push_rules_available) do
::Feature.enabled?(:group_push_rules, @subject.root_ancestor) && @subject.feature_available?(:push_rules)
end
rule { public_group | logged_in_viewable }.policy do
enable :read_wiki
enable :download_wiki_code
......@@ -212,6 +234,26 @@ module EE
prevent(*create_read_update_admin_destroy(:wiki))
prevent(:download_wiki_code)
end
rule { admin | (commit_committer_check_disabled_globally & can?(:maintainer_access)) }.policy do
enable :change_commit_committer_check
end
rule { commit_committer_check_available }.policy do
enable :read_commit_committer_check
end
rule { ~commit_committer_check_available }.policy do
prevent :change_commit_committer_check
end
rule { admin | (reject_unsigned_commits_disabled_globally & can?(:maintainer_access)) }.enable :change_reject_unsigned_commits
rule { reject_unsigned_commits_available }.enable :read_reject_unsigned_commits
rule { ~reject_unsigned_commits_available }.prevent :change_reject_unsigned_commits
rule { can?(:maintainer_access) & push_rules_available }.enable :change_push_rules
end
override :lookup_access_level!
......
......@@ -80,6 +80,34 @@ module EE
License.feature_available?(:cluster_health)
end
with_scope :subject
condition(:group_push_rules_enabled) do
@subject.group && ::Feature.enabled?(:group_push_rules, @subject.group.root_ancestor)
end
with_scope :subject
condition(:group_push_rule_present) do
group_push_rules_enabled? && subject.group.push_rule
end
with_scope :subject
condition(:reject_unsigned_commits_disabled_by_group) do
if group_push_rule_present?
!subject.group.push_rule.reject_unsigned_commits
else
true
end
end
with_scope :subject
condition(:commit_committer_check_disabled_by_group) do
if group_push_rule_present?
!subject.group.push_rule.commit_committer_check
else
true
end
end
with_scope :subject
condition(:commit_committer_check_available) do
@subject.feature_available?(:commit_committer_check)
......@@ -273,13 +301,13 @@ module EE
rule { ~can?(:push_code) }.prevent :push_code_to_protected_branches
rule { admin | (reject_unsigned_commits_disabled_globally & can?(:maintainer_access)) }.enable :change_reject_unsigned_commits
rule { admin | (reject_unsigned_commits_disabled_globally & reject_unsigned_commits_disabled_by_group & can?(:maintainer_access)) }.enable :change_reject_unsigned_commits
rule { reject_unsigned_commits_available }.enable :read_reject_unsigned_commits
rule { ~reject_unsigned_commits_available }.prevent :change_reject_unsigned_commits
rule { admin | (commit_committer_check_disabled_globally & can?(:maintainer_access)) }.policy do
rule { admin | (commit_committer_check_disabled_globally & commit_committer_check_disabled_by_group & can?(:maintainer_access)) }.policy do
enable :change_commit_committer_check
end
......
......@@ -7,7 +7,12 @@ module EE
override :execute
def execute
super.tap { |group| log_audit_event if group&.persisted? }
super.tap do |group|
next unless group&.persisted?
log_audit_event
create_push_rule_for_group
end
end
private
......@@ -36,6 +41,18 @@ module EE
action: :create
).for_group.security_event
end
def create_push_rule_for_group
return unless ::Feature.enabled?(:group_push_rules, group.root_ancestor) && group.feature_available?(:push_rules)
push_rule = group.predefined_push_rule
return unless push_rule
attributes = push_rule.attributes.symbolize_keys.except(:is_sample, :id)
PushRule.create(attributes.compact).tap do |push_rule|
group.update(push_rule: push_rule)
end
end
end
end
end
......@@ -56,13 +56,29 @@ module EE
predefined_push_rule = PushRule.find_by(is_sample: true)
if predefined_push_rule
if group_push_rule_available?
create_push_rule_from_group
elsif predefined_push_rule
log_info(predefined_push_rule)
push_rule = predefined_push_rule.dup.tap { |gh| gh.is_sample = false }
project.push_rule = push_rule
project.project_setting.update(push_rule: push_rule)
end
end
def group_push_rule_available?
return false unless project.group
return false unless ::Feature.enabled?(:group_push_rules, project.group.root_ancestor)
!!project.group.predefined_push_rule
end
def create_push_rule_from_group
push_rule_attributes = project.group.predefined_push_rule.attributes.except("id")
project.create_push_rule(push_rule_attributes.merge(is_sample: false))
project.project_setting.update(push_rule: project.push_rule)
end
# When using a project template from a Group, the new project can only be created
# under the template owner's group or subgroups
def validate_namespace_used_with_template(project, group_with_project_templates_id)
......
......@@ -3,4 +3,4 @@
.alert.alert-danger
- @push_rule.errors.full_messages.each do |msg|
%p= msg
= render "shared/push_rules/form", f: f
= render "shared/push_rules/form", f: f, context: nil
- page_title "Push Rules"
%h3
= _("Pre-defined push rules.")
%p.light
= _("Rules that define what git pushes are accepted for a project in this group. All newly created projects in this group will use these settings.")
%hr.clearfix
= form_for @push_rule, url: group_push_rules_path(@group), as: :push_rule, method: :put do |f|
- if @push_rule.errors.any?
.alert.alert-danger
- @push_rule.errors.full_messages.each do |msg|
%p= msg
= render "shared/push_rules/form", f: f, context: @group
- return unless can_modify_group_push_rules?(current_user, @group)
= nav_link(controller: :push_rules) do
= link_to edit_group_push_rules_path(@group), title: _('Push Rules') do
.nav-icon-container
= sprite_icon('push-rules')
%span.nav-item-name
= _('Push Rules')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :push_rules, html_options: { class: "fly-out-top-item" } ) do
= link_to edit_group_push_rules_path(@group), title: _('Push Rules') do
%strong.fly-out-top-item-name
= _('Push Rules')
......@@ -15,4 +15,4 @@
= form_for [@project.namespace.becomes(Namespace), @project, @push_rule] do |f|
= form_errors(@push_rule)
= render "shared/push_rules/form", f: f
= render "shared/push_rules/form", f: f, context: @project
- return unless push_rule.available?(:commit_committer_check)
- context = local_assigns.fetch(:context)
- return unless push_rule.available?(:commit_committer_check, object: context)
- form = local_assigns.fetch(:form)
- push_rule = local_assigns.fetch(:push_rule)
.form-check
= form.check_box :commit_committer_check, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :commit_committer_check), data: { qa_selector: 'committer_restriction_checkbox' }
= form.check_box :commit_committer_check, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :commit_committer_check, context), data: { qa_selector: 'committer_restriction_checkbox' }
= form.label :commit_committer_check, class: "label-bold form-check-label" do
= s_("PushRule|Committer restriction")
%p.text-muted
......
= render 'shared/push_rules/commit_committer_check_setting', form: f, push_rule: f.object
= render 'shared/push_rules/commit_committer_check_setting', form: f, push_rule: f.object, context: context
= render 'shared/push_rules/reject_unsigned_commits_setting', form: f, push_rule: f.object
= render 'shared/push_rules/reject_unsigned_commits_setting', form: f, push_rule: f.object, context: context
.form-check
= f.check_box :deny_delete_tag, class: "form-check-input", data: { qa_selector: 'deny_delete_tag_checkbox' }
......
- return unless push_rule.available?(:reject_unsigned_commits)
- context = local_assigns.fetch(:context)
- return unless push_rule.available?(:reject_unsigned_commits, object: context)
- form = local_assigns.fetch(:form)
- push_rule = local_assigns.fetch(:push_rule)
.form-check
= form.check_box :reject_unsigned_commits, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :reject_unsigned_commits), data: { qa_selector: 'reject_unsigned_commits_checkbox' }
= form.check_box :reject_unsigned_commits, class: "form-check-input", disabled: !can_change_push_rule?(form.object, :reject_unsigned_commits, context), data: { qa_selector: 'reject_unsigned_commits_checkbox' }
= form.label :reject_unsigned_commits, class: "label-bold form-check-label" do
Reject unsigned commits
%p.text-muted
......
---
title: Add push rules configuration for groups - frontend
merge_request: 31085
author:
type: added
......@@ -146,6 +146,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
end
resource :push_rules, only: [:edit, :update]
resource :saml_providers, path: 'saml', only: [:show, :create, :update] do
callback_methods = Rails.env.test? ? [:get, :post] : [:post]
match :callback, to: 'omniauth_callbacks#group_saml', via: callback_methods
......
# frozen_string_literal: true
require 'spec_helper'
describe Groups::PushRulesController do
let_it_be(:group) { create(:group, :private) }
let_it_be(:user) { create(:user) }
describe '#show' do
context 'when user is at least a maintainer' do
before do
sign_in(user)
group.add_maintainer(user)
end
context 'when push rules feature is disabled' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404 status' do
get :edit, params: { group_id: group }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when push rules feature is enabled' do
before do
stub_licensed_features(push_rules: true)
end
it 'returns 200 status' do
get :edit, params: { group_id: group }
expect(response).to have_gitlab_http_status(:ok)
end
end
end
context 'when user role is lower than maintainer' do
before do
sign_in(user)
group.add_developer(user)
end
context 'when push rules feature is disabled' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404 status' do
get :edit, params: { group_id: group }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when push rules feature is enabled' do
before do
stub_licensed_features(push_rules: true)
end
it 'returns 404 status' do
get :edit, params: { group_id: group }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe '#update' do
def do_update
patch :update, params: { group_id: group, push_rule: { prevent_secrets: true } }
end
context 'when user is at least a maintainer' do
before do
sign_in(user)
group.add_maintainer(user)
end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404 status' do
do_update
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'push rules licensed', :enable_admin_mode do
before do
stub_licensed_features(push_rules: true)
end
it 'updates the push rule' do
do_update
expect(response).to have_gitlab_http_status(:found)
expect(group.reload.push_rule.prevent_secrets).to be_truthy
end
shared_examples 'updateable setting' do |rule_attr, new_value|
it 'updates the setting' do
patch :update, params: { group_id: group, push_rule: { rule_attr => new_value } }
expect(group.reload.push_rule.public_send(rule_attr)).to eq(new_value)
end
end
shared_examples 'not updateable setting' do |rule_attr, new_value|
it 'does not update the setting' do
expect do
patch :update, params: { group_id: group, push_rule: { rule_attr => new_value } }
end.not_to change { group.reload.push_rule.public_send(rule_attr) }
end
end
shared_examples 'an updatable setting with global default' do |rule_attr|
context "when #{rule_attr} not specified on global level" do
before do
stub_licensed_features(rule_attr => true)
end
it_behaves_like 'updateable setting', rule_attr, true
end
context "when global setting #{rule_attr} is enabled" do
before do
stub_licensed_features(rule_attr => true)
create(:push_rule_sample, rule_attr => true)
end
it_behaves_like 'updateable setting', rule_attr, true
end
end
shared_examples 'a not updatable setting with global default' do |rule_attr|
context "when #{rule_attr} is disabled" do
before do
stub_licensed_features(rule_attr => false)
end
it_behaves_like 'not updateable setting', rule_attr, true
end
context "when global setting #{rule_attr} is enabled" do
before do
stub_licensed_features(rule_attr => true)
create(:push_rule_sample, rule_attr => true)
end
it_behaves_like 'not updateable setting', rule_attr, true
end
end
PushRule::SETTINGS_WITH_GLOBAL_DEFAULT.each do |rule_attr|
context "Updating #{rule_attr} rule" do
let(:push_rule_for_group) { create(:push_rule, rule_attr => false) }
before do
group.update!(push_rule_id: push_rule_for_group.id)
end
context 'as an admin' do
let(:user) { create(:admin) }
it_behaves_like 'an updatable setting with global default', rule_attr, updates: true
end
context 'as a maintainer user' do
before do
group.add_maintainer(user)
end
context "when global setting #{rule_attr} is disabled" do
before do
stub_licensed_features(rule_attr => false)
create(:push_rule_sample, rule_attr => true)
end
it_behaves_like 'updateable setting', rule_attr, true
end
context "when global setting #{rule_attr} is enabled" do
before do
stub_licensed_features(rule_attr => true)
create(:push_rule_sample, rule_attr => true)
end
it_behaves_like 'not updateable setting', rule_attr, true
end
end
context 'as a developer user' do
before do
group.add_developer(user)
end
it_behaves_like 'a not updatable setting with global default', rule_attr
end
end
end
end
end
context 'when user role is lower than maintainer' do
before do
sign_in(user)
group.add_developer(user)
end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
it 'returns 404 status' do
do_update
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'push rules licensed' do
before do
stub_licensed_features(push_rules: true)
end
it 'returns 404 status' do
do_update
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
end
......@@ -20,5 +20,9 @@ FactoryBot.define do
factory :push_rule_sample do
is_sample { true }
end
factory :push_rule_without_project do
project { nil }
end
end
end
......@@ -13,6 +13,7 @@ describe 'Group navbar' do
before do
group.add_maintainer(user)
stub_feature_flags(group_push_rules: false)
sign_in(user)
end
......@@ -154,7 +155,6 @@ describe 'Group navbar' do
nav_sub_items: [_('Package Registry')]
}
)
visit group_path(group)
end
......@@ -176,4 +176,27 @@ describe 'Group navbar' do
it_behaves_like 'verified navigation bar'
end
end
context 'when push_rules for groups are available' do
before do
group.add_owner(user)
stub_feature_flags(group_push_rules: true)
insert_after_nav_item(
_('Merge Requests'),
new_nav_item: {
nav_item: _('Push Rules'),
nav_sub_items: []
}
)
insert_after_nav_item(_('Members'), new_nav_item: settings_nav_item)
insert_after_nav_item(_('Settings'), new_nav_item: administration_nav_item)
visit group_path(group)
end
it_behaves_like 'verified navigation bar'
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Groups > Push Rules', :js do
let_it_be(:user) { create(:user) }
let_it_be(:push_rule) { create(:push_rule_without_project) }
let_it_be(:group) { create(:group, push_rule: push_rule) }
before do
group.add_maintainer(user)
sign_in(user)
end
push_rules_with_titles = {
reject_unsigned_commits: 'Reject unsigned commits',
commit_committer_check: 'Committer restriction'
}
push_rules_with_titles.each do |rule_attr, title|
describe "#{rule_attr} rule" do
context 'unlicensed' do
before do
stub_licensed_features(rule_attr => false)
end
it 'does not render the setting checkbox' do
visit edit_group_push_rules_path(group)
expect(page).not_to have_content(title)
end
end
context 'licensed' do
before do
stub_licensed_features(rule_attr => true)
end
it 'renders the setting checkbox' do
visit edit_group_push_rules_path(group)
expect(page).to have_content(title)
end
describe 'with GL.com plans' do
before do
stub_application_setting(check_namespace_plan: true)
end
context 'when disabled' do
it 'does not render the setting checkbox' do
create(:gitlab_subscription, :bronze, namespace: group)
visit edit_group_push_rules_path(group)
expect(page).not_to have_content(title)
end
end
context 'when enabled' do
it 'renders the setting checkbox' do
create(:gitlab_subscription, :gold, namespace: group)
visit edit_group_push_rules_path(group)
expect(page).to have_content(title)
end
end
end
end
end
end
end
......@@ -15,7 +15,6 @@ describe PushRule do
end
describe "Validation" do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_numericality_of(:max_file_size).is_greater_than_or_equal_to(0).only_integer }
it 'validates RE2 regex syntax' do
......
......@@ -728,6 +728,115 @@ describe GroupPolicy do
end
end
context 'commit_committer_check is not enabled by the current license' do
before do
stub_licensed_features(commit_committer_check: false)
end
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.not_to be_allowed(:read_commit_committer_check) }
end
context 'commit_committer_check is enabled by the current license' do
before do
stub_licensed_features(commit_committer_check: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'it is enabled on global level' do
before do
create(:push_rule_sample, commit_committer_check: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
end
end
context 'reject_unsigned_commits is not enabled by the current license' do
before do
stub_licensed_features(reject_unsigned_commits: false)
end
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.not_to be_allowed(:read_reject_unsigned_commits) }
end
context 'reject_unsigned_commits is enabled by the current license' do
before do
stub_licensed_features(reject_unsigned_commits: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'it is enabled on global level' do
before do
create(:push_rule_sample, reject_unsigned_commits: true)
end
context 'the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'the user is an admin', :enable_admin_mode do
let(:current_user) { admin }
it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
end
end
shared_examples 'analytics policy' do |action|
shared_examples 'policy by role' do |role|
context role do
......
......@@ -1126,6 +1126,46 @@ describe ProjectPolicy do
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'it is enabled on global level' do
before do
create(:push_rule_sample, commit_committer_check: true)
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
end
context 'it is enabled on group level' do
let(:push_rule) { create(:push_rule, commit_committer_check: true) }
let(:group) { create(:group, push_rule: push_rule) }
let(:project) { create(:project, namespace_id: group.id) }
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_commit_committer_check) }
it { is_expected.to be_allowed(:read_commit_committer_check) }
end
end
end
context 'reject_unsigned_commits is not enabled by the current license' do
......@@ -1144,19 +1184,59 @@ describe ProjectPolicy do
stub_licensed_features(reject_unsigned_commits: true)
end
context 'the user is a maintainer' do
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'the user is a developer' do
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'it is enabled on global level' do
before do
create(:push_rule_sample, reject_unsigned_commits: true)
end
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
end
context 'it is enabled on group level' do
let(:push_rule) { create(:push_rule_without_project, reject_unsigned_commits: true) }
let(:group) { create(:group, push_rule: push_rule) }
let(:project) { create(:project, namespace_id: group.id) }
context 'when the user is a maintainer' do
let(:current_user) { maintainer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
context 'when the user is a developer' do
let(:current_user) { developer }
it { is_expected.not_to be_allowed(:change_reject_unsigned_commits) }
it { is_expected.to be_allowed(:read_reject_unsigned_commits) }
end
end
end
context 'when timelogs report feature is enabled' do
......
......@@ -85,6 +85,61 @@ describe Groups::CreateService, '#execute' do
end
end
context 'creating group push rule' do
context 'when feature is available' do
before do
stub_licensed_features(push_rules: true)
end
context 'when there are push rules settings' do
let!(:sample) { create(:push_rule_sample) }
it 'uses the configured push rules settings' do
group = create_group(user, group_params)
expect(group.reload.push_rule).to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
context 'when feature flag is switched off' do
before do
stub_feature_flags(group_push_rules: false)
end
it 'does not create push rule' do
group = create_group(user, group_params)
expect(group.push_rule).to be_nil
end
end
end
context 'when there are not push rules settings' do
it 'is not creating the group push rule' do
group = create_group(user, group_params)
expect(group.push_rule).to be_nil
end
end
end
context 'when feature not is available' do
before do
stub_licensed_features(push_rules: false)
end
it 'ignores the group push rule' do
group = create_group(user, group_params)
expect(group.push_rule).to be_nil
end
end
end
def create_group(user, opts)
described_class.new(user, opts).execute
end
......
......@@ -186,33 +186,120 @@ describe Projects::CreateService, '#execute' do
end
end
context 'push rules sample' do
let!(:sample) { create(:push_rule_sample) }
context 'push rules' do
context 'with sample' do
let!(:sample) { create(:push_rule_sample) }
subject(:push_rule) { create_project(user, opts).push_rule }
before do
stub_licensed_features(push_rules: true)
end
it 'creates push rule from sample' do
is_expected.to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
subject(:push_rule) { create_project(user, opts).push_rule }
it 'creates association between project settings and push rule' do
project_setting = subject.project.project_setting
it 'creates push rule from sample' do
is_expected.to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
expect(project_setting.push_rule_id).to eq(subject.id)
it 'creates association between project settings and push rule' do
project_setting = subject.project.project_setting
expect(project_setting.push_rule_id).to eq(subject.id)
end
context 'push rules unlicensed' do
before do
stub_licensed_features(push_rules: false)
end
subject(:push_rule) { create_project(user, opts).push_rule }
it 'ignores the push rule sample' do
is_expected.to be_nil
end
end
end
context 'push rules unlicensed' do
context 'group push rules' do
before do
stub_licensed_features(push_rules: false)
stub_licensed_features(push_rules: true)
end
context 'project created within a group' do
let(:group) { create(:group) }
let(:opts) do
{
name: "GitLab",
namespace_id: group.id
}
end
before do
group.add_owner(user)
end
context 'when group has push rule defined' do
let(:push_rule) { create(:push_rule_without_project, force_push_regex: 'testing me') }
before do
group.update!(push_rule: push_rule)
group.add_owner(user)
end
it 'creates push rule from group push rule' do
project = create_project(user, opts)
project_push_rule = project.push_rule
expect(project_push_rule).to have_attributes(
force_push_regex: push_rule.force_push_regex,
deny_delete_tag: push_rule.deny_delete_tag,
delete_branch_regex: push_rule.delete_branch_regex,
commit_message_regex: push_rule.commit_message_regex,
is_sample: false
)
expect(project.project_setting.push_rule_id).to eq(project_push_rule.id)
end
context 'when feature flag is switched off' do
let!(:sample) { create(:push_rule_sample) }
before do
stub_feature_flags(group_push_rules: false)
end
it 'creates push rule from sample' do
expect(create_project(user, opts).push_rule).to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
end
end
context 'when group has not push rule defined' do
let!(:sample) { create(:push_rule_sample) }
it 'creates push rule from sample' do
expect(create_project(user, opts).push_rule).to have_attributes(
force_push_regex: sample.force_push_regex,
deny_delete_tag: sample.deny_delete_tag,
delete_branch_regex: sample.delete_branch_regex,
commit_message_regex: sample.commit_message_regex
)
end
end
end
end
it 'ignores the push rule sample' do
is_expected.to be_nil
context 'when there are no push rules' do
it 'does not create push rule' do
expect(create_project(user, opts).push_rule).to be_nil
end
end
end
......
......@@ -15697,6 +15697,9 @@ msgstr ""
msgid "Point to any links you like: documentation, built binaries, or other related materials. These can be internal or external links from your GitLab instance. Duplicate URLs are not allowed."
msgstr ""
msgid "Pre-defined push rules."
msgstr ""
msgid "Preferences"
msgstr ""
......@@ -18271,6 +18274,9 @@ msgstr ""
msgid "Rook"
msgstr ""
msgid "Rules that define what git pushes are accepted for a project in this group. All newly created projects in this group will use these settings."
msgstr ""
msgid "Rules that define what git pushes are accepted for a project. All newly created projects will use these settings."
msgstr ""
......
......@@ -10,7 +10,42 @@ describe 'Group navbar' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:structure) do
[
{
nav_item: _('Group overview'),
nav_sub_items: [
_('Details'),
_('Activity')
]
},
{
nav_item: _('Issues'),
nav_sub_items: [
_('List'),
_('Board'),
_('Labels'),
_('Milestones')
]
},
{
nav_item: _('Merge Requests'),
nav_sub_items: []
},
{
nav_item: _('Kubernetes'),
nav_sub_items: []
},
(analytics_nav_item if Gitlab.ee?),
{
nav_item: _('Members'),
nav_sub_items: []
}
]
end
before do
stub_feature_flags(group_push_rules: false)
group.add_maintainer(user)
sign_in(user)
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