complex_config 0.11.3 → 0.12.0

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