hybrid_platforms_conductor 33.3.0 → 33.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +31 -2
  4. data/docs/config_dsl.md +43 -0
  5. data/lib/hybrid_platforms_conductor/bitbucket.rb +134 -90
  6. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +12 -44
  7. data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +9 -31
  8. data/lib/hybrid_platforms_conductor/confluence.rb +93 -88
  9. data/lib/hybrid_platforms_conductor/credentials.rb +112 -95
  10. data/lib/hybrid_platforms_conductor/deployer.rb +2 -2
  11. data/lib/hybrid_platforms_conductor/github.rb +39 -0
  12. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +4 -2
  13. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +3 -1
  14. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/keepass.rb +2 -1
  15. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +3 -1
  16. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -1
  17. data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +4 -1
  18. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +6 -2
  19. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +6 -2
  20. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +3 -1
  21. data/lib/hybrid_platforms_conductor/logger_helpers.rb +7 -1
  22. data/lib/hybrid_platforms_conductor/thycotic.rb +80 -75
  23. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  24. data/spec/hybrid_platforms_conductor_test.rb +6 -0
  25. data/spec/hybrid_platforms_conductor_test/api/credentials_spec.rb +247 -0
  26. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/keepass_spec.rb +280 -319
  27. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +2 -2
  28. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +49 -69
  29. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +29 -39
  30. metadata +18 -2
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'tmpdir'
3
+ require 'webmock/rspec'
3
4
  require 'hybrid_platforms_conductor/config'
4
5
  require 'hybrid_platforms_conductor/platforms_handler'
5
6
  require 'hybrid_platforms_conductor/actions_executor'
@@ -83,6 +84,9 @@ module HybridPlatformsConductorTest
83
84
  # Make sure the tested components are being reset before each test case
84
85
  RSpec.configure do |config|
85
86
  config.before do
87
+ # We allow for connections by default.
88
+ # Tests that need to test specifically connections at a given point call WebMock.disable_net_connect!
89
+ WebMock.allow_net_connect!
86
90
  @actions_executor = nil
87
91
  @cmd_runner = nil
88
92
  @config = nil
@@ -95,6 +99,8 @@ module HybridPlatformsConductorTest
95
99
  ENV.delete 'hpc_platforms'
96
100
  ENV.delete 'hpc_ssh_gateways_conf'
97
101
  ENV.delete 'hpc_ssh_gateway_user'
102
+ ENV.delete 'hpc_user_for_github'
103
+ ENV.delete 'hpc_password_for_github'
98
104
  ENV.delete 'hpc_user_for_proxmox'
99
105
  ENV.delete 'hpc_password_for_proxmox'
100
106
  ENV.delete 'hpc_realm_for_proxmox'
@@ -0,0 +1,247 @@
1
+ describe HybridPlatformsConductor::Credentials do
2
+
3
+ # Create a container class for the credential Mixin to be tested, as a plugin as credentials can be used in any plugin.
4
+ let(:credential_tester_class) do
5
+ Class.new(HybridPlatformsConductor::Plugin) do
6
+ include HybridPlatformsConductor::Credentials
7
+ end
8
+ end
9
+
10
+ # Expect credentials to be as a given user and password
11
+ #
12
+ # Parameters::
13
+ # * *expected_user* (String or nil): The expected user
14
+ # * *expected_password* (String or nil): The expected password
15
+ # * *resource* (String or nil): The resource for which we query the credentials, or nil if none [default: nil]
16
+ def expect_credentials_to_be(expected_user, expected_password, resource: nil)
17
+ creds = {}
18
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
19
+ with_credentials_for(:test_credential, resource: resource) do |user, password|
20
+ creds = {
21
+ user: user,
22
+ # We clone the value as for security reasons it is removed when exiting the block
23
+ password: password.clone
24
+ }
25
+ end
26
+ end
27
+ expect(creds).to eq(
28
+ user: expected_user,
29
+ password: expected_password
30
+ )
31
+ end
32
+
33
+ it 'returns no credentials when they are not set' do
34
+ with_platforms '' do
35
+ # Check that .netrc won't be read
36
+ expect(::Netrc).not_to receive(:read)
37
+ expect_credentials_to_be nil, nil
38
+ end
39
+ end
40
+
41
+ it 'returns credentials taken from environment variables' do
42
+ with_platforms '' do
43
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
44
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
45
+ begin
46
+ # Check that .netrc won't be read
47
+ expect(::Netrc).not_to receive(:read)
48
+ expect_credentials_to_be 'env_test_user', 'env_test_password'
49
+ ensure
50
+ ENV.delete('hpc_user_for_test_credential')
51
+ ENV.delete('hpc_password_for_test_credential')
52
+ end
53
+ end
54
+ end
55
+
56
+ it 'erases the value of the password taken from environment variable after usage' do
57
+ with_platforms '' do
58
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
59
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
60
+ begin
61
+ leaked_password = nil
62
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
63
+ with_credentials_for(:test_credential) do |_user, password|
64
+ leaked_password = password
65
+ end
66
+ end
67
+ expect(leaked_password).to eq 'gotyou!' * 100
68
+ ensure
69
+ ENV.delete('hpc_user_for_test_credential')
70
+ ENV.delete('hpc_password_for_test_credential')
71
+ end
72
+ end
73
+ end
74
+
75
+ it 'returns credentials taken from .netrc when a resource is specified' do
76
+ with_platforms '' do
77
+ expect(::Netrc).to receive(:read) do
78
+ mocked_netrc = instance_double(::Netrc)
79
+ expect(mocked_netrc).to receive(:[]).with('my_domain.com').and_return %w[test_user test_password]
80
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return []
81
+ mocked_netrc
82
+ end
83
+ expect_credentials_to_be 'test_user', 'test_password', resource: 'http://My_Domain.com/path/to/resource'
84
+ end
85
+ end
86
+
87
+ it 'returns credentials taken from .netrc when a non-URL resource is specified' do
88
+ with_platforms '' do
89
+ expect(::Netrc).to receive(:read) do
90
+ mocked_netrc = instance_double(::Netrc)
91
+ expect(mocked_netrc).to receive(:[]).with('This is:not/ a URL!').and_return %w[test_user test_password]
92
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return []
93
+ mocked_netrc
94
+ end
95
+ expect_credentials_to_be 'test_user', 'test_password', resource: 'This is:not/ a URL!'
96
+ end
97
+ end
98
+
99
+ it 'erases the value of the password taken from netrc' do
100
+ with_platforms '' do
101
+ netrc_data = [['mocked_data']]
102
+ expect(::Netrc).to receive(:read) do
103
+ mocked_netrc = instance_double(::Netrc)
104
+ expect(mocked_netrc).to receive(:[]).with('my_domain.com').and_return %w[test_user test_password]
105
+ expect(mocked_netrc).to receive(:instance_variable_get).with(:@data).and_return netrc_data
106
+ mocked_netrc
107
+ end
108
+ leaked_password = nil
109
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
110
+ with_credentials_for(:test_credential, resource: 'http://My_Domain.com/path/to/resource') do |_user, password|
111
+ leaked_password = password
112
+ end
113
+ end
114
+ expect(leaked_password).to eq 'gotyou!' * 100
115
+ expect(netrc_data).to eq [['GotYou!!!' * 100]]
116
+ end
117
+ end
118
+
119
+ it 'returns credentials taken from config' do
120
+ with_platforms(
121
+ <<~'EO_CONFIG'
122
+ credentials_for(:test_credential) do |resource, requester|
123
+ requester.call "user_for_#{resource}", "password_for_#{resource}"
124
+ end
125
+ EO_CONFIG
126
+ ) do
127
+ # Check that netrc is not called when config is used, and that env vars are ignored
128
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
129
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
130
+ begin
131
+ # Check that .netrc won't be read
132
+ expect(::Netrc).not_to receive(:read)
133
+ expect_credentials_to_be 'user_for_', 'password_for_'
134
+ ensure
135
+ ENV.delete('hpc_user_for_test_credential')
136
+ ENV.delete('hpc_password_for_test_credential')
137
+ end
138
+ end
139
+ end
140
+
141
+ it 'returns credentials taken from config for a given resource' do
142
+ with_platforms(
143
+ <<~'EO_CONFIG'
144
+ credentials_for(:test_credential) do |resource, requester|
145
+ requester.call "user_for_#{resource}", "password_for_#{resource}"
146
+ end
147
+ EO_CONFIG
148
+ ) do
149
+ # Check that netrc is not called when config is used, and that env vars are ignored
150
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
151
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
152
+ begin
153
+ # Check that .netrc won't be read
154
+ expect(::Netrc).not_to receive(:read)
155
+ expect_credentials_to_be 'user_for_test_resource', 'password_for_test_resource', resource: 'test_resource'
156
+ ensure
157
+ ENV.delete('hpc_user_for_test_credential')
158
+ ENV.delete('hpc_password_for_test_credential')
159
+ end
160
+ end
161
+ end
162
+
163
+ it 'returns credentials taken from config for a given resource even when they are nil' do
164
+ with_platforms(
165
+ <<~'EO_CONFIG'
166
+ credentials_for(:test_credential) do |resource, requester|
167
+ requester.call nil, nil
168
+ end
169
+ EO_CONFIG
170
+ ) do
171
+ # Check that netrc is not called when config is used, and that env vars are ignored
172
+ ENV['hpc_user_for_test_credential'] = 'env_test_user'
173
+ ENV['hpc_password_for_test_credential'] = 'env_test_password'
174
+ begin
175
+ # Check that .netrc won't be read
176
+ expect(::Netrc).not_to receive(:read)
177
+ expect_credentials_to_be nil, nil, resource: 'test_resource'
178
+ ensure
179
+ ENV.delete('hpc_user_for_test_credential')
180
+ ENV.delete('hpc_password_for_test_credential')
181
+ end
182
+ end
183
+ end
184
+
185
+ it 'returns credentials taken from config after filtering the resource name' do
186
+ with_platforms(
187
+ <<~'EO_CONFIG'
188
+ credentials_for(:test_credential, resource: 'another_resource') do |resource, requester|
189
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
190
+ end
191
+ credentials_for(:test_credential, resource: /test_.*/) do |resource, requester|
192
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
193
+ end
194
+ credentials_for(:test_credential, resource: /_resource/) do |resource, requester|
195
+ requester.call "correct_user_for_#{resource}", "correct_password_for_#{resource}"
196
+ end
197
+ credentials_for(:test_credential, resource: 'test_resource2') do |resource, requester|
198
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
199
+ end
200
+ EO_CONFIG
201
+ ) do
202
+ expect_credentials_to_be 'correct_user_for_test_resource', 'correct_password_for_test_resource', resource: 'test_resource'
203
+ end
204
+ end
205
+
206
+ it 'returns credentials taken from config after filtering the resource name when no resource is given' do
207
+ with_platforms(
208
+ <<~'EO_CONFIG'
209
+ credentials_for(:test_credential, resource: 'another_resource') do |resource, requester|
210
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
211
+ end
212
+ credentials_for(:test_credential, resource: /test_.*/) do |resource, requester|
213
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
214
+ end
215
+ credentials_for(:test_credential) do |resource, requester|
216
+ requester.call "correct_user_for_#{resource}", "correct_password_for_#{resource}"
217
+ end
218
+ credentials_for(:test_credential, resource: /_resource/) do |resource, requester|
219
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
220
+ end
221
+ credentials_for(:test_credential, resource: 'test_resource2') do |resource, requester|
222
+ requester.call "wrong_user_for_#{resource}", "wrong_password_for_#{resource}"
223
+ end
224
+ EO_CONFIG
225
+ ) do
226
+ expect_credentials_to_be 'correct_user_for_', 'correct_password_for_'
227
+ end
228
+ end
229
+
230
+ it 'fails if the requester is not called from config' do
231
+ with_platforms(
232
+ <<~'EO_CONFIG'
233
+ credentials_for(:test_credential) do |resource, requester|
234
+ end
235
+ EO_CONFIG
236
+ ) do
237
+ expect do
238
+ credential_tester_class.new(logger: logger, logger_stderr: logger, config: test_config).instance_exec do
239
+ with_credentials_for(:test_credential) do |_user, _password|
240
+ nil
241
+ end
242
+ end
243
+ end.to raise_error 'Requester not called by the credentials provider for test_credential (resource: ) - Please check the credentials_for code in your configuration.'
244
+ end
245
+ end
246
+
247
+ end
@@ -57,12 +57,9 @@ describe HybridPlatformsConductor::Deployer do
57
57
  # * *additional_config* (String): Additional config
58
58
  # * *platform_info* (Hash): Platform configuration [default: 1 node having 1 service]
59
59
  # * *mock_keepass_password* (String): Password to be returned by credentials [default: 'test_keepass_password']
60
- # * *mock_xml* (String): XML to be mocked [default: xml_single_entry]
61
60
  # * *expect_key_file* (String or nil): Key file to be expected, or nil if none [default: nil]
62
61
  # * *expect_password_enc* (String or nil): Encrypted password to be expected, or nil if none [default: nil]
63
62
  # * *expect_kpscript_calls* (Boolean): Should we expect calls to KPScript? [default: true]
64
- # * *expect_nbr_credentials_calls* (Integer): How many calls to the credentials are expected? [default: 1]
65
- # * *block* (Proc): Code called when the platform is setup
66
63
  def with_test_platform_for_keepass_test(
67
64
  additional_config,
68
65
  platform_info: {
@@ -70,33 +67,35 @@ describe HybridPlatformsConductor::Deployer do
70
67
  deployable_services: %w[service]
71
68
  },
72
69
  mock_keepass_password: 'test_keepass_password',
73
- mock_xml: xml_single_entry,
70
+ mock_databases: { '/path/to/database.kdbx' => xml_single_entry },
74
71
  expect_key_file: nil,
75
72
  expect_password_enc: nil,
76
- expect_kpscript_calls: true,
77
- expect_nbr_credentials_calls: 1,
78
- &block
73
+ expect_kpscript_calls: true
79
74
  )
80
- expect(HybridPlatformsConductor::Credentials).to receive(:with_credentials_for).exactly(expect_nbr_credentials_calls).times do |id, _logger, _logger_stderr, url: nil, &client_code|
81
- expect(id).to eq :keepass
82
- client_code.call nil, mock_keepass_password
83
- end
84
- if expect_kpscript_calls
85
- expect_calls_to_kpscript [
86
- [
87
- %r{/path/to/kpscript "/path/to/database.kdbx"#{mock_keepass_password.nil? ? '' : " -pw:\"#{Regexp.escape(mock_keepass_password)}\""}#{expect_password_enc.nil? ? '' : " -pw-enc:\"#{Regexp.escape(expect_password_enc)}\""}#{expect_key_file.nil? ? '' : " -keyfile:\"#{Regexp.escape(expect_key_file)}\""} -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
88
- {
89
- stdout: 'OK: Operation completed successfully.',
90
- xml: mock_xml
91
- }
92
- ]
93
- ]
94
- end
95
75
  with_test_platform(
96
76
  platform_info,
97
- additional_config: "read_secrets_from :keepass\n#{additional_config}",
98
- &block
99
- )
77
+ additional_config: "read_secrets_from :keepass\n#{additional_config}"
78
+ ) do
79
+ mock_databases.each do |database, _xml|
80
+ expect(test_deployer.instance_variable_get(:@secrets_readers)[:keepass]).to receive(:with_credentials_for).with(:keepass, resource: database) do |_id, resource: nil, &client_code|
81
+ client_code.call nil, mock_keepass_password
82
+ end
83
+ end
84
+ if expect_kpscript_calls
85
+ expect_calls_to_kpscript(
86
+ mock_databases.map do |database, xml|
87
+ [
88
+ %r{/path/to/kpscript "#{Regexp.escape(database)}"#{mock_keepass_password.nil? ? '' : " -pw:\"#{Regexp.escape(mock_keepass_password)}\""}#{expect_password_enc.nil? ? '' : " -pw-enc:\"#{Regexp.escape(expect_password_enc)}\""}#{expect_key_file.nil? ? '' : " -keyfile:\"#{Regexp.escape(expect_key_file)}\""} -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
89
+ {
90
+ stdout: 'OK: Operation completed successfully.',
91
+ xml: xml
92
+ }
93
+ ]
94
+ end
95
+ )
96
+ end
97
+ yield
98
+ end
100
99
  end
101
100
 
102
101
  # Expect secrets to be set to given values
@@ -224,7 +223,7 @@ describe HybridPlatformsConductor::Deployer do
224
223
  <<~EO_CONFIG,
225
224
  secrets_from_keepass(database: '/path/to/database.kdbx')
226
225
  EO_CONFIG
227
- expect_nbr_credentials_calls: 0,
226
+ mock_databases: {},
228
227
  expect_kpscript_calls: false
229
228
  ) do
230
229
  expect { test_deployer.deploy_on(%w[node]) }.to raise_error 'Missing KPScript configuration. Please use use_kpscript_from to set it.'
@@ -237,63 +236,65 @@ describe HybridPlatformsConductor::Deployer do
237
236
  use_kpscript_from '/path/to/kpscript'
238
237
  secrets_from_keepass(database: '/path/to/database.kdbx')
239
238
  EO_CONFIG
240
- mock_xml: <<~EO_XML
241
- <KeePassFile>
242
- <Root>
243
- <Group>
244
- <Entry>
245
- <String>
246
- <Key>Password</Key>
247
- <Value ProtectInMemory="True">TestPassword0</Value>
248
- </String>
249
- <String>
250
- <Key>Title</Key>
251
- <Value>Secret 0</Value>
252
- </String>
253
- </Entry>
239
+ mock_databases: {
240
+ '/path/to/database.kdbx' => <<~EO_XML
241
+ <KeePassFile>
242
+ <Root>
254
243
  <Group>
255
- <Name>Group1</UUID>
256
244
  <Entry>
257
245
  <String>
258
246
  <Key>Password</Key>
259
- <Value ProtectInMemory="True">TestPassword1</Value>
247
+ <Value ProtectInMemory="True">TestPassword0</Value>
260
248
  </String>
261
249
  <String>
262
250
  <Key>Title</Key>
263
- <Value>Secret 1</Value>
251
+ <Value>Secret 0</Value>
264
252
  </String>
265
253
  </Entry>
266
254
  <Group>
267
- <Name>Group2</UUID>
255
+ <Name>Group1</UUID>
268
256
  <Entry>
269
257
  <String>
270
258
  <Key>Password</Key>
271
- <Value ProtectInMemory="True">TestPassword2</Value>
259
+ <Value ProtectInMemory="True">TestPassword1</Value>
272
260
  </String>
273
261
  <String>
274
262
  <Key>Title</Key>
275
- <Value>Secret 2</Value>
276
- </String>
277
- </Entry>
278
- </Group>
279
- <Group>
280
- <Name>Group3</UUID>
281
- <Entry>
282
- <String>
283
- <Key>Password</Key>
284
- <Value ProtectInMemory="True">TestPassword3</Value>
285
- </String>
286
- <String>
287
- <Key>Title</Key>
288
- <Value>Secret 3</Value>
263
+ <Value>Secret 1</Value>
289
264
  </String>
290
265
  </Entry>
266
+ <Group>
267
+ <Name>Group2</UUID>
268
+ <Entry>
269
+ <String>
270
+ <Key>Password</Key>
271
+ <Value ProtectInMemory="True">TestPassword2</Value>
272
+ </String>
273
+ <String>
274
+ <Key>Title</Key>
275
+ <Value>Secret 2</Value>
276
+ </String>
277
+ </Entry>
278
+ </Group>
279
+ <Group>
280
+ <Name>Group3</UUID>
281
+ <Entry>
282
+ <String>
283
+ <Key>Password</Key>
284
+ <Value ProtectInMemory="True">TestPassword3</Value>
285
+ </String>
286
+ <String>
287
+ <Key>Title</Key>
288
+ <Value>Secret 3</Value>
289
+ </String>
290
+ </Entry>
291
+ </Group>
291
292
  </Group>
292
293
  </Group>
293
- </Group>
294
- </Root>
295
- </KeePassFile>
296
- EO_XML
294
+ </Root>
295
+ </KeePassFile>
296
+ EO_XML
297
+ }
297
298
  ) do
298
299
  expect_secrets_to_be(
299
300
  'Secret 0' => { 'password' => 'TestPassword0' },
@@ -316,57 +317,59 @@ describe HybridPlatformsConductor::Deployer do
316
317
  use_kpscript_from '/path/to/kpscript'
317
318
  secrets_from_keepass(database: '/path/to/database.kdbx')
318
319
  EO_CONFIG
319
- mock_xml: <<~EO_XML
320
- <KeePassFile>
321
- <Meta>
322
- <Binaries>
323
- <Binary ID="0" Compressed="True">#{
324
- str = StringIO.new
325
- gz = Zlib::GzipWriter.new(str)
326
- gz.write('File 0 Content')
327
- gz.close
328
- Base64.encode64(str.string).strip
329
- }</Binary>
330
- <Binary ID="1">#{Base64.encode64('File 1 Content').strip}</Binary>
331
- </Binaries>
332
- </Meta>
333
- <Root>
334
- <Group>
335
- <Entry>
336
- <String>
337
- <Key>Password</Key>
338
- <Value ProtectInMemory="True">TestPassword0</Value>
339
- </String>
340
- <String>
341
- <Key>Title</Key>
342
- <Value>Secret 0</Value>
343
- </String>
344
- <Binary>
345
- <Key>file0.txt</Key>
346
- <Value Ref="0" />
347
- </Binary>
348
- </Entry>
320
+ mock_databases: {
321
+ '/path/to/database.kdbx' => <<~EO_XML
322
+ <KeePassFile>
323
+ <Meta>
324
+ <Binaries>
325
+ <Binary ID="0" Compressed="True">#{
326
+ str = StringIO.new
327
+ gz = Zlib::GzipWriter.new(str)
328
+ gz.write('File 0 Content')
329
+ gz.close
330
+ Base64.encode64(str.string).strip
331
+ }</Binary>
332
+ <Binary ID="1">#{Base64.encode64('File 1 Content').strip}</Binary>
333
+ </Binaries>
334
+ </Meta>
335
+ <Root>
349
336
  <Group>
350
- <Name>Group1</UUID>
351
337
  <Entry>
352
338
  <String>
353
339
  <Key>Password</Key>
354
- <Value ProtectInMemory="True">TestPassword1</Value>
340
+ <Value ProtectInMemory="True">TestPassword0</Value>
355
341
  </String>
356
342
  <String>
357
343
  <Key>Title</Key>
358
- <Value>Secret 1</Value>
344
+ <Value>Secret 0</Value>
359
345
  </String>
360
346
  <Binary>
361
- <Key>file1.txt</Key>
362
- <Value Ref="1" />
347
+ <Key>file0.txt</Key>
348
+ <Value Ref="0" />
363
349
  </Binary>
364
350
  </Entry>
351
+ <Group>
352
+ <Name>Group1</UUID>
353
+ <Entry>
354
+ <String>
355
+ <Key>Password</Key>
356
+ <Value ProtectInMemory="True">TestPassword1</Value>
357
+ </String>
358
+ <String>
359
+ <Key>Title</Key>
360
+ <Value>Secret 1</Value>
361
+ </String>
362
+ <Binary>
363
+ <Key>file1.txt</Key>
364
+ <Value Ref="1" />
365
+ </Binary>
366
+ </Entry>
367
+ </Group>
365
368
  </Group>
366
- </Group>
367
- </Root>
368
- </KeePassFile>
369
- EO_XML
369
+ </Root>
370
+ </KeePassFile>
371
+ EO_XML
372
+ }
370
373
  ) do
371
374
  expect_secrets_to_be(
372
375
  'Secret 0' => { 'file0.txt' => 'File 0 Content', 'password' => 'TestPassword0' },
@@ -410,47 +413,33 @@ describe HybridPlatformsConductor::Deployer do
410
413
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
411
414
  deployable_services: %w[service1 service2]
412
415
  },
413
- expect_kpscript_calls: false,
414
- expect_nbr_credentials_calls: 2
416
+ mock_databases: {
417
+ '/path/to/database1.kdbx' => xml_single_entry,
418
+ '/path/to/database2.kdbx' => <<~EO_XML
419
+ <KeePassFile>
420
+ <Root>
421
+ <Group>
422
+ <Entry>
423
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
424
+ <String>
425
+ <Key>Password</Key>
426
+ <Value ProtectInMemory="True">TestPassword2</Value>
427
+ </String>
428
+ <String>
429
+ <Key>Title</Key>
430
+ <Value>Test Secret 2</Value>
431
+ </String>
432
+ <String>
433
+ <Key>UserName</Key>
434
+ <Value>Test User Name 2</Value>
435
+ </String>
436
+ </Entry>
437
+ </Group>
438
+ </Root>
439
+ </KeePassFile>
440
+ EO_XML
441
+ }
415
442
  ) do
416
- expect_calls_to_kpscript [
417
- [
418
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
419
- {
420
- stdout: 'OK: Operation completed successfully.',
421
- xml: xml_single_entry
422
- }
423
- ],
424
- [
425
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
426
- {
427
- stdout: 'OK: Operation completed successfully.',
428
- xml: <<~EO_XML
429
- <KeePassFile>
430
- <Root>
431
- <Group>
432
- <Entry>
433
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
434
- <String>
435
- <Key>Password</Key>
436
- <Value ProtectInMemory="True">TestPassword2</Value>
437
- </String>
438
- <String>
439
- <Key>Title</Key>
440
- <Value>Test Secret 2</Value>
441
- </String>
442
- <String>
443
- <Key>UserName</Key>
444
- <Value>Test User Name 2</Value>
445
- </String>
446
- </Entry>
447
- </Group>
448
- </Root>
449
- </KeePassFile>
450
- EO_XML
451
- }
452
- ]
453
- ]
454
443
  expect(test_services_handler).to receive(:package).with(
455
444
  services: { 'node1' => %w[service1], 'node2' => %w[service2] },
456
445
  secrets: {
@@ -500,122 +489,108 @@ describe HybridPlatformsConductor::Deployer do
500
489
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
501
490
  deployable_services: %w[service1 service2]
502
491
  },
503
- expect_kpscript_calls: false,
504
- expect_nbr_credentials_calls: 2
492
+ mock_databases: {
493
+ '/path/to/database1.kdbx' => <<~EO_XML,
494
+ <KeePassFile>
495
+ <Root>
496
+ <Group>
497
+ <Entry>
498
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
499
+ <String>
500
+ <Key>Password</Key>
501
+ <Value ProtectInMemory="True">TestPassword1</Value>
502
+ </String>
503
+ <String>
504
+ <Key>Title</Key>
505
+ <Value>Test Secret 1</Value>
506
+ </String>
507
+ <String>
508
+ <Key>UserName</Key>
509
+ <Value>Test User Name 1</Value>
510
+ </String>
511
+ </Entry>
512
+ <Group>
513
+ <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
514
+ <Name>Group1</Name>
515
+ <Entry>
516
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
517
+ <String>
518
+ <Key>Password</Key>
519
+ <Value ProtectInMemory="True">TestPassword3</Value>
520
+ </String>
521
+ <String>
522
+ <Key>Title</Key>
523
+ <Value>Test Secret 3</Value>
524
+ </String>
525
+ <String>
526
+ <Key>UserName</Key>
527
+ <Value>Test User Name 3</Value>
528
+ </String>
529
+ </Entry>
530
+ </Group>
531
+ </Group>
532
+ </Root>
533
+ </KeePassFile>
534
+ EO_XML
535
+ '/path/to/database2.kdbx' => <<~EO_XML
536
+ <KeePassFile>
537
+ <Root>
538
+ <Group>
539
+ <Entry>
540
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
541
+ <String>
542
+ <Key>Password</Key>
543
+ <Value ProtectInMemory="True">TestPassword2</Value>
544
+ </String>
545
+ <String>
546
+ <Key>Title</Key>
547
+ <Value>Test Secret 2</Value>
548
+ </String>
549
+ <String>
550
+ <Key>UserName</Key>
551
+ <Value>Test User Name 2</Value>
552
+ </String>
553
+ </Entry>
554
+ <Group>
555
+ <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
556
+ <Name>Group1</Name>
557
+ <Entry>
558
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
559
+ <String>
560
+ <Key>Password</Key>
561
+ <Value ProtectInMemory="True">TestPassword3</Value>
562
+ </String>
563
+ <String>
564
+ <Key>Title</Key>
565
+ <Value>Test Secret 3</Value>
566
+ </String>
567
+ <String>
568
+ <Key>Notes</Key>
569
+ <Value>Notes 3</Value>
570
+ </String>
571
+ </Entry>
572
+ <Entry>
573
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
574
+ <String>
575
+ <Key>Password</Key>
576
+ <Value ProtectInMemory="True">TestPassword4</Value>
577
+ </String>
578
+ <String>
579
+ <Key>Title</Key>
580
+ <Value>Test Secret 4</Value>
581
+ </String>
582
+ <String>
583
+ <Key>UserName</Key>
584
+ <Value>Test User Name 4</Value>
585
+ </String>
586
+ </Entry>
587
+ </Group>
588
+ </Group>
589
+ </Root>
590
+ </KeePassFile>
591
+ EO_XML
592
+ }
505
593
  ) do
506
- expect_calls_to_kpscript [
507
- [
508
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
509
- {
510
- stdout: 'OK: Operation completed successfully.',
511
- xml: <<~EO_XML
512
- <KeePassFile>
513
- <Root>
514
- <Group>
515
- <Entry>
516
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
517
- <String>
518
- <Key>Password</Key>
519
- <Value ProtectInMemory="True">TestPassword1</Value>
520
- </String>
521
- <String>
522
- <Key>Title</Key>
523
- <Value>Test Secret 1</Value>
524
- </String>
525
- <String>
526
- <Key>UserName</Key>
527
- <Value>Test User Name 1</Value>
528
- </String>
529
- </Entry>
530
- <Group>
531
- <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
532
- <Name>Group1</Name>
533
- <Entry>
534
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
535
- <String>
536
- <Key>Password</Key>
537
- <Value ProtectInMemory="True">TestPassword3</Value>
538
- </String>
539
- <String>
540
- <Key>Title</Key>
541
- <Value>Test Secret 3</Value>
542
- </String>
543
- <String>
544
- <Key>UserName</Key>
545
- <Value>Test User Name 3</Value>
546
- </String>
547
- </Entry>
548
- </Group>
549
- </Group>
550
- </Root>
551
- </KeePassFile>
552
- EO_XML
553
- }
554
- ],
555
- [
556
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
557
- {
558
- stdout: 'OK: Operation completed successfully.',
559
- xml: <<~EO_XML
560
- <KeePassFile>
561
- <Root>
562
- <Group>
563
- <Entry>
564
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
565
- <String>
566
- <Key>Password</Key>
567
- <Value ProtectInMemory="True">TestPassword2</Value>
568
- </String>
569
- <String>
570
- <Key>Title</Key>
571
- <Value>Test Secret 2</Value>
572
- </String>
573
- <String>
574
- <Key>UserName</Key>
575
- <Value>Test User Name 2</Value>
576
- </String>
577
- </Entry>
578
- <Group>
579
- <UUID>RsonCc3VHk+k85z5zHhZzQ==</UUID>
580
- <Name>Group1</Name>
581
- <Entry>
582
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
583
- <String>
584
- <Key>Password</Key>
585
- <Value ProtectInMemory="True">TestPassword3</Value>
586
- </String>
587
- <String>
588
- <Key>Title</Key>
589
- <Value>Test Secret 3</Value>
590
- </String>
591
- <String>
592
- <Key>Notes</Key>
593
- <Value>Notes 3</Value>
594
- </String>
595
- </Entry>
596
- <Entry>
597
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
598
- <String>
599
- <Key>Password</Key>
600
- <Value ProtectInMemory="True">TestPassword4</Value>
601
- </String>
602
- <String>
603
- <Key>Title</Key>
604
- <Value>Test Secret 4</Value>
605
- </String>
606
- <String>
607
- <Key>UserName</Key>
608
- <Value>Test User Name 4</Value>
609
- </String>
610
- </Entry>
611
- </Group>
612
- </Group>
613
- </Root>
614
- </KeePassFile>
615
- EO_XML
616
- }
617
- ]
618
- ]
619
594
  expect(test_services_handler).to receive(:package).with(
620
595
  services: { 'node1' => %w[service1], 'node2' => %w[service2] },
621
596
  secrets: {
@@ -645,69 +620,55 @@ describe HybridPlatformsConductor::Deployer do
645
620
  nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
646
621
  deployable_services: %w[service1 service2]
647
622
  },
648
- expect_kpscript_calls: false,
649
- expect_nbr_credentials_calls: 2
623
+ mock_databases: {
624
+ '/path/to/database1.kdbx' => <<~EO_XML,
625
+ <KeePassFile>
626
+ <Root>
627
+ <Group>
628
+ <Entry>
629
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
630
+ <String>
631
+ <Key>Password</Key>
632
+ <Value ProtectInMemory="True">TestPassword1</Value>
633
+ </String>
634
+ <String>
635
+ <Key>Title</Key>
636
+ <Value>Test Secret 1</Value>
637
+ </String>
638
+ <String>
639
+ <Key>UserName</Key>
640
+ <Value>Test User Name 1</Value>
641
+ </String>
642
+ </Entry>
643
+ </Group>
644
+ </Root>
645
+ </KeePassFile>
646
+ EO_XML
647
+ '/path/to/database2.kdbx' => <<~EO_XML
648
+ <KeePassFile>
649
+ <Root>
650
+ <Group>
651
+ <Entry>
652
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
653
+ <String>
654
+ <Key>Password</Key>
655
+ <Value ProtectInMemory="True">OtherTestPassword1</Value>
656
+ </String>
657
+ <String>
658
+ <Key>Title</Key>
659
+ <Value>Test Secret 1</Value>
660
+ </String>
661
+ <String>
662
+ <Key>UserName</Key>
663
+ <Value>Test User Name 1</Value>
664
+ </String>
665
+ </Entry>
666
+ </Group>
667
+ </Root>
668
+ </KeePassFile>
669
+ EO_XML
670
+ }
650
671
  ) do
651
- expect_calls_to_kpscript [
652
- [
653
- %r{/path/to/kpscript "/path/to/database1.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
654
- {
655
- stdout: 'OK: Operation completed successfully.',
656
- xml: <<~EO_XML
657
- <KeePassFile>
658
- <Root>
659
- <Group>
660
- <Entry>
661
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
662
- <String>
663
- <Key>Password</Key>
664
- <Value ProtectInMemory="True">TestPassword1</Value>
665
- </String>
666
- <String>
667
- <Key>Title</Key>
668
- <Value>Test Secret 1</Value>
669
- </String>
670
- <String>
671
- <Key>UserName</Key>
672
- <Value>Test User Name 1</Value>
673
- </String>
674
- </Entry>
675
- </Group>
676
- </Root>
677
- </KeePassFile>
678
- EO_XML
679
- }
680
- ],
681
- [
682
- %r{/path/to/kpscript "/path/to/database2.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+"},
683
- {
684
- stdout: 'OK: Operation completed successfully.',
685
- xml: <<~EO_XML
686
- <KeePassFile>
687
- <Root>
688
- <Group>
689
- <Entry>
690
- <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
691
- <String>
692
- <Key>Password</Key>
693
- <Value ProtectInMemory="True">OtherTestPassword1</Value>
694
- </String>
695
- <String>
696
- <Key>Title</Key>
697
- <Value>Test Secret 1</Value>
698
- </String>
699
- <String>
700
- <Key>UserName</Key>
701
- <Value>Test User Name 1</Value>
702
- </String>
703
- </Entry>
704
- </Group>
705
- </Root>
706
- </KeePassFile>
707
- EO_XML
708
- }
709
- ]
710
- ]
711
672
  expect { test_deployer.deploy_on(%w[node1 node2]) }.to raise_error 'Secret set at path Test Secret 1->password by /path/to/database2.kdbx for service service2 on node node2 has conflicting values (set debug for value details).'
712
673
  end
713
674
  end