39
39
import org .opensaml .saml .saml2 .core .Assertion ;
40
40
import org .opensaml .saml .saml2 .core .AuthnRequest ;
41
41
42
+ import org .springframework .beans .factory .BeanCreationException ;
42
43
import org .springframework .beans .factory .annotation .Autowired ;
43
44
import org .springframework .context .ConfigurableApplicationContext ;
44
45
import org .springframework .context .annotation .Bean ;
62
63
import org .springframework .security .core .authority .SimpleGrantedAuthority ;
63
64
import org .springframework .security .core .authority .mapping .GrantedAuthoritiesMapper ;
64
65
import org .springframework .security .saml2 .Saml2Exception ;
66
+ import org .springframework .security .saml2 .core .Saml2Utils ;
65
67
import org .springframework .security .saml2 .core .TestSaml2X509Credentials ;
66
68
import org .springframework .security .saml2 .provider .service .authentication .OpenSamlAuthenticationProvider ;
67
69
import org .springframework .security .saml2 .provider .service .authentication .OpenSamlAuthenticationRequestFactory ;
77
79
import org .springframework .security .saml2 .provider .service .servlet .filter .Saml2WebSsoAuthenticationFilter ;
78
80
import org .springframework .security .saml2 .provider .service .web .Saml2AuthenticationRequestContextResolver ;
79
81
import org .springframework .security .web .FilterChainProxy ;
82
+ import org .springframework .security .web .SecurityFilterChain ;
80
83
import org .springframework .security .web .authentication .AuthenticationConverter ;
81
84
import org .springframework .security .web .context .HttpRequestResponseHolder ;
82
85
import org .springframework .security .web .context .HttpSessionSecurityContextRepository ;
89
92
import org .springframework .web .util .UriComponentsBuilder ;
90
93
91
94
import static org .assertj .core .api .Assertions .assertThat ;
95
+ import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
92
96
import static org .mockito .ArgumentMatchers .any ;
93
97
import static org .mockito .ArgumentMatchers .anyString ;
94
98
import static org .mockito .BDDMockito .given ;
@@ -115,6 +119,8 @@ public class Saml2LoginConfigurerTests {
115
119
116
120
private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=" ;
117
121
122
+ private static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock (AuthenticationConverter .class );
123
+
118
124
@ Autowired
119
125
private ConfigurableApplicationContext context ;
120
126
@@ -210,6 +216,33 @@ public void authenticateWhenCustomAuthenticationConverterThenUses() throws Excep
210
216
verify (CustomAuthenticationConverter .authenticationConverter ).convert (any (HttpServletRequest .class ));
211
217
}
212
218
219
+ @ Test
220
+ public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates () {
221
+ assertThatExceptionOfType (BeanCreationException .class )
222
+ .isThrownBy (() -> this .spring .register (CustomLoginProcessingUrlDefaultAuthenticationConverter .class )
223
+ .autowire ())
224
+ .havingRootCause ().isInstanceOf (IllegalStateException .class )
225
+ .withMessage ("loginProcessingUrl must contain {registrationId} path variable" );
226
+ }
227
+
228
+ @ Test
229
+ public void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate ()
230
+ throws Exception {
231
+ this .spring .register (CustomLoginProcessingUrlCustomAuthenticationConverter .class ).autowire ();
232
+ RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations .noCredentials ()
233
+ .assertingPartyDetails ((party ) -> party .verificationX509Credentials (
234
+ (c ) -> c .add (TestSaml2X509Credentials .relyingPartyVerifyingCredential ())))
235
+ .build ();
236
+ String response = new String (Saml2Utils .samlDecode (SIGNED_RESPONSE ));
237
+ given (AUTHENTICATION_CONVERTER .convert (any (HttpServletRequest .class )))
238
+ .willReturn (new Saml2AuthenticationToken (relyingPartyRegistration , response ));
239
+ // @formatter:off
240
+ MockHttpServletRequestBuilder request = post ("/my/custom/url" ).param ("SAMLResponse" , SIGNED_RESPONSE );
241
+ // @formatter:on
242
+ this .mvc .perform (request ).andExpect (redirectedUrl ("/" ));
243
+ verify (AUTHENTICATION_CONVERTER ).convert (any (HttpServletRequest .class ));
244
+ }
245
+
213
246
private void validateSaml2WebSsoAuthenticationFilterConfiguration () {
214
247
// get the OpenSamlAuthenticationProvider
215
248
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter (this .springSecurityFilterChain );
@@ -325,10 +358,10 @@ static class CustomAuthenticationRequestContextResolver extends WebSecurityConfi
325
358
protected void configure (HttpSecurity http ) throws Exception {
326
359
// @formatter:off
327
360
http
328
- .authorizeRequests ((authz ) -> authz
329
- .anyRequest ().authenticated ()
330
- )
331
- .saml2Login (withDefaults ());
361
+ .authorizeRequests ((authz ) -> authz
362
+ .anyRequest ().authenticated ()
363
+ )
364
+ .saml2Login (withDefaults ());
332
365
// @formatter:on
333
366
}
334
367
@@ -347,11 +380,11 @@ static class CustomAuthenticationRequestContextConverterResolver extends WebSecu
347
380
protected void configure (HttpSecurity http ) throws Exception {
348
381
// @formatter:off
349
382
http
350
- .authorizeRequests ((authz ) -> authz
351
- .anyRequest ().authenticated ()
352
- )
353
- .saml2Login ((saml2 ) -> {
354
- });
383
+ .authorizeRequests ((authz ) -> authz
384
+ .anyRequest ().authenticated ()
385
+ )
386
+ .saml2Login ((saml2 ) -> {
387
+ });
355
388
// @formatter:on
356
389
}
357
390
@@ -382,6 +415,41 @@ protected void configure(HttpSecurity http) throws Exception {
382
415
383
416
}
384
417
418
+ @ EnableWebSecurity
419
+ @ Import (Saml2LoginConfigBeans .class )
420
+ static class CustomLoginProcessingUrlDefaultAuthenticationConverter {
421
+
422
+ @ Bean
423
+ SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
424
+ // @formatter:off
425
+ http
426
+ .authorizeRequests ((authz ) -> authz .anyRequest ().authenticated ())
427
+ .saml2Login ((saml2 ) -> saml2 .loginProcessingUrl ("/my/custom/url" ));
428
+ // @formatter:on
429
+ return http .build ();
430
+ }
431
+
432
+ }
433
+
434
+ @ EnableWebSecurity
435
+ @ Import (Saml2LoginConfigBeans .class )
436
+ static class CustomLoginProcessingUrlCustomAuthenticationConverter {
437
+
438
+ @ Bean
439
+ SecurityFilterChain securityFilterChain (HttpSecurity http ) throws Exception {
440
+ // @formatter:off
441
+ http
442
+ .authorizeRequests ((authz ) -> authz .anyRequest ().authenticated ())
443
+ .saml2Login ((saml2 ) -> saml2
444
+ .loginProcessingUrl ("/my/custom/url" )
445
+ .authenticationConverter (AUTHENTICATION_CONVERTER )
446
+ );
447
+ // @formatter:on
448
+ return http .build ();
449
+ }
450
+
451
+ }
452
+
385
453
static class Saml2LoginConfigBeans {
386
454
387
455
@ Bean
0 commit comments