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