Files
gitlab-foss/lib/api/helpers/packages_helpers.rb
2025-07-14 18:12:28 +00:00

181 lines
5.9 KiB
Ruby

# frozen_string_literal: true
module API
module Helpers
module PackagesHelpers
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
MAX_PACKAGE_FILE_SIZE = 50.megabytes.freeze
def require_packages_enabled!
not_found! unless ::Gitlab.config.packages.enabled
end
def require_dependency_proxy_enabled!
not_found! unless ::Gitlab.config.dependency_proxy.enabled
end
def authorize_admin_package!(subject = user_project)
authorize!(:admin_package, subject)
end
def authorize_read_package!(subject = user_project)
authorize!(:read_package, subject.try(:packages_policy_subject) || subject)
end
def authorize_create_package!(subject = user_project)
authorize!(:create_package, subject)
end
def authorize_destroy_package!(subject = user_project)
authorize!(:destroy_package, subject)
end
def authorize_packages_access!(subject = user_project, required_permission = :read_package)
require_packages_enabled!
case required_permission
when :read_package
authorize_read_package!(subject)
when :read_package_within_public_registries
authorize!(required_permission, subject.packages_policy_subject)
when :read_group
authorize!(required_permission, subject)
else
forbidden!
end
end
def authorize_workhorse!(
subject: user_project,
has_length: true,
maximum_size: MAX_PACKAGE_FILE_SIZE,
use_final_store_path: false
)
authorize_upload!(subject)
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
params = { has_length: has_length, use_final_store_path: use_final_store_path }
params[:maximum_size] = maximum_size unless has_length
params[:final_store_path_config] = { root_hash: subject.id } if use_final_store_path
::Packages::PackageFileUploader.workhorse_authorize(**params)
end
def authorize_upload!(subject = user_project)
authorize_create_package!(subject)
require_gitlab_workhorse!
end
override :user_project
def user_project(action: :read_project)
case action
when :read_project
super()
when :read_package
user_project_with_read_package
else
raise ArgumentError, "unexpected action: #{action}"
end
end
# This function is similar to the `find_project!` function, but it considers the `read_package` ability.
def user_project_with_read_package
project = find_project(params[:id])
return forbidden! unless authorized_project_scope?(project)
project && authorize_job_token_policies!(project) && return
return project if can?(current_user, :read_package, project&.packages_policy_subject)
# guest users can have :read_project but not :read_package
return forbidden! if can?(current_user, :read_project, project)
return unauthorized! if authenticate_non_public?
not_found!('Project')
end
strong_memoize_attr :user_project_with_read_package
def track_package_event(action, scope, **args)
service = ::Packages::CreateEventService.new(
args[:project],
current_user,
namespace: args[:namespace],
event_name: action,
scope: scope
)
service.execute
category = args.delete(:category) || self.options[:for].name
args[:user] = current_user if current_user
event_name = "i_package_#{scope}_user"
::Gitlab::Tracking.event(
category,
action.to_s,
property: event_name,
label: 'redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly',
context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context],
**args
)
if action.to_s == 'push_package' && service.originator_type == :deploy_token
track_snowplow_event(
'push_package_by_deploy_token',
'package_pushed_using_deploy_token',
category,
args
)
elsif action.to_s == 'pull_package' && service.originator_type == :guest
track_snowplow_event(
'pull_package_by_guest',
'package_pulled_by_guest',
category,
args
)
end
end
def present_package_file!(package_file, supports_direct_download: true, content_disposition: nil)
package_file.package.touch_last_downloaded_at
present_carrierwave_file!(
package_file.file,
supports_direct_download: supports_direct_download,
content_disposition: content_disposition,
extra_send_url_params: ::Packages::SsrfProtection.params_for(package_file.package)
)
end
def protect_package!(package_name, package_type)
service_response =
::Packages::Protection::CheckRuleExistenceService.for_push(
project: user_project,
current_user: current_user,
params: { package_name: package_name, package_type: package_type }
).execute
bad_request!(service_response.message) if service_response.error?
forbidden!('Package protected.') if service_response[:protection_rule_exists?]
end
private
def track_snowplow_event(action_name, snowplow_event_name, category, args)
event_name = "i_package_#{action_name}"
key_path = "counts.package_events_i_package_#{action_name}"
context = Gitlab::Tracking::ServicePingContext.new(data_source: :redis, event: snowplow_event_name).to_context
Gitlab::Tracking.event(
category,
action_name,
property: event_name,
label: key_path,
context: [context],
**args
)
end
end
end
end