Commit 175b4fa2 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 4eea104c
...@@ -315,8 +315,7 @@ export default { ...@@ -315,8 +315,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-if="showDelete" v-if="showDelete"
class="text-danger" class="text-danger js-delete-board"
data-qa-selector="delete_board_button"
@click.prevent="showPage('delete')" @click.prevent="showPage('delete')"
> >
{{ s__('IssueBoards|Delete board') }} {{ s__('IssueBoards|Delete board') }}
......
# 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
# 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
...@@ -5,6 +5,9 @@ require 'digest/md5' ...@@ -5,6 +5,9 @@ require 'digest/md5'
class Key < ApplicationRecord class Key < ApplicationRecord
include AfterCommitQueue include AfterCommitQueue
include Sortable include Sortable
include Sha256Attribute
sha256_attribute :fingerprint_sha256
belongs_to :user belongs_to :user
...@@ -34,6 +37,8 @@ class Key < ApplicationRecord ...@@ -34,6 +37,8 @@ class Key < ApplicationRecord
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_destroy :refresh_user_cache after_destroy :refresh_user_cache
alias_attribute :fingerprint_md5, :fingerprint
def self.regular_keys def self.regular_keys
where(type: ['Key', nil]) where(type: ['Key', nil])
end end
...@@ -114,10 +119,12 @@ class Key < ApplicationRecord ...@@ -114,10 +119,12 @@ class Key < ApplicationRecord
def generate_fingerprint def generate_fingerprint
self.fingerprint = nil self.fingerprint = nil
self.fingerprint_sha256 = nil
return unless public_key.valid? 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 end
def key_meets_restrictions def key_meets_restrictions
......
...@@ -163,7 +163,7 @@ module Git ...@@ -163,7 +163,7 @@ module Git
end end
def logger def logger
if Sidekiq.server? if Gitlab::Runtime.sidekiq?
Sidekiq.logger Sidekiq.logger
else else
# This service runs in Sidekiq, so this shouldn't ever be # This service runs in Sidekiq, so this shouldn't ever be
......
...@@ -17,11 +17,21 @@ ...@@ -17,11 +17,21 @@
.col-md-8 .col-md-8
= form_errors(@key, type: 'key') unless @key.valid? = form_errors(@key, type: 'key') unless @key.valid?
%p
%span.light= _('Fingerprint:')
%code.key-fingerprint= @key.fingerprint
%pre.well-pre %pre.well-pre
= @key.key = @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 .col-md-12
.float-right .float-right
- if @key.can_delete? - if @key.can_delete?
......
---
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
...@@ -22,6 +22,7 @@ module Gitlab ...@@ -22,6 +22,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/current_settings') 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/read_only')
require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check') 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. # Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers # Application configuration should go into files in config/initializers
...@@ -255,7 +256,7 @@ module Gitlab ...@@ -255,7 +256,7 @@ module Gitlab
caching_config_hash[:compress] = false caching_config_hash[:compress] = false
caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever 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_size] = Gitlab::Redis::Cache.pool_size
caching_config_hash[:pool_timeout] = 1 caching_config_hash[:pool_timeout] = 1
end end
......
...@@ -46,7 +46,7 @@ Rails.application.configure do ...@@ -46,7 +46,7 @@ Rails.application.configure do
# Do not log asset requests # Do not log asset requests
config.assets.quiet = true 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 live shell (REPL) on every stack frame
BetterErrors::Middleware.allow_ip!("127.0.0.1/0") BetterErrors::Middleware.allow_ip!("127.0.0.1/0")
......
...@@ -75,5 +75,5 @@ Rails.application.configure do ...@@ -75,5 +75,5 @@ Rails.application.configure do
config.eager_load = true config.eager_load = true
config.allow_concurrency = defined?(::Puma) config.allow_concurrency = Gitlab::Runtime.multi_threaded?
end end
# 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
...@@ -364,7 +364,7 @@ Gitlab.ee do ...@@ -364,7 +364,7 @@ Gitlab.ee do
# To ensure acceptable performance we only allow feature to be used with # 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 # multithreaded web-server Puma. This will be removed once download logic is moved
# to GitLab workhorse # to GitLab workhorse
Settings.dependency_proxy['enabled'] = false unless defined?(::Puma) Settings.dependency_proxy['enabled'] = false unless Gitlab::Runtime.puma?
end end
# #
......
...@@ -4,11 +4,11 @@ require 'prometheus/client' ...@@ -4,11 +4,11 @@ require 'prometheus/client'
def prometheus_default_multiproc_dir def prometheus_default_multiproc_dir
return unless Rails.env.development? || Rails.env.test? return unless Rails.env.development? || Rails.env.test?
if Sidekiq.server? if Gitlab::Runtime.sidekiq?
Rails.root.join('tmp/prometheus_multiproc_dir/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') Rails.root.join('tmp/prometheus_multiproc_dir/unicorn')
elsif defined?(::Puma) elsif Gitlab::Runtime.puma?
Rails.root.join('tmp/prometheus_multiproc_dir/puma') Rails.root.join('tmp/prometheus_multiproc_dir/puma')
else else
Rails.root.join('tmp/prometheus_multiproc_dir') Rails.root.join('tmp/prometheus_multiproc_dir')
...@@ -55,9 +55,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? ...@@ -55,9 +55,9 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Cluster::LifecycleEvents.on_master_start do Gitlab::Cluster::LifecycleEvents.on_master_start do
::Prometheus::Client.reinitialize_on_pid_change(force: true) ::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 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 Gitlab::Metrics::Samplers::PumaSampler.instance(Settings.monitoring.puma_sampler_interval).start
end end
...@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled? ...@@ -65,7 +65,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
end end
end end
if defined?(::Unicorn) || defined?(::Puma) if Gitlab::Runtime.app_server?
Gitlab::Cluster::LifecycleEvents.on_master_start do Gitlab::Cluster::LifecycleEvents.on_master_start do
Gitlab::Metrics::Exporter::WebExporter.instance.start Gitlab::Metrics::Exporter::WebExporter.instance.start
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Don't handle sidekiq configuration as it # Don't handle sidekiq configuration as it
# has its own special active record configuration here # 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 Gitlab::Cluster::LifecycleEvents.on_worker_start do
ActiveSupport.on_load(:active_record) do ActiveSupport.on_load(:active_record) do
ActiveRecord::Base.establish_connection ActiveRecord::Base.establish_connection
......
...@@ -5,10 +5,8 @@ ...@@ -5,10 +5,8 @@
# #
# Follow-up the issue: https://gitlab.com/gitlab-org/gitlab/issues/34107 # 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) Puma::Cluster.prepend(::Gitlab::Cluster::Mixins::PumaCluster)
end elsif Gitlab::Runtime.unicorn?
if defined?(::Unicorn::HttpServer)
Unicorn::HttpServer.prepend(::Gitlab::Cluster::Mixins::UnicornHttpServer) Unicorn::HttpServer.prepend(::Gitlab::Cluster::Mixins::UnicornHttpServer)
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# when running on puma, scale connection pool size with the number # when running on puma, scale connection pool size with the number
# of threads per worker process # of threads per worker process
if defined?(::Puma) if Gitlab::Runtime.puma?
db_config = Gitlab::Database.config || db_config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env] Rails.application.config.database_configuration[Rails.env]
puma_options = Puma.cli_config.options puma_options = Puma.cli_config.options
......
# Only use Lograge for Rails # Only use Lograge for Rails
unless Sidekiq.server? unless Gitlab::Runtime.sidekiq?
filename = File.join(Rails.root, 'log', "#{Rails.env}_json.log") filename = File.join(Rails.root, 'log', "#{Rails.env}_json.log")
Rails.application.configure do Rails.application.configure do
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# and it's used only as the last resort. In such case this termination is # 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. # 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' require 'rack/timeout/base'
Gitlab::Application.configure do |config| Gitlab::Application.configure do |config|
......
...@@ -13,7 +13,7 @@ if Labkit::Tracing.enabled? ...@@ -13,7 +13,7 @@ if Labkit::Tracing.enabled?
end end
# Instrument Sidekiq server calls when running Sidekiq server # Instrument Sidekiq server calls when running Sidekiq server
if Sidekiq.server? if Gitlab::Runtime.sidekiq?
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.server_middleware do |chain| config.server_middleware do |chain|
chain.add Labkit::Tracing::Sidekiq::ServerMiddleware chain.add Labkit::Tracing::Sidekiq::ServerMiddleware
......
# frozen_string_literal: true # 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' raise 'Puma is only supported in Cluster-mode: workers > 0'
end end
# 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
# 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
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # 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 # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -2206,7 +2206,9 @@ ActiveRecord::Schema.define(version: 2019_12_06_122926) do ...@@ -2206,7 +2206,9 @@ ActiveRecord::Schema.define(version: 2019_12_06_122926) do
t.string "fingerprint" t.string "fingerprint"
t.boolean "public", default: false, null: false t.boolean "public", default: false, null: false
t.datetime "last_used_at" t.datetime "last_used_at"
t.binary "fingerprint_sha256"
t.index ["fingerprint"], name: "index_keys_on_fingerprint", unique: true 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 ["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" t.index ["user_id"], name: "index_keys_on_user_id"
end end
......
...@@ -4,13 +4,19 @@ ...@@ -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. 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 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 ```json
{ {
...@@ -51,3 +57,74 @@ Parameters: ...@@ -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
}
}
```
...@@ -16,6 +16,23 @@ module API ...@@ -16,6 +16,23 @@ module API
present key, with: Entities::SSHKeyWithUser, current_user: current_user present key, with: Entities::SSHKeyWithUser, current_user: current_user
end 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 end
end end
...@@ -100,8 +100,8 @@ module Gitlab ...@@ -100,8 +100,8 @@ module Gitlab
end end
def self.process_name def self.process_name
return 'sidekiq' if Sidekiq.server? return 'sidekiq' if Gitlab::Runtime.sidekiq?
return 'console' if defined?(Rails::Console) return 'console' if Gitlab::Runtime.console?
return 'test' if Rails.env.test? return 'test' if Rails.env.test?
'web' 'web'
......
# 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: pages:
script: script:
...@@ -9,9 +20,3 @@ pages: ...@@ -9,9 +20,3 @@ pages:
- public - public
only: only:
- master - master
test:
script:
- hugo
except:
- master
...@@ -149,10 +149,10 @@ module Gitlab ...@@ -149,10 +149,10 @@ module Gitlab
def in_clustered_environment? def in_clustered_environment?
# Sidekiq doesn't fork # Sidekiq doesn't fork
return false if Sidekiq.server? return false if Gitlab::Runtime.sidekiq?
# Unicorn always forks # Unicorn always forks
return true if defined?(::Unicorn) return true if Gitlab::Runtime.unicorn?
# Puma sometimes forks # Puma sometimes forks
return true if in_clustered_puma? return true if in_clustered_puma?
...@@ -162,7 +162,7 @@ module Gitlab ...@@ -162,7 +162,7 @@ module Gitlab
end end
def in_clustered_puma? def in_clustered_puma?
return false unless defined?(::Puma) return false unless Gitlab::Runtime.puma?
@puma_options && @puma_options[:workers] && @puma_options[:workers] > 0 @puma_options && @puma_options[:workers] && @puma_options[:workers] > 0
end end
......
# 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
...@@ -29,7 +29,7 @@ module Gitlab ...@@ -29,7 +29,7 @@ module Gitlab
PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m.freeze PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m.freeze
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION' SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'
MAXIMUM_GITALY_CALLS = 30 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' GITALY_METADATA_FILENAME = '.gitaly-metadata'
MUTEX = Mutex.new MUTEX = Mutex.new
...@@ -383,17 +383,13 @@ module Gitlab ...@@ -383,17 +383,13 @@ module Gitlab
end end
def self.long_timeout def self.long_timeout
if web_app_server? if Gitlab::Runtime.app_server?
default_timeout default_timeout
else else
6.hours 6.hours
end end
end end
def self.web_app_server?
defined?(::Unicorn) || defined?(::Puma)
end
def self.storage_metadata_file_path(storage) def self.storage_metadata_file_path(storage)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do Gitlab::GitalyClient::StorageSettings.allow_disk_access do
File.join( File.join(
......
...@@ -135,7 +135,7 @@ module Gitlab ...@@ -135,7 +135,7 @@ module Gitlab
end end
def cleanup_time 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 end
def tmp_keychains_created def tmp_keychains_created
......
...@@ -18,7 +18,7 @@ module Gitlab ...@@ -18,7 +18,7 @@ module Gitlab
end end
def check def check
return unless defined?(::Puma) return unless Gitlab::Runtime.puma?
stats = Puma.stats stats = Puma.stats
stats = JSON.parse(stats) stats = JSON.parse(stats)
......
...@@ -30,7 +30,7 @@ module Gitlab ...@@ -30,7 +30,7 @@ module Gitlab
# to change so we can cache the list of servers. # to change so we can cache the list of servers.
def http_servers def http_servers
strong_memoize(:http_servers) do strong_memoize(:http_servers) do
next unless defined?(::Unicorn::HttpServer) next unless Gitlab::Runtime.unicorn?
ObjectSpace.each_object(::Unicorn::HttpServer).to_a ObjectSpace.each_object(::Unicorn::HttpServer).to_a
end end
......
...@@ -68,7 +68,7 @@ module Gitlab ...@@ -68,7 +68,7 @@ module Gitlab
end end
def timeout_time def timeout_time
Sidekiq.server? ? TIMEOUT_BACKGROUND : TIMEOUT_FOREGROUND Gitlab::Runtime.sidekiq? ? TIMEOUT_BACKGROUND : TIMEOUT_FOREGROUND
end end
def link_dependencies(text, highlighted_text) def link_dependencies(text, highlighted_text)
......
...@@ -10,6 +10,7 @@ module Gitlab ...@@ -10,6 +10,7 @@ module Gitlab
# #
class InsecureKeyFingerprint class InsecureKeyFingerprint
attr_accessor :key attr_accessor :key
alias_attribute :fingerprint_md5, :fingerprint
# #
# Gets the base64 encoded string representing a rsa or dsa key # Gets the base64 encoded string representing a rsa or dsa key
...@@ -21,5 +22,9 @@ module Gitlab ...@@ -21,5 +22,9 @@ module Gitlab
def fingerprint def fingerprint
OpenSSL::Digest::MD5.hexdigest(Base64.decode64(@key)).scan(/../).join(':') OpenSSL::Digest::MD5.hexdigest(Base64.decode64(@key)).scan(/../).join(':')
end end
def fingerprint_sha256
Digest::SHA256.base64digest(Base64.decode64(@key)).scan(/../).join('').delete("=")
end
end end
end end
...@@ -150,7 +150,7 @@ module Gitlab ...@@ -150,7 +150,7 @@ module Gitlab
# Returns the prefix to use for the name of a series. # Returns the prefix to use for the name of a series.
def series_prefix def series_prefix
@series_prefix ||= Sidekiq.server? ? 'sidekiq_' : 'rails_' @series_prefix ||= Gitlab::Runtime.sidekiq? ? 'sidekiq_' : 'rails_'
end end
# Allow access from other metrics related middlewares # Allow access from other metrics related middlewares
......
...@@ -39,14 +39,10 @@ module Gitlab ...@@ -39,14 +39,10 @@ module Gitlab
end end
def add_metric(series, values, tags = {}) def add_metric(series, values, tags = {})
prefix = sidekiq? ? 'sidekiq_' : 'rails_' prefix = Gitlab::Runtime.sidekiq? ? 'sidekiq_' : 'rails_'
@metrics << Metric.new("#{prefix}#{series}", values, tags) @metrics << Metric.new("#{prefix}#{series}", values, tags)
end end
def sidekiq?
Sidekiq.server?
end
end end
end end
end end
......
...@@ -61,7 +61,7 @@ module Gitlab ...@@ -61,7 +61,7 @@ module Gitlab
# it takes around 80ms. The instances of HttpServers are not a subject # it takes around 80ms. The instances of HttpServers are not a subject
# to change so we can cache the list of servers. # to change so we can cache the list of servers.
def http_servers def http_servers
return [] unless defined?(::Unicorn::HttpServer) return [] unless Gitlab::Runtime.unicorn?
@http_servers ||= ObjectSpace.each_object(::Unicorn::HttpServer).to_a @http_servers ||= ObjectSpace.each_object(::Unicorn::HttpServer).to_a
end end
......
...@@ -22,10 +22,10 @@ module Gitlab ...@@ -22,10 +22,10 @@ module Gitlab
def pool_size def pool_size
# heuristic constant 5 should be a config setting somewhere -- related to CPU count? # heuristic constant 5 should be a config setting somewhere -- related to CPU count?
size = 5 size = 5
if Sidekiq.server? if Gitlab::Runtime.sidekiq?
# the pool will be used in a multi-threaded context # the pool will be used in a multi-threaded context
size += Sidekiq.options[:concurrency] size += Sidekiq.options[:concurrency]
elsif defined?(::Puma) elsif Gitlab::Runtime.puma?
size += Puma.cli_config.options[:max_threads] size += Puma.cli_config.options[:max_threads]
end end
......
# 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
...@@ -5,11 +5,11 @@ module Prometheus ...@@ -5,11 +5,11 @@ module Prometheus
extend self extend self
def worker_id def worker_id
if Sidekiq.server? if Gitlab::Runtime.sidekiq?
sidekiq_worker_id sidekiq_worker_id
elsif defined?(Unicorn::Worker) elsif Gitlab::Runtime.unicorn?
unicorn_worker_id unicorn_worker_id
elsif defined?(::Puma) elsif Gitlab::Runtime.puma?
puma_worker_id puma_worker_id
else else
unknown_process_id unknown_process_id
......
...@@ -7703,9 +7703,6 @@ msgstr "" ...@@ -7703,9 +7703,6 @@ msgstr ""
msgid "Fingerprint" msgid "Fingerprint"
msgstr "" msgstr ""
msgid "Fingerprint:"
msgstr ""
msgid "Fingerprints" msgid "Fingerprints"
msgstr "" msgstr ""
......
# 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
...@@ -16,6 +16,7 @@ describe 'Database config initializer' do ...@@ -16,6 +16,7 @@ describe 'Database config initializer' do
let(:puma_options) { { max_threads: 8 } } let(:puma_options) { { max_threads: 8 } }
before do before do
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
stub_const("Puma", puma) stub_const("Puma", puma)
allow(puma).to receive_message_chain(:cli_config, :options).and_return(puma_options) allow(puma).to receive_message_chain(:cli_config, :options).and_return(puma_options)
end end
......
...@@ -26,7 +26,7 @@ describe Gitlab::GitalyClient do ...@@ -26,7 +26,7 @@ describe Gitlab::GitalyClient do
context 'running in Unicorn' do context 'running in Unicorn' do
before do before do
stub_const('Unicorn', 1) allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
end end
it { expect(subject.long_timeout).to eq(55) } it { expect(subject.long_timeout).to eq(55) }
...@@ -34,7 +34,7 @@ describe Gitlab::GitalyClient do ...@@ -34,7 +34,7 @@ describe Gitlab::GitalyClient do
context 'running in Puma' do context 'running in Puma' do
before do before do
stub_const('Puma', 1) allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
end end
it { expect(subject.long_timeout).to eq(55) } it { expect(subject.long_timeout).to eq(55) }
......
...@@ -236,7 +236,7 @@ describe Gitlab::Gpg do ...@@ -236,7 +236,7 @@ describe Gitlab::Gpg do
context 'when running in Sidekiq' do context 'when running in Sidekiq' do
before do before do
allow(Sidekiq).to receive(:server?).and_return(true) allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
end end
it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::BG_CLEANUP_RUNTIME_S it_behaves_like 'multiple deletion attempts of the tmp-dir', described_class::BG_CLEANUP_RUNTIME_S
......
...@@ -22,6 +22,7 @@ describe Gitlab::HealthChecks::PumaCheck do ...@@ -22,6 +22,7 @@ describe Gitlab::HealthChecks::PumaCheck do
context 'when Puma is not loaded' do context 'when Puma is not loaded' do
before do before do
allow(Gitlab::Runtime).to receive(:puma?).and_return(false)
hide_const('Puma') hide_const('Puma')
end end
...@@ -33,6 +34,7 @@ describe Gitlab::HealthChecks::PumaCheck do ...@@ -33,6 +34,7 @@ describe Gitlab::HealthChecks::PumaCheck do
context 'when Puma is loaded' do context 'when Puma is loaded' do
before do before do
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
stub_const('Puma', Module.new) stub_const('Puma', Module.new)
end end
......
...@@ -26,6 +26,7 @@ describe Gitlab::HealthChecks::UnicornCheck do ...@@ -26,6 +26,7 @@ describe Gitlab::HealthChecks::UnicornCheck do
context 'when Unicorn is not loaded' do context 'when Unicorn is not loaded' do
before do before do
allow(Gitlab::Runtime).to receive(:unicorn?).and_return(false)
hide_const('Unicorn') hide_const('Unicorn')
end end
...@@ -39,6 +40,7 @@ describe Gitlab::HealthChecks::UnicornCheck do ...@@ -39,6 +40,7 @@ describe Gitlab::HealthChecks::UnicornCheck do
let(:http_server_class) { Struct.new(:worker_processes) } let(:http_server_class) { Struct.new(:worker_processes) }
before do before do
allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
stub_const('Unicorn::HttpServer', http_server_class) stub_const('Unicorn::HttpServer', http_server_class)
end end
......
...@@ -111,7 +111,7 @@ describe Gitlab::Highlight do ...@@ -111,7 +111,7 @@ describe Gitlab::Highlight do
end end
it 'utilizes longer timeout for sidekiq' do 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 expect(Timeout).to receive(:timeout).with(described_class::TIMEOUT_BACKGROUND).and_call_original
subject.highlight("Content") subject.highlight("Content")
......
...@@ -11,10 +11,17 @@ describe Gitlab::InsecureKeyFingerprint do ...@@ -11,10 +11,17 @@ describe Gitlab::InsecureKeyFingerprint do
end end
let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" } 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 describe "#fingerprint" do
it "generates the key's 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 end
end end
...@@ -63,7 +63,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do ...@@ -63,7 +63,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
describe '#add_metric' do describe '#add_metric' do
it 'prefixes the series name for a Rails process' 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) expect(Gitlab::Metrics::Metric).to receive(:new)
.with('rails_cats', { value: 10 }, {}) .with('rails_cats', { value: 10 }, {})
...@@ -73,7 +73,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do ...@@ -73,7 +73,7 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
end end
it 'prefixes the series name for a Sidekiq process' do 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) expect(Gitlab::Metrics::Metric).to receive(:new)
.with('sidekiq_cats', { value: 10 }, {}) .with('sidekiq_cats', { value: 10 }, {})
......
# 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
...@@ -183,6 +183,34 @@ describe Gitlab::SSHPublicKey, lib: true do ...@@ -183,6 +183,34 @@ describe Gitlab::SSHPublicKey, lib: true do
end end
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 describe '#key_text' do
let(:key) { 'this is not a key' } let(:key) { 'this is not a key' }
......
...@@ -6,16 +6,13 @@ describe Prometheus::PidProvider do ...@@ -6,16 +6,13 @@ describe Prometheus::PidProvider do
describe '.worker_id' do describe '.worker_id' do
subject { described_class.worker_id } subject { described_class.worker_id }
let(:sidekiq_module) { Module.new }
before do before do
allow(sidekiq_module).to receive(:server?).and_return(false) allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
stub_const('Sidekiq', sidekiq_module)
end end
context 'when running in Sidekiq server mode' do context 'when running in Sidekiq server mode' do
before do before do
expect(Sidekiq).to receive(:server?).and_return(true) allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
end end
context 'in a clustered setup' do context 'in a clustered setup' do
...@@ -33,8 +30,7 @@ describe Prometheus::PidProvider do ...@@ -33,8 +30,7 @@ describe Prometheus::PidProvider do
context 'when running in Unicorn mode' do context 'when running in Unicorn mode' do
before do before do
stub_const('Unicorn::Worker', Class.new) allow(Gitlab::Runtime).to receive(:unicorn?).and_return(true)
hide_const('Puma')
expect(described_class).to receive(:process_name) expect(described_class).to receive(:process_name)
.at_least(:once) .at_least(:once)
...@@ -94,8 +90,7 @@ describe Prometheus::PidProvider do ...@@ -94,8 +90,7 @@ describe Prometheus::PidProvider do
context 'when running in Puma mode' do context 'when running in Puma mode' do
before do before do
stub_const('Puma', Module.new) allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
hide_const('Unicorn::Worker')
expect(described_class).to receive(:process_name) expect(described_class).to receive(:process_name)
.at_least(:once) .at_least(:once)
...@@ -116,11 +111,6 @@ describe Prometheus::PidProvider do ...@@ -116,11 +111,6 @@ describe Prometheus::PidProvider do
end end
context 'when running in unknown mode' do 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}" } it { is_expected.to eq "process_#{Process.pid}" }
end end
end end
......
# 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
...@@ -92,6 +92,7 @@ describe Key, :mailer do ...@@ -92,6 +92,7 @@ describe Key, :mailer do
with_them do with_them do
let!(:key) { create(factory) } let!(:key) { create(factory) }
let!(:original_fingerprint) { key.fingerprint } let!(:original_fingerprint) { key.fingerprint }
let!(:original_fingerprint_sha256) { key.fingerprint_sha256 }
it 'accepts a key with blank space characters after stripping them' do it 'accepts a key with blank space characters after stripping them' do
modified_key = key.key.insert(100, chars.first).insert(40, chars.last) modified_key = key.key.insert(100, chars.first).insert(40, chars.last)
...@@ -104,6 +105,8 @@ describe Key, :mailer do ...@@ -104,6 +105,8 @@ describe Key, :mailer do
expect(content).not_to match(/\s/) expect(content).not_to match(/\s/)
expect(original_fingerprint).to eq(key.fingerprint) 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 end
end end
......
...@@ -25,7 +25,6 @@ describe API::Keys do ...@@ -25,7 +25,6 @@ describe API::Keys do
it 'returns single ssh key with user information' do it 'returns single ssh key with user information' do
user.keys << key user.keys << key
user.save
get api("/keys/#{key.id}", admin) get api("/keys/#{key.id}", admin)
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['title']).to eq(key.title) expect(json_response['title']).to eq(key.title)
...@@ -40,4 +39,73 @@ describe API::Keys do ...@@ -40,4 +39,73 @@ describe API::Keys do
end end
end 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 end
...@@ -108,7 +108,7 @@ describe Git::BranchPushService, services: true do ...@@ -108,7 +108,7 @@ describe Git::BranchPushService, services: true do
end end
it 'reports an error' do 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(Sidekiq.logger).to receive(:warn)
expect { subject }.not_to change { Ci::Pipeline.count } expect { subject }.not_to change { Ci::Pipeline.count }
......
...@@ -118,7 +118,7 @@ RSpec.shared_examples "redis_shared_examples" do ...@@ -118,7 +118,7 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when running not on sidekiq workers' do context 'when running not on sidekiq workers' do
before do before do
allow(Sidekiq).to receive(:server?).and_return(false) allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(false)
end end
it 'instantiates a connection pool with size 5' do it 'instantiates a connection pool with size 5' do
...@@ -130,7 +130,7 @@ RSpec.shared_examples "redis_shared_examples" do ...@@ -130,7 +130,7 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when running on sidekiq workers' do context 'when running on sidekiq workers' do
before 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 }) allow(Sidekiq).to receive(:options).and_return({ concurrency: 18 })
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment