conjur-api 5.3.8.pre.319 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +10 -0
  3. data/.dockerignore +1 -0
  4. data/.github/CODEOWNERS +10 -0
  5. data/.gitignore +32 -0
  6. data/.gitleaks.toml +219 -0
  7. data/.overcommit.yml +16 -0
  8. data/.project +18 -0
  9. data/.rubocop.yml +3 -0
  10. data/.rubocop_settings.yml +86 -0
  11. data/.rubocop_todo.yml +709 -0
  12. data/.yardopts +1 -0
  13. data/CHANGELOG.md +448 -0
  14. data/CONTRIBUTING.md +138 -0
  15. data/Dockerfile +16 -0
  16. data/Gemfile +7 -0
  17. data/Jenkinsfile +136 -0
  18. data/LICENSE +202 -0
  19. data/README.md +162 -0
  20. data/Rakefile +47 -0
  21. data/SECURITY.md +42 -0
  22. data/VERSION +1 -1
  23. data/bin/parse-changelog.sh +12 -0
  24. data/ci/configure_v4.sh +12 -0
  25. data/ci/configure_v5.sh +19 -0
  26. data/ci/oauth/keycloak/create_client +18 -0
  27. data/ci/oauth/keycloak/create_user +21 -0
  28. data/ci/oauth/keycloak/fetch_certificate +18 -0
  29. data/ci/oauth/keycloak/keycloak_functions.sh +71 -0
  30. data/ci/oauth/keycloak/standalone.xml +578 -0
  31. data/ci/oauth/keycloak/wait_for_server +56 -0
  32. data/ci/submit-coverage +36 -0
  33. data/conjur-api.gemspec +41 -0
  34. data/dev/Dockerfile.dev +12 -0
  35. data/dev/docker-compose.yml +56 -0
  36. data/dev/start +22 -0
  37. data/dev/stop +5 -0
  38. data/docker-compose.yml +98 -0
  39. data/example/demo_v4.rb +49 -0
  40. data/example/demo_v5.rb +57 -0
  41. data/features/authenticators.feature +41 -0
  42. data/features/authn.feature +14 -0
  43. data/features/authn_local.feature +32 -0
  44. data/features/exists.feature +37 -0
  45. data/features/group.feature +11 -0
  46. data/features/host.feature +50 -0
  47. data/features/host_factory_create_host.feature +28 -0
  48. data/features/host_factory_token.feature +63 -0
  49. data/features/load_policy.feature +61 -0
  50. data/features/members.feature +51 -0
  51. data/features/new_api.feature +36 -0
  52. data/features/permitted.feature +70 -0
  53. data/features/permitted_roles.feature +30 -0
  54. data/features/public_keys.feature +11 -0
  55. data/features/resource_fields.feature +53 -0
  56. data/features/role_fields.feature +15 -0
  57. data/features/rotate_api_key.feature +13 -0
  58. data/features/step_definitions/api_steps.rb +52 -0
  59. data/features/step_definitions/policy_steps.rb +134 -0
  60. data/features/step_definitions/result_steps.rb +11 -0
  61. data/features/support/env.rb +19 -0
  62. data/features/support/hooks.rb +3 -0
  63. data/features/support/world.rb +12 -0
  64. data/features/update_password.feature +14 -0
  65. data/features/user.feature +58 -0
  66. data/features/variable_fields.feature +20 -0
  67. data/features/variable_value.feature +60 -0
  68. data/features_v4/authn_local.feature +27 -0
  69. data/features_v4/exists.feature +29 -0
  70. data/features_v4/host.feature +18 -0
  71. data/features_v4/host_factory_token.feature +49 -0
  72. data/features_v4/members.feature +39 -0
  73. data/features_v4/permitted.feature +15 -0
  74. data/features_v4/permitted_roles.feature +8 -0
  75. data/features_v4/resource_fields.feature +47 -0
  76. data/features_v4/rotate_api_key.feature +13 -0
  77. data/features_v4/step_definitions/api_steps.rb +17 -0
  78. data/features_v4/step_definitions/result_steps.rb +3 -0
  79. data/features_v4/support/env.rb +23 -0
  80. data/features_v4/support/policy.yml +34 -0
  81. data/features_v4/support/world.rb +12 -0
  82. data/features_v4/variable_fields.feature +11 -0
  83. data/features_v4/variable_value.feature +54 -0
  84. data/lib/conjur/acts_as_resource.rb +123 -0
  85. data/lib/conjur/acts_as_role.rb +142 -0
  86. data/lib/conjur/acts_as_rolsource.rb +32 -0
  87. data/lib/conjur/acts_as_user.rb +68 -0
  88. data/lib/conjur/api/authenticators.rb +43 -0
  89. data/lib/conjur/api/authn.rb +144 -0
  90. data/lib/conjur/api/host_factories.rb +71 -0
  91. data/lib/conjur/api/ldap_sync.rb +38 -0
  92. data/lib/conjur/api/policies.rb +56 -0
  93. data/lib/conjur/api/pubkeys.rb +53 -0
  94. data/lib/conjur/api/resources.rb +109 -0
  95. data/lib/conjur/api/roles.rb +98 -0
  96. data/lib/conjur/api/router/v4.rb +206 -0
  97. data/lib/conjur/api/router/v5.rb +269 -0
  98. data/lib/conjur/api/variables.rb +59 -0
  99. data/lib/conjur/api.rb +105 -0
  100. data/lib/conjur/base.rb +355 -0
  101. data/lib/conjur/base_object.rb +57 -0
  102. data/lib/conjur/build_object.rb +47 -0
  103. data/lib/conjur/cache.rb +26 -0
  104. data/lib/conjur/cert_utils.rb +63 -0
  105. data/lib/conjur/cidr.rb +71 -0
  106. data/lib/conjur/configuration.rb +460 -0
  107. data/lib/conjur/escape.rb +129 -0
  108. data/lib/conjur/exceptions.rb +4 -0
  109. data/lib/conjur/group.rb +41 -0
  110. data/lib/conjur/has_attributes.rb +98 -0
  111. data/lib/conjur/host.rb +27 -0
  112. data/lib/conjur/host_factory.rb +75 -0
  113. data/lib/conjur/host_factory_token.rb +78 -0
  114. data/lib/conjur/id.rb +71 -0
  115. data/lib/conjur/layer.rb +9 -0
  116. data/lib/conjur/log.rb +72 -0
  117. data/lib/conjur/log_source.rb +60 -0
  118. data/lib/conjur/policy.rb +34 -0
  119. data/lib/conjur/policy_load_result.rb +61 -0
  120. data/lib/conjur/query_string.rb +12 -0
  121. data/lib/conjur/resource.rb +29 -0
  122. data/lib/conjur/role.rb +29 -0
  123. data/lib/conjur/role_grant.rb +85 -0
  124. data/lib/conjur/routing.rb +29 -0
  125. data/lib/conjur/user.rb +40 -0
  126. data/lib/conjur/variable.rb +208 -0
  127. data/lib/conjur/webservice.rb +30 -0
  128. data/lib/conjur-api/version.rb +24 -0
  129. data/lib/conjur-api.rb +2 -0
  130. data/publish.sh +5 -0
  131. data/spec/api/host_factories_spec.rb +34 -0
  132. data/spec/api_spec.rb +254 -0
  133. data/spec/base_object_spec.rb +13 -0
  134. data/spec/cert_utils_spec.rb +173 -0
  135. data/spec/cidr_spec.rb +34 -0
  136. data/spec/configuration_spec.rb +330 -0
  137. data/spec/has_attributes_spec.rb +63 -0
  138. data/spec/helpers/errors_matcher.rb +34 -0
  139. data/spec/helpers/request_helpers.rb +10 -0
  140. data/spec/id_spec.rb +29 -0
  141. data/spec/ldap_sync_spec.rb +21 -0
  142. data/spec/log_source_spec.rb +13 -0
  143. data/spec/log_spec.rb +42 -0
  144. data/spec/roles_spec.rb +24 -0
  145. data/spec/spec_helper.rb +113 -0
  146. data/spec/ssl_spec.rb +109 -0
  147. data/spec/uri_escape_spec.rb +21 -0
  148. data/test.sh +76 -0
  149. data/tmp/.keep +0 -0
  150. metadata +196 -5
data/spec/api_spec.rb ADDED
@@ -0,0 +1,254 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/spec_helpers'
3
+
4
+ describe Conjur::API do
5
+
6
+ let(:account) { 'api-spec-acount' }
7
+ let(:remote_ip) { nil }
8
+ before { allow(Conjur.configuration).to receive_messages account: account }
9
+
10
+ shared_context "logged in", logged_in: true do
11
+ let(:login) { "bob" }
12
+ let(:token) { { 'data' => login, 'timestamp' => Time.now.to_s } }
13
+ subject(:api) { Conjur::API.new_from_token(token, remote_ip: remote_ip) }
14
+ end
15
+
16
+ shared_context "logged in with an API key", logged_in: :api_key do
17
+ include_context "logged in"
18
+ let(:api_key) { "theapikey" }
19
+ subject(:api) { Conjur::API.new_from_key(login, api_key, account: account ,remote_ip: remote_ip) }
20
+ end
21
+
22
+ shared_context "logged in with a token file", logged_in: :token_file do
23
+ include FakeFS::SpecHelpers
24
+ include_context "logged in"
25
+ let(:token_file) { "token_file" }
26
+ subject(:api) { Conjur::API.new_from_token_file(token_file, remote_ip: remote_ip) }
27
+ end
28
+
29
+ def time_travel delta
30
+ allow(api.authenticator).to receive(:gettime).and_wrap_original do |m|
31
+ m[] + delta
32
+ end
33
+ allow(api.authenticator).to receive(:monotonic_time).and_wrap_original do |m|
34
+ m[] + delta
35
+ end
36
+ allow(Time).to receive(:now).and_wrap_original do |m|
37
+ m[] + delta
38
+ end
39
+ end
40
+
41
+ describe '#token' do
42
+ context 'with token file available', logged_in: :token_file do
43
+ def write_token token
44
+ File.write token_file, JSON.generate(token)
45
+ end
46
+
47
+ before do
48
+ write_token token
49
+ end
50
+
51
+ it "reads the file to get a token" do
52
+ expect(api.instance_variable_get("@token")).to eq(nil)
53
+ expect(api.token).to eq(token)
54
+ expect(api.credentials).to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login })
55
+ end
56
+
57
+ context "after expiration" do
58
+ it 'it reads a new token' do
59
+ expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
60
+
61
+ time_travel 6.minutes
62
+ new_token = token.merge "timestamp" => Time.now.to_s
63
+ write_token new_token
64
+
65
+ expect(api.token).to eq(new_token)
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'with API key available', logged_in: :api_key do
71
+ it "authenticates to get a token" do
72
+ expect(Conjur::API).to receive(:authenticate).with(login, api_key, account: account).and_return token
73
+
74
+ expect(api.instance_variable_get("@token")).to eq(nil)
75
+ expect(api.token).to eq(token)
76
+ expect(api.credentials).to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login })
77
+ end
78
+
79
+ context "after expiration" do
80
+
81
+ shared_examples "it gets a new token" do
82
+ it 'by refreshing' do
83
+ allow(Conjur::API).to receive(:authenticate).with(login, api_key, account: account).and_return token
84
+ expect(Time.parse(api.token['timestamp'])).to be_within(5.seconds).of(Time.now)
85
+
86
+ time_travel 6.minutes
87
+ new_token = token.merge "timestamp" => Time.now.to_s
88
+
89
+ expect(Conjur::API).to receive(:authenticate).with(login, api_key, account: account).and_return new_token
90
+ expect(api.token).to eq(new_token)
91
+ end
92
+ end
93
+
94
+ it_should_behave_like "it gets a new token"
95
+ end
96
+ end
97
+
98
+ context 'with no API key available', logged_in: true do
99
+ it "returns the token used to create it" do
100
+ expect(api.token).to eq token
101
+ end
102
+
103
+ it "doesn't try to refresh an old token" do
104
+ expect(Conjur::API).not_to receive :authenticate
105
+ api.token # vivify
106
+ time_travel 6.minutes
107
+ expect { api.token }.not_to raise_error
108
+ end
109
+ end
110
+ end
111
+
112
+ context "credential handling", logged_in: true do
113
+ context "from token" do
114
+ describe '#credentials' do
115
+ subject { super().credentials }
116
+ it { is_expected.to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"" }, username: login }) }
117
+ end
118
+
119
+ context "with remote_ip" do
120
+ let(:remote_ip) { "66.0.0.1" }
121
+ describe '#credentials' do
122
+ subject { super().credentials }
123
+ it { is_expected.to eq({ headers: { authorization: "Token token=\"#{Base64.strict_encode64(token.to_json)}\"", :x_forwarded_for=>"66.0.0.1" }, username: login }) }
124
+ end
125
+ end
126
+ end
127
+
128
+ context "from logged-in RestClient::Resource" do
129
+ let (:authz_header) { %Q{Token token="#{token_encoded}"} }
130
+ let (:priv_header) { nil }
131
+ let (:forwarded_for_header) { nil }
132
+ let (:audit_roles_header) { nil }
133
+ let (:audit_resources_header) { nil }
134
+ let (:username) { 'bob' }
135
+ subject { resource.conjur_api }
136
+
137
+ shared_examples "it can clone itself" do
138
+ it "has the authz header" do
139
+ expect(subject.credentials[:headers][:authorization]).to eq(authz_header)
140
+ end
141
+ it "has the username" do
142
+ expect(subject.credentials[:username]).to eq(username)
143
+ end
144
+ end
145
+
146
+ let(:token_encoded) { Base64.strict_encode64(token.to_json) }
147
+ let(:base_headers) { { authorization: authz_header } }
148
+ let(:headers) { base_headers }
149
+ let(:resource) { RestClient::Resource.new("http://example.com", { headers: headers })}
150
+ context 'basic functioning' do
151
+ it_behaves_like 'it can clone itself'
152
+ end
153
+
154
+ context "forwarded for" do
155
+ let(:forwarded_for_header) { "66.0.0.1" }
156
+ let(:headers) { base_headers.merge(x_forwarded_for: forwarded_for_header) }
157
+ it_behaves_like 'it can clone itself'
158
+ end
159
+ end
160
+ end
161
+
162
+ describe "#role_from_username", logged_in: true do
163
+ it "returns a user role when username is plain" do
164
+ expect(Conjur::API.role_from_username(api, "plain-username", account).id).to eq("#{account}:user:plain-username")
165
+ end
166
+
167
+ it "returns an appropriate role kind when username is qualified" do
168
+ expect(Conjur::API.role_from_username(api, "host/foo/bar", account).id).to eq("#{account}:host:foo/bar")
169
+ end
170
+ end
171
+
172
+ describe "#username" do
173
+ let(:jwt_payload) do
174
+ 'eyJzdWIiOiJ1c2VyLTlhYjBiYmZiOWJlNjA5Yzk2ZjUyN2Y1YiIsImlhdCI6MTYwMzQ5MDA4MH0='
175
+ end
176
+
177
+ let(:jwt_header) do
178
+ 'eyJhbGciOiJjb25qdXIub3JnL3Nsb3NpbG8vdjIiLCJraWQiOiI2MWZjOGRiZDM4MjA4NDll' \
179
+ 'ZDI4YTZhYTAwMzFjNjM5MjkxZjJmMDQzNDVjYTU0MWI5NzUxMGQ5NjkyM2I3NDlmIn0='
180
+ end
181
+
182
+ let(:conjur_token) do
183
+ {
184
+ 'data' => 'conjur-user-1234',
185
+ 'timestamp' => Time.now.to_s
186
+ }
187
+ end
188
+
189
+ let(:jwt_token) do
190
+ {
191
+ 'protected' => jwt_header,
192
+ 'payload' => jwt_payload,
193
+ }
194
+ end
195
+
196
+ it "can correctly extract the username from old Conjur token" do
197
+ expect(Conjur::API.new_from_token(conjur_token).username).to(
198
+ eq('conjur-user-1234')
199
+ )
200
+ end
201
+
202
+ context 'when using JWT token' do
203
+ it "can correctly extract username" do
204
+ expect(Conjur::API.new_from_token(jwt_token).username).to(
205
+ eq('user-9ab0bbfb9be609c96f527f5b')
206
+ )
207
+ end
208
+
209
+ it "returns nil when JWT token has no payload field" do
210
+ no_payload_jwt_token = { 'protected' => jwt_header }
211
+ expect(Conjur::API.new_from_token(no_payload_jwt_token).username).to be_nil
212
+ end
213
+
214
+ it "returns nil when JWT token has no 'sub' field in payload" do
215
+ no_sub_token = { 'payload' => 'eyJpYXQiOjE2MDM0OTAwODB9' }
216
+ expect(Conjur::API.new_from_token(no_sub_token).username).to be_nil
217
+ end
218
+ end
219
+ end
220
+
221
+ describe "#current_role", logged_in: true do
222
+ context "when logged in as user" do
223
+ let(:login) { 'joerandom' }
224
+ it "returns a user role" do
225
+ expect(api.current_role(account).id).to eq("#{account}:user:joerandom")
226
+ end
227
+ end
228
+
229
+ context "when logged in as host" do
230
+ let(:host) { "somehost" }
231
+ let(:login) { "host/#{host}" }
232
+ it "returns a host role" do
233
+ expect(api.current_role(account).id).to eq("#{account}:host:somehost")
234
+ end
235
+ end
236
+ end
237
+
238
+ describe 'url escapes' do
239
+ let(:urls){[
240
+ 'foo/bar@baz',
241
+ '/test/some group with spaces'
242
+ ]}
243
+
244
+ describe '#fully_escape' do
245
+ let(:expected){[
246
+ 'foo%2Fbar%40baz',
247
+ '%2Ftest%2Fsome%20group%20with%20spaces'
248
+ ]}
249
+ it 'escapes the urls correctly' do
250
+ expect(urls.map{|u| Conjur::API.fully_escape u}).to eq(expected)
251
+ end
252
+ end
253
+ end
254
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Conjur::BaseObject do
6
+
7
+ it "returns custom string for #inspect" do
8
+ id_str = 'foo:bar:baz'
9
+ base_obj = Conjur::BaseObject.new(Conjur::Id.new(id_str), { username: 'foo' })
10
+ expect(base_obj.inspect).to include("id='#{id_str}'")
11
+ expect(base_obj.inspect).to include(Conjur::BaseObject.name)
12
+ end
13
+ end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ describe Conjur::CertUtils do
4
+ describe '.parse_certs' do
5
+ let(:cert1_raw) do
6
+ """-----BEGIN CERTIFICATE-----
7
+ MIIDPjCCAiagAwIBAgIVAKW1gdmOFrXt6xB0iQmYQ4z8Pf+kMA0GCSqGSIb3DQEB
8
+ CwUAMD0xETAPBgNVBAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDAS
9
+ BgNVBAMTC2N1a2UtbWFzdGVyMB4XDTE1MTAwNzE2MzAwNloXDTI1MTAwNDE2MzAw
10
+ NlowFjEUMBIGA1UEAwwLY3VrZS1tYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
11
+ DwAwggEKAoIBAQC9e8bGIHOLOypKA4lsLcAOcDLAq+ICuVxn9Vg0No0m32Ok/K7G
12
+ uEGtlC8RidObntblUwqdX2uP7mqAQm19j78UTl1KT97vMmmFrpVZ7oQvEm1FUq3t
13
+ FBmJglthJrSbpdZjLf7a7eL1NnunkfBdI1DK9QL9ndMjNwZNFbXhld4fC5zuSr/L
14
+ PxawSzTEsoTaB0Nw0DdRowaZgrPxc0hQsrj9OF20gTIJIYO7ctZzE/JJchmBzgI4
15
+ CdfAYg7zNS+0oc0ylV0CWMerQtLICI6BtiQ482bCuGYJ00NlDcdjd3w+A2cj7PrH
16
+ wH5UhtORL5Q6i9EfGGUCDbmfpiVD9Bd3ukbXAgMBAAGjXDBaMA4GA1UdDwEB/wQE
17
+ AwIFoDAdBgNVHQ4EFgQU2jmj7l5rSw0yVb/vlWAYkK/YBwkwKQYDVR0RBCIwIIIL
18
+ Y3VrZS1tYXN0ZXKCCWxvY2FsaG9zdIIGY29uanVyMA0GCSqGSIb3DQEBCwUAA4IB
19
+ AQBCepy6If67+sjuVnT9NGBmjnVaLa11kgGNEB1BZQnvCy0IN7gpLpshoZevxYDR
20
+ 3DnPAetQiZ70CSmCwjL4x6AVxQy59rRj0Awl9E1dgFTYI3JxxgLsI9ePdIRVEPnH
21
+ dhXqPY5ZIZhvdHlLStjsXX7laaclEtMeWfSzxe4AmP/Sm/er4ks0gvLQU6/XJNIu
22
+ RnRH59ZB1mZMsIv9Ii790nnioYFR54JmQu1JsIib77ZdSXIJmxAtraJSTLcZbU1E
23
+ +SM3XCE423Xols7onyluMYDy3MCUTFwoVMRBcRWCAk5gcv6XvZDfLi6Zwdne6x3Y
24
+ bGenr4vsPuSFsycM03/EcQDT
25
+ -----END CERTIFICATE-----
26
+ """
27
+ end
28
+ let(:cert2_raw) do
29
+ """-----BEGIN CERTIFICATE-----
30
+ MIIDhzCCAm+gAwIBAgIJAJnsrJ1+j9MhMA0GCSqGSIb3DQEBCwUAMD0xETAPBgNV
31
+ BAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDASBgNVBAMTC2N1a2Ut
32
+ bWFzdGVyMB4XDTE1MTAwNzE2MzAwM1oXDTI1MTAwNDE2MzAwM1owPTERMA8GA1UE
33
+ ChMIY3VjdW1iZXIxEjAQBgNVBAsTCUNvbmp1ciBDQTEUMBIGA1UEAxMLY3VrZS1t
34
+ YXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsuZ06Ld4JDhxZ
35
+ FcxKVxu7MTjXVv6W8pI7qFKmgr39aNqmDpKYJ1H9aM+r9zaTAeithpM4wJpVswkJ
36
+ d0RSuKdm1LOx11yHLyZ1OvlPHFhsVWdZIQZ6R9srhPYBUCMem4sHR5IAcBBX+HkR
37
+ 35gaPYUl1uFV/9zCniekt92Kdta+it1WL7XinXTBURlhDawiD/kv1C9x6dICEJVe
38
+ IT/jRohmqHAoM/JSOQTthaDli3Qvu5K8XAx8UXvWVmv3eStZFVDbC4ZEueRd9KAe
39
+ 4IZ5FxdpFYkPBgt2lBYeydYKRShyYrDKye1uJBDkeplNaYW4cS4mOhYuRkdKn7MH
40
+ uY/xb1lFAgMBAAGjgYkwgYYwKQYDVR0RBCIwIIILY3VrZS1tYXN0ZXKCCWxvY2Fs
41
+ aG9zdIIGY29uanVyMB0GA1UdDgQWBBRHpGF7aQbHdORYgQKDC2hV6NzEKzAfBgNV
42
+ HSMEGDAWgBRHpGF7aQbHdORYgQKDC2hV6NzEKzAMBgNVHRMEBTADAQH/MAsGA1Ud
43
+ DwQEAwIB5jANBgkqhkiG9w0BAQsFAAOCAQEAGZT9Wek1hYluIVaxu03wSKCKIJ4p
44
+ KxTHw+mLDapg1y9t3Fa/5IQQK0Bx0xGU2qWiQKjda3vdFPJWO6l6XJvsUY5Nwtm5
45
+ Gcsk8l3L/zWCrjrFTH3TdVad5E+DTwVhThelmEjw68AyM+WuOL61j0MItd9mLW74
46
+ Lv2zouj9nQBdnUBHWQ0EL/9d5cfaCVu/bFlDfYt7Yj0IzXCuaWZfJeHodU1hmqVX
47
+ BvYRjnTB2LSxfmSnkrCeFPmhE11bWVtsLIdrGIgtEMX0/s9xg58QuNnva1U3pJsW
48
+ RjvSxre4Xg2qlI9Laybb4oZ4g6DI8hRbL0VdFAsveg6SXg2RxgJcXeJUFw==
49
+ -----END CERTIFICATE-----
50
+ """
51
+ end
52
+
53
+ let(:cert1) { OpenSSL::X509::Certificate.new cert1_raw }
54
+ let(:cert2) { OpenSSL::X509::Certificate.new cert2_raw }
55
+
56
+ it 'parses a certificate' do
57
+ expect(Conjur::CertUtils.parse_certs(cert1_raw).map(&:to_der))\
58
+ .to eq [cert1.to_der]
59
+ end
60
+
61
+ it 'parses two certificates' do
62
+ expect(Conjur::CertUtils.parse_certs(cert1_raw + cert2_raw).map(&:to_der))\
63
+ .to eq [cert1.to_der, cert2.to_der]
64
+ end
65
+
66
+ it 'parses the certificate correctly even if the whitespace is wrong' do
67
+ bad_whitespace = cert1_raw.gsub "\n", " "
68
+ expect(Conjur::CertUtils.parse_certs(bad_whitespace).map(&:to_der))\
69
+ .to eq [cert1.to_der]
70
+ end
71
+
72
+ it 'shows a bad cert in error message' do
73
+ bad_cert = "-----BEGIN CERTIFICATE-----\nfoo\n-----END CERTIFICATE-----\n"
74
+ expect do
75
+ Conjur::CertUtils.parse_certs(bad_cert)
76
+ end.to raise_error(OpenSSL::X509::CertificateError) do |exn|
77
+ expect(exn.message).to include bad_cert
78
+ end
79
+ end
80
+ end
81
+
82
+ describe '.add_chained_cert' do
83
+ let(:one_certificate_chain) do
84
+ """-----BEGIN CERTIFICATE-----
85
+ MIIDPjCCAiagAwIBAgIVAKW1gdmOFrXt6xB0iQmYQ4z8Pf+kMA0GCSqGSIb3DQEB
86
+ CwUAMD0xETAPBgNVBAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDAS
87
+ BgNVBAMTC2N1a2UtbWFzdGVyMB4XDTE1MTAwNzE2MzAwNloXDTI1MTAwNDE2MzAw
88
+ NlowFjEUMBIGA1UEAwwLY3VrZS1tYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
89
+ DwAwggEKAoIBAQC9e8bGIHOLOypKA4lsLcAOcDLAq+ICuVxn9Vg0No0m32Ok/K7G
90
+ uEGtlC8RidObntblUwqdX2uP7mqAQm19j78UTl1KT97vMmmFrpVZ7oQvEm1FUq3t
91
+ FBmJglthJrSbpdZjLf7a7eL1NnunkfBdI1DK9QL9ndMjNwZNFbXhld4fC5zuSr/L
92
+ PxawSzTEsoTaB0Nw0DdRowaZgrPxc0hQsrj9OF20gTIJIYO7ctZzE/JJchmBzgI4
93
+ CdfAYg7zNS+0oc0ylV0CWMerQtLICI6BtiQ482bCuGYJ00NlDcdjd3w+A2cj7PrH
94
+ wH5UhtORL5Q6i9EfGGUCDbmfpiVD9Bd3ukbXAgMBAAGjXDBaMA4GA1UdDwEB/wQE
95
+ AwIFoDAdBgNVHQ4EFgQU2jmj7l5rSw0yVb/vlWAYkK/YBwkwKQYDVR0RBCIwIIIL
96
+ Y3VrZS1tYXN0ZXKCCWxvY2FsaG9zdIIGY29uanVyMA0GCSqGSIb3DQEBCwUAA4IB
97
+ AQBCepy6If67+sjuVnT9NGBmjnVaLa11kgGNEB1BZQnvCy0IN7gpLpshoZevxYDR
98
+ 3DnPAetQiZ70CSmCwjL4x6AVxQy59rRj0Awl9E1dgFTYI3JxxgLsI9ePdIRVEPnH
99
+ dhXqPY5ZIZhvdHlLStjsXX7laaclEtMeWfSzxe4AmP/Sm/er4ks0gvLQU6/XJNIu
100
+ RnRH59ZB1mZMsIv9Ii790nnioYFR54JmQu1JsIib77ZdSXIJmxAtraJSTLcZbU1E
101
+ +SM3XCE423Xols7onyluMYDy3MCUTFwoVMRBcRWCAk5gcv6XvZDfLi6Zwdne6x3Y
102
+ bGenr4vsPuSFsycM03/EcQDT
103
+ -----END CERTIFICATE-----
104
+ """
105
+ end
106
+
107
+ let(:two_certificates_chain) do
108
+ """-----BEGIN CERTIFICATE-----
109
+ MIIDPjCCAiagAwIBAgIVAKW1gdmOFrXt6xB0iQmYQ4z8Pf+kMA0GCSqGSIb3DQEB
110
+ CwUAMD0xETAPBgNVBAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDAS
111
+ BgNVBAMTC2N1a2UtbWFzdGVyMB4XDTE1MTAwNzE2MzAwNloXDTI1MTAwNDE2MzAw
112
+ NlowFjEUMBIGA1UEAwwLY3VrZS1tYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
113
+ DwAwggEKAoIBAQC9e8bGIHOLOypKA4lsLcAOcDLAq+ICuVxn9Vg0No0m32Ok/K7G
114
+ uEGtlC8RidObntblUwqdX2uP7mqAQm19j78UTl1KT97vMmmFrpVZ7oQvEm1FUq3t
115
+ FBmJglthJrSbpdZjLf7a7eL1NnunkfBdI1DK9QL9ndMjNwZNFbXhld4fC5zuSr/L
116
+ PxawSzTEsoTaB0Nw0DdRowaZgrPxc0hQsrj9OF20gTIJIYO7ctZzE/JJchmBzgI4
117
+ CdfAYg7zNS+0oc0ylV0CWMerQtLICI6BtiQ482bCuGYJ00NlDcdjd3w+A2cj7PrH
118
+ wH5UhtORL5Q6i9EfGGUCDbmfpiVD9Bd3ukbXAgMBAAGjXDBaMA4GA1UdDwEB/wQE
119
+ AwIFoDAdBgNVHQ4EFgQU2jmj7l5rSw0yVb/vlWAYkK/YBwkwKQYDVR0RBCIwIIIL
120
+ Y3VrZS1tYXN0ZXKCCWxvY2FsaG9zdIIGY29uanVyMA0GCSqGSIb3DQEBCwUAA4IB
121
+ AQBCepy6If67+sjuVnT9NGBmjnVaLa11kgGNEB1BZQnvCy0IN7gpLpshoZevxYDR
122
+ 3DnPAetQiZ70CSmCwjL4x6AVxQy59rRj0Awl9E1dgFTYI3JxxgLsI9ePdIRVEPnH
123
+ dhXqPY5ZIZhvdHlLStjsXX7laaclEtMeWfSzxe4AmP/Sm/er4ks0gvLQU6/XJNIu
124
+ RnRH59ZB1mZMsIv9Ii790nnioYFR54JmQu1JsIib77ZdSXIJmxAtraJSTLcZbU1E
125
+ +SM3XCE423Xols7onyluMYDy3MCUTFwoVMRBcRWCAk5gcv6XvZDfLi6Zwdne6x3Y
126
+ bGenr4vsPuSFsycM03/EcQDT
127
+ -----END CERTIFICATE-----
128
+ -----BEGIN CERTIFICATE-----
129
+ MIIDhzCCAm+gAwIBAgIJAJnsrJ1+j9MhMA0GCSqGSIb3DQEBCwUAMD0xETAPBgNV
130
+ BAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDASBgNVBAMTC2N1a2Ut
131
+ bWFzdGVyMB4XDTE1MTAwNzE2MzAwM1oXDTI1MTAwNDE2MzAwM1owPTERMA8GA1UE
132
+ ChMIY3VjdW1iZXIxEjAQBgNVBAsTCUNvbmp1ciBDQTEUMBIGA1UEAxMLY3VrZS1t
133
+ YXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsuZ06Ld4JDhxZ
134
+ FcxKVxu7MTjXVv6W8pI7qFKmgr39aNqmDpKYJ1H9aM+r9zaTAeithpM4wJpVswkJ
135
+ d0RSuKdm1LOx11yHLyZ1OvlPHFhsVWdZIQZ6R9srhPYBUCMem4sHR5IAcBBX+HkR
136
+ 35gaPYUl1uFV/9zCniekt92Kdta+it1WL7XinXTBURlhDawiD/kv1C9x6dICEJVe
137
+ IT/jRohmqHAoM/JSOQTthaDli3Qvu5K8XAx8UXvWVmv3eStZFVDbC4ZEueRd9KAe
138
+ 4IZ5FxdpFYkPBgt2lBYeydYKRShyYrDKye1uJBDkeplNaYW4cS4mOhYuRkdKn7MH
139
+ uY/xb1lFAgMBAAGjgYkwgYYwKQYDVR0RBCIwIIILY3VrZS1tYXN0ZXKCCWxvY2Fs
140
+ aG9zdIIGY29uanVyMB0GA1UdDgQWBBRHpGF7aQbHdORYgQKDC2hV6NzEKzAfBgNV
141
+ HSMEGDAWgBRHpGF7aQbHdORYgQKDC2hV6NzEKzAMBgNVHRMEBTADAQH/MAsGA1Ud
142
+ DwQEAwIB5jANBgkqhkiG9w0BAQsFAAOCAQEAGZT9Wek1hYluIVaxu03wSKCKIJ4p
143
+ KxTHw+mLDapg1y9t3Fa/5IQQK0Bx0xGU2qWiQKjda3vdFPJWO6l6XJvsUY5Nwtm5
144
+ Gcsk8l3L/zWCrjrFTH3TdVad5E+DTwVhThelmEjw68AyM+WuOL61j0MItd9mLW74
145
+ Lv2zouj9nQBdnUBHWQ0EL/9d5cfaCVu/bFlDfYt7Yj0IzXCuaWZfJeHodU1hmqVX
146
+ BvYRjnTB2LSxfmSnkrCeFPmhE11bWVtsLIdrGIgtEMX0/s9xg58QuNnva1U3pJsW
147
+ RjvSxre4Xg2qlI9Laybb4oZ4g6DI8hRbL0VdFAsveg6SXg2RxgJcXeJUFw==
148
+ -----END CERTIFICATE-----
149
+ """
150
+ end
151
+
152
+ let(:store){ double('default store') }
153
+
154
+ context 'with one certificate in the chain' do
155
+ subject{ Conjur::CertUtils.add_chained_cert(store, one_certificate_chain) }
156
+
157
+ it 'adds one certificate to the store' do
158
+ expect(store).to receive(:add_cert).once
159
+ expect(subject).to be_truthy
160
+ end
161
+ end
162
+
163
+ context 'with two certificate in the chain' do
164
+ subject{ Conjur::CertUtils.add_chained_cert(store, two_certificates_chain) }
165
+
166
+ it 'adds both certificate to the store' do
167
+ expect(store).to receive(:add_cert).twice
168
+ expect(subject).to be_truthy
169
+ end
170
+ end
171
+
172
+ end
173
+ end
data/spec/cidr_spec.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'conjur/cidr'
2
+
3
+ describe Conjur::CIDR do
4
+ def cidr addr
5
+ Conjur::CIDR.validate addr
6
+ end
7
+
8
+ describe '.validate' do
9
+ it 'rejects malformed addresses' do
10
+ expect { Conjur::CIDR.validate '192.0.2.2/255.255.0.255' }.to raise_error ArgumentError
11
+ expect { Conjur::CIDR.validate '192.0.2.2/0.255.0.0' }.to raise_error ArgumentError
12
+ expect { Conjur::CIDR.validate '192.0.256.2' }.to raise_error ArgumentError
13
+ expect { Conjur::CIDR.validate '::/:ffff:' }.to raise_error ArgumentError
14
+ end
15
+ end
16
+
17
+ describe '#prefixlen' do
18
+ it 'calculates prefix mask length' do
19
+ expected = {
20
+ '0.0.0.0/0' => 0,
21
+ '192.0.2.0/24' => 24,
22
+ '192.0.2.1' => 32,
23
+ '192.0.2.0/255.255.255.0' => 24,
24
+ '10.0.0.0/255.0.0.0' => 8,
25
+ '1234::/42' => 42,
26
+ '1234::/ffff::' => 16,
27
+ '::/::' => 0,
28
+ }
29
+ expected.each do |addr, len|
30
+ expect(Conjur::CIDR.validate(addr).prefixlen).to eq len
31
+ end
32
+ end
33
+ end
34
+ end