chamber 2.9.1 → 2.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ecd2c070d7f7877b3b727f904c140acc8974712
4
- data.tar.gz: 79b12b587b92be9f7b177c4ad4349b93a4425610
3
+ metadata.gz: f64e70d25c37ca329a62d1fb9154e8c20afcb672
4
+ data.tar.gz: fcd7a592e5200ff1d9cefe0b893eb38a9ee588d1
5
5
  SHA512:
6
- metadata.gz: 0b6038491608666d4b67b1d4eed6d7c87b8e623d27c09878da29affb9d7b78b4a39a8433584e839926beec8b615dbcadc5c07f7b917a2794c3dbe1d5be2153ea
7
- data.tar.gz: 7924650c9d08ecd452b9e89f5b9b592e065678f2b681d040e2ec48b5640727c471f76d442f5a7c60c7c662e1aca4c89e36ca63cabc4fdcdd956b3f27a08d4c5f
6
+ metadata.gz: 88901e1606334bea29432cc4942029e75f9348481c94583c915244ad5fccdca1fe27560aa74fede87a95d6c95902d238eb5ca11559b004b414f89af8a9dd0e25
7
+ data.tar.gz: eb9cb535f93b01b1bde53d55e345914a9586aef32ee76d199eb0df9b1dd0810b035cf862854cf22cfa51b928cf8bbbf7c1643632faeea957809bf6a64ebb1831
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -5,7 +5,7 @@ class Ssl
5
5
 
6
6
  def self.encrypt(_key, value, encryption_key)
7
7
  value = YAML.dump(value)
8
- cipher = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
8
+ cipher = OpenSSL::Cipher.new('AES-128-CBC')
9
9
  cipher.encrypt
10
10
  symmetric_key = cipher.random_key
11
11
  iv = cipher.random_iv
@@ -34,7 +34,7 @@ class Ssl
34
34
  end
35
35
  key = decryption_key.private_decrypt(key)
36
36
 
37
- cipher_dec = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
37
+ cipher_dec = OpenSSL::Cipher.new('AES-128-CBC')
38
38
 
39
39
  cipher_dec.decrypt
40
40
 
@@ -14,6 +14,7 @@ class BooleanConversionFilter
14
14
 
15
15
  attr_accessor :data
16
16
 
17
+ # rubocop:disable Metrics/BlockNesting
17
18
  def execute(settings = data)
18
19
  settings.each_pair do |key, value|
19
20
  if value.respond_to? :each_pair
@@ -34,6 +35,7 @@ class BooleanConversionFilter
34
35
  end
35
36
  end
36
37
  end
38
+ # rubocop:enable Metrics/BlockNesting
37
39
  end
38
40
  end
39
41
  end
@@ -57,7 +57,11 @@ class EncryptionFilter
57
57
  end
58
58
 
59
59
  def encryption_method(value)
60
- if value.respond_to?(:match) && value.match(BASE64_STRING_PATTERN)
60
+ value_is_encrypted = value.respond_to?(:match) &&
61
+ (value.match(BASE64_STRING_PATTERN) ||
62
+ value.match(LARGE_DATA_STRING_PATTERN))
63
+
64
+ if value_is_encrypted
61
65
  EncryptionMethods::None
62
66
  else
63
67
  serialized_value = YAML.dump(value)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'chamber/configuration'
3
3
  require 'chamber/file_set'
4
+ require 'chamber/settings'
4
5
 
5
6
  module Chamber
6
7
  class Instance
@@ -24,6 +25,37 @@ class Instance
24
25
  files.secure
25
26
  end
26
27
 
28
+ def encrypt(data, options = {})
29
+ config = configuration.to_hash.merge(options)
30
+
31
+ Settings.
32
+ new(
33
+ config.merge(
34
+ settings: data,
35
+ pre_filters: [Filters::EncryptionFilter],
36
+ post_filters: [],
37
+ ),
38
+ ).
39
+ to_hash
40
+ end
41
+
42
+ def decrypt(data, options = {})
43
+ config = configuration.to_hash.merge(options)
44
+
45
+ Settings.
46
+ new(
47
+ config.merge(
48
+ settings: data,
49
+ pre_filters: [Filters::NamespaceFilter],
50
+ post_filters: [
51
+ Filters::DecryptionFilter,
52
+ Filters::FailedDecryptionFilter,
53
+ ],
54
+ ),
55
+ ).
56
+ to_hash
57
+ end
58
+
27
59
  def to_s(options = {})
