@@ -101,76 +101,99 @@ public ResilientOcspCertificateRevocationChecker(OcspClient ocspClient,
101101 @ Override
102102 public List <RevocationInfo > validateCertificateNotRevoked (X509Certificate subjectCertificate ,
103103 X509Certificate issuerCertificate ) throws AuthTokenException {
104- OcspService ocspService ;
104+ OcspService primaryService = resolvePrimaryOcspService (subjectCertificate );
105+
106+ if (primaryService .getFallbackService () == null ) {
107+ return List .of (request (primaryService , subjectCertificate , issuerCertificate , false ));
108+ }
109+
110+ List <RevocationInfo > revocationInfoList = new ArrayList <>();
111+ CheckedSupplier <RevocationInfo > fallbackSupplier = buildFallbackSupplier (primaryService , subjectCertificate ,
112+ issuerCertificate , revocationInfoList );
113+ CheckedSupplier <RevocationInfo > decoratedSupplier = decorateWithResilience (primaryService , subjectCertificate ,
114+ issuerCertificate , revocationInfoList , fallbackSupplier );
115+
116+ RevocationInfo revocationInfo = processResult (Try .of (decoratedSupplier ::get ), subjectCertificate , revocationInfoList );
117+ revocationInfoList .add (revocationInfo );
118+ return revocationInfoList ;
119+ }
120+
121+ private OcspService resolvePrimaryOcspService (X509Certificate subjectCertificate ) throws AuthTokenException {
105122 try {
106- ocspService = getOcspServiceProvider ().getService (subjectCertificate );
123+ return getOcspServiceProvider ().getService (subjectCertificate );
107124 } catch (CertificateException e ) {
108125 throw new ResilientUserCertificateOCSPCheckFailedException (new ValidationInfo (subjectCertificate , List .of ()));
109126 }
110- final FallbackOcspService firstFallbackService = ocspService .getFallbackService ();
111- if (firstFallbackService == null ) {
112- return List .of (request (ocspService , subjectCertificate , issuerCertificate , false ));
113- }
114-
115- CircuitBreaker circuitBreaker = circuitBreakerRegistry .circuitBreaker (ocspService .getAccessLocation ().toASCIIString ());
116-
117- List <RevocationInfo > revocationInfoList = new ArrayList <>();
127+ }
118128
119- CheckedSupplier <RevocationInfo > primarySupplier = () -> {
129+ private CheckedSupplier <RevocationInfo > buildFallbackSupplier (OcspService primaryService ,
130+ X509Certificate subjectCertificate ,
131+ X509Certificate issuerCertificate ,
132+ List <RevocationInfo > revocationInfoList ) {
133+ final FallbackOcspService firstFallbackService = primaryService .getFallbackService ();
134+ CheckedSupplier <RevocationInfo > firstFallbackSupplier = () -> {
120135 try {
121- return request (ocspService , subjectCertificate , issuerCertificate , false );
136+ return request (firstFallbackService , subjectCertificate , issuerCertificate , true );
122137 } catch (Exception e ) {
123138 createAndAddRevocationInfoToList (e , revocationInfoList );
124139 throw e ;
125140 }
126141 };
127- CheckedSupplier <RevocationInfo > firstFallbackSupplier = () -> {
142+ OcspService secondFallbackService = firstFallbackService .getNextFallback ();
143+ if (secondFallbackService == null ) {
144+ return firstFallbackSupplier ;
145+ }
146+ CheckedSupplier <RevocationInfo > secondFallbackSupplier = () -> {
128147 try {
129- return request (firstFallbackService , subjectCertificate , issuerCertificate , true );
148+ return request (secondFallbackService , subjectCertificate , issuerCertificate , true );
149+ } catch (Exception e ) {
150+ createAndAddRevocationInfoToList (e , revocationInfoList );
151+ throw e ;
152+ }
153+ };
154+ return () -> {
155+ try {
156+ return firstFallbackSupplier .get ();
157+ } catch (ResilientUserCertificateRevokedException e ) {
158+ // NOTE: ResilientUserCertificateRevokedException must be re-thrown before the generic
159+ // catch (Exception) block. Without this, a "revoked" verdict from the first fallback would
160+ // be swallowed, and the second fallback could silently override it with a "good" response.
161+ throw e ;
162+ } catch (Exception e ) {
163+ return secondFallbackSupplier .get ();
164+ }
165+ };
166+ }
167+
168+ private CheckedSupplier <RevocationInfo > decorateWithResilience (OcspService primaryService ,
169+ X509Certificate subjectCertificate ,
170+ X509Certificate issuerCertificate ,
171+ List <RevocationInfo > revocationInfoList ,
172+ CheckedSupplier <RevocationInfo > fallbackSupplier
173+ ) {
174+ CheckedSupplier <RevocationInfo > primarySupplier = () -> {
175+ try {
176+ return request (primaryService , subjectCertificate , issuerCertificate , false );
130177 } catch (Exception e ) {
131178 createAndAddRevocationInfoToList (e , revocationInfoList );
132179 throw e ;
133180 }
134181 };
135- OcspService secondFallbackService = firstFallbackService .getNextFallback ();
136- CheckedSupplier <RevocationInfo > fallbackSupplier ;
137- if (secondFallbackService == null ) {
138- fallbackSupplier = firstFallbackSupplier ;
139- } else {
140- CheckedSupplier <RevocationInfo > secondFallbackSupplier = () -> {
141- try {
142- return request (secondFallbackService , subjectCertificate , issuerCertificate , true );
143- } catch (Exception e ) {
144- createAndAddRevocationInfoToList (e , revocationInfoList );
145- throw e ;
146- }
147- };
148- fallbackSupplier = () -> {
149- try {
150- return firstFallbackSupplier .get ();
151- } catch (ResilientUserCertificateRevokedException e ) {
152- // NOTE: ResilientUserCertificateRevokedException must be re-thrown before the generic
153- // catch (Exception) block. Without this, a "revoked" verdict from the first fallback would
154- // be swallowed, and the second fallback could silently override it with a "good" response.
155- throw e ;
156- } catch (Exception e ) {
157- return secondFallbackSupplier .get ();
158- }
159- };
160- }
161182 Decorators .DecorateCheckedSupplier <RevocationInfo > decorateCheckedSupplier = Decorators .ofCheckedSupplier (primarySupplier );
162183 if (retryRegistry != null ) {
163- Retry retry = retryRegistry .retry (ocspService .getAccessLocation ().toASCIIString ());
184+ Retry retry = retryRegistry .retry (primaryService .getAccessLocation ().toASCIIString ());
164185 decorateCheckedSupplier .withRetry (retry );
165186 }
187+ CircuitBreaker circuitBreaker = circuitBreakerRegistry .circuitBreaker (primaryService .getAccessLocation ().toASCIIString ());
166188 decorateCheckedSupplier .withCircuitBreaker (circuitBreaker )
167189 .withFallback (List .of (ResilientUserCertificateOCSPCheckFailedException .class , CallNotPermittedException .class ), e -> fallbackSupplier .get ());
168190
169- CheckedSupplier <RevocationInfo > decoratedSupplier = decorateCheckedSupplier .decorate ();
170-
171- Try <RevocationInfo > result = Try .of (decoratedSupplier ::get );
191+ return decorateCheckedSupplier .decorate ();
192+ }
172193
173- RevocationInfo revocationInfo = result .getOrElseThrow (throwable -> {
194+ private RevocationInfo processResult (Try <RevocationInfo > result , X509Certificate subjectCertificate ,
195+ List <RevocationInfo > revocationInfoList ) throws AuthTokenException {
196+ return result .getOrElseThrow (throwable -> {
174197 if (throwable instanceof ResilientUserCertificateOCSPCheckFailedException exception ) {
175198 exception .setValidationInfo (new ValidationInfo (subjectCertificate , revocationInfoList ));
176199 return exception ;
@@ -182,9 +205,6 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
182205 // TODO This should always be TaraUserCertificateOCSPCheckFailedException when reached?
183206 return new ResilientUserCertificateOCSPCheckFailedException (new ValidationInfo (subjectCertificate , revocationInfoList ));
184207 });
185-
186- revocationInfoList .add (revocationInfo );
187- return revocationInfoList ;
188208 }
189209
190210 private void createAndAddRevocationInfoToList (Throwable throwable , List <RevocationInfo > revocationInfoList ) {
0 commit comments