muchkeys 0.3.3 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ff8551deefce6e7a842d98364ea5667fa4532e3
4
- data.tar.gz: ff2729f7f21a56f51bb9998729c80a7c96d87ec5
3
+ metadata.gz: 7b1691f5996a3bbe70a2dc689f6168b08859f5a7
4
+ data.tar.gz: 1d49e1b24e67b72c55adf5ed724e0269e0ac0a0a
5
5
  SHA512:
6
- metadata.gz: 4d0541d5c159955b22471d808fb2edb0f042e5b3a03fe725deb7ea7c041a3ed5d92f1f5a120223531742da56478976ecde08e833ae0399564a3f4145b26c67e5
7
- data.tar.gz: 413f9778cd45d3aed726c7c23c685a032259f870c021c62b2bbd7f72b9f73f1d307ef568ed12263bc873feb3e5dc2524fd5e51d2803012833e6b2fcc947528a2
6
+ metadata.gz: d4ec21f49f82ce55a894ce5fbfaf8b5a567fe56dd0313576e17d7a1cc553dd08a6895877df76176694e90e890e8d6063ba146f1032e482ed437bbca2c54a9a3c
7
+ data.tar.gz: c49e8fe3eab027bfe23c032eba3bae91dd54e9320035221a5fac78471150f4f646cb9bd8628730a20298a799ca64dfa8e0fab4d61f2bd97037f721ab6b00c300
@@ -0,0 +1,17 @@
1
+ module MuchKeys
2
+ class CLI
3
+
4
+ class Validation
5
+ attr_accessor :errors
6
+
7
+ def initialize
8
+ @errors = []
9
+ end
10
+
11
+ def valid?
12
+ errors.length < 1
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative "../errors"
2
+ require_relative "./validation"
2
3
 
3
4
  class MuchKeys::CLI::Validator
4
5
 
@@ -6,24 +7,48 @@ class MuchKeys::CLI::Validator
6
7
  raise MuchKeys::CLIOptionsError, primary_mode_error_message unless options_has_one_mode?(options)
7
8
  end
8
9
 
9
- def self.validate_encrypt(options)
10
- abort "--encrypt needs the --file option passed." if !options[:file]
11
- abort "--encrypt needs the --public_key option passed." if !options[:public_key]
10
+ def self.validate_encrypt_options(options)
11
+ validation = MuchKeys::CLI::Validation.new
12
+ if !options[:file] || !options[:public_key]
13
+ validation.errors << "--decrypt needs the --file and --public_key set."
14
+ end
15
+
16
+ validation
12
17
  end
13
18
 
14
- def self.validate_decrypt(options)
19
+ def self.validate_decrypt_options(options)
20
+ validation = MuchKeys::CLI::Validation.new
21
+ if options[:consul_key] && options[:public_key] && options[:private_key]
22
+ validate_automatic_certificate(validation, options)
23
+ else
24
+ validation.errors << "--decrypt needs the --consul_key, --public_key and --private_key set."
25
+ end
26
+
27
+ validation
28
+ end
29
+
30
+ def self.validate_automatic_certificate(chained_validation, options)
31
+ # i won't mutate chained_validation on principle
32
+ validation = MuchKeys::CLI::Validation.new
33
+ validation.errors = chained_validation.errors.dup
15
34
  key_name = options[:consul_key]
16
- abort "--decrypt needs the --consul_key option passed." if !key_name
17
35
 
18
36
  if !secret_adapter.auto_certificates_exist_for_key?(key_name)
19
37
  certfile_expected = secret_adapter.certfile_name(key_name)
20
- abort "--decrypt needs the --public_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:public_key]
21
- abort "--decrypt needs the --private_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:private_key]
38
+ validation.errors << "--decrypt needs the --public_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:public_key]
39
+ validation.errors << "--decrypt needs the --private_key option passed or a PEM file needs to be at #{certfile_expected}." if !options[:private_key]
22
40
  end
41
+
42
+ validation
23
43
  end
24
44
 
