chamber 1.0.3 → 2.0.0
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 +4 -4
- data/README.md +288 -173
- data/bin/chamber +2 -288
- data/lib/chamber.rb +19 -67
- data/lib/chamber/binary/heroku.rb +59 -0
- data/lib/chamber/binary/runner.rb +94 -0
- data/lib/chamber/binary/travis.rb +23 -0
- data/lib/chamber/commands/base.rb +33 -0
- data/lib/chamber/commands/comparable.rb +37 -0
- data/lib/chamber/commands/compare.rb +46 -0
- data/lib/chamber/commands/context_resolver.rb +72 -0
- data/lib/chamber/commands/files.rb +12 -0
- data/lib/chamber/commands/heroku.rb +30 -0
- data/lib/chamber/commands/heroku/clear.rb +25 -0
- data/lib/chamber/commands/heroku/compare.rb +31 -0
- data/lib/chamber/commands/heroku/pull.rb +30 -0
- data/lib/chamber/commands/heroku/push.rb +25 -0
- data/lib/chamber/commands/initialize.rb +73 -0
- data/lib/chamber/commands/securable.rb +48 -0
- data/lib/chamber/commands/secure.rb +16 -0
- data/lib/chamber/commands/show.rb +23 -0
- data/lib/chamber/commands/travis.rb +14 -0
- data/lib/chamber/commands/travis/secure.rb +35 -0
- data/lib/chamber/configuration.rb +34 -0
- data/lib/chamber/environmentable.rb +23 -0
- data/lib/chamber/errors/undecryptable_value_error.rb +6 -0
- data/lib/chamber/file.rb +17 -5
- data/lib/chamber/file_set.rb +18 -12
- data/lib/chamber/filters/boolean_conversion_filter.rb +41 -0
- data/lib/chamber/filters/decryption_filter.rb +69 -0
- data/lib/chamber/filters/encryption_filter.rb +57 -0
- data/lib/chamber/filters/environment_filter.rb +75 -0
- data/lib/chamber/filters/namespace_filter.rb +37 -0
- data/lib/chamber/filters/secure_filter.rb +39 -0
- data/lib/chamber/instance.rb +40 -0
- data/lib/chamber/namespace_set.rb +55 -16
- data/lib/chamber/rails/railtie.rb +1 -1
- data/lib/chamber/settings.rb +103 -42
- data/lib/chamber/system_environment.rb +3 -93
- data/lib/chamber/version.rb +2 -2
- data/spec/fixtures/settings.yml +27 -0
- data/spec/lib/chamber/commands/context_resolver_spec.rb +106 -0
- data/spec/lib/chamber/commands/files_spec.rb +19 -0
- data/spec/lib/chamber/commands/heroku/clear_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/compare_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/pull_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/push_spec.rb +11 -0
- data/spec/lib/chamber/commands/secure_spec.rb +29 -0
- data/spec/lib/chamber/commands/show_spec.rb +43 -0
- data/spec/lib/chamber/file_set_spec.rb +1 -1
- data/spec/lib/chamber/file_spec.rb +32 -9
- data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +44 -0
- data/spec/lib/chamber/filters/decryption_filter_spec.rb +55 -0
- data/spec/lib/chamber/filters/encryption_filter_spec.rb +48 -0
- data/spec/lib/chamber/filters/environment_filter_spec.rb +35 -0
- data/spec/lib/chamber/filters/namespace_filter_spec.rb +73 -0
- data/spec/lib/chamber/filters/secure_filter_spec.rb +36 -0
- data/spec/lib/chamber/namespace_set_spec.rb +61 -18
- data/spec/lib/chamber/settings_spec.rb +99 -23
- data/spec/lib/chamber/system_environment_spec.rb +1 -71
- data/spec/lib/chamber_spec.rb +40 -26
- data/spec/rails-2-test/config.ru +0 -0
- data/spec/rails-2-test/config/application.rb +5 -0
- data/spec/rails-2-test/script/console +0 -0
- data/spec/rails-3-test/config.ru +0 -0
- data/spec/rails-3-test/config/application.rb +5 -0
- data/spec/rails-3-test/script/rails +0 -0
- data/spec/rails-4-test/bin/rails +0 -0
- data/spec/rails-4-test/config.ru +0 -0
- data/spec/rails-4-test/config/application.rb +5 -0
- data/spec/spec_key +27 -0
- data/spec/spec_key.pub +9 -0
- metadata +85 -4
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
require 'hashie/mash'
|
4
|
+
require 'chamber/errors/undecryptable_value_error'
|
5
|
+
|
6
|
+
module Chamber
|
7
|
+
module Filters
|
8
|
+
class DecryptionFilter
|
9
|
+
SECURE_KEY_TOKEN = %r{\A_secure_}
|
10
|
+
BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+\/]{342}==\z}
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
self.decryption_key = options.fetch(:decryption_key, nil)
|
14
|
+
self.data = options.fetch(:data).dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.execute(options = {})
|
18
|
+
self.new(options).send(:execute)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
attr_accessor :data
|
24
|
+
attr_reader :decryption_key
|
25
|
+
|
26
|
+
def execute(raw_data = data)
|
27
|
+
settings = Hashie::Mash.new
|
28
|
+
|
29
|
+
raw_data.each_pair do |key, value|
|
30
|
+
if value.respond_to? :each_pair
|
31
|
+
value = execute(value)
|
32
|
+
elsif value.respond_to? :match
|
33
|
+
if key.match(SECURE_KEY_TOKEN)
|
34
|
+
key = key.to_s.sub(SECURE_KEY_TOKEN, '')
|
35
|
+
value = if value.match(BASE64_STRING_PATTERN)
|
36
|
+
raise Errors::UndecryptableValueError.new("#{key} appears to need decrypting but the decryption key is not available.") if decryption_key.nil?
|
37
|
+
|
38
|
+
decoded_string = Base64.strict_decode64(value)
|
39
|
+
decryption_key.private_decrypt(decoded_string)
|
40
|
+
else
|
41
|
+
warn "WARNING: It appears that you would like to keep your information for #{key} secure, however the value for that setting does not appear to be encrypted. Make sure you run 'chamber settings secure' before committing."
|
42
|
+
|
43
|
+
value
|
44
|
+
end
|
45
|
+
else
|
46
|
+
key = key.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
settings[key] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
settings
|
54
|
+
end
|
55
|
+
|
56
|
+
def decryption_key=(keyish)
|
57
|
+
return @decryption_key = nil if keyish.nil?
|
58
|
+
|
59
|
+
key_content = if ::File.readable?(::File.expand_path(keyish))
|
60
|
+
::File.read(::File.expand_path(keyish))
|
61
|
+
else
|
62
|
+
keyish
|
63
|
+
end
|
64
|
+
|
65
|
+
@decryption_key = OpenSSL::PKey::RSA.new(key_content)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
require 'hashie/mash'
|
4
|
+
|
5
|
+
module Chamber
|
6
|
+
module Filters
|
7
|
+
class EncryptionFilter
|
8
|
+
SECURE_KEY_TOKEN = %r{\A_secure_}
|
9
|
+
BASE64_STRING_PATTERN = %r{\A[A-Za-z0-9\+\/]{342}==\z}
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
self.encryption_key = options.fetch(:encryption_key, nil)
|
13
|
+
self.data = options.fetch(:data).dup
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.execute(options = {})
|
17
|
+
self.new(options).send(:execute)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
attr_accessor :data
|
23
|
+
attr_reader :encryption_key
|
24
|
+
|
25
|
+
def execute(raw_data = data)
|
26
|
+
settings = Hashie::Mash.new
|
27
|
+
|
28
|
+
raw_data.each_pair do |key, value|
|
29
|
+
if value.respond_to? :each_pair
|
30
|
+
value = execute(value)
|
31
|
+
elsif value.respond_to? :match
|
32
|
+
if key.match(SECURE_KEY_TOKEN) && !value.match(BASE64_STRING_PATTERN)
|
33
|
+
encrypted_string = encryption_key.public_encrypt(value)
|
34
|
+
value = Base64.strict_encode64(encrypted_string)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
settings[key] = value
|
39
|
+
end
|
40
|
+
|
41
|
+
settings
|
42
|
+
end
|
43
|
+
|
44
|
+
def encryption_key=(keyish)
|
45
|
+
return @encryption_key = nil if keyish.nil?
|
46
|
+
|
47
|
+
key_content = if ::File.readable?(::File.expand_path(keyish))
|
48
|
+
::File.read(::File.expand_path(keyish))
|
49
|
+
else
|
50
|
+
keyish
|
51
|
+
end
|
52
|
+
|
53
|
+
@encryption_key = OpenSSL::PKey::RSA.new(key_content)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'chamber/environmentable'
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Filters
|
5
|
+
class EnvironmentFilter
|
6
|
+
include Environmentable
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
self.data = options.fetch(:data)
|
10
|
+
end
|
11
|
+
|
12
|
+
###
|
13
|
+
# Internal: Allows the existing environment to be injected into the passed in
|
14
|
+
# hash. The hash that is passed in is *not* modified, instead a new hash is
|
15
|
+
# returned.
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
#
|
19
|
+
# ###
|
20
|
+
# # Injects the current environment variables
|
21
|
+
# #
|
22
|
+
# ENV['LEVEL_ONE_1_LEVEL_TWO_1'] = 'env value 1'
|
23
|
+
# ENV['LEVEL_ONE_1_LEVEL_TWO_2_LEVEL_THREE_1'] = 'env value 2'
|
24
|
+
#
|
25
|
+
# EnvironmentFilter.execute(
|
26
|
+
# level_one_1: {
|
27
|
+
# level_two_1: 'value 1',
|
28
|
+
# level_two_2: {
|
29
|
+
# level_three_1: 'value 2' } } )
|
30
|
+
#
|
31
|
+
# # => {
|
32
|
+
# 'level_one_1' => {
|
33
|
+
# 'level_two_1' => 'env value 1',
|
34
|
+
# 'level_two_2' => {
|
35
|
+
# 'level_three_1' => 'env value 2',
|
36
|
+
# }
|
37
|
+
#
|
38
|
+
# ###
|
39
|
+
# # Can inject environment variables if said variables are prefixed
|
40
|
+
# #
|
41
|
+
# ENV['PREFIX_LEVEL_TWO_1'] = 'env value 1'
|
42
|
+
# ENV['PREFIX_LEVEL_TWO_2'] = 'env value 2'
|
43
|
+
#
|
44
|
+
# EnvironmentFilter.execute({
|
45
|
+
# level_two_1: 'value 1',
|
46
|
+
# level_two_2: 'value 2'
|
47
|
+
# },
|
48
|
+
# ['prefix'])
|
49
|
+
#
|
50
|
+
# # => {
|
51
|
+
# 'level_two_1' => 'env value 1',
|
52
|
+
# 'level_two_2' => 'env value 2',
|
53
|
+
# }
|
54
|
+
#
|
55
|
+
#
|
56
|
+
def self.execute(options = {})
|
57
|
+
self.new(options).send(:execute)
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
attr_accessor :data
|
63
|
+
|
64
|
+
def execute(settings = data, parent_keys = [])
|
65
|
+
with_environment(settings, parent_keys,
|
66
|
+
->(key, value, environment_keys) do
|
67
|
+
{ key => execute(value, environment_keys) }
|
68
|
+
end,
|
69
|
+
->(key, value, environment_key) do
|
70
|
+
{ key => (ENV[environment_key] || value) }
|
71
|
+
end)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Filters
|
5
|
+
class NamespaceFilter
|
6
|
+
def initialize(options = {})
|
7
|
+
self.data = Hashie::Mash.new(options.fetch(:data))
|
8
|
+
self.namespaces = options.fetch(:namespaces)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.execute(options = {})
|
12
|
+
self.new(options).send(:execute)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
attr_accessor :data,
|
18
|
+
:namespaces
|
19
|
+
|
20
|
+
def execute
|
21
|
+
if data_is_namespaced?
|
22
|
+
namespaces.each_with_object(Hashie::Mash.new) do |namespace, filtered_data|
|
23
|
+
filtered_data.merge!(data[namespace]) if data[namespace]
|
24
|
+
end
|
25
|
+
else
|
26
|
+
Hashie::Mash.new(data)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def data_is_namespaced?
|
33
|
+
@data_is_namespaced ||= data.keys.any? { |key| namespaces.include? key.to_s }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Filters
|
5
|
+
class SecureFilter
|
6
|
+
SECURE_KEY_TOKEN = %r{\A_secure_}
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
self.data = Hashie::Mash.new(options.fetch(:data))
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.execute(options = {})
|
13
|
+
self.new(options).send(:execute)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
attr_accessor :data
|
19
|
+
|
20
|
+
def execute(raw_data = data)
|
21
|
+
settings = Hashie::Mash.new
|
22
|
+
|
23
|
+
raw_data.each_pair do |key, value|
|
24
|
+
secure_value = if value.respond_to? :each_pair
|
25
|
+
execute(value)
|
26
|
+
elsif key.respond_to? :match
|
27
|
+
if key.match(SECURE_KEY_TOKEN)
|
28
|
+
value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
settings[key] = secure_value unless secure_value.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
settings
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'chamber/configuration'
|
2
|
+
require 'chamber/file_set'
|
3
|
+
|
4
|
+
module Chamber
|
5
|
+
class Instance
|
6
|
+
attr_accessor :configuration,
|
7
|
+
:files
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
self.configuration = Configuration.new options
|
11
|
+
self.files = FileSet.new configuration.to_hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def settings
|
15
|
+
@settings ||= files.to_settings { |settings| @settings = settings }
|
16
|
+
end
|
17
|
+
|
18
|
+
def filenames
|
19
|
+
files.filenames
|
20
|
+
end
|
21
|
+
|
22
|
+
def secure
|
23
|
+
files.secure
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s(options = {})
|
27
|
+
settings.to_s(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name, *args)
|
31
|
+
return settings.public_send(name, *args) if settings.respond_to?(name)
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_to_missing?(name, include_private = false)
|
37
|
+
settings.respond_to?(name, include_private)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -8,7 +8,7 @@ require 'set'
|
|
8
8
|
# a NamespaceSet from either an array-like or hash-like object and the ability
|
9
9
|
# to allow callables to be passed which will then be executed.
|
10
10
|
#
|
11
|
-
|
11
|
+
module Chamber
|
12
12
|
class NamespaceSet
|
13
13
|
include Enumerable
|
14
14
|
|
@@ -16,7 +16,21 @@ class NamespaceSet
|
|
16
16
|
# Internal: Creates a new NamespaceSet from arrays, hashes and sets.
|
17
17
|
#
|
18
18
|
def initialize(raw_namespaces = {})
|
19
|
-
self.
|
19
|
+
self.raw_namespaces = raw_namespaces
|
20
|
+
end
|
21
|
+
|
22
|
+
###
|
23
|
+
# Internal: Allows for more compact NamespaceSet creation by giving a list of
|
24
|
+
# namespace values.
|
25
|
+
#
|
26
|
+
# Examples:
|
27
|
+
#
|
28
|
+
# NamespaceSet['development', -> { ENV['HOST'] }]
|
29
|
+
#
|
30
|
+
# Returns a new NamespaceSet
|
31
|
+
#
|
32
|
+
def self.[](*namespace_values)
|
33
|
+
self.new(namespace_values)
|
20
34
|
end
|
21
35
|
|
22
36
|
###
|
@@ -78,7 +92,7 @@ class NamespaceSet
|
|
78
92
|
# Returns a Boolean
|
79
93
|
#
|
80
94
|
def ==(other)
|
81
|
-
self.
|
95
|
+
self.to_a.eql? other.to_a
|
82
96
|
end
|
83
97
|
|
84
98
|
###
|
@@ -88,15 +102,13 @@ class NamespaceSet
|
|
88
102
|
# Returns a Boolean
|
89
103
|
#
|
90
104
|
def eql?(other)
|
91
|
-
other.is_a?(
|
105
|
+
other.is_a?( NamespaceSet) &&
|
92
106
|
self.namespaces == other.namespaces
|
93
107
|
end
|
94
108
|
|
95
109
|
protected
|
96
110
|
|
97
|
-
|
98
|
-
@namespaces ||= Set.new
|
99
|
-
end
|
111
|
+
attr_accessor :raw_namespaces
|
100
112
|
|
101
113
|
###
|
102
114
|
# Internal: Sets the namespaces for the set from a variety of objects and
|
@@ -108,6 +120,7 @@ class NamespaceSet
|
|
108
120
|
#
|
109
121
|
# # Can be set to an array
|
110
122
|
# namespace_set.namespaces = %w{namespace_value_1 namespace_value_2}
|
123
|
+
# namespace_set.namespaces
|
111
124
|
# # => ['namespace_value_1', 'namespace_value_2']
|
112
125
|
#
|
113
126
|
# # Can be set to a hash
|
@@ -116,6 +129,16 @@ class NamespaceSet
|
|
116
129
|
# namespace_set.namespaces
|
117
130
|
# # => ['development', 'my host']
|
118
131
|
#
|
132
|
+
# # Can be set to a NamespaceSet
|
133
|
+
# namespace_set.namespaces = NamespaceSet.new('development')
|
134
|
+
# namespace_set.namespaces
|
135
|
+
# # => ['development']
|
136
|
+
#
|
137
|
+
# # Can be set to a single value
|
138
|
+
# namespace_set.namespaces = 'development'
|
139
|
+
# namespace_set.namespaces
|
140
|
+
# # => ['development']
|
141
|
+
#
|
119
142
|
# # Can be set to a callable
|
120
143
|
# namespace_set.namespaces = { environment: -> { 'called' } }
|
121
144
|
# namespace_set.namespaces
|
@@ -126,16 +149,32 @@ class NamespaceSet
|
|
126
149
|
# namespace_set.namespaces
|
127
150
|
# # => ['namespace_value']
|
128
151
|
#
|
129
|
-
def namespaces
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
152
|
+
def namespaces
|
153
|
+
@namespaces ||= Set.new namespace_values.map do |value|
|
154
|
+
(value.respond_to?(:call) ? value.call : value).to_s
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def raw_namespaces=(raw_namespaces)
|
159
|
+
@raw_namespaces = if raw_namespaces.is_a? NamespaceSet
|
160
|
+
raw_namespaces.to_ary
|
161
|
+
else
|
162
|
+
raw_namespaces
|
163
|
+
end
|
164
|
+
end
|
135
165
|
|
136
|
-
|
137
|
-
|
138
|
-
|
166
|
+
private
|
167
|
+
|
168
|
+
def namespace_values
|
169
|
+
if raw_namespaces.respond_to? :map
|
170
|
+
if raw_namespaces.respond_to? :values
|
171
|
+
raw_namespaces.values
|
172
|
+
else
|
173
|
+
raw_namespaces
|
174
|
+
end
|
175
|
+
else
|
176
|
+
[raw_namespaces]
|
177
|
+
end
|
139
178
|
end
|
140
179
|
end
|
141
180
|
end
|