hybrid_platforms_conductor 33.2.4 → 33.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,17 +9,19 @@ describe HybridPlatformsConductor::Deployer do
9
9
  end
10
10
 
11
11
  it 'returns the retriable errors correctly' do
12
- with_platforms '
13
- retry_deploy_for_errors_on_stdout \'Retry stdout global\'
14
- retry_deploy_for_errors_on_stderr [
15
- \'Retry stderr global\',
16
- /.+Retry stderr regexp global/
17
- ]
18
- for_nodes(%w[node1 node2 node3]) do
19
- retry_deploy_for_errors_on_stdout \'Retry stdout nodes\'
20
- retry_deploy_for_errors_on_stderr \'Retry stderr nodes\'
21
- end
22
- ' do
12
+ with_platforms(
13
+ <<~EO_CONFIG
14
+ retry_deploy_for_errors_on_stdout 'Retry stdout global'
15
+ retry_deploy_for_errors_on_stderr [
16
+ 'Retry stderr global',
17
+ /.+Retry stderr regexp global/
18
+ ]
19
+ for_nodes(%w[node1 node2 node3]) do
20
+ retry_deploy_for_errors_on_stdout 'Retry stdout nodes'
21
+ retry_deploy_for_errors_on_stderr 'Retry stderr nodes'
22
+ end
23
+ EO_CONFIG
24
+ ) do
23
25
  sort_proc = proc { |retriable_error_info| ((retriable_error_info[:errors_on_stdout] || []) + (retriable_error_info[:errors_on_stderr] || [])).first.to_s }
