18
18
import com .aws .greengrass .clientdevices .auth .connectivity .CISShadowMonitor ;
19
19
import com .aws .greengrass .clientdevices .auth .exception .CertificateChainLoadingException ;
20
20
import com .aws .greengrass .clientdevices .auth .exception .CertificateGenerationException ;
21
+ import com .aws .greengrass .clientdevices .auth .exception .InvalidConfigurationException ;
21
22
import com .aws .greengrass .clientdevices .auth .helpers .CertificateTestHelpers ;
22
23
import com .aws .greengrass .config .Topics ;
23
24
import com .aws .greengrass .dependency .State ;
52
53
import java .net .URISyntaxException ;
53
54
import java .nio .file .Path ;
54
55
import java .security .KeyPair ;
56
+ import java .security .KeyStoreException ;
55
57
import java .security .NoSuchAlgorithmException ;
56
58
import java .security .cert .CertificateException ;
57
59
import java .security .cert .X509Certificate ;
58
60
import java .util .Collections ;
59
61
import java .util .List ;
62
+ import java .util .Objects ;
60
63
import java .util .concurrent .CompletableFuture ;
61
64
import java .util .concurrent .CountDownLatch ;
62
65
import java .util .concurrent .ExecutionException ;
63
66
import java .util .concurrent .TimeUnit ;
64
67
import java .util .concurrent .TimeoutException ;
68
+ import java .util .concurrent .atomic .AtomicBoolean ;
65
69
import java .util .concurrent .atomic .AtomicReference ;
66
70
import java .util .function .Consumer ;
67
71
74
78
import static org .hamcrest .Matchers .is ;
75
79
import static org .junit .jupiter .api .Assertions .assertEquals ;
76
80
import static org .junit .jupiter .api .Assertions .assertTrue ;
81
+ import static org .mockito .ArgumentMatchers .any ;
77
82
import static org .mockito .Mockito .atLeastOnce ;
78
83
import static org .mockito .Mockito .doReturn ;
79
84
import static org .mockito .Mockito .lenient ;
80
85
import static org .mockito .Mockito .spy ;
86
+ import static org .mockito .Mockito .times ;
81
87
import static org .mockito .Mockito .verify ;
82
88
import static org .mockito .Mockito .when ;
83
89
@@ -128,21 +134,29 @@ void cleanup() {
128
134
kernel .shutdown ();
129
135
}
130
136
131
- // TODO: Consolidate this test helpers with ClientDevicesAuthServiceTest
132
- private void givenNucleusRunningWithConfig (String configFileName ) throws InterruptedException {
137
+ private void givenNucleusRunningWithConfig (String configFileName , Consumer <State > consumer ) throws InterruptedException {
133
138
CountDownLatch authServiceRunning = new CountDownLatch (1 );
134
139
kernel .parseArgs ("-r" , rootDir .toAbsolutePath ().toString (), "-i" ,
135
- getClass ().getResource (configFileName ).toString ());
140
+ Objects . requireNonNull ( getClass ().getResource (configFileName ) ).toString ());
136
141
kernel .getContext ().addGlobalStateChangeListener ((service , was , newState ) -> {
137
- if (ClientDevicesAuthService .CLIENT_DEVICES_AUTH_SERVICE_NAME .equals (service .getName ()) && service .getState ()
138
- .equals (State .RUNNING )) {
139
- authServiceRunning .countDown ();
142
+ if (ClientDevicesAuthService .CLIENT_DEVICES_AUTH_SERVICE_NAME .equals (service .getName ())) {
143
+ State serviceState = service .getState ();
144
+ consumer .accept (serviceState );
145
+
146
+ if (serviceState .equals (State .RUNNING )) {
147
+ authServiceRunning .countDown ();
148
+ }
140
149
}
141
150
});
142
151
kernel .launch ();
152
+
143
153
assertThat (authServiceRunning .await (30L , TimeUnit .SECONDS ), is (true ));
144
154
}
145
155
156
+ private void givenNucleusRunningWithConfig (String configFileName ) throws InterruptedException {
157
+ givenNucleusRunningWithConfig (configFileName , (State s ) -> {});
158
+ }
159
+
146
160
private static Pair <X509Certificate [], KeyPair []> givenRootAndIntermediateCA () throws NoSuchAlgorithmException ,
147
161
CertificateException ,
148
162
OperatorCreationException , CertIOException {
@@ -317,4 +331,47 @@ void GIVEN_managedCAConfiguration_WHEN_updatedToCustomCAConfiguration_THEN_serve
317
331
CertificateUpdateEvent event = eventRef .get ();
318
332
assertTrue (CertificateTestHelpers .wasCertificateIssuedBy (intermediateCA , event .getCertificate ()));
319
333
}
334
+
335
+ @ Test
336
+ void GIVEN_invalidConfigServiceBroken_WHEN_whenCorrected_THEN_serviceCanRecover (ExtensionContext context )
337
+ throws CertificateException , NoSuchAlgorithmException , OperatorCreationException , CertIOException ,
338
+ URISyntaxException , InterruptedException , KeyLoadingException , ServiceUnavailableException ,
339
+ CertificateChainLoadingException , ServiceLoadException , KeyStoreException {
340
+ ignoreExceptionOfType (context , InvalidConfigurationException .class );
341
+ ignoreExceptionOfType (context , URISyntaxException .class );
342
+ Pair <X509Certificate [], KeyPair []> credentials = givenRootAndIntermediateCA ();
343
+ X509Certificate [] chain = credentials .getLeft ();
344
+ KeyPair [] certificateKeys = credentials .getRight ();
345
+ KeyPair intermediateKeyPair = certificateKeys [0 ];
346
+
347
+ CountDownLatch authServiceBroken = new CountDownLatch (1 );
348
+ CountDownLatch recoveredFromBroken = new CountDownLatch (1 );
349
+ AtomicBoolean wasBroken = new AtomicBoolean (false );
350
+ Consumer <State > serviceStateChangeListener = (State s ) -> {
351
+ if (s .equals (State .BROKEN )) {
352
+ wasBroken .getAndSet (true );
353
+ authServiceBroken .countDown ();
354
+ }
355
+
356
+ if (wasBroken .get () && s .equals (State .RUNNING )) {
357
+ recoveredFromBroken .countDown ();
358
+ }
359
+ };
360
+
361
+
362
+ givenNucleusRunningWithConfig ("config.yaml" , serviceStateChangeListener );
363
+ verify (certificateStoreSpy , times (1 )).setCaKeyAndCertificateChain (any (), any (), any ());
364
+
365
+ // Do enough bad operations until the service goes belly up
366
+ givenCDAWithCustomCertificateAuthority (new URI ("file:///private.key" ), new URI ("" ));
367
+ assertThat (authServiceBroken .await (10L , TimeUnit .SECONDS ), is (true ));
368
+
369
+ // Do the right thing
370
+ URI privateKeyUri = new URI ("file:///private.key" );
371
+ URI certificateUri = new URI ("file:///certificate.pem" );
372
+ when (securityServiceMock .getKeyPair (privateKeyUri , certificateUri )).thenReturn (intermediateKeyPair );
373
+ doReturn (chain ).when (certificateStoreSpy ).loadCaCertificateChain (privateKeyUri , certificateUri );
374
+ givenCDAWithCustomCertificateAuthority (privateKeyUri , certificateUri );
375
+ assertThat (recoveredFromBroken .await (10L , TimeUnit .SECONDS ), is (true ));
376
+ }
320
377
}
0 commit comments