Completed
Push — master ( 164aeb...fde2ba )
by Fike
45s
created

SSHKeygenHandler.raise_invalid_key_exception()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
dl 0
loc 6
rs 9.4285
c 1
b 1
f 0
1
# frozen_string_literal: true
2
3
# rubocop:disable Style/Documentation
4
5
require 'English'
6
7
require_relative 'exception/invalid_key_exception'
8
require_relative 'exception/ssh_keygen_missing_exception'
9
10
module AMA
11
  module Chef
12
    module SSHPrivateKeys
13
      class SSHKeygenHandler
14
        def initialize(binary)
15
          @binary = binary
16
        end
17
18
        def public_key_fingerprint(public_key)
19
          execution = run_with_temporary_file(public_key) do |path|
20
            return [@binary, '-l', '-f', path]
21
          end
22
          if execution.error
23
            message = 'Failed to generate fingerprint for ' \
24
              "public key #{public_key}"
25
            raise_execution_exception(execution, message)
26
          end
27
          execution
28
        end
29
30
        # @param [AMA::Chef::SSHPrivateKeys::Model::KeyPair] pair
31
        # @return [AMA::Chef::SSHPrivateKeys::Model::PublicKey]
32
        def generate_public_key(pair)
33
          execution = run_with_temporary_file(pair.private_key) do |path|
34
            [@binary, '-y', '-f', path, '-P', pair.passphrase.to_s]
35
          end
36
          if execution.error?
37
            message = "Failed to create public key from private key #{pair.id}"
38
            raise_execution_exception(execution, message)
39
          end
40
          raw = execution.stdout.chomp
41
          match_data = /([\w\-]+)\s+([^\s]+)\s*(.*)/.match(raw)
42
          unless match_data
43
            msg = "Failed to read public key created from private key: #{raw}"
44
            raise_invalid_key_exception(msg)
45
          end
46
          AMA::Chef::SSHPrivateKeys::Model::PublicKey.new.tap do |key|
47
            key.type = match_data[1]
48
            key.data = match_data[2]
49
            key.comment = match_data[3]
50
          end
51
        end
52
53
        def self.locate_binary
54
          execution = ::Mixlib::ShellOut.new('which', 'ssh-keygen')
55
          execution.run_command
56
          execution.error? ? nil : execution.stdout.lines.first.chomp
57
        end
58
59
        def self.locate_binary!
60
          binary = locate_binary
61
          return binary if binary
62
          raise(
63
            AMA::Chef::SSHPrivateKeys::Exception::SSHKeygenMissingException,
64
            'Failed to locate ssh-keygen binary for key validation'
65
          )
66
        end
67
68
        private
69
70
        def execute(*command)
71
          ::Chef::Log.debug("Executing command: #{command}")
72
          ::Mixlib::ShellOut.new(*command).tap(&:run_command)
73
        end
74
75
        def run_with_temporary_file(content)
76
          with_temporary_file(content) do |path|
77
            execute(*yield(path))
78
          end
79
        end
80
81
        def with_temporary_file(content)
82
          target = Tempfile.new(['ama-ssh-private-keys-'])
83
          ::IO.write(target.path, content)
84
          begin
85
            return yield(target.path)
86
          ensure
87
            target.close(true)
88
          end
89
        end
90
91
        def raise_invalid_key_exception(*message)
92
          raise(
93
            AMA::Chef::SSHPrivateKeys::Exception::InvalidKeyException,
94
            message.join(" #{$ORS}")
95
          )
96
        end
97
98
        def raise_execution_exception(execution, *message)
99
          message.push("STDOUT: #{execution.stdout}")
100
          message.push("STDERR: #{execution.stderr}")
101
          raise_invalid_key_exception(*message)
102
        end
103
      end
104
    end
105
  end
106
end
107