summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue3
-rw-r--r--app/finders/keys_finder.rb56
-rw-r--r--app/models/concerns/sha256_attribute.rb49
-rw-r--r--app/models/key.rb9
-rw-r--r--app/services/git/base_hooks_service.rb2
-rw-r--r--app/views/profiles/keys/_key_details.html.haml16
-rw-r--r--changelogs/unreleased/feat-ssh-sha256.yml5
-rw-r--r--config/application.rb3
-rw-r--r--config/environments/development.rb2
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/initializers/0_runtime_identify.rb13
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--config/initializers/7_prometheus_metrics.rb12
-rw-r--r--config/initializers/active_record_lifecycle.rb2
-rw-r--r--config/initializers/cluster_events_before_phased_restart.rb6
-rw-r--r--config/initializers/database_config.rb2
-rw-r--r--config/initializers/lograge.rb2
-rw-r--r--config/initializers/rack_timeout.rb2
-rw-r--r--config/initializers/tracing.rb2
-rw-r--r--config/initializers/validate_puma.rb2
-rw-r--r--db/migrate/20191208071111_add_fingerprint_sha256_to_key.rb13
-rw-r--r--db/migrate/20191208071112_add_fingerprint_sha256_index_to_key.rb17
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/keys.md83
-rw-r--r--lib/api/keys.rb17
-rw-r--r--lib/gitlab.rb4
-rw-r--r--lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml21
-rw-r--r--lib/gitlab/cluster/lifecycle_events.rb6
-rw-r--r--lib/gitlab/database/sha256_attribute.rb33
-rw-r--r--lib/gitlab/gitaly_client.rb8
-rw-r--r--lib/gitlab/gpg.rb2
-rw-r--r--lib/gitlab/health_checks/puma_check.rb2
-rw-r--r--lib/gitlab/health_checks/unicorn_check.rb2
-rw-r--r--lib/gitlab/highlight.rb2
-rw-r--r--lib/gitlab/insecure_key_fingerprint.rb5
-rw-r--r--lib/gitlab/metrics/influx_db.rb2
-rw-r--r--lib/gitlab/metrics/samplers/influx_sampler.rb6
-rw-r--r--lib/gitlab/metrics/samplers/unicorn_sampler.rb2
-rw-r--r--lib/gitlab/redis/wrapper.rb4
-rw-r--r--lib/gitlab/runtime.rb62
-rw-r--r--lib/prometheus/pid_provider.rb6
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/finders/keys_finder_spec.rb77
-rw-r--r--spec/initializers/database_config_spec.rb1
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb4
-rw-r--r--spec/lib/gitlab/gpg_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/puma_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/unicorn_check_spec.rb2
-rw-r--r--spec/lib/gitlab/highlight_spec.rb2
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb9
-rw-r--r--spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb4
-rw-r--r--spec/lib/gitlab/runtime_spec.rb112
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb28
-rw-r--r--spec/lib/prometheus/pid_provider_spec.rb18
-rw-r--r--spec/models/concerns/sha256_attribute_spec.rb91
-rw-r--r--spec/models/key_spec.rb3
-rw-r--r--spec/requests/api/keys_spec.rb70
-rw-r--r--spec/services/git/branch_push_service_spec.rb2
-rw-r--r--spec/support/redis/redis_shared_examples.rb4
59 files changed, 833 insertions, 94 deletions
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 32491dfbcb6..5d7be0c705a 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -315,8 +315,7 @@ export default {
<gl-dropdown-item
v-if="showDelete"
- class="text-danger"
- data-qa-selector="delete_board_button"
+ class="text-danger js-delete-board"
@click.prevent="showPage('delete')"
>
{{ s__('IssueBoards|Delete board') }}
diff --git a/app/finders/keys_finder.rb b/app/finders/keys_finder.rb
new file mode 100644
index 00000000000..d6ba7cb290d
--- /dev/null
+++ b/app/finders/keys_finder.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+class KeysFinder
+ InvalidFingerprint = Class.new(StandardError)
+ GitLabAccessDeniedError = Class.new(StandardError)
+
+ FINGERPRINT_ATTRIBUTES = {
+ 'sha256' => 'fingerprint_sha256',
+ 'md5' => 'fingerprint'
+ }.freeze
+
+ def initialize(current_user, params)
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ raise GitLabAccessDeniedError unless current_user.admin?
+ raise InvalidFingerprint unless valid_fingerprint_param?
+
+ Key.where(fingerprint_query).first # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ private
+
+ attr_reader :current_user, :params
+
+ def valid_fingerprint_param?
+ if fingerprint_type == "sha256"
+ Base64.decode64(fingerprint).length == 32
+ else
+ fingerprint =~ /^(\h{2}:){15}\h{2}/
+ end
+ end
+
+ def fingerprint_query
+ fingerprint_attribute = FINGERPRINT_ATTRIBUTES[fingerprint_type]
+
+ Key.arel_table[fingerprint_attribute].eq(fingerprint)
+ end
+
+ def fingerprint_type
+ if params[:fingerprint].start_with?(/sha256:|SHA256:/)
+ "sha256"
+ else
+ "md5"
+ end
+ end
+
+ def fingerprint
+ if fingerprint_type == "sha256"
+ params[:fingerprint].gsub(/sha256:|SHA256:/, "")
+ else
+ params[:fingerprint]
+ end
+ end
+end
diff --git a/app/models/concerns/sha256_attribute.rb b/app/models/concerns/sha256_attribute.rb
new file mode 100644
index 00000000000..1bd1ad177a2
--- /dev/null
+++ b/app/models/concerns/sha256_attribute.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Sha256Attribute
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def sha256_attribute(name)
+ return if ENV['STATIC_VERIFICATION']
+
+ validate_binary_column_exists!(name) unless Rails.env.production?
+
+ attribute(name, Gitlab::Database::Sha256Attribute.new)
+ end
+
+ # This only gets executed in non-production environments as an additional check to ensure
+ # the column is the correct type. In production it should behave like any other attribute.
+ # See https://gitlab.com/gitlab-org/gitlab/merge_requests/5502 for more discussion
+ def validate_binary_column_exists!(name)
+ return unless database_exists?
+
+ unless table_exists?
+ warn "WARNING: sha256_attribute #{name.inspect} is invalid since the table doesn't exist - you may need to run database migrations"
+ return
+ end
+
+ column = columns.find { |c| c.name == name.to_s }
+
+ unless column
+ warn "WARNING: sha256_attribute #{name.inspect} is invalid since the column doesn't exist - you may need to run database migrations"
+ return
+ end
+
+ unless column.type == :binary
+ raise ArgumentError.new("sha256_attribute #{name.inspect} is invalid since the column type is not :binary")
+ end
+ rescue => error
+ Gitlab::AppLogger.error "Sha256Attribute initialization: #{error.message}"
+ raise
+ end
+
+ def database_exists?
+ ApplicationRecord.connection
+
+ true
+ rescue
+ false
+ end
+ end
+end
diff --git a/app/models/key.rb b/app/models/key.rb
index ff601966c26..f66aa4fb329 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -5,6 +5,9 @@ require 'digest/md5'
class Key < ApplicationRecord
include AfterCommitQueue
include Sortable
+ include Sha256Attribute
+
+ sha256_attribute :fingerprint_sha256
belongs_to :user
@@ -34,6 +37,8 @@ class Key < ApplicationRecord
after_destroy :post_destroy_hook
after_destroy :refresh_user_cache
+ alias_attribute :fingerprint_md5, :fingerprint
+
def self.regular_keys
where(type: ['Key', nil])
end
@@ -114,10 +119,12 @@ class Key < ApplicationRecord
def generate_fingerprint
self.fingerprint = nil
+ self.fingerprint_sha256 = nil
return unless public_key.valid?
- self.fingerprint = public_key.fingerprint
+ self.fingerprint_md5 = public_key.fingerprint
+ self.fingerprint_sha256 = public_key.fingerprint("SHA256").gsub("SHA256:", "")
end
def key_meets_restrictions
diff --git a/app/services/git/base_hooks_service.rb b/app/services/git/base_hooks_service.rb
index d935d9e8cdc..a49983a84fc 100644
--- a/app/services/git/base_hooks_service.rb
+++ b/app/services/git/base_hooks_service.rb
@@ -163,7 +163,7 @@ module Git
end
def logger
- if Sidekiq.server?
+ if Gitlab::Runtime.sidekiq?
Sidekiq.logger
else
# This service runs in Sidekiq, so this shouldn't ever be
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 0ef01dec493..02f1a267044 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -17,11 +17,21 @@
.col-md-8
= form_errors(@key, type: 'key') unless @key.valid?
- %p
- %span.light= _('Fingerprint:')
- %code.key-fingerprint= @key.fingerprint
%pre.well-pre
= @key.key
+ .card
+ .card-header
+ = _('Fingerprints')
+ %ul.content-list
+ %li
+ %span.light= 'MD5:'
+ %code.key-fingerprint= @key.fingerprint
+ - if @key.fingerprint_sha256.present?
+ %li
+ %span.light= 'SHA256:'
+ %code.key-fingerprint= @key.fingerprint_sha256
+
+
.col-md-12
.float-right
- if @key.can_delete?
diff --git a/changelogs/unreleased/feat-ssh-sha256.yml b/changelogs/unreleased/feat-ssh-sha256.yml
new file mode 100644
index 00000000000..91a881a5962
--- /dev/null
+++ b/changelogs/unreleased/feat-ssh-sha256.yml
@@ -0,0 +1,5 @@
+---
+title: add sha256 fingerprint to keys model, view and extend users API to search user via fingerprint
+merge_request: 19860
+author: Roger Meier
+type: added
diff --git a/config/application.rb b/config/application.rb
index cad5c8bbe76..28c1eba920b 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -22,6 +22,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/current_settings')
require_dependency Rails.root.join('lib/gitlab/middleware/read_only')
require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check')
+ require_dependency Rails.root.join('lib/gitlab/runtime')
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
@@ -255,7 +256,7 @@ module Gitlab
caching_config_hash[:compress] = false
caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
- if Sidekiq.server? || defined?(::Puma) # threaded context
+ if Gitlab::Runtime.multi_threaded?
caching_config_hash[:pool_size] = Gitlab::Redis::Cache.pool_size
caching_config_hash[:pool_timeout] = 1
end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 2939e13ef94..dc804197fef 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -46,7 +46,7 @@ Rails.application.configure do
# Do not log asset requests
config.assets.quiet = true
- config.allow_concurrency = defined?(::Puma)
+ config.allow_concurrency = Gitlab::Runtime.multi_threaded?
# BetterErrors live shell (REPL) on every stack frame
BetterErrors::Middleware.allow_ip!("127.0.0.1/0")
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 09bcf49a9a5..7ec18547b2f 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -75,5 +75,5 @@ Rails.application.configure do
config.eager_load = true
- config.allow_concurrency = defined?(::Puma)
+ config.allow_concurrency = Gitlab::Runtime.multi_threaded?
end
diff --git a/config/initializers/0_runtime_identify.rb b/config/initializers/0_runtime_identify.rb
new file mode 100644
index 00000000000..2b5d08102eb
--- /dev/null
+++ b/config/initializers/0_runtime_identify.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+begin
+ Gitlab::AppLogger.info("Runtime: #{Gitlab::Runtime.name}")
+rescue => e
+ message = <<-NOTICE
+ \n!! RUNTIME IDENTIFICATION FAILED: #{e}
+ Runtime based configuration settings may not work properly.
+ If you continue to see this error, please file an issue via
+ https://gitlab.com/gitlab-org/gitlab/issues/new
+ NOTICE
+ Gitlab::AppLogger.error(message)
+end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 8e4aa5701b4..bb0c4696eff 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -364,7 +364,7 @@ Gitlab.ee do
# To ensure acceptable performance we only allow feature to be used with
# multithreaded web-server Puma. This will be removed once download logic is moved
# to GitLab workhorse
- Settings.dependency_proxy['enabled'] = false unless defined?(::Puma)
+ Settings.dependency_proxy['enabled'] = false unless Gitlab::Runtime.puma?
end
#
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index d40049970c1..513f4d9e6ad 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -4,11 +4,11 @@ require 'prometheus/client'
def prometheus_default_multiproc_dir
return unless Rails.env.development? || Rails.env.test?
- if Sidekiq.server?
+ if Gitlab::Runtime.sidekiq?
Rails.root.join('tmp/prometheus_multiproc_dir/sidekiq')
- elsif defined?(Unicorn::Worker)
+ elsif Gitlab::Runtime.unicorn?
Rails.root.join('tmp/prometheus_multiproc_dir/unicorn')
- elsif defined?(::Puma)
+ elsif Gitlab::Runtime.puma?
Rails.root.join('tmp/prometheus_multiproc_dir/puma')
else
Rails.root.join('tmp/prometheus_multiproc_dir')
@@ -55,9 +55,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Cluster::LifecycleEvents.on_master_start do
::Prometheus::Client.reinitialize_on_pid_change(force: true)
- if defined?(::Unicorn)
+ if Gitlab::Runtime.unicorn?
Gitlab::Metrics::Samplers::UnicornSampler.instance(Settings.monitoring.unicorn_sampler_interval).start
- elsif defined?(::Puma)
+ elsif Gitlab::Runtime.puma?
Gitlab::Metrics::Samplers::PumaSampler.instance(Settings.monitoring.puma_sampler_interval).start
end
@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
end
end
-if defined?(::Unicorn) || defined?(::Puma)
+if Gitlab::Runtime.app_server?
Gitlab::Cluster::LifecycleEvents.on_master_start do
Gitlab::Metrics::Exporter::WebExporter.instance.start
end
diff --git a/config/initializers/active_record_lifecycle.rb b/config/initializers/active_record_lifecycle.rb
index 61f1d299960..2cf0f0439a9 100644
--- a/config/initializers/active_record_lifecycle.rb
+++ b/config/initializers/active_record_lifecycle.rb
@@ -2,7 +2,7 @@
# Don't handle sidekiq configuration as it
# has its own special active record configuration here
-if defined?(ActiveRecord::Base) && !Sidekiq.server?
+if defined?(ActiveRecord::Base) && !Gitlab::Runtime.sidekiq?
Gitlab::Cluster::LifecycleEvents.on_worker_start do
ActiveSupport.on_load(:active_record) do
ActiveRecord::Base.establish_connection
diff --git a/config/initializers/cluster_events_before_phased_restart.rb b/config/initializers/cluster_events_before_phased_restart.rb
index cbb1dd1a53a..aae5470d6ae 100644
--- a/config/initializers/cluster_events_before_phased_restart.rb
+++ b/config/initializers/cluster_events_before_phased_restart.rb
@@ -5,10 +5,8 @@
#
# Follow-up the issue: https://gitlab.com/gitlab-org/gitlab/issues/34107
-if defined?(::Puma)
+if Gitlab::Runtime.puma?
Puma::Cluster.prepend(::Gitlab::Cluster::Mixins::PumaCluster)
-end
-
-if defined?(::Unicorn::HttpServer)
+elsif Gitlab::Runtime.unicorn?
Unicorn::HttpServer.prepend(::Gitlab::Cluster::Mixins::UnicornHttpServer)
end
diff --git a/config/initializers/database_config.rb b/config/initializers/database_config.rb
index d8c2821066b..509f04c9b02 100644
--- a/config/initializers/database_config.rb
+++ b/config/initializers/database_config.rb
@@ -2,7 +2,7 @@
# when running on puma, scale connection pool size with the number
# of threads per worker process
-if defined?(::Puma)
+if Gitlab::Runtime.puma?
db_config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env]
puma_options = Puma.cli_config.options
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index a8207862739..0acbe6a9258 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -1,5 +1,5 @@
# Only use Lograge for Rails
-unless Sidekiq.server?
+unless Gitlab::Runtime.sidekiq?
filename = File.join(Rails.root, 'log', "#{Rails.env}_json.log")
Rails.application.configure do
diff --git a/config/initializers/rack_timeout.rb b/config/initializers/rack_timeout.rb
index 246cf3482a4..1f1264de208 100644
--- a/config/initializers/rack_timeout.rb
+++ b/config/initializers/rack_timeout.rb
@@ -9,7 +9,7 @@
# and it's used only as the last resort. In such case this termination is
# logged and we should fix the potential timeout issue in the code itself.
-if defined?(::Puma) && !Rails.env.test?
+if Gitlab::Runtime.puma? && !Rails.env.test?
require 'rack/timeout/base'
Gitlab::Application.configure do |config|
diff --git a/config/initializers/tracing.rb b/config/initializers/tracing.rb
index 5b55a06692e..0ae57021fcf 100644
--- a/config/initializers/tracing.rb
+++ b/config/initializers/tracing.rb
@@ -13,7 +13,7 @@ if Labkit::Tracing.enabled?
end
# Instrument Sidekiq server calls when running Sidekiq server
- if Sidekiq.server?
+ if Gitlab::Runtime.sidekiq?
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Labkit::Tracing::Sidekiq::ServerMiddleware
diff --git a/config/initializers/validate_puma.rb b/config/initializers/validate_puma.rb
index 64bd6e7bbc1..5abcfbfe6be 100644
--- a/config/initializers/validate_puma.rb
+++ b/config/initializers/validate_puma.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-if defined?(::Puma) && ::Puma.cli_config.options[:workers].to_i.zero?
+if Gitlab::Runtime.puma? && ::Puma.cli_config.options[:workers].to_i.zero?
raise 'Puma is only supported in Cluster-mode: workers > 0'
end
diff --git a/db/migrate/20191208071111_add_fingerprint_sha256_to_key.rb b/db/migrate/20191208071111_add_fingerprint_sha256_to_key.rb
new file mode 100644
index 00000000000..1bc87357f7d
--- /dev/null
+++ b/db/migrate/20191208071111_add_fingerprint_sha256_to_key.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddFingerprintSha256ToKey < ActiveRecord::Migration[5.0]
+ DOWNTIME = false
+
+ def up
+ add_column(:keys, :fingerprint_sha256, :binary)
+ end
+
+ def down
+ remove_column(:keys, :fingerprint_sha256) if column_exists?(:keys, :fingerprint_sha256)
+ end
+end
diff --git a/db/migrate/20191208071112_add_fingerprint_sha256_index_to_key.rb b/db/migrate/20191208071112_add_fingerprint_sha256_index_to_key.rb
new file mode 100644
index 00000000000..6f0c4bcd321
--- /dev/null
+++ b/db/migrate/20191208071112_add_fingerprint_sha256_index_to_key.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddFingerprintSha256IndexToKey < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:keys, "fingerprint_sha256")
+ end
+
+ def down
+ remove_concurrent_index(:keys, "fingerprint_sha256")
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index deebbcb430d..c677944bad6 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_12_06_122926) do
+ActiveRecord::Schema.define(version: 2019_12_08_071112) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -2206,7 +2206,9 @@ ActiveRecord::Schema.define(version: 2019_12_06_122926) do
t.string "fingerprint"
t.boolean "public", default: false, null: false
t.datetime "last_used_at"
+ t.binary "fingerprint_sha256"
t.index ["fingerprint"], name: "index_keys_on_fingerprint", unique: true
+ t.index ["fingerprint_sha256"], name: "index_keys_on_fingerprint_sha256"
t.index ["id", "type"], name: "index_on_deploy_keys_id_and_type_and_public", unique: true, where: "(public = true)"
t.index ["user_id"], name: "index_keys_on_user_id"
end
diff --git a/doc/api/keys.md b/doc/api/keys.md
index 06b31a67d6a..5dedb630a27 100644
--- a/doc/api/keys.md
+++ b/doc/api/keys.md
@@ -4,13 +4,19 @@
Get SSH key with user by ID of an SSH key. Note only administrators can lookup SSH key with user by ID of an SSH key.
-```
+```text
GET /keys/:id
```
-Parameters:
+| Attribute | Type | Required | Description |
+|:----------|:--------|:---------|:---------------------|
+| `id` | integer | yes | The ID of an SSH key |
+
+Example request:
-- `id` (required) - The ID of an SSH key
+```sh
+curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/keys/1
+```
```json
{
@@ -51,3 +57,74 @@ Parameters:
}
}
```
+
+## Get user by fingerprint of SSH key
+
+You can search for a user that owns a specific SSH key. Note only administrators can lookup SSH key with the fingerprint of an SSH key.
+
+```text
+GET /keys
+```
+
+| Attribute | Type | Required | Description |
+|:--------------|:-------|:---------|:------------------------------|
+| `fingerprint` | string | yes | The fingerprint of an SSH key |
+
+Example request:
+
+```sh
+curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/keys?fingerprint=ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1'
+```
+
+If using sha256 fingerprint API calls, make sure that the fingerprint is URL-encoded.
+
+For example, `/` is represented by `%2F` and `:` is represented by`%3A`:
+
+```sh
+curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/keys?fingerprint=SHA256%3AnUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo%2FlCg
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "title": "Sample key 1",
+ "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
+ "created_at": "2019-11-14T15:11:13.222Z",
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://0.0.0.0:3000/root",
+ "created_at": "2019-11-14T15:09:34.831Z",
+ "bio": null,
+ "location": null,
+ "public_email": "",
+ "skype": "",
+ "linkedin": "",
+ "twitter": "",
+ "website_url": "",
+ "organization": null,
+ "last_sign_in_at": "2019-11-16T22:41:26.663Z",
+ "confirmed_at": "2019-11-14T15:09:34.575Z",
+ "last_activity_on": "2019-11-20",
+ "email": "admin@example.com",
+ "theme_id": 1,
+ "color_scheme_id": 1,
+ "projects_limit": 100000,
+ "current_sign_in_at": "2019-11-19T14:42:18.078Z",
+ "identities": [
+ ],
+ "can_create_group": true,
+ "can_create_project": true,
+ "two_factor_enabled": false,
+ "external": false,
+ "private_profile": false,
+ "shared_runners_minutes_limit": null,
+ "extra_shared_runners_minutes_limit": null
+ }
+}
+```
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index d5280a0035d..8f2fd8cbae2 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -16,6 +16,23 @@ module API
present key, with: Entities::SSHKeyWithUser, current_user: current_user
end
+
+ desc 'Get SSH Key information' do
+ success Entities::UserWithAdmin
+ end
+ params do
+ requires :fingerprint, type: String, desc: 'Search for a SSH fingerprint'
+ end
+ get do
+ authenticated_with_full_private_access!
+
+ key = KeysFinder.new(current_user, params).execute
+
+ not_found!('Key') unless key
+ present key, with: Entities::SSHKeyWithUser, current_user: current_user
+ rescue KeysFinder::InvalidFingerprint
+ render_api_error!('Failed to return the key', 400)
+ end
end
end
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 0e6db54eb46..f2bff51df38 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -100,8 +100,8 @@ module Gitlab
end
def self.process_name
- return 'sidekiq' if Sidekiq.server?
- return 'console' if defined?(Rails::Console)
+ return 'sidekiq' if Gitlab::Runtime.sidekiq?
+ return 'console' if Gitlab::Runtime.console?
return 'test' if Rails.env.test?
'web'
diff --git a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
index 9a3ecd1c34f..975cb3b7698 100644
--- a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
@@ -1,5 +1,16 @@
-# Full project: https://gitlab.com/pages/hugo
-image: dettmering/hugo-build
+---
+# All available Hugo versions are listed here:
+# https://gitlab.com/pages/hugo/container_registry
+image: registry.gitlab.com/pages/hugo:latest
+
+variables:
+ GIT_SUBMODULE_STRATEGY: recursive
+
+test:
+ script:
+ - hugo
+ except:
+ - master
pages:
script:
@@ -9,9 +20,3 @@ pages:
- public
only:
- master
-
-test:
- script:
- - hugo
- except:
- - master
diff --git a/lib/gitlab/cluster/lifecycle_events.rb b/lib/gitlab/cluster/lifecycle_events.rb
index 2b3dc94fc5e..4ae75e0db0a 100644
--- a/lib/gitlab/cluster/lifecycle_events.rb
+++ b/lib/gitlab/cluster/lifecycle_events.rb
@@ -149,10 +149,10 @@ module Gitlab
def in_clustered_environment?
# Sidekiq doesn't fork
- return false if Sidekiq.server?
+ return false if Gitlab::Runtime.sidekiq?
# Unicorn always forks
- return true if defined?(::Unicorn)
+ return true if Gitlab::Runtime.unicorn?
# Puma sometimes forks
return true if in_clustered_puma?
@@ -162,7 +162,7 @@ module Gitlab
end
def in_clustered_puma?
- return false unless defined?(::Puma)
+ return false unless Gitlab::Runtime.puma?
@puma_options && @puma_options[:workers] && @puma_options[:workers] > 0
end
diff --git a/lib/gitlab/database/sha256_attribute.rb b/lib/gitlab/database/sha256_attribute.rb
new file mode 100644
index 00000000000..adf3f7fb5a6
--- /dev/null
+++ b/lib/gitlab/database/sha256_attribute.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ # Class for casting binary data to hexadecimal SHA256 hashes (and vice-versa).
+ #
+ # Using Sha256Attribute allows you to store SHA256 values as binary while still
+ # using them as if they were stored as string values. This gives you the
+ # ease of use of string values, but without the storage overhead.
+ class Sha256Attribute < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea
+ # Casts binary data to a SHA256 and remove trailing = and newline from encode64
+ def deserialize(value)
+ value = super(value)
+ if value.present?
+ Base64.encode64(value).delete("=").chomp("\n")
+ else
+ nil
+ end
+ end
+
+ # Casts a SHA256 in a proper binary format. which is 32 bytes long
+ def serialize(value)
+ arg = if value.present?
+ Base64.decode64(value)
+ else
+ nil
+ end
+
+ super(arg)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 5b47853b9c1..373539f5516 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -29,7 +29,7 @@ module Gitlab
PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m.freeze
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'
MAXIMUM_GITALY_CALLS = 30
- CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
+ CLIENT_NAME = (Gitlab::Runtime.sidekiq? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
GITALY_METADATA_FILENAME = '.gitaly-metadata'
MUTEX = Mutex.new
@@ -383,17 +383,13 @@ module Gitlab
end
def self.long_timeout
- if web_app_server?
+ if Gitlab::Runtime.app_server?
default_timeout
else
6.hours
end
end
- def self.web_app_server?
- defined?(::Unicorn) || defined?(::Puma)
- end
-
def self.storage_metadata_file_path(storage)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
File.join(
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index 829e64b11a4..abe90bba19c 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -135,7 +135,7 @@ module Gitlab
end
def cleanup_time
- Sidekiq.server? ? BG_CLEANUP_RUNTIME_S : FG_CLEANUP_RUNTIME_S
+ Gitlab::Runtime.sidekiq? ? BG_CLEANUP_RUNTIME_S : FG_CLEANUP_RUNTIME_S
end
def tmp_keychains_created
diff --git a/lib/gitlab/health_checks/puma_check.rb b/lib/gitlab/health_checks/puma_check.rb
index 7aafe29fbae..9f09070a57d 100644
--- a/lib/gitlab/health_checks/puma_check.rb
+++ b/lib/gitlab/health_checks/puma_check.rb
@@ -18,7 +18,7 @@ module Gitlab
end
def check
- return unless defined?(::Puma)
+ return unless Gitlab::Runtime.puma?
stats = Puma.stats
stats = JSON.parse(stats)
diff --git a/lib/gitlab/health_checks/unicorn_check.rb b/lib/gitlab/health_checks/unicorn_check.rb
index a30ae015257..cdc6d2a7519 100644
--- a/lib/gitlab/health_checks/unicorn_check.rb
+++ b/lib/gitlab/health_checks/unicorn_check.rb
@@ -30,7 +30,7 @@ module Gitlab
# to change so we can cache the list of servers.
def http_servers
strong_memoize(:http_servers) do
- next unless defined?(::Unicorn::HttpServer)
+ next unless Gitlab::Runtime.unicorn?
ObjectSpace.each_object(::Unicorn::HttpServer).to_a
end
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 381f1dd4e55..5663b9f20cf 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -68,7 +68,7 @@ module Gitlab
end
def timeout_time
- Sidekiq.server? ? TIMEOUT_BACKGROUND : TIMEOUT_FOREGROUND
+ Gitlab::Runtime.sidekiq? ? TIMEOUT_BACKGROUND : TIMEOUT_FOREGROUND
end
def link_dependencies(text, highlighted_text)
diff --git a/lib/gitlab/insecure_key_fingerprint.rb b/lib/gitlab/insecure_key_fingerprint.rb
index e4f0e9d2c73..7b1cf5e7931 100644
--- a/lib/gitlab/insecure_key_fingerprint.rb
+++ b/lib/gitlab/insecure_key_fingerprint.rb
@@ -10,6 +10,7 @@ module Gitlab
#
class InsecureKeyFingerprint
attr_accessor :key
+ alias_attribute :fingerprint_md5, :fingerprint
#
# Gets the base64 encoded string representing a rsa or dsa key
@@ -21,5 +22,9 @@ module Gitlab
def fingerprint
OpenSSL::Digest::MD5.hexdigest(Base64.decode64(@key)).scan(/../).join(':')
end
+
+ def fingerprint_sha256
+ Digest::SHA256.base64digest(Base64.decode64(@key)).scan(/../).join('').delete("=")
+ end
end
end
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 269d90fa971..1f252572461 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -150,7 +150,7 @@ module Gitlab
# Returns the prefix to use for the name of a series.
def series_prefix
- @series_prefix ||= Sidekiq.server? ? 'sidekiq_' : 'rails_'
+ @series_prefix ||= Gitlab::Runtime.sidekiq? ? 'sidekiq_' : 'rails_'
end
# Allow access from other metrics related middlewares
diff --git a/lib/gitlab/metrics/samplers/influx_sampler.rb b/lib/gitlab/metrics/samplers/influx_sampler.rb
index 1eae0a7bf45..4e16e335bee 100644
--- a/lib/gitlab/metrics/samplers/influx_sampler.rb
+++ b/lib/gitlab/metrics/samplers/influx_sampler.rb
@@ -39,14 +39,10 @@ module Gitlab
end
def add_metric(series, values, tags = {})
- prefix = sidekiq? ? 'sidekiq_' : 'rails_'
+ prefix = Gitlab::Runtime.sidekiq? ? 'sidekiq_' : 'rails_'
@metrics << Metric.new("#{prefix}#{series}", values, tags)
end
-
- def sidekiq?
- Sidekiq.server?
- end
end
end
end
diff --git a/lib/gitlab/metrics/samplers/unicorn_sampler.rb b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
index 355f938704e..8c4d150adad 100644
--- a/lib/gitlab/metrics/samplers/unicorn_sampler.rb
+++ b/lib/gitlab/metrics/samplers/unicorn_sampler.rb
@@ -61,7 +61,7 @@ module Gitlab
# it takes around 80ms. The instances of HttpServers are not a subject
# to change so we can cache the list of servers.
def http_servers
- return [] unless defined?(::Unicorn::HttpServer)
+ return [] unless Gitlab::Runtime.unicorn?
@http_servers ||= ObjectSpace.each_object(::Unicorn::HttpServer).to_a
end
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index 412d00c6939..beceed3fa75 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -22,10 +22,10 @@ module Gitlab
def pool_size
# heuristic constant 5 should be a config setting somewhere -- related to CPU count?
size = 5
- if Sidekiq.server?
+ if Gitlab::Runtime.sidekiq?
# the pool will be used in a multi-threaded context
size += Sidekiq.options[:concurrency]
- elsif defined?(::Puma)
+ elsif Gitlab::Runtime.puma?
size += Puma.cli_config.options[:max_threads]
end
diff --git a/lib/gitlab/runtime.rb b/lib/gitlab/runtime.rb
new file mode 100644
index 00000000000..07a3afb8834
--- /dev/null
+++ b/lib/gitlab/runtime.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Gitlab
+ # Provides routines to identify the current runtime as which the application
+ # executes, such as whether it is an application server and which one.
+ module Runtime
+ class << self
+ def name
+ matches = []
+ matches << :puma if puma?
+ matches << :unicorn if unicorn?
+ matches << :console if console?
+ matches << :sidekiq if sidekiq?
+
+ raise "Ambiguous process match: #{matches}" if matches.size > 1
+
+ matches.first || :unknown
+ end
+
+ def puma?
+ !!(defined?(::Puma) && bin == 'puma')
+ end
+
+ # For unicorn, we need to check for actual server instances to avoid false positives.
+ def unicorn?
+ !!(defined?(::Unicorn) && defined?(::Unicorn::HttpServer))
+ end
+
+ def sidekiq?
+ !!(defined?(::Sidekiq) && Sidekiq.server? && bin == 'sidekiq')
+ end
+
+ def console?
+ !!defined?(::Rails::Console)
+ end
+
+ def app_server?
+ puma? || unicorn?
+ end
+
+ def multi_threaded?
+ puma? || sidekiq?
+ end
+
+ private
+
+ # Some example values from my system:
+ # puma: /data/cache/bundle-2.5/bin/puma
+ # unicorn: unicorn_rails master -E development -c /tmp/unicorn.rb -l 0.0.0.0:8080
+ # sidekiq: /data/cache/bundle-2.5/bin/sidekiq
+ # thin: bin/rails
+ # console: bin/rails
+ def script_name
+ $0
+ end
+
+ def bin
+ File.basename(script_name)
+ end
+ end
+ end
+end
diff --git a/lib/prometheus/pid_provider.rb b/lib/prometheus/pid_provider.rb
index 228639357ac..32beeb0d31e 100644
--- a/lib/prometheus/pid_provider.rb
+++ b/lib/prometheus/pid_provider.rb
@@ -5,11 +5,11 @@ module Prometheus
extend self
def worker_id
- if Sidekiq.server?
+ if Gitlab::Runtime.sidekiq?
sidekiq_worker_id
- elsif defined?(Unicorn::Worker)
+ elsif Gitlab::Runtime.unicorn?
unicorn_worker_id
- elsif defined?(::Puma)
+ elsif Gitlab::Runtime.puma?
puma_worker_id
else
unknown_process_id
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5c417aa1f29..e5ca8f39991 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7703,9 +7703,6 @@ msgstr ""
msgid "Fingerprint"
msgstr ""
-msgid "Fingerprint:"
-msgstr ""
-
msgid "Fingerprints"
msgstr ""
diff --git a/spec/finders/keys_finder_spec.rb b/spec/finders/keys_finder_spec.rb
new file mode 100644
index 00000000000..147e6ee3d84
--- /dev/null
+++ b/spec/finders/keys_finder_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe KeysFinder do
+ subject(:keys_finder) { described_class.new(user, params) }
+
+ let(:user) { create(:user) }
+ let(:fingerprint_type) { 'md5' }
+ let(:fingerprint) { 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1' }
+
+ let(:params) do
+ {
+ type: fingerprint_type,
+ fingerprint: fingerprint
+ }
+ end
+
+ let!(:key) do
+ create(:key, user: user,
+ key: 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt1016k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=',
+ fingerprint: 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1',
+ fingerprint_sha256: 'nUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo/lCg'
+ )
+ end
+
+ context 'with a regular user' do
+ it 'raises GitLabAccessDeniedError' do
+ expect do
+ keys_finder.execute
+ end.to raise_error(KeysFinder::GitLabAccessDeniedError)
+ end
+ end
+
+ context 'with an admin user' do
+ let(:user) {create(:admin)}
+
+ context 'with invalid MD5 fingerprint' do
+ let(:fingerprint) { '11:11:11:11' }
+
+ it 'raises InvalidFingerprint' do
+ expect { keys_finder.execute }
+ .to raise_error(KeysFinder::InvalidFingerprint)
+ end
+ end
+
+ context 'with invalid SHA fingerprint' do
+ let(:fingerprint_type) { 'sha256' }
+ let(:fingerprint) { 'nUhzNyftwAAKs7HufskYTte2g' }
+
+ it 'raises InvalidFingerprint' do
+ expect { keys_finder.execute }
+ .to raise_error(KeysFinder::InvalidFingerprint)
+ end
+ end
+
+ context 'with valid MD5 params' do
+ it 'returns key if the fingerprint is found' do
+ result = keys_finder.execute
+
+ expect(result).to eq(key)
+ expect(key.user).to eq(user)
+ end
+ end
+
+ context 'with valid SHA256 params' do
+ let(:fingerprint) { 'ba:81:59:68:d7:6c:cd:02:02:bf:6a:9b:55:4e:af:d1' }
+
+ it 'returns key if the fingerprint is found' do
+ result = keys_finder.execute
+
+ expect(result).to eq(key)
+ expect(key.user).to eq(user)
+ end
+ end
+ end
+end
diff --git a/spec/initializers/database_config_spec.rb b/spec/initializers/database_config_spec.rb
index a5a074f5884..9200a625b38 100644
--- a/spec/initializers/database_config_spec.rb
+++ b/spec/initializers/database_config_spec.rb
@@ -16,6 +16,7 @@ describe 'Database config initializer' do
let(:puma_options) { { max_threads: 8 } }
before do
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
stub_const("Puma", puma)
allow(puma).to receive_message_chain(:cli_config, :options).and_return(puma_options)
end
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index 9ebd34140c1..80c1493d01b 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -26,7 +26,7 @@ describe Gitlab::GitalyClient do
context 'running in Unicorn' do
before do
- stub_const('Unicorn', 1)
+ allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
end
it { expect(subject.long_timeout).to eq(55) }
@@ -34,7 +34,7 @@ describe Gitlab::GitalyClient do
context 'running in Puma' do
before do
- stub_const('Puma', 1)
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
end
it { expect(subject.long_timeout).to eq(55) }
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index cd593390821..5d43023502c 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -236,7 +236,7 @@ describe Gitlab::Gpg do
context 'when running in Sidekiq' do
before do
- allow(Sidekiq).to receive(:server?).and_return(true)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
end
it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::BG_CLEANUP_RUNTIME_S
diff --git a/spec/lib/gitlab/health_checks/puma_check_spec.rb b/spec/lib/gitlab/health_checks/puma_check_spec.rb
index dd052a4dd2c..93ef81978a8 100644
--- a/spec/lib/gitlab/health_checks/puma_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/puma_check_spec.rb
@@ -22,6 +22,7 @@ describe Gitlab::HealthChecks::PumaCheck do
context 'when Puma is not loaded' do
before do
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(false)
hide_const('Puma')
end
@@ -33,6 +34,7 @@ describe Gitlab::HealthChecks::PumaCheck do
context 'when Puma is loaded' do
before do
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
stub_const('Puma', Module.new)
end
diff --git a/spec/lib/gitlab/health_checks/unicorn_check_spec.rb b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
index 931b61cb168..7c57b6f1ca5 100644
--- a/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/unicorn_check_spec.rb
@@ -26,6 +26,7 @@ describe Gitlab::HealthChecks::UnicornCheck do
context 'when Unicorn is not loaded' do
before do
+ allow(Gitlab::Runtime).to receive(:unicorn?).and_return(false)
hide_const('Unicorn')
end
@@ -39,6 +40,7 @@ describe Gitlab::HealthChecks::UnicornCheck do
let(:http_server_class) { Struct.new(:worker_processes) }
before do
+ allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
stub_const('Unicorn::HttpServer', http_server_class)
end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index 5a45d724b83..2140cbae488 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -111,7 +111,7 @@ describe Gitlab::Highlight do
end
it 'utilizes longer timeout for sidekiq' do
- allow(Sidekiq).to receive(:server?).and_return(true)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
expect(Timeout).to receive(:timeout).with(described_class::TIMEOUT_BACKGROUND).and_call_original
subject.highlight("Content")
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
index 7f20ae98b06..8d0422bae9f 100644
--- a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -11,10 +11,17 @@ describe Gitlab::InsecureKeyFingerprint do
end
let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" }
+ let(:fingerprint_sha256) { "MQHWhS9nhzUezUdD42ytxubZoBKrZLbyBZzxCkmnxXc" }
describe "#fingerprint" do
it "generates the key's fingerprint" do
- expect(described_class.new(key.split[1]).fingerprint).to eq(fingerprint)
+ expect(described_class.new(key.split[1]).fingerprint_md5).to eq(fingerprint)
+ end
+ end
+
+ describe "#fingerprint" do
+ it "generates the key's fingerprint" do
+ expect(described_class.new(key.split[1]).fingerprint_sha256).to eq(fingerprint_sha256)
end
end
end
diff --git a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
index 2d4b27a6ac1..939c057c342 100644
--- a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
@@ -63,7 +63,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
describe '#add_metric' do
it 'prefixes the series name for a Rails process' do
- expect(sampler).to receive(:sidekiq?).and_return(false)
+ expect(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
expect(Gitlab::Metrics::Metric).to receive(:new)
.with('rails_cats', { value: 10 }, {})
@@ -73,7 +73,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
end
it 'prefixes the series name for a Sidekiq process' do
- expect(sampler).to receive(:sidekiq?).and_return(true)
+ expect(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
expect(Gitlab::Metrics::Metric).to receive(:new)
.with('sidekiq_cats', { value: 10 }, {})
diff --git a/spec/lib/gitlab/runtime_spec.rb b/spec/lib/gitlab/runtime_spec.rb
new file mode 100644
index 00000000000..914c0fe2be7
--- /dev/null
+++ b/spec/lib/gitlab/runtime_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Runtime do
+ REAL_PATH = $0
+
+ after(:all) do
+ $0 = REAL_PATH
+ end
+
+ context "when unknown" do
+ it "identifies as :unknown" do
+ expect(subject.name).to eq(:unknown)
+ end
+ end
+
+ context "on multiple matches" do
+ before do
+ $0 = '/data/cache/bundle-2.5/bin/puma'
+ stub_const('::Puma', double)
+ stub_const('::Rails::Console', double)
+ end
+
+ it "raises an exception when trying to identify" do
+ expect { subject.name }.to raise_error(RuntimeError, "Ambiguous process match: [:puma, :console]")
+ end
+ end
+
+ context "puma" do
+ let(:puma_type) { double('::Puma') }
+
+ before do
+ $0 = '/data/cache/bundle-2.5/bin/puma'
+ stub_const('::Puma', puma_type)
+ end
+
+ it "identifies itself" do
+ expect(subject.name).to eq(:puma)
+ expect(subject.puma?).to be(true)
+ end
+
+ it "does not identify as others" do
+ expect(subject.unicorn?).to be(false)
+ expect(subject.sidekiq?).to be(false)
+ expect(subject.console?).to be(false)
+ end
+ end
+
+ context "unicorn" do
+ let(:unicorn_type) { Module.new }
+ let(:unicorn_server_type) { Class.new }
+
+ before do
+ $0 = 'unicorn_rails master -E development -c /tmp/unicorn.rb -l 0.0.0.0:8080'
+ stub_const('::Unicorn', unicorn_type)
+ stub_const('::Unicorn::HttpServer', unicorn_server_type)
+ end
+
+ it "identifies itself" do
+ expect(subject.name).to eq(:unicorn)
+ expect(subject.unicorn?).to be(true)
+ end
+
+ it "does not identify as others" do
+ expect(subject.puma?).to be(false)
+ expect(subject.sidekiq?).to be(false)
+ expect(subject.console?).to be(false)
+ end
+ end
+
+ context "sidekiq" do
+ let(:sidekiq_type) { double('::Sidekiq') }
+
+ before do
+ $0 = '/data/cache/bundle-2.5/bin/sidekiq'
+ stub_const('::Sidekiq', sidekiq_type)
+ allow(sidekiq_type).to receive(:server?).and_return(true)
+ end
+
+ it "identifies itself" do
+ expect(subject.name).to eq(:sidekiq)
+ expect(subject.sidekiq?).to be(true)
+ end
+
+ it "does not identify as others" do
+ expect(subject.unicorn?).to be(false)
+ expect(subject.puma?).to be(false)
+ expect(subject.console?).to be(false)
+ end
+ end
+
+ context "console" do
+ let(:console_type) { double('::Rails::Console') }
+
+ before do
+ $0 = 'bin/rails'
+ stub_const('::Rails::Console', console_type)
+ end
+
+ it "identifies itself" do
+ expect(subject.name).to eq(:console)
+ expect(subject.console?).to be(true)
+ end
+
+ it "does not identify as others" do
+ expect(subject.unicorn?).to be(false)
+ expect(subject.sidekiq?).to be(false)
+ expect(subject.puma?).to be(false)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index f8becb0c796..08e008c82d9 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -183,6 +183,34 @@ describe Gitlab::SSHPublicKey, lib: true do
end
end
+ describe '#fingerprint in SHA256 format' do
+ subject { public_key.fingerprint("SHA256").gsub("SHA256:", "") if public_key.fingerprint("SHA256") }
+
+ where(:factory, :fingerprint_sha256) do
+ [
+ [:rsa_key_2048, 'GdtgO0eHbwLB+mK47zblkoXujkqKRZjgMQrHH6Kks3E'],
+ [:rsa_key_4096, 'ByDU7hQ1JB95l6p53rHrffc4eXvEtqGUtQhS+Dhyy7g'],
+ [:rsa_key_5120, 'PCCupLbFHScm4AbEufbGDvhBU27IM0MVAor715qKQK8'],
+ [:rsa_key_8192, 'CtHFQAS+9Hb8z4vrv4gVQPsHjNN0WIZhWODaB1mQLs4'],
+ [:dsa_key_2048, '+a3DQ7cU5GM+gaYOfmc0VWNnykHQSuth3VRcCpWuYNI'],
+ [:ecdsa_key_256, 'C+I5k3D+IGeM6k5iBR1ZsphqTKV+7uvL/XZ5hcrTr7g'],
+ [:ed25519_key_256, 'DCKAjzxWrdOTjaGKBBjtCW8qY5++GaiAJflrHPmp6W0']
+ ]
+ end
+
+ with_them do
+ let(:key) { attributes_for(factory)[:key] }
+
+ it { is_expected.to eq(fingerprint_sha256) }
+ end
+
+ context 'with an invalid SSH key' do
+ let(:key) { 'this is not a key' }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
describe '#key_text' do
let(:key) { 'this is not a key' }
diff --git a/spec/lib/prometheus/pid_provider_spec.rb b/spec/lib/prometheus/pid_provider_spec.rb
index 6fdc11b14c4..5a17f25f144 100644
--- a/spec/lib/prometheus/pid_provider_spec.rb
+++ b/spec/lib/prometheus/pid_provider_spec.rb
@@ -6,16 +6,13 @@ describe Prometheus::PidProvider do
describe '.worker_id' do
subject { described_class.worker_id }
- let(:sidekiq_module) { Module.new }
-
before do
- allow(sidekiq_module).to receive(:server?).and_return(false)
- stub_const('Sidekiq', sidekiq_module)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
end
context 'when running in Sidekiq server mode' do
before do
- expect(Sidekiq).to receive(:server?).and_return(true)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
end
context 'in a clustered setup' do
@@ -33,8 +30,7 @@ describe Prometheus::PidProvider do
context 'when running in Unicorn mode' do
before do
- stub_const('Unicorn::Worker', Class.new)
- hide_const('Puma')
+ allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
expect(described_class).to receive(:process_name)
.at_least(:once)
@@ -94,8 +90,7 @@ describe Prometheus::PidProvider do
context 'when running in Puma mode' do
before do
- stub_const('Puma', Module.new)
- hide_const('Unicorn::Worker')
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
expect(described_class).to receive(:process_name)
.at_least(:once)
@@ -116,11 +111,6 @@ describe Prometheus::PidProvider do
end
context 'when running in unknown mode' do
- before do
- hide_const('Puma')
- hide_const('Unicorn::Worker')
- end
-
it { is_expected.to eq "process_#{Process.pid}" }
end
end
diff --git a/spec/models/concerns/sha256_attribute_spec.rb b/spec/models/concerns/sha256_attribute_spec.rb
new file mode 100644
index 00000000000..213723c2dcb
--- /dev/null
+++ b/spec/models/concerns/sha256_attribute_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Sha256Attribute do
+ let(:model) { Class.new { include Sha256Attribute } }
+
+ before do
+ columns = [
+ double(:column, name: 'name', type: :text),
+ double(:column, name: 'sha256', type: :binary)
+ ]
+
+ allow(model).to receive(:columns).and_return(columns)
+ end
+
+ describe '#sha_attribute' do
+ context 'when in non-production' do
+ before do
+ stub_rails_env('development')
+ end
+
+ context 'when the table exists' do
+ before do
+ allow(model).to receive(:table_exists?).and_return(true)
+ end
+
+ it 'defines a SHA attribute for a binary column' do
+ expect(model).to receive(:attribute)
+ .with(:sha256, an_instance_of(Gitlab::Database::Sha256Attribute))
+
+ model.sha256_attribute(:sha256)
+ end
+
+ it 'raises ArgumentError when the column type is not :binary' do
+ expect { model.sha256_attribute(:name) }.to raise_error(ArgumentError)
+ end
+ end
+
+ context 'when the table does not exist' do
+ it 'allows the attribute to be added and issues a warning' do
+ allow(model).to receive(:table_exists?).and_return(false)
+
+ expect(model).not_to receive(:columns)
+ expect(model).to receive(:attribute)
+ expect(model).to receive(:warn)
+
+ model.sha256_attribute(:name)
+ end
+ end
+
+ context 'when the column does not exist' do
+ it 'allows the attribute to be added and issues a warning' do
+ allow(model).to receive(:table_exists?).and_return(true)
+
+ expect(model).to receive(:columns)
+ expect(model).to receive(:attribute)
+ expect(model).to receive(:warn)
+
+ model.sha256_attribute(:no_name)
+ end
+ end
+
+ context 'when other execeptions are raised' do
+ it 'logs and re-rasises the error' do
+ allow(model).to receive(:table_exists?).and_raise(ActiveRecord::NoDatabaseError.new('does not exist'))
+
+ expect(model).not_to receive(:columns)
+ expect(model).not_to receive(:attribute)
+ expect(Gitlab::AppLogger).to receive(:error)
+
+ expect { model.sha256_attribute(:name) }.to raise_error(ActiveRecord::NoDatabaseError)
+ end
+ end
+ end
+
+ context 'when in production' do
+ before do
+ stub_rails_env('production')
+ end
+
+ it 'defines a SHA attribute' do
+ expect(model).not_to receive(:table_exists?)
+ expect(model).not_to receive(:columns)
+ expect(model).to receive(:attribute).with(:sha256, an_instance_of(Gitlab::Database::Sha256Attribute))
+
+ model.sha256_attribute(:sha256)
+ end
+ end
+ end
+end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index a0b6eff88d5..559dc95768a 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -92,6 +92,7 @@ describe Key, :mailer do
with_them do
let!(:key) { create(factory) }
let!(:original_fingerprint) { key.fingerprint }
+ let!(:original_fingerprint_sha256) { key.fingerprint_sha256 }
it 'accepts a key with blank space characters after stripping them' do
modified_key = key.key.insert(100, chars.first).insert(40, chars.last)
@@ -104,6 +105,8 @@ describe Key, :mailer do
expect(content).not_to match(/\s/)
expect(original_fingerprint).to eq(key.fingerprint)
+ expect(original_fingerprint).to eq(key.fingerprint_md5)
+ expect(original_fingerprint_sha256).to eq(key.fingerprint_sha256)
end
end
end
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index 6802a0cfdab..f7da1abcfdf 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -25,7 +25,6 @@ describe API::Keys do
it 'returns single ssh key with user information' do
user.keys << key
- user.save
get api("/keys/#{key.id}", admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq(key.title)
@@ -40,4 +39,73 @@ describe API::Keys do
end
end
end
+
+ describe 'GET /keys?fingerprint=' do
+ it 'returns authentication error' do
+ get api("/keys?fingerprint=#{key.fingerprint}")
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+
+ it 'returns authentication error when authenticated as user' do
+ get api("/keys?fingerprint=#{key.fingerprint}", user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ context 'when authenticated as admin' do
+ it 'returns 404 for non-existing SSH md5 fingerprint' do
+ get api("/keys?fingerprint=11:11:11:11:11:11:11:11:11:11:11:11:11:11:11:11", admin)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Key Not Found')
+ end
+
+ it 'returns 404 for non-existing SSH sha256 fingerprint' do
+ get api("/keys?fingerprint=#{URI.encode_www_form_component("SHA256:nUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo1lCg")}", admin)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Key Not Found')
+ end
+
+ it 'returns user if SSH md5 fingerprint found' do
+ user.keys << key
+
+ get api("/keys?fingerprint=#{key.fingerprint}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['title']).to eq(key.title)
+ expect(json_response['user']['id']).to eq(user.id)
+ expect(json_response['user']['username']).to eq(user.username)
+ end
+
+ it 'returns user if SSH sha256 fingerprint found' do
+ user.keys << key
+
+ get api("/keys?fingerprint=#{URI.encode_www_form_component("SHA256:" + key.fingerprint_sha256)}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['title']).to eq(key.title)
+ expect(json_response['user']['id']).to eq(user.id)
+ expect(json_response['user']['username']).to eq(user.username)
+ end
+
+ it 'returns user if SSH sha256 fingerprint found' do
+ user.keys << key
+
+ get api("/keys?fingerprint=#{URI.encode_www_form_component("sha256:" + key.fingerprint_sha256)}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['title']).to eq(key.title)
+ expect(json_response['user']['id']).to eq(user.id)
+ expect(json_response['user']['username']).to eq(user.username)
+ end
+
+ it "does not include the user's `is_admin` flag" do
+ get api("/keys?fingerprint=#{key.fingerprint}", admin)
+
+ expect(json_response['user']['is_admin']).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
index 19d7b84a3ce..e7f005cff0b 100644
--- a/spec/services/git/branch_push_service_spec.rb
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -108,7 +108,7 @@ describe Git::BranchPushService, services: true do
end
it 'reports an error' do
- allow(Sidekiq).to receive(:server?).and_return(true)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
expect(Sidekiq.logger).to receive(:warn)
expect { subject }.not_to change { Ci::Pipeline.count }
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index 97a23f02b3e..e079c32d6ae 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -118,7 +118,7 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when running not on sidekiq workers' do
before do
- allow(Sidekiq).to receive(:server?).and_return(false)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
end
it 'instantiates a connection pool with size 5' do
@@ -130,7 +130,7 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when running on sidekiq workers' do
before do
- allow(Sidekiq).to receive(:server?).and_return(true)
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
allow(Sidekiq).to receive(:options).and_return({ concurrency: 18 })
end