summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/branches.rb25
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/issues.rb1
-rw-r--r--lib/api/merge_requests.rb1
-rw-r--r--lib/api/pipeline_schedules.rb85
-rw-r--r--lib/api/runner.rb4
-rw-r--r--lib/api/users.rb150
-rw-r--r--lib/api/v3/triggers.rb32
-rw-r--r--lib/gitlab/git/repository.rb43
-rw-r--r--lib/gitlab/gpg.rb2
-rw-r--r--lib/gitlab/gpg/commit.rb34
-rw-r--r--lib/gitlab/gpg/invalid_gpg_signature_updater.rb2
-rw-r--r--lib/gitlab/issuables_count_for_state.rb50
-rw-r--r--lib/gitlab/sql/pattern.rb23
-rw-r--r--lib/system_check/app/git_user_default_ssh_config_check.rb69
-rw-r--r--lib/tasks/gettext.rake3
-rw-r--r--lib/tasks/gitlab/check.rake1
18 files changed, 475 insertions, 59 deletions
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index a989394ad91..642c1140fcc 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -24,17 +24,22 @@ module API
present paginate(branches), with: Entities::RepoBranch, project: user_project
end
- desc 'Get a single branch' do
- success Entities::RepoBranch
- end
- params do
- requires :branch, type: String, desc: 'The name of the branch'
- end
- get ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
- branch = user_project.repository.find_branch(params[:branch])
- not_found!("Branch") unless branch
+ resource ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
+ desc 'Get a single branch' do
+ success Entities::RepoBranch
+ end
+ params do
+ requires :branch, type: String, desc: 'The name of the branch'
+ end
+ head do
+ user_project.repository.branch_exists?(params[:branch]) ? status(204) : status(404)
+ end
+ get do
+ branch = user_project.repository.find_branch(params[:branch])
+ not_found!('Branch') unless branch
- present branch, with: Entities::RepoBranch, project: user_project
+ present branch, with: Entities::RepoBranch, project: user_project
+ end
end
# Note: This API will be deprecated in favor of the protected branches API.
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 6314ea63197..829eef18795 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -103,7 +103,7 @@ module API
when 'success'
status.success!
when 'failed'
- status.drop!
+ status.drop!(:api_failure)
when 'canceled'
status.cancel!
else
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index f13f2d723bb..031dd02c6eb 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -491,6 +491,10 @@ module API
expose :user, using: Entities::UserPublic
end
+ class GPGKey < Grape::Entity
+ expose :id, :key, :created_at
+ end
+
class Note < Grape::Entity
# Only Issue and MergeRequest have iid
NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze
@@ -819,7 +823,7 @@ module API
class Variable < Grape::Entity
expose :key, :value
- expose :protected?, as: :protected
+ expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
end
class Pipeline < PipelineBasic
@@ -840,6 +844,7 @@ module API
class PipelineScheduleDetails < PipelineSchedule
expose :last_pipeline, using: Entities::PipelineBasic
+ expose :variables, using: Entities::Variable
end
class EnvironmentBasic < Grape::Entity
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index e4c2c390853..1729df2aad0 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -36,6 +36,7 @@ module API
optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`'
+ optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
use :pagination
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 7bcbf9f20ff..56d72d511da 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -40,6 +40,7 @@ module API
optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`'
+ optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
use :pagination
end
end
diff --git a/lib/api/pipeline_schedules.rb b/lib/api/pipeline_schedules.rb
index ef01cbc7875..37f32411296 100644
--- a/lib/api/pipeline_schedules.rb
+++ b/lib/api/pipeline_schedules.rb
@@ -31,10 +31,6 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
get ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
-
present pipeline_schedule, with: Entities::PipelineScheduleDetails
end
@@ -74,9 +70,6 @@ module API
optional :active, type: Boolean, desc: 'The activation of pipeline schedule'
end
put ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
authorize! :update_pipeline_schedule, pipeline_schedule
if pipeline_schedule.update(declared_params(include_missing: false))
@@ -93,9 +86,6 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
post ':id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
authorize! :update_pipeline_schedule, pipeline_schedule
if pipeline_schedule.own!(current_user)
@@ -112,21 +102,84 @@ module API
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
end
delete ':id/pipeline_schedules/:pipeline_schedule_id' do
- authorize! :read_pipeline_schedule, user_project
-
- not_found!('PipelineSchedule') unless pipeline_schedule
authorize! :admin_pipeline_schedule, pipeline_schedule
destroy_conditionally!(pipeline_schedule)
end
+
+ desc 'Create a new pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ requires :value, type: String, desc: 'The value of the variable'
+ end
+ post ':id/pipeline_schedules/:pipeline_schedule_id/variables' do
+ authorize! :update_pipeline_schedule, pipeline_schedule
+
+ variable_params = declared_params(include_missing: false)
+ variable = pipeline_schedule.variables.create(variable_params)
+ if variable.persisted?
+ present variable, with: Entities::Variable
+ else
+ render_validation_error!(variable)
+ end
+ end
+
+ desc 'Edit a pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ optional :value, type: String, desc: 'The value of the variable'
+ end
+ put ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
+ authorize! :update_pipeline_schedule, pipeline_schedule
+
+ if pipeline_schedule_variable.update(declared_params(include_missing: false))
+ present pipeline_schedule_variable, with: Entities::Variable
+ else
+ render_validation_error!(pipeline_schedule_variable)
+ end
+ end
+
+ desc 'Delete a pipeline schedule variable' do
+ success Entities::Variable
+ end
+ params do
+ requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
+ requires :key, type: String, desc: 'The key of the variable'
+ end
+ delete ':id/pipeline_schedules/:pipeline_schedule_id/variables/:key' do
+ authorize! :admin_pipeline_schedule, pipeline_schedule
+
+ status :accepted
+ present pipeline_schedule_variable.destroy, with: Entities::Variable
+ end
end
helpers do
def pipeline_schedule
@pipeline_schedule ||=
- user_project.pipeline_schedules
- .preload(:owner, :last_pipeline)
- .find_by(id: params.delete(:pipeline_schedule_id))
+ user_project
+ .pipeline_schedules
+ .preload(:owner, :last_pipeline)
+ .find_by(id: params.delete(:pipeline_schedule_id)).tap do |pipeline_schedule|
+ unless can?(current_user, :read_pipeline_schedule, pipeline_schedule)
+ not_found!('Pipeline Schedule')
+ end
+ end
+ end
+
+ def pipeline_schedule_variable
+ @pipeline_schedule_variable ||=
+ pipeline_schedule.variables.find_by(key: params[:key]).tap do |pipeline_schedule_variable|
+ unless pipeline_schedule_variable
+ not_found!('Pipeline Schedule Variable')
+ end
+ end
end
end
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index 11999354594..a3987c560dd 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -114,6 +114,8 @@ module API
requires :id, type: Integer, desc: %q(Job's ID)
optional :trace, type: String, desc: %q(Job's full trace)
optional :state, type: String, desc: %q(Job's status: success, failed)
+ optional :failure_reason, type: String, values: CommitStatus.failure_reasons.keys,
+ desc: %q(Job's failure_reason)
end
put '/:id' do
job = authenticate_job!
@@ -127,7 +129,7 @@ module API
when 'success'
job.success
when 'failed'
- job.drop
+ job.drop(params[:failure_reason] || :unknown_failure)
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 96f47bb618a..1825c90a23b 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -233,6 +233,86 @@ module API
destroy_conditionally!(key)
end
+ desc 'Add a GPG key to a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key, type: String, desc: 'The new GPG key'
+ end
+ post ':id/gpg_keys' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params.delete(:id))
+ not_found!('User') unless user
+
+ key = user.gpg_keys.new(declared_params(include_missing: false))
+
+ if key.save
+ present key, with: Entities::GPGKey
+ else
+ render_validation_error!(key)
+ end
+ end
+
+ desc 'Get the GPG keys of a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ use :pagination
+ end
+ get ':id/gpg_keys' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ present paginate(user.gpg_keys), with: Entities::GPGKey
+ end
+
+ desc 'Delete an existing GPG key from a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ delete ':id/gpg_keys/:key_id' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ key = user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ status 204
+ key.destroy
+ end
+
+ desc 'Revokes an existing GPG key from a specified user. Available only for admins.' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :id, type: Integer, desc: 'The ID of the user'
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ post ':id/gpg_keys/:key_id/revoke' do
+ authenticated_as_admin!
+
+ user = User.find_by(id: params[:id])
+ not_found!('User') unless user
+
+ key = user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ key.revoke
+ status :accepted
+ end
+
desc 'Add an email address to a specified user. Available only for admins.' do
success Entities::Email
end
@@ -492,6 +572,76 @@ module API
destroy_conditionally!(key)
end
+ desc "Get the currently authenticated user's GPG keys" do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ use :pagination
+ end
+ get 'gpg_keys' do
+ present paginate(current_user.gpg_keys), with: Entities::GPGKey
+ end
+
+ desc 'Get a single GPG key owned by currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ get 'gpg_keys/:key_id' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ present key, with: Entities::GPGKey
+ end
+
+ desc 'Add a new GPG key to the currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ success Entities::GPGKey
+ end
+ params do
+ requires :key, type: String, desc: 'The new GPG key'
+ end
+ post 'gpg_keys' do
+ key = current_user.gpg_keys.new(declared_params)
+
+ if key.save
+ present key, with: Entities::GPGKey
+ else
+ render_validation_error!(key)
+ end
+ end
+
+ desc 'Revoke a GPG key owned by currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the GPG key'
+ end
+ post 'gpg_keys/:key_id/revoke' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ key.revoke
+ status :accepted
+ end
+
+ desc 'Delete a GPG key from the currently authenticated user' do
+ detail 'This feature was added in GitLab 10.0'
+ end
+ params do
+ requires :key_id, type: Integer, desc: 'The ID of the SSH key'
+ end
+ delete 'gpg_keys/:key_id' do
+ key = current_user.gpg_keys.find_by(id: params[:key_id])
+ not_found!('GPG Key') unless key
+
+ status 204
+ key.destroy
+ end
+
desc "Get the currently authenticated user's email addresses" do
success Entities::Email
end
diff --git a/lib/api/v3/triggers.rb b/lib/api/v3/triggers.rb
index e9d4c35307b..534911fde5c 100644
--- a/lib/api/v3/triggers.rb
+++ b/lib/api/v3/triggers.rb
@@ -16,25 +16,31 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/builds", requirements: { ref: /.+/ } do
- project = find_project(params[:id])
- trigger = Ci::Trigger.find_by_token(params[:token].to_s)
- not_found! unless project && trigger
- unauthorized! unless trigger.project == project
-
# validate variables
- variables = params[:variables].to_h
- unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) }
+ params[:variables] = params[:variables].to_h
+ unless params[:variables].all? { |key, value| key.is_a?(String) && value.is_a?(String) }
render_api_error!('variables needs to be a map of key-valued strings', 400)
end
- # create request and trigger builds
- result = Ci::CreateTriggerRequestService.execute(project, trigger, params[:ref].to_s, variables)
- pipeline = result.pipeline
+ project = find_project(params[:id])
+ not_found! unless project
+
+ result = Ci::PipelineTriggerService.new(project, nil, params).execute
+ not_found! unless result
- if pipeline.persisted?
- present result.trigger_request, with: ::API::V3::Entities::TriggerRequest
+ if result[:http_status]
+ render_api_error!(result[:message], result[:http_status])
else
- render_validation_error!(pipeline)
+ pipeline = result[:pipeline]
+
+ # We switched to Ci::PipelineVariable from Ci::TriggerRequest.variables.
+ # Ci::TriggerRequest doesn't save variables anymore.
+ # Here is copying Ci::PipelineVariable to Ci::TriggerRequest.variables for presenting the variables.
+ # The same endpoint in v4 API pressents Pipeline instead of TriggerRequest, so it doesn't need such a process.
+ trigger_request = pipeline.trigger_requests.last
+ trigger_request.variables = params[:variables]
+
+ present trigger_request, with: ::API::V3::Entities::TriggerRequest
end
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index c67f7724307..f7577b02d5d 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -605,6 +605,49 @@ module Gitlab
# TODO: implement this method
end
+ def add_branch(branch_name, committer:, target:)
+ target_object = Ref.dereference_object(lookup(target))
+ raise InvalidRef.new("target not found: #{target}") unless target_object
+
+ OperationService.new(committer, self).add_branch(branch_name, target_object.oid)
+ find_branch(branch_name)
+ rescue Rugged::ReferenceError => ex
+ raise InvalidRef, ex
+ end
+
+ def add_tag(tag_name, committer:, target:, message: nil)
+ target_object = Ref.dereference_object(lookup(target))
+ raise InvalidRef.new("target not found: #{target}") unless target_object
+
+ committer = Committer.from_user(committer) if committer.is_a?(User)
+
+ options = nil # Use nil, not the empty hash. Rugged cares about this.
+ if message
+ options = {
+ message: message,
+ tagger: Gitlab::Git.committer_hash(email: committer.email, name: committer.name)
+ }
+ end
+
+ OperationService.new(committer, self).add_tag(tag_name, target_object.oid, options)
+
+ find_tag(tag_name)
+ rescue Rugged::ReferenceError => ex
+ raise InvalidRef, ex
+ end
+
+ def rm_branch(branch_name, committer:)
+ OperationService.new(committer, self).rm_branch(find_branch(branch_name))
+ end
+
+ def rm_tag(tag_name, committer:)
+ OperationService.new(committer, self).rm_tag(find_tag(tag_name))
+ end
+
+ def find_tag(name)
+ tags.find { |tag| tag.name == name }
+ end
+
# Delete the specified branch from the repository
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/476
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index 45e9f9d65ae..025f826e65f 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -39,7 +39,7 @@ module Gitlab
fingerprints = CurrentKeyChain.fingerprints_from_key(key)
GPGME::Key.find(:public, fingerprints).flat_map do |raw_key|
- raw_key.uids.map { |uid| { name: uid.name, email: uid.email } }
+ raw_key.uids.map { |uid| { name: uid.name, email: uid.email.downcase } }
end
end
end
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 606c7576f70..86bd9f5b125 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -1,17 +1,12 @@
module Gitlab
module Gpg
class Commit
- def self.for_commit(commit)
- new(commit.project, commit.sha)
- end
-
- def initialize(project, sha)
- @project = project
- @sha = sha
+ def initialize(commit)
+ @commit = commit
@signature_text, @signed_text =
begin
- Rugged::Commit.extract_signature(project.repository.rugged, sha)
+ Rugged::Commit.extract_signature(@commit.project.repository.rugged, @commit.sha)
rescue Rugged::OdbError
nil
end
@@ -26,7 +21,7 @@ module Gitlab
return @signature if @signature
- cached_signature = GpgSignature.find_by(commit_sha: @sha)
+ cached_signature = GpgSignature.find_by(commit_sha: @commit.sha)
return @signature = cached_signature if cached_signature.present?
@signature = create_cached_signature!
@@ -73,20 +68,31 @@ module Gitlab
def attributes(gpg_key)
user_infos = user_infos(gpg_key)
+ verification_status = verification_status(gpg_key)
{
- commit_sha: @sha,
- project: @project,
+ commit_sha: @commit.sha,
+ project: @commit.project,
gpg_key: gpg_key,
gpg_key_primary_keyid: gpg_key&.primary_keyid || verified_signature.fingerprint,
gpg_key_user_name: user_infos[:name],
gpg_key_user_email: user_infos[:email],
- valid_signature: gpg_signature_valid_signature_value(gpg_key)
+ verification_status: verification_status
}
end
- def gpg_signature_valid_signature_value(gpg_key)
- !!(gpg_key && gpg_key.verified? && verified_signature.valid?)
+ def verification_status(gpg_key)
+ return :unknown_key unless gpg_key
+ return :unverified_key unless gpg_key.verified?
+ return :unverified unless verified_signature.valid?
+
+ if gpg_key.verified_and_belongs_to_email?(@commit.committer_email)
+ :verified
+ elsif gpg_key.user.all_emails.include?(@commit.committer_email)
+ :same_user_different_email
+ else
+ :other_user
+ end
end
def user_infos(gpg_key)
diff --git a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
index a525ee7a9ee..e085eab26c9 100644
--- a/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
+++ b/lib/gitlab/gpg/invalid_gpg_signature_updater.rb
@@ -8,7 +8,7 @@ module Gitlab
def run
GpgSignature
.select(:id, :commit_sha, :project_id)
- .where('gpg_key_id IS NULL OR valid_signature = ?', false)
+ .where('gpg_key_id IS NULL OR verification_status <> ?', GpgSignature.verification_statuses[:verified])
.where(gpg_key_primary_keyid: @gpg_key.primary_keyid)
.find_each { |sig| sig.gpg_commit.update_signature!(sig) }
end
diff --git a/lib/gitlab/issuables_count_for_state.rb b/lib/gitlab/issuables_count_for_state.rb
new file mode 100644
index 00000000000..505810964bc
--- /dev/null
+++ b/lib/gitlab/issuables_count_for_state.rb
@@ -0,0 +1,50 @@
+module Gitlab
+ # Class for counting and caching the number of issuables per state.
+ class IssuablesCountForState
+ # The name of the RequestStore cache key.
+ CACHE_KEY = :issuables_count_for_state
+
+ # The state values that can be safely casted to a Symbol.
+ STATES = %w[opened closed merged all].freeze
+
+ # finder - The finder class to use for retrieving the issuables.
+ def initialize(finder)
+ @finder = finder
+ @cache =
+ if RequestStore.active?
+ RequestStore[CACHE_KEY] ||= initialize_cache
+ else
+ initialize_cache
+ end
+ end
+
+ def for_state_or_opened(state = nil)
+ self[state || :opened]
+ end
+
+ # Returns the count for the given state.
+ #
+ # state - The name of the state as either a String or a Symbol.
+ #
+ # Returns an Integer.
+ def [](state)
+ state = state.to_sym if cast_state_to_symbol?(state)
+
+ cache_for_finder[state] || 0
+ end
+
+ private
+
+ def cache_for_finder
+ @cache[@finder]
+ end
+
+ def cast_state_to_symbol?(state)
+ state.is_a?(String) && STATES.include?(state)
+ end
+
+ def initialize_cache
+ Hash.new { |hash, finder| hash[finder] = finder.count_by_state }
+ end
+ end
+end
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index b42bc67ccfc..7c2d1d8f887 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -4,6 +4,7 @@ module Gitlab
extend ActiveSupport::Concern
MIN_CHARS_FOR_PARTIAL_MATCHING = 3
+ REGEX_QUOTED_WORD = /(?<=^| )"[^"]+"(?= |$)/
class_methods do
def to_pattern(query)
@@ -17,6 +18,28 @@ module Gitlab
def partial_matching?(query)
query.length >= MIN_CHARS_FOR_PARTIAL_MATCHING
end
+
+ def to_fuzzy_arel(column, query)
+ words = select_fuzzy_words(query)
+
+ matches = words.map { |word| arel_table[column].matches(to_pattern(word)) }
+
+ matches.reduce { |result, match| result.and(match) }
+ end
+
+ def select_fuzzy_words(query)
+ quoted_words = query.scan(REGEX_QUOTED_WORD)
+
+ query = quoted_words.reduce(query) { |q, quoted_word| q.sub(quoted_word, '') }
+
+ words = query.split(/\s+/)
+
+ quoted_words.map! { |quoted_word| quoted_word[1..-2] }
+
+ words.concat(quoted_words)
+
+ words.select { |word| partial_matching?(word) }
+ end
end
end
end
diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb
new file mode 100644
index 00000000000..7b486d78cf0
--- /dev/null
+++ b/lib/system_check/app/git_user_default_ssh_config_check.rb
@@ -0,0 +1,69 @@
+module SystemCheck
+ module App
+ class GitUserDefaultSSHConfigCheck < SystemCheck::BaseCheck
+ # These files are allowed in the .ssh directory. The `config` file is not
+ # whitelisted as it may change the SSH client's behaviour dramatically.
+ WHITELIST = %w[
+ authorized_keys
+ authorized_keys2
+ known_hosts
+ ].freeze
+
+ set_name 'Git user has default SSH configuration?'
+ set_skip_reason 'skipped (git user is not present or configured)'
+
+ def skip?
+ !home_dir || !File.directory?(home_dir)
+ end
+
+ def check?
+ forbidden_files.empty?
+ end
+
+ def show_error
+ backup_dir = "~/gitlab-check-backup-#{Time.now.to_i}"
+
+ instructions = forbidden_files.map do |filename|
+ "sudo mv #{Shellwords.escape(filename)} #{backup_dir}"
+ end
+
+ try_fixing_it("mkdir #{backup_dir}", *instructions)
+ for_more_information('doc/ssh/README.md in section "SSH on the GitLab server"')
+ fix_and_rerun
+ end
+
+ private
+
+ def git_user
+ Gitlab.config.gitlab.user
+ end
+
+ def home_dir
+ return @home_dir if defined?(@home_dir)
+
+ @home_dir =
+ begin
+ File.expand_path("~#{git_user}")
+ rescue ArgumentError
+ nil
+ end
+ end
+
+ def ssh_dir
+ return nil unless home_dir
+
+ File.join(home_dir, '.ssh')
+ end
+
+ def forbidden_files
+ @forbidden_files ||=
+ begin
+ present = Dir[File.join(ssh_dir, '*')]
+ whitelisted = WHITELIST.map { |basename| File.join(ssh_dir, basename) }
+
+ present - whitelisted
+ end
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gettext.rake b/lib/tasks/gettext.rake
index f7f2fa2f14c..35ba729c156 100644
--- a/lib/tasks/gettext.rake
+++ b/lib/tasks/gettext.rake
@@ -1,5 +1,4 @@
require "gettext_i18n_rails/tasks"
-require 'simple_po_parser'
namespace :gettext do
# Customize list of translatable files
@@ -23,6 +22,8 @@ namespace :gettext do
desc 'Lint all po files in `locale/'
task lint: :environment do
+ require 'simple_po_parser'
+
FastGettext.silence_errors
files = Dir.glob(Rails.root.join('locale/*/gitlab.po'))
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 1bd36bbe20a..92a3f503fcb 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -33,6 +33,7 @@ namespace :gitlab do
SystemCheck::App::RedisVersionCheck,
SystemCheck::App::RubyVersionCheck,
SystemCheck::App::GitVersionCheck,
+ SystemCheck::App::GitUserDefaultSSHConfigCheck,
SystemCheck::App::ActiveUsersCheck
]