summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/admin/instance_clusters.rb5
-rw-r--r--lib/api/broadcast_messages.rb4
-rw-r--r--lib/api/ci/jobs.rb25
-rw-r--r--lib/api/ci/pipelines.rb10
-rw-r--r--lib/api/ci/runner.rb23
-rw-r--r--lib/api/ci/runners.rb10
-rw-r--r--lib/api/ci/secure_files.rb63
-rw-r--r--lib/api/commits.rb2
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb5
-rw-r--r--lib/api/container_repositories.rb8
-rw-r--r--lib/api/deploy_tokens.rb30
-rw-r--r--lib/api/entities/broadcast_message.rb2
-rw-r--r--lib/api/entities/ci/runner.rb4
-rw-r--r--lib/api/entities/ci/secure_file.rb1
-rw-r--r--lib/api/entities/container_registry.rb1
-rw-r--r--lib/api/entities/error_tracking.rb6
-rw-r--r--lib/api/entities/issuable_time_stats.rb2
-rw-r--r--lib/api/entities/label_basic.rb6
-rw-r--r--lib/api/entities/project.rb1
-rw-r--r--lib/api/entities/project_integration.rb15
-rw-r--r--lib/api/entities/user_safe.rb9
-rw-r--r--lib/api/entities/wiki_page.rb10
-rw-r--r--lib/api/error_tracking/collector.rb4
-rw-r--r--lib/api/generic_packages.rb6
-rw-r--r--lib/api/group_clusters.rb9
-rw-r--r--lib/api/helpers.rb17
-rw-r--r--lib/api/helpers/integrations_helpers.rb27
-rw-r--r--lib/api/helpers/projects_helpers.rb4
-rw-r--r--lib/api/helpers/wikis_helpers.rb4
-rw-r--r--lib/api/internal/base.rb6
-rw-r--r--lib/api/internal/kubernetes.rb1
-rw-r--r--lib/api/issues.rb17
-rw-r--r--lib/api/merge_requests.rb10
-rw-r--r--lib/api/notes.rb4
-rw-r--r--lib/api/package_files.rb2
-rw-r--r--lib/api/project_clusters.rb9
-rw-r--r--lib/api/project_import.rb82
-rw-r--r--lib/api/project_snippets.rb13
-rw-r--r--lib/api/pypi_packages.rb2
-rw-r--r--lib/api/repositories.rb2
-rw-r--r--lib/api/search.rb2
-rw-r--r--lib/api/snippets.rb13
-rw-r--r--lib/api/statistics.rb2
-rw-r--r--lib/api/system_hooks.rb12
-rw-r--r--lib/api/topics.rb14
-rw-r--r--lib/api/user_counts.rb8
-rw-r--r--lib/api/users.rb31
-rw-r--r--lib/api/validations/validators/file_path.rb3
-rw-r--r--lib/api/wikis.rb4
49 files changed, 390 insertions, 160 deletions
diff --git a/lib/api/admin/instance_clusters.rb b/lib/api/admin/instance_clusters.rb
index b724d3a38dc..4aebd9c0d40 100644
--- a/lib/api/admin/instance_clusters.rb
+++ b/lib/api/admin/instance_clusters.rb
@@ -9,6 +9,7 @@ module API
before do
authenticated_as_admin!
+ ensure_feature_enabled!
end
namespace 'admin' do
@@ -133,6 +134,10 @@ module API
def update_cluster_params
declared_params(include_missing: false).without(:cluster_id)
end
+
+ def ensure_feature_enabled!
+ not_found! unless Feature.enabled?(:certificate_based_clusters, clusterable_instance, default_enabled: :yaml, type: :ops)
+ end
end
end
end
diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb
index 0762c276aad..e081265b418 100644
--- a/lib/api/broadcast_messages.rb
+++ b/lib/api/broadcast_messages.rb
@@ -36,6 +36,8 @@ module API
optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now }
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
+ optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast type. Defaults to banner', default: -> { 'banner' }
optional :dismissable, type: Boolean, desc: 'Is dismissable'
@@ -76,6 +78,8 @@ module API
optional :ends_at, type: DateTime, desc: 'Ending time'
optional :color, type: String, desc: 'Background color'
optional :font, type: String, desc: 'Foreground color'
+ optional :target_access_levels, type: Array[Integer], coerce_with: Validations::Types::CommaSeparatedToIntegerArray.coerce,
+ values: BroadcastMessage::ALLOWED_TARGET_ACCESS_LEVELS, desc: 'Target user roles'
optional :target_path, type: String, desc: 'Target path'
optional :broadcast_type, type: String, values: BroadcastMessage.broadcast_types.keys, desc: 'Broadcast Type'
optional :dismissable, type: Boolean, desc: 'Is dismissable'
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index 30ce1454419..d9d0da2e4d1 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -38,7 +38,7 @@ module API
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
- get ':id/jobs', feature_category: :continuous_integration do
+ get ':id/jobs', urgency: :low, feature_category: :continuous_integration do
authorize_read_builds!
builds = user_project.builds.order('id DESC')
@@ -55,7 +55,7 @@ module API
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
end
- get ':id/jobs/:job_id', feature_category: :continuous_integration do
+ get ':id/jobs/:job_id', urgency: :low, feature_category: :continuous_integration do
authorize_read_builds!
build = find_build!(params[:job_id])
@@ -70,7 +70,7 @@ module API
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
end
- get ':id/jobs/:job_id/trace', feature_category: :continuous_integration do
+ get ':id/jobs/:job_id/trace', urgency: :low, feature_category: :continuous_integration do
authorize_read_builds!
build = find_build!(params[:job_id])
@@ -92,7 +92,7 @@ module API
params do
requires :job_id, type: Integer, desc: 'The ID of a job'
end
- post ':id/jobs/:job_id/cancel', feature_category: :continuous_integration do
+ post ':id/jobs/:job_id/cancel', urgency: :low, feature_category: :continuous_integration do
authorize_update_builds!
build = find_build!(params[:job_id])
@@ -109,7 +109,7 @@ module API
params do
requires :job_id, type: Integer, desc: 'The ID of a build'
end
- post ':id/jobs/:job_id/retry', feature_category: :continuous_integration do
+ post ':id/jobs/:job_id/retry', urgency: :low, feature_category: :continuous_integration do
authorize_update_builds!
build = find_build!(params[:job_id])
@@ -127,7 +127,7 @@ module API
params do
requires :job_id, type: Integer, desc: 'The ID of a build'
end
- post ':id/jobs/:job_id/erase', feature_category: :continuous_integration do
+ post ':id/jobs/:job_id/erase', urgency: :low, feature_category: :continuous_integration do
authorize_update_builds!
build = find_build!(params[:job_id])
@@ -144,9 +144,14 @@ module API
end
params do
requires :job_id, type: Integer, desc: 'The ID of a Job'
+ optional :job_variables_attributes, type: Array,
+ desc: 'User defined variables that will be included when running the job' do
+ requires :key, type: String, desc: 'The name of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ end
end
- post ":id/jobs/:job_id/play", feature_category: :continuous_integration do
+ post ':id/jobs/:job_id/play', urgency: :low, feature_category: :continuous_integration do
authorize_read_builds!
job = find_job!(params[:job_id])
@@ -155,7 +160,7 @@ module API
bad_request!("Unplayable Job") unless job.playable?
- job.play(current_user)
+ job.play(current_user, params[:job_variables_attributes])
status 200
@@ -168,11 +173,11 @@ module API
end
resource :job do
- desc 'Get current project using job token' do
+ desc 'Get current job using job token' do
success Entities::Ci::Job
end
route_setting :authentication, job_token_allowed: true
- get '', feature_category: :continuous_integration do
+ get '', feature_category: :continuous_integration, urgency: :low do
validate_current_authenticated_job
present current_authenticated_job, with: Entities::Ci::Job
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index e0086f624a8..2d7a437ca08 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -123,7 +123,7 @@ module API
use :pagination
end
- get ':id/pipelines/:pipeline_id/jobs', feature_category: :continuous_integration do
+ get ':id/pipelines/:pipeline_id/jobs', urgency: :low, feature_category: :continuous_integration do
authorize!(:read_pipeline, user_project)
pipeline = user_project.all_pipelines.find(params[:pipeline_id])
@@ -223,9 +223,13 @@ module API
post ':id/pipelines/:pipeline_id/retry', feature_category: :continuous_integration do
authorize! :update_pipeline, pipeline
- pipeline.retry_failed(current_user)
+ response = pipeline.retry_failed(current_user)
- present pipeline, with: Entities::Ci::Pipeline
+ if response.success?
+ present pipeline, with: Entities::Ci::Pipeline
+ else
+ render_api_error!(response.errors.join(', '), response.http_status)
+ end
end
desc 'Cancel all builds in the pipeline' do
diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb
index 4df9600322c..0e3b295396b 100644
--- a/lib/api/ci/runner.rb
+++ b/lib/api/ci/runner.rb
@@ -38,7 +38,7 @@ module API
attributes[:maintenance_note] ||= deprecated_note if deprecated_note
attributes[:active] = !attributes.delete(:paused) if attributes.include?(:paused)
- @runner = ::Ci::RegisterRunnerService.new.execute(params[:token], attributes)
+ @runner = ::Ci::Runners::RegisterRunnerService.new.execute(params[:token], attributes)
forbidden! unless @runner
if @runner.persisted?
@@ -57,7 +57,7 @@ module API
delete '/', feature_category: :runner do
authenticate_runner!
- destroy_conditionally!(current_runner) { ::Ci::UnregisterRunnerService.new(current_runner).execute }
+ destroy_conditionally!(current_runner) { ::Ci::Runners::UnregisterRunnerService.new(current_runner, params[:token]).execute }
end
desc 'Validates authentication credentials' do
@@ -71,6 +71,19 @@ module API
status 200
body "200"
end
+
+ desc 'Reset runner authentication token with current token' do
+ success Entities::Ci::ResetTokenResult
+ end
+ params do
+ requires :token, type: String, desc: 'The current authentication token of the runner'
+ end
+ post '/reset_authentication_token', feature_category: :runner do
+ authenticate_runner!
+
+ current_runner.reset_token!
+ present current_runner.token_with_expiration, with: Entities::Ci::ResetTokenResult
+ end
end
resource :jobs do
@@ -118,7 +131,7 @@ module API
formatter :build_json, ->(object, _) { object }
parser :build_json, ::Grape::Parser::Json
- post '/request', feature_category: :continuous_integration do
+ post '/request', urgency: :low, feature_category: :continuous_integration do
authenticate_runner!
unless current_runner.active?
@@ -172,7 +185,7 @@ module API
end
optional :exit_code, type: Integer, desc: %q(Job's exit code)
end
- put '/:id', feature_category: :continuous_integration do
+ put '/:id', urgency: :low, feature_category: :continuous_integration do
job = authenticate_job!(heartbeat_runner: true)
Gitlab::Metrics.add_event(:update_build)
@@ -199,7 +212,7 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :token, type: String, desc: %q(Job's authentication token)
end
- patch '/:id/trace', feature_category: :continuous_integration do
+ patch '/:id/trace', urgency: :default, feature_category: :continuous_integration do
job = authenticate_job!(heartbeat_runner: true)
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
diff --git a/lib/api/ci/runners.rb b/lib/api/ci/runners.rb
index 8a7ffab97dd..3c9e887e751 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -90,7 +90,7 @@ module API
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
params[:active] = !params.delete(:paused) if params.include?(:paused)
- update_service = ::Ci::UpdateRunnerService.new(runner)
+ update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
if update_service.update(declared_params(include_missing: false))
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
@@ -110,7 +110,7 @@ module API
authenticate_delete_runner!(runner)
- destroy_conditionally!(runner) { ::Ci::UnregisterRunnerService.new(runner).execute }
+ destroy_conditionally!(runner) { ::Ci::Runners::UnregisterRunnerService.new(runner, current_user).execute }
end
desc 'List jobs running on a runner' do
@@ -187,7 +187,7 @@ module API
runner = get_runner(params[:runner_id])
authenticate_enable_runner!(runner)
- if runner.assign_to(user_project)
+ if ::Ci::Runners::AssignRunnerService.new(runner, user_project, current_user).execute
present runner, with: Entities::Ci::Runner
else
render_validation_error!(runner)
@@ -246,9 +246,9 @@ module API
success Entities::Ci::ResetTokenResult
end
post 'reset_registration_token' do
- authorize! :update_runners_registration_token
+ authorize! :update_runners_registration_token, ApplicationSetting.current
- ApplicationSetting.current.reset_runners_registration_token!
+ ::Ci::Runners::ResetRegistrationTokenService.new(ApplicationSetting.current, current_user).execute
present ApplicationSetting.current_without_cache.runners_registration_token_with_expiration, with: Entities::Ci::ResetTokenResult
end
end
diff --git a/lib/api/ci/secure_files.rb b/lib/api/ci/secure_files.rb
index 715a8b37fae..d5b21e2ef29 100644
--- a/lib/api/ci/secure_files.rb
+++ b/lib/api/ci/secure_files.rb
@@ -7,8 +7,8 @@ module API
before do
authenticate!
- authorize! :admin_build, user_project
feature_flag_enabled?
+ authorize! :read_secure_files, user_project
end
feature_category :pipeline_authoring
@@ -52,39 +52,44 @@ module API
body secure_file.file.read
end
- desc 'Upload a Secure File'
- params do
- requires :name, type: String, desc: 'The name of the file'
- requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded'
- optional :permissions, type: String, desc: 'The file permissions', default: 'read_only', values: %w[read_only read_write execute]
- end
-
- route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
- post ':id/secure_files' do
- secure_file = user_project.secure_files.new(
- name: params[:name],
- permissions: params[:permissions] || :read_only
- )
-
- secure_file.file = params[:file]
-
- file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
+ resource do
+ before do
+ authorize! :admin_secure_files, user_project
+ end
- if secure_file.save
- present secure_file, with: Entities::Ci::SecureFile
- else
- render_validation_error!(secure_file)
+ desc 'Upload a Secure File'
+ params do
+ requires :name, type: String, desc: 'The name of the file'
+ requires :file, types: [Rack::Multipart::UploadedFile, ::API::Validations::Types::WorkhorseFile], desc: 'The secure file to be uploaded'
+ optional :permissions, type: String, desc: 'The file permissions', default: 'read_only', values: %w[read_only read_write execute]
+ end
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
+ post ':id/secure_files' do
+ secure_file = user_project.secure_files.new(
+ name: params[:name],
+ permissions: params[:permissions] || :read_only
+ )
+
+ secure_file.file = params[:file]
+
+ file_too_large! unless secure_file.file.size < ::Ci::SecureFile::FILE_SIZE_LIMIT.to_i
+
+ if secure_file.save
+ present secure_file, with: Entities::Ci::SecureFile
+ else
+ render_validation_error!(secure_file)
+ end
end
- end
- desc 'Delete an individual Secure File'
- route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
- delete ':id/secure_files/:secure_file_id' do
- secure_file = user_project.secure_files.find(params[:secure_file_id])
+ desc 'Delete an individual Secure File'
+ route_setting :authentication, basic_auth_personal_access_token: true, job_token_allowed: true
+ delete ':id/secure_files/:secure_file_id' do
+ secure_file = user_project.secure_files.find(params[:secure_file_id])
- secure_file.destroy!
+ ::Ci::DestroySecureFileService.new(user_project, current_user).execute(secure_file)
- no_content!
+ no_content!
+ end
end
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 8b8d8192524..dedda82091f 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -44,6 +44,8 @@ module API
use :pagination
end
get ':id/repository/commits', urgency: :low do
+ not_found! 'Repository' unless user_project.repository_exists?
+
path = params[:path]
before = params[:until]
after = params[:since]
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
index edf20b6aebe..e241633fa8b 100644
--- a/lib/api/concerns/packages/conan_endpoints.rb
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -38,6 +38,10 @@ module API
helpers ::API::Helpers::Packages::Conan::ApiHelpers
helpers ::API::Helpers::RelatedResourcesHelpers
+ rescue_from ActiveRecord::RecordInvalid do |e|
+ render_api_error!(e.message, 400)
+ end
+
before do
require_packages_enabled!
@@ -285,6 +289,7 @@ module API
params do
requires :file_name, type: String, desc: 'Package file name', values: CONAN_FILES
end
+
namespace 'export/:file_name', requirements: FILE_NAME_REQUIREMENTS do
desc 'Download recipe files' do
detail 'This feature was introduced in GitLab 12.6'
diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb
index 9cd3e449687..17d667fb6df 100644
--- a/lib/api/container_repositories.rb
+++ b/lib/api/container_repositories.rb
@@ -23,11 +23,17 @@ module API
params do
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
optional :tags_count, type: Boolean, default: false, desc: 'Determines if the tags count should be included'
+ optional :size, type: Boolean, default: false, desc: 'Determines if the size should be included'
end
get ':id' do
authorize!(:read_container_image, repository)
- present repository, with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count], user: current_user
+ present repository,
+ with: Entities::ContainerRegistry::Repository,
+ tags: params[:tags],
+ tags_count: params[:tags_count],
+ size: params[:size],
+ user: current_user
end
end
end
diff --git a/lib/api/deploy_tokens.rb b/lib/api/deploy_tokens.rb
index e9beeb18d62..074c307e881 100644
--- a/lib/api/deploy_tokens.rb
+++ b/lib/api/deploy_tokens.rb
@@ -93,6 +93,21 @@ module API
end
end
+ desc 'Get a project deploy token' do
+ detail 'This feature was introduced in GitLab 14.9'
+ success Entities::DeployToken
+ end
+ params do
+ requires :token_id, type: Integer, desc: 'The deploy token ID'
+ end
+ get ':id/deploy_tokens/:token_id' do
+ authorize!(:read_deploy_token, user_project)
+
+ deploy_token = user_project.deploy_tokens.find(params[:token_id])
+
+ present deploy_token, with: Entities::DeployToken
+ end
+
desc 'Delete a project deploy token' do
detail 'This feature was introduced in GitLab 12.9'
end
@@ -159,6 +174,21 @@ module API
end
end
+ desc 'Get a group deploy token' do
+ detail 'This feature was introduced in GitLab 14.9'
+ success Entities::DeployToken
+ end
+ params do
+ requires :token_id, type: Integer, desc: 'The deploy token ID'
+ end
+ get ':id/deploy_tokens/:token_id' do
+ authorize!(:read_deploy_token, user_group)
+
+ deploy_token = user_group.deploy_tokens.find(params[:token_id])
+
+ present deploy_token, with: Entities::DeployToken
+ end
+
desc 'Delete a group deploy token' do
detail 'This feature was introduced in GitLab 12.9'
end
diff --git a/lib/api/entities/broadcast_message.rb b/lib/api/entities/broadcast_message.rb
index e42b110adbe..5a31d64fd86 100644
--- a/lib/api/entities/broadcast_message.rb
+++ b/lib/api/entities/broadcast_message.rb
@@ -3,7 +3,7 @@
module API
module Entities
class BroadcastMessage < Grape::Entity
- expose :id, :message, :starts_at, :ends_at, :color, :font, :target_path, :broadcast_type, :dismissable
+ expose :id, :message, :starts_at, :ends_at, :color, :font, :target_access_levels, :target_path, :broadcast_type, :dismissable
expose :active?, as: :active
end
end
diff --git a/lib/api/entities/ci/runner.rb b/lib/api/entities/ci/runner.rb
index a6944b8c925..e29d55771f2 100644
--- a/lib/api/entities/ci/runner.rb
+++ b/lib/api/entities/ci/runner.rb
@@ -7,7 +7,7 @@ module API
expose :id
expose :description
expose :ip_address
- expose :active # TODO Remove in %15.0 in favor of `paused` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/351109
+ expose :active # TODO Remove in %16.0 in favor of `paused` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/351109
expose :paused do |runner|
!runner.active
end
@@ -16,7 +16,7 @@ module API
expose :name
expose :online?, as: :online
# DEPRECATED
- # TODO Remove in %15.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
+ # TODO Remove in %16.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
expose :deprecated_rest_status, as: :status
end
end
diff --git a/lib/api/entities/ci/secure_file.rb b/lib/api/entities/ci/secure_file.rb
index 041c864156b..b60a1a6ac90 100644
--- a/lib/api/entities/ci/secure_file.rb
+++ b/lib/api/entities/ci/secure_file.rb
@@ -9,6 +9,7 @@ module API
expose :permissions
expose :checksum
expose :checksum_algorithm
+ expose :created_at
end
end
end
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
index c9c2c5156cc..2fdfac40c32 100644
--- a/lib/api/entities/container_registry.rb
+++ b/lib/api/entities/container_registry.rb
@@ -22,6 +22,7 @@ module API
expose :tags_count, if: -> (_, options) { options[:tags_count] }
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) }
+ expose :size, if: -> (_, options) { options[:size] }
private
diff --git a/lib/api/entities/error_tracking.rb b/lib/api/entities/error_tracking.rb
index b55cba05ea0..163bda92680 100644
--- a/lib/api/entities/error_tracking.rb
+++ b/lib/api/entities/error_tracking.rb
@@ -9,6 +9,12 @@ module API
expose :sentry_external_url
expose :api_url
expose :integrated
+
+ def integrated
+ return false unless ::Feature.enabled?(:integrated_error_tracking, object.project)
+
+ object.integrated_client?
+ end
end
class ClientKey < Grape::Entity
diff --git a/lib/api/entities/issuable_time_stats.rb b/lib/api/entities/issuable_time_stats.rb
index 7c3452a10a1..f93b4651b1f 100644
--- a/lib/api/entities/issuable_time_stats.rb
+++ b/lib/api/entities/issuable_time_stats.rb
@@ -18,7 +18,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def total_time_spent
# Avoids an N+1 query since timelogs are preloaded
- object.timelogs.map(&:time_spent).sum
+ object.timelogs.sum(&:time_spent)
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/lib/api/entities/label_basic.rb b/lib/api/entities/label_basic.rb
index ed52688638e..7c846180558 100644
--- a/lib/api/entities/label_basic.rb
+++ b/lib/api/entities/label_basic.rb
@@ -3,7 +3,11 @@
module API
module Entities
class LabelBasic < Grape::Entity
- expose :id, :name, :color, :description, :description_html, :text_color
+ expose :id, :name, :description, :description_html, :text_color
+
+ expose :color do |label, options|
+ label.color.to_s
+ end
end
end
end
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index 74097dc2883..8f9a8add938 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -74,6 +74,7 @@ module API
expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
expose(:container_registry_access_level) { |project, options| project.project_feature.string_access_level(:container_registry) }
+ expose(:security_and_compliance_access_level) { |project, options| project.project_feature.string_access_level(:security_and_compliance) }
expose :emails_disabled
expose :shared_runners_enabled
diff --git a/lib/api/entities/project_integration.rb b/lib/api/entities/project_integration.rb
index 649e4d015b8..155136d2f80 100644
--- a/lib/api/entities/project_integration.rb
+++ b/lib/api/entities/project_integration.rb
@@ -5,19 +5,8 @@ module API
class ProjectIntegration < Entities::ProjectIntegrationBasic
# Expose serialized properties
expose :properties do |integration, options|
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
-
- attributes =
- if integration.data_fields_present?
- integration.data_fields.as_json.keys
- else
- integration.properties.keys
- end
-
- attributes &= integration.api_field_names
-
- attributes.each_with_object({}) do |attribute, hash|
- hash[attribute] = integration.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
+ integration.api_field_names.to_h do |name|
+ [name, integration.public_send(name)] # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/lib/api/entities/user_safe.rb b/lib/api/entities/user_safe.rb
index c7349026a88..fb99c2e960d 100644
--- a/lib/api/entities/user_safe.rb
+++ b/lib/api/entities/user_safe.rb
@@ -5,14 +5,7 @@ module API
class UserSafe < Grape::Entity
expose :id, :username
expose :name do |user|
- next user.name unless user.project_bot?
-
- next user.name if options[:current_user]&.can?(:read_project, user.projects.first)
-
- # If the requester does not have permission to read the project bot name,
- # the API returns an arbitrary string. UI changes will be addressed in a follow up issue:
- # https://gitlab.com/gitlab-org/gitlab/-/issues/346058
- '****'
+ user.redacted_name(options[:current_user])
end
end
end
diff --git a/lib/api/entities/wiki_page.rb b/lib/api/entities/wiki_page.rb
index a8ef0bd857c..43af6a336d2 100644
--- a/lib/api/entities/wiki_page.rb
+++ b/lib/api/entities/wiki_page.rb
@@ -3,7 +3,15 @@
module API
module Entities
class WikiPage < WikiPageBasic
- expose :content
+ include ::MarkupHelper
+
+ expose :content do |wiki_page, options|
+ options[:render_html] ? render_wiki_content(wiki_page, ref: wiki_page.version.id) : wiki_page.content
+ end
+
+ expose :encoding do |wiki_page|
+ wiki_page.content.encoding.name
+ end
end
end
end
diff --git a/lib/api/error_tracking/collector.rb b/lib/api/error_tracking/collector.rb
index 13fda356257..22a4e04a91c 100644
--- a/lib/api/error_tracking/collector.rb
+++ b/lib/api/error_tracking/collector.rb
@@ -28,8 +28,8 @@ module API
end
def feature_enabled?
- project.error_tracking_setting&.enabled? &&
- project.error_tracking_setting&.integrated_client?
+ Feature.enabled?(:integrated_error_tracking, project) &&
+ project.error_tracking_setting&.integrated_enabled?
end
def find_client_key(public_key)
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 8cca3378eec..97230976482 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -14,8 +14,6 @@ module API
before do
require_packages_enabled!
authenticate_non_get!
-
- require_generic_packages_available!
end
params do
@@ -113,10 +111,6 @@ module API
include ::API::Helpers::PackagesHelpers
include ::API::Helpers::Packages::BasicAuthHelpers
- def require_generic_packages_available!
- not_found! unless Feature.enabled?(:generic_packages, project, default_enabled: true)
- end
-
def project
authorized_user_project
end
diff --git a/lib/api/group_clusters.rb b/lib/api/group_clusters.rb
index 81944a653c8..a5a60ce8741 100644
--- a/lib/api/group_clusters.rb
+++ b/lib/api/group_clusters.rb
@@ -4,7 +4,10 @@ module API
class GroupClusters < ::API::Base
include PaginationParams
- before { authenticate! }
+ before do
+ authenticate!
+ ensure_feature_enabled!
+ end
feature_category :kubernetes_management
@@ -133,6 +136,10 @@ module API
def update_cluster_params
declared_params(include_missing: false).without(:cluster_id)
end
+
+ def ensure_feature_enabled!
+ not_found! unless Feature.enabled?(:certificate_based_clusters, user_group, default_enabled: :yaml, type: :ops)
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 184fe7868a5..de9d42bdce7 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -119,7 +119,7 @@ module API
def find_project(id)
return unless id
- projects = Project.without_deleted
+ projects = Project.without_deleted.not_hidden
if id.is_a?(Integer) || id =~ /^\d+$/
projects.find_by(id: id)
@@ -474,17 +474,22 @@ module API
model.errors.messages
end
- def render_spam_error!
- render_api_error!({ error: 'Spam detected' }, 400)
+ def render_api_error!(message, status)
+ render_structured_api_error!({ 'message' => message }, status)
end
- def render_api_error!(message, status)
+ def render_structured_api_error!(hash, status)
+ # Use this method instead of `render_api_error!` when you have additional top-level
+ # hash entries in addition to 'message' which need to be passed to `#error!`
+ set_status_code_in_env(status)
+ error!(hash, status, header)
+ end
+
+ def set_status_code_in_env(status)
# grape-logging doesn't pass the status code, so this is a
# workaround for getting that information in the loggers:
# https://github.com/aserafin/grape_logging/issues/71
env[API_RESPONSE_STATUS_CODE] = Rack::Utils.status_code(status)
-
- error!({ 'message' => message }, status, header)
end
def handle_api_exception(exception)
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 86dedc12fca..0fbd0e6be44 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -440,6 +440,32 @@ module API
},
chat_notification_events
].flatten,
+ 'harbor' => [
+ {
+ required: true,
+ name: :url,
+ type: String,
+ desc: 'The base URL to the Harbor instance which is being linked to this GitLab project. For example, https://demo.goharbor.io.'
+ },
+ {
+ required: true,
+ name: :project_name,
+ type: String,
+ desc: 'The Project name to the Harbor instance. For example, testproject.'
+ },
+ {
+ required: true,
+ name: :username,
+ type: String,
+ desc: 'The username created from Harbor interface.'
+ },
+ {
+ required: true,
+ name: :password,
+ type: String,
+ desc: 'The password of the user.'
+ }
+ ],
'irker' => [
{
required: true,
@@ -856,6 +882,7 @@ module API
::Integrations::ExternalWiki,
::Integrations::Flowdock,
::Integrations::HangoutsChat,
+ ::Integrations::Harbor,
::Integrations::Irker,
::Integrations::Jenkins,
::Integrations::Jira,
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 00f745067e7..f1125899f8c 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -36,6 +36,7 @@ module API
optional :operations_access_level, type: String, values: %w(disabled private enabled), desc: 'Operations access level. One of `disabled`, `private` or `enabled`'
optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
optional :container_registry_access_level, type: String, values: %w(disabled private enabled), desc: 'Controls visibility of the container registry. One of `disabled`, `private` or `enabled`. `private` will make the container registry accessible only to project members (reporter role and above). `enabled` will make the container registry accessible to everyone who has access to the project. `disabled` will disable the container registry'
+ optional :security_and_compliance_access_level, type: String, values: %w(disabled private enabled), desc: 'Security and compliance access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
@@ -118,6 +119,7 @@ module API
def self.update_params_at_least_one_of
[
:allow_merge_on_skipped_pipeline,
+ :analytics_access_level,
:autoclose_referenced_issues,
:auto_devops_enabled,
:auto_devops_deploy_strategy,
@@ -145,6 +147,7 @@ module API
:name,
:only_allow_merge_if_all_discussions_are_resolved,
:only_allow_merge_if_pipeline_succeeds,
+ :operations_access_level,
:pages_access_level,
:path,
:printing_merge_request_link_enabled,
@@ -154,6 +157,7 @@ module API
:request_access_enabled,
:resolve_outdated_diff_discussions,
:restrict_user_defined_variables,
+ :security_and_compliance_access_level,
:squash_option,
:shared_runners_enabled,
:snippets_access_level,
diff --git a/lib/api/helpers/wikis_helpers.rb b/lib/api/helpers/wikis_helpers.rb
index 4a14dc1f40a..a9cd0e2919d 100644
--- a/lib/api/helpers/wikis_helpers.rb
+++ b/lib/api/helpers/wikis_helpers.rb
@@ -13,8 +13,8 @@ module API
raise "Unknown wiki container #{kind}"
end
- def wiki_page
- Wiki.for_container(container, current_user).find_page(params[:slug]) || not_found!('Wiki Page')
+ def wiki_page(version = nil)
+ Wiki.for_container(container, current_user).find_page(params[:slug], version.presence) || not_found!('Wiki Page')
end
def commit_params(attrs)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 48157a91477..9c527f28d44 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -92,6 +92,8 @@ module API
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
end
+ send_git_audit_streaming_event(protocol: params[:protocol], action: params[:action])
+
response_with_status(**payload)
when ::Gitlab::GitAccessResult::CustomAction
response_with_status(code: 300, payload: check_result.payload, gl_console_messages: check_result.console_messages)
@@ -100,6 +102,10 @@ module API
end
end
+ def send_git_audit_streaming_event(msg)
+ # Defined in EE
+ end
+
def access_check!(actor, params)
access_checker = access_checker_for(actor, params[:protocol])
access_checker.check(params[:action], params[:changes]).tap do |result|
diff --git a/lib/api/internal/kubernetes.rb b/lib/api/internal/kubernetes.rb
index 3977da4bda4..df887a83c4f 100644
--- a/lib/api/internal/kubernetes.rb
+++ b/lib/api/internal/kubernetes.rb
@@ -39,6 +39,7 @@ module API
def gitaly_repository(project)
{
+ default_branch: project.default_branch_or_main,
storage_name: project.repository_storage,
relative_path: project.disk_path + '.git',
gl_repository: repo_type.identifier_for_container(project),
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index a5d6a6d7cf3..e9bb9fe7a97 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -4,6 +4,7 @@ module API
class Issues < ::API::Base
include PaginationParams
helpers Helpers::IssuesHelpers
+ helpers SpammableActions::CaptchaCheck::RestApiActionsSupport
before { authenticate_non_get! }
@@ -262,8 +263,6 @@ module API
post ':id/issues' do
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/21140')
- check_rate_limit!(:issues_create, scope: current_user) if Feature.disabled?("rate_limited_service_issues_create", user_project, default_enabled: :yaml)
-
authorize! :create_issue, user_project
issue_params = declared_params(include_missing: false)
@@ -277,14 +276,12 @@ module API
params: issue_params,
spam_params: spam_params).execute
- if issue.spam?
- render_api_error!({ error: 'Spam detected' }, 400)
- end
-
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
else
- render_validation_error!(issue)
+ with_captcha_check_rest_api(spammable: issue) do
+ render_validation_error!(issue)
+ end
end
rescue ::ActiveRecord::RecordNotUnique
render_api_error!('Duplicated issue', 409)
@@ -322,12 +319,12 @@ module API
params: update_params,
spam_params: spam_params).execute(issue)
- render_spam_error! if issue.spam?
-
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
else
- render_validation_error!(issue)
+ with_captcha_check_rest_api(spammable: issue) do
+ render_validation_error!(issue)
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index f7df8d33418..de9a2a198d9 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -304,10 +304,6 @@ module API
end
get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- project = merge_request.project
-
- not_found! unless project.context_commits_enabled?
-
context_commits =
paginate(merge_request.merge_request_context_commits).map(&:to_commit)
@@ -328,9 +324,6 @@ module API
end
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- project = merge_request.project
-
- not_found! unless project.context_commits_enabled?
authorize!(:update_merge_request, merge_request)
@@ -351,9 +344,6 @@ module API
delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do
commit_ids = params[:commits]
merge_request = find_merge_request_with_access(params[:merge_request_iid])
- project = merge_request.project
-
- not_found! unless project.context_commits_enabled?
authorize!(:destroy_merge_request, merge_request)
project = merge_request.target_project
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 93ef77d5a62..b260f5289b3 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -75,6 +75,7 @@ module API
requires :body, type: String, desc: 'The content of a note'
optional :confidential, type: Boolean, desc: 'Confidentiality note flag, default is false'
optional :created_at, type: String, desc: 'The creation date of the note'
+ optional :merge_request_diff_head_sha, type: String, desc: 'The SHA of the head commit'
end
post ":id/#{noteables_str}/:noteable_id/notes", feature_category: feature_category do
allowlist =
@@ -87,7 +88,8 @@ module API
noteable_type: noteables_str.classify,
noteable_id: noteable.id,
confidential: params[:confidential],
- created_at: params[:created_at]
+ created_at: params[:created_at],
+ merge_request_diff_head_sha: params[:merge_request_diff_head_sha]
}
note = create_note(noteable, opts)
diff --git a/lib/api/package_files.rb b/lib/api/package_files.rb
index e80355e80c7..4861c0c740e 100644
--- a/lib/api/package_files.rb
+++ b/lib/api/package_files.rb
@@ -29,7 +29,7 @@ module API
.new(user_project, params[:package_id]).execute
package_files = package.installable_package_files
- .preload_pipelines
+ .preload_pipelines.order_id_asc
present paginate(package_files), with: ::API::Entities::PackageFile
end
diff --git a/lib/api/project_clusters.rb b/lib/api/project_clusters.rb
index 6785b28ddef..8bba67a53af 100644
--- a/lib/api/project_clusters.rb
+++ b/lib/api/project_clusters.rb
@@ -4,7 +4,10 @@ module API
class ProjectClusters < ::API::Base
include PaginationParams
- before { authenticate! }
+ before do
+ authenticate!
+ ensure_feature_enabled!
+ end
feature_category :kubernetes_management
@@ -138,6 +141,10 @@ module API
def update_cluster_params
declared_params(include_missing: false).without(:cluster_id)
end
+
+ def ensure_feature_enabled!
+ not_found! unless Feature.enabled?(:certificate_based_clusters, user_project, default_enabled: :yaml, type: :ops)
+ end
end
end
end
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index a3d76e571a9..fae170d638b 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -87,14 +87,16 @@ module API
validate_file!
- response = ::Import::GitlabProjects::CreateProjectFromUploadedFileService.new(
+ response = ::Import::GitlabProjects::CreateProjectService.new(
current_user,
- path: import_params[:path],
- namespace: namespace_from(import_params, current_user),
- name: import_params[:name],
- file: import_params[:file],
- overwrite: import_params[:overwrite],
- override: filtered_override_params(import_params)
+ params: {
+ path: import_params[:path],
+ namespace: namespace_from(import_params, current_user),
+ name: import_params[:name],
+ file: import_params[:file],
+ overwrite: import_params[:overwrite],
+ override: filtered_override_params(import_params)
+ }
).execute
if response.success?
@@ -137,14 +139,66 @@ module API
check_rate_limit! :project_import, scope: [current_user, :project_import]
- response = ::Import::GitlabProjects::CreateProjectFromRemoteFileService.new(
+ response = ::Import::GitlabProjects::CreateProjectService.new(
current_user,
- path: import_params[:path],
- namespace: namespace_from(import_params, current_user),
- name: import_params[:name],
- remote_import_url: import_params[:url],
- overwrite: import_params[:overwrite],
- override: filtered_override_params(import_params)
+ params: {
+ path: import_params[:path],
+ namespace: namespace_from(import_params, current_user),
+ name: import_params[:name],
+ remote_import_url: import_params[:url],
+ overwrite: import_params[:overwrite],
+ override: filtered_override_params(import_params)
+ },
+ file_acquisition_strategy: ::Import::GitlabProjects::FileAcquisitionStrategies::RemoteFile
+ ).execute
+
+ if response.success?
+ present(response.payload, with: Entities::ProjectImportStatus)
+ else
+ render_api_error!(response.message, response.http_status)
+ end
+ end
+
+ params do
+ requires :region, type: String, desc: 'AWS region'
+ requires :bucket_name, type: String, desc: 'Bucket name'
+ requires :file_key, type: String, desc: 'File key'
+ requires :access_key_id, type: String, desc: 'Access key id'
+ requires :secret_access_key, type: String, desc: 'Secret access key'
+ requires :path, type: String, desc: 'The new project path and name'
+ optional :name, type: String, desc: 'The name of the project to be imported. Defaults to the path of the project if not provided.'
+ optional :namespace, type: String, desc: "The ID or name of the namespace that the project will be imported into. Defaults to the current user's namespace."
+ optional :overwrite, type: Boolean, default: false, desc: 'If there is a project in the same namespace and with the same name overwrite it'
+ optional :override_params,
+ type: Hash,
+ desc: 'New project params to override values in the export' do
+ use :optional_project_params
+ end
+ end
+ desc 'Create a new project import using a file from AWS S3' do
+ detail 'This feature was introduced in GitLab 14.9.'
+ success Entities::ProjectImportStatus
+ end
+ post 'remote-import-s3' do
+ not_found! unless ::Feature.enabled?(:import_project_from_remote_file_s3, default_enabled: :yaml)
+
+ check_rate_limit! :project_import, scope: [current_user, :project_import]
+
+ response = ::Import::GitlabProjects::CreateProjectService.new(
+ current_user,
+ params: {
+ path: import_params[:path],
+ namespace: namespace_from(import_params, current_user),
+ name: import_params[:name],
+ overwrite: import_params[:overwrite],
+ override: filtered_override_params(import_params),
+ region: import_params[:region],
+ bucket_name: import_params[:bucket_name],
+ file_key: import_params[:file_key],
+ access_key_id: import_params[:access_key_id],
+ secret_access_key: import_params[:secret_access_key]
+ },
+ file_acquisition_strategy: ::Import::GitlabProjects::FileAcquisitionStrategies::RemoteFileS3
).execute
if response.success?
diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb
index fdbfdf1f7a9..a80e45637dc 100644
--- a/lib/api/project_snippets.rb
+++ b/lib/api/project_snippets.rb
@@ -13,6 +13,7 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
helpers Helpers::SnippetsHelpers
+ helpers SpammableActions::CaptchaCheck::RestApiActionsSupport
helpers do
def check_snippets_enabled
forbidden! unless user_project.feature_available?(:snippets, current_user)
@@ -82,9 +83,9 @@ module API
if service_response.success?
present snippet, with: Entities::ProjectSnippet, current_user: current_user
else
- render_spam_error! if snippet.spam?
-
- render_api_error!({ error: service_response.message }, service_response.http_status)
+ with_captcha_check_rest_api(spammable: snippet) do
+ render_api_error!({ error: service_response.message }, service_response.http_status)
+ end
end
end
@@ -124,9 +125,9 @@ module API
if service_response.success?
present snippet, with: Entities::ProjectSnippet, current_user: current_user
else
- render_spam_error! if snippet.spam?
-
- render_api_error!({ error: service_response.message }, service_response.http_status)
+ with_captcha_check_rest_api(spammable: snippet) do
+ render_api_error!({ error: service_response.message }, service_response.http_status)
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 706c0702fce..86f36d4fc00 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -170,9 +170,9 @@ module API
params do
requires :content, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
- requires :requires_python, type: String
requires :name, type: String
requires :version, type: String
+ optional :requires_python, type: String
optional :md5_digest, type: String
optional :sha256_digest, type: String
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index c3632c812f3..2e21f591667 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -248,6 +248,8 @@ module API
changelog = service.execute(commit_to_changelog: false)
present changelog, with: Entities::Changelog
+ rescue Gitlab::Changelog::Error => ex
+ render_api_error!("Failed to generate the changelog: #{ex.message}", 422)
end
desc 'Generates a changelog section for a release and commits it in a changelog file' do
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 60a7e944b43..4ef8fef329c 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -7,7 +7,7 @@ module API
before do
authenticate!
- check_rate_limit!(:user_email_lookup, scope: [current_user]) if search_service.params.email_lookup?
+ check_rate_limit!(:search_rate_limit, scope: [current_user])
end
feature_category :global_search
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index c4b17a62b59..9a3c68bc854 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -9,6 +9,7 @@ module API
resource :snippets do
helpers Helpers::SnippetsHelpers
+ helpers SpammableActions::CaptchaCheck::RestApiActionsSupport
helpers do
def snippets_for_current_user
SnippetsFinder.new(current_user, author: current_user).execute
@@ -91,9 +92,9 @@ module API
if service_response.success?
present snippet, with: Entities::PersonalSnippet, current_user: current_user
else
- render_spam_error! if snippet.spam?
-
- render_api_error!({ error: service_response.message }, service_response.http_status)
+ with_captcha_check_rest_api(spammable: snippet) do
+ render_api_error!({ error: service_response.message }, service_response.http_status)
+ end
end
end
@@ -135,9 +136,9 @@ module API
if service_response.success?
present snippet, with: Entities::PersonalSnippet, current_user: current_user
else
- render_spam_error! if snippet.spam?
-
- render_api_error!({ error: service_response.message }, service_response.http_status)
+ with_captcha_check_rest_api(spammable: snippet) do
+ render_api_error!({ error: service_response.message }, service_response.http_status)
+ end
end
end
diff --git a/lib/api/statistics.rb b/lib/api/statistics.rb
index 6818c04fd2e..a12a2ed08d7 100644
--- a/lib/api/statistics.rb
+++ b/lib/api/statistics.rb
@@ -12,7 +12,7 @@ module API
desc 'Get the current application statistics' do
success Entities::ApplicationStatistics
end
- get "application/statistics" do
+ get "application/statistics", urgency: :low do
counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS)
present counts, with: Entities::ApplicationStatistics
end
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index e4133713c1f..7c91fbd36d9 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -22,6 +22,18 @@ module API
present paginate(SystemHook.all), with: Entities::Hook
end
+ desc 'Get a hook' do
+ success Entities::Hook
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the system hook'
+ end
+ get ":id" do
+ hook = SystemHook.find(params[:id])
+
+ present hook, with: Entities::Hook
+ end
+
desc 'Create a new system hook' do
success Entities::Hook
end
diff --git a/lib/api/topics.rb b/lib/api/topics.rb
index b9c2bcc2da8..e4a1fa2367e 100644
--- a/lib/api/topics.rb
+++ b/lib/api/topics.rb
@@ -77,5 +77,19 @@ module API
render_validation_error!(topic)
end
end
+
+ desc 'Delete a topic' do
+ detail 'This feature was introduced in GitLab 14.9.'
+ end
+ params do
+ requires :id, type: Integer, desc: 'ID of project topic'
+ end
+ delete 'topics/:id' do
+ authenticated_as_admin!
+
+ topic = ::Projects::Topic.find(params[:id])
+
+ destroy_conditionally!(topic)
+ end
end
end
diff --git a/lib/api/user_counts.rb b/lib/api/user_counts.rb
index 634dd0f2179..e5dfac3b1a1 100644
--- a/lib/api/user_counts.rb
+++ b/lib/api/user_counts.rb
@@ -11,13 +11,19 @@ module API
get do
unauthorized! unless current_user
- {
+ counts = {
merge_requests: current_user.assigned_open_merge_requests_count, # @deprecated
assigned_issues: current_user.assigned_open_issues_count,
assigned_merge_requests: current_user.assigned_open_merge_requests_count,
review_requested_merge_requests: current_user.review_requested_open_merge_requests_count,
todos: current_user.todos_pending_count
}
+
+ if Feature.enabled?(:mr_attention_requests, default_enabled: :yaml)
+ counts[:attention_requests] = current_user.attention_requested_open_merge_requests_count
+ end
+
+ counts
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 6d4f12d80f8..0f710e0a307 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -142,13 +142,11 @@ module API
get ":id", feature_category: :users do
forbidden!('Not authorized!') unless current_user
- if Feature.enabled?(:rate_limit_user_by_id_endpoint, type: :development)
- unless current_user.admin?
- check_rate_limit!(:users_get_by_id,
- scope: current_user,
- users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
- )
- end
+ unless current_user.admin?
+ check_rate_limit!(:users_get_by_id,
+ scope: current_user,
+ users_allowlist: Gitlab::CurrentSettings.current_application_settings.users_get_by_id_limit_allowlist
+ )
end
user = User.find_by(id: params[:id])
@@ -383,6 +381,23 @@ module API
present paginate(keys), with: Entities::SSHKey
end
+ desc 'Get a SSH key of a specified user.' do
+ success Entities::SSHKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ get ':id/keys/:key_id', requirements: API::USER_REQUIREMENTS, feature_category: :authentication_and_authorization do
+ user = find_user(params[:id])
+ not_found!('User') unless user && can?(current_user, :read_user, user)
+
+ key = user.keys.find_by(id: params[:key_id]) # rubocop: disable CodeReuse/ActiveRecord
+ not_found!('Key') unless key
+
+ present key, with: Entities::SSHKey
+ end
+
desc 'Delete an existing SSH key from a specified user. Available only for admins.' do
success Entities::SSHKey
end
@@ -687,6 +702,8 @@ module API
if user.ldap_blocked?
forbidden!('LDAP blocked users cannot be modified by the API')
+ elsif current_user == user
+ forbidden!('The API initiating user cannot be blocked by the API')
end
break if user.blocked?
diff --git a/lib/api/validations/validators/file_path.rb b/lib/api/validations/validators/file_path.rb
index 246c445658f..268ddc29d4e 100644
--- a/lib/api/validations/validators/file_path.rb
+++ b/lib/api/validations/validators/file_path.rb
@@ -8,8 +8,7 @@ module API
options = @option.is_a?(Hash) ? @option : {}
path_allowlist = options.fetch(:allowlist, [])
path = params[attr_name]
- path = Gitlab::Utils.check_path_traversal!(path)
- Gitlab::Utils.check_allowed_absolute_path!(path, path_allowlist)
+ Gitlab::Utils.check_allowed_absolute_path_and_path_traversal!(path, path_allowlist)
rescue StandardError
raise Grape::Exceptions::Validation.new(
params: [@scope.full_name(attr_name)],
diff --git a/lib/api/wikis.rb b/lib/api/wikis.rb
index fdce3c5ce18..e90d88940a5 100644
--- a/lib/api/wikis.rb
+++ b/lib/api/wikis.rb
@@ -45,11 +45,13 @@ module API
end
params do
requires :slug, type: String, desc: 'The slug of a wiki page'
+ optional :version, type: String, desc: 'The version hash of a wiki page'
+ optional :render_html, type: Boolean, default: false, desc: 'Render content to HTML'
end
get ':id/wikis/:slug' do
authorize! :read_wiki, container
- present wiki_page, with: Entities::WikiPage
+ present wiki_page(params[:version]), with: Entities::WikiPage, render_html: params[:render_html]
end
desc 'Create a wiki page' do