secret_config 0.7.1 → 0.8.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 +4 -4
- data/README.md +84 -42
- data/Rakefile +7 -7
- data/bin/secret_config +1 -1
- data/lib/secret_config.rb +30 -1
- data/lib/secret_config/config.rb +44 -0
- data/lib/secret_config/parser.rb +75 -0
- data/lib/secret_config/providers/file.rb +17 -4
- data/lib/secret_config/providers/ssm.rb +9 -2
- data/lib/secret_config/registry.rb +42 -29
- data/lib/secret_config/setting_interpolator.rb +4 -17
- data/lib/secret_config/string_interpolator.rb +5 -4
- data/lib/secret_config/utils.rb +1 -1
- data/lib/secret_config/version.rb +1 -1
- data/test/config/application.yml +34 -4
- data/test/parser_test.rb +82 -0
- data/test/providers/file_test.rb +4 -4
- data/test/providers/ssm_test.rb +37 -12
- data/test/registry_test.rb +51 -26
- data/test/secret_config_test.rb +35 -4
- data/test/setting_interpolator_test.rb +17 -17
- data/test/test_helper.rb +6 -6
- data/test/utils_test.rb +4 -4
- metadata +7 -3
data/test/providers/file_test.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
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__),
|
7
|
+
File.join(File.dirname(__FILE__), "..", "config", "application.yml")
|
8
8
|
end
|
9
9
|
|
10
10
|
let :path do
|
@@ -27,8 +27,8 @@ module Providers
|
|
27
27
|
}
|
28
28
|
end
|
29
29
|
|
30
|
-
describe
|
31
|
-
it
|
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 }
|
data/test/providers/ssm_test.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
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__),
|
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" => "
|
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[
|
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
|
37
|
-
|
38
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
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)
|
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
|
data/test/registry_test.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
2
|
-
require
|
1
|
+
require_relative "test_helper"
|
2
|
+
require "socket"
|
3
3
|
|
4
4
|
class RegistryTest < Minitest::Test
|
5
5
|
describe SecretConfig::Registry do
|
6
6
|
let :file_name do
|
7
|
-
File.join(File.dirname(__FILE__),
|
7
|
+
File.join(File.dirname(__FILE__), "config", "application.yml")
|
8
8
|
end
|
9
9
|
|
10
10
|
let :path do
|
@@ -28,97 +28,122 @@ class RegistryTest < Minitest::Test
|
|
28
28
|
"/test/my_application/mysql/password" => "secret_configrules",
|
29
29
|
"/test/my_application/mysql/username" => "secret_config",
|
30
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",
|
31
35
|
"/test/my_application/secrets/secret_key_base" => "somereallylongteststring",
|
32
36
|
"/test/my_application/symmetric_encryption/key" => "QUJDREVGMTIzNDU2Nzg5MEFCQ0RFRjEyMzQ1Njc4OTA=",
|
33
|
-
"/test/my_application/symmetric_encryption/version" => 2,
|
37
|
+
"/test/my_application/symmetric_encryption/version" => "2",
|
34
38
|
"/test/my_application/symmetric_encryption/iv" => "QUJDREVGMTIzNDU2Nzg5MA=="
|
35
39
|
}
|
36
40
|
end
|
37
41
|
|
38
|
-
describe
|
39
|
-
it
|
42
|
+
describe "#configuration" do
|
43
|
+
it "returns a copy of the config" do
|
40
44
|
assert_equal "127.0.0.1", registry.configuration.dig("mysql", "host")
|
41
45
|
end
|
42
46
|
|
43
|
-
it
|
47
|
+
it "filters passwords" do
|
44
48
|
assert_equal SecretConfig::FILTERED, registry.configuration.dig("mysql", "password")
|
45
49
|
end
|
46
50
|
|
47
|
-
it
|
51
|
+
it "filters key" do
|
48
52
|
assert_equal SecretConfig::FILTERED, registry.configuration.dig("symmetric_encryption", "key")
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
|
-
describe
|
53
|
-
it
|
54
|
-
expected.each_pair do |key,
|
56
|
+
describe "#key?" do
|
57
|
+
it "has key" do
|
58
|
+
expected.each_pair do |key, _value|
|
55
59
|
key = key.sub("#{path}/", "")
|
56
60
|
assert registry.key?(key), "Path: #{key}"
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
60
|
-
it
|
64
|
+
it "returns false with missing relative key" do
|
61
65
|
refute registry.key?("invalid/path")
|
62
66
|
end
|
63
67
|
|
64
|
-
it
|
68
|
+
it "returns nil with missing full key" do
|
65
69
|
refute registry.key?("/test/invalid/path")
|
66
70
|
end
|
67
71
|
end
|
68
72
|
|
69
|
-
describe
|
70
|
-
it
|
73
|
+
describe "#[]" do
|
74
|
+
it "returns values" do
|
71
75
|
expected.each_pair do |key, value|
|
72
76
|
key = key.sub("#{path}/", "")
|
73
77
|
assert_equal value, registry[key], "Path: #{key}"
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
it
|
81
|
+
it "returns nil with missing relative key" do
|
78
82
|
assert_nil registry["invalid/path"]
|
79
83
|
end
|
80
84
|
|
81
|
-
it
|
85
|
+
it "returns nil with missing full key" do
|
82
86
|
assert_nil registry["/test/invalid/path"]
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
86
|
-
describe
|
87
|
-
it
|
90
|
+
describe "#fetch" do
|
91
|
+
it "returns values" do
|
88
92
|
expected.each_pair do |key, value|
|
89
93
|
key = key.sub("#{path}/", "")
|
90
94
|
assert_equal value, registry.fetch(key), "Path: #{key}"
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
94
|
-
it
|
98
|
+
it "exception missing relative key" do
|
95
99
|
assert_raises SecretConfig::MissingMandatoryKey do
|
96
100
|
registry.fetch("invalid/path")
|
97
101
|
end
|
98
102
|
end
|
99
103
|
|
100
|
-
it
|
104
|
+
it "returns nil with missing full key" do
|
101
105
|
assert_raises SecretConfig::MissingMandatoryKey do
|
102
106
|
registry.fetch("/test/invalid/path")
|
103
107
|
end
|
104
108
|
end
|
105
109
|
|
106
|
-
it
|
110
|
+
it "returns default with missing key" do
|
107
111
|
assert_equal "default_value", registry.fetch("/test/invalid/path", default: "default_value")
|
108
112
|
end
|
109
113
|
|
110
|
-
it
|
114
|
+
it "returns default with false value" do
|
111
115
|
assert_equal false, registry.fetch("/test/invalid/path", default: false, type: :boolean)
|
112
116
|
end
|
113
117
|
|
114
|
-
it
|
118
|
+
it "converts to integer" do
|
115
119
|
assert_equal 2, registry.fetch("symmetric_encryption/version", type: :integer)
|
116
120
|
end
|
117
121
|
|
118
|
-
|
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
|
+
end
|
143
|
+
|
144
|
+
it "decodes Base 64" do
|
119
145
|
assert_equal "ABCDEF1234567890ABCDEF1234567890", registry.fetch("symmetric_encryption/key", encoding: :base64)
|
120
146
|
end
|
121
147
|
end
|
122
148
|
end
|
123
149
|
end
|
124
|
-
|
data/test/secret_config_test.rb
CHANGED
@@ -15,19 +15,19 @@ class SecretConfigTest < Minitest::Test
|
|
15
15
|
SecretConfig.use :file, path: path, file_name: file_name
|
16
16
|
end
|
17
17
|
|
18
|
-
describe "
|
18
|
+
describe ".configuration" do
|
19
19
|
it "returns a copy of the config" do
|
20
20
|
assert_equal "127.0.0.1", SecretConfig.configuration.dig("mysql", "host")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe "
|
24
|
+
describe ".key?" do
|
25
25
|
it "has key" do
|
26
26
|
assert SecretConfig.key?("mysql/database")
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
describe "
|
30
|
+
describe ".[]" do
|
31
31
|
it "returns values" do
|
32
32
|
assert_equal "secret_config_test", SecretConfig["mysql/database"]
|
33
33
|
end
|
@@ -37,7 +37,7 @@ class SecretConfigTest < Minitest::Test
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
describe "
|
40
|
+
describe ".fetch" do
|
41
41
|
after do
|
42
42
|
ENV["MYSQL_DATABASE"] = nil
|
43
43
|
SecretConfig.check_env_var = true
|
@@ -66,5 +66,36 @@ class SecretConfigTest < Minitest::Test
|
|
66
66
|
assert_equal "secret_config_test", SecretConfig.fetch("mysql/database")
|
67
67
|
end
|
68
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
|
98
|
+
end
|
99
|
+
|
69
100
|
end
|
70
101
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "test_helper"
|
2
2
|
module SecretConfig
|
3
3
|
class SettingInterpolatorTest < Minitest::Test
|
4
4
|
describe SettingInterpolator do
|
5
5
|
let(:interpolator) { SettingInterpolator.new }
|
6
6
|
|
7
|
-
describe
|
7
|
+
describe "#parse" do
|
8
8
|
it "handles good key" do
|
9
9
|
string = "Set a date of %{date} here."
|
10
10
|
expected = string.gsub("%{date}", Date.today.strftime("%Y%m%d"))
|
@@ -30,21 +30,21 @@ module SecretConfig
|
|
30
30
|
end
|
31
31
|
|
32
32
|
describe "#date" do
|
33
|
-
it
|
33
|
+
it "interpolates date only" do
|
34
34
|
string = "%{date}"
|
35
35
|
expected = Date.today.strftime("%Y%m%d")
|
36
36
|
actual = interpolator.parse(string)
|
37
37
|
assert_equal expected, actual, string
|
38
38
|
end
|
39
39
|
|
40
|
-
it
|
40
|
+
it "interpolates date" do
|
41
41
|
string = "Set a date of %{date} here."
|
42
42
|
expected = string.gsub("%{date}", Date.today.strftime("%Y%m%d"))
|
43
43
|
actual = interpolator.parse(string)
|
44
44
|
assert_equal expected, actual, string
|
45
45
|
end
|
46
46
|
|
47
|
-
it
|
47
|
+
it "interpolates date with custom format" do
|
48
48
|
string = "Set a custom %{date:%m%d%Y} here."
|
49
49
|
expected = string.gsub("%{date:%m%d%Y}", Date.today.strftime("%m%d%Y"))
|
50
50
|
actual = interpolator.parse(string)
|
@@ -53,7 +53,7 @@ module SecretConfig
|
|
53
53
|
end
|
54
54
|
|
55
55
|
describe "#time" do
|
56
|
-
it
|
56
|
+
it "interpolates time only" do
|
57
57
|
string = "%{time}"
|
58
58
|
time = Time.now
|
59
59
|
Time.stub(:now, time) do
|
@@ -63,7 +63,7 @@ module SecretConfig
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
it
|
66
|
+
it "interpolates time" do
|
67
67
|
string = "Set a time of %{time} here."
|
68
68
|
time = Time.now
|
69
69
|
Time.stub(:now, time) do
|
@@ -73,7 +73,7 @@ module SecretConfig
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
it
|
76
|
+
it "interpolates time with custom format" do
|
77
77
|
string = "Set a custom time of %{time:%H%M} here."
|
78
78
|
expected = string.gsub("%{time:%H%M}", Time.now.strftime("%H%M"))
|
79
79
|
actual = interpolator.parse(string)
|
@@ -86,20 +86,20 @@ module SecretConfig
|
|
86
86
|
ENV["TEST_SETTING"] = "Secret"
|
87
87
|
end
|
88
88
|
|
89
|
-
it
|
89
|
+
it "fetches existing ENV var" do
|
90
90
|
string = "%{env:TEST_SETTING}"
|
91
91
|
actual = interpolator.parse(string)
|
92
92
|
assert_equal "Secret", actual, string
|
93
93
|
end
|
94
94
|
|
95
|
-
it
|
95
|
+
it "fetches existing ENV var into a larger string" do
|
96
96
|
string = "Hello %{env:TEST_SETTING}. How are you?"
|
97
97
|
actual = interpolator.parse(string)
|
98
98
|
expected = string.gsub("%{env:TEST_SETTING}", "Secret")
|
99
99
|
assert_equal expected, actual, string
|
100
100
|
end
|
101
101
|
|
102
|
-
it
|
102
|
+
it "handles missing ENV var" do
|
103
103
|
string = "%{env:OTHER_TEST_SETTING}"
|
104
104
|
actual = interpolator.parse(string)
|
105
105
|
assert_equal "", actual, string
|
@@ -107,21 +107,21 @@ module SecretConfig
|
|
107
107
|
end
|
108
108
|
|
109
109
|
describe "#hostname" do
|
110
|
-
it
|
110
|
+
it "returns hostname" do
|
111
111
|
string = "%{hostname}"
|
112
112
|
actual = interpolator.parse(string)
|
113
113
|
assert_equal Socket.gethostname, actual, string
|
114
114
|
end
|
115
115
|
|
116
|
-
it
|
116
|
+
it "returns short hostname" do
|
117
117
|
string = "%{hostname:short}"
|
118
118
|
actual = interpolator.parse(string)
|
119
|
-
assert_equal Socket.gethostname.split(
|
119
|
+
assert_equal Socket.gethostname.split(".")[0], actual, string
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
123
|
describe "#pid" do
|
124
|
-
it
|
124
|
+
it "returns process id" do
|
125
125
|
string = "%{pid}"
|
126
126
|
actual = interpolator.parse(string)
|
127
127
|
assert_equal $$.to_s, actual, string
|
@@ -129,7 +129,7 @@ module SecretConfig
|
|
129
129
|
end
|
130
130
|
|
131
131
|
describe "#random" do
|
132
|
-
it
|
132
|
+
it "interpolates random 32 byte string" do
|
133
133
|
string = "%{random}"
|
134
134
|
random = SecureRandom.urlsafe_base64(32)
|
135
135
|
SecureRandom.stub(:urlsafe_base64, random) do
|
@@ -138,7 +138,7 @@ module SecretConfig
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
-
it
|
141
|
+
it "interpolates custom length random string" do
|
142
142
|
string = "%{random:64}"
|
143
143
|
random = SecureRandom.urlsafe_base64(64)
|
144
144
|
SecureRandom.stub(:urlsafe_base64, random) do
|