Backport authorized_keys branch 'find-key-by-fingerprint'
Add find key by base64 key or fingerprint to the internal API See merge request !250 Squashed changes: Add unique index to fingerprint Add new index to schema Add internal api to get ssh key by fingerprint Change API endpoint to authorized_keys Add InsecureKeyFingerprint that calculates the fingerprint without shelling out Add require for gitlab key fingerprint Remove uniqueness of fingerprint index Remove unique option from migration Fix spec style in fingerprint test Fix rubocop complain Extract insecure key fingerprint to separate file Change migration to support building index concurrently Remove those hideous tabs
This commit is contained in:
parent
ae46ceaab0
commit
ac86b2043d
5 changed files with 117 additions and 0 deletions
16
db/migrate/20160301174731_add_fingerprint_index.rb
Normal file
16
db/migrate/20160301174731_add_fingerprint_index.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# rubocop:disable all
|
||||
class AddFingerprintIndex < ActiveRecord::Migration
|
||||
disable_ddl_transaction!
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
args = [:keys, :fingerprint]
|
||||
|
||||
if Gitlab::Database.postgresql?
|
||||
args << { algorithm: :concurrently }
|
||||
end
|
||||
|
||||
add_index(*args)
|
||||
end
|
||||
end
|
|
@ -81,6 +81,18 @@ module API
|
|||
merge_request_urls
|
||||
end
|
||||
|
||||
#
|
||||
# Get a ssh key using the fingerprint
|
||||
#
|
||||
get "/authorized_keys" do
|
||||
fingerprint = params.fetch(:fingerprint) do
|
||||
Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint
|
||||
end
|
||||
key = Key.find_by(fingerprint: fingerprint)
|
||||
not_found!("Key") if key.nil?
|
||||
present key, with: Entities::SSHKey
|
||||
end
|
||||
|
||||
#
|
||||
# Discover user by ssh key or user id
|
||||
#
|
||||
|
|
23
lib/gitlab/insecure_key_fingerprint.rb
Normal file
23
lib/gitlab/insecure_key_fingerprint.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
module Gitlab
|
||||
#
|
||||
# Calculates the fingerprint of a given key without using
|
||||
# openssh key validations. For this reason, only use
|
||||
# for calculating the fingerprint to find the key with it.
|
||||
#
|
||||
# DO NOT use it for checking the validity of a ssh key.
|
||||
#
|
||||
class InsecureKeyFingerprint
|
||||
attr_accessor :key
|
||||
|
||||
#
|
||||
# Gets the base64 encoded string representing a rsa or dsa key
|
||||
#
|
||||
def initialize(key_base64)
|
||||
@key = key_base64
|
||||
end
|
||||
|
||||
def fingerprint
|
||||
OpenSSL::Digest::MD5.hexdigest(Base64.decode64(@key)).scan(/../).join(':')
|
||||
end
|
||||
end
|
||||
end
|
18
spec/lib/gitlab/insecure_key_fingerprint_spec.rb
Normal file
18
spec/lib/gitlab/insecure_key_fingerprint_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::InsecureKeyFingerprint do
|
||||
let(:key) do
|
||||
'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn' \
|
||||
'1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qk' \
|
||||
'r8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMg' \
|
||||
'Jw0='
|
||||
end
|
||||
|
||||
let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" }
|
||||
|
||||
describe "#fingerprint" do
|
||||
it "generates the key's fingerprint" do
|
||||
expect(described_class.new(key.split[1]).fingerprint).to eq(fingerprint)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -192,6 +192,54 @@ describe API::Internal do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /internal/authorized_keys" do
|
||||
context "unsing an existing key's fingerprint" do
|
||||
it "finds the key" do
|
||||
get(api('/internal/authorized_keys'), fingerprint: key.fingerprint, secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_response["key"]).to eq(key.key)
|
||||
end
|
||||
end
|
||||
|
||||
context "non existing key's fingerprint" do
|
||||
it "returns 404" do
|
||||
get(api('/internal/authorized_keys'), fingerprint: "no:t-:va:li:d0", secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
context "using a partial fingerprint" do
|
||||
it "returns 404" do
|
||||
get(api('/internal/authorized_keys'), fingerprint: "#{key.fingerprint[0..5]}%", secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
context "sending the key" do
|
||||
it "finds the key" do
|
||||
get(api('/internal/authorized_keys'), key: key.key.split[1], secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_response["key"]).to eq(key.key)
|
||||
end
|
||||
|
||||
it "returns 404 with a partial key" do
|
||||
get(api('/internal/authorized_keys'), key: key.key.split[1][0...-3], secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
|
||||
it "returns 404 with an not valid base64 string" do
|
||||
get(api('/internal/authorized_keys'), key: "whatever!", secret_token: secret_token)
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /internal/allowed", :clean_gitlab_redis_shared_state do
|
||||
context "access granted" do
|
||||
around do |example|
|
||||
|
|
Loading…
Reference in a new issue