chamber 1.0.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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