Skip to content

Add Session Index Support #10784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,6 +17,7 @@
package org.springframework.security.saml2.provider.service.authentication;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

Expand All @@ -34,14 +35,22 @@ public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPri

private final Map<String, List<Object>> attributes;

private final List<String> sessionIndexes;

private String registrationId;

public DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes) {
this(name, attributes, Collections.emptyList());
}

public DefaultSaml2AuthenticatedPrincipal(String name, Map<String, List<Object>> attributes,
List<String> sessionIndexes) {
Assert.notNull(name, "name cannot be null");
Assert.notNull(attributes, "attributes cannot be null");
Assert.notNull(sessionIndexes, "sessionIndexes cannot be null");
this.name = name;
this.attributes = attributes;
this.registrationId = null;
this.sessionIndexes = sessionIndexes;
}

@Override
Expand All @@ -54,6 +63,11 @@ public Map<String, List<Object>> getAttributes() {
return this.attributes;
}

@Override
public List<String> getSessionIndexes() {
return this.sessionIndexes;
}

@Override
public String getRelyingPartyRegistrationId() {
return this.registrationId;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -76,4 +76,8 @@ default String getRelyingPartyRegistrationId() {
return null;
}

default List<String> getSessionIndexes() {
return Collections.emptyList();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,10 +31,12 @@
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.LogoutRequest;
import org.opensaml.saml.saml2.core.NameID;
import org.opensaml.saml.saml2.core.SessionIndex;
import org.opensaml.saml.saml2.core.impl.IssuerBuilder;
import org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder;
import org.opensaml.saml.saml2.core.impl.LogoutRequestMarshaller;
import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
import org.opensaml.saml.saml2.core.impl.SessionIndexBuilder;
import org.w3c.dom.Element;

import org.springframework.security.core.Authentication;
Expand Down Expand Up @@ -67,6 +69,8 @@ final class OpenSamlLogoutRequestResolver {

private final NameIDBuilder nameIdBuilder;

private final SessionIndexBuilder sessionIndexBuilder;

private final LogoutRequestBuilder logoutRequestBuilder;

private final RelyingPartyRegistrationResolver relyingPartyRegistrationResolver;
Expand All @@ -87,6 +91,9 @@ final class OpenSamlLogoutRequestResolver {
Assert.notNull(this.issuerBuilder, "issuerBuilder must be configured in OpenSAML");
this.nameIdBuilder = (NameIDBuilder) registry.getBuilderFactory().getBuilder(NameID.DEFAULT_ELEMENT_NAME);
Assert.notNull(this.nameIdBuilder, "nameIdBuilder must be configured in OpenSAML");
this.sessionIndexBuilder = (SessionIndexBuilder) registry.getBuilderFactory()
.getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME);
Assert.notNull(this.sessionIndexBuilder, "sessionIndexBuilder must be configured in OpenSAML");
}

/**
Expand Down Expand Up @@ -122,6 +129,14 @@ Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentica
NameID nameId = this.nameIdBuilder.buildObject();
nameId.setValue(authentication.getName());
logoutRequest.setNameID(nameId);
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();
for (String index : principal.getSessionIndexes()) {
SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
sessionIndex.setSessionIndex(index);
logoutRequest.getSessionIndexes().add(sessionIndex);
}
}
logoutRequestConsumer.accept(registration, logoutRequest);
if (logoutRequest.getID() == null) {
logoutRequest.setID("LR" + UUID.randomUUID());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -57,6 +57,7 @@
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Attribute;
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.Condition;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.OneTimeUse;
Expand Down Expand Up @@ -425,7 +426,9 @@ public static Converter<ResponseToken, Saml2Authentication> createDefaultRespons
Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
String username = assertion.getSubject().getNameID().getValue();
Map<String, List<Object>> attributes = getAssertionAttributes(assertion);
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes);
List<String> sessionIndexes = getSessionIndexes(assertion);
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes,
sessionIndexes);
String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();
principal.setRelyingPartyRegistrationId(registrationId);
return new Saml2Authentication(principal, token.getSaml2Response(),
Expand Down Expand Up @@ -617,6 +620,14 @@ private static Map<String, List<Object>> getAssertionAttributes(Assertion assert
return attributeMap;
}

private static List<String> getSessionIndexes(Assertion assertion) {
List<String> sessionIndexes = new ArrayList<>();
for (AuthnStatement statement : assertion.getAuthnStatements()) {
sessionIndexes.add(statement.getSessionIndex());
}
return sessionIndexes;
}

private static Object getXmlObjectValue(XMLObject xmlObject) {
if (xmlObject instanceof XSAny) {
return ((XSAny) xmlObject).getTextContent();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -247,6 +247,7 @@ public void authenticateWhenAssertionContainsAttributesThenItSucceeds() {
expected.put("registeredDate", Collections.singletonList(registeredDate));
assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe");
assertThat(principal.getAttributes()).isEqualTo(expected);
assertThat(principal.getSessionIndexes()).contains("session-index");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -49,6 +49,7 @@
import org.opensaml.saml.saml2.core.AttributeStatement;
import org.opensaml.saml.saml2.core.AttributeValue;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.AuthnStatement;
import org.opensaml.saml.saml2.core.Conditions;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.EncryptedAttribute;
Expand Down Expand Up @@ -153,6 +154,9 @@ static Assertion assertion(String username, String issuerEntityId, String recipi
confirmationData.setRecipient(recipientUri);
subjectConfirmation.setSubjectConfirmationData(confirmationData);
assertion.getSubject().getSubjectConfirmations().add(subjectConfirmation);
AuthnStatement statement = build(AuthnStatement.DEFAULT_ELEMENT_NAME);
statement.setSessionIndex("session-index");
assertion.getAuthnStatements().add(statement);
return assertion;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -86,10 +87,13 @@ public void resolvePostWhenAuthenticatedThenIncludesName() {
Saml2MessageBinding binding = registration.getAssertingPartyDetails().getSingleLogoutServiceBinding();
LogoutRequest logoutRequest = getLogoutRequest(saml2LogoutRequest.getSamlRequest(), binding);
assertThat(logoutRequest.getNameID().getValue()).isEqualTo(authentication.getName());
assertThat(logoutRequest.getSessionIndexes()).hasSize(1);
assertThat(logoutRequest.getSessionIndexes().get(0).getSessionIndex()).isEqualTo("session-index");
}

private Saml2Authentication authentication(RelyingPartyRegistration registration) {
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", new HashMap<>());
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", new HashMap<>(),
Arrays.asList("session-index"));
principal.setRelyingPartyRegistrationId(registration.getRegistrationId());
return new Saml2Authentication(principal, "response", new ArrayList<>());
}
Expand Down