summaryrefslogtreecommitdiff
path: root/app/services
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-04-13 15:08:52 +0200
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-04-13 15:08:52 +0200
commita8231ea1befd803fb5892ea3e6679219f5d7d8e5 (patch)
treea5bfe0cec13735bb36ba010c7dbacfaf13ba135f /app/services
parent57f8f2d7ff851fc4f5d1c81a28a023855f1985b7 (diff)
parent37ab389139a21a8ab10ddbbddec1b61f720b27ab (diff)
downloadgitlab-ce-a8231ea1befd803fb5892ea3e6679219f5d7d8e5.tar.gz
Merge branch 'master' into feature/gb/manual-actions-protected-branches-permissions
* master: (641 commits) Revert "Fix registry for projects with uppercases in path" Fix registry for projects with uppercases in path Move event icons into events_helper Reset New branch button when issue state changes Add link to environments on kubernetes.md Indent system notes on desktop screens Improve webpack-dev-server compatibility with non-localhost setups. Add changelog entry Fix recent searches icon alignment in Safari Use preload to avoid Rails using JOIN Fix NUMBER_OF_TRUNCATED_DIFF_LINES re-definition error Prepare for zero downtime migrations Fix filtered search input width for IE Fix the `gitlab:gitlab_shell:check` task Fixed random failures with Poll spec Include CONTRIBUTING.md file when importing .gitlab-ci.yml templates Let uses hide verbose output by default Separate examples for each other Collapse similar sibling scenarios Use empty_project for resources that are independent of the repo ... Conflicts: app/views/projects/ci/builds/_build.html.haml
Diffstat (limited to 'app/services')
-rw-r--r--app/services/auth/container_registry_authentication_service.rb58
-rw-r--r--app/services/ci/create_pipeline_service.rb18
-rw-r--r--app/services/ci/expire_pipeline_cache_service.rb51
-rw-r--r--app/services/ci/retry_pipeline_service.rb4
-rw-r--r--app/services/concerns/issues/resolve_discussions.rb4
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/git_push_service.rb2
-rw-r--r--app/services/issues/build_service.rb13
-rw-r--r--app/services/notes/build_service.rb25
-rw-r--r--app/services/notes/create_service.rb8
-rw-r--r--app/services/projects/destroy_service.rb18
-rw-r--r--app/services/protected_branches/update_service.rb7
-rw-r--r--app/services/protected_tags/create_service.rb11
-rw-r--r--app/services/protected_tags/update_service.rb10
-rw-r--r--app/services/slash_commands/interpret_service.rb2
-rw-r--r--app/services/system_note_service.rb8
-rw-r--r--app/services/users/create_service.rb6
-rw-r--r--app/services/users/destroy_service.rb19
-rw-r--r--app/services/users/migrate_to_ghost_user_service.rb59
19 files changed, 256 insertions, 69 deletions
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index db82b8f6c30..5e151b0f044 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -17,6 +17,7 @@ module Auth
end
def self.full_access_token(*names)
+ names = names.flatten
registry = Gitlab.config.registry
token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer
@@ -37,13 +38,13 @@ module Auth
private
def authorized_token(*accesses)
- token = JSONWebToken::RSAToken.new(registry.key)
- token.issuer = registry.issuer
- token.audience = params[:service]
- token.subject = current_user.try(:username)
- token.expire_time = self.class.token_expire_at
- token[:access] = accesses.compact
- token
+ JSONWebToken::RSAToken.new(registry.key).tap do |token|
+ token.issuer = registry.issuer
+ token.audience = params[:service]
+ token.subject = current_user.try(:username)
+ token.expire_time = self.class.token_expire_at
+ token[:access] = accesses.compact
+ end
end
def scope
@@ -55,20 +56,43 @@ module Auth
def process_scope(scope)
type, name, actions = scope.split(':', 3)
actions = actions.split(',')
+ path = ContainerRegistry::Path.new(name)
+
return unless type == 'repository'
- process_repository_access(type, name, actions)
+ process_repository_access(type, path, actions)
end
- def process_repository_access(type, name, actions)
- requested_project = Project.find_by_full_path(name)
+ def process_repository_access(type, path, actions)
+ return unless path.valid?
+
+ requested_project = path.repository_project
+
return unless requested_project
actions = actions.select do |action|
can_access?(requested_project, action)
end
- { type: type, name: name, actions: actions } if actions.present?
+ return unless actions.present?
+
+ # At this point user/build is already authenticated.
+ #
+ ensure_container_repository!(path, actions)
+
+ { type: type, name: path.to_s, actions: actions }
+ end
+
+ ##
+ # Because we do not have two way communication with registry yet,
+ # we create a container repository image resource when push to the
+ # registry is successfuly authorized.
+ #
+ def ensure_container_repository!(path, actions)
+ return if path.has_repository?
+ return unless actions.include?('push')
+
+ ContainerRepository.create_from_path!(path)
end
def can_access?(requested_project, requested_action)
@@ -101,6 +125,11 @@ module Auth
can?(current_user, :read_container_image, requested_project)
end
+ ##
+ # We still support legacy pipeline triggers which do not have associated
+ # actor. New permissions model and new triggers are always associated with
+ # an actor, so this should be improved in 10.0 version of GitLab.
+ #
def build_can_push?(requested_project)
# Build can push only to the project from which it originates
has_authentication_ability?(:build_create_container_image) &&
@@ -113,14 +142,11 @@ module Auth
end
def error(code, status:, message: '')
- {
- errors: [{ code: code, message: message }],
- http_status: status
- }
+ { errors: [{ code: code, message: message }], http_status: status }
end
def has_authentication_ability?(capability)
- (@authentication_abilities || []).include?(capability)
+ @authentication_abilities.to_a.include?(capability)
end
end
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 38a85e9fc42..21350be5557 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -53,6 +53,8 @@ module Ci
.execute(pipeline)
end
+ cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
+
pipeline.tap(&:process!)
end
@@ -63,6 +65,22 @@ module Ci
pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i
end
+ def cancel_pending_pipelines
+ Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines) do |cancelables|
+ cancelables.find_each do |cancelable|
+ cancelable.auto_cancel_running(pipeline)
+ end
+ end
+ end
+
+ def auto_cancelable_pipelines
+ project.pipelines
+ .where(ref: pipeline.ref)
+ .where.not(id: pipeline.id)
+ .where.not(sha: project.repository.sha_from_ref(pipeline.ref))
+ .created_or_pending
+ end
+
def commit
@commit ||= project.commit(origin_sha || origin_ref)
end
diff --git a/app/services/ci/expire_pipeline_cache_service.rb b/app/services/ci/expire_pipeline_cache_service.rb
new file mode 100644
index 00000000000..91d9c1d2ba1
--- /dev/null
+++ b/app/services/ci/expire_pipeline_cache_service.rb
@@ -0,0 +1,51 @@
+module Ci
+ class ExpirePipelineCacheService < BaseService
+ attr_reader :pipeline
+
+ def execute(pipeline)
+ @pipeline = pipeline
+ store = Gitlab::EtagCaching::Store.new
+
+ store.touch(project_pipelines_path)
+ store.touch(commit_pipelines_path) if pipeline.commit
+ store.touch(new_merge_request_pipelines_path)
+ merge_requests_pipelines_paths.each { |path| store.touch(path) }
+
+ Gitlab::Cache::Ci::ProjectPipelineStatus.update_for_pipeline(@pipeline)
+ end
+
+ private
+
+ def project_pipelines_path
+ Gitlab::Routing.url_helpers.namespace_project_pipelines_path(
+ project.namespace,
+ project,
+ format: :json)
+ end
+
+ def commit_pipelines_path
+ Gitlab::Routing.url_helpers.pipelines_namespace_project_commit_path(
+ project.namespace,
+ project,
+ pipeline.commit.id,
+ format: :json)
+ end
+
+ def new_merge_request_pipelines_path
+ Gitlab::Routing.url_helpers.new_namespace_project_merge_request_path(
+ project.namespace,
+ project,
+ format: :json)
+ end
+
+ def merge_requests_pipelines_paths
+ pipeline.merge_requests.collect do |merge_request|
+ Gitlab::Routing.url_helpers.pipelines_namespace_project_merge_request_path(
+ project.namespace,
+ project,
+ merge_request,
+ format: :json)
+ end
+ end
+ end
+end
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index f72ddbf690c..ecc6173a96a 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -7,9 +7,7 @@ module Ci
raise Gitlab::Access::AccessDeniedError
end
- pipeline.builds.latest.failed_or_canceled.find_each do |build|
- next unless build.retryable?
-
+ pipeline.retryable_builds.find_each do |build|
Ci::RetryBuildService.new(project, current_user)
.reprocess(build)
end
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 297c7d696c3..910a2a15e5d 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -21,11 +21,11 @@ module Issues
@discussions_to_resolve ||=
if discussion_to_resolve_id
discussion_or_nil = merge_request_to_resolve_discussions_of
- .find_diff_discussion(discussion_to_resolve_id)
+ .find_discussion(discussion_to_resolve_id)
Array(discussion_or_nil)
else
merge_request_to_resolve_discussions_of
- .resolvable_discussions
+ .discussions_to_be_resolved
end
end
end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 11a045f4c31..38a113caec7 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -11,7 +11,7 @@ class DeleteBranchService < BaseService
return error('Cannot remove HEAD branch', 405)
end
- if project.protected_branch?(branch_name)
+ if ProtectedBranch.protected?(project, branch_name)
return error('Protected branch cant be removed', 405)
end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index bc7431c89a8..45411c779cc 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -127,7 +127,7 @@ class GitPushService < BaseService
project.change_head(branch_name)
# Set protection on the default branch if configured
- if current_application_settings.default_branch_protection != PROTECTION_NONE && !@project.protected_branch?(@project.default_branch)
+ if current_application_settings.default_branch_protection != PROTECTION_NONE && !ProtectedBranch.protected?(@project, @project.default_branch)
params = {
name: @project.default_branch,
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 77bced4bd5c..3a4f7b159f1 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -35,14 +35,19 @@ module Issues
end
def item_for_discussion(discussion)
- first_note = discussion.first_note_to_resolve || discussion.first_note
+ first_note_to_resolve = discussion.first_note_to_resolve || discussion.first_note
+
+ is_very_first_note = first_note_to_resolve == discussion.first_note
+ action = is_very_first_note ? "started" : "commented on"
+
+ note_url = Gitlab::UrlBuilder.build(first_note_to_resolve)
+
other_note_count = discussion.notes.size - 1
- note_url = Gitlab::UrlBuilder.build(first_note)
- discussion_info = "- [ ] #{first_note.author.to_reference} commented on a [discussion](#{note_url}): "
+ discussion_info = "- [ ] #{first_note_to_resolve.author.to_reference} #{action} a [discussion](#{note_url}): "
discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0
- note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note.note).call
+ note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note_to_resolve.note).call
spaces = ' ' * 4
quote = note_without_block_quotes.lines.map { |line| "#{spaces}> #{line}" }.join
diff --git a/app/services/notes/build_service.rb b/app/services/notes/build_service.rb
new file mode 100644
index 00000000000..ea7cacc956c
--- /dev/null
+++ b/app/services/notes/build_service.rb
@@ -0,0 +1,25 @@
+module Notes
+ class BuildService < ::BaseService
+ def execute
+ in_reply_to_discussion_id = params.delete(:in_reply_to_discussion_id)
+
+ if project && in_reply_to_discussion_id.present?
+ discussion = project.notes.find_discussion(in_reply_to_discussion_id)
+
+ unless discussion
+ note = Note.new
+ note.errors.add(:base, 'Discussion to reply to cannot be found')
+ return note
+ end
+
+ params.merge!(discussion.reply_attributes)
+ end
+
+ note = Note.new(params)
+ note.project = project
+ note.author = current_user
+
+ note
+ end
+ end
+end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 61d66a26932..f3954f6f8c4 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -1,12 +1,10 @@
module Notes
- class CreateService < BaseService
+ class CreateService < ::BaseService
def execute
merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha)
- note = Note.new(params)
- note.project = project
- note.author = current_user
- note.system = false
+ note = Notes::BuildService.new(project, current_user, params).execute
+ return note unless note.valid?
# We execute commands (extracted from `params[:note]`) on the noteable
# **before** we save the note because if the note consists of commands
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index a7142d5950e..06d8d143231 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -31,16 +31,16 @@ module Projects
project.team.truncate
project.destroy!
- unless remove_registry_tags
- raise_error('Failed to remove project container registry. Please try again or contact administrator')
+ unless remove_legacy_registry_tags
+ raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
end
unless remove_repository(repo_path)
- raise_error('Failed to remove project repository. Please try again or contact administrator')
+ raise_error('Failed to remove project repository. Please try again or contact administrator.')
end
unless remove_repository(wiki_path)
- raise_error('Failed to remove wiki repository. Please try again or contact administrator')
+ raise_error('Failed to remove wiki repository. Please try again or contact administrator.')
end
end
@@ -68,10 +68,16 @@ module Projects
end
end
- def remove_registry_tags
+ ##
+ # This method makes sure that we correctly remove registry tags
+ # for legacy image repository (when repository path equals project path).
+ #
+ def remove_legacy_registry_tags
return true unless Gitlab.config.registry.enabled
- project.container_registry_repository.delete_tags
+ ContainerRepository.build_root_repository(project).tap do |repository|
+ return repository.has_tags? ? repository.delete_tags! : true
+ end
end
def raise_error(message)
diff --git a/app/services/protected_branches/update_service.rb b/app/services/protected_branches/update_service.rb
index 89d8ba60134..4b3337a5c9d 100644
--- a/app/services/protected_branches/update_service.rb
+++ b/app/services/protected_branches/update_service.rb
@@ -1,13 +1,10 @@
module ProtectedBranches
class UpdateService < BaseService
- attr_reader :protected_branch
-
def execute(protected_branch)
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project)
- @protected_branch = protected_branch
- @protected_branch.update(params)
- @protected_branch
+ protected_branch.update(params)
+ protected_branch
end
end
end
diff --git a/app/services/protected_tags/create_service.rb b/app/services/protected_tags/create_service.rb
new file mode 100644
index 00000000000..faba7865a17
--- /dev/null
+++ b/app/services/protected_tags/create_service.rb
@@ -0,0 +1,11 @@
+module ProtectedTags
+ class CreateService < BaseService
+ attr_reader :protected_tag
+
+ def execute
+ raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project)
+
+ project.protected_tags.create(params)
+ end
+ end
+end
diff --git a/app/services/protected_tags/update_service.rb b/app/services/protected_tags/update_service.rb
new file mode 100644
index 00000000000..aea6a48968d
--- /dev/null
+++ b/app/services/protected_tags/update_service.rb
@@ -0,0 +1,10 @@
+module ProtectedTags
+ class UpdateService < BaseService
+ def execute(protected_tag)
+ raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_project, project)
+
+ protected_tag.update(params)
+ protected_tag
+ end
+ end
+end
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/slash_commands/interpret_service.rb
index 595653ea58a..49d45ec9dbd 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/slash_commands/interpret_service.rb
@@ -7,6 +7,8 @@ module SlashCommands
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable)
+ return [content, {}] unless current_user.can?(:use_slash_commands)
+
@issuable = issuable
@updates = {}
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 35cfcc3682e..c9e25c7aaa2 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -228,12 +228,10 @@ module SystemNoteService
def discussion_continued_in_issue(discussion, project, author, issue)
body = "created #{issue.to_reference} to continue this discussion"
+ note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body)
- note_params = discussion.reply_attributes.merge(project: project, author: author, note: body)
- note_params[:type] = note_params.delete(:note_type)
-
- note = Note.create(note_params.merge(system: true))
- note.system_note_metadata = SystemNoteMetadata.new({ action: 'discussion' })
+ note = Note.create(note_attributes.merge(system: true))
+ note.system_note_metadata = SystemNoteMetadata.new(action: 'discussion')
note
end
diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb
index a847a71a66a..93ca7b1141a 100644
--- a/app/services/users/create_service.rb
+++ b/app/services/users/create_service.rb
@@ -11,7 +11,7 @@ module Users
user = User.new(build_user_params)
- if current_user&.is_admin?
+ if current_user&.admin?
if params[:reset_password]
@reset_token = user.generate_reset_token
params[:force_random_password] = true
@@ -47,7 +47,7 @@ module Users
private
def can_create_user?
- (current_user.nil? && current_application_settings.signup_enabled?) || current_user&.is_admin?
+ (current_user.nil? && current_application_settings.signup_enabled?) || current_user&.admin?
end
# Allowed params for creating a user (admins only)
@@ -94,7 +94,7 @@ module Users
end
def build_user_params
- if current_user&.is_admin?
+ if current_user&.admin?
user_params = params.slice(*admin_create_params)
user_params[:created_by_id] = current_user&.id
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index a3b32a71a64..ba58b174cc0 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -26,7 +26,7 @@ module Users
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
end
- move_issues_to_ghost_user(user)
+ MigrateToGhostUserService.new(user).execute
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
namespace = user.namespace
@@ -35,22 +35,5 @@ module Users
user_data
end
-
- private
-
- def move_issues_to_ghost_user(user)
- # Block the user before moving issues to prevent a data race.
- # If the user creates an issue after `move_issues_to_ghost_user`
- # runs and before the user is destroyed, the destroy will fail with
- # an exception. We block the user so that issues can't be created
- # after `move_issues_to_ghost_user` runs and before the destroy happens.
- user.block
-
- ghost_user = User.ghost
-
- user.issues.update_all(author_id: ghost_user.id)
-
- user.reload
- end
end
end
diff --git a/app/services/users/migrate_to_ghost_user_service.rb b/app/services/users/migrate_to_ghost_user_service.rb
new file mode 100644
index 00000000000..1e1ed1791ec
--- /dev/null
+++ b/app/services/users/migrate_to_ghost_user_service.rb
@@ -0,0 +1,59 @@
+# When a user is destroyed, some of their associated records are
+# moved to a "Ghost User", to prevent these associated records from
+# being destroyed.
+#
+# For example, all the issues/MRs a user has created are _not_ destroyed
+# when the user is destroyed.
+module Users
+ class MigrateToGhostUserService
+ extend ActiveSupport::Concern
+
+ attr_reader :ghost_user, :user
+
+ def initialize(user)
+ @user = user
+ end
+
+ def execute
+ # Block the user before moving records to prevent a data race.
+ # For example, if the user creates an issue after `migrate_issues`
+ # runs and before the user is destroyed, the destroy will fail with
+ # an exception.
+ user.block
+
+ user.transaction do
+ @ghost_user = User.ghost
+
+ migrate_issues
+ migrate_merge_requests
+ migrate_notes
+ migrate_abuse_reports
+ migrate_award_emoji
+ end
+
+ user.reload
+ end
+
+ private
+
+ def migrate_issues
+ user.issues.update_all(author_id: ghost_user.id)
+ end
+
+ def migrate_merge_requests
+ user.merge_requests.update_all(author_id: ghost_user.id)
+ end
+
+ def migrate_notes
+ user.notes.update_all(author_id: ghost_user.id)
+ end
+
+ def migrate_abuse_reports
+ user.reported_abuse_reports.update_all(reporter_id: ghost_user.id)
+ end
+
+ def migrate_award_emoji
+ user.award_emoji.update_all(user_id: ghost_user.id)
+ end
+ end
+end