28
60
  settings.to_s(options)
29
61
  end
@@ -0,0 +1,79 @@
1
+ require 'active_support/json'
2
+ require 'chamber'
3
+
4
+ # rubocop:disable Lint/HandleExceptions, Style/EmptyLinesAroundModuleBody
5
+ module Chamber
6
+
7
+ begin
8
+ require 'active_record/type/value'
9
+
10
+ CHAMBER_TYPE_VALUE_SUPERCLASS = ActiveRecord::Type::Value
11
+ rescue LoadError
12
+ end
13
+
14
+ begin
15
+ require 'active_model/type/value'
16
+
17
+ CHAMBER_TYPE_VALUE_SUPERCLASS = ActiveModel::Type::Value
18
+ rescue LoadError
19
+ end
20
+
21
+ module Types
22
+ class Secured < CHAMBER_TYPE_VALUE_SUPERCLASS
23
+ attr_accessor :decryption_key,
24
+ :encryption_key
25
+
26
+ def initialize(options = {})
27
+ self.encryption_key = options.fetch(:encryption_key,
28
+ Chamber.configuration.encryption_key)
29
+ self.decryption_key = options.fetch(:decryption_key,
30
+ Chamber.configuration.decryption_key)
31
+ end
32
+
33
+ def type
34
+ :jsonb
35
+ end
36
+
37
+ def cast(value)
38
+ case value
39
+ when Hash
40
+ value
41
+ when String
42
+ ::ActiveSupport::JSON.decode(value)
43
+ when NilClass
44
+ nil
45
+ else
46
+ fail ArgumentError, 'Any attributes encrypted with Chamber must be either a Hash or a valid JSON string'
47
+ end
48
+ end
49
+ alias type_cast_from_user cast
50
+
51
+ def deserialize(value)
52
+ value = cast(value)
53
+
54
+ return if value.nil?
55
+
56
+ Chamber.decrypt(value,
57
+ decryption_key: decryption_key,
58
+ encryption_key: encryption_key)
59
+ end
60
+ alias type_cast_from_database deserialize
61
+
62
+ def serialize(value)
63
+ fail ArgumentError, 'Any attributes encrypted with Chamber must be a Hash' unless value.is_a?(Hash)
64
+
65
+ ::ActiveSupport::JSON.encode(
66
+ Chamber.encrypt(value,
67
+ decryption_key: decryption_key,
68
+ encryption_key: encryption_key),
69
+ )
70
+ end
71
+ alias type_cast_for_database serialize
72
+
73
+ def changed_in_place?(raw_old_value, new_value)
74
+ deserialize(raw_old_value) == new_value
75
+ end
76
+ end
77
+ end
78
+ end
79
+ # rubocop:enable Lint/HandleExceptions, Style/EmptyLinesAroundModuleBody
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Chamber
3
- VERSION = '2.9.1'.freeze
3
+ VERSION = '2.10.0'.freeze
4
4
  end
@@ -61,7 +61,7 @@ describe EncryptionFilter do
61
61
  EncryptionFilter::BASE64_STRING_PATTERN
62
62
  end
63
63
 
