From c9fac154cd99233c9a6f1cbb88316a476fffc3ad Mon Sep 17 00:00:00 2001
From: Pablo Carranza <pcarranza@gmail.com>
Date: Tue, 8 Mar 2016 17:49:22 +0000
Subject: [PATCH] Add authorized keys bin script to find keys by fingerprint

---
 bin/authorized_keys           | 25 +++++++++++++++++++++++++
 lib/gitlab_authorized_keys.rb |  0
 lib/gitlab_keys.rb            | 26 +++++++++++++++-----------
 lib/gitlab_net.rb             |  6 ++++--
 spec/gitlab_net_spec.rb       |  8 ++++----
 5 files changed, 48 insertions(+), 17 deletions(-)
 create mode 100755 bin/authorized_keys
 create mode 100644 lib/gitlab_authorized_keys.rb

diff --git a/bin/authorized_keys b/bin/authorized_keys
new file mode 100755
index 0000000..6f9880c
--- /dev/null
+++ b/bin/authorized_keys
@@ -0,0 +1,25 @@
+#!/usr/bin/env ruby
+
+#
+# GitLab shell authorized_keys. Query gitlab API to get the authorized command for a given ssh key fingerprint
+#
+# Ex.
+#   /bin/authorized_keys e6:17:f2:f3:b7
+#
+# Returns
+#   command="/bin/gitlab-shell key-#",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...
+#
+
+fingerprint = ARGV[0]
+abort "# No fingerprint provided" if fingerprint.nil?
+
+require_relative "../lib/gitlab_init"
+require_relative "../lib/gitlab_net"
+require_relative "../lib/gitlab_keys"
+
+authorized_key = GitlabNet.new.authorized_key(fingerprint)
+unless authorized_key.nil?
+  puts GitlabKey.new.key_line(authorized_key["id"], authorized_key["key"])
+else
+  puts "# No key was found with fingerprint #{fingerprint}"
+end
diff --git a/lib/gitlab_authorized_keys.rb b/lib/gitlab_authorized_keys.rb
new file mode 100644
index 0000000..e69de29
diff --git a/lib/gitlab_keys.rb b/lib/gitlab_keys.rb
index 3710f96..f17e6b7 100644
--- a/lib/gitlab_keys.rb
+++ b/lib/gitlab_keys.rb
@@ -11,6 +11,7 @@ class GitlabKeys
     @key_id = ARGV.shift
     @key = ARGV.shift
     @auth_file = GitlabConfig.new.auth_file
+    @gitlab_key = GitlabKey.new
   end
 
   def exec
@@ -32,7 +33,7 @@ class GitlabKeys
   def add_key
     lock do
       $logger.info "Adding key #{@key_id} => #{@key.inspect}"
-      auth_line = key_line(@key_id, @key)
+      auth_line = @gitlab_key.key_line(@key_id, @key)
       open(auth_file, 'a') { |file| file.puts(auth_line) }
     end
     true
@@ -59,7 +60,7 @@ class GitlabKeys
           abort("#{$0}: invalid input #{input.inspect}") unless tokens.count == 2
           key_id, public_key = tokens
           $logger.info "Adding key #{key_id} => #{public_key.inspect}"
-          file.puts(key_line(key_id, public_key))
+          file.puts(@gitlab_key.key_line(key_id, public_key))
         end
       end
     end
@@ -70,20 +71,12 @@ class GitlabKeys
     $stdin
   end
 
-  def key_command(key_id)
-    "#{ROOT_PATH}/bin/gitlab-shell #{key_id}"
-  end
-
-  def key_line(key_id, public_key)
-    auth_line = "command=\"#{key_command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}"
-  end
-
   def rm_key
     lock do
       $logger.info "Removing key #{@key_id}"
       open(auth_file, 'r+') do |f|
         while line = f.gets do
-          next unless line.start_with?("command=\"#{key_command(@key_id)}\"")
+          next unless line.start_with?("command=\"#{@gitlab_key.command(@key_id)}\"")
           f.seek(-line.length, IO::SEEK_CUR)
           # Overwrite the line with #'s. Because the 'line' variable contains
           # a terminating '\n', we write line.length - 1 '#' characters.
@@ -115,3 +108,14 @@ class GitlabKeys
     @lock_file ||= auth_file + '.lock'
   end
 end
+
+
+class GitlabKey
+  def command(key_id)
+    "#{ROOT_PATH}/bin/gitlab-shell #{key_id}"
+  end
+
+  def key_line(key_id, public_key)
+    "command=\"#{command(key_id)}\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{public_key}"
+  end
+end
diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb
index ddf2a06..503a267 100644
--- a/lib/gitlab_net.rb
+++ b/lib/gitlab_net.rb
@@ -56,9 +56,11 @@ class GitlabNet
     get("#{host}/check", read_timeout: CHECK_TIMEOUT)
   end
 
-  def ssh_key(fingerprint)
+  def authorized_key(fingerprint)
     resp = get("#{host}/ssh-key?fingerprint=#{fingerprint}")
-    JSON.parse(resp.body) rescue nil
+    JSON.parse(resp.body) if resp.code == "200"
+  rescue
+    nil
   end
 
   protected
diff --git a/spec/gitlab_net_spec.rb b/spec/gitlab_net_spec.rb
index 964e398..786316d 100644
--- a/spec/gitlab_net_spec.rb
+++ b/spec/gitlab_net_spec.rb
@@ -76,24 +76,24 @@ describe GitlabNet, vcr: true do
     end
   end
 
-  describe :ssh_key do
+  describe :authorized_key do
     it "should return nil when the resource is not implemented" do
       VCR.use_cassette("ssh-key-not-implemented") do
-        result = gitlab_net.ssh_key("whatever")
+        result = gitlab_net.authorized_key("whatever")
         result.should be_nil
       end
     end
 
     it "should return nil when the fingerprint is not found" do
       VCR.use_cassette("ssh-key-not-found") do
-        result = gitlab_net.ssh_key("whatever")
+        result = gitlab_net.authorized_key("whatever")
         result.should be_nil
       end
     end
 
     it "should return a ssh key with a valid fingerprint" do
       VCR.use_cassette("ssh-key-ok") do
-        result = gitlab_net.ssh_key("42:18:16")
+        result = gitlab_net.authorized_key("42:18:16")
         result.should eq({
           "created_at" => "2016-03-04T18:27:36.959Z",
           "id" => 2,
-- 
2.30.9