complex_config 0.11.3 → 0.12.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: 36463346db914460243f6b82006897359d9cc23e
4
- data.tar.gz: 2291339fdfeeb19917e04b74635480edec94610e
3
+ metadata.gz: 185f936e61f349365b82896fab33d19d19ae5d06
4
+ data.tar.gz: ed44eb14a555726bf98c67f1b55c3b810b09b9c7
5
5
  SHA512:
6
- metadata.gz: f99da473894de17af5d3a404ab497efee39fcc43c4b412808b7d80532234c26e360d19844195148d4f60f3c4348bea4106dba2a481d3db97dc2297ead14a113a
7
- data.tar.gz: ae84d09a5c8c9971ebc5c041fdd986d1dcb2e2303c7b53a3451f02827b4e43234153ece932c72e90b5096888cc1b798341c85f5d7045f3df6332041bdfc83210
6
+ metadata.gz: 7bdb5fdd4f6f8cfb22c7a566ccb7bb9150648c665b597b696fcb31a0e1d9b32a20aeb02ca42747f5e647516f64e932808a2b8ff04a06cba42a7e42a6551d010e
7
+ data.tar.gz: eedadb08ce6a0b8ec49dd0530efcbb040bb111f52f81cf3e6d01bcfbda944b17ca75362750a8420e1880229feb81133f755b91473c7d5e961ddd9cb3171c448a
data/README.md CHANGED
@@ -143,6 +143,11 @@ Here is the `ComplexConfig::Plugins::MONEY` plugin for example:
143
143
  end
144
144
 
145
145
  ## Changes
146
+
147
+ * 2017-11-16 Release 0.12.0
148
+ * Supports writing of configurations (encrypted or unencrypted)
149
+ * 2017-11-16 Release 0.11.3
150
+ * Small bugfix
146
151
  * 2017-10-30 Release 0.11.2
147
152
  * Small bugfix
148
153
  * 2017-10-27 Release 0.11.1
@@ -191,7 +196,6 @@ Here is the `ComplexConfig::Plugins::MONEY` plugin for example:
191
196
  * Some small fixes for handling of arrays
192
197
  * 2014-12-15 Release 0.1.0
193
198
  * Freeze configuration by default.
194
-
195
199
  * 2014-12-12 Release 0.0.0
196
200
 
197
201
  ## Download
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.11.3
1
+ 0.12.0
@@ -1,18 +1,18 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: complex_config 0.11.3 ruby lib
2
+ # stub: complex_config 0.12.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "complex_config".freeze
6
- s.version = "0.11.3"
6
+ s.version = "0.12.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
10
10
  s.authors = ["Florian Frank".freeze]
11
- s.date = "2017-11-02"
11
+ s.date = "2017-11-16"
12
12
  s.description = "This library allows you to access configuration files via a simple interface".freeze
13
13
  s.email = "flori@ping.de".freeze
14
14
  s.extra_rdoc_files = ["README.md".freeze, "lib/complex_config.rb".freeze, "lib/complex_config/config.rb".freeze, "lib/complex_config/encryption.rb".freeze, "lib/complex_config/errors.rb".freeze, "lib/complex_config/plugins.rb".freeze, "lib/complex_config/plugins/enable.rb".freeze, "lib/complex_config/plugins/money.rb".freeze, "lib/complex_config/plugins/uri.rb".freeze, "lib/complex_config/provider.rb".freeze, "lib/complex_config/provider/shortcuts.rb".freeze, "lib/complex_config/proxy.rb".freeze, "lib/complex_config/railtie.rb".freeze, "lib/complex_config/rude.rb".freeze, "lib/complex_config/settings.rb".freeze, "lib/complex_config/shortcuts.rb".freeze, "lib/complex_config/version.rb".freeze]
