Optimize permission checks in CI build policies by removing jailbreak
What does this MR do and why?
This MR optimizes permission checks in CI build policies by simplifying the authorization logic for protected refs and protected environments.
Problem:
The getPipelineDetails GraphQL query was experiencing high CPU time. Profiling revealed that permission calculations for build operations were spending significant time evaluating complex nested conditions, particularly the jailbreak pattern for protected refs.
Solution:
- Remove the separate
~can?(:jailbreak) & protected_refpolicy block fromCi::BuildPolicy - Simplify the
reporter_has_access_to_protected_environmentlogic by removing thejailbreakenable/prevent pattern and using it directly in prevention rules
References
Related to #549069
Screenshots or screen recordings
Permission calculation trees
With a pipeline having many jobs.
ActiveRecord::Base.logger = nil
user = User.find(1)
pipeline = Ci::Pipeline.find(569)
Gitlab::SafeRequestStore.ensure_request_store do
pipeline.builds.each do |build|
policy = DeclarativePolicy.policy_for(user, build, cache: ::Gitlab::SafeRequestStore.storage)
policy.runner(:cancel_build).debug
puts "..."
end
end
This will print the calculation trees of cancel_build of each build.
Before
...
- [0] prevent when placeholder_user ((@root : Ci::Build/844))
- [0] prevent when import_user ((@root : Ci::Build/844))
+ [0] enable when can?(:developer_access) ((@root : Project/20))
- [0] prevent when builds_disabled ((@root : Project/20))
- [0] prevent when repository_disabled ((@root : Project/20))
- [0] prevent when read_only ((@root : Project/20))
- [0] prevent when all?(ci_cancellation_maintainers_only, ~can?(:maintainer_access)) ((@root : Project/20))
- [0] prevent when ci_cancellation_no_one ((@root : Project/20))
- [0] prevent when ~in_current_organization ((@root : Project/20))
- [0] prevent when placeholder_user ((@root : Project/20))
- [0] prevent when import_user ((@root : Project/20))
- [0] prevent when all?(anonymous, ~public_project) ((@root : Project/20))
- [0] prevent when ~project_allowed_for_job_token ((@root : Project/20))
- [0] prevent when visual_review_bot ((@root : Project/20))
- [0] prevent when user_banned_from_namespace ((@root : Project/20))
- [0] prevent when all?(duo_workflow_token, ~duo_features_enabled) ((@root : Project/20))
- [0] prevent when builds_disabled ((@root : Project/20))
- [0] prevent when repository_disabled ((@root : Project/20))
- [7] prevent when has_outdated_deployment ((@root : Ci::Build/844))
- [7] prevent when all?(ip_enforcement_prevents_access, ~admin, ~auditor) ((@root : Project/20))
- [14] prevent when ~in_current_organization ((@root : Ci::Build/844))
- [40] prevent when all?(~can?(:jailbreak), protected_environment) ((@root : Ci::Build/844))
- [40] prevent when all?(~can?(:jailbreak), protected_ref) ((@root : Ci::Build/844))
After
...
- [0] prevent when placeholder_user ((@root : Ci::Build/841))
- [0] prevent when import_user ((@root : Ci::Build/841))
+ [0] enable when can?(:developer_access) ((@root : Project/20))
- [0] prevent when builds_disabled ((@root : Project/20))
- [0] prevent when repository_disabled ((@root : Project/20))
- [0] prevent when read_only ((@root : Project/20))
- [0] prevent when all?(ci_cancellation_maintainers_only, ~can?(:maintainer_access)) ((@root : Project/20))
- [0] prevent when ci_cancellation_no_one ((@root : Project/20))
- [0] prevent when ~in_current_organization ((@root : Project/20))
- [0] prevent when placeholder_user ((@root : Project/20))
- [0] prevent when import_user ((@root : Project/20))
- [0] prevent when all?(anonymous, ~public_project) ((@root : Project/20))
- [0] prevent when ~project_allowed_for_job_token ((@root : Project/20))
- [0] prevent when visual_review_bot ((@root : Project/20))
- [0] prevent when user_banned_from_namespace ((@root : Project/20))
- [0] prevent when all?(duo_workflow_token, ~duo_features_enabled) ((@root : Project/20))
- [0] prevent when builds_disabled ((@root : Project/20))
- [0] prevent when repository_disabled ((@root : Project/20))
- [7] prevent when has_outdated_deployment ((@root : Ci::Build/841))
- [7] prevent when all?(ip_enforcement_prevents_access, ~admin, ~auditor) ((@root : Project/20))
- [14] prevent when ~in_current_organization ((@root : Ci::Build/841))
- [28] prevent when all?(~reporter_has_access_to_protected_environment, protected_environment) ((@root : Ci::Build/841))
- [14] prevent when all?(~reporter_has_access_to_protected_environment, protected_ref) ((@root : Ci::Build/841))
GraphQL Query
Click to expand
require 'benchmark'
ActiveRecord::Base.logger = nil
query = <<~QUERY
query getPipelineDetails($projectPath: ID!, $iid: ID!) {
project(fullPath: $projectPath) {
pipeline(iid: $iid) {
stages {
nodes {
groups {
nodes {
jobs {
nodes {
name
status: detailedStatus {
hasDetails
action {
buttonTitle
}
}
}
}
}
}
}
}
}
}
}
QUERY
variables = {
"projectPath" => "root/gitlab-clone",
"iid" => 3
}
context = {
current_user: User.find(1),
is_sessionless_user: false,
current_organization: Organizations::Organization.find(1)
}
operation_name = "getPipelineDetails"
n = 50
Benchmark.bm do |benchmark|
2.times do
Gitlab::SafeRequestStore.ensure_request_store do
# warm up
GitlabSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
end
end
benchmark.report("NEW") do
n.times do
Gitlab::SafeRequestStore.ensure_request_store do
GitlabSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
end
end
end
end
user system total real
OLD 8.305731 0.041631 8.347362 ( 8.375858)
user system total real
NEW 7.416806 0.070538 7.487344 ( 7.546441)
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Edited by Furkan Ayhan