25
- def self.validate_plain(options)
26
- abort "--plain needs the --consul_key option passed." if !options[:consul_key]
45
+ def self.validate_plain_options(options)
46
+ validation = MuchKeys::CLI::Validation.new
47
+ if !options[:consul_key]
48
+ validation.errors << "--plain needs the --consul_key option passed."
49
+ end
50
+
51
+ validation
27
52
  end
28
53
 
29
54
  def self.options_has_one_mode?(options)
data/lib/muchkeys/cli.rb CHANGED
@@ -8,18 +8,8 @@ module MuchKeys
8
8
  # I'd like to not be doing this kind of check
9
9
  # But this is tricky because we need to know
10
10
  # later if help was invoked so we don't execute run with blank options.
11
- @help_invoked = false
11
+ @cli_should_exit = false
12
12
  parsed = parse_options(arguments)
13
- return if @help_invoked
14
-
15
- begin
16
- set_primary_mode
17
- rescue MuchKeys::CLIOptionsError => e
18
- puts e.message
19
- puts @opts
20
- end
21
-
22
- configure_muchkeys
23
13
  end
24
14
 
25
15
  def parse_options(arguments)
@@ -32,14 +22,11 @@ module MuchKeys
32
22
  o.string "--private_key", "Location of your private key"
33
23
  o.string "--public_key", "Location of your public key"
34
24
 
35
- # add -f
36
- o.string "--file", "File to encrypt"
25
+ o.string "-f", "--file", "File to encrypt"
37
26
  o.string "-c", "--consul_key", "Consul key to decrypt"
38
27
 
39
- o.on "-h", "--help" do
40
- # Save the fact we ran help so when run() runs, it prints help and exits.
41
- @help_invoked = true
42
- end
28
+ o.bool "-h", "--help", "Shows usage"
29
+ o.bool "-v", "--version", "Prints the version number"
43
30
  end
44
31
  end
45
32
 
@@ -58,20 +45,53 @@ module MuchKeys
58
45
 
59
46
  # this guy figures out the appropriate action to take given CLI options
60
47
  def run
61
- if @opts[:help]
48
+
49
+ check_for_early_exit_actions
50
+ return if @cli_should_exit
51
+
52
+ begin
53
+ set_primary_mode
54
+ rescue MuchKeys::CLIOptionsError => e
55
+ puts e.message
62
56
  puts @opts
63
- return
64
57
  end
65
58
 
59
+ return if @cli_should_exit
60
+
61
+ configure_muchkeys
62
+
66
63
  if @opts[:encrypt]
67
- MuchKeys::CLI::Validator.validate_encrypt(file: @opts[:file], public_key: @opts[:public_key])
68
- encrypt(@opts[:file], @opts[:public_key])
64
+ options = { file: @opts[:file], public_key: @opts[:public_key] }
65
+ validation = MuchKeys::CLI::Validator.validate_encrypt_options(options)
66
+
67
+ if validation.valid?
68
+ encrypt(@opts[:file], @opts[:public_key])
69
+ else
70
+ puts validation.errors
71
+ return
72
+ end
69
73
  elsif @opts[:decrypt]
70
- MuchKeys::CLI::Validator.validate_decrypt(consul_key: @opts[:consul_key], public_key: @opts[:public_key], private_key: @opts[:private_key])
71
- decrypt(@opts[:consul_key], @opts[:public_key], @opts[:private_key])
74
+ options = { consul_key: @opts[:consul_key], public_key: @opts[:public_key], private_key: @opts[:private_key] }
75
+ validation = MuchKeys::CLI::Validator.validate_decrypt_options(options)
76
+
77
+ if validation.valid?
78
+ decrypt(@opts[:consul_key], @opts[:public_key], @opts[:private_key])
79
+ else
80
+ puts validation.errors
81
+ return
82
+ end
72
83
  elsif @opts[:plain]
73
- plain(@opts[:consul_key])
84
+ options = { consul_key: @opts[:consul_key] }
85
+ validation = MuchKeys::CLI::Validator.validate_plain_options(options)
86
+
87
+ if validation.valid?
88
+ plain(@opts[:consul_key])
89
+ else
90
+ puts validation.errors
91
+ return
92
+ end
74
93
  end
