diff options
-rw-r--r-- | app/models/key.rb | 44 | ||||
-rw-r--r-- | lib/gitlab/key_fingerprint.rb | 55 | ||||
-rw-r--r-- | spec/lib/gitlab/key_fingerprint_spec.rb | 12 |
3 files changed, 71 insertions, 40 deletions
diff --git a/app/models/key.rb b/app/models/key.rb index 480a16f9335..016eee86992 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -16,7 +16,6 @@ require 'digest/md5' class Key < ActiveRecord::Base include Sortable - include Gitlab::Popen belongs_to :user @@ -79,44 +78,9 @@ class Key < ActiveRecord::Base def generate_fingerprint self.fingerprint = nil - return unless key.present? - - cmd_status = 0 - cmd_output = '' - explicit_fingerprint_algorithm = false - Tempfile.open('gitlab_key_file') do |file| - file.puts key - file.rewind - - # OpenSSH 6.8 introduces a new default output format for fingerprints. - # Check the version and decide which command to use. - version_output, version_status = popen(%W(ssh -V)) - if version_status.zero? - out, _ = version_output.scan /.*?(\d)\.(\d).*?,/ - major, minor = out[0], out[1] - if major.to_i > 6 or (major.to_i == 6 and minor.to_i >= 8) - explicit_fingerprint_algorithm = true - end - end - - if explicit_fingerprint_algorithm - cmd_output, cmd_status = popen(%W(ssh-keygen -E md5 -lf #{file.path}), '/tmp') - else - cmd_output, cmd_status = popen(%W(ssh-keygen -lf #{file.path}), '/tmp') - end - end - - if cmd_status.zero? - if explicit_fingerprint_algorithm - cmd_output.gsub /(MD5:)(\h{2}:)+\h{2}/ do |match| - match.slice! /^MD5:/ - self.fingerprint = match - end - else - cmd_output.gsub /(\h{2}:)+\h{2}/ do |match| - self.fingerprint = match - end - end - end + + return unless self.key.present? + + self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint end end diff --git a/lib/gitlab/key_fingerprint.rb b/lib/gitlab/key_fingerprint.rb new file mode 100644 index 00000000000..550b10a4c94 --- /dev/null +++ b/lib/gitlab/key_fingerprint.rb @@ -0,0 +1,55 @@ +module Gitlab + class KeyFingerprint + include Gitlab::Popen + + attr_accessor :key + + def initialize(key) + @key = key + end + + def fingerprint + cmd_status = 0 + cmd_output = '' + + Tempfile.open('gitlab_key_file') do |file| + file.puts key + file.rewind + + cmd = [] + cmd.push *%W(ssh-keygen) + cmd.push *%W(-E md5) if explicit_fingerprint_algorithm? + cmd.push *%W(-lf #{file.path}) + + cmd_output, cmd_status = popen(cmd, '/tmp') + end + + return nil unless cmd_status.zero? + + # 16 hex bytes separated by ':', optionally starting with "MD5:" + fingerprint_match = cmd_output.match(/(MD5:)?(?<fingerprint>(?:\h{2}:){15}\h{2})/) + return nil unless fingerprint_match + + fingerprint_match[:fingerprint] + end + + private + + def explicit_fingerprint_algorithm? + # OpenSSH 6.8 introduces a new default output format for fingerprints. + # Check the version and decide which command to use. + + version_output, version_status = popen(%W(ssh -V)) + return false unless version_status.zero? + + version_matches = version_output.match(/OpenSSH_(?<major>\d+)\.(?<minor>\d+)/) + return false unless version_matches + + version_info = Gitlab::VersionInfo.new(version_matches[:major].to_i, version_matches[:minor].to_i) + + required_version_info = Gitlab::VersionInfo.new(6, 8) + + version_info >= required_version_info + end + end +end diff --git a/spec/lib/gitlab/key_fingerprint_spec.rb b/spec/lib/gitlab/key_fingerprint_spec.rb new file mode 100644 index 00000000000..266eab6e793 --- /dev/null +++ b/spec/lib/gitlab/key_fingerprint_spec.rb @@ -0,0 +1,12 @@ +require "spec_helper" + +describe Gitlab::KeyFingerprint do + let(:key) { "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" } + 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(Gitlab::KeyFingerprint.new(key).fingerprint).to eq(fingerprint) + end + end +end |