15
- s.files = [".gitignore".freeze, ".rspec".freeze, ".travis.yml".freeze, ".utilsrc".freeze, "COPYING".freeze, "Gemfile".freeze, "README.md".freeze, "Rakefile".freeze, "TODO.md".freeze, "VERSION".freeze, "complex_config.gemspec".freeze, "config/products.yml".freeze, "lib/complex_config.rb".freeze, "lib/complex_config/config.rb".freeze, "lib/complex_config/encryption.rb".freeze, "lib/complex_config/errors.rb".freeze, "lib/complex_config/plugins.rb".freeze, "lib/complex_config/plugins/enable.rb".freeze, "lib/complex_config/plugins/money.rb".freeze, "lib/complex_config/plugins/uri.rb".freeze, "lib/complex_config/provider.rb".freeze, "lib/complex_config/provider/shortcuts.rb".freeze, "lib/complex_config/proxy.rb".freeze, "lib/complex_config/railtie.rb".freeze, "lib/complex_config/rude.rb".freeze, "lib/complex_config/settings.rb".freeze, "lib/complex_config/shortcuts.rb".freeze, "lib/complex_config/version.rb".freeze, "spec/complex_config/config_spec.rb".freeze, "spec/complex_config/encryption_spec.rb".freeze, "spec/complex_config/plugins_spec.rb".freeze, "spec/complex_config/provider_spec.rb".freeze, "spec/complex_config/settings_spec.rb".freeze, "spec/complex_config/shortcuts_spec.rb".freeze, "spec/config/broken_config.yml".freeze, "spec/config/config.yml".freeze, "spec/spec_helper.rb".freeze]
15
+ s.files = [".gitignore".freeze, ".rspec".freeze, ".travis.yml".freeze, ".utilsrc".freeze, "COPYING".freeze, "Gemfile".freeze, "README.md".freeze, "Rakefile".freeze, "TODO.md".freeze, "VERSION".freeze, "complex_config.gemspec".freeze, "config/products.yml".freeze, "lib/complex_config.rb".freeze, "lib/complex_config/config.rb".freeze, "lib/complex_config/encryption.rb".freeze, "lib/complex_config/errors.rb".freeze, "lib/complex_config/plugins.rb".freeze, "lib/complex_config/plugins/enable.rb".freeze, "lib/complex_config/plugins/money.rb".freeze, "lib/complex_config/plugins/uri.rb".freeze, "lib/complex_config/provider.rb".freeze, "lib/complex_config/provider/shortcuts.rb".freeze, "lib/complex_config/proxy.rb".freeze, "lib/complex_config/railtie.rb".freeze, "lib/complex_config/rude.rb".freeze, "lib/complex_config/settings.rb".freeze, "lib/complex_config/shortcuts.rb".freeze, "lib/complex_config/version.rb".freeze, "spec/complex_config/config_spec.rb".freeze, "spec/complex_config/encryption_spec.rb".freeze, "spec/complex_config/plugins_spec.rb".freeze, "spec/complex_config/provider_spec.rb".freeze, "spec/complex_config/settings_spec.rb".freeze, "spec/complex_config/shortcuts_spec.rb".freeze, "spec/config/broken_config.yml".freeze, "spec/config/config.yml".freeze, "spec/config/with-key-file.yml.enc".freeze, "spec/config/with-key-file.yml.key".freeze, "spec/config/with-shell-script.yml.enc".freeze, "spec/config/without-key-file.yml.enc".freeze, "spec/spec_helper.rb".freeze]
16
16
  s.homepage = "https://github.com/flori/complex_config".freeze
17
17
  s.licenses = ["Apache-2.0".freeze]
18
18
  s.rdoc_options = ["--title".freeze, "ComplexConfig -- configuration library".freeze, "--main".freeze, "README.md".freeze]
@@ -2,12 +2,10 @@ require "openssl"
2
2
  require "base64"
3
3
 
4
4
  class ComplexConfig::Encryption
5
- class EncryptionError < StandardError; end
6
-
7
- class DecryptionFailed < EncryptionError; end
8
-
9
5
  def initialize(secret)
10
6
  @secret = secret