94
+
75
95
  end
76
96
 
77
97
  def encrypt(file, public_key)
@@ -87,6 +107,17 @@ module MuchKeys
87
107
  puts MuchKeys.fetch_key(consul_key)
88
108
  end
89
109
 
110
+ def print_version_number
111
+ puts MuchKeys::VERSION
112
+ @cli_should_exit = true
113
+ end
114
+
115
+ def print_help
116
+ # this is how the slop gem prints help
117
+ puts @opts
118
+ @cli_should_exit = true
119
+ end
120
+
90
121
 
91
122
  private
92
123
  def select_primary_mode(options)
@@ -99,5 +130,15 @@ module MuchKeys
99
130
  MuchKeys::Secret
100
131
  end
101
132
 
133
+ def check_for_early_exit_actions
134
+ if @opts[:help]
135
+ print_help
136
+ end
137
+
138
+ if @opts[:version]
139
+ print_version_number
140
+ end
141
+ end
142
+
102
143
  end
103
144
  end
@@ -1,5 +1,6 @@
1
1
  module MuchKeys
2
- InvalidKey = Class.new(StandardError)
3
- CLIOptionsError = Class.new(StandardError)
4
- NoKeysSet = Class.new(StandardError)
2
+ InvalidKey = Class.new(StandardError)
3
+ CLIOptionsError = Class.new(StandardError)
4
+ NoKeysSet = Class.new(StandardError)
5
+ UnknownApplication = Class.new(StandardError)
5
6
  end
@@ -1,3 +1,3 @@
1
1
  module MuchKeys
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.6"
3
3
  end
data/lib/muchkeys.rb CHANGED
@@ -10,6 +10,10 @@ require "net/http"
10
10
 
11
11
  module MuchKeys
12
12
 
13
+ # revealing intention
14
+ module BlankKey; false; end
15
+
16
+
13
17
  class << self
14
18
  attr_accessor :configuration
15
19
 
@@ -20,37 +24,22 @@ module MuchKeys
20
24
  end
21
25
  end
22
26
 
23
- def search_order(application_name, key_name)
24
- if MuchKeys.configuration.search_paths
25
- search_paths = MuchKeys.configuration.search_paths.collect do |path|
26
- "#{path}/#{key_name}"
27
- end
28
- else
29
- search_paths = [
30
- "git/#{application_name}/secrets/#{key_name}",
31
- "git/#{application_name}/config/#{key_name}",
32
- "git/shared/secrets/#{key_name}",
33
- "git/shared/config/#{key_name}"
34
- ]
35
- end
36
- end
37
-
27
+ # this is the entry point to the ENV-like object that devs will use
38
28
  def find_first(key_name)
39
- return ENV[key_name] unless ENV[key_name].nil?
40
-
41
29
  unless MuchKeys.configuration.search_paths
42
- application_name = detect_app_name
43
- return false if !application_name
30
+ raise MuchKeys::UnknownApplication, "Can't detect app name and application_name isn't set." if !application_name
44
31
  end
45
32
 
46
- consul_paths = search_order(application_name, key_name)
33
+ consul_paths_to_search = search_order(application_name, key_name)
47
34
 
35
+ # search consul in a specific order until we find something
48
36
  response = nil
49
- search_order(application_name, key_name).detect do |consul_path|
37
+ consul_paths_to_search.detect do |consul_path|
50
38
  response = fetch_key(consul_path)
39
+ response if response != MuchKeys::BlankKey
51
40
  end
52
41
 
53
- if !response
42
+ if response == BlankKey
54
43
  raise MuchKeys::NoKeysSet, "Bailing. Consul isn't set with any keys for #{key_name}."
55
44
  end
56
45
 
@@ -72,46 +61,47 @@ module MuchKeys
72
61
  response = fetch_plain_key(key_name)
73
62
  end
74
63
 
75
- # empty and unset keys from consul are empty strings, so return false
76
- # here TODO: UnsetKey would be better here.
77
- return false if response == ""
78
-
79
64
  # otherwise, we got consul data so return that
80
65
  response
81
66
  end
