Commit 72b27b3f authored by Tetiana Chupryna's avatar Tetiana Chupryna

Merge branch '343494-change-ci-minutes-policy' into 'master'

Only allow owners & admins to access CI minutes

See merge request gitlab-org/gitlab!77118
parents 00f9fec1 723c7229
...@@ -3,15 +3,13 @@ ...@@ -3,15 +3,13 @@
module Ci module Ci
module Minutes module Minutes
class Context class Context
delegate :shared_runners_minutes_limit_enabled?, to: :level delegate :shared_runners_minutes_limit_enabled?, to: :namespace
delegate :name, to: :namespace, prefix: true delegate :name, to: :namespace, prefix: true
attr_reader :level attr_reader :namespace
def initialize(project, namespace, tracking_strategy: nil) def initialize(project, namespace, tracking_strategy: nil)
@project = project
@namespace = project&.shared_runners_limit_namespace || namespace @namespace = project&.shared_runners_limit_namespace || namespace
@level = project || namespace
@tracking_strategy = tracking_strategy @tracking_strategy = tracking_strategy
end end
...@@ -21,8 +19,6 @@ module Ci ...@@ -21,8 +19,6 @@ module Ci
private private
attr_reader :project, :namespace
def quota def quota
@quota ||= ::Ci::Minutes::Quota.new(namespace, tracking_strategy: @tracking_strategy) @quota ||= ::Ci::Minutes::Quota.new(namespace, tracking_strategy: @tracking_strategy)
end end
......
...@@ -17,10 +17,10 @@ module Ci ...@@ -17,10 +17,10 @@ module Ci
def show?(current_user, cookies = false) def show?(current_user, cookies = false)
return false unless @stage return false unless @stage
return false unless @context.level return false unless @context.namespace
return false if alert_has_been_dismissed?(cookies) return false if alert_has_been_dismissed?(cookies)
Ability.allowed?(current_user, :read_ci_minutes_quota, @context.level) Ability.allowed?(current_user, :read_ci_minutes_quota, @context.namespace)
end end
def text def text
......
...@@ -298,7 +298,6 @@ module EE ...@@ -298,7 +298,6 @@ module EE
rule { developer }.policy do rule { developer }.policy do
enable :create_wiki enable :create_wiki
enable :admin_merge_request enable :admin_merge_request
enable :read_ci_minutes_quota
enable :read_group_audit_events enable :read_group_audit_events
end end
...@@ -316,6 +315,7 @@ module EE ...@@ -316,6 +315,7 @@ module EE
enable :read_group_compliance_dashboard enable :read_group_compliance_dashboard
enable :read_group_credentials_inventory enable :read_group_credentials_inventory
enable :admin_group_credentials_inventory enable :admin_group_credentials_inventory
enable :read_ci_minutes_quota
end end
rule { (admin | owner) & group_merge_request_approval_settings_enabled }.policy do rule { (admin | owner) & group_merge_request_approval_settings_enabled }.policy do
......
...@@ -183,11 +183,14 @@ module EE ...@@ -183,11 +183,14 @@ module EE
enable :create_vulnerability_feedback enable :create_vulnerability_feedback
enable :destroy_vulnerability_feedback enable :destroy_vulnerability_feedback
enable :update_vulnerability_feedback enable :update_vulnerability_feedback
enable :read_ci_minutes_quota
enable :admin_feature_flags_issue_links enable :admin_feature_flags_issue_links
enable :read_project_audit_events enable :read_project_audit_events
end end
rule { can?(:owner_access) }.policy do
enable :read_ci_minutes_quota
end
rule { can?(:developer_access) & iterations_available }.policy do rule { can?(:developer_access) & iterations_available }.policy do
enable :create_iteration enable :create_iteration
enable :admin_iteration enable :admin_iteration
......
...@@ -13,153 +13,166 @@ RSpec.describe 'CI shared runner limits' do ...@@ -13,153 +13,166 @@ RSpec.describe 'CI shared runner limits' do
let!(:job) { create(:ci_build, pipeline: pipeline) } let!(:job) { create(:ci_build, pipeline: pipeline) }
before do before do
group.add_user(user, membership_level)
sign_in(user) sign_in(user)
end end
context 'when on a project related page' do where(:membership_level, :visible) do
before do :owner | true
group.add_developer(user) :developer | false
end end
where(:case_name, :percent_threshold, :minutes_limit, :minutes_used) do with_them do
'warning level' | 30 | 1000 | 800 context 'when on a project related page' do
'danger level' | 5 | 1000 | 980 where(:membership_level, :visible) do
end :owner | true
:developer | false
end
with_them do before do
context "when there is a notification and minutes still exist", :js do group.add_user(user, membership_level)
let(:message) do end
"#{group.name} namespace has #{percent_threshold}% or less Shared Runner Pipeline minutes remaining. " \
"After it runs out, no new jobs or pipelines in its projects will run."
end
before do where(:case_name, :percent_threshold, :minutes_limit, :minutes_used) do
group.update!(shared_runners_minutes_limit: minutes_limit) 'warning level' | 30 | 1000 | 800
allow_any_instance_of(::Ci::Minutes::Quota).to receive(:total_minutes_used).and_return(minutes_used) 'danger level' | 5 | 1000 | 980
end end
it 'displays a warning message on pipelines page' do with_them do
visit project_pipelines_path(project) context "when there is a notification and minutes still exist", :js do
let(:message) do
"#{group.name} namespace has #{percent_threshold}% or less Shared Runner Pipeline minutes remaining. " \
"After it runs out, no new jobs or pipelines in its projects will run."
end
before do
group.update!(shared_runners_minutes_limit: minutes_limit)
allow_any_instance_of(::Ci::Minutes::Quota).to receive(:total_minutes_used).and_return(minutes_used)
end
it 'displays a warning message on pipelines page' do
visit project_pipelines_path(project)
alerts_according_to_role(visible: visible, message: message)
end
it 'displays a warning message on project homepage' do
visit project_path(project)
expect_quota_exceeded_alert(message) alerts_according_to_role(visible: visible, message: message)
end
it 'displays a warning message on a job page' do
visit project_job_path(project, job)
alerts_according_to_role(visible: visible, message: message)
end
end
end
context 'when limit is exceeded', :js do
let(:group) { create(:group, :with_used_build_minutes_limit) }
let(:message) do
"#{group.name} namespace has exceeded its pipeline minutes quota. " \
"Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
end end
it 'displays a warning message on project homepage' do it 'displays a warning message on project homepage' do
visit project_path(project) visit project_path(project)
expect_quota_exceeded_alert(message) alerts_according_to_role(visible: visible, message: message)
end
it 'displays a warning message on pipelines page' do
visit project_pipelines_path(project)
alerts_according_to_role(visible: visible, message: message)
end end
it 'displays a warning message on a job page' do it 'displays a warning message on a job page' do
visit project_job_path(project, job) visit project_job_path(project, job)
expect_quota_exceeded_alert(message) alerts_according_to_role(visible: visible, message: message)
end end
end end
end
context 'when limit is exceeded', :js do context 'when limit not yet exceeded' do
let(:group) { create(:group, :with_used_build_minutes_limit) } let(:group) { create(:group, :with_not_used_build_minutes_limit) }
let(:message) do
"#{group.name} namespace has exceeded its pipeline minutes quota. " \
"Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
end
it 'displays a warning message on project homepage' do it 'does not display a warning message on project homepage' do
visit project_path(project) visit project_path(project)
expect_quota_exceeded_alert(message) expect_no_quota_exceeded_alert
end end
it 'displays a warning message on pipelines page' do it 'does not display a warning message on pipelines page' do
visit project_pipelines_path(project) visit project_pipelines_path(project)
expect_quota_exceeded_alert(message) expect_no_quota_exceeded_alert
end end
it 'displays a warning message on a job page' do it 'displays a warning message on a job page' do
visit project_job_path(project, job) visit project_job_path(project, job)
expect_quota_exceeded_alert(message) expect_no_quota_exceeded_alert
end
end end
end end
context 'when limit not yet exceeded' do context 'when on a group related page' do
let(:group) { create(:group, :with_not_used_build_minutes_limit) } where(:case_name, :percent_threshold, :minutes_limit, :minutes_used) do
'warning level' | 30 | 1000 | 800
it 'does not display a warning message on project homepage' do 'danger level' | 5 | 1000 | 980
visit project_path(project)
expect_no_quota_exceeded_alert
end end
it 'does not display a warning message on pipelines page' do with_them do
visit project_pipelines_path(project) context "when there is a notification and minutes still exist", :js do
let(:message) do
"#{group.name} namespace has #{percent_threshold}% or less Shared Runner Pipeline minutes remaining. " \
"After it runs out, no new jobs or pipelines in its projects will run."
end
expect_no_quota_exceeded_alert before do
end group.update!(shared_runners_minutes_limit: minutes_limit)
allow_any_instance_of(::Ci::Minutes::Quota).to receive(:total_minutes_used).and_return(minutes_used)
end
it 'displays a warning message on a job page' do it 'displays a warning message on group information page' do
visit project_job_path(project, job) visit group_path(group)
expect_no_quota_exceeded_alert alerts_according_to_role(visible: visible, message: message)
end
end
end end
end
end
context 'when on a group related page' do context 'when limit is exceeded', :js do
before do let(:group) { create(:group, :with_used_build_minutes_limit) }
group.add_owner(user)
end
where(:case_name, :percent_threshold, :minutes_limit, :minutes_used) do
'warning level' | 30 | 1000 | 800
'danger level' | 5 | 1000 | 980
end
with_them do
context "when there is a notification and minutes still exist", :js do
let(:message) do let(:message) do
"#{group.name} namespace has #{percent_threshold}% or less Shared Runner Pipeline minutes remaining. " \ "#{group.name} namespace has exceeded its pipeline minutes quota. " \
"After it runs out, no new jobs or pipelines in its projects will run." "Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
end
before do
group.update!(shared_runners_minutes_limit: minutes_limit)
allow_any_instance_of(::Ci::Minutes::Quota).to receive(:total_minutes_used).and_return(minutes_used)
end end
it 'displays a warning message on group information page' do it 'displays a warning message on group information page' do
visit group_path(group) visit group_path(group)
expect_quota_exceeded_alert(message) alerts_according_to_role(visible: visible, message: message)
end end
end end
end
context 'when limit is exceeded', :js do context 'when limit not yet exceeded' do
let(:group) { create(:group, :with_used_build_minutes_limit) } let(:group) { create(:group, :with_not_used_build_minutes_limit) }
let(:message) do
"#{group.name} namespace has exceeded its pipeline minutes quota. " \
"Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
end
it 'displays a warning message on group information page' do it 'does not display a warning message on group information page' do
visit group_path(group) visit group_path(group)
expect_quota_exceeded_alert(message) expect_no_quota_exceeded_alert
end
end end
end end
end
context 'when limit not yet exceeded' do def alerts_according_to_role(visible: false, message: '')
let(:group) { create(:group, :with_not_used_build_minutes_limit) } visible ? expect_quota_exceeded_alert(message) : expect_no_quota_exceeded_alert
it 'does not display a warning message on group information page' do
visit group_path(group)
expect_no_quota_exceeded_alert
end
end
end end
def expect_quota_exceeded_alert(message) def expect_quota_exceeded_alert(message)
......
...@@ -9,7 +9,7 @@ RSpec.describe Ci::Minutes::Context do ...@@ -9,7 +9,7 @@ RSpec.describe Ci::Minutes::Context do
describe 'delegation' do describe 'delegation' do
subject { described_class.new(project, group) } subject { described_class.new(project, group) }
it { is_expected.to delegate_method(:shared_runners_minutes_limit_enabled?).to(:level) } it { is_expected.to delegate_method(:shared_runners_minutes_limit_enabled?).to(:namespace) }
it { is_expected.to delegate_method(:name).to(:namespace).with_prefix } it { is_expected.to delegate_method(:name).to(:namespace).with_prefix }
end end
end end
...@@ -189,7 +189,7 @@ RSpec.describe Ci::Minutes::Notification do ...@@ -189,7 +189,7 @@ RSpec.describe Ci::Minutes::Notification do
context 'when at project level' do context 'when at project level' do
context 'when eligible to see notifications' do context 'when eligible to see notifications' do
before do before do
group.add_developer(user) group.add_owner(user)
end end
describe '#show?' do describe '#show?' do
...@@ -217,12 +217,22 @@ RSpec.describe Ci::Minutes::Notification do ...@@ -217,12 +217,22 @@ RSpec.describe Ci::Minutes::Notification do
subject { described_class.new(injected_project, nil) } subject { described_class.new(injected_project, nil) }
end end
end end
context 'when user is not in the correct role' do
before do
group.add_developer user
end
it_behaves_like 'not eligible to see notifications' do
subject { described_class.new(injected_project, nil) }
end
end
end end
context 'when at namespace level' do context 'when at namespace level' do
context 'when eligible to see notifications' do context 'when eligible to see notifications' do
before do before do
group.add_developer(user) group.add_owner(user)
end end
context 'with a project that has runners enabled inside namespace' do context 'with a project that has runners enabled inside namespace' do
...@@ -259,5 +269,15 @@ RSpec.describe Ci::Minutes::Notification do ...@@ -259,5 +269,15 @@ RSpec.describe Ci::Minutes::Notification do
subject { described_class.new(injected_project, nil) } subject { described_class.new(injected_project, nil) }
end end
end end
context 'when user is not in the correct role' do
before do
group.add_developer user
end
it_behaves_like 'not eligible to see notifications' do
subject { described_class.new(injected_project, nil) }
end
end
end end
end end
...@@ -1349,8 +1349,8 @@ RSpec.describe GroupPolicy do ...@@ -1349,8 +1349,8 @@ RSpec.describe GroupPolicy do
where(:role, :admin_mode, :allowed) do where(:role, :admin_mode, :allowed) do
:guest | nil | false :guest | nil | false
:reporter | nil | false :reporter | nil | false
:developer | nil | true :developer | nil | false
:maintainer | nil | true :maintainer | nil | false
:owner | nil | true :owner | nil | true
:admin | true | true :admin | true | true
:admin | false | false :admin | false | false
......
...@@ -1542,8 +1542,8 @@ RSpec.describe ProjectPolicy do ...@@ -1542,8 +1542,8 @@ RSpec.describe ProjectPolicy do
where(:role, :admin_mode, :allowed) do where(:role, :admin_mode, :allowed) do
:guest | nil | false :guest | nil | false
:reporter | nil | false :reporter | nil | false
:developer | nil | true :developer | nil | false
:maintainer | nil | true :maintainer | nil | false
:owner | nil | true :owner | nil | true
:admin | false | false :admin | false | false
:admin | true | true :admin | true | true
......
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