secret_config 0.6.4 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,7 +34,7 @@ module SecretConfig
34
34
  h[key] = value
35
35
  return h
36
36
  end
37
- last = full_path.split("/").reduce(h) do |target, path|
37
+ last = full_path.split("/").reduce(h) do |target, path|
38
38
  if path == ""
39
39
  target
40
40
  elsif target.key?(path)
@@ -1,3 +1,3 @@
1
1
  module SecretConfig
2
- VERSION = "0.6.4".freeze
2
+ VERSION = "0.9.1".freeze
3
3
  end
@@ -1,9 +1,5 @@
1
1
  # Local application config goes here. Do not check in production secrets.
2
2
  # These are for development and test only.
3
-
4
- #
5
- # Development - Local - Root: '/test/my_application'
6
- #
7
3
  development:
8
4
  my_application:
9
5
  symmetric_encryption:
@@ -31,17 +27,51 @@ test:
31
27
  key: QUJDREVGMTIzNDU2Nzg5MEFCQ0RFRjEyMzQ1Njc4OTA=
32
28
  iv: QUJDREVGMTIzNDU2Nzg5MA==
33
29
  version: 2
30
+ previous_key:
31
+ key: key1
32
+ iv: iv1
33
+ version: 1
34
34
 
35
35
  mysql:
36
36
  database: secret_config_test
37
37
  username: secret_config
38
38
  password: secret_configrules
39
39
  host: 127.0.0.1
40
+ ports: "12345,5343,26815"
41
+ ports2: " 12345, 5343 , 26815"
42
+ hostnames: "primary.example.net,secondary.example.net,backup.example.net"
43
+ hostnames2: " primary.example.net, secondary.example.net , backup.example.net"
40
44
 
41
45
  mongo:
42
46
  database: secret_config_test
43
47
  primary: 127.0.0.1:27017
44
- secondary: 127.0.0.1:27018
48
+ secondary: "%{hostname}:27018"
45
49
 
46
50
  secrets:
47
51
  secret_key_base: somereallylongteststring
52
+
53
+ other_application:
54
+ symmetric_encryption:
55
+ version: 3
56
+ __import__: /test/my_application/symmetric_encryption
57
+ iv: MTIzNDU2Nzg5MEFCQ0RFRg==
58
+ previous_key:
59
+ key: key0
60
+
61
+ mysql:
62
+ # database: "%{fetch: /test/my_application/mysql/database }"
63
+ username: other
64
+ password: otherrules
65
+ host: "%{hostname}"
66
+
67
+ mongo:
68
+ database: secret_config_test
69
+ primary: localhost:27017
70
+ secondary: "%{hostname}:27018"
71
+
72
+ mongo2:
73
+ __import__: mongo
74
+ database: secret_config_test2
75
+
76
+ mongo3:
77
+ __import__: mongo
@@ -0,0 +1,82 @@
1
+ require_relative "test_helper"
2
+ require "socket"
3
+
4
+ class ParserTest < Minitest::Test
5
+ describe SecretConfig::Registry do
6
+ let :file_name do
7
+ File.join(File.dirname(__FILE__), "config", "application.yml")
8
+ end
9
+
10
+ let :path do
11
+ "/test/other_application"
12
+ end
13
+
14
+ let :provider do
15
+ SecretConfig::Providers::File.new(file_name: file_name)
16
+ end
17
+
18
+ let :registry do
19
+ SecretConfig::Registry.new(path: path, provider: provider)
20
+ end
21
+
22
+ # let :parser do
23
+ # SecretConfig::Parser.new(path, registry)
24
+ # end
25
+
26
+ #
27
+ # Retrieve values elsewhere in the registry.
28
+ # Paths can be relative to the current root, or absolute paths outside the current root.
29
+ # %{fetch:key} # Fetches a single value from a relative or absolute path
30
+ # Return the value of the supplied key.
31
+ #
32
+ # With a relative key, look for the value in the current registry.
33
+ # With an absolute key call the provider and fetch the value directly.
34
+ #
35
+ # Notes:
36
+ # - A lot of absolute key lookups can be expensive since each one is a separate call.
37
+ # def fetch(key)
38
+ # fetch_list[key] = key
39
+ # end
40
+ # describe "#fetch" do
41
+ # it "inside current path" do
42
+ #
43
+ # end
44
+ #
45
+ # it "outside current path" do
46
+ #
47
+ # end
48
+ # end
49
+
50
+ # %{import:path} # Imports a path of keys and values into the current path
51
+ # Replace the current value with a tree of values with the supplied path.
52
+ #
53
+ describe "#import" do
54
+ it "removes import key" do
55
+ refute registry.key?("symmetric_encryption/__import__"), -> { registry.configuration(filters: nil).ai }
56
+ end
57
+
58
+ it "retains overrides" do
59
+ assert_equal "3", registry["symmetric_encryption/version"], -> { registry.configuration(filters: nil).ai }
60
+ assert_equal "MTIzNDU2Nzg5MEFCQ0RFRg==", registry["symmetric_encryption/iv"]
61
+ end
62
+
63
+ it "retains child overrides" do
64
+ assert_equal "key0", registry["symmetric_encryption/previous_key/key"], -> { registry.configuration(filters: nil).ai }
65
+ end
66
+
67
+ it "imports new fields" do
68
+ assert_equal "QUJDREVGMTIzNDU2Nzg5MEFCQ0RFRjEyMzQ1Njc4OTA=", registry["symmetric_encryption/key"]
69
+ end
70
+
71
+ it "relative import empty" do
72
+ assert_equal "secret_config_test", registry["mongo3/database"]
73
+ assert_equal "localhost:27017", registry["mongo3/primary"]
74
+ end
75
+
76
+ it "relative import with overrides" do
77
+ assert_equal "secret_config_test2", registry["mongo2/database"]
78
+ assert_equal "localhost:27017", registry["mongo3/primary"]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,10 +1,10 @@
1
- require_relative '../test_helper'
1
+ require_relative "../test_helper"
2
2
 
3
3
  module Providers
4
4
  class FileTest < Minitest::Test
5
5
  describe SecretConfig::Providers::File do
6
6
  let :file_name do
7
- File.join(File.dirname(__FILE__), '..', 'config', 'application.yml')
7
+ File.join(File.dirname(__FILE__), "..", "config", "application.yml")
8
8
  end
9
9
 
10
10
  let :path do
@@ -15,7 +15,7 @@ module Providers
15
15
  {
16
16
  "/test/my_application/mongo/database" => "secret_config_test",
17
17
  "/test/my_application/mongo/primary" => "127.0.0.1:27017",
18
- "/test/my_application/mongo/secondary" => "127.0.0.1:27018",
18
+ "/test/my_application/mongo/secondary" => "%{hostname}:27018",
19
19
  "/test/my_application/mysql/database" => "secret_config_test",
20
20
  "/test/my_application/mysql/password" => "secret_configrules",
21
21
  "/test/my_application/mysql/username" => "secret_config",
@@ -27,8 +27,8 @@ module Providers
27
27
  }
28
28
  end
29
29
 
30
- describe '#each' do
31
- it 'file' do
30
+ describe "#each" do
31
+ it "file" do
32
32
  file_provider = SecretConfig::Providers::File.new(file_name: file_name)
33
33
  paths = {}
34
34
  file_provider.each(path) { |key, value| paths[key] = value }
@@ -1,10 +1,10 @@
1
- require_relative '../test_helper'
1
+ require_relative "../test_helper"
2
2
 
3
3
  module Providers
4
4
  class SsmTest < Minitest::Test
5
5
  describe SecretConfig::Providers::Ssm do
6
6
  let :file_name do
7
- File.join(File.dirname(__FILE__), '..', 'config', 'application.yml')
7
+ File.join(File.dirname(__FILE__), "..", "config", "application.yml")
8
8
  end
9
9
 
10
10
  let :path do