24
26
  expect(test_config.retriable_errors.sort_by(&sort_proc)).to eq [
25
27
  {
@@ -0,0 +1,719 @@
1
+ require 'open3'
2
+
3
+ describe HybridPlatformsConductor::Deployer do
4
+
5
+ context 'when checking secrets_reader plugins' do
6
+
7
+ context 'with keepass' do
8
+
9
+ # Expect some calls to be done on KPScript
10
+ #
11
+ # Parameters::
12
+ # * *expected_calls* (Array<[String, String or Hash]>): The list of calls and their corresponding mocked response:
13
+ # * String: Mocked stdout
14
+ # * Hash<Symbol,Object>: More complete structure defining the mocked response:
15
+ # * *exit_status* (Integer): The command exit status [default: 0]
16
+ # * *stdout* (String): The command stdout
17
+ # * *xml* (String or nil): XML document to generate as an export, or nil for none [default: nil]
18
+ def expect_calls_to_kpscript(expected_calls)
19
+ if expected_calls.empty?
20
+ expect(Open3).not_to receive(:popen3)
21
+ else
22
+ expect(Open3).to receive(:popen3).exactly(expected_calls.size).times do |cmd, &block|
23
+ expected_call, mocked_call = expected_calls.shift
24
+ if expected_call.is_a?(Regexp)
25
+ expect(cmd).to match expected_call
26
+ else
27
+ expect(cmd).to eq expected_call
28
+ end
29
+ mocked_call = { stdout: mocked_call } if mocked_call.is_a?(String)
30
+ mocked_call[:exit_status] = 0 unless mocked_call.key?(:exit_status)
31
+ wait_thr_double = instance_double(Process::Waiter)
32
+ allow(wait_thr_double).to receive(:value) do
33
+ wait_thr_value_double = instance_double(Process::Status)
34
+ allow(wait_thr_value_double).to receive(:exitstatus) do
35
+ mocked_call[:exit_status]
36
+ end
37
+ wait_thr_value_double
38
+ end
39
+ if mocked_call[:xml]
40
+ xml_file = cmd.match(/-OutFile:"([^"]+)"/)[1]
41
+ logger.debug "Mock KPScript XML file #{xml_file} with\n#{mocked_call[:xml]}"
42
+ File.write(xml_file, mocked_call[:xml])
43
+ end
44
+ block.call(
45
+ StringIO.new,
46
+ StringIO.new(mocked_call[:stdout]),
47
+ StringIO.new,
48
+ wait_thr_double
49
+ )
50
+ end
51
+ end
52
+ end
53
+
54
+ # Setup a platform for tests
55
+ #
56
+ # Parameters::
57
+ # * *additional_config* (String): Additional config
58
+ # * *platform_info* (Hash): Platform configuration [default: 1 node having 1 service]
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
+ # * *expect_key_file* (String or nil): Key file to be expected, or nil if none [default: nil]
62
+ # * *expect_password_enc* (String or nil): Encrypted password to be expected, or nil if none [default: nil]
63
+ # * *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
+ def with_test_platform_for_keepass_test(
67
+ additional_config,
68
+ platform_info: {
69
+ nodes: { 'node' => { services: %w[service] } },
70
+ deployable_services: %w[service]
71
+ },
72
+ mock_keepass_password: 'test_keepass_password',
73
+ mock_xml: xml_single_entry,
74
+ expect_key_file: nil,
75
+ expect_password_enc: nil,
76
+ expect_kpscript_calls: true,
77
+ expect_nbr_credentials_calls: 1,
78
+ &block
79
+ )
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
+ with_test_platform(
96
+ platform_info,
97
+ additional_config: "read_secrets_from :keepass\n#{additional_config}",
98
+ &block
99
+ )
100
+ end
101
+
102
+ # Expect secrets to be set to given values
103
+ #
104
+ # Parameters::
105
+ # * *expected_secrets* (Hash): Expected secrets
106
+ def expect_secrets_to_be(expected_secrets)
107
+ expect(test_services_handler).to receive(:package).with(
108
+ services: { 'node' => %w[service] },
109
+ secrets: expected_secrets,
110
+ local_environment: false
111
+ ) { raise 'Abort as testing secrets is enough' }
112
+ expect { test_deployer.deploy_on(%w[node]) }.to raise_error 'Abort as testing secrets is enough'
113
+ end
114
+
115
+ let(:xml_single_entry) do
116
+ <<~EO_XML
117
+ <KeePassFile>
118
+ <Root>
119
+ <Group>
120
+ <Entry>
121
+ <UUID>Iv3JjMzpPEaijOB+SFZpRw==</UUID>
122
+ <String>
123
+ <Key>Password</Key>
124
+ <Value ProtectInMemory="True">TestPassword</Value>
125
+ </String>
126
+ <String>
127
+ <Key>Title</Key>
128
+ <Value>Test Secret</Value>
129
+ </String>
130
+ <String>
131
+ <Key>UserName</Key>
132
+ <Value>Test User Name</Value>
133
+ </String>
134
+ </Entry>
135
+ </Group>
136
+ </Root>
137
+ </KeePassFile>
138
+ EO_XML
139
+ end
140
+
141
+ it 'gets secrets from a KeePass database with password' do
142
+ with_test_platform_for_keepass_test(
143
+ <<~EO_CONFIG
144
+ use_kpscript_from '/path/to/kpscript'
145
+ secrets_from_keepass(database: '/path/to/database.kdbx')
146
+ EO_CONFIG
147
+ ) do
148
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
149
+ end
150
+ end
151
+
152
+ it 'gets secrets from a KeePass database with password and key file' do
153
+ with_test_platform_for_keepass_test(
154
+ <<~EO_CONFIG,
155
+ use_kpscript_from '/path/to/kpscript'
156
+ secrets_from_keepass(database: '/path/to/database.kdbx')
157
+ EO_CONFIG
158
+ expect_key_file: '/path/to/database.key'
159
+ ) do
160
+ ENV['hpc_key_file_for_keepass'] = '/path/to/database.key'
161
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
162
+ end
163
+ end
164
+
165
+ it 'gets secrets from a KeePass database with encrypted password' do
166
+ with_test_platform_for_keepass_test(
167
+ <<~EO_CONFIG,
168
+ use_kpscript_from '/path/to/kpscript'
169
+ secrets_from_keepass(database: '/path/to/database.kdbx')
170
+ EO_CONFIG
171
+ mock_keepass_password: nil,
172
+ expect_password_enc: 'PASSWORD_ENC'
173
+ ) do
174
+ ENV['hpc_password_enc_for_keepass'] = 'PASSWORD_ENC'
175
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
176
+ end
177
+ end
178
+
179
+ it 'gets secrets from a KeePass database with encrypted password and key file' do
180
+ with_test_platform_for_keepass_test(
181
+ <<~EO_CONFIG,
182
+ use_kpscript_from '/path/to/kpscript'
183
+ secrets_from_keepass(database: '/path/to/database.kdbx')
184
+ EO_CONFIG
185
+ mock_keepass_password: nil,
186
+ expect_password_enc: 'PASSWORD_ENC',
187
+ expect_key_file: '/path/to/database.key'
188
+ ) do
189
+ ENV['hpc_password_enc_for_keepass'] = 'PASSWORD_ENC'
190
+ ENV['hpc_key_file_for_keepass'] = '/path/to/database.key'
191
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
192
+ end
193
+ end
194
+
195
+ it 'gets secrets from a KeePass database with key file' do
196
+ with_test_platform_for_keepass_test(
197
+ <<~EO_CONFIG,
198
+ use_kpscript_from '/path/to/kpscript'
199
+ secrets_from_keepass(database: '/path/to/database.kdbx')
200
+ EO_CONFIG
201
+ mock_keepass_password: nil,
202
+ expect_key_file: '/path/to/database.key'
203
+ ) do
204
+ ENV['hpc_key_file_for_keepass'] = '/path/to/database.key'
205
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
206
+ end
207
+ end
208
+
209
+ it 'fails to get secrets from a KeePass database when no authentication mechanisms are provided' do
210
+ with_test_platform_for_keepass_test(
211
+ <<~EO_CONFIG,
212
+ use_kpscript_from '/path/to/kpscript'
213
+ secrets_from_keepass(database: '/path/to/database.kdbx')
214
+ EO_CONFIG
215
+ mock_keepass_password: nil,
216
+ expect_kpscript_calls: false
217
+ ) do
218
+ expect { test_deployer.deploy_on(%w[node]) }.to raise_error 'Please specify at least one of password, password_enc or key_file arguments'
219
+ end
220
+ end
221
+
222
+ it 'fails to get secrets if KPScript is not configured' do
223
+ with_test_platform_for_keepass_test(
224
+ <<~EO_CONFIG,
225
+ secrets_from_keepass(database: '/path/to/database.kdbx')
226
+ EO_CONFIG
227
+ expect_nbr_credentials_calls: 0,
228
+ expect_kpscript_calls: false
229
+ ) do
230
+ expect { test_deployer.deploy_on(%w[node]) }.to raise_error 'Missing KPScript configuration. Please use use_kpscript_from to set it.'
231
+ end
232
+ end
233
+
234
+ it 'gets secrets from KeePass groups' do
235
+ with_test_platform_for_keepass_test(
236
+ <<~EO_CONFIG,
237
+ use_kpscript_from '/path/to/kpscript'
238
+ secrets_from_keepass(database: '/path/to/database.kdbx')
239
+ 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>
254
+ <Group>
255
+ <Name>Group1</UUID>
256
+ <Entry>
257
+ <String>
258
+ <Key>Password</Key>
259
+ <Value ProtectInMemory="True">TestPassword1</Value>
260
+ </String>
261
+ <String>
262
+ <Key>Title</Key>
263
+ <Value>Secret 1</Value>
264
+ </String>
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>
292
+ </Group>
293
+ </Group>
294
+ </Root>
295
+ </KeePassFile>
296
+ EO_XML
297
+ ) do
298
+ expect_secrets_to_be(
299
+ 'Secret 0' => { 'password' => 'TestPassword0' },
300
+ 'Group1' => {
301
+ 'Secret 1' => { 'password' => 'TestPassword1' },
302
+ 'Group2' => {
303
+ 'Secret 2' => { 'password' => 'TestPassword2' }
304
+ },
305
+ 'Group3' => {
306
+ 'Secret 3' => { 'password' => 'TestPassword3' }
307
+ }
308
+ }
309
+ )
310
+ end
311
+ end
312
+
313
+ it 'gets secrets with attachments' do
314
+ with_test_platform_for_keepass_test(
315
+ <<~EO_CONFIG,
316
+ use_kpscript_from '/path/to/kpscript'
317
+ secrets_from_keepass(database: '/path/to/database.kdbx')
318
+ 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>
349
+ <Group>
350
+ <Name>Group1</UUID>
351
+ <Entry>
352
+ <String>
353
+ <Key>Password</Key>
354
+ <Value ProtectInMemory="True">TestPassword1</Value>
355
+ </String>
356
+ <String>
357
+ <Key>Title</Key>
358
+ <Value>Secret 1</Value>
359
+ </String>
360
+ <Binary>
361
+ <Key>file1.txt</Key>
362
+ <Value Ref="1" />
363
+ </Binary>
364
+ </Entry>
365
+ </Group>
366
+ </Group>
367
+ </Root>
368
+ </KeePassFile>
369
+ EO_XML
370
+ ) do
371
+ expect_secrets_to_be(
372
+ 'Secret 0' => { 'file0.txt' => 'File 0 Content', 'password' => 'TestPassword0' },
373
+ 'Group1' => {
374
+ 'Secret 1' => { 'file1.txt' => 'File 1 Content', 'password' => 'TestPassword1' }
375
+ }
376
+ )
377
+ end
378
+ end
379
+
380
+ it 'gets secrets from a KeePass database for several nodes' do
381
+ with_test_platform_for_keepass_test(
382
+ <<~EO_CONFIG,
383
+ use_kpscript_from '/path/to/kpscript'
384
+ secrets_from_keepass(database: '/path/to/database.kdbx')
385
+ EO_CONFIG
386
+ platform_info: {
387
+ nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
388
+ deployable_services: %w[service1 service2]
389
+ }
390
+ ) do
391
+ expect(test_services_handler).to receive(:package).with(
392
+ services: { 'node1' => %w[service1], 'node2' => %w[service2] },
393
+ secrets: { 'Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' } },
394
+ local_environment: false
395
+ ) { raise 'Abort as testing secrets is enough' }
396
+ expect { test_deployer.deploy_on(%w[node1 node2]) }.to raise_error 'Abort as testing secrets is enough'
397
+ end
398
+ end
399
+
400
+ it 'gets secrets from a KeePass database for several databases' do
401
+ with_test_platform_for_keepass_test(
402
+ <<~EO_CONFIG,
403
+ use_kpscript_from '/path/to/kpscript'
404
+ secrets_from_keepass(database: '/path/to/database1.kdbx')
405
+ for_nodes('node2') do
406
+ secrets_from_keepass(database: '/path/to/database2.kdbx')
407
+ end
408
+ EO_CONFIG
409
+ platform_info: {
410
+ nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
411
+ deployable_services: %w[service1 service2]
412
+ },
413
+ expect_kpscript_calls: false,
414
+ expect_nbr_credentials_calls: 2
415
+ ) 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
+ expect(test_services_handler).to receive(:package).with(
455
+ services: { 'node1' => %w[service1], 'node2' => %w[service2] },
456
+ secrets: {
457
+ 'Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' },
458
+ 'Test Secret 2' => { 'password' => 'TestPassword2', 'user_name' => 'Test User Name 2' }
459
+ },
460
+ local_environment: false
461
+ ) { raise 'Abort as testing secrets is enough' }
462
+ expect { test_deployer.deploy_on(%w[node1 node2]) }.to raise_error 'Abort as testing secrets is enough'
463
+ end
464
+ end
465
+
466
+ it 'gets secrets from a group path in a KeePass database' do
467
+ with_test_platform_for_keepass_test(
468
+ <<~EO_CONFIG,
469
+ use_kpscript_from '/path/to/kpscript'
470
+ secrets_from_keepass(
471
+ database: '/path/to/database.kdbx',
472
+ group_path: %w[Group1 Group2 Group3]
473
+ )
474
+ EO_CONFIG
475
+ expect_kpscript_calls: false
476
+ ) do
477
+ expect_calls_to_kpscript [
478
+ [
479
+ %r{/path/to/kpscript "/path/to/database.kdbx" -pw:"test_keepass_password" -c:Export -Format:"KeePass XML \(2.x\)" -OutFile:"/tmp/.+" -GroupPath:"Group1/Group2/Group3"},
480
+ {
481
+ stdout: 'OK: Operation completed successfully.',
482
+ xml: xml_single_entry
483
+ }
484
+ ]
485
+ ]
486
+ expect_secrets_to_be('Test Secret' => { 'password' => 'TestPassword', 'user_name' => 'Test User Name' })
487
+ end
488
+ end
489
+
490
+ it 'merges secrets from several KeePass databases' do
491
+ with_test_platform_for_keepass_test(
492
+ <<~EO_CONFIG,
493
+ use_kpscript_from '/path/to/kpscript'
494
+ secrets_from_keepass(database: '/path/to/database1.kdbx')
495
+ for_nodes('node2') do
496
+ secrets_from_keepass(database: '/path/to/database2.kdbx')
497
+ end
498
+ EO_CONFIG
499
+ platform_info: {
500
+ nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
501
+ deployable_services: %w[service1 service2]
502
+ },
503
+ expect_kpscript_calls: false,
504
+ expect_nbr_credentials_calls: 2
505
+ ) 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
+ expect(test_services_handler).to receive(:package).with(
620
+ services: { 'node1' => %w[service1], 'node2' => %w[service2] },
621
+ secrets: {
622
+ 'Test Secret 1' => { 'password' => 'TestPassword1', 'user_name' => 'Test User Name 1' },
623
+ 'Test Secret 2' => { 'password' => 'TestPassword2', 'user_name' => 'Test User Name 2' },
624
+ 'Group1' => {
625
+ 'Test Secret 3' => { 'password' => 'TestPassword3', 'user_name' => 'Test User Name 3', 'notes' => 'Notes 3' },
626
+ 'Test Secret 4' => { 'password' => 'TestPassword4', 'user_name' => 'Test User Name 4' }
627
+ }
628
+ },
629
+ local_environment: false
630
+ ) { raise 'Abort as testing secrets is enough' }
631
+ expect { test_deployer.deploy_on(%w[node1 node2]) }.to raise_error 'Abort as testing secrets is enough'
632
+ end
633
+ end
634
+
635
+ it 'fails in case of secrets conflicts between several KeePass databases' do
636
+ with_test_platform_for_keepass_test(
637
+ <<~EO_CONFIG,
638
+ use_kpscript_from '/path/to/kpscript'
639
+ secrets_from_keepass(database: '/path/to/database1.kdbx')
640
+ for_nodes('node2') do
641
+ secrets_from_keepass(database: '/path/to/database2.kdbx')
642
+ end
643
+ EO_CONFIG
644
+ platform_info: {
645
+ nodes: { 'node1' => { services: %w[service1] }, 'node2' => { services: %w[service2] } },
646
+ deployable_services: %w[service1 service2]
647
+ },
648
+ expect_kpscript_calls: false,
649
+ expect_nbr_credentials_calls: 2
650
+ ) 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
+ 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
+ end
713
+ end
714
+
715
+ end
716
+
717
+ end
718
+
719
+ end