diff options
Diffstat (limited to 'lib/api')
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 |