@@ -15,7 +15,7 @@ module Providers
15
15
  {
16
16
  "/test/my_application/mongo/database" => "secret_config_test",
17
17
  "/test/my_application/mongo/primary" => "127.0.0.1:27017",
18
- "/test/my_application/mongo/secondary" => "127.0.0.1:27018",
18
+ "/test/my_application/mongo/secondary" => "%{hostname}:27018",
19
19
  "/test/my_application/mysql/database" => "secret_config_test",
20
20
  "/test/my_application/mysql/password" => "secret_configrules",
21
21
  "/test/my_application/mysql/username" => "secret_config",
@@ -28,21 +28,43 @@ module Providers
28
28
  end
29
29
 
30
30
  before do
31
- unless ENV['AWS_ACCESS_KEY_ID']
31
+ unless ENV["AWS_ACCESS_KEY_ID"]
32
32
  skip "Skipping AWS SSM Parameter Store tests because env var 'AWS_ACCESS_KEY_ID' is not defined."
33
33
  end
34
34
  end
35
35
 
36
- describe '#each' do
37
- it 'fetches all keys in path' do
38
- ssm = SecretConfig::Providers::Ssm.new
36
+ describe "#each" do
37
+ let :ssm_provider do
38
+ SecretConfig::Providers::Ssm.new
39
+ end
40
+
41
+ let :ssm_extended_provider do
42
+ SecretConfig::Providers::Ssm.new(credentials: ::Aws::AssumeRoleCredentials.new(
43
+ role_arn: "arn:aws:iam::#{ENV['SECRET_CONFIG_ACCOUNT_ID']}:role/secret_config_test",
44
+ role_session_name: "SecretConfigSession-#{SecureRandom.uuid}"
45
+ ))
46
+ end
47
+
48
+ def fill_paths(provider)
39
49
  paths = {}
40
- ssm.each(path) { |key, value| paths[key] = value }
41
50
 
42
- if paths.empty?
43
- upload_settings(ssm) unless paths.key?("/test/my_application/mongo/database")
44
- ssm.each(path) { |key, value| paths[key] = value }
51
+ provider.each(path) { |key, value| paths[key] = value }
52
+
53
+ upload_settings(provider) if paths.empty?
54
+
55
+ paths
56
+ end
57
+
58
+ it "fetches all keys in path" do
59
+ paths = fill_paths(ssm_provider)
60
+
61
+ expected.each_pair do |key, value|
62
+ assert_equal paths[key], value, "Path: #{key}"
45
63
  end
64
+ end
65
+
66
+ it "provider with extended credentials fetches all keys in path" do
67
+ paths = fill_paths(ssm_extended_provider)
46
68
 
47
69
  expected.each_pair do |key, value|
48
70
  assert_equal paths[key], value, "Path: #{key}"
@@ -52,7 +74,10 @@ module Providers
52
74
 
53
75
  def upload_settings(ssm)
54
76
  file_provider = SecretConfig::Providers::File.new(file_name: file_name)
55
- file_provider.each(path) { |key, value| ap key; ssm.set(key, value) }
77
+ file_provider.each(path) do |key, value|
78
+ ap key
79
+ ssm.set(key, value)
80
+ end
56
81
  end
57
82
  end
58
83
  end
@@ -1,9 +1,10 @@
1
- require_relative 'test_helper'
1
+ require_relative "test_helper"
2
+ require "socket"
2
3
 
3
4
  class RegistryTest < Minitest::Test
4
5
  describe SecretConfig::Registry do
5
6
  let :file_name do
6
- File.join(File.dirname(__FILE__), 'config', 'application.yml')
7
+ File.join(File.dirname(__FILE__), "config", "application.yml")
7
8
  end
8
9
 
9
10
  let :path do
@@ -22,102 +23,132 @@ class RegistryTest < Minitest::Test
22
23
  {
23
24
  "/test/my_application/mongo/database" => "secret_config_test",
24
25
  "/test/my_application/mongo/primary" => "127.0.0.1:27017",
25
- "/test/my_application/mongo/secondary" => "127.0.0.1:27018",
26
+ "/test/my_application/mongo/secondary" => "#{Socket.gethostname}:27018",
26
27
  "/test/my_application/mysql/database" => "secret_config_test",
27
28
  "/test/my_application/mysql/password" => "secret_configrules",
28
29
  "/test/my_application/mysql/username" => "secret_config",
29
30
  "/test/my_application/mysql/host" => "127.0.0.1",
31
+ "/test/my_application/mysql/ports" => "12345,5343,26815",
32
+ "/test/my_application/mysql/ports2" => " 12345, 5343 , 26815",
33
+ "/test/my_application/mysql/hostnames" => "primary.example.net,secondary.example.net,backup.example.net",
34
+ "/test/my_application/mysql/hostnames2" => " primary.example.net, secondary.example.net , backup.example.net",
30
35
  "/test/my_application/secrets/secret_key_base" => "somereallylongteststring",
31
36
  "/test/my_application/symmetric_encryption/key" => "QUJDREVGMTIzNDU2Nzg5MEFCQ0RFRjEyMzQ1Njc4OTA=",
32
- "/test/my_application/symmetric_encryption/version" => 2,
37
+ "/test/my_application/symmetric_encryption/version" => "2",
33
38
  "/test/my_application/symmetric_encryption/iv" => "QUJDREVGMTIzNDU2Nzg5MA=="
34
39
  }
35
40
  end
36
41
 
37
- describe '#configuration' do
38
- it 'returns a copy of the config' do
42
+ describe "#configuration" do
43
+ it "returns a copy of the config" do
39
44
  assert_equal "127.0.0.1", registry.configuration.dig("mysql", "host")
40
45
  end
41
46
 
42
- it 'filters passwords' do
47
+ it "filters passwords" do
43
48
  assert_equal SecretConfig::FILTERED, registry.configuration.dig("mysql", "password")
44
49
  end
45
50
 
46
- it 'filters key' do
51
+ it "filters key" do
47
52
  assert_equal SecretConfig::FILTERED, registry.configuration.dig("symmetric_encryption", "key")
48
53
  end
49
54
  end
50
55
 
51
- describe '#key?' do
52
- it 'has key' do
53
- expected.each_pair do |key, value|
56
+ describe "#key?" do
57
+ it "has key" do
58
+ expected.each_pair do |key, _value|
54
59
  key = key.sub("#{path}/", "")
55
60
  assert registry.key?(key), "Path: #{key}"
56
61
  end
57
62
  end
58
63
 
59
- it 'returns false with missing relative key' do
64
+ it "returns false with missing relative key" do
60
65
  refute registry.key?("invalid/path")
61
66
  end
62
67
 
63
- it 'returns nil with missing full key' do
68
+ it "returns nil with missing full key" do
64
69
  refute registry.key?("/test/invalid/path")
65
70
  end
66
71
  end
67
72
 
68
- describe '#[]' do
69
- it 'returns values' do
73
+ describe "#[]" do
74
+ it "returns values" do
70
75
  expected.each_pair do |key, value|
71
76
  key = key.sub("#{path}/", "")
72
77
  assert_equal value, registry[key], "Path: #{key}"
73
78
  end
74
79
  end
75
80
 
76
- it 'returns nil with missing relative key' do
81
+ it "returns nil with missing relative key" do
77
82
  assert_nil registry["invalid/path"]
78
83
  end
79
84
 
80
- it 'returns nil with missing full key' do
85
+ it "returns nil with missing full key" do
81
86
  assert_nil registry["/test/invalid/path"]
82
87
  end
83
88
  end
84
89
 
85
- describe '#fetch' do
86
- it 'returns values' do
90
+ describe "#fetch" do
91
+ it "returns values" do
87
92
  expected.each_pair do |key, value|
88
93
  key = key.sub("#{path}/", "")
89
94
  assert_equal value, registry.fetch(key), "Path: #{key}"
90
95
  end
91
96
  end
92
97
 
93
- it 'exception missing relative key' do
98
+ it "exception missing relative key" do
94
99
  assert_raises SecretConfig::MissingMandatoryKey do
95
100
  registry.fetch("invalid/path")
96
101
  end
97
102
  end
98
103
 
99
- it 'returns nil with missing full key' do
104
+ it "returns nil with missing full key" do
100
105
  assert_raises SecretConfig::MissingMandatoryKey do
101
106
  registry.fetch("/test/invalid/path")
102
107
  end
103
108
  end
104
109
 
105
- it 'returns default with missing key' do
110
+ it "returns default with missing key" do
106
111
  assert_equal "default_value", registry.fetch("/test/invalid/path", default: "default_value")
107
112
  end
108
113
 
109
- it 'returns default with false value' do
114
+ it "returns default with false value" do
110
115
  assert_equal false, registry.fetch("/test/invalid/path", default: false, type: :boolean)
111
116
  end
112
117
 
113
- it 'converts to integer' do
118
+ it "converts to integer" do
114
119
  assert_equal 2, registry.fetch("symmetric_encryption/version", type: :integer)
115
120
  end
116
121
 
117
- it 'decodes Base 64' do
122
+ describe "uses separator to extract an array" do
123
+ it "of strings" do
124
+ value = registry.fetch("mysql/hostnames", separator: ",")
125
+ assert_equal ["primary.example.net", "secondary.example.net", "backup.example.net"], value
126
+ end
127
+
128
+ it "of strings with spaces" do
129
+ value = registry.fetch("mysql/hostnames2", separator: ",")
130
+ assert_equal ["primary.example.net", "secondary.example.net", "backup.example.net"], value
131
+ end
132
+
133
+ it "of integers" do
134
+ value = registry.fetch("mysql/ports", type: :integer, separator: ",")
135
+ assert_equal([12345, 5343, 26815], value)
136
+ end
137
+
138
+ it "of integers with spaces" do
139
+ value = registry.fetch("mysql/ports2", type: :integer, separator: ",")
140
+ assert_equal([12345, 5343, 26815], value)
141
+ end
142
+
143
+ it "accepts a default without requiring conversion" do
144
+ value = registry.fetch("mysql/ports5", type: :integer, separator: ",", default: [23, 45, 72])
145
+ assert_equal([23, 45, 72], value)
146
+ end
147
+ end
148
+
149
+ it "decodes Base 64" do
118
150
  assert_equal "ABCDEF1234567890ABCDEF1234567890", registry.fetch("symmetric_encryption/key", encoding: :base64)
119
151
  end
120
152
  end
121
153
  end
122
154
  end
123
-
@@ -1,9 +1,10 @@
1
- require_relative 'test_helper'
1
+ require_relative "test_helper"
2
+ require "socket"
2
3
 
3
4
  class SecretConfigTest < Minitest::Test
4
5
  describe SecretConfig::Providers::File do
5
6
  let :file_name do
6
- File.join(File.dirname(__FILE__), 'config', 'application.yml')
7
+ File.join(File.dirname(__FILE__), "config", "application.yml")
7
8
  end
8
9
 
9
10
  let :path do
@@ -14,39 +15,87 @@ class SecretConfigTest < Minitest::Test
14
15
  SecretConfig.use :file, path: path, file_name: file_name
15
16
  end
16
17
 
17
- describe '#configuration' do
18
- it 'returns a copy of the config' do
18
+ describe ".configuration" do
19
+ it "returns a copy of the config" do
19
20
  assert_equal "127.0.0.1", SecretConfig.configuration.dig("mysql", "host")
20
21
  end
21
22
  end
22
23
 
23
- describe '#key?' do
24
- it 'has key' do
24
+ describe ".key?" do
25
+ it "has key" do
25
26
  assert SecretConfig.key?("mysql/database")
26
27
  end
27
28
  end
28
29
 
29
- describe '#[]' do
30
- it 'returns values' do
30
+ describe ".[]" do
31
+ it "returns values" do
31
32
  assert_equal "secret_config_test", SecretConfig["mysql/database"]
32
33
  end
34
+
35
+ it "returns values with interpolation" do
36
+ assert_equal "#{Socket.gethostname}:27018", SecretConfig["mongo/secondary"]
37
+ end
33
38
  end
34
39
 
35
- describe '#fetch' do
40
+ describe ".fetch" do
36
41
  after do
37
- ENV['MYSQL_DATABASE'] = nil
42
+ ENV["MYSQL_DATABASE"] = nil
43
+ SecretConfig.check_env_var = true
38
44
  end
39
45
 
40
- it 'fetches values' do
46
+ it "fetches values" do
41
47
  assert_equal "secret_config_test", SecretConfig.fetch("mysql/database")
42
48
  end
43
49
 
44
- it 'can be overridden by an environment variable' do
45
- ENV['MYSQL_DATABASE'] = 'other'
50
+ it "can be overridden by an environment variable" do
51
+ ENV["MYSQL_DATABASE"] = "other"
46
52
 
47
53
  SecretConfig.use :file, path: path, file_name: file_name
48
54
  assert_equal "other", SecretConfig.fetch("mysql/database")
49
55
  end
56
+
57
+ it "returns values with interpolation" do
58
+ assert_equal "#{Socket.gethostname}:27018", SecretConfig.fetch("mongo/secondary")
59
+ end
60
+
61
+ it "can be omitted an environment variable override with #check_env_var configuration" do
62
+ ENV["MYSQL_DATABASE"] = "other"
63
+
64
+ SecretConfig.check_env_var = false
65
+ SecretConfig.use :file, path: path, file_name: file_name
66
+ assert_equal "secret_config_test", SecretConfig.fetch("mysql/database")
67
+ end
68
+ end
69
+
70
+ describe ".configure" do
71
+ before do
72
+ SecretConfig.use :file, path: path, file_name: file_name
73
+ end
74
+
75
+ it "#fetch" do
76
+ database = nil
77
+ SecretConfig.configure("mysql") do |config|
78
+ database = config.fetch("database")
79
+ end
80
+ assert_equal "secret_config_test", database
81
+ end
82
+
83
+ it "#[]" do
84
+ database = nil
85
+ SecretConfig.configure("mysql") do |config|
86
+ database = config["database"]
87
+ end
88
+ assert_equal "secret_config_test", database
89
+ end
90
+
91
+ it "#key?" do
92
+ database = nil
93
+ SecretConfig.configure("mysql") do |config|
94
+ database = config.key?("database")
95
+ end
96
+ assert_equal true, database
97
+ end
50
98
  end
99
+
51
100
  end
52
101
  end