muchkeys 0.3.3 → 0.3.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 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