startapp 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +11 -0
- data/README.md +95 -0
- data/Rakefile +6 -0
- data/autocomplete/rhc_bash +1672 -0
- data/bin/app +37 -0
- data/conf/express.conf +8 -0
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +191 -0
- data/features/deployments_feature.rb +129 -0
- data/features/domains_feature.rb +58 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +166 -0
- data/lib/rhc/auth/basic.rb +64 -0
- data/lib/rhc/auth/token.rb +102 -0
- data/lib/rhc/auth/token_store.rb +53 -0
- data/lib/rhc/auth.rb +5 -0
- data/lib/rhc/autocomplete.rb +66 -0
- data/lib/rhc/autocomplete_templates/bash.erb +39 -0
- data/lib/rhc/cartridge_helpers.rb +118 -0
- data/lib/rhc/cli.rb +40 -0
- data/lib/rhc/command_runner.rb +185 -0
- data/lib/rhc/commands/account.rb +25 -0
- data/lib/rhc/commands/alias.rb +124 -0
- data/lib/rhc/commands/app.rb +726 -0
- data/lib/rhc/commands/apps.rb +20 -0
- data/lib/rhc/commands/authorization.rb +115 -0
- data/lib/rhc/commands/base.rb +174 -0
- data/lib/rhc/commands/cartridge.rb +329 -0
- data/lib/rhc/commands/clone.rb +66 -0
- data/lib/rhc/commands/configure.rb +20 -0
- data/lib/rhc/commands/create.rb +100 -0
- data/lib/rhc/commands/delete.rb +19 -0
- data/lib/rhc/commands/deploy.rb +32 -0
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/domain.rb +172 -0
- data/lib/rhc/commands/env.rb +142 -0
- data/lib/rhc/commands/force_stop.rb +17 -0
- data/lib/rhc/commands/git_clone.rb +34 -0
- data/lib/rhc/commands/logout.rb +51 -0
- data/lib/rhc/commands/logs.rb +21 -0
- data/lib/rhc/commands/member.rb +148 -0
- data/lib/rhc/commands/port_forward.rb +197 -0
- data/lib/rhc/commands/reload.rb +17 -0
- data/lib/rhc/commands/restart.rb +17 -0
- data/lib/rhc/commands/scp.rb +54 -0
- data/lib/rhc/commands/server.rb +40 -0
- data/lib/rhc/commands/setup.rb +60 -0
- data/lib/rhc/commands/show.rb +43 -0
- data/lib/rhc/commands/snapshot.rb +137 -0
- data/lib/rhc/commands/ssh.rb +51 -0
- data/lib/rhc/commands/sshkey.rb +97 -0
- data/lib/rhc/commands/start.rb +17 -0
- data/lib/rhc/commands/stop.rb +17 -0
- data/lib/rhc/commands/tail.rb +47 -0
- data/lib/rhc/commands/threaddump.rb +14 -0
- data/lib/rhc/commands/tidy.rb +17 -0
- data/lib/rhc/commands.rb +396 -0
- data/lib/rhc/config.rb +321 -0
- data/lib/rhc/context_helper.rb +121 -0
- data/lib/rhc/core_ext.rb +202 -0
- data/lib/rhc/coverage_helper.rb +33 -0
- data/lib/rhc/deployment_helpers.rb +111 -0
- data/lib/rhc/exceptions.rb +256 -0
- data/lib/rhc/git_helpers.rb +106 -0
- data/lib/rhc/help_formatter.rb +55 -0
- data/lib/rhc/helpers.rb +481 -0
- data/lib/rhc/highline_extensions.rb +479 -0
- data/lib/rhc/json.rb +51 -0
- data/lib/rhc/output_helpers.rb +260 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/alias.rb +42 -0
- data/lib/rhc/rest/api.rb +87 -0
- data/lib/rhc/rest/application.rb +348 -0
- data/lib/rhc/rest/attributes.rb +36 -0
- data/lib/rhc/rest/authorization.rb +8 -0
- data/lib/rhc/rest/base.rb +79 -0
- data/lib/rhc/rest/cartridge.rb +162 -0
- data/lib/rhc/rest/client.rb +650 -0
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/domain.rb +98 -0
- data/lib/rhc/rest/environment_variable.rb +15 -0
- data/lib/rhc/rest/gear_group.rb +16 -0
- data/lib/rhc/rest/httpclient.rb +145 -0
- data/lib/rhc/rest/key.rb +44 -0
- data/lib/rhc/rest/membership.rb +105 -0
- data/lib/rhc/rest/mock.rb +1042 -0
- data/lib/rhc/rest/user.rb +32 -0
- data/lib/rhc/rest.rb +148 -0
- data/lib/rhc/scp_helpers.rb +27 -0
- data/lib/rhc/ssh_helpers.rb +380 -0
- data/lib/rhc/tar_gz.rb +51 -0
- data/lib/rhc/usage_templates/command_help.erb +51 -0
- data/lib/rhc/usage_templates/command_syntax_help.erb +11 -0
- data/lib/rhc/usage_templates/help.erb +61 -0
- data/lib/rhc/usage_templates/missing_help.erb +1 -0
- data/lib/rhc/usage_templates/options_help.erb +12 -0
- data/lib/rhc/vendor/okjson.rb +600 -0
- data/lib/rhc/vendor/parseconfig.rb +178 -0
- data/lib/rhc/vendor/sshkey.rb +253 -0
- data/lib/rhc/vendor/zliby.rb +628 -0
- data/lib/rhc/version.rb +5 -0
- data/lib/rhc/wizard.rb +637 -0
- data/lib/rhc.rb +34 -0
- data/spec/coverage_helper.rb +82 -0
- data/spec/direct_execution_helper.rb +339 -0
- data/spec/keys/example.pem +23 -0
- data/spec/keys/example_private.pem +27 -0
- data/spec/keys/server.pem +19 -0
- data/spec/rest_spec_helper.rb +31 -0
- data/spec/rhc/assets/cert.crt +22 -0
- data/spec/rhc/assets/cert_key_rsa +27 -0
- data/spec/rhc/assets/empty.txt +0 -0
- data/spec/rhc/assets/env_vars.txt +7 -0
- data/spec/rhc/assets/env_vars_2.txt +1 -0
- data/spec/rhc/assets/foo.txt +1 -0
- data/spec/rhc/assets/targz_corrupted.tar.gz +1 -0
- data/spec/rhc/assets/targz_sample.tar.gz +0 -0
- data/spec/rhc/auth_spec.rb +442 -0
- data/spec/rhc/cli_spec.rb +186 -0
- data/spec/rhc/command_spec.rb +435 -0
- data/spec/rhc/commands/account_spec.rb +42 -0
- data/spec/rhc/commands/alias_spec.rb +333 -0
- data/spec/rhc/commands/app_spec.rb +777 -0
- data/spec/rhc/commands/apps_spec.rb +39 -0
- data/spec/rhc/commands/authorization_spec.rb +157 -0
- data/spec/rhc/commands/cartridge_spec.rb +665 -0
- data/spec/rhc/commands/clone_spec.rb +41 -0
- data/spec/rhc/commands/deployment_spec.rb +327 -0
- data/spec/rhc/commands/domain_spec.rb +401 -0
- data/spec/rhc/commands/env_spec.rb +493 -0
- data/spec/rhc/commands/git_clone_spec.rb +102 -0
- data/spec/rhc/commands/logout_spec.rb +86 -0
- data/spec/rhc/commands/member_spec.rb +247 -0
- data/spec/rhc/commands/port_forward_spec.rb +217 -0
- data/spec/rhc/commands/scp_spec.rb +77 -0
- data/spec/rhc/commands/server_spec.rb +69 -0
- data/spec/rhc/commands/setup_spec.rb +118 -0
- data/spec/rhc/commands/snapshot_spec.rb +179 -0
- data/spec/rhc/commands/ssh_spec.rb +163 -0
- data/spec/rhc/commands/sshkey_spec.rb +188 -0
- data/spec/rhc/commands/tail_spec.rb +81 -0
- data/spec/rhc/commands/threaddump_spec.rb +84 -0
- data/spec/rhc/config_spec.rb +407 -0
- data/spec/rhc/helpers_spec.rb +531 -0
- data/spec/rhc/highline_extensions_spec.rb +314 -0
- data/spec/rhc/json_spec.rb +30 -0
- data/spec/rhc/rest_application_spec.rb +258 -0
- data/spec/rhc/rest_client_spec.rb +752 -0
- data/spec/rhc/rest_spec.rb +740 -0
- data/spec/rhc/targz_spec.rb +55 -0
- data/spec/rhc/wizard_spec.rb +756 -0
- data/spec/spec_helper.rb +575 -0
- data/spec/wizard_spec_helper.rb +330 -0
- metadata +469 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# modified version of parseconfig rubygem module
|
|
2
|
+
#
|
|
3
|
+
# Author:: BJ Dierkes <derks@bjdierkes.com>
|
|
4
|
+
# Copyright:: Copyright (c) 2006,2012 BJ Dierkes
|
|
5
|
+
# License:: MIT
|
|
6
|
+
# URL:: https://github.com/derks/ruby-parseconfig
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
# This class was written to simplify the parsing of configuration
|
|
10
|
+
# files in the format of "param = value". Please review the
|
|
11
|
+
# demo files included with this package.
|
|
12
|
+
#
|
|
13
|
+
# For further information please refer to the './doc' directory
|
|
14
|
+
# as well as the ChangeLog and README files included.
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
# Note: A group is a set of parameters defined for a subpart of a
|
|
18
|
+
# config file
|
|
19
|
+
|
|
20
|
+
module RHC
|
|
21
|
+
module Vendor
|
|
22
|
+
class ParseConfig
|
|
23
|
+
|
|
24
|
+
Version = '1.0.2'
|
|
25
|
+
|
|
26
|
+
attr_accessor :config_file, :params, :groups
|
|
27
|
+
|
|
28
|
+
# Initialize the class with the path to the 'config_file'
|
|
29
|
+
# The class objects are dynamically generated by the
|
|
30
|
+
# name of the 'param' in the config file. Therefore, if
|
|
31
|
+
# the config file is 'param = value' then the itializer
|
|
32
|
+
# will eval "@param = value"
|
|
33
|
+
#
|
|
34
|
+
def initialize(config_file=nil)
|
|
35
|
+
@config_file = config_file
|
|
36
|
+
@params = {}
|
|
37
|
+
@groups = []
|
|
38
|
+
|
|
39
|
+
if(self.config_file)
|
|
40
|
+
self.validate_config()
|
|
41
|
+
self.import_config()
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Validate the config file, and contents
|
|
46
|
+
def validate_config()
|
|
47
|
+
if !File.readable?(self.config_file)
|
|
48
|
+
raise Errno::EACCES, "#{self.config_file} is not readable"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# FIX ME: need to validate contents/structure?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Import data from the config to our config object.
|
|
55
|
+
def import_config()
|
|
56
|
+
# The config is top down.. anything after a [group] gets added as part
|
|
57
|
+
# of that group until a new [group] is found.
|
|
58
|
+
group = nil
|
|
59
|
+
File.open(self.config_file) { |f| f.each do |line|
|
|
60
|
+
line.strip!
|
|
61
|
+
unless (/^\#/.match(line))
|
|
62
|
+
if(/\s*=\s*/.match(line))
|
|
63
|
+
param, value = line.split(/\s*=\s*/, 2)
|
|
64
|
+
var_name = "#{param}".chomp.strip
|
|
65
|
+
value = value.chomp.strip
|
|
66
|
+
new_value = ''
|
|
67
|
+
if (value)
|
|
68
|
+
if value =~ /^['"](.*)['"]$/
|
|
69
|
+
new_value = $1
|
|
70
|
+
else
|
|
71
|
+
new_value = value
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
new_value = ''
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if group
|
|
78
|
+
self.add_to_group(group, var_name, new_value)
|
|
79
|
+
else
|
|
80
|
+
self.add(var_name, new_value)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
elsif(/^\[(.+)\]$/.match(line).to_a != [])
|
|
84
|
+
group = /^\[(.+)\]$/.match(line).to_a[1]
|
|
85
|
+
self.add(group, {})
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# This method will provide the value held by the object "@param"
|
|
93
|
+
# where "@param" is actually the name of the param in the config
|
|
94
|
+
# file.
|
|
95
|
+
#
|
|
96
|
+
# DEPRECATED - will be removed in future versions
|
|
97
|
+
#
|
|
98
|
+
def get_value(param)
|
|
99
|
+
puts "ParseConfig Deprecation Warning: get_value() is deprecated. Use " + \
|
|
100
|
+
"config['param'] or config['group']['param'] instead."
|
|
101
|
+
return self.params[param]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# This method is a shortcut to accessing the @params variable
|
|
105
|
+
def [](param)
|
|
106
|
+
return self.params[param]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# This method returns all parameters/groups defined in a config file.
|
|
110
|
+
def get_params()
|
|
111
|
+
return self.params.keys
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# List available sub-groups of the config.
|
|
115
|
+
def get_groups()
|
|
116
|
+
return self.groups
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# This method adds an element to the config object (not the config file)
|
|
120
|
+
# By adding a Hash, you create a new group
|
|
121
|
+
def add(param_name, value)
|
|
122
|
+
if value.class == Hash
|
|
123
|
+
if self.params.has_key?(param_name)
|
|
124
|
+
if self.params[param_name].class == Hash
|
|
125
|
+
self.params[param_name].merge!(value)
|
|
126
|
+
elsif self.params.has_key?(param_name)
|
|
127
|
+
if self.params[param_name].class != value.class
|
|
128
|
+
raise ArgumentError, "#{param_name} already exists, and is of different type!"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
else
|
|
132
|
+
self.params[param_name] = value
|
|
133
|
+
end
|
|
134
|
+
if ! self.groups.include?(param_name)
|
|
135
|
+
self.groups.push(param_name)
|
|
136
|
+
end
|
|
137
|
+
else
|
|
138
|
+
self.params[param_name] = value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Add parameters to a group. Note that parameters with the same name
|
|
143
|
+
# could be placed in different groups
|
|
144
|
+
def add_to_group(group, param_name, value)
|
|
145
|
+
if ! self.groups.include?(group)
|
|
146
|
+
self.add(group, {})
|
|
147
|
+
end
|
|
148
|
+
self.params[group][param_name] = value
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Writes out the config file to output_stream
|
|
152
|
+
def write(output_stream=STDOUT)
|
|
153
|
+
self.params.each do |name,value|
|
|
154
|
+
if value.class.to_s != 'Hash'
|
|
155
|
+
if value.scan(/\w+/).length > 1
|
|
156
|
+
output_stream.puts "#{name} = \"#{value}\""
|
|
157
|
+
else
|
|
158
|
+
output_stream.puts "#{name} = #{value}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
output_stream.puts "\n"
|
|
163
|
+
|
|
164
|
+
self.groups.each do |group|
|
|
165
|
+
output_stream.puts "[#{group}]"
|
|
166
|
+
self.params[group].each do |param, value|
|
|
167
|
+
if value.scan(/\w+/).length > 1
|
|
168
|
+
output_stream.puts "#{param} = \"#{value}\""
|
|
169
|
+
else
|
|
170
|
+
output_stream.puts "#{param} = #{value}"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
output_stream.puts "\n"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# modified version of SSHKey rubygem module
|
|
2
|
+
# https://github.com/bensie/sshkey
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2011 James Miller
|
|
5
|
+
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
# a copy of this software and associated documentation files (the
|
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
# the following conditions:
|
|
13
|
+
|
|
14
|
+
# The above copyright notice and this permission notice shall be
|
|
15
|
+
# included in all copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
+
|
|
25
|
+
require 'openssl'
|
|
26
|
+
require 'base64'
|
|
27
|
+
require 'digest/md5'
|
|
28
|
+
require 'digest/sha1'
|
|
29
|
+
|
|
30
|
+
module RHC
|
|
31
|
+
module Vendor
|
|
32
|
+
class SSHKey
|
|
33
|
+
SSH_TYPES = {"rsa" => "ssh-rsa", "dsa" => "ssh-dss"}
|
|
34
|
+
SSH_CONVERSION = {"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}
|
|
35
|
+
|
|
36
|
+
attr_reader :key_object, :comment, :type
|
|
37
|
+
attr_accessor :passphrase
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
# Generate a new keypair and return an SSHKey object
|
|
41
|
+
#
|
|
42
|
+
# The default behavior when providing no options will generate a 2048-bit RSA
|
|
43
|
+
# keypair.
|
|
44
|
+
#
|
|
45
|
+
# ==== Parameters
|
|
46
|
+
# * options<~Hash>:
|
|
47
|
+
# * :type<~String> - "rsa" or "dsa", "rsa" by default
|
|
48
|
+
# * :bits<~Integer> - Bit length
|
|
49
|
+
# * :comment<~String> - Comment to use for the public key, defaults to ""
|
|
50
|
+
# * :passphrase<~String> - Encrypt the key with this passphrase
|
|
51
|
+
#
|
|
52
|
+
def generate(options = {})
|
|
53
|
+
type = options[:type] || "rsa"
|
|
54
|
+
|
|
55
|
+
# JRuby modulus size must range from 512 to 1024
|
|
56
|
+
default_bits = type == "rsa" ? 2048 : 1024
|
|
57
|
+
|
|
58
|
+
bits = options[:bits] || default_bits
|
|
59
|
+
cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC") if options[:passphrase]
|
|
60
|
+
|
|
61
|
+
case type.downcase
|
|
62
|
+
when "rsa" then new(OpenSSL::PKey::RSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
|
|
63
|
+
when "dsa" then new(OpenSSL::PKey::DSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
|
|
64
|
+
else
|
|
65
|
+
raise "Unknown key type: #{type}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Validate an existing SSH public key
|
|
70
|
+
#
|
|
71
|
+
# Returns true or false depending on the validity of the public key provided
|
|
72
|
+
#
|
|
73
|
+
# ==== Parameters
|
|
74
|
+
# * ssh_public_key<~String> - "ssh-rsa AAAAB3NzaC1yc2EA...."
|
|
75
|
+
#
|
|
76
|
+
def valid_ssh_public_key?(ssh_public_key)
|
|
77
|
+
ssh_type, encoded_key = ssh_public_key.split(" ")
|
|
78
|
+
type = SSH_TYPES.invert[ssh_type]
|
|
79
|
+
prefix = [0,0,0,7].pack("C*")
|
|
80
|
+
decoded = Base64.decode64(encoded_key)
|
|
81
|
+
|
|
82
|
+
# Base64 decoding is too permissive, so we should validate if encoding is correct
|
|
83
|
+
return false unless Base64.encode64(decoded).gsub("\n", "") == encoded_key
|
|
84
|
+
return false unless decoded.sub!(/^#{prefix}#{ssh_type}/, "")
|
|
85
|
+
|
|
86
|
+
unpacked = decoded.unpack("C*")
|
|
87
|
+
data = []
|
|
88
|
+
index = 0
|
|
89
|
+
until unpacked[index].nil?
|
|
90
|
+
datum_size = from_byte_array unpacked[index..index+4-1], 4
|
|
91
|
+
index = index + 4
|
|
92
|
+
datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
|
|
93
|
+
data << datum
|
|
94
|
+
index = index + datum_size
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
SSH_CONVERSION[type].size == data.size
|
|
98
|
+
rescue
|
|
99
|
+
false
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Fingerprints
|
|
103
|
+
#
|
|
104
|
+
# Accepts either a public or private key
|
|
105
|
+
#
|
|
106
|
+
# MD5 fingerprint for the given SSH key
|
|
107
|
+
def md5_fingerprint(key)
|
|
108
|
+
if key.match(/PRIVATE/)
|
|
109
|
+
new(key).md5_fingerprint
|
|
110
|
+
else
|
|
111
|
+
Digest::MD5.hexdigest(decoded_key(key)).gsub(fingerprint_regex, '\1:\2')
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
alias_method :fingerprint, :md5_fingerprint
|
|
115
|
+
|
|
116
|
+
# SHA1 fingerprint for the given SSH key
|
|
117
|
+
def sha1_fingerprint(key)
|
|
118
|
+
if key.match(/PRIVATE/)
|
|
119
|
+
new(key).sha1_fingerprint
|
|
120
|
+
else
|
|
121
|
+
Digest::SHA1.hexdigest(decoded_key(key)).gsub(fingerprint_regex, '\1:\2')
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def from_byte_array(byte_array, expected_size = nil)
|
|
128
|
+
num = 0
|
|
129
|
+
raise "Byte array too short" if !expected_size.nil? && expected_size != byte_array.size
|
|
130
|
+
byte_array.reverse.each_with_index do |item, index|
|
|
131
|
+
num += item * 256**(index)
|
|
132
|
+
end
|
|
133
|
+
num
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def decoded_key(key)
|
|
137
|
+
Base64.decode64(key.chomp.gsub(/ssh-[dr]s[as] /, ''))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def fingerprint_regex
|
|
141
|
+
/(.{2})(?=.)/
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Create a new SSHKey object
|
|
146
|
+
#
|
|
147
|
+
# ==== Parameters
|
|
148
|
+
# * private_key - Existing RSA or DSA private key
|
|
149
|
+
# * options<~Hash>
|
|
150
|
+
# * :comment<~String> - Comment to use for the public key, defaults to ""
|
|
151
|
+
# * :passphrase<~String> - If the key is encrypted, supply the passphrase
|
|
152
|
+
#
|
|
153
|
+
def initialize(private_key, options = {})
|
|
154
|
+
@passphrase = options[:passphrase]
|
|
155
|
+
@comment = options[:comment] || ""
|
|
156
|
+
begin
|
|
157
|
+
@key_object = OpenSSL::PKey::RSA.new(private_key, passphrase)
|
|
158
|
+
@type = "rsa"
|
|
159
|
+
rescue
|
|
160
|
+
@key_object = OpenSSL::PKey::DSA.new(private_key, passphrase)
|
|
161
|
+
@type = "dsa"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Fetch the RSA/DSA private key
|
|
166
|
+
#
|
|
167
|
+
# rsa_private_key and dsa_private_key are aliased for backward compatibility
|
|
168
|
+
def private_key
|
|
169
|
+
key_object.to_pem
|
|
170
|
+
end
|
|
171
|
+
alias_method :rsa_private_key, :private_key
|
|
172
|
+
alias_method :dsa_private_key, :private_key
|
|
173
|
+
|
|
174
|
+
# Fetch the encrypted RSA/DSA private key using the passphrase provided
|
|
175
|
+
#
|
|
176
|
+
# If no passphrase is set, returns the unencrypted private key
|
|
177
|
+
def encrypted_private_key
|
|
178
|
+
return private_key unless passphrase
|
|
179
|
+
key_object.to_pem(OpenSSL::Cipher::Cipher.new("AES-128-CBC"), passphrase)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Fetch the RSA/DSA public key
|
|
183
|
+
#
|
|
184
|
+
# rsa_public_key and dsa_public_key are aliased for backward compatibility
|
|
185
|
+
def public_key
|
|
186
|
+
key_object.public_key.to_pem
|
|
187
|
+
end
|
|
188
|
+
alias_method :rsa_public_key, :public_key
|
|
189
|
+
alias_method :dsa_public_key, :public_key
|
|
190
|
+
|
|
191
|
+
# SSH public key
|
|
192
|
+
def ssh_public_key
|
|
193
|
+
[SSH_TYPES[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Fingerprints
|
|
197
|
+
#
|
|
198
|
+
# MD5 fingerprint for the given SSH public key
|
|
199
|
+
def md5_fingerprint
|
|
200
|
+
Digest::MD5.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
|
|
201
|
+
end
|
|
202
|
+
alias_method :fingerprint, :md5_fingerprint
|
|
203
|
+
|
|
204
|
+
# SHA1 fingerprint for the given SSH public key
|
|
205
|
+
def sha1_fingerprint
|
|
206
|
+
Digest::SHA1.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
# SSH Public Key Conversion
|
|
212
|
+
#
|
|
213
|
+
# All data type encoding is defined in the section #5 of RFC #4251.
|
|
214
|
+
# String and mpint (multiple precision integer) types are encoded this way:
|
|
215
|
+
# 4-bytes word: data length (unsigned big-endian 32 bits integer)
|
|
216
|
+
# n bytes: binary representation of the data
|
|
217
|
+
|
|
218
|
+
# For instance, the "ssh-rsa" string is encoded as the following byte array
|
|
219
|
+
# [0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a']
|
|
220
|
+
def ssh_public_key_conversion
|
|
221
|
+
out = [0,0,0,7].pack("C*")
|
|
222
|
+
out += SSH_TYPES[type]
|
|
223
|
+
|
|
224
|
+
SSH_CONVERSION[type].each do |method|
|
|
225
|
+
byte_array = to_byte_array(key_object.public_key.send(method).to_i)
|
|
226
|
+
out += encode_unsigned_int_32(byte_array.length).pack("c*")
|
|
227
|
+
out += byte_array.pack("C*")
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
return out
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def encode_unsigned_int_32(value)
|
|
234
|
+
out = []
|
|
235
|
+
out[0] = value >> 24 & 0xff
|
|
236
|
+
out[1] = value >> 16 & 0xff
|
|
237
|
+
out[2] = value >> 8 & 0xff
|
|
238
|
+
out[3] = value & 0xff
|
|
239
|
+
return out
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def to_byte_array(num)
|
|
243
|
+
result = []
|
|
244
|
+
begin
|
|
245
|
+
result << (num & 0xff)
|
|
246
|
+
num >>= 8
|
|
247
|
+
end until (num == 0 || num == -1) && (result.last[7] == num[7])
|
|
248
|
+
result.reverse
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|