64
- it 'will not attempt to encrypt values if it guesses that they are already encrypted' do
64
+ it 'will not attempt to encrypt normal values if it guesses that they are already encrypted' do
65
65
  filtered_settings = EncryptionFilter.execute(
66
66
  data: {
67
67
  _secure_my_secure_setting: 'fNI5wlBniNhEU4396pmhWwx+A09bRAMJOUASuP7PzprewBX8C' \
@@ -87,6 +87,55 @@ describe EncryptionFilter do
87
87
  'uF3CDIKRIC6U+mnM5SRMO4Dzysw=='
88
88
  end
89
89
 
90
+ it 'will not attempt to encrypt large values if it guesses that they are already encrypted' do
91
+ filtered_settings = EncryptionFilter.execute(
92
+ data: {
93
+ _secure_my_secure_setting: 'AcMY7ALLoGZRakL3ibyo2WB438ipdMDIjsa4SCDBP2saOY63A' \
94
+ 'D3C/SZanexlYDQoYoYC0V5J5EvKHgGMDAU8qnp9LjzU5VCwJ3' \
95
+ 'SVRGz3J0c7LXgTlC585Lgy8LX+/yjYFm4D13hlMvvsoI35Bo8' \
96
+ 'EVkTSU2+0gRSjRpQJeK1o7az5+fBuNmFipevA4YfLnarnpwo2' \
97
+ 'd2oO+BqStI2QQI1UWwN2R04rvOdHoEzA6DLsdvYX+QTKDk4K5' \
98
+ 'oSKXfuMBvzOCaCGT75cmt85ZY7XZnwbKi6c4mtL1ajrCr8sQF' \
99
+ 'TA/GyG1EiYLFp1uQco0m2/S9yFf26REjax4ZE6O/ilXgT6xg=' \
100
+ '=#YAm25swWRQx4ip1RjVzpGQ==#vRGvgjErI+dATM4UOtFkkg' \
101
+ 'efFpFTvxGpHN0gRbf1VCO4K07eqAQPb46BDI67a8iNum9cBph' \
102
+ 'es7oGmuNnUvBg4JiZhKsXnolcRWdITDVh/XYNioXRmesvj4x+' \
103
+ 'tY0FVhkLV2zubRVfC7CDJgin6wRHP+bcZhICDD2YqB+XRS4ou' \
104
+ '66UeaiGA4eV4G6sPIo+DPjDM3m8JFnuRFMvGk73wthbN4MdAp' \
105
+ '9xONt5wfobJUiUR11k2iAqwhx7Wyj0imz/afI8goDTdMfQt3V' \
106
+ 'DOYqYG3y2AcYOfsOL6m0GtQRlKvtsvw+m8/ICwSGiL2Loup0j' \
107
+ '/jDGhFi1lwf4ded8aSwyS+2/Ks9C008dsJwpR1SxJ59z1KSzd' \
108
+ 'QcTcrJTnxd+2qpOVVIoaRGud2tSV+5wKXy9dWRflLsjEtBRFR' \
109
+ 'eFurTVQPodjDy+Lhs452/O/+KAJOXMKeYegCGOe8z9tLD3tel' \
110
+ 'jjTyJPeW/1FE3+tP3G3HJAV4sgoO0YwhNY1Nji56igCl3UvEP' \
111
+ 'nEQcJgu0w/+dqSreqwp6TqaqXY3lzr8vi733lti4nss=',
112
+ },
113
+ encryption_key: './spec/spec_key.pub',
114
+ )
115
+
116
+ my_secure_setting = filtered_settings._secure_my_secure_setting
117
+
118
+ expect(my_secure_setting).to eql 'AcMY7ALLoGZRakL3ibyo2WB438ipdMDIjsa4SCDBP2saOY63A' \
119
+ 'D3C/SZanexlYDQoYoYC0V5J5EvKHgGMDAU8qnp9LjzU5VCwJ3' \
120
+ 'SVRGz3J0c7LXgTlC585Lgy8LX+/yjYFm4D13hlMvvsoI35Bo8' \
121
+ 'EVkTSU2+0gRSjRpQJeK1o7az5+fBuNmFipevA4YfLnarnpwo2' \
122
+ 'd2oO+BqStI2QQI1UWwN2R04rvOdHoEzA6DLsdvYX+QTKDk4K5' \
123
+ 'oSKXfuMBvzOCaCGT75cmt85ZY7XZnwbKi6c4mtL1ajrCr8sQF' \
124
+ 'TA/GyG1EiYLFp1uQco0m2/S9yFf26REjax4ZE6O/ilXgT6xg=' \
125
+ '=#YAm25swWRQx4ip1RjVzpGQ==#vRGvgjErI+dATM4UOtFkkg' \
126
+ 'efFpFTvxGpHN0gRbf1VCO4K07eqAQPb46BDI67a8iNum9cBph' \
127
+ 'es7oGmuNnUvBg4JiZhKsXnolcRWdITDVh/XYNioXRmesvj4x+' \
128
+ 'tY0FVhkLV2zubRVfC7CDJgin6wRHP+bcZhICDD2YqB+XRS4ou' \
129
+ '66UeaiGA4eV4G6sPIo+DPjDM3m8JFnuRFMvGk73wthbN4MdAp' \
130
+ '9xONt5wfobJUiUR11k2iAqwhx7Wyj0imz/afI8goDTdMfQt3V' \
131
+ 'DOYqYG3y2AcYOfsOL6m0GtQRlKvtsvw+m8/ICwSGiL2Loup0j' \
132
+ '/jDGhFi1lwf4ded8aSwyS+2/Ks9C008dsJwpR1SxJ59z1KSzd' \
133
+ 'QcTcrJTnxd+2qpOVVIoaRGud2tSV+5wKXy9dWRflLsjEtBRFR' \
134
+ 'eFurTVQPodjDy+Lhs452/O/+KAJOXMKeYegCGOe8z9tLD3tel' \
135
+ 'jjTyJPeW/1FE3+tP3G3HJAV4sgoO0YwhNY1Nji56igCl3UvEP' \
136
+ 'nEQcJgu0w/+dqSreqwp6TqaqXY3lzr8vi733lti4nss='
137
+ end
138
+
90
139
  it 'can encrypt long multiline strings' do
91
140
  filtered_settings = EncryptionFilter.execute(
92
141
  data: {
@@ -0,0 +1,70 @@
1
+ require 'rspectacular'
2
+ require 'active_support/hash_with_indifferent_access'
3
+ require 'chamber/types/secured'
4
+
5
+ module Chamber
6
+ module Types
7
+ describe Secured do
8
+ BASE64_STRING_PATTERN = %r{[A-Za-z0-9\+/]{342}==}
9
+
10
+ subject do
11
+ Secured.new(decryption_key: './spec/spec_key',
12
+ encryption_key: './spec/spec_key.pub')
13
+ end
14
+
15
+ it 'allows strings to be cast from the user' do
16
+ json_string = '{ "hello": "there", "whatever": 3 }'
17
+ secured = subject.cast(json_string)
18
+
19
+ expect(secured).to eql('hello' => 'there', 'whatever' => 3)
20
+ end
21
+
22
+ it 'allows hashes to be cast from a user' do
23
+ json_hash = { 'hello' => 'there', 'whatever' => 3 }
24
+ secured = subject.cast(json_hash)
25
+
26
+ expect(secured).to eql('hello' => 'there', 'whatever' => 3)
27
+ end
28
+
29
+ it 'allows nils to be cast from a user' do
30
+ secured = subject.cast(nil)
31
+
32
+ expect(secured).to be_nil
33
+ end
34
+
35
+ it 'fails if passed something that it cannot be cast' do
36
+ expect { subject.cast(3) }.to \
37
+ raise_error(ArgumentError).
38
+ with_message('Any attributes encrypted with Chamber must ' \
39
+ 'be either a Hash or a valid JSON string')
40
+ end
41
+
42
+ it 'can deserialize a hash' do
43
+ json_string = '{' \
44
+ '"_secure_hello":"cpsTajQ/28E0YLQBpJ2tORnLSc6wliCqrmMzU0QfQZJlUWf' \
45
+ 'Q1yuev2xLsX56o5QkuJiqaspH9W68qXDC17UqcV0pB0y75d' \
46
+ '6ttQZbk3p9QbYgWGZOVlHEA8eJIqDUzisShrrOo+nSin6QK' \
47
+ 'UqizSjqhQC3Ii7CjTpMOK5RVc2y34vsVvYoJaqz5IYUEatA' \
48
+ 'XxzHsQ5tkcqy++a9LTJVFOt+ug+mTCstNJHW2sUK9L1XrbD' \
49
+ '2+KwUNkImCbhl6qeA+4CeVXMFgcpxjaawg5cQCgfSPj8gSy' \
50
+ 'pisbID59P0QVXRDQTdncrRv7q16RLmTqKI0xhNGevreFkNG' \
51
+ 'LAtSQjFRYfAQA==",' \
52
+ '"whatever":3' \
53
+ '}'
54
+ secured = subject.deserialize(json_string)
55
+
56
+ expect(secured).to eql('_secure_hello' => 'there', 'whatever' => 3)
57
+ end
58
+
59
+ # rubocop:disable Metrics/LineLength
60
+ it 'can serialize a hash' do
61
+ json_hash = { '_secure_hello' => 'there', 'whatever' => 3 }
62
+ secured = subject.serialize(json_hash)
63
+
64
+ expect(secured).to be_a String
65
+ expect(secured).to match(/{\"_secure_hello\":\"#{BASE64_STRING_PATTERN}\",\"whatever\":3}/)
66
+ end
67
+ # rubocop:enable Metrics/LineLength
68
+ end
69
+ end
70
+ end
@@ -3,7 +3,6 @@ require 'rspectacular'
3
3
  require 'chamber'
4
4
  require 'fileutils'
5
5
 
6
- # rubocop:disable Metrics/LineLength
7
6
  FileUtils.mkdir_p '/tmp/chamber/settings' unless File.exist? '/tmp/chamber/settings'
8
7
 
9
8
  File.open('/tmp/chamber/settings.yml', 'w+') do |file|
@@ -313,4 +312,3 @@ describe 'Chamber' do
313
312
  expect(Chamber.test.my_encrpyted_setting).to eql 'hello'
314
313
  end
315
314
  end
316
- # rubocop:enable Metrics/LineLength
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chamber
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.1
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thekompanee
@@ -34,7 +34,7 @@ cert_chain:
34
34
  QwSfDGz6+zsImi5N3UT71+mk7YcviQSgvMRl3VkAv8MZ6wcJ5SQRpf9w0OeFH6Ln
35
35
  nNbCoHiYeXX/lz/M6AIbxDIZZTwxcyvF7bdrQ2fbH5MsfQ==
36
36
  -----END CERTIFICATE-----
37
- date: 2016-08-23 00:00:00.000000000 Z
37
+ date: 2017-02-04 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: thor
@@ -92,6 +92,34 @@ dependencies:
92
92
  - - "~>"
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0.46'
95
+ - !ruby/object:Gem::Dependency
96
+ name: activemodel
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '5.0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '5.0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: activesupport
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '5.0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '5.0'
95
123
  description: "\n Chamber lets you source your Settings from
96
124
  an arbitrary number of YAML files and\n provides a simple
97
125
  mechanism for overriding settings from the ENV, which is\n friendly
@@ -149,6 +177,7 @@ files:
149
177
  - lib/chamber/rails/railtie.rb
150
178
  - lib/chamber/rubinius_fix.rb
151
179
  - lib/chamber/settings.rb
180
+ - lib/chamber/types/secured.rb
152
181
  - lib/chamber/version.rb
153
182
  - spec/fixtures/settings.yml
154
183
  - spec/lib/chamber/commands/files_spec.rb
@@ -172,6 +201,7 @@ files:
172
201
  - spec/lib/chamber/filters/translate_secure_keys_filter_spec.rb
173
202
  - spec/lib/chamber/namespace_set_spec.rb
174
203
  - spec/lib/chamber/settings_spec.rb
204
+ - spec/lib/chamber/types/secured_spec.rb
175
205
  - spec/lib/chamber_spec.rb
176
206
  - spec/rails-2-test/config.ru
177
207
  - spec/rails-2-test/config/application.rb
@@ -209,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
239
  version: '0'
210
240
  requirements: []
211
241
  rubyforge_project:
212
- rubygems_version: 2.5.1
242
+ rubygems_version: 2.6.8
213
243
  signing_key:
214
244
  specification_version: 4
215
245
  summary: A surprisingly configurable convention-based approach to managing your application's
@@ -237,6 +267,7 @@ test_files:
237
267
  - spec/lib/chamber/filters/translate_secure_keys_filter_spec.rb
238
268
  - spec/lib/chamber/namespace_set_spec.rb
239
269
  - spec/lib/chamber/settings_spec.rb
270
+ - spec/lib/chamber/types/secured_spec.rb
240
271
  - spec/lib/chamber_spec.rb
241
272
  - spec/rails-2-test/config/application.rb
242
273
  - spec/rails-2-test/config.ru
@@ -252,4 +283,3 @@ test_files:
252
283
  - spec/rails-engine-test/spec/dummy/script/rails
253
284
  - spec/spec_key
254
285
  - spec/spec_key.pub
255
- has_rdoc:
metadata.gz.sig CHANGED
Binary file