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:
Pablo Carranza 2016-03-01 17:53:01 +00:00 committed by James Edwards-Jones
parent ae46ceaab0
commit ac86b2043d
5 changed files with 117 additions and 0 deletions

View 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

View file

@ -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
#

View 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

View 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

View file

@ -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|