summaryrefslogtreecommitdiff
path: root/lib/api
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-22 11:31:16 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-22 11:31:16 +0000
commit905c1110b08f93a19661cf42a276c7ea90d0a0ff (patch)
tree756d138db422392c00471ab06acdff92c5a9b69c /lib/api
parent50d93f8d1686950fc58dda4823c4835fd0d8c14b (diff)
downloadgitlab-ce-905c1110b08f93a19661cf42a276c7ea90d0a0ff.tar.gz
Add latest changes from gitlab-org/gitlab@12-4-stable-ee
Diffstat (limited to 'lib/api')
-rw-r--r--lib/api/api_guard.rb24
-rw-r--r--lib/api/commit_statuses.rb20
-rw-r--r--lib/api/commits.rb9
-rw-r--r--lib/api/deploy_keys.rb18
-rw-r--r--lib/api/deployments.rb82
-rw-r--r--lib/api/entities.rb84
-rw-r--r--lib/api/group_labels.rb52
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/helpers/graphql_helpers.rb2
-rw-r--r--lib/api/helpers/groups_helpers.rb10
-rw-r--r--lib/api/helpers/label_helpers.rb46
-rw-r--r--lib/api/helpers/runner.rb5
-rw-r--r--lib/api/internal/base.rb76
-rw-r--r--lib/api/internal/pages.rb9
-rw-r--r--lib/api/issues.rb3
-rw-r--r--lib/api/labels.rb70
-rw-r--r--lib/api/members.rb21
-rw-r--r--lib/api/notes.rb2
-rw-r--r--lib/api/project_container_repositories.rb12
-rw-r--r--lib/api/project_import.rb2
-rw-r--r--lib/api/protected_branches.rb4
-rw-r--r--lib/api/runner.rb8
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/users.rb38
-rw-r--r--lib/api/version.rb5
26 files changed, 487 insertions, 121 deletions
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index a3fa7cd5cf9..02ea321df67 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -17,6 +17,8 @@ module API
request.access_token
end
+ use AdminModeMiddleware
+
helpers HelperMethods
install_error_responders(base)
@@ -52,6 +54,11 @@ module API
forbidden!(api_access_denied_message(user))
end
+ # Set admin mode for API requests (if admin)
+ if Feature.enabled?(:user_mode_in_session)
+ Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(skip_password_validation: true)
+ end
+
user
end
@@ -141,5 +148,22 @@ module API
end
end
end
+
+ class AdminModeMiddleware < ::Grape::Middleware::Base
+ def initialize(app, **options)
+ super
+ end
+
+ def call(env)
+ if Feature.enabled?(:user_mode_in_session)
+ session = {}
+ Gitlab::Session.with_session(session) do
+ app.call(env)
+ end
+ else
+ app.call(env)
+ end
+ end
+ end
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index d58a5e214ed..d108c811f4b 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -58,7 +58,6 @@ module API
post ':id/statuses/:sha' do
authorize! :create_commit_status, user_project
- commit = @project.commit(params[:sha])
not_found! 'Commit' unless commit
# Since the CommitStatus is attached to Ci::Pipeline (in the future Pipeline)
@@ -68,14 +67,15 @@ module API
# If we don't receive it, we will attach the CommitStatus to
# the first found branch on that commit
+ pipeline = all_matching_pipelines.first
+
ref = params[:ref]
+ ref ||= pipeline&.ref
ref ||= @project.repository.branch_names_contains(commit.sha).first
not_found! 'References for commit' unless ref
name = params[:name] || params[:context] || 'default'
- pipeline = @project.pipeline_for(ref, commit.sha, params[:pipeline_id])
-
unless pipeline
pipeline = @project.ci_pipelines.create!(
source: :external,
@@ -126,6 +126,20 @@ module API
end
end
# rubocop: enable CodeReuse/ActiveRecord
+ helpers do
+ def commit
+ strong_memoize(:commit) do
+ user_project.commit(params[:sha])
+ end
+ end
+
+ def all_matching_pipelines
+ pipelines = user_project.ci_pipelines.newest_first(sha: commit.sha)
+ pipelines = pipelines.for_ref(params[:ref]) if params[:ref]
+ pipelines = pipelines.for_id(params[:pipeline_id]) if params[:pipeline_id]
+ pipelines
+ end
+ end
end
end
end
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index a2f3e87ebd2..ffff40141de 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -37,6 +37,7 @@ module API
optional :path, type: String, desc: 'The file path'
optional :all, type: Boolean, desc: 'Every commit will be returned'
optional :with_stats, type: Boolean, desc: 'Stats about each commit will be added to the response'
+ optional :first_parent, type: Boolean, desc: 'Only include the first parent of merges'
use :pagination
end
get ':id/repository/commits' do
@@ -47,6 +48,7 @@ module API
offset = (params[:page] - 1) * params[:per_page]
all = params[:all]
with_stats = params[:with_stats]
+ first_parent = params[:first_parent]
commits = user_project.repository.commits(ref,
path: path,
@@ -54,11 +56,12 @@ module API
offset: offset,
before: before,
after: after,
- all: all)
+ all: all,
+ first_parent: first_parent)
commit_count =
- if all || path || before || after
- user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all)
+ if all || path || before || after || first_parent
+ user_project.repository.count_commits(ref: ref, path: path, before: before, after: after, all: all, first_parent: first_parent)
else
# Cacheable commit count.
user_project.repository.commit_count_for_ref(ref)
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index df6d2721977..e86bcc19b2b 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -115,14 +115,20 @@ module API
put ":id/deploy_keys/:key_id" do
deploy_keys_project = find_by_deploy_key(user_project, params[:key_id])
- authorize!(:update_deploy_key, deploy_keys_project.deploy_key)
+ if !can?(current_user, :update_deploy_key, deploy_keys_project.deploy_key) &&
+ !can?(current_user, :update_deploy_keys_project, deploy_keys_project)
+ forbidden!(nil)
+ end
+
+ update_params = {}
+ update_params[:can_push] = params[:can_push] if params.key?(:can_push)
+ update_params[:deploy_key_attributes] = { id: params[:key_id] }
- can_push = params[:can_push].nil? ? deploy_keys_project.can_push : params[:can_push]
- title = params[:title] || deploy_keys_project.deploy_key.title
+ if can?(current_user, :update_deploy_key, deploy_keys_project.deploy_key)
+ update_params[:deploy_key_attributes][:title] = params[:title] if params.key?(:title)
+ end
- result = deploy_keys_project.update(can_push: can_push,
- deploy_key_attributes: { id: params[:key_id],
- title: title })
+ result = deploy_keys_project.update(update_params)
if result
present deploy_keys_project, with: Entities::DeployKeysProject
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index eb45df31ff9..da882547071 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -42,6 +42,88 @@ module API
present deployment, with: Entities::Deployment
end
+
+ desc 'Creates a new deployment' do
+ detail 'This feature was introduced in GitLab 12.4'
+ success Entities::Deployment
+ end
+ params do
+ requires :environment,
+ type: String,
+ desc: 'The name of the environment to deploy to'
+
+ requires :sha,
+ type: String,
+ desc: 'The SHA of the commit that was deployed'
+
+ requires :ref,
+ type: String,
+ desc: 'The name of the branch or tag that was deployed'
+
+ requires :tag,
+ type: Boolean,
+ desc: 'A boolean indicating if the deployment ran for a tag'
+
+ requires :status,
+ type: String,
+ desc: 'The status of the deployment',
+ values: %w[running success failed canceled]
+ end
+ post ':id/deployments' do
+ authorize!(:create_deployment, user_project)
+ authorize!(:create_environment, user_project)
+
+ environment = user_project
+ .environments
+ .find_or_create_by_name(params[:environment])
+
+ unless environment.persisted?
+ render_validation_error!(deployment)
+ end
+
+ authorize!(:create_deployment, environment)
+
+ service = ::Deployments::CreateService
+ .new(environment, current_user, declared_params)
+
+ deployment = service.execute
+
+ if deployment.persisted?
+ present(deployment, with: Entities::Deployment, current_user: current_user)
+ else
+ render_validation_error!(deployment)
+ end
+ end
+
+ desc 'Updates an existing deployment' do
+ detail 'This feature was introduced in GitLab 12.4'
+ success Entities::Deployment
+ end
+ params do
+ requires :status,
+ type: String,
+ desc: 'The new status of the deployment',
+ values: %w[running success failed canceled]
+ end
+ put ':id/deployments/:deployment_id' do
+ authorize!(:read_deployment, user_project)
+
+ deployment = user_project.deployments.find(params[:deployment_id])
+
+ authorize!(:update_deployment, deployment)
+
+ if deployment.deployable
+ forbidden!('Deployments created using GitLab CI can not be updated using the API')
+ end
+
+ service = ::Deployments::UpdateService.new(deployment, declared_params)
+
+ if service.execute
+ present(deployment, with: Entities::Deployment, current_user: current_user)
+ else
+ render_validation_error!(deployment)
+ end
+ end
end
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 89951498489..91811efacd7 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -378,6 +378,13 @@ module API
class Group < BasicGroupDetails
expose :path, :description, :visibility
+ expose :share_with_group_lock
+ expose :require_two_factor_authentication
+ expose :two_factor_grace_period
+ expose :project_creation_level_str, as: :project_creation_level
+ expose :auto_devops_enabled
+ expose :subgroup_creation_level_str, as: :subgroup_creation_level
+ expose :emails_disabled
expose :lfs_enabled?, as: :lfs_enabled
expose :avatar_url do |group, options|
group.avatar_url(only_path: false)
@@ -682,6 +689,7 @@ module API
class PipelineBasic < Grape::Entity
expose :id, :sha, :ref, :status
+ expose :created_at, :updated_at
expose :web_url do |pipeline, _options|
Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline)
@@ -771,7 +779,7 @@ module API
end
class MergeRequest < MergeRequestBasic
- expose :subscribed do |merge_request, options|
+ expose :subscribed, if: -> (_, options) { options.fetch(:include_subscribed, true) } do |merge_request, options|
merge_request.subscribed?(options[:current_user], options[:project])
end
@@ -925,8 +933,8 @@ module API
end
class PushEventPayload < Grape::Entity
- expose :commit_count, :action, :ref_type, :commit_from, :commit_to
- expose :ref, :commit_title
+ expose :commit_count, :action, :ref_type, :commit_from, :commit_to, :ref,
+ :commit_title, :ref_count
end
class Event < Grape::Entity
@@ -965,13 +973,7 @@ module API
end
expose :target_url do |todo, options|
- target_type = todo.target_type.underscore
- target_url = "#{todo.parent.class.to_s.underscore}_#{target_type}_url"
- target_anchor = "note_#{todo.note_id}" if todo.note_id?
-
- Gitlab::Routing
- .url_helpers
- .public_send(target_url, todo.parent, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
+ todo_target_url(todo)
end
expose :body
@@ -983,6 +985,19 @@ module API
# see also https://gitlab.com/gitlab-org/gitlab-foss/issues/59719
::API::Entities.const_get(target_type, false)
end
+
+ def todo_target_url(todo)
+ target_type = todo.target_type.underscore
+ target_url = "#{todo.resource_parent.class.to_s.underscore}_#{target_type}_url"
+
+ Gitlab::Routing
+ .url_helpers
+ .public_send(target_url, todo.resource_parent, todo.target, anchor: todo_target_anchor(todo)) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def todo_target_anchor(todo)
+ "note_#{todo.note_id}" if todo.note_id?
+ end
end
class NamespaceBasic < Grape::Entity
@@ -1045,7 +1060,7 @@ module API
expose :job_events
# Expose serialized properties
expose :properties do |service, options|
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
if service.data_fields_present?
service.data_fields.as_json.slice(*service.api_field_names)
else
@@ -1276,7 +1291,7 @@ module API
class Release < Grape::Entity
expose :name
- expose :tag, as: :tag_name, if: lambda { |_, _| can_download_code? }
+ expose :tag, as: :tag_name, if: ->(_, _) { can_download_code? }
expose :description
expose :description_html do |entity|
MarkupHelper.markdown_field(entity, :description)
@@ -1284,26 +1299,61 @@ module API
expose :created_at
expose :released_at
expose :author, using: Entities::UserBasic, if: -> (release, _) { release.author.present? }
- expose :commit, using: Entities::Commit, if: lambda { |_, _| can_download_code? }
+ expose :commit, using: Entities::Commit, if: ->(_, _) { can_download_code? }
expose :upcoming_release?, as: :upcoming_release
expose :milestones, using: Entities::Milestone, if: -> (release, _) { release.milestones.present? }
-
+ expose :commit_path, if: ->(_, _) { can_download_code? }
+ expose :tag_path, if: ->(_, _) { can_download_code? }
expose :assets do
expose :assets_count, as: :count do |release, _|
assets_to_exclude = can_download_code? ? [] : [:sources]
release.assets_count(except: assets_to_exclude)
end
- expose :sources, using: Entities::Releases::Source, if: lambda { |_, _| can_download_code? }
+ expose :sources, using: Entities::Releases::Source, if: ->(_, _) { can_download_code? }
expose :links, using: Entities::Releases::Link do |release, options|
release.links.sorted
end
end
+ expose :_links do
+ expose :merge_requests_url, if: -> (_) { release_mr_issue_urls_available? }
+ expose :issues_url, if: -> (_) { release_mr_issue_urls_available? }
+ end
private
def can_download_code?
Ability.allowed?(options[:current_user], :download_code, object.project)
end
+
+ def commit_path
+ return unless object.commit
+
+ Gitlab::Routing.url_helpers.project_commit_path(project, object.commit.id)
+ end
+
+ def tag_path
+ Gitlab::Routing.url_helpers.project_tag_path(project, object.tag)
+ end
+
+ def merge_requests_url
+ Gitlab::Routing.url_helpers.project_merge_requests_url(project, params_for_issues_and_mrs)
+ end
+
+ def issues_url
+ Gitlab::Routing.url_helpers.project_issues_url(project, params_for_issues_and_mrs)
+ end
+
+ def params_for_issues_and_mrs
+ { scope: 'all', state: 'opened', release_tag: object.tag }
+ end
+
+ def release_mr_issue_urls_available?
+ ::Feature.enabled?(:release_mr_issue_urls, project)
+ end
+
+ def project
+ @project ||= object.project
+ end
end
class Tag < Grape::Entity
@@ -1448,15 +1498,17 @@ module API
end
class Deployment < Grape::Entity
- expose :id, :iid, :ref, :sha, :created_at
+ expose :id, :iid, :ref, :sha, :created_at, :updated_at
expose :user, using: Entities::UserBasic
expose :environment, using: Entities::EnvironmentBasic
expose :deployable, using: Entities::Job
+ expose :status
end
class Environment < EnvironmentBasic
expose :project, using: Entities::BasicProjectDetails
expose :last_deployment, using: Entities::Deployment, if: { last_deployment: true }
+ expose :state
end
class LicenseBasic < Grape::Entity
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index 79a44941c81..7585293031f 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -18,10 +18,24 @@ module API
params do
optional :with_counts, type: Boolean, default: false,
desc: 'Include issue and merge request counts'
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
use :pagination
end
get ':id/labels' do
- get_labels(user_group, Entities::GroupLabel)
+ get_labels(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
+ end
+
+ desc 'Get a single label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
+ end
+ get ':id/labels/:name' do
+ get_label(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
end
desc 'Create a new label' do
@@ -36,22 +50,21 @@ module API
end
desc 'Update an existing label. At least one optional parameter is required.' do
- detail 'This feature was added in GitLab 11.8'
+ detail 'This feature was added in GitLab 11.8 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
- requires :name, type: String, desc: 'The name of the label to be updated'
- optional :new_name, type: String, desc: 'The new name of the label'
- optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
- optional :description, type: String, desc: 'The new description of label'
- at_least_one_of :new_name, :color, :description
+ optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :name, type: String, desc: 'The name of the label to be updated'
+ use :group_label_update_params
+ exactly_one_of :label_id, :name
end
put ':id/labels' do
update_label(user_group, Entities::GroupLabel)
end
desc 'Delete an existing label' do
- detail 'This feature was added in GitLab 11.8'
+ detail 'This feature was added in GitLab 11.8 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
@@ -60,6 +73,29 @@ module API
delete ':id/labels' do
delete_label(user_group)
end
+
+ desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be updated'
+ use :group_label_update_params
+ end
+ put ':id/labels/:name' do
+ update_label(user_group, Entities::GroupLabel)
+ end
+
+ desc 'Delete an existing label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be deleted'
+ end
+ delete ':id/labels/:name' do
+ delete_label(user_group)
+ end
end
end
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index fad8bb13150..19c29847ce3 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -350,7 +350,7 @@ module API
render_api_error!(message || '409 Conflict', 409)
end
- def file_to_large!
+ def file_too_large!
render_api_error!('413 Request Entity Too Large', 413)
end
diff --git a/lib/api/helpers/graphql_helpers.rb b/lib/api/helpers/graphql_helpers.rb
index bd60470fbd6..3ddef0c16b3 100644
--- a/lib/api/helpers/graphql_helpers.rb
+++ b/lib/api/helpers/graphql_helpers.rb
@@ -6,7 +6,7 @@ module API
# against the graphql API. Helper code for the graphql server implementation
# should be in app/graphql/ or lib/gitlab/graphql/
module GraphqlHelpers
- def conditionally_graphql!(fallback:, query:, context: {}, transform: nil)
+ def run_graphql!(query:, context: {}, transform: nil)
result = GitlabSchema.execute(query, context: context)
if transform
diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb
index 585ae1eb5c4..2cc18acb7ec 100644
--- a/lib/api/helpers/groups_helpers.rb
+++ b/lib/api/helpers/groups_helpers.rb
@@ -10,12 +10,16 @@ module API
optional :description, type: String, desc: 'The description of the group'
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
- default: Gitlab::VisibilityLevel.string_level(
- Gitlab::CurrentSettings.current_application_settings.default_group_visibility),
desc: 'The visibility of the group'
+ optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
+ optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users in this group to setup Two-factor authentication'
+ optional :two_factor_grace_period, type: Integer, desc: 'Time before Two-factor authentication is enforced'
+ optional :project_creation_level, type: String, values: ::Gitlab::Access.project_creation_string_values, desc: 'Determine if developers can create projects in the group', as: :project_creation_level_str
+ optional :auto_devops_enabled, type: Boolean, desc: 'Default to Auto DevOps pipeline for all projects within this group'
+ optional :subgroup_creation_level, type: String, values: ::Gitlab::Access.subgroup_creation_string_values, desc: 'Allowed to create subgroups', as: :subgroup_creation_level_str
+ optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
- optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
end
params :optional_params_ee do
diff --git a/lib/api/helpers/label_helpers.rb b/lib/api/helpers/label_helpers.rb
index ec5b688dd1c..2fb2d9b79cf 100644
--- a/lib/api/helpers/label_helpers.rb
+++ b/lib/api/helpers/label_helpers.rb
@@ -11,6 +11,23 @@ module API
optional :description, type: String, desc: 'The description of label to be created'
end
+ params :label_update_params do
+ optional :new_name, type: String, desc: 'The new name of the label'
+ optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
+ optional :description, type: String, desc: 'The new description of label'
+ end
+
+ params :project_label_update_params do
+ use :label_update_params
+ optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
+ at_least_one_of :new_name, :color, :description, :priority
+ end
+
+ params :group_label_update_params do
+ use :label_update_params
+ at_least_one_of :new_name, :color, :description
+ end
+
def find_label(parent, id_or_title, include_ancestor_groups: true)
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
label = labels.find_by_id(id_or_title) || labels.find_by_title(id_or_title)
@@ -18,14 +35,20 @@ module API
label || not_found!('Label')
end
- def get_labels(parent, entity)
- present paginate(available_labels_for(parent)),
+ def get_labels(parent, entity, include_ancestor_groups: true)
+ present paginate(available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)),
with: entity,
current_user: current_user,
parent: parent,
with_counts: params[:with_counts]
end
+ def get_label(parent, entity, include_ancestor_groups: true)
+ label = find_label(parent, params_id_or_title, include_ancestor_groups: include_ancestor_groups)
+
+ present label, with: entity, current_user: current_user, parent: parent
+ end
+
def create_label(parent, entity)
authorize! :admin_label, parent
@@ -57,6 +80,7 @@ module API
# params is used to update the label so we need to remove this field here
params.delete(:label_id)
+ params.delete(:name)
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
render_validation_error!(label) unless label.valid?
@@ -80,6 +104,24 @@ module API
destroy_conditionally!(label)
end
+ def promote_label(parent)
+ authorize! :admin_label, parent
+
+ label = find_label(parent, params[:name], include_ancestor_groups: false)
+
+ begin
+ group_label = ::Labels::PromoteService.new(parent, current_user).execute(label)
+
+ if group_label
+ present group_label, with: Entities::GroupLabel, current_user: current_user, parent: parent.group
+ else
+ render_api_error!('Failed to promote project label to group label', 400)
+ end
+ rescue => error
+ render_api_error!(error.to_s, 400)
+ end
+ end
+
def params_id_or_title
@params_id_or_title ||= params[:label_id] || params[:name]
end
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 11631378137..fa8b9ad79bd 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -59,8 +59,9 @@ module API
token && job.valid_token?(token)
end
- def max_artifacts_size
- Gitlab::CurrentSettings.max_artifacts_size.megabytes.to_i
+ def max_artifacts_size(job)
+ max_size = job.project.closest_setting(:max_artifacts_size)
+ max_size.megabytes.to_i
end
def job_forbidden!(job, reason)
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index d5f0ddb0805..d9a22484c1f 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -26,20 +26,11 @@ module API
def ee_post_receive_response_hook(response)
# Hook for EE to add messages
end
- end
- namespace 'internal' do
- # Check if git command is allowed for project
- #
- # Params:
- # key_id - ssh key id for Git over SSH
- # user_id - user id for Git over HTTP or over SSH in keyless SSH CERT mode
- # username - user name for Git over SSH in keyless SSH cert mode
- # protocol - Git access protocol being used, e.g. HTTP or SSH
- # project - project full_path (not path on disk)
- # action - git action (git-upload-pack or git-receive-pack)
- # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList
- post "/allowed" do
+ def check_allowed(params)
+ # This is a separate method so that EE can alter its behaviour more
+ # easily.
+
# Stores some Git-specific env thread-safely
env = parse_env
Gitlab::Git::HookEnv.set(gl_repository, env) if project
@@ -53,11 +44,11 @@ module API
@project ||= access_checker.project
result
rescue Gitlab::GitAccess::UnauthorizedError => e
- break response_with_status(code: 401, success: false, message: e.message)
+ return response_with_status(code: 401, success: false, message: e.message)
rescue Gitlab::GitAccess::TimeoutError => e
- break response_with_status(code: 503, success: false, message: e.message)
+ return response_with_status(code: 503, success: false, message: e.message)
rescue Gitlab::GitAccess::NotFoundError => e
- break response_with_status(code: 404, success: false, message: e.message)
+ return response_with_status(code: 404, success: false, message: e.message)
end
log_user_activity(actor.user)
@@ -78,6 +69,10 @@ module API
receive_max_input_size = Gitlab::CurrentSettings.receive_max_input_size.to_i
if receive_max_input_size > 0
payload[:git_config_options] << "receive.maxInputSize=#{receive_max_input_size.megabytes}"
+
+ if Feature.enabled?(:gitaly_upload_pack_filter, project)
+ payload[:git_config_options] << "uploadpack.allowFilter=true" << "uploadpack.allowAnySHA1InWant=true"
+ end
end
response_with_status(**payload)
@@ -87,6 +82,26 @@ module API
response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR)
end
end
+ end
+
+ namespace 'internal' do
+ # Check if git command is allowed for project
+ #
+ # Params:
+ # key_id - ssh key id for Git over SSH
+ # user_id - user id for Git over HTTP or over SSH in keyless SSH CERT mode
+ # username - user name for Git over SSH in keyless SSH cert mode
+ # protocol - Git access protocol being used, e.g. HTTP or SSH
+ # project - project full_path (not path on disk)
+ # action - git action (git-upload-pack or git-receive-pack)
+ # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList
+ # check_ip - optional, only in EE version, may limit access to
+ # group resources based on its IP restrictions
+ post "/allowed" do
+ # It was moved to a separate method so that EE can alter its behaviour more
+ # easily.
+ check_allowed(params)
+ end
# rubocop: disable CodeReuse/ActiveRecord
post "/lfs_authenticate" do
@@ -108,10 +123,6 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- get "/merge_request_urls" do
- merge_request_urls
- end
-
#
# Get a ssh key using the fingerprint
#
@@ -129,20 +140,15 @@ module API
#
# Discover user by ssh key, user id or username
#
- # rubocop: disable CodeReuse/ActiveRecord
- get "/discover" do
+ get '/discover' do
if params[:key_id]
- key = Key.find(params[:key_id])
- user = key.user
- elsif params[:user_id]
- user = User.find_by(id: params[:user_id])
+ user = UserFinder.new(params[:key_id]).find_by_ssh_key_id
elsif params[:username]
user = UserFinder.new(params[:username]).find_by_username
end
present user, with: Entities::UserSafe
end
- # rubocop: enable CodeReuse/ActiveRecord
get "/check" do
{
@@ -153,22 +159,6 @@ module API
}
end
- get "/broadcast_messages" do
- if messages = BroadcastMessage.current
- present messages, with: Entities::BroadcastMessage
- else
- []
- end
- end
-
- get "/broadcast_message" do
- if message = BroadcastMessage.current&.last
- present message, with: Entities::BroadcastMessage
- else
- {}
- end
- end
-
# rubocop: disable CodeReuse/ActiveRecord
post '/two_factor_recovery_codes' do
status 200
diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb
index eaa434cff51..003af7f6dd4 100644
--- a/lib/api/internal/pages.rb
+++ b/lib/api/internal/pages.rb
@@ -17,11 +17,18 @@ module API
namespace 'internal' do
namespace 'pages' do
+ desc 'Get GitLab Pages domain configuration by hostname' do
+ detail 'This feature was introduced in GitLab 12.3.'
+ end
+ params do
+ requires :host, type: String, desc: 'The host to query for'
+ end
get "/" do
- host = PagesDomain.find_by_domain(params[:host])
+ host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain(params[:host])
not_found! unless host
virtual_domain = host.pages_virtual_domain
+ no_content! unless virtual_domain
present virtual_domain, with: Entities::Internal::Pages::VirtualDomain
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index de6af980896..4208385a48d 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -343,7 +343,8 @@ module API
present paginate(::Kaminari.paginate_array(merge_requests)),
with: Entities::MergeRequest,
current_user: current_user,
- project: user_project
+ project: user_project,
+ include_subscribed: false
end
desc 'List merge requests closing issue' do
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index de89e94b0c0..2b283d82e4a 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -17,10 +17,24 @@ module API
params do
optional :with_counts, type: Boolean, default: false,
desc: 'Include issue and merge request counts'
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
use :pagination
end
get ':id/labels' do
- get_labels(user_project, Entities::ProjectLabel)
+ get_labels(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
+ end
+
+ desc 'Get a single label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
+ end
+ get ':id/labels/:name' do
+ get_label(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
end
desc 'Create a new label' do
@@ -35,23 +49,21 @@ module API
end
desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was deprecated in GitLab 12.4.'
success Entities::ProjectLabel
end
params do
optional :label_id, type: Integer, desc: 'The id of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
- optional :new_name, type: String, desc: 'The new name of the label'
- optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
- optional :description, type: String, desc: 'The new description of label'
- optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
+ use :project_label_update_params
exactly_one_of :label_id, :name
- at_least_one_of :new_name, :color, :description, :priority
end
put ':id/labels' do
update_label(user_project, Entities::ProjectLabel)
end
desc 'Delete an existing label' do
+ detail 'This feature was deprecated in GitLab 12.4.'
success Entities::ProjectLabel
end
params do
@@ -64,28 +76,48 @@ module API
end
desc 'Promote a label to a group label' do
- detail 'This feature was added in GitLab 12.3'
+ detail 'This feature was added in GitLab 12.3 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be promoted'
end
put ':id/labels/promote' do
- authorize! :admin_label, user_project
+ promote_label(user_project)
+ end
- label = find_label(user_project, params[:name], include_ancestor_groups: false)
+ desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be updated'
+ use :project_label_update_params
+ end
+ put ':id/labels/:name' do
+ update_label(user_project, Entities::ProjectLabel)
+ end
- begin
- group_label = ::Labels::PromoteService.new(user_project, current_user).execute(label)
+ desc 'Delete an existing label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be deleted'
+ end
+ delete ':id/labels/:name' do
+ delete_label(user_project)
+ end
- if group_label
- present group_label, with: Entities::GroupLabel, current_user: current_user, parent: user_project.group
- else
- render_api_error!('Failed to promote project label to group label', 400)
- end
- rescue => error
- render_api_error!(error.to_s, 400)
- end
+ desc 'Promote a label to a group label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be promoted'
+ end
+ put ':id/labels/:name/promote' do
+ promote_label(user_project)
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 461ffe71a62..1d4616fed52 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -18,6 +18,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
+ optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -26,6 +27,7 @@ module API
members = source.members.where.not(user_id: nil).includes(:user)
members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
members = paginate(members)
present members, with: Entities::Member
@@ -37,6 +39,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
+ optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -45,6 +48,7 @@ module API
members = find_all_members(source_type, source)
members = members.includes(:user).references(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
members = paginate(members)
present members, with: Entities::Member
@@ -68,6 +72,23 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Gets a member of a group or project, including those who gained membership through ancestor group' do
+ success Entities::Member
+ end
+ params do
+ requires :user_id, type: Integer, desc: 'The user ID of the member'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ":id/members/all/:user_id" do
+ source = find_source(source_type, params[:id])
+
+ members = find_all_members(source_type, source)
+ member = members.find_by!(user_id: params[:user_id])
+
+ present member, with: Entities::Member
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Adds a member to a group or project.' do
success Entities::Member
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 16fca9acccb..89e4da5a42e 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -80,7 +80,7 @@ module API
note = create_note(noteable, opts)
if note.valid?
- present note, with: Entities.const_get(note.class.name)
+ present note, with: Entities.const_get(note.class.name, false)
else
bad_request!("Note #{note.errors.messages}")
end
diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb
index c10ef96922c..2a05974509a 100644
--- a/lib/api/project_container_repositories.rb
+++ b/lib/api/project_container_repositories.rb
@@ -106,9 +106,15 @@ module API
authorize_destroy_container_image!
validate_tag!
- tag.delete
-
- status :ok
+ result = ::Projects::ContainerRepository::DeleteTagsService
+ .new(repository.project, current_user, tags: [declared_params[:tag_name]])
+ .execute(repository)
+
+ if result[:status] == :success
+ status :ok
+ else
+ status :bad_request
+ end
end
end
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
index 7f1ae5ffbe6..b3f17447ea0 100644
--- a/lib/api/project_import.rb
+++ b/lib/api/project_import.rb
@@ -29,6 +29,7 @@ module API
requires :path, type: String, desc: 'The new project path and name'
# TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
requires :file, type: File, desc: 'The project export file to be imported' # rubocop:disable Scalability/FileUploads
+ 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,
@@ -55,6 +56,7 @@ module API
project_params = {
path: import_params[:path],
namespace_id: namespace.id,
+ name: import_params[:name],
file: import_params[:file]['tempfile'],
overwrite: import_params[:overwrite]
}
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index ca75ee906ce..c7665c20234 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -42,7 +42,7 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
- desc 'Protect a single branch or wildcard' do
+ desc 'Protect a single branch' do
success Entities::ProtectedBranch
end
params do
@@ -93,3 +93,5 @@ module API
end
end
end
+
+API::ProtectedBranches.prepend_if_ee('EE::API::ProtectedBranches')
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index fdf4904e9f5..f383c541f8a 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -221,14 +221,16 @@ module API
job = authenticate_job!
forbidden!('Job is not running') unless job.running?
+ max_size = max_artifacts_size(job)
+
if params[:filesize]
file_size = params[:filesize].to_i
- file_to_large! unless file_size < max_artifacts_size
+ file_too_large! unless file_size < max_size
end
status 200
content_type Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
- JobArtifactUploader.workhorse_authorize(has_length: false, maximum_size: max_artifacts_size)
+ JobArtifactUploader.workhorse_authorize(has_length: false, maximum_size: max_size)
end
desc 'Upload artifacts for job' do
@@ -268,7 +270,7 @@ module API
metadata = UploadedFile.from_params(params, :metadata, JobArtifactUploader.workhorse_local_upload_path)
bad_request!('Missing artifacts file!') unless artifacts
- file_to_large! unless artifacts.size < max_artifacts_size
+ file_too_large! unless artifacts.size < max_artifacts_size(job)
expire_in = params['expire_in'] ||
Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index e4ef507228b..c90ba0c9b5d 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -101,6 +101,8 @@ module API
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
optional :project_export_enabled, type: Boolean, desc: 'Enable project export'
optional :prometheus_metrics_enabled, type: Boolean, desc: 'Enable Prometheus metrics'
+ optional :push_event_hooks_limit, type: Integer, desc: "Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value."
+ optional :push_event_activities_limit, type: Integer, desc: 'Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push event will be created. Bulk push event will be created if it surpasses that value.'
optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
given recaptcha_enabled: ->(val) { val } do
requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 404675bfaec..e3f3aca27df 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -49,7 +49,7 @@ module API
resource :todos do
helpers do
def issuable_and_awardable?(type)
- obj_type = Object.const_get(type)
+ obj_type = Object.const_get(type, false)
(obj_type < Issuable) && (obj_type < Awardable)
rescue NameError
diff --git a/lib/api/users.rb b/lib/api/users.rb
index ff8b82e1898..ff0b1e87b03 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -459,6 +459,42 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Activate a deactivated user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ post ':id/activate' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+ forbidden!('A blocked user must be unblocked to be activated') if user.blocked?
+
+ user.activate
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ desc 'Deactivate an active user. Available only for admins.'
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ post ':id/deactivate' do
+ authenticated_as_admin!
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ break if user.deactivated?
+
+ unless user.can_be_deactivated?
+ forbidden!('A blocked user cannot be deactivated by the API') if user.blocked?
+ forbidden!("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ end
+
+ user.deactivate
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Block a user. Available only for admins.'
params do
requires :id, type: Integer, desc: 'The ID of the user'
@@ -489,6 +525,8 @@ module API
if user.ldap_blocked?
forbidden!('LDAP blocked users cannot be unblocked by the API')
+ elsif user.deactivated?
+ forbidden!('Deactivated users cannot be unblocked by the API')
else
user.activate
end
diff --git a/lib/api/version.rb b/lib/api/version.rb
index eca1b529094..f79bb3428f2 100644
--- a/lib/api/version.rb
+++ b/lib/api/version.rb
@@ -19,11 +19,10 @@ module API
detail 'This feature was introduced in GitLab 8.13.'
end
get '/version' do
- conditionally_graphql!(
+ run_graphql!(
query: METADATA_QUERY,
context: { current_user: current_user },
- transform: ->(result) { result.dig('data', 'metadata') },
- fallback: -> { { version: Gitlab::VERSION, revision: Gitlab.revision } }
+ transform: ->(result) { result.dig('data', 'metadata') }
)
end
end