Skip to content
Snippets Groups Projects
Verified Commit 8935a7b8 authored by Alex Buijs's avatar Alex Buijs
Browse files

Allow managing group CI/CD variables

For user with a custom role with the admin_cicd_variables custom ability
parent 8eeb8e7d
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !143369. Comments created here will be created in the context of that merge request.
Showing
with 123 additions and 39 deletions
......@@ -5,6 +5,8 @@ import UpdateSharedRunnersForm from './components/shared_runners_form.vue';
export default (containerId = 'update-shared-runners-form') => {
const containerEl = document.getElementById(containerId);
if (!containerEl) return null;
const {
groupId,
groupName,
......
......@@ -5,7 +5,7 @@ module Settings
class CiCdController < Groups::ApplicationController
layout 'group_settings'
skip_cross_project_access_check :show
before_action :authorize_admin_group!
before_action :authorize_admin_cicd_variables!
before_action :authorize_update_max_artifacts_size!, only: [:update]
before_action :define_variables, only: [:show]
before_action :push_licensed_features, only: [:show]
......
......@@ -2,7 +2,7 @@
module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_group!
before_action :authorize_admin_cicd_variables!
skip_cross_project_access_check :show, :update
......@@ -52,10 +52,6 @@ def group_variables_params
def variable_params_attributes
%i[id variable_type key description secret_value protected masked raw _destroy]
end
def authorize_admin_build!
return render_404 unless can?(current_user, :admin_build, group)
end
end
end
......
......@@ -242,7 +242,7 @@ class GroupType < NamespaceType
Types::Ci::GroupVariableType.connection_type,
null: true,
description: "List of the group's CI/CD variables.",
authorize: :admin_group,
authorize: :admin_cicd_variables,
resolver: Resolvers::Ci::VariablesResolver
field :runners, Types::Ci::RunnerType.connection_type,
......
......@@ -246,6 +246,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :update_runners_registration_token
enable :owner_access
enable :update_git_access_protocol
enable :admin_cicd_variables
enable :read_billing
enable :edit_billing
......
......@@ -19,38 +19,40 @@
.settings-content
= render 'groups/settings/ci_cd/form', group: @group
%section.settings#ci-variables.no-animate{ class: ('expanded' if expanded) }
.settings-header
= render 'ci/variables/header', expanded: expanded
.settings-content
= render 'ci/variables/index', save_endpoint: group_variables_path
- if can?(current_user, :admin_cicd_variables, @group)
%section.settings#ci-variables.no-animate{ class: ('expanded' if expanded) }
.settings-header
= render 'ci/variables/header', expanded: expanded
.settings-content
= render 'ci/variables/index', save_endpoint: group_variables_path
%section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Runners')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p.gl-text-secondary
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
= link_to s_('What is GitLab Runner?'), 'https://docs.gitlab.com/runner/', target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'groups/runners/settings'
- if can?(current_user, :admin_group, @group)
%section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Runners')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p.gl-text-secondary
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
= link_to s_('What is GitLab Runner?'), 'https://docs.gitlab.com/runner/', target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'groups/runners/settings'
%section.settings#auto-devops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Auto DevOps')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p.gl-text-secondary
- auto_devops_url = help_page_path('topics/autodevops/index')
- quickstart_url = help_page_path('topics/autodevops/cloud_deployments/auto_devops_with_gke')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
- quickstart_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: quickstart_url }
= html_escape(s_('AutoDevOps|%{auto_devops_start}Automate building, testing, and deploying%{auto_devops_end} your applications based on your continuous integration and delivery configuration. %{quickstart_start}How do I get started?%{quickstart_end}')) % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe, quickstart_start: quickstart_start, quickstart_end: '</a>'.html_safe }
%section.settings#auto-devops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Auto DevOps')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p.gl-text-secondary
- auto_devops_url = help_page_path('topics/autodevops/index')
- quickstart_url = help_page_path('topics/autodevops/cloud_deployments/auto_devops_with_gke')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
- quickstart_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: quickstart_url }
= html_escape(s_('AutoDevOps|%{auto_devops_start}Automate building, testing, and deploying%{auto_devops_end} your applications based on your continuous integration and delivery configuration. %{quickstart_start}How do I get started?%{quickstart_end}')) % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe, quickstart_start: quickstart_start, quickstart_end: '</a>'.html_safe }
.settings-content
= render 'groups/settings/ci_cd/auto_devops_form', group: @group
.settings-content
= render 'groups/settings/ci_cd/auto_devops_form', group: @group
= render_if_exists 'groups/settings/ci_cd/protected_environments', expanded: expanded
= render_if_exists 'groups/settings/ci_cd/protected_environments', expanded: expanded
......@@ -7,7 +7,7 @@ class NamespaceCiCdSettingsUpdate < BaseMutation
include ResolvesNamespace
authorize :admin_namespace
authorize :admin_cicd_variables
argument :allow_stale_runner_pruning, GraphQL::Types::Boolean,
required: false,
......
......@@ -236,6 +236,15 @@ module GroupPolicy
).has_ability?
end
desc "Custom role on group that enables admin CI/CD variables"
condition(:role_enables_admin_cicd_variables) do
::Auth::MemberRoleAbilityLoader.new(
user: @user,
resource: @subject,
ability: :admin_cicd_variables
).has_ability?
end
rule { owner & unique_project_download_limit_enabled }.policy do
enable :ban_group_member
end
......@@ -541,6 +550,10 @@ module GroupPolicy
enable :admin_member_role
end
rule { custom_roles_allowed & role_enables_admin_cicd_variables }.policy do
enable :admin_cicd_variables
end
rule { can?(:read_group_security_dashboard) }.policy do
enable :create_vulnerability_export
enable :read_security_resource
......
......@@ -30,6 +30,9 @@ def configure_menu_items
# They only get the Repository settings which only show the Push Rules section for maintainers.
add_item(repository_menu_item) if can?(context.current_user, :change_push_rules, context.group)
# Managing CI/CD settings is a custom ability independent of the access level.
add_item(ci_cd_menu_item) if can?(context.current_user, :admin_cicd_variables, context.group)
add_item(billing_menu_item) if can?(context.current_user, :read_billing, context.group)
end
end
......
......@@ -294,5 +294,27 @@
end
end
end
context 'for a user with the `admin_cicd_variables` custom ability', feature_category: :permissions do
let_it_be(:user) { create(:user) }
let_it_be(:role) { create(:member_role, :guest, namespace: group, admin_cicd_variables: true) }
let_it_be(:member) { create(:group_member, :guest, member_role: role, user: user, group: group) }
subject { menu.renderable_items.find { |e| e.item_id == item_id } }
before do
stub_licensed_features(custom_roles: true)
end
describe 'CI/CD menu item' do
let(:item_id) { :ci_cd }
it { is_expected.to be_present }
it 'does not show any other menu items' do
expect(menu.renderable_items.length).to equal(1)
end
end
end
end
end
......@@ -3214,6 +3214,13 @@ def create_member_role(member, abilities = member_role_abilities)
it { is_expected.to be_disallowed(*allowed_abilities) }
end
end
context 'for a custom role with the `admin_cicd_variables` ability' do
let(:member_role_abilities) { { admin_cicd_variables: true } }
let(:allowed_abilities) { [:admin_cicd_variables] }
it_behaves_like 'custom roles abilities'
end
end
context 'for :read_limit_alert' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User with admin_cicd_variables custom role', feature_category: :secrets_management do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:role) { create(:member_role, :guest, namespace: group, admin_cicd_variables: true) }
let_it_be(:member) { create(:group_member, :guest, member_role: role, user: user, group: group) }
before do
stub_licensed_features(custom_roles: true)
sign_in(user)
end
describe Groups::VariablesController do
describe '#show' do
it 'user can view CI/CD variables' do
get group_variables_path(group, format: :json)
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to have_key('variables')
end
end
describe '#update' do
it 'user can create CI/CD variables' do
params = { variables_attributes: [{ key: 'new_key', secret_value: 'dummy_value' }] }
put group_variables_path(group, params: params, format: :json)
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)['variables'][0])
.to include('key' => 'new_key', 'value' => 'dummy_value')
end
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