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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +288 -173
  3. data/bin/chamber +2 -288
  4. data/lib/chamber.rb +19 -67
  5. data/lib/chamber/binary/heroku.rb +59 -0
  6. data/lib/chamber/binary/runner.rb +94 -0
  7. data/lib/chamber/binary/travis.rb +23 -0
  8. data/lib/chamber/commands/base.rb +33 -0
  9. data/lib/chamber/commands/comparable.rb +37 -0
  10. data/lib/chamber/commands/compare.rb +46 -0
  11. data/lib/chamber/commands/context_resolver.rb +72 -0
  12. data/lib/chamber/commands/files.rb +12 -0
  13. data/lib/chamber/commands/heroku.rb +30 -0
  14. data/lib/chamber/commands/heroku/clear.rb +25 -0
  15. data/lib/chamber/commands/heroku/compare.rb +31 -0
  16. data/lib/chamber/commands/heroku/pull.rb +30 -0
  17. data/lib/chamber/commands/heroku/push.rb +25 -0
  18. data/lib/chamber/commands/initialize.rb +73 -0
  19. data/lib/chamber/commands/securable.rb +48 -0
  20. data/lib/chamber/commands/secure.rb +16 -0
  21. data/lib/chamber/commands/show.rb +23 -0
  22. data/lib/chamber/commands/travis.rb +14 -0
  23. data/lib/chamber/commands/travis/secure.rb +35 -0
  24. data/lib/chamber/configuration.rb +34 -0
  25. data/lib/chamber/environmentable.rb +23 -0
  26. data/lib/chamber/errors/undecryptable_value_error.rb +6 -0
  27. data/lib/chamber/file.rb +17 -5
  28. data/lib/chamber/file_set.rb +18 -12
  29. data/lib/chamber/filters/boolean_conversion_filter.rb +41 -0
  30. data/lib/chamber/filters/decryption_filter.rb +69 -0
  31. data/lib/chamber/filters/encryption_filter.rb +57 -0
  32. data/lib/chamber/filters/environment_filter.rb +75 -0
  33. data/lib/chamber/filters/namespace_filter.rb +37 -0
  34. data/lib/chamber/filters/secure_filter.rb +39 -0
  35. data/lib/chamber/instance.rb +40 -0
  36. data/lib/chamber/namespace_set.rb +55 -16
  37. data/lib/chamber/rails/railtie.rb +1 -1
  38. data/lib/chamber/settings.rb +103 -42
  39. data/lib/chamber/system_environment.rb +3 -93
  40. data/lib/chamber/version.rb +2 -2
  41. data/spec/fixtures/settings.yml +27 -0
  42. data/spec/lib/chamber/commands/context_resolver_spec.rb +106 -0
  43. data/spec/lib/chamber/commands/files_spec.rb +19 -0
  44. data/spec/lib/chamber/commands/heroku/clear_spec.rb +11 -0
  45. data/spec/lib/chamber/commands/heroku/compare_spec.rb +11 -0
  46. data/spec/lib/chamber/commands/heroku/pull_spec.rb +11 -0
  47. data/spec/lib/chamber/commands/heroku/push_spec.rb +11 -0
  48. data/spec/lib/chamber/commands/secure_spec.rb +29 -0
  49. data/spec/lib/chamber/commands/show_spec.rb +43 -0
  50. data/spec/lib/chamber/file_set_spec.rb +1 -1
  51. data/spec/lib/chamber/file_spec.rb +32 -9
  52. data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +44 -0
  53. data/spec/lib/chamber/filters/decryption_filter_spec.rb +55 -0
  54. data/spec/lib/chamber/filters/encryption_filter_spec.rb +48 -0
  55. data/spec/lib/chamber/filters/environment_filter_spec.rb +35 -0
  56. data/spec/lib/chamber/filters/namespace_filter_spec.rb +73 -0
  57. data/spec/lib/chamber/filters/secure_filter_spec.rb +36 -0
  58. data/spec/lib/chamber/namespace_set_spec.rb +61 -18
  59. data/spec/lib/chamber/settings_spec.rb +99 -23
  60. data/spec/lib/chamber/system_environment_spec.rb +1 -71
  61. data/spec/lib/chamber_spec.rb +40 -26
  62. data/spec/rails-2-test/config.ru +0 -0
  63. data/spec/rails-2-test/config/application.rb +5 -0
  64. data/spec/rails-2-test/script/console +0 -0
  65. data/spec/rails-3-test/config.ru +0 -0
  66. data/spec/rails-3-test/config/application.rb +5 -0
  67. data/spec/rails-3-test/script/rails +0 -0
  68. data/spec/rails-4-test/bin/rails +0 -0
  69. data/spec/rails-4-test/config.ru +0 -0
  70. data/spec/rails-4-test/config/application.rb +5 -0
  71. data/spec/spec_key +27 -0
  72. data/spec/spec_key.pub +9 -0
  73. 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
- class Chamber
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.namespaces = raw_namespaces
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.to_ary.eql? other.to_ary
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?( Chamber::NamespaceSet) &&
105
+ other.is_a?( NamespaceSet) &&
92
106
  self.namespaces == other.namespaces
93
107
  end
94
108
 
95
109
  protected
96
110
 
97
- def namespaces
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=(raw_namespaces)
130
- namespace_values = if raw_namespaces.respond_to? :values
131
- raw_namespaces.values
132
- else
133
- raw_namespaces
134
- end
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
- @namespaces = Set.new namespace_values.map do |value|
137
- value.respond_to?(:call) ? value.call : value
138
- end
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