1
1
/*
2
- * Copyright 2002-2020 the original author or authors.
2
+ * Copyright 2002-2021 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .security .config .annotation .web .configurers .saml2 ;
18
18
19
- import java .io .ByteArrayOutputStream ;
20
19
import java .io .IOException ;
21
20
import java .net .URLDecoder ;
22
- import java .nio .charset .StandardCharsets ;
23
21
import java .time .Duration ;
24
22
import java .util .Arrays ;
25
23
import java .util .Base64 ;
26
24
import java .util .Collection ;
27
25
import java .util .Collections ;
28
- import java .util .zip .Inflater ;
29
- import java .util .zip .InflaterOutputStream ;
30
26
31
27
import javax .servlet .ServletException ;
32
28
import javax .servlet .http .HttpServletRequest ;
29
+ import javax .servlet .http .HttpServletResponse ;
33
30
34
31
import org .junit .After ;
35
32
import org .junit .Assert ;
36
33
import org .junit .Before ;
37
34
import org .junit .Rule ;
38
35
import org .junit .Test ;
36
+ import org .mockito .ArgumentCaptor ;
39
37
import org .opensaml .saml .saml2 .core .Assertion ;
40
38
import org .opensaml .saml .saml2 .core .AuthnRequest ;
41
39
61
59
import org .springframework .security .core .GrantedAuthority ;
62
60
import org .springframework .security .core .authority .SimpleGrantedAuthority ;
63
61
import org .springframework .security .core .authority .mapping .GrantedAuthoritiesMapper ;
64
- import org .springframework .security .saml2 .Saml2Exception ;
62
+ import org .springframework .security .saml2 .core .Saml2ErrorCodes ;
63
+ import org .springframework .security .saml2 .core .Saml2Utils ;
65
64
import org .springframework .security .saml2 .core .TestSaml2X509Credentials ;
66
65
import org .springframework .security .saml2 .provider .service .authentication .OpenSamlAuthenticationProvider ;
67
66
import org .springframework .security .saml2 .provider .service .authentication .OpenSamlAuthenticationRequestFactory ;
68
67
import org .springframework .security .saml2 .provider .service .authentication .Saml2Authentication ;
68
+ import org .springframework .security .saml2 .provider .service .authentication .Saml2AuthenticationException ;
69
69
import org .springframework .security .saml2 .provider .service .authentication .Saml2AuthenticationRequestContext ;
70
70
import org .springframework .security .saml2 .provider .service .authentication .Saml2AuthenticationRequestFactory ;
71
71
import org .springframework .security .saml2 .provider .service .authentication .Saml2AuthenticationToken ;
78
78
import org .springframework .security .saml2 .provider .service .web .Saml2AuthenticationRequestContextResolver ;
79
79
import org .springframework .security .web .FilterChainProxy ;
80
80
import org .springframework .security .web .authentication .AuthenticationConverter ;
81
+ import org .springframework .security .web .authentication .AuthenticationFailureHandler ;
81
82
import org .springframework .security .web .context .HttpRequestResponseHolder ;
82
83
import org .springframework .security .web .context .HttpSessionSecurityContextRepository ;
83
84
import org .springframework .security .web .context .SecurityContextRepository ;
@@ -188,7 +189,7 @@ public void authenticationRequestWhenAuthnRequestContextConverterThenUses() thro
188
189
UriComponents components = UriComponentsBuilder .fromHttpUrl (result .getResponse ().getRedirectedUrl ()).build ();
189
190
String samlRequest = components .getQueryParams ().getFirst ("SAMLRequest" );
190
191
String decoded = URLDecoder .decode (samlRequest , "UTF-8" );
191
- String inflated = samlInflate (samlDecode (decoded ));
192
+ String inflated = Saml2Utils . samlInflate (Saml2Utils . samlDecode (decoded ));
192
193
assertThat (inflated ).contains ("ForceAuthn=\" true\" " );
193
194
}
194
195
@@ -199,7 +200,7 @@ public void authenticateWhenCustomAuthenticationConverterThenUses() throws Excep
199
200
.assertingPartyDetails ((party ) -> party .verificationX509Credentials (
200
201
(c ) -> c .add (TestSaml2X509Credentials .relyingPartyVerifyingCredential ())))
201
202
.build ();
202
- String response = new String (samlDecode (SIGNED_RESPONSE ));
203
+ String response = new String (Saml2Utils . samlDecode (SIGNED_RESPONSE ));
203
204
given (CustomAuthenticationConverter .authenticationConverter .convert (any (HttpServletRequest .class )))
204
205
.willReturn (new Saml2AuthenticationToken (relyingPartyRegistration , response ));
205
206
// @formatter:off
@@ -210,6 +211,24 @@ public void authenticateWhenCustomAuthenticationConverterThenUses() throws Excep
210
211
verify (CustomAuthenticationConverter .authenticationConverter ).convert (any (HttpServletRequest .class ));
211
212
}
212
213
214
+ @ Test
215
+ public void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses () throws Exception {
216
+ this .spring .register (CustomAuthenticationFailureHandler .class ).autowire ();
217
+ byte [] invalidDeflated = Saml2Utils .invalidSamlDeflate ("response" );
218
+ String encoded = Saml2Utils .samlEncode (invalidDeflated );
219
+ MockHttpServletRequestBuilder request = get ("/login/saml2/sso/registration-id" ).queryParam ("SAMLResponse" ,
220
+ encoded );
221
+ this .mvc .perform (request );
222
+ ArgumentCaptor <Saml2AuthenticationException > captor = ArgumentCaptor
223
+ .forClass (Saml2AuthenticationException .class );
224
+ verify (CustomAuthenticationFailureHandler .authenticationFailureHandler ).onAuthenticationFailure (
225
+ any (HttpServletRequest .class ), any (HttpServletResponse .class ), captor .capture ());
226
+ Saml2AuthenticationException exception = captor .getValue ();
227
+ assertThat (exception .getSaml2Error ().getErrorCode ()).isEqualTo (Saml2ErrorCodes .INVALID_RESPONSE );
228
+ assertThat (exception .getSaml2Error ().getDescription ()).isEqualTo ("Unable to inflate string" );
229
+ assertThat (exception .getCause ()).isInstanceOf (IOException .class );
230
+ }
231
+
213
232
private void validateSaml2WebSsoAuthenticationFilterConfiguration () {
214
233
// get the OpenSamlAuthenticationProvider
215
234
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter (this .springSecurityFilterChain );
@@ -244,26 +263,6 @@ private void performSaml2Login(String expected) throws IOException, ServletExcep
244
263
.hasToString (expected );
245
264
}
246
265
247
- private static org .apache .commons .codec .binary .Base64 BASE64 = new org .apache .commons .codec .binary .Base64 (0 ,
248
- new byte [] { '\n' });
249
-
250
- private static byte [] samlDecode (String s ) {
251
- return BASE64 .decode (s );
252
- }
253
-
254
- private static String samlInflate (byte [] b ) {
255
- try {
256
- ByteArrayOutputStream out = new ByteArrayOutputStream ();
257
- InflaterOutputStream iout = new InflaterOutputStream (out , new Inflater (true ));
258
- iout .write (b );
259
- iout .finish ();
260
- return new String (out .toByteArray (), StandardCharsets .UTF_8 );
261
- }
262
- catch (IOException ex ) {
263
- throw new Saml2Exception ("Unable to inflate string" , ex );
264
- }
265
- }
266
-
267
266
private static AuthenticationManager getAuthenticationManagerMock (String role ) {
268
267
return new AuthenticationManager () {
269
268
@ Override
@@ -314,6 +313,21 @@ public <O extends OpenSamlAuthenticationProvider> O postProcess(O provider) {
314
313
315
314
}
316
315
316
+ @ EnableWebSecurity
317
+ @ Import (Saml2LoginConfigBeans .class )
318
+ static class CustomAuthenticationFailureHandler extends WebSecurityConfigurerAdapter {
319
+
320
+ static final AuthenticationFailureHandler authenticationFailureHandler = mock (
321
+ AuthenticationFailureHandler .class );
322
+
323
+ @ Override
324
+ protected void configure (HttpSecurity http ) throws Exception {
325
+ http .authorizeRequests ((authz ) -> authz .anyRequest ().authenticated ())
326
+ .saml2Login ((saml2 ) -> saml2 .failureHandler (authenticationFailureHandler ));
327
+ }
328
+
329
+ }
330
+
317
331
@ EnableWebSecurity
318
332
@ Import (Saml2LoginConfigBeans .class )
319
333
static class CustomAuthenticationRequestContextResolver extends WebSecurityConfigurerAdapter {
0 commit comments