82
67
 
83
- def consul_url(key_name)
84
- URI("#{configuration.consul_url}/v1/kv/#{key_name}?raw")
85
- end
86
-
87
- # TODO: inline this
88
- def fetch_body(response)
89
- response.body
90
- end
91
-
92
- def handle_response(response_code)
93
- case response_code
94
- when (400..599)
95
- raise MuchKeys::InvalidKey
68
+ def search_order(application_name, key_name)
69
+ if MuchKeys.configuration.search_paths
70
+ search_paths = MuchKeys.configuration.search_paths.collect do |path|
71
+ "#{path}/#{key_name}"
72
+ end
73
+ else
74
+ search_paths = [
75
+ "git/#{application_name}/secrets/#{key_name}",
76
+ "git/#{application_name}/config/#{key_name}",
77
+ "git/shared/secrets/#{key_name}",
78
+ "git/shared/config/#{key_name}"
79
+ ]
96
80
  end
81
+
82
+ search_paths
97
83
  end
98
84
 
99
85
 
100
86
  private
87
+ def consul_url(key_name)
88
+ URI("#{configuration.consul_url}/v1/kv/#{key_name}?raw")
89
+ end
90
+
101
91
  def fetch_plain_key(key_name)
102
92
  url = consul_url(key_name)
103
93
  response = Net::HTTP.get_response url
104
- handle_response(response)
105
- fetch_body(response)
94
+
95
+ return MuchKeys::BlankKey if !response.body || response.body.empty?
96
+ response.body
106
97
  end
107
98
 
108
99
  def fetch_secret_key(key_name, public_pem=nil, private_pem=nil)
109
100
  result = fetch_plain_key(key_name)
110
- # FIXME: omg use a class like MuchKeys::UnsetKey instead of "" as a
111
- # return value -- there are other places like this too.
112
101
 
113
102
  # we hit a key that doesn't exist, so don't try to decrypt it
114
- return "" if !result || result.empty?
103
+ return MuchKeys::BlankKey if result == MuchKeys::BlankKey
104
+
115
105
  secret_adapter.decrypt_string(result, public_pem, private_pem)
116
106
  end
117
107
 
@@ -128,18 +118,15 @@ module MuchKeys
128
118
  end
129
119
 
130
120
  # Detecting Rails app names is a known quantity.
131
- # TODO: figure out how to detect plain Rack apps. :(
132
- def detect_app_name
121
+ # Rack apps need to set the name through config.
122
+ def application_name
133
123
  return configuration.application_name if configuration.application_name
134
124
 
135
- # Rails.application is "Monorail::Application".
136
125
  if defined?(Rails)
126
+ # Rails.application looks something like "Monorail::Application"
137
127
  application_name = Rails.application.class.to_s.split("::").first
138
- elsif defined?(Rack)
139
- application_name = "Asset_Server"
140
128
  else
141
- $stderr.puts "can't detect app name"
142
- return
129
+ return false
143
130
  end
144
131
 
145
132
  snakecase(application_name)
@@ -164,8 +151,11 @@ MuchKeys.configuration ||= MuchKeys::Configuration.new
164
151
  # This interface looks like ENV which is more friendly to devs
165
152
  class MUCHKEYS
166
153
 
167
- def self.[](i)
168
- MuchKeys.find_first(i)
154
+ def self.[](key_name)
155
+ # if an ENV key is set, use that first
156
+ return ENV[key_name] unless ENV[key_name].nil?
157
+
158
+ MuchKeys.find_first(key_name)
169
159
  end
170
160
 
171
161
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: muchkeys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat O'Brien
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-04-28 00:00:00.000000000 Z
12
+ date: 2016-05-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: slop
@@ -129,6 +129,7 @@ files:
129
129
  - exe/muchkeys
130
130
  - lib/muchkeys.rb
131
131
  - lib/muchkeys/cli.rb
132
+ - lib/muchkeys/cli/validation.rb
132
133
  - lib/muchkeys/cli/validator.rb
133
134
  - lib/muchkeys/configuration.rb
134
135
  - lib/muchkeys/errors.rb