7
+ @secret.size != 16 and raise ComplexConfig::EncryptionKeyInvalid,
8
+ "encryption key #{@secret.inspect} must be 16 bytes"
11
9
  @cipher = OpenSSL::Cipher.new('aes-128-gcm')
12
10
  end
13
11
 
@@ -32,7 +30,7 @@ class ComplexConfig::Encryption
32
30
  encrypted, iv, auth_tag = text.split('--').map { |v| base64_decode(v) }
33
31
 
34
32
  auth_tag.nil? || auth_tag.bytes.length != 16 and
35
- raise DecryptionFailed, "auth_tag #{auth_tag.inspect} invalid"
33
+ raise ComplexConfig::DecryptionFailed, "auth_tag #{auth_tag.inspect} invalid"
36
34
 
37
35
  @cipher.decrypt
38
36
  @cipher.key = @secret
@@ -16,4 +16,13 @@ module ComplexConfig
16
16
 
17
17
  class ConfigurationSyntaxError < ComplexConfigError
18
18
  end
19
+
20
+ class EncryptionError < ComplexConfigError
21
+ end
22
+
23
+ class EncryptionKeyInvalid < EncryptionError
24
+ end
25
+
26
+ class DecryptionFailed < EncryptionError
27
+ end
19
28
  end
@@ -3,6 +3,7 @@ require 'erb'
3
3
  require 'pathname'
4
4
  require 'yaml'
5
5
  require 'mize'
6
+ require 'tins/xt/secure_write'
6
7
 
7
8
  class ComplexConfig::Provider
8
9
  include Tins::SexySingleton
@@ -67,8 +68,7 @@ class ComplexConfig::Provider
67
68
  datas << IO.binread(pathname)
68
69
  end
69
70
  if enc_pathname = pathname.to_s + '.enc' and
70
- File.exist?(enc_pathname) and
71
- my_key = key(pathname)
71
+ File.exist?(enc_pathname) and my_key = key(pathname)
72
72
  then
73
73
  text = IO.binread(enc_pathname)
74
74
  datas << ComplexConfig::Encryption.new(my_key).decrypt(text)
@@ -98,6 +98,41 @@ class ComplexConfig::Provider
98
98
  end
99
99
  memoize method: :[]
100
100
 
101
+ def write_config(name, value, encrypt: false, store_key: false)
102
+ config_pathname = pathname(name).to_s
103
+ key = case encrypt
104
+ when :random
105
+ SecureRandom.random_bytes(16)
106
+ when true
107
+ key(config_pathname)
108
+ when String
109
+ encrypt
110
+ end
111
+ hex_key = nil
112
+ settings = ComplexConfig::Settings[value]
113
+ if encrypt
114
+ key or raise ComplexConfig::EncryptionKeyInvalid,
115
+ "encryption key is missing"
116
+ key.size != 16 and raise ComplexConfig::EncryptionKeyInvalid,
117
+ "encryption keys has to be of 16 bytes lenght"
118
+ File.secure_write(config_pathname + '.enc') do |out|
119
+ out.puts ComplexConfig::Encryption.new(key).encrypt(settings.to_yaml)
120
+ end
121
+ hex_key = key.unpack('H*').first
122
+ if store_key
123
+ File.secure_write(config_pathname + '.key') do |out|
124
+ out.puts hex_key
125
+ end
126
+ end
127
+ else
128
+ File.secure_write(config_pathname) do |out|
129
+ out.puts settings.to_yaml
130
+ end
131
+ end
132
+ flush_cache
133
+ hex_key
134
+ end
135
+
101
136
  def exist?(name)
102
137
  !!config(pathname(name), name)
103
138
  rescue ComplexConfig::ConfigurationFileMissing
@@ -130,8 +165,9 @@ class ComplexConfig::Provider
130
165
  key = [
131
166
  @key,
132
167
  read_key_from_file(pathname),
133
- ENV['RAILS_MASTER_KEY']
134
- ].compact[0, 1]
168
+ ENV['COMPLEX_CONFIG_KEY'],
169
+ ENV['RAILS_MASTER_KEY'],
170
+ ].compact[0, 1].map(&:strip)
135
171
  unless key.empty?
136
172
  key.pack('H*')
137
173
  end
@@ -143,7 +179,7 @@ class ComplexConfig::Provider
143
179
 
144
180
  def read_key_from_file(pathname)
145
181
  if pathname
146
- IO.binread(pathname.to_s + '.key').strip
182
+ IO.binread(pathname.to_s + '.key')
147
183
  end
148
184
  rescue Errno::ENOENT
149
185
  end
@@ -81,6 +81,13 @@ class ComplexConfig::Settings < BasicObject
81
81
  self
82
82
  end
83
83
 
84
+ #def write_config(encrypt: false, store_key: false)
85
+ # ::ComplexConfig::Provider.write_config(
86
+ # name_prefix, self, encrypt: encrypt, store_key: store_key
87
+ # )
88
+ # self
89
+ #end
90
+
84
91
  def to_h
85
92
  table_enumerator.each_with_object({}) do |(k, v), h|
86
93
  h[k] =
@@ -94,6 +101,14 @@ class ComplexConfig::Settings < BasicObject
94
101
  end
95
102
  end
96
103
 
104
+ def ==(other)
105
+ to_h == other.to_h
106
+ end
107
+
108
+ def to_yaml
109
+ to_h.to_yaml
110
+ end
111
+
97
112
  def size
98
113
  each.count
99
114
  end
@@ -1,6 +1,6 @@
1
1
  module ComplexConfig
2
2
  # ComplexConfig version
3
- VERSION = '0.11.3'
3
+ VERSION = '0.12.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -1,11 +1,21 @@
1
1
  require 'spec_helper'
2
+ require 'fileutils'
2
3
 
3
4
  RSpec.describe ComplexConfig::Provider do
4
5
  let :provider do
5
6
  ComplexConfig::Provider
6
7
  end
7
8
 
9
+ reset_new_config = -> * {
10
+ provider.config_dir = Pathname.new(__FILE__).dirname.dirname + 'config'
11
+ provider.key = nil
12
+ FileUtils.rm_f(provider.config_dir + 'new_config.yml')
13
+ FileUtils.rm_f(provider.config_dir + 'new_config.yml.enc')
14
+ FileUtils.rm_f(provider.config_dir + 'new_config.yml.key')
15
+ }
16
+
8
17
  after do
18
+ instance_eval(&reset_new_config)
9
19
  provider.flush_cache
10
20
  end
11
21
 
@@ -92,6 +102,95 @@ RSpec.describe ComplexConfig::Provider do
92
102
  end
93
103
  end
94
104
 
105
+ context 'writing configurations' do
106
+ before do
107
+ provider.config_dir = Pathname.new(__FILE__).dirname.dirname + 'config'
108
+ end
109
+
110
+ let :config do
111
+ provider.config(asset('config.yml'))
112
+ end
113
+
114
+ it 'can be written' do
115
+ provider.write_config('new_config', config)
116
+ expect(provider.config(asset('new_config.yml'))).to eq config
117
+ end
118
+
119
+ it 'can be changed and written' do
120
+ provider.deep_freeze = false
121
+ config.development.config.baz = 'something else'
122
+ provider.write_config('new_config', config)
123
+ expect(provider.config(asset('new_config.yml')).development.config.baz).to eq 'something else'
124
+ end
125
+ end
126
+
127
+ context 'reading encrypted configurations' do
128
+ before do
129
+ provider.config_dir = Pathname.new(__FILE__).dirname.dirname + 'config'
130
+ end
131
+
132
+ let :key do
133
+ IO.binread(provider.config_dir + 'with-key-file.yml.key')
134
+ end
135
+
136
+ it 'can read when key is set by accessor' do
137
+ provider.key = key
138
+ expect(provider['without-key-file'].development.foo.bar).to eq 'baz'
139
+ end
140
+
141
+ it 'can read when key is in ENV var' do
142
+ ENV['RAILS_MASTER_KEY'] = key
143
+ expect(provider['without-key-file'].development.foo.bar).to eq 'baz'
144
+ end
145
+
146
+ it 'can read when key is stored in file' do
147
+ expect(provider['with-key-file'].development.foo.bar).to eq 'baz'
148
+ end
149
+
150
+ it 'can read when key is obtained by calling shell script' do
151
+ expect(provider['with-shell-script'].development.foo.bar).to eq 'baz'
152
+ end
153
+ end
154
+
155
+ context 'writing encrypted configurations' do
156
+ before do
157
+ provider.config_dir = Pathname.new(__FILE__).dirname.dirname + 'config'
158
+ instance_eval(&reset_new_config)
159
+ end
160
+
161
+ let :config do
162
+ provider.config(asset('config.yml'))
163
+ end
164
+
165
+ it 'can be written with random key' do
166
+ key = provider.write_config('new_config', config, encrypt: :random, store_key: false)
167
+ provider.key = key
168
+ expect(provider.config(asset('new_config.yml'))).to eq config
169
+ end
170
+
171
+ it 'can be written with random key and store key' do
172
+ provider.write_config('new_config', config, encrypt: :random, store_key: true)
173
+ expect(provider.config(asset('new_config.yml'))).to eq config
174
+ end
175
+
176
+ it 'can be written with passed key' do
177
+ provider.write_config('new_config', config)
178
+ expect(provider.config(asset('new_config.yml'))).to eq config
179
+ end
180
+
181
+ it 'can be written with configured key' do
182
+ provider.write_config('new_config', config)
183
+ expect(provider.config(asset('new_config.yml'))).to eq config
184
+ end
185
+
186
+ it 'can be changed and written' do
187
+ provider.deep_freeze = false
188
+ config.development.config.baz = 'something else'
189
+ provider.write_config('new_config', config)
190
+ expect(provider.config(asset('new_config.yml')).development.config.baz).to eq 'something else'
191
+ end
192
+ end
193
+
95
194
  context 'handling configuration files with []' do
96
195
  before do
97
196
  provider.config_dir = Pathname.new(__FILE__).dirname.dirname + 'config'
@@ -0,0 +1 @@
1
+ 0MKZ0eU56L2MCkL143wST6kchDW/hnyGq7xm3O2NUMY1xa0PoEnZutzR3+LVGu/eWtk=--zhIiqrnKQu422P/V--p0v54ziCv+LBHmg3GYk16Q==
@@ -0,0 +1 @@
1
+ 90ec1139596f9dfdb51e72277735ce9a
@@ -0,0 +1 @@
1
+ 0MKZ0eU56L2MCkL143wST6kchDW/hnyGq7xm3O2NUMY1xa0PoEnZutzR3+LVGu/eWtk=--zhIiqrnKQu422P/V--p0v54ziCv+LBHmg3GYk16Q==
@@ -0,0 +1 @@
1
+ 0MKZ0eU56L2MCkL143wST6kchDW/hnyGq7xm3O2NUMY1xa0PoEnZutzR3+LVGu/eWtk=--zhIiqrnKQu422P/V--p0v54ziCv+LBHmg3GYk16Q==
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: complex_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.3
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-02 00:00:00.000000000 Z
11
+ date: 2017-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gem_hadar
@@ -187,6 +187,10 @@ files:
187
187
  - spec/complex_config/shortcuts_spec.rb
188
188
  - spec/config/broken_config.yml
189
189
  - spec/config/config.yml
190
+ - spec/config/with-key-file.yml.enc
191
+ - spec/config/with-key-file.yml.key
192
+ - spec/config/with-shell-script.yml.enc
193
+ - spec/config/without-key-file.yml.enc
190
194
  - spec/spec_helper.rb
191
195
  homepage: https://github.com/flori/complex_config
192
196
  licenses: