summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--db/migrate/20160301174731_add_fingerprint_index.rb16
-rw-r--r--lib/api/internal.rb12
-rw-r--r--lib/gitlab/insecure_key_fingerprint.rb23
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb18
-rw-r--r--spec/requests/api/internal_spec.rb48
5 files changed, 117 insertions, 0 deletions
diff --git a/db/migrate/20160301174731_add_fingerprint_index.rb b/db/migrate/20160301174731_add_fingerprint_index.rb
new file mode 100644
index 00000000000..b7c4f7d140a
--- /dev/null
+++ b/db/migrate/20160301174731_add_fingerprint_index.rb
@@ -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
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 79b302aae70..8bf53939751 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -82,6 +82,18 @@ module API
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
#
get "/discover" do
diff --git a/lib/gitlab/insecure_key_fingerprint.rb b/lib/gitlab/insecure_key_fingerprint.rb
new file mode 100644
index 00000000000..f85b6e9197f
--- /dev/null
+++ b/lib/gitlab/insecure_key_fingerprint.rb
@@ -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
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
new file mode 100644
index 00000000000..6532579b1c9
--- /dev/null
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -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
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 7b25047ea8f..7b5fddde456 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -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|