Module: API::Helpers
- Includes:
- Caching, Pagination, PaginationStrategies, Gitlab::Allowable, Gitlab::Ci::Artifacts::Logger, Gitlab::RackLoadBalancingHelpers, Gitlab::Utils, Gitlab::Utils::StrongMemoize
- Defined in:
- lib/api/helpers.rb,
lib/api/helpers/caching.rb,
lib/api/helpers/unidiff.rb,
lib/api/helpers/version.rb,
lib/api/helpers/open_api.rb,
lib/api/helpers/pagination.rb,
lib/api/helpers/award_emoji.rb,
lib/api/helpers/kas_helpers.rb,
lib/api/helpers/presentable.rb,
lib/api/helpers/blob_helpers.rb,
lib/api/helpers/packages/npm.rb,
lib/api/helpers/rate_limiter.rb,
lib/api/helpers/label_helpers.rb,
lib/api/helpers/notes_helpers.rb,
lib/api/helpers/users_helpers.rb,
lib/api/helpers/wikis_helpers.rb,
lib/api/helpers/authentication.rb,
lib/api/helpers/badges_helpers.rb,
lib/api/helpers/common_helpers.rb,
lib/api/helpers/events_helpers.rb,
lib/api/helpers/groups_helpers.rb,
lib/api/helpers/issues_helpers.rb,
lib/api/helpers/packages/maven.rb,
lib/api/helpers/packages/nuget.rb,
lib/api/helpers/search_helpers.rb,
lib/api/helpers/graphql_helpers.rb,
lib/api/helpers/headers_helpers.rb,
lib/api/helpers/members_helpers.rb,
lib/api/helpers/internal_helpers.rb,
lib/api/helpers/packages_helpers.rb,
lib/api/helpers/projects_helpers.rb,
lib/api/helpers/settings_helpers.rb,
lib/api/helpers/snippets_helpers.rb,
lib/api/helpers/custom_attributes.rb,
lib/api/helpers/variables_helpers.rb,
lib/api/helpers/web_hooks_helpers.rb,
lib/api/helpers/discussions_helpers.rb,
lib/api/helpers/file_upload_helpers.rb,
lib/api/helpers/integrations_helpers.rb,
lib/api/helpers/import_github_helpers.rb,
lib/api/helpers/pagination_strategies.rb,
lib/api/helpers/merge_requests_helpers.rb,
lib/api/helpers/protected_tags_helpers.rb,
lib/api/helpers/remote_mirrors_helpers.rb,
lib/api/helpers/performance_bar_helpers.rb,
lib/api/helpers/resource_events_helpers.rb,
lib/api/helpers/kubernetes/agent_helpers.rb,
lib/api/helpers/project_snapshots_helpers.rb,
lib/api/helpers/related_resources_helpers.rb,
lib/api/helpers/bulk_imports/audit_helpers.rb,
lib/api/helpers/container_registry_helpers.rb,
lib/api/helpers/packages/conan/api_helpers.rb,
lib/api/helpers/protected_branches_helpers.rb,
lib/api/helpers/snippets/http_response_map.rb,
lib/api/helpers/authz/postfiltering_helpers.rb,
lib/api/helpers/packages/basic_auth_helpers.rb,
lib/api/helpers/commits_body_uploader_helper.rb,
lib/api/helpers/personal_access_tokens_helpers.rb,
lib/api/helpers/packages_manager_clients_helpers.rb,
lib/api/helpers/packages/dependency_proxy_helpers.rb,
lib/api/helpers/packages/maven/basic_auth_helpers.rb,
lib/api/helpers/project_stats_refresh_conflicts_helpers.rb
Defined Under Namespace
Modules: Authentication, Authz, AwardEmoji, BadgesHelpers, BlobHelpers, BulkImports, Caching, CommitsBodyUploaderHelper, CommonHelpers, ContainerRegistryHelpers, CustomAttributes, DiscussionsHelpers, EventsHelpers, FileUploadHelpers, GraphqlHelpers, GroupsHelpers, HeadersHelpers, ImportGithubHelpers, IntegrationsHelpers, InternalHelpers, IssuesHelpers, KasHelpers, Kubernetes, LabelHelpers, MembersHelpers, MergeRequestsHelpers, NotesHelpers, OpenApi, Packages, PackagesHelpers, PackagesManagerClientsHelpers, Pagination, PaginationStrategies, PerformanceBarHelpers, PersonalAccessTokensHelpers, Presentable, ProjectSnapshotsHelpers, ProjectStatsRefreshConflictsHelpers, ProjectsHelpers, ProtectedBranchesHelpers, ProtectedTagsHelpers, RateLimiter, RelatedResourcesHelpers, RemoteMirrorsHelpers, ResourceEventsHelpers, SearchHelpers, SettingsHelpers, Snippets, SnippetsHelpers, Unidiff, UsersHelpers, VariablesHelpers, WebHooksHelpers, WikisHelpers
Classes: Version
Constant Summary
collapse
"HTTP_SUDO"
"Gitlab-Shared-Secret"
- SUDO_PARAM =
:sudo
- API_USER_ENV =
'gitlab.api.user'
- API_EXCEPTION_ENV =
'gitlab.api.exception'
- API_RESPONSE_STATUS_CODE =
'gitlab.api.response_status_code'
- INTEGER_ID_REGEX =
/^-?\d+$/
- TOKEN_SCOPES_TO_AUDIT =
ai_workflows scope is used by Duo Workflow which is an AI automation tool, requests authenticated by token with this scope are audited to keep track of all actions done by Duo Workflow.
[:ai_workflows].freeze
Constants included
from Caching
Caching::DEFAULT_CACHE_OPTIONS, Caching::PAGINATION_HEADERS
Gitlab::Cache::Helpers::DEFAULT_EXPIRY
Instance Method Summary
collapse
-
#accepted!(message = nil) ⇒ Object
-
#attributes_for_keys(keys, custom_params = nil) ⇒ Object
-
#authenticate! ⇒ Object
-
#authenticate_by_gitlab_shell_or_workhorse_token! ⇒ Object
-
#authenticate_by_gitlab_shell_token! ⇒ Object
-
#authenticate_non_get! ⇒ Object
-
#authenticated_as_admin! ⇒ Object
-
#authenticated_with_can_read_all_resources! ⇒ Object
-
#authorize!(action, subject = :global, reason = nil) ⇒ Object
-
#authorize_admin_group ⇒ Object
-
#authorize_admin_group_integrations ⇒ Object
-
#authorize_admin_member_role_on_group! ⇒ Object
-
#authorize_admin_member_role_on_instance! ⇒ Object
-
#authorize_admin_project ⇒ Object
-
#authorize_admin_project_integrations ⇒ Object
-
#authorize_admin_tag ⇒ Object
-
#authorize_any!(abilities, subject = :global, reason = nil) ⇒ Object
-
#authorize_cancel_builds! ⇒ Object
-
#authorize_delete_job_artifact! ⇒ Object
-
#authorize_job_token_policies!(project) ⇒ Object
-
#authorize_push_project ⇒ Object
-
#authorize_read_application_statistics! ⇒ Object
-
#authorize_read_attestations! ⇒ Object
-
#authorize_read_build_trace!(build) ⇒ Object
-
#authorize_read_builds! ⇒ Object
-
#authorize_read_code! ⇒ Object
-
#authorize_read_job_artifacts!(build) ⇒ Object
-
#authorized_project_scope?(project) ⇒ Boolean
-
#available_labels_for(label_parent, params = { include_ancestor_groups: true, only_group_labels: true }) ⇒ Object
-
#bad_request!(reason = nil) ⇒ Object
-
#bad_request_missing_attribute!(attribute) ⇒ Object
-
#check_group_access(group) ⇒ Object
-
#check_namespace_access(namespace) ⇒ Object
-
#check_pipeline_access(pipeline) ⇒ Object
-
#check_sha_param!(params, merge_request) ⇒ Object
-
#check_unmodified_since!(last_modified) ⇒ Object
-
#conflict!(message = nil) ⇒ Object
-
#created!(message = nil) ⇒ Object
-
#current_authenticated_job ⇒ Object
Returns the job associated with the token provided for authentication, if any.
-
#current_user ⇒ Object
rubocop:disable Gitlab/ModuleWithInstanceVariables We can’t rewrite this with StrongMemoize because sudo! would actually write to ‘@current_user`, and sudo? would immediately call current_user again which reads from `@current_user`.
-
#declared_params(options = {}) ⇒ Object
-
#destroy_conditionally!(resource, last_updated: nil) ⇒ Object
-
#file_too_large!(message = nil) ⇒ Object
-
#filter_by_iid(items, iid) ⇒ Object
-
#filter_by_search(items, text) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#filter_by_title(items, title) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_branch!(branch_name) ⇒ Object
-
#find_build!(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#find_group(id, organization: nil) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_group!(id, organization: nil) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#find_group_by_full_path!(full_path) ⇒ Object
-
#find_job!(id) ⇒ Object
-
#find_merge_request_with_access(iid, access_level = :read_merge_request) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_namespace(id) ⇒ Object
find_namespace returns the namespace regardless of user access level on the namespace rubocop: disable CodeReuse/ActiveRecord.
-
#find_namespace!(id) ⇒ Object
find_namespace! returns the namespace if the current user can read the given namespace Otherwise, returns a not_found! error.
-
#find_namespace_by_path(path) ⇒ Object
-
#find_namespace_by_path!(path) ⇒ Object
-
#find_organization!(id) ⇒ Object
-
#find_pipeline(id) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_pipeline!(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#find_project(id) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_project!(id) ⇒ Object
-
#find_project_commit(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#find_project_issue(iid, project_id = nil) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_project_merge_request(iid, project: user_project) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#find_project_scopes ⇒ Object
Can be overriden by API endpoints.
-
#find_tag!(tag_name) ⇒ Object
-
#find_user(id) ⇒ Object
-
#forbidden!(reason = nil) ⇒ Object
-
#handle_api_exception(exception) ⇒ Object
-
#increment_counter(event_name) ⇒ Object
-
#increment_unique_values(event_name, values) ⇒ Object
-
#job_token_authentication? ⇒ Boolean
-
#logger ⇒ Object
-
#model_errors(model) ⇒ Object
-
#no_content!(message = nil) ⇒ Object
-
#not_acceptable!(message = nil) ⇒ Object
-
#not_allowed!(message = nil) ⇒ Object
-
#not_found!(resource = nil) ⇒ Object
-
#not_modified!(message = nil) ⇒ Object
-
#order_by_similarity?(allow_unauthorized: true) ⇒ Boolean
-
#order_options_with_tie_breaker(override_created_at: true) ⇒ Object
-
#present_artifacts_file!(file, **args) ⇒ Object
-
#present_carrierwave_file!(file, supports_direct_download: true, content_disposition: nil, content_type: nil, extra_response_headers: {}, extra_send_url_params: {}) ⇒ Object
Return back the given file depending on the object storage configuration.
-
#present_disk_file!(path, filename, content_type: nil, extra_response_headers: {}) ⇒ Object
-
#process_create_params(args) ⇒ Object
-
#process_update_params(args) ⇒ Object
-
#project_finder_params ⇒ Object
rubocop: enable CodeReuse/ActiveRecord.
-
#read_project_ability ⇒ Object
-
#redirect!(location_url) ⇒ Object
An error is raised to interrupt user’s request and redirect them to the right route.
-
#render_api_error!(message, status) ⇒ Object
-
#render_api_error_with_reason!(status, message, reason) ⇒ Object
-
#render_structured_api_error!(hash, status) ⇒ Object
-
#render_validation_error!(models, status = 400) ⇒ Object
-
#reorder_projects(projects) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord.
-
#require_gitlab_workhorse! ⇒ Object
-
#require_pages_config_enabled! ⇒ Object
-
#require_pages_enabled! ⇒ Object
-
#require_repository_enabled!(subject = :global) ⇒ Object
-
#required_attributes!(keys) ⇒ Object
Checks the occurrences of required attributes, each attribute must be present in the params hash or a Bad Request error is invoked.
-
#save_current_user_in_env(user) ⇒ Object
-
#service_unavailable!(message = nil) ⇒ Object
-
#set_current_organization(user: current_user) ⇒ Object
rubocop:enable Gitlab/ModuleWithInstanceVariables.
-
#set_status_code_in_env(status) ⇒ Object
-
#sudo? ⇒ Boolean
-
#too_many_requests!(message = nil, retry_after: 1.minute) ⇒ Object
-
#track_event(event_name, user:, send_snowplow_event: true, namespace_id: nil, project_id: nil, additional_properties: {}) ⇒ Object
-
#unauthorized!(reason = nil) ⇒ Object
-
#unprocessable_entity!(message = nil) ⇒ Object
-
#user_group ⇒ Object
-
#user_project ⇒ Object
-
#validate_params_for_multiple_files(snippet) ⇒ Object
-
#verify_workhorse_api! ⇒ Object
#load_balancer_stick_request
#log_artifacts_context, #log_artifacts_filesize, #log_build_dependencies, log_created, log_deleted
#paginate_with_strategies, #paginator
Methods included from Pagination
#paginate
Methods included from Caching
#cache_action, #cache_action_if, #cache_action_unless, #present_cached
#cache, #render_cached
#can?, #can_all?, #can_any?
Instance Method Details
#accepted!(message = nil) ⇒ Object
619
620
621
|
# File 'lib/api/helpers.rb', line 619
def accepted!(message = nil)
render_api_error!(message || '202 Accepted', 202)
end
|
#attributes_for_keys(keys, custom_params = nil) ⇒ Object
501
502
503
504
505
506
507
508
509
510
511
|
# File 'lib/api/helpers.rb', line 501
def attributes_for_keys(keys, custom_params = nil)
params_hash = custom_params || params
attrs = {}
keys.each do |key|
if params_hash[key].present? || (params_hash.key?(key) && params_hash[key] == false)
attrs[key] = params_hash[key]
end
end
permitted_attrs = ActionController::Parameters.new(attrs).permit!
permitted_attrs.to_h
end
|
#authenticate! ⇒ Object
361
362
363
|
# File 'lib/api/helpers.rb', line 361
def authenticate!
unauthorized! unless current_user
end
|
#authenticate_by_gitlab_shell_or_workhorse_token! ⇒ Object
373
374
375
376
377
|
# File 'lib/api/helpers.rb', line 373
def authenticate_by_gitlab_shell_or_workhorse_token!
return require_gitlab_workhorse! unless Gitlab::Shell.()
authenticate_by_gitlab_shell_token!
end
|
#authenticate_by_gitlab_shell_token! ⇒ Object
369
370
371
|
# File 'lib/api/helpers.rb', line 369
def authenticate_by_gitlab_shell_token!
unauthorized! unless Gitlab::Shell.verify_api_request()
end
|
#authenticate_non_get! ⇒ Object
365
366
367
|
# File 'lib/api/helpers.rb', line 365
def authenticate_non_get!
authenticate! unless %w[GET HEAD].include?(route.request_method)
end
|
#authenticated_as_admin! ⇒ Object
384
385
386
387
|
# File 'lib/api/helpers.rb', line 384
def authenticated_as_admin!
authenticate!
forbidden! unless current_user.can_admin_all_resources?
end
|
#authenticated_with_can_read_all_resources! ⇒ Object
379
380
381
382
|
# File 'lib/api/helpers.rb', line 379
def authenticated_with_can_read_all_resources!
authenticate!
forbidden! unless current_user.can_read_all_resources?
end
|
#authorize!(action, subject = :global, reason = nil) ⇒ Object
394
395
396
|
# File 'lib/api/helpers.rb', line 394
def authorize!(action, subject = :global, reason = nil)
forbidden!(reason) unless can?(current_user, action, subject)
end
|
#authorize_admin_group ⇒ Object
422
423
424
|
# File 'lib/api/helpers.rb', line 422
def authorize_admin_group
authorize! :admin_group, user_group
end
|
#authorize_admin_group_integrations ⇒ Object
418
419
420
|
# File 'lib/api/helpers.rb', line 418
def authorize_admin_group_integrations
authorize! :admin_integrations, user_group
end
|
#authorize_admin_member_role_on_group! ⇒ Object
426
427
428
|
# File 'lib/api/helpers.rb', line 426
def authorize_admin_member_role_on_group!
authorize! :admin_member_role, user_group
end
|
#authorize_admin_member_role_on_instance! ⇒ Object
430
431
432
|
# File 'lib/api/helpers.rb', line 430
def authorize_admin_member_role_on_instance!
authorize! :admin_member_role
end
|
#authorize_admin_project ⇒ Object
410
411
412
|
# File 'lib/api/helpers.rb', line 410
def authorize_admin_project
authorize! :admin_project, user_project
end
|
#authorize_admin_project_integrations ⇒ Object
414
415
416
|
# File 'lib/api/helpers.rb', line 414
def authorize_admin_project_integrations
authorize! :admin_integrations, user_project
end
|
#authorize_admin_tag ⇒ Object
406
407
408
|
# File 'lib/api/helpers.rb', line 406
def authorize_admin_tag
authorize! :admin_tag, user_project
end
|
#authorize_any!(abilities, subject = :global, reason = nil) ⇒ Object
398
399
400
|
# File 'lib/api/helpers.rb', line 398
def authorize_any!(abilities, subject = :global, reason = nil)
forbidden!(reason) unless can_any?(current_user, abilities, subject)
end
|
#authorize_cancel_builds! ⇒ Object
458
459
460
|
# File 'lib/api/helpers.rb', line 458
def authorize_cancel_builds!
authorize! :cancel_build, user_project
end
|
#authorize_delete_job_artifact! ⇒ Object
454
455
456
|
# File 'lib/api/helpers.rb', line 454
def authorize_delete_job_artifact!
authorize! :delete_job_artifact, user_project
end
|
#authorize_job_token_policies!(project) ⇒ Object
194
195
196
|
# File 'lib/api/helpers.rb', line 194
def authorize_job_token_policies!(project)
forbidden!(job_token_policies_unauthorized_message(project)) unless job_token_policies_authorized?(project)
end
|
#authorize_push_project ⇒ Object
402
403
404
|
# File 'lib/api/helpers.rb', line 402
def authorize_push_project
authorize! :push_code, user_project
end
|
#authorize_read_application_statistics! ⇒ Object
389
390
391
392
|
# File 'lib/api/helpers.rb', line 389
def authorize_read_application_statistics!
authenticate!
forbidden! unless current_user.can?(:read_application_statistics)
end
|
#authorize_read_attestations! ⇒ Object
434
435
436
|
# File 'lib/api/helpers.rb', line 434
def authorize_read_attestations!
authorize! :read_attestation, user_project
end
|
#authorize_read_build_trace!(build) ⇒ Object
446
447
448
|
# File 'lib/api/helpers.rb', line 446
def authorize_read_build_trace!(build)
authorize! :read_build_trace, build
end
|
#authorize_read_builds! ⇒ Object
438
439
440
|
# File 'lib/api/helpers.rb', line 438
def authorize_read_builds!
authorize! :read_build, user_project
end
|
#authorize_read_code! ⇒ Object
442
443
444
|
# File 'lib/api/helpers.rb', line 442
def authorize_read_code!
authorize! :read_code, user_project
end
|
#authorize_read_job_artifacts!(build) ⇒ Object
450
451
452
|
# File 'lib/api/helpers.rb', line 450
def authorize_read_job_artifacts!(build)
authorize! :read_job_artifacts, build
end
|
#authorized_project_scope?(project) ⇒ Boolean
202
203
204
205
206
207
|
# File 'lib/api/helpers.rb', line 202
def authorized_project_scope?(project)
return true unless job_token_authentication?
return true unless route_authentication_setting[:job_token_scope] == :project
current_authenticated_job.project == project
end
|
#available_labels_for(label_parent, params = { include_ancestor_groups: true, only_group_labels: true }) ⇒ Object
137
138
139
140
141
142
143
144
145
146
|
# File 'lib/api/helpers.rb', line 137
def available_labels_for(label_parent, params = { include_ancestor_groups: true, only_group_labels: true })
if label_parent.is_a?(Project)
params.delete(:only_group_labels)
params[:project_id] = label_parent.id
else
params[:group_id] = label_parent.id
end
LabelsFinder.new(current_user, params).execute
end
|
#bad_request!(reason = nil) ⇒ Object
552
553
554
|
# File 'lib/api/helpers.rb', line 552
def bad_request!(reason = nil)
render_api_error_with_reason!(400, '400 Bad request', reason)
end
|
#bad_request_missing_attribute!(attribute) ⇒ Object
556
557
558
|
# File 'lib/api/helpers.rb', line 556
def bad_request_missing_attribute!(attribute)
bad_request!("\"#{attribute}\" not given")
end
|
#check_group_access(group) ⇒ Object
264
265
266
267
268
269
|
# File 'lib/api/helpers.rb', line 264
def check_group_access(group)
return group if can?(current_user, :read_group, group)
return unauthorized! if authenticate_non_public?
not_found!('Group')
end
|
#check_namespace_access(namespace) ⇒ Object
271
272
273
274
275
|
# File 'lib/api/helpers.rb', line 271
def check_namespace_access(namespace)
return namespace if can?(current_user, :read_namespace_via_membership, namespace)
not_found!('Namespace')
end
|
#check_pipeline_access(pipeline) ⇒ Object
224
225
226
227
228
229
230
231
|
# File 'lib/api/helpers.rb', line 224
def check_pipeline_access(pipeline)
return forbidden! unless authorized_project_scope?(pipeline&.project)
return pipeline if can?(current_user, :read_pipeline, pipeline)
return unauthorized! if authenticate_non_public?
not_found!('Pipeline')
end
|
#check_sha_param!(params, merge_request) ⇒ Object
567
568
569
570
571
|
# File 'lib/api/helpers.rb', line 567
def check_sha_param!(params, merge_request)
if params[:sha] && merge_request.diff_head_sha != params[:sha]
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.diff_head_sha}", 409)
end
end
|
#check_unmodified_since!(last_modified) ⇒ Object
35
36
37
38
39
40
41
42
43
44
45
|
# File 'lib/api/helpers.rb', line 35
def check_unmodified_since!(last_modified)
if_unmodified_since = begin
Time.parse(['If-Unmodified-Since'])
rescue StandardError
nil
end
if if_unmodified_since && last_modified && last_modified > if_unmodified_since
render_api_error!('412 Precondition Failed', 412)
end
end
|
#conflict!(message = nil) ⇒ Object
589
590
591
|
# File 'lib/api/helpers.rb', line 589
def conflict!(message = nil)
render_api_error!(message || '409 Conflict', 409)
end
|
#created!(message = nil) ⇒ Object
615
616
617
|
# File 'lib/api/helpers.rb', line 615
def created!(message = nil)
render_api_error!(message || '201 Created', 201)
end
|
#current_authenticated_job ⇒ Object
Returns the job associated with the token provided for authentication, if any
68
69
70
71
72
73
74
|
# File 'lib/api/helpers.rb', line 68
def current_authenticated_job
if try(:namespace_inheritable, :authentication)
ci_build_from_namespace_inheritable
else
@current_authenticated_job end
end
|
#current_user ⇒ Object
rubocop:disable Gitlab/ModuleWithInstanceVariables We can’t rewrite this with StrongMemoize because sudo! would actually write to ‘@current_user`, and sudo? would immediately call current_user again which reads from `@current_user`. We should rewrite this in a way that using StrongMemoize is possible
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
# File 'lib/api/helpers.rb', line 81
def current_user
return @current_user if defined?(@current_user)
@current_user = initial_current_user
Gitlab::I18n.locale = @current_user&.preferred_language
sudo!
unless sudo?
token = validate_and_save_access_token!(scopes: scopes_registered_for_endpoint)
if token
result = ::Authz::Tokens::AuthorizeGranularScopesService.new(
boundary: boundary_for_endpoint, permissions: permissions_for_endpoint, token: token
).execute
raise Gitlab::Auth::GranularPermissionsError, result.message if result.error?
end
end
save_current_user_in_env(@current_user) if @current_user
if @current_user
load_balancer_stick_request(::ApplicationRecord, :user, @current_user.id)
audit_request_with_token_scope(@current_user)
end
@current_user
end
|
#declared_params(options = {}) ⇒ Object
30
31
32
33
|
# File 'lib/api/helpers.rb', line 30
def declared_params(options = {})
options = { include_parent_namespaces: false }.merge(options)
declared(params, options).to_h.symbolize_keys
end
|
#destroy_conditionally!(resource, last_updated: nil) ⇒ Object
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
# File 'lib/api/helpers.rb', line 47
def destroy_conditionally!(resource, last_updated: nil)
last_updated ||= resource.updated_at
check_unmodified_since!(last_updated)
status 204
body false
if block_given?
yield resource
else
resource.destroy
end
end
|
#file_too_large!(message = nil) ⇒ Object
597
598
599
|
# File 'lib/api/helpers.rb', line 597
def file_too_large!(message = nil)
render_api_error!(message || '413 Request Entity Too Large', 413)
end
|
#filter_by_iid(items, iid) ⇒ Object
513
514
515
|
# File 'lib/api/helpers.rb', line 513
def filter_by_iid(items, iid)
items.iid_in(iid)
end
|
#filter_by_search(items, text) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
523
524
525
|
# File 'lib/api/helpers.rb', line 523
def filter_by_search(items, text)
items.search(text)
end
|
#filter_by_title(items, title) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
518
519
520
|
# File 'lib/api/helpers.rb', line 518
def filter_by_title(items, title)
items.where(title: title)
end
|
#find_branch!(branch_name) ⇒ Object
307
308
309
310
311
312
313
|
# File 'lib/api/helpers.rb', line 307
def find_branch!(branch_name)
if Gitlab::GitRefValidator.validate(branch_name)
user_project.repository.find_branch(branch_name) || not_found!('Branch')
else
render_api_error!('The branch refname is invalid', 400)
end
end
|
#find_build!(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
353
354
355
|
# File 'lib/api/helpers.rb', line 353
def find_build!(id)
user_project.builds.find(id.to_i)
end
|
#find_group(id, organization: nil) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
239
240
241
242
243
244
245
246
247
|
# File 'lib/api/helpers.rb', line 239
def find_group(id, organization: nil)
collection = organization.present? ? Group.in_organization(organization) : Group.all
if INTEGER_ID_REGEX.match?(id.to_s)
collection.find_by(id: id)
else
collection.find_by_full_path(id)
end
end
|
#find_group!(id, organization: nil) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
250
251
252
253
254
255
256
257
|
# File 'lib/api/helpers.rb', line 250
def find_group!(id, organization: nil)
group = find_group(id, organization: organization)
::Gitlab::ApplicationContext.push(namespace: group) if group
check_group_access(group)
end
|
#find_group_by_full_path!(full_path) ⇒ Object
259
260
261
262
|
# File 'lib/api/helpers.rb', line 259
def find_group_by_full_path!(full_path)
group = Group.find_by_full_path(full_path)
check_group_access(group)
end
|
#find_job!(id) ⇒ Object
357
358
359
|
# File 'lib/api/helpers.rb', line 357
def find_job!(id)
user_project.processables.find(id.to_i)
end
|
#find_merge_request_with_access(iid, access_level = :read_merge_request) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
346
347
348
349
350
|
# File 'lib/api/helpers.rb', line 346
def find_merge_request_with_access(iid, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find_by!(iid: iid)
authorize! access_level, merge_request
merge_request
end
|
#find_namespace(id) ⇒ Object
find_namespace returns the namespace regardless of user access level on the namespace rubocop: disable CodeReuse/ActiveRecord
279
280
281
282
283
284
285
286
287
288
289
290
|
# File 'lib/api/helpers.rb', line 279
def find_namespace(id)
if INTEGER_ID_REGEX.match?(id.to_s)
::Namespace.sticking.find_caught_up_replica(:namespace, id)
Namespace.without_project_namespaces.find_by(id: id)
else
find_namespace_by_path(id)
end
end
|
#find_namespace!(id) ⇒ Object
find_namespace! returns the namespace if the current user can read the given namespace Otherwise, returns a not_found! error
295
296
297
|
# File 'lib/api/helpers.rb', line 295
def find_namespace!(id)
check_namespace_access(find_namespace(id))
end
|
#find_namespace_by_path(path) ⇒ Object
299
300
301
|
# File 'lib/api/helpers.rb', line 299
def find_namespace_by_path(path)
Namespace.without_project_namespaces.find_by_full_path(path)
end
|
#find_namespace_by_path!(path) ⇒ Object
303
304
305
|
# File 'lib/api/helpers.rb', line 303
def find_namespace_by_path!(path)
check_namespace_access(find_namespace_by_path(path))
end
|
#find_organization!(id) ⇒ Object
233
234
235
236
|
# File 'lib/api/helpers.rb', line 233
def find_organization!(id)
organization = ::Organizations::Organization.find_by_id(id)
check_organization_access(organization)
end
|
#find_pipeline(id) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
210
211
212
213
214
215
216
|
# File 'lib/api/helpers.rb', line 210
def find_pipeline(id)
return unless id
if INTEGER_ID_REGEX.match?(id.to_s)
::Ci::Pipeline.find_by(id: id)
end
end
|
#find_pipeline!(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
219
220
221
222
|
# File 'lib/api/helpers.rb', line 219
def find_pipeline!(id)
pipeline = find_pipeline(id)
check_pipeline_access(pipeline)
end
|
#find_project(id) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
153
154
155
156
157
158
159
160
161
162
163
|
# File 'lib/api/helpers.rb', line 153
def find_project(id)
return unless id
projects = find_project_scopes
if id.is_a?(Integer) || id =~ INTEGER_ID_REGEX
projects.find_by(id: id)
elsif id.include?("/")
projects.find_by_full_path(id, follow_redirects: true)
end
end
|
#find_project!(id) ⇒ Object
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
# File 'lib/api/helpers.rb', line 171
def find_project!(id)
project = find_project(id)
return forbidden!("This project's CI/CD job token cannot be used to authenticate with the container registry of a different project.") unless authorized_project_scope?(project)
return not_found!('Project') if project.nil?
unless can?(current_user, read_project_ability, project)
return unauthorized! if authenticate_non_public?
return handle_job_token_failure!(project)
end
authorize_job_token_policies!(project) && return
if project_moved?(id, project)
return not_allowed!('Non GET methods are not allowed for moved projects') unless request.get?
return redirect!(url_with_project_id(project))
end
project
end
|
#find_project_commit(id) ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
341
342
343
|
# File 'lib/api/helpers.rb', line 341
def find_project_commit(id)
user_project.commit_by(oid: id)
end
|
#find_project_issue(iid, project_id = nil) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
#find_project_merge_request(iid, project: user_project) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
336
337
338
|
# File 'lib/api/helpers.rb', line 336
def find_project_merge_request(iid, project: user_project)
MergeRequestsFinder.new(current_user, project_id: project.id).find_by!(iid: iid)
end
|
#find_project_scopes ⇒ Object
Can be overriden by API endpoints
167
168
169
|
# File 'lib/api/helpers.rb', line 167
def find_project_scopes
Project.without_deleted.not_hidden
end
|
#find_tag!(tag_name) ⇒ Object
315
316
317
318
319
320
321
|
# File 'lib/api/helpers.rb', line 315
def find_tag!(tag_name)
if Gitlab::GitRefValidator.validate(tag_name)
user_project.repository.find_tag(tag_name) || not_found!('Tag')
else
render_api_error!('The tag refname is invalid', 400)
end
end
|
#forbidden!(reason = nil) ⇒ Object
548
549
550
|
# File 'lib/api/helpers.rb', line 548
def forbidden!(reason = nil)
render_api_error_with_reason!(403, '403 Forbidden', reason)
end
|
#handle_api_exception(exception) ⇒ Object
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
|
# File 'lib/api/helpers.rb', line 661
def handle_api_exception(exception)
if report_exception?(exception)
context_user = begin
current_user
rescue StandardError
nil
end
define_params_for_grape_middleware
Gitlab::ApplicationContext.push(user: context_user, remote_ip: request.ip)
Gitlab::ErrorTracking.track_exception(exception)
end
env[API_EXCEPTION_ENV] = exception
trace = exception.backtrace
message = ["\n#{exception.class} (#{exception.message}):\n"]
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
message = message.join
API.logger.add Logger::FATAL, message
response_message =
if Rails.env.test?
message
else
'500 Internal Server Error'
end
rack_response({ 'message' => response_message }.to_json, 500)
end
|
#increment_counter(event_name) ⇒ Object
774
775
776
777
778
|
# File 'lib/api/helpers.rb', line 774
def increment_counter(event_name)
Gitlab::UsageDataCounters.count(event_name)
rescue StandardError => error
Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
end
|
#increment_unique_values(event_name, values) ⇒ Object
782
783
784
785
786
787
788
|
# File 'lib/api/helpers.rb', line 782
def increment_unique_values(event_name, values)
return unless values.present?
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: values)
rescue StandardError => error
Gitlab::AppLogger.warn("Redis tracking event failed for event: #{event_name}, message: #{error.message}")
end
|
#job_token_authentication? ⇒ Boolean
62
63
64
|
# File 'lib/api/helpers.rb', line 62
def job_token_authentication?
initial_current_user && @current_authenticated_job.present? end
|
#logger ⇒ Object
26
27
28
|
# File 'lib/api/helpers.rb', line 26
def logger
API.logger
end
|
#model_errors(model) ⇒ Object
633
634
635
|
# File 'lib/api/helpers.rb', line 633
def model_errors(model)
model.errors
end
|
#no_content!(message = nil) ⇒ Object
611
612
613
|
# File 'lib/api/helpers.rb', line 611
def no_content!(message = nil)
render_api_error!(message || '204 No Content', 204)
end
|
#not_acceptable!(message = nil) ⇒ Object
581
582
583
|
# File 'lib/api/helpers.rb', line 581
def not_acceptable!(message = nil)
render_api_error!(message || '406 Not Acceptable', 406)
end
|
#not_allowed!(message = nil) ⇒ Object
577
578
579
|
# File 'lib/api/helpers.rb', line 577
def not_allowed!(message = nil)
render_api_error!(message || '405 Method Not Allowed', :method_not_allowed)
end
|
#not_found!(resource = nil) ⇒ Object
560
561
562
563
564
565
|
# File 'lib/api/helpers.rb', line 560
def not_found!(resource = nil)
message = ["404"]
message << resource if resource
message << "Not Found"
render_api_error!(message.join(' '), 404)
end
|
#not_modified!(message = nil) ⇒ Object
607
608
609
|
# File 'lib/api/helpers.rb', line 607
def not_modified!(message = nil)
render_api_error!(message || '304 Not Modified', 304)
end
|
#order_by_similarity?(allow_unauthorized: true) ⇒ Boolean
814
815
816
|
# File 'lib/api/helpers.rb', line 814
def order_by_similarity?(allow_unauthorized: true)
params[:order_by] == 'similarity' && params[:search].present? && (allow_unauthorized || current_user.present?)
end
|
#order_options_with_tie_breaker(override_created_at: true) ⇒ Object
527
528
529
530
531
532
533
534
535
536
537
|
# File 'lib/api/helpers.rb', line 527
def order_options_with_tie_breaker(override_created_at: true)
order_by = if params[:order_by] == 'created_at' && override_created_at
'id'
else
params[:order_by]
end
order_options = { order_by => params[:sort] }
order_options['id'] ||= params[:sort] || 'asc'
order_options
end
|
#present_artifacts_file!(file, **args) ⇒ Object
727
728
729
730
731
|
# File 'lib/api/helpers.rb', line 727
def present_artifacts_file!(file, **args)
log_artifacts_filesize(file&.model)
present_carrierwave_file!(file, **args)
end
|
#present_carrierwave_file!(file, supports_direct_download: true, content_disposition: nil, content_type: nil, extra_response_headers: {}, extra_send_url_params: {}) ⇒ Object
Return back the given file depending on the object storage configuration. For disabled mode, the disk file is returned. For enabled mode, the response depends on the direct download support:
* direct download supported by the uploader class: a redirect to the file signed url is returned.
* direct download not supported: a workhorse send_url response is returned.
Params:
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
|
# File 'lib/api/helpers.rb', line 746
def present_carrierwave_file!(file, supports_direct_download: true, content_disposition: nil, content_type: nil, extra_response_headers: {}, extra_send_url_params: {})
return not_found! unless file&.exists?
if content_disposition
response_disposition = ActionDispatch::Http::ContentDisposition.format(disposition: content_disposition, filename: file.filename)
end
if file.file_storage?
present_disk_file!(file.path, file.filename, content_type: content_type, extra_response_headers: )
elsif supports_direct_download && file.direct_download_enabled?
return redirect(ObjectStorage::S3.signed_head_url(file)) if request.head? && file.fog_credentials[:provider] == 'AWS'
redirect_params = {}
if content_disposition
redirect_params[:query] = { 'response-content-disposition' => response_disposition, 'response-content-type' => content_type || file.content_type }
end
file_url = ObjectStorage::CDN::FileUrl.new(file: file, ip_address: ip_address, redirect_params: redirect_params)
redirect(file_url.url)
else
= .merge('Content-Type' => content_type, 'Content-Disposition' => response_disposition).compact_blank
(*Gitlab::Workhorse.send_url(file.url, response_headers: , **))
status :ok
body '' end
end
|
#present_disk_file!(path, filename, content_type: nil, extra_response_headers: {}) ⇒ Object
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
|
# File 'lib/api/helpers.rb', line 710
def present_disk_file!(path, filename, content_type: nil, extra_response_headers: {})
filename ||= File.basename(path)
.compact_blank.each { |k, v| [k] = v }
['Content-Disposition'] = ActionDispatch::Http::ContentDisposition.format(disposition: 'attachment', filename: filename)
['Content-Transfer-Encoding'] = 'binary'
content_type(content_type || 'application/octet-stream')
case ['X-Sendfile-Type']
when 'X-Sendfile'
['X-Sendfile'] = path
body '' else
sendfile path
end
end
|
#process_create_params(args) ⇒ Object
78
79
80
81
82
83
84
85
86
|
# File 'lib/api/helpers/snippets_helpers.rb', line 78
def process_create_params(args)
args[:snippet_actions] = args.delete(:files)&.map do |file|
file[:action] = :create
file.symbolize_keys
end
args[:organization_id] = Current.organization.id
args
end
|
#process_update_params(args) ⇒ Object
88
89
90
91
92
|
# File 'lib/api/helpers/snippets_helpers.rb', line 88
def process_update_params(args)
args[:snippet_actions] = args.delete(:files)&.map(&:symbolize_keys)
args
end
|
#project_finder_params ⇒ Object
rubocop: enable CodeReuse/ActiveRecord
704
705
706
|
# File 'lib/api/helpers.rb', line 704
def project_finder_params
project_finder_params_ce.merge(project_finder_params_ee)
end
|
#read_project_ability ⇒ Object
198
199
200
|
# File 'lib/api/helpers.rb', line 198
def read_project_ability
:read_project
end
|
#redirect!(location_url) ⇒ Object
An error is raised to interrupt user’s request and redirect them to the right route. The error! helper behaves similarly, but it cannot be used because it formats the response message.
542
543
544
|
# File 'lib/api/helpers.rb', line 542
def redirect!(location_url)
raise ::API::API::MovedPermanentlyError, location_url
end
|
#render_api_error!(message, status) ⇒ Object
643
644
645
|
# File 'lib/api/helpers.rb', line 643
def render_api_error!(message, status)
render_structured_api_error!({ 'message' => message }, status)
end
|
#render_api_error_with_reason!(status, message, reason) ⇒ Object
637
638
639
640
641
|
# File 'lib/api/helpers.rb', line 637
def render_api_error_with_reason!(status, message, reason)
message = [message]
message << "- #{reason}" if reason
render_api_error!(message.join(' '), status)
end
|
#render_structured_api_error!(hash, status) ⇒ Object
647
648
649
650
651
652
|
# File 'lib/api/helpers.rb', line 647
def render_structured_api_error!(hash, status)
set_status_code_in_env(status)
error!(hash, status, )
end
|
#render_validation_error!(models, status = 400) ⇒ Object
623
624
625
626
627
628
629
630
631
|
# File 'lib/api/helpers.rb', line 623
def render_validation_error!(models, status = 400)
models = Array(models)
errors = models.map { |m| model_errors(m) }.filter(&:present?)
messages = errors.map(&:messages)
messages = messages.count == 1 ? messages.first : messages.join(" ")
render_api_error!(messages || '400 Bad Request', status) if errors.any?
end
|
#reorder_projects(projects) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord
699
700
701
|
# File 'lib/api/helpers.rb', line 699
def reorder_projects(projects)
projects.reorder(order_options_with_tie_breaker)
end
|
#require_gitlab_workhorse! ⇒ Object
466
467
468
469
470
471
472
|
# File 'lib/api/helpers.rb', line 466
def require_gitlab_workhorse!
verify_workhorse_api!
unless env['HTTP_GITLAB_WORKHORSE'].present?
forbidden!('Request should be executed via GitLab Workhorse')
end
end
|
#require_pages_config_enabled! ⇒ Object
486
487
488
|
# File 'lib/api/helpers.rb', line 486
def require_pages_config_enabled!
not_found! unless Gitlab.config.pages.enabled
end
|
#require_pages_enabled! ⇒ Object
482
483
484
|
# File 'lib/api/helpers.rb', line 482
def require_pages_enabled!
not_found! unless ::Gitlab::Pages.enabled?
end
|
#require_repository_enabled!(subject = :global) ⇒ Object
462
463
464
|
# File 'lib/api/helpers.rb', line 462
def require_repository_enabled!(subject = :global)
not_found!("Repository") unless user_project.feature_available?(:repository, current_user)
end
|
#required_attributes!(keys) ⇒ Object
Checks the occurrences of required attributes, each attribute must be present in the params hash or a Bad Request error is invoked.
Parameters:
keys (required) - A hash consisting of keys that must be present
495
496
497
498
499
|
# File 'lib/api/helpers.rb', line 495
def required_attributes!(keys)
keys.each do |key|
bad_request_missing_attribute!(key) unless params[key].present?
end
end
|
#save_current_user_in_env(user) ⇒ Object
121
122
123
|
# File 'lib/api/helpers.rb', line 121
def save_current_user_in_env(user)
env[API_USER_ENV] = { user_id: user.id, username: user.username }
end
|
#service_unavailable!(message = nil) ⇒ Object
585
586
587
|
# File 'lib/api/helpers.rb', line 585
def service_unavailable!(message = nil)
render_api_error!(message || '503 Service Unavailable', 503)
end
|
#set_current_organization(user: current_user) ⇒ Object
rubocop:enable Gitlab/ModuleWithInstanceVariables
#set_status_code_in_env(status) ⇒ Object
654
655
656
657
658
659
|
# File 'lib/api/helpers.rb', line 654
def set_status_code_in_env(status)
env[API_RESPONSE_STATUS_CODE] = Rack::Utils.status_code(status)
end
|
#sudo? ⇒ Boolean
125
126
127
|
# File 'lib/api/helpers.rb', line 125
def sudo?
initial_current_user != current_user
end
|
#too_many_requests!(message = nil, retry_after: 1.minute) ⇒ Object
601
602
603
604
605
|
# File 'lib/api/helpers.rb', line 601
def too_many_requests!(message = nil, retry_after: 1.minute)
['Retry-After'] = retry_after.to_i if retry_after
render_api_error!(message || '429 Too Many Requests', 429)
end
|
#track_event(event_name, user:, send_snowplow_event: true, namespace_id: nil, project_id: nil, additional_properties: {}) ⇒ Object
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
|
# File 'lib/api/helpers.rb', line 790
def track_event(event_name, user:, send_snowplow_event: true, namespace_id: nil, project_id: nil, additional_properties: {})
return unless user.present?
namespace = Namespace.find(namespace_id) if namespace_id
project = Project.find(project_id) if project_id
Gitlab::InternalEvents.track_event(
event_name,
send_snowplow_event: send_snowplow_event,
additional_properties: additional_properties,
user: user,
namespace: namespace,
project: project
)
rescue Gitlab::Tracking::EventValidator::UnknownEventError => e
Gitlab::ErrorTracking.track_exception(e, event_name: event_name)
unprocessable_entity!(e.message) if Gitlab.dev_or_test_env?
rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, event_name: event_name)
end
|
#unauthorized!(reason = nil) ⇒ Object
573
574
575
|
# File 'lib/api/helpers.rb', line 573
def unauthorized!(reason = nil)
render_api_error_with_reason!(401, '401 Unauthorized', reason)
end
|
#unprocessable_entity!(message = nil) ⇒ Object
593
594
595
|
# File 'lib/api/helpers.rb', line 593
def unprocessable_entity!(message = nil)
render_api_error!(message || '422 Unprocessable Entity', :unprocessable_entity)
end
|
#user_group ⇒ Object
129
130
131
|
# File 'lib/api/helpers.rb', line 129
def user_group
@group ||= find_group!(params[:id])
end
|
#user_project ⇒ Object
133
134
135
|
# File 'lib/api/helpers.rb', line 133
def user_project
@project ||= find_project!(params[:id])
end
|
#validate_params_for_multiple_files(snippet) ⇒ Object
94
95
96
97
98
99
100
|
# File 'lib/api/helpers/snippets_helpers.rb', line 94
def validate_params_for_multiple_files(snippet)
return unless params[:content] || params[:file_name]
if snippet.multiple_files?
render_api_error!({ error: _('To update Snippets with multiple files, you must use the `files` parameter') }, 400)
end
end
|
#verify_workhorse_api! ⇒ Object