Skip to content

Commit 36c7b91

Browse files
chschujzheaux
authored andcommitted
SAML 2.0 Single Logout Uses Saml2AuthenticationInfo
This allows SLO to be triggered without the authentication principal needing to implement a given interface. Issue gh-10820
1 parent ffd6e3c commit 36c7b91

File tree

9 files changed

+108
-46
lines changed

9 files changed

+108
-46
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
3434
import org.springframework.security.core.Authentication;
3535
import org.springframework.security.core.context.SecurityContextHolderStrategy;
36-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
36+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
3737
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
3838
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
3939
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
@@ -531,10 +531,7 @@ private static class Saml2RequestMatcher implements RequestMatcher {
531531
@Override
532532
public boolean matches(HttpServletRequest request) {
533533
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
534-
if (authentication == null) {
535-
return false;
536-
}
537-
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
534+
return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
538535
}
539536

540537
}

config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import org.springframework.security.core.Authentication;
3232
import org.springframework.security.core.context.SecurityContextHolder;
3333
import org.springframework.security.core.context.SecurityContextHolderStrategy;
34-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
34+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
3535
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
3636
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
3737
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
@@ -236,10 +236,7 @@ public static class Saml2RequestMatcher implements RequestMatcher {
236236
@Override
237237
public boolean matches(HttpServletRequest request) {
238238
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
239-
if (authentication == null) {
240-
return false;
241-
}
242-
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
239+
return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
243240
}
244241

245242
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticatedPrincipal.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* @author Clement Stoquart
3232
* @since 5.2.2
3333
*/
34-
public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
34+
public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal, Saml2AuthenticationInfo {
3535

3636
/**
3737
* Get the first value of Saml2 token attribute by name
@@ -72,10 +72,12 @@ default Map<String, List<Object>> getAttributes() {
7272
* @return the {@link RelyingPartyRegistration} identifier
7373
* @since 5.6
7474
*/
75+
@Override
7576
default String getRelyingPartyRegistrationId() {
7677
return null;
7778
}
7879

80+
@Override
7981
default List<String> getSessionIndexes() {
8082
return Collections.emptyList();
8183
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.saml2.provider.service.authentication;
18+
19+
import java.util.List;
20+
21+
import org.opensaml.saml.saml2.core.SessionIndex;
22+
23+
import org.springframework.security.core.Authentication;
24+
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
25+
26+
/**
27+
* Additional SAML 2.0 authentication information
28+
*
29+
* <p>
30+
* SAML 2.0 Single Logout requires that the {@link Authentication#getPrincipal()
31+
* authenticated principal} or the {@link Authentication} itself implements this
32+
* interface.
33+
*
34+
* @author Christian Schuster
35+
*/
36+
public interface Saml2AuthenticationInfo {
37+
38+
/**
39+
* Get the {@link RelyingPartyRegistration} identifier
40+
* @return the {@link RelyingPartyRegistration} identifier
41+
*/
42+
String getRelyingPartyRegistrationId();
43+
44+
/**
45+
* Get the {@link SessionIndex} values of the authenticated principal
46+
* @return the {@link SessionIndex} values of the authenticated principal
47+
*/
48+
List<String> getSessionIndexes();
49+
50+
/**
51+
* Try to obtain a {@link Saml2AuthenticationInfo} instance from an
52+
* {@link Authentication}
53+
*
54+
* <p>
55+
* The result is either the {@link Authentication#getPrincipal() authenticated
56+
* principal}, the {@link Authentication} itself, or {@code null}.
57+
*
58+
* <p>
59+
* Returning {@code null} indicates that the given {@link Authentication} does not
60+
* represent a SAML 2.0 authentication.
61+
* @param authentication the {@link Authentication}
62+
* @return the {@link Saml2AuthenticationInfo} or {@code null} if unavailable
63+
*/
64+
static Saml2AuthenticationInfo fromAuthentication(Authentication authentication) {
65+
if (authentication == null) {
66+
return null;
67+
}
68+
Object principal = authentication.getPrincipal();
69+
if (principal instanceof Saml2AuthenticationInfo) {
70+
return (Saml2AuthenticationInfo) principal;
71+
}
72+
if (authentication instanceof Saml2AuthenticationInfo) {
73+
return (Saml2AuthenticationInfo) authentication;
74+
}
75+
return null;
76+
}
77+
78+
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutRequestResolver.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import org.springframework.security.core.Authentication;
4343
import org.springframework.security.saml2.core.OpenSamlInitializationService;
4444
import org.springframework.security.saml2.core.Saml2ParameterNames;
45-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
45+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
4646
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
4747
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
4848
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
@@ -149,9 +149,9 @@ public Saml2LogoutRequest resolve(HttpServletRequest request, Authentication aut
149149
NameID nameId = this.nameIdBuilder.buildObject();
150150
nameId.setValue(authentication.getName());
151151
logoutRequest.setNameID(nameId);
152-
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
153-
Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();
154-
for (String index : principal.getSessionIndexes()) {
152+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
153+
if (info != null) {
154+
for (String index : info.getSessionIndexes()) {
155155
SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
156156
sessionIndex.setValue(index);
157157
logoutRequest.getSessionIndexes().add(sessionIndex);
@@ -191,12 +191,9 @@ private String getRegistrationId(Authentication authentication) {
191191
if (this.logger.isTraceEnabled()) {
192192
this.logger.trace("Attempting to resolve registrationId from " + authentication);
193193
}
194-
if (authentication == null) {
195-
return null;
196-
}
197-
Object principal = authentication.getPrincipal();
198-
if (principal instanceof Saml2AuthenticatedPrincipal) {
199-
return ((Saml2AuthenticatedPrincipal) principal).getRelyingPartyRegistrationId();
194+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
195+
if (info != null) {
196+
return info.getRelyingPartyRegistrationId();
200197
}
201198
return null;
202199
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutRequestValidatorParametersResolver.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import org.springframework.security.saml2.core.OpenSamlInitializationService;
2525
import org.springframework.security.saml2.core.Saml2Error;
2626
import org.springframework.security.saml2.core.Saml2ParameterNames;
27-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
2827
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
28+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
2929
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
3030
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
3131
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
@@ -130,11 +130,9 @@ private String getRegistrationId(RequestMatcher.MatchResult result, Authenticati
130130
if (registrationId != null) {
131131
return registrationId;
132132
}
133-
if (authentication == null) {
134-
return null;
135-
}
136-
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
137-
return principal.getRelyingPartyRegistrationId();
133+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
134+
if (info != null) {
135+
return info.getRelyingPartyRegistrationId();
138136
}
139137
return null;
140138
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/BaseOpenSamlLogoutResponseResolver.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
import org.springframework.security.saml2.core.Saml2Error;
4747
import org.springframework.security.saml2.core.Saml2ErrorCodes;
4848
import org.springframework.security.saml2.core.Saml2ParameterNames;
49-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
5049
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
50+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
5151
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;
5252
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
5353
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
@@ -217,12 +217,9 @@ private String getRegistrationId(Authentication authentication) {
217217
if (this.logger.isTraceEnabled()) {
218218
this.logger.trace("Attempting to resolve registrationId from " + authentication);
219219
}
220-
if (authentication == null) {
221-
return null;
222-
}
223-
Object principal = authentication.getPrincipal();
224-
if (principal instanceof Saml2AuthenticatedPrincipal) {
225-
return ((Saml2AuthenticatedPrincipal) principal).getRelyingPartyRegistrationId();
220+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
221+
if (info != null) {
222+
return info.getRelyingPartyRegistrationId();
226223
}
227224
return null;
228225
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutRequestFilter.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
import org.springframework.security.saml2.core.Saml2Error;
3434
import org.springframework.security.saml2.core.Saml2ErrorCodes;
3535
import org.springframework.security.saml2.core.Saml2ParameterNames;
36-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
3736
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
37+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
3838
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
3939
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
4040
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
@@ -329,11 +329,9 @@ private String getRegistrationId(RequestMatcher.MatchResult result, Authenticati
329329
if (registrationId != null) {
330330
return registrationId;
331331
}
332-
if (authentication == null) {
333-
return null;
334-
}
335-
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
336-
return principal.getRelyingPartyRegistrationId();
332+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
333+
if (info != null) {
334+
return info.getRelyingPartyRegistrationId();
337335
}
338336
return null;
339337
}

saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlLogoutRequestValidatorParametersResolver.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
import org.springframework.security.saml2.core.OpenSamlInitializationService;
2929
import org.springframework.security.saml2.core.Saml2Error;
3030
import org.springframework.security.saml2.core.Saml2ParameterNames;
31-
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
3231
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
32+
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
3333
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
3434
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
3535
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
@@ -144,11 +144,9 @@ private String getRegistrationId(RequestMatcher.MatchResult result, Authenticati
144144
if (registrationId != null) {
145145
return registrationId;
146146
}
147-
if (authentication == null) {
148-
return null;
149-
}
150-
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
151-
return principal.getRelyingPartyRegistrationId();
147+
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
148+
if (info != null) {
149+
return info.getRelyingPartyRegistrationId();
152150
}
153151
return null;
154152
}

0 commit comments

Comments
 (0)