KEYCLOAK-3864 Add support for SAML2 <Extensions> element in protocol messages
This commit is contained in:
parent
6baf9b89fe
commit
904a5c3ca5
25 changed files with 539 additions and 5 deletions
|
@ -53,6 +53,18 @@
|
||||||
<groupId>org.apache.santuario</groupId>
|
<groupId>org.apache.santuario</groupId>
|
||||||
<artifactId>xmlsec</artifactId>
|
<artifactId>xmlsec</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-core</artifactId>
|
||||||
|
<version>1.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
|
@ -25,15 +25,19 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author pedroigor
|
* @author pedroigor
|
||||||
*/
|
*/
|
||||||
public class SAML2AuthnRequestBuilder {
|
public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuilder<SAML2AuthnRequestBuilder> {
|
||||||
|
|
||||||
private final AuthnRequestType authnRequestType;
|
private final AuthnRequestType authnRequestType;
|
||||||
protected String destination;
|
protected String destination;
|
||||||
protected String issuer;
|
protected String issuer;
|
||||||
|
protected final List<NodeGenerator> extensions = new LinkedList<>();
|
||||||
|
|
||||||
public SAML2AuthnRequestBuilder destination(String destination) {
|
public SAML2AuthnRequestBuilder destination(String destination) {
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
|
@ -45,6 +49,12 @@ public class SAML2AuthnRequestBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAML2AuthnRequestBuilder addExtension(NodeGenerator extension) {
|
||||||
|
this.extensions.add(extension);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SAML2AuthnRequestBuilder() {
|
public SAML2AuthnRequestBuilder() {
|
||||||
try {
|
try {
|
||||||
this.authnRequestType = new AuthnRequestType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant());
|
this.authnRequestType = new AuthnRequestType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant());
|
||||||
|
@ -90,6 +100,14 @@ public class SAML2AuthnRequestBuilder {
|
||||||
|
|
||||||
authnRequestType.setDestination(URI.create(this.destination));
|
authnRequestType.setDestination(URI.create(this.destination));
|
||||||
|
|
||||||
|
if (! this.extensions.isEmpty()) {
|
||||||
|
ExtensionsType extensionsType = new ExtensionsType();
|
||||||
|
for (NodeGenerator extension : this.extensions) {
|
||||||
|
extensionsType.addExtension(extension);
|
||||||
|
}
|
||||||
|
authnRequestType.setExtensions(extensionsType);
|
||||||
|
}
|
||||||
|
|
||||||
return new SAML2Request().convert(authnRequestType);
|
return new SAML2Request().convert(authnRequestType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Could not convert " + authnRequestType + " to a document.", e);
|
throw new RuntimeException("Could not convert " + authnRequestType + " to a document.", e);
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
|
|
||||||
package org.keycloak.saml;
|
package org.keycloak.saml;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
@ -32,11 +35,12 @@ import org.w3c.dom.Document;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SAML2ErrorResponseBuilder {
|
public class SAML2ErrorResponseBuilder implements SamlProtocolExtensionsAwareBuilder<SAML2ErrorResponseBuilder> {
|
||||||
|
|
||||||
protected String status;
|
protected String status;
|
||||||
protected String destination;
|
protected String destination;
|
||||||
protected String issuer;
|
protected String issuer;
|
||||||
|
protected final List<NodeGenerator> extensions = new LinkedList<>();
|
||||||
|
|
||||||
public SAML2ErrorResponseBuilder status(String status) {
|
public SAML2ErrorResponseBuilder status(String status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
@ -53,6 +57,11 @@ public class SAML2ErrorResponseBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAML2ErrorResponseBuilder addExtension(NodeGenerator extension) {
|
||||||
|
this.extensions.add(extension);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Document buildDocument() throws ProcessingException {
|
public Document buildDocument() throws ProcessingException {
|
||||||
|
|
||||||
|
@ -66,6 +75,14 @@ public class SAML2ErrorResponseBuilder {
|
||||||
statusResponse.setIssuer(issuer);
|
statusResponse.setIssuer(issuer);
|
||||||
statusResponse.setDestination(destination);
|
statusResponse.setDestination(destination);
|
||||||
|
|
||||||
|
if (! this.extensions.isEmpty()) {
|
||||||
|
ExtensionsType extensionsType = new ExtensionsType();
|
||||||
|
for (NodeGenerator extension : this.extensions) {
|
||||||
|
extensionsType.addExtension(extension);
|
||||||
|
}
|
||||||
|
statusResponse.setExtensions(extensionsType);
|
||||||
|
}
|
||||||
|
|
||||||
SAML2Response saml2Response = new SAML2Response();
|
SAML2Response saml2Response = new SAML2Response();
|
||||||
return saml2Response.convert(statusResponse);
|
return saml2Response.convert(statusResponse);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
|
|
|
@ -39,6 +39,9 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
import static org.keycloak.saml.common.util.StringUtil.isNotNull;
|
import static org.keycloak.saml.common.util.StringUtil.isNotNull;
|
||||||
|
|
||||||
|
@ -49,7 +52,7 @@ import static org.keycloak.saml.common.util.StringUtil.isNotNull;
|
||||||
*
|
*
|
||||||
* @author bburke@redhat.com
|
* @author bburke@redhat.com
|
||||||
*/
|
*/
|
||||||
public class SAML2LoginResponseBuilder {
|
public class SAML2LoginResponseBuilder implements SamlProtocolExtensionsAwareBuilder<SAML2LoginResponseBuilder> {
|
||||||
protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
|
protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
|
||||||
|
|
||||||
protected String destination;
|
protected String destination;
|
||||||
|
@ -64,6 +67,7 @@ public class SAML2LoginResponseBuilder {
|
||||||
protected String authMethod;
|
protected String authMethod;
|
||||||
protected String requestIssuer;
|
protected String requestIssuer;
|
||||||
protected String sessionIndex;
|
protected String sessionIndex;
|
||||||
|
protected final List<NodeGenerator> extensions = new LinkedList<>();
|
||||||
|
|
||||||
|
|
||||||
public SAML2LoginResponseBuilder sessionIndex(String sessionIndex) {
|
public SAML2LoginResponseBuilder sessionIndex(String sessionIndex) {
|
||||||
|
@ -136,6 +140,12 @@ public class SAML2LoginResponseBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAML2LoginResponseBuilder addExtension(NodeGenerator extension) {
|
||||||
|
this.extensions.add(extension);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Document buildDocument(ResponseType responseType) throws ConfigurationException, ProcessingException {
|
public Document buildDocument(ResponseType responseType) throws ConfigurationException, ProcessingException {
|
||||||
Document samlResponseDocument = null;
|
Document samlResponseDocument = null;
|
||||||
|
|
||||||
|
@ -207,6 +217,14 @@ public class SAML2LoginResponseBuilder {
|
||||||
assertion.addStatement(authnStatement);
|
assertion.addStatement(authnStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! this.extensions.isEmpty()) {
|
||||||
|
ExtensionsType extensionsType = new ExtensionsType();
|
||||||
|
for (NodeGenerator extension : this.extensions) {
|
||||||
|
extensionsType.addExtension(extension);
|
||||||
|
}
|
||||||
|
responseType.setExtensions(extensionsType);
|
||||||
|
}
|
||||||
|
|
||||||
return responseType;
|
return responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,18 +27,22 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SAML2LogoutRequestBuilder {
|
public class SAML2LogoutRequestBuilder implements SamlProtocolExtensionsAwareBuilder<SAML2LogoutRequestBuilder> {
|
||||||
protected String userPrincipal;
|
protected String userPrincipal;
|
||||||
protected String userPrincipalFormat;
|
protected String userPrincipalFormat;
|
||||||
protected String sessionIndex;
|
protected String sessionIndex;
|
||||||
protected long assertionExpiration;
|
protected long assertionExpiration;
|
||||||
protected String destination;
|
protected String destination;
|
||||||
protected String issuer;
|
protected String issuer;
|
||||||
|
protected final List<NodeGenerator> extensions = new LinkedList<>();
|
||||||
|
|
||||||
public SAML2LogoutRequestBuilder destination(String destination) {
|
public SAML2LogoutRequestBuilder destination(String destination) {
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
|
@ -50,6 +54,12 @@ public class SAML2LogoutRequestBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAML2LogoutRequestBuilder addExtension(NodeGenerator extension) {
|
||||||
|
this.extensions.add(extension);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Length of time in seconds the assertion is valid for
|
* Length of time in seconds the assertion is valid for
|
||||||
* See SAML core specification 2.5.1.2 NotOnOrAfter
|
* See SAML core specification 2.5.1.2 NotOnOrAfter
|
||||||
|
@ -99,6 +109,15 @@ public class SAML2LogoutRequestBuilder {
|
||||||
|
|
||||||
if (assertionExpiration > 0) lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionExpiration * 1000));
|
if (assertionExpiration > 0) lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionExpiration * 1000));
|
||||||
lort.setDestination(URI.create(destination));
|
lort.setDestination(URI.create(destination));
|
||||||
|
|
||||||
|
if (! this.extensions.isEmpty()) {
|
||||||
|
ExtensionsType extensionsType = new ExtensionsType();
|
||||||
|
for (NodeGenerator extension : this.extensions) {
|
||||||
|
extensionsType.addExtension(extension);
|
||||||
|
}
|
||||||
|
lort.setExtensions(extensionsType);
|
||||||
|
}
|
||||||
|
|
||||||
return lort;
|
return lort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,16 +31,20 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class SAML2LogoutResponseBuilder {
|
public class SAML2LogoutResponseBuilder implements SamlProtocolExtensionsAwareBuilder<SAML2LogoutResponseBuilder> {
|
||||||
|
|
||||||
protected String logoutRequestID;
|
protected String logoutRequestID;
|
||||||
protected String destination;
|
protected String destination;
|
||||||
protected String issuer;
|
protected String issuer;
|
||||||
|
protected final List<NodeGenerator> extensions = new LinkedList<>();
|
||||||
|
|
||||||
public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) {
|
public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) {
|
||||||
this.logoutRequestID = logoutRequestID;
|
this.logoutRequestID = logoutRequestID;
|
||||||
|
@ -57,6 +61,11 @@ public class SAML2LogoutResponseBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SAML2LogoutResponseBuilder addExtension(NodeGenerator extension) {
|
||||||
|
this.extensions.add(extension);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Document buildDocument() throws ProcessingException {
|
public Document buildDocument() throws ProcessingException {
|
||||||
Document samlResponse = null;
|
Document samlResponse = null;
|
||||||
|
@ -77,6 +86,14 @@ public class SAML2LogoutResponseBuilder {
|
||||||
statusResponse.setIssuer(issuer);
|
statusResponse.setIssuer(issuer);
|
||||||
statusResponse.setDestination(destination);
|
statusResponse.setDestination(destination);
|
||||||
|
|
||||||
|
if (! this.extensions.isEmpty()) {
|
||||||
|
ExtensionsType extensionsType = new ExtensionsType();
|
||||||
|
for (NodeGenerator extension : this.extensions) {
|
||||||
|
extensionsType.addExtension(extension);
|
||||||
|
}
|
||||||
|
statusResponse.setExtensions(extensionsType);
|
||||||
|
}
|
||||||
|
|
||||||
SAML2Response saml2Response = new SAML2Response();
|
SAML2Response saml2Response = new SAML2Response();
|
||||||
samlResponse = saml2Response.convert(statusResponse);
|
samlResponse = saml2Response.convert(statusResponse);
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.saml;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLStreamWriter;
|
||||||
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementations of this interface are builders that can register <samlp:Extensions>
|
||||||
|
* content providers.
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public interface SamlProtocolExtensionsAwareBuilder<T> {
|
||||||
|
|
||||||
|
public interface NodeGenerator {
|
||||||
|
/**
|
||||||
|
* Generate contents of the <samlp:Extensions> tag. When this method is invoked,
|
||||||
|
* the writer has already emitted the <samlp:Extensions> start tag.
|
||||||
|
*
|
||||||
|
* @param writer Writer to use for producing XML output
|
||||||
|
* @throws ProcessingException If any exception fails
|
||||||
|
*/
|
||||||
|
void write(XMLStreamWriter writer) throws ProcessingException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a given node subtree as a SAML protocol extension into the SAML protocol message.
|
||||||
|
*
|
||||||
|
* @param extension
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
T addExtension(NodeGenerator extension);
|
||||||
|
}
|
|
@ -450,6 +450,11 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger {
|
||||||
return new RuntimeException(ErrorCodes.EXPECTED_TAG + tag + ">. Found <" + foundElementTag + ">");
|
return new RuntimeException(ErrorCodes.EXPECTED_TAG + tag + ">. Found <" + foundElementTag + ">");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RuntimeException parserExpectedNamespace(String ns, String foundElementNs) {
|
||||||
|
return new RuntimeException(ErrorCodes.EXPECTED_NAMESPACE + ns + ">. Found <" + foundElementNs + ">");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*(non-Javadoc)
|
*(non-Javadoc)
|
||||||
*
|
*
|
||||||
|
@ -2378,4 +2383,10 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger {
|
||||||
return new ProcessingException("Wrong audience [" + serviceURL + "].");
|
return new ProcessingException("Wrong audience [" + serviceURL + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProcessingException samlExtensionUnknownChild(Class<?> clazz) {
|
||||||
|
return new ProcessingException("Unknown child type specified for extension: "
|
||||||
|
+ (clazz == null ? "<null>" : clazz.getSimpleName())
|
||||||
|
+ ".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,8 @@ public interface ErrorCodes {
|
||||||
|
|
||||||
String EXPECTED_TAG = "PL00066: Parser : Expected start tag:";
|
String EXPECTED_TAG = "PL00066: Parser : Expected start tag:";
|
||||||
|
|
||||||
|
String EXPECTED_NAMESPACE = "PL00107: Parser : Expected start element namespace:";
|
||||||
|
|
||||||
String EXPECTED_TEXT_VALUE = "PL00071: Parser: Expected text value:";
|
String EXPECTED_TEXT_VALUE = "PL00071: Parser: Expected text value:";
|
||||||
|
|
||||||
String EXPECTED_END_TAG = "PL00066: Parser : Expected end tag:";
|
String EXPECTED_END_TAG = "PL00066: Parser : Expected end tag:";
|
||||||
|
|
|
@ -296,6 +296,14 @@ public interface PicketLinkLogger {
|
||||||
*/
|
*/
|
||||||
RuntimeException parserExpectedTag(String tag, String foundElementTag);
|
RuntimeException parserExpectedTag(String tag, String foundElementTag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ns
|
||||||
|
* @param foundElementNs
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
RuntimeException parserExpectedNamespace(String ns, String foundElementNs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param elementName
|
* @param elementName
|
||||||
*
|
*
|
||||||
|
@ -1219,4 +1227,6 @@ public interface PicketLinkLogger {
|
||||||
RuntimeException parserFeatureNotSupported(String feature);
|
RuntimeException parserFeatureNotSupported(String feature);
|
||||||
|
|
||||||
ProcessingException samlAssertionWrongAudience(String serviceURL);
|
ProcessingException samlAssertionWrongAudience(String serviceURL);
|
||||||
|
|
||||||
|
ProcessingException samlExtensionUnknownChild(Class<?> clazz);
|
||||||
}
|
}
|
|
@ -58,6 +58,8 @@ public class SAMLArtifactResolveParser extends SAMLRequestAbstractParser impleme
|
||||||
continue;
|
continue;
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
continue;
|
||||||
} else
|
} else
|
||||||
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
||||||
+ startElement.getLocation());
|
+ startElement.getLocation());
|
||||||
|
|
|
@ -68,6 +68,9 @@ public class SAMLArtifactResponseParser extends SAMLStatusResponseTypeParser imp
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
Element sig = StaxParserUtil.getDOMElement(xmlEventReader);
|
Element sig = StaxParserUtil.getDOMElement(xmlEventReader);
|
||||||
response.setSignature(sig);
|
response.setSignature(sig);
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser();
|
||||||
|
response.setExtensions(extensionsParser.parse(xmlEventReader));
|
||||||
} else if (JBossSAMLConstants.AUTHN_REQUEST.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.AUTHN_REQUEST.get().equals(elementName)) {
|
||||||
SAMLAuthNRequestParser authnParser = new SAMLAuthNRequestParser();
|
SAMLAuthNRequestParser authnParser = new SAMLAuthNRequestParser();
|
||||||
AuthnRequestType authn = (AuthnRequestType) authnParser.parse(xmlEventReader);
|
AuthnRequestType authn = (AuthnRequestType) authnParser.parse(xmlEventReader);
|
||||||
|
|
|
@ -60,6 +60,8 @@ public class SAMLAttributeQueryParser extends SAMLRequestAbstractParser implemen
|
||||||
continue;
|
continue;
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
continue;
|
||||||
} else
|
} else
|
||||||
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
||||||
+ startElement.getLocation());
|
+ startElement.getLocation());
|
||||||
|
|
|
@ -76,6 +76,8 @@ public class SAMLAuthNRequestParser extends SAMLRequestAbstractParser implements
|
||||||
continue;
|
continue;
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
continue;
|
||||||
} else
|
} else
|
||||||
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location="
|
||||||
+ startElement.getLocation());
|
+ startElement.getLocation());
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.saml.processing.core.parsers.saml;
|
||||||
|
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
import javax.xml.stream.XMLEventReader;
|
||||||
|
import javax.xml.stream.events.EndElement;
|
||||||
|
import javax.xml.stream.events.StartElement;
|
||||||
|
import javax.xml.stream.events.XMLEvent;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
import org.keycloak.saml.common.PicketLinkLogger;
|
||||||
|
import org.keycloak.saml.common.PicketLinkLoggerFactory;
|
||||||
|
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
||||||
|
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
|
import org.keycloak.saml.common.parsers.ParserNamespaceSupport;
|
||||||
|
import org.keycloak.saml.common.util.StaxParserUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses <samlp:Extensions> SAML2 element into series of DOM nodes.
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public class SAMLExtensionsParser implements ParserNamespaceSupport {
|
||||||
|
|
||||||
|
private static final String EXTENSIONS = JBossSAMLConstants.EXTENSIONS.get();
|
||||||
|
|
||||||
|
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtensionsType parse(XMLEventReader xmlEventReader) throws ParsingException {
|
||||||
|
// Get the startelement
|
||||||
|
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||||
|
StaxParserUtil.validate(startElement, EXTENSIONS);
|
||||||
|
|
||||||
|
ExtensionsType extensions = new ExtensionsType();
|
||||||
|
|
||||||
|
while (xmlEventReader.hasNext()) {
|
||||||
|
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
|
||||||
|
if (xmlEvent instanceof EndElement) {
|
||||||
|
EndElement endElement = (EndElement) xmlEvent;
|
||||||
|
if (StaxParserUtil.matches(endElement, EXTENSIONS)) {
|
||||||
|
endElement = StaxParserUtil.getNextEndElement(xmlEventReader);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
startElement = StaxParserUtil.peekNextStartElement(xmlEventReader);
|
||||||
|
if (startElement == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
extensions.addExtension(StaxParserUtil.getDOMElement(xmlEventReader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(QName qname) {
|
||||||
|
String nsURI = qname.getNamespaceURI();
|
||||||
|
String localPart = qname.getLocalPart();
|
||||||
|
|
||||||
|
return nsURI.equals(JBossSAMLURIConstants.PROTOCOL_NSURI.get())
|
||||||
|
&& localPart.equals(JBossSAMLConstants.EXTENSIONS.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -71,6 +71,9 @@ public class SAMLResponseParser extends SAMLStatusResponseTypeParser implements
|
||||||
} else if (JBossSAMLConstants.ASSERTION.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.ASSERTION.get().equals(elementName)) {
|
||||||
SAMLAssertionParser assertionParser = new SAMLAssertionParser();
|
SAMLAssertionParser assertionParser = new SAMLAssertionParser();
|
||||||
response.addAssertion(new RTChoiceType((AssertionType) assertionParser.parse(xmlEventReader)));
|
response.addAssertion(new RTChoiceType((AssertionType) assertionParser.parse(xmlEventReader)));
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser();
|
||||||
|
response.setExtensions(extensionsParser.parse(xmlEventReader));
|
||||||
} else if (JBossSAMLConstants.STATUS.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.STATUS.get().equals(elementName)) {
|
||||||
response.setStatus(parseStatus(xmlEventReader));
|
response.setStatus(parseStatus(xmlEventReader));
|
||||||
} else if (JBossSAMLConstants.ENCRYPTED_ASSERTION.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.ENCRYPTED_ASSERTION.get().equals(elementName)) {
|
||||||
|
|
|
@ -74,6 +74,8 @@ public class SAMLSloRequestParser extends SAMLRequestAbstractParser implements P
|
||||||
continue;
|
continue;
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
continue;
|
||||||
} else
|
} else
|
||||||
throw logger.parserUnknownTag(elementName, startElement.getLocation());
|
throw logger.parserUnknownTag(elementName, startElement.getLocation());
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,9 @@ public class SAMLSloResponseParser extends SAMLStatusResponseTypeParser implemen
|
||||||
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) {
|
||||||
startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
|
startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
|
||||||
StaxParserUtil.bypassElementBlock(xmlEventReader, JBossSAMLConstants.SIGNATURE.get());
|
StaxParserUtil.bypassElementBlock(xmlEventReader, JBossSAMLConstants.SIGNATURE.get());
|
||||||
|
} else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) {
|
||||||
|
SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser();
|
||||||
|
response.setExtensions(extensionsParser.parse(xmlEventReader));
|
||||||
} else if (JBossSAMLConstants.STATUS.get().equals(elementName)) {
|
} else if (JBossSAMLConstants.STATUS.get().equals(elementName)) {
|
||||||
response.setStatus(parseStatus(xmlEventReader));
|
response.setStatus(parseStatus(xmlEventReader));
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,12 @@ import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
import org.keycloak.saml.SamlProtocolExtensionsAwareBuilder;
|
||||||
|
|
||||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
||||||
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Class for the Stax writers for SAML
|
* Base Class for the Stax writers for SAML
|
||||||
|
@ -244,6 +248,28 @@ public class BaseWriter {
|
||||||
StaxUtil.flush(writer);
|
StaxUtil.flush(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void write(ExtensionsType extensions) throws ProcessingException {
|
||||||
|
if (extensions.getAny().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.EXTENSIONS.get(), PROTOCOL_NSURI.get());
|
||||||
|
|
||||||
|
for (Object o : extensions.getAny()) {
|
||||||
|
if (o instanceof Node) {
|
||||||
|
StaxUtil.writeDOMNode(writer, (Node) o);
|
||||||
|
} else if (o instanceof SamlProtocolExtensionsAwareBuilder.NodeGenerator) {
|
||||||
|
SamlProtocolExtensionsAwareBuilder.NodeGenerator ng = (SamlProtocolExtensionsAwareBuilder.NodeGenerator) o;
|
||||||
|
ng.write(writer);
|
||||||
|
} else {
|
||||||
|
throw logger.samlExtensionUnknownChild(o == null ? null : o.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StaxUtil.writeEndElement(writer);
|
||||||
|
StaxUtil.flush(writer);
|
||||||
|
}
|
||||||
|
|
||||||
private void write(SubjectConfirmationType subjectConfirmationType) throws ProcessingException {
|
private void write(SubjectConfirmationType subjectConfirmationType) throws ProcessingException {
|
||||||
StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.SUBJECT_CONFIRMATION.get(),
|
StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.SUBJECT_CONFIRMATION.get(),
|
||||||
ASSERTION_NSURI.get());
|
ASSERTION_NSURI.get());
|
||||||
|
|
|
@ -36,6 +36,7 @@ import javax.xml.namespace.QName;
|
||||||
import javax.xml.stream.XMLStreamWriter;
|
import javax.xml.stream.XMLStreamWriter;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
||||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
||||||
|
@ -122,6 +123,11 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
StaxUtil.writeDOMElement(writer, sig);
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExtensionsType extensions = request.getExtensions();
|
||||||
|
if (extensions != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
NameIDPolicyType nameIDPolicy = request.getNameIDPolicy();
|
NameIDPolicyType nameIDPolicy = request.getNameIDPolicy();
|
||||||
if (nameIDPolicy != null) {
|
if (nameIDPolicy != null) {
|
||||||
write(nameIDPolicy);
|
write(nameIDPolicy);
|
||||||
|
@ -171,6 +177,11 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
StaxUtil.writeDOMElement(writer, signature);
|
StaxUtil.writeDOMElement(writer, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExtensionsType extensions = logOutRequest.getExtensions();
|
||||||
|
if (extensions != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
NameIDType nameID = logOutRequest.getNameID();
|
NameIDType nameID = logOutRequest.getNameID();
|
||||||
if (nameID != null) {
|
if (nameID != null) {
|
||||||
write(nameID, new QName(ASSERTION_NSURI.get(), JBossSAMLConstants.NAMEID.get(), ASSERTION_PREFIX));
|
write(nameID, new QName(ASSERTION_NSURI.get(), JBossSAMLConstants.NAMEID.get(), ASSERTION_PREFIX));
|
||||||
|
@ -278,6 +289,11 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
if (sig != null) {
|
if (sig != null) {
|
||||||
StaxUtil.writeDOMElement(writer, sig);
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
}
|
}
|
||||||
|
ExtensionsType extensions = request.getExtensions();
|
||||||
|
if (extensions != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
String artifact = request.getArtifact();
|
String artifact = request.getArtifact();
|
||||||
if (StringUtil.isNotNull(artifact)) {
|
if (StringUtil.isNotNull(artifact)) {
|
||||||
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.ARTIFACT.get(), PROTOCOL_NSURI.get());
|
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.ARTIFACT.get(), PROTOCOL_NSURI.get());
|
||||||
|
@ -315,6 +331,10 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
if (sig != null) {
|
if (sig != null) {
|
||||||
StaxUtil.writeDOMElement(writer, sig);
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
}
|
}
|
||||||
|
ExtensionsType extensions = request.getExtensions();
|
||||||
|
if (extensions != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
SubjectType subject = request.getSubject();
|
SubjectType subject = request.getSubject();
|
||||||
if (subject != null) {
|
if (subject != null) {
|
||||||
write(subject);
|
write(subject);
|
||||||
|
|
|
@ -37,6 +37,7 @@ import javax.xml.namespace.QName;
|
||||||
import javax.xml.stream.XMLStreamWriter;
|
import javax.xml.stream.XMLStreamWriter;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a SAML Response to stream
|
* Write a SAML Response to stream
|
||||||
|
@ -78,6 +79,10 @@ public class SAMLResponseWriter extends BaseWriter {
|
||||||
if (sig != null) {
|
if (sig != null) {
|
||||||
StaxUtil.writeDOMElement(writer, sig);
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
}
|
}
|
||||||
|
ExtensionsType extensions = response.getExtensions();
|
||||||
|
if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
StatusType status = response.getStatus();
|
StatusType status = response.getStatus();
|
||||||
write(status);
|
write(status);
|
||||||
|
@ -119,6 +124,10 @@ public class SAMLResponseWriter extends BaseWriter {
|
||||||
if (sig != null) {
|
if (sig != null) {
|
||||||
StaxUtil.writeDOMElement(writer, sig);
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
}
|
}
|
||||||
|
ExtensionsType extensions = response.getExtensions();
|
||||||
|
if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
StatusType status = response.getStatus();
|
StatusType status = response.getStatus();
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
|
@ -163,6 +172,15 @@ public class SAMLResponseWriter extends BaseWriter {
|
||||||
NameIDType issuer = response.getIssuer();
|
NameIDType issuer = response.getIssuer();
|
||||||
write(issuer, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX));
|
write(issuer, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX));
|
||||||
|
|
||||||
|
Element sig = response.getSignature();
|
||||||
|
if (sig != null) {
|
||||||
|
StaxUtil.writeDOMElement(writer, sig);
|
||||||
|
}
|
||||||
|
ExtensionsType extensions = response.getExtensions();
|
||||||
|
if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) {
|
||||||
|
write(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
StatusType status = response.getStatus();
|
StatusType status = response.getStatus();
|
||||||
write(status);
|
write(status);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.saml.processing.core.parsers.saml;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test class for SAML parser.
|
||||||
|
*
|
||||||
|
* TODO: Add further tests.
|
||||||
|
*
|
||||||
|
* @author hmlnarik
|
||||||
|
*/
|
||||||
|
public class SAMLParserTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaml20EncryptedAssertionsSignedReceivedWithRedirectBinding() throws Exception {
|
||||||
|
InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response.xml");
|
||||||
|
SAMLParser parser = new SAMLParser();
|
||||||
|
|
||||||
|
Object parsedObject = parser.parse(st);
|
||||||
|
assertThat(parsedObject, instanceOf(ResponseType.class));
|
||||||
|
|
||||||
|
ResponseType resp = (ResponseType) parsedObject;
|
||||||
|
assertThat(resp.getSignature(), nullValue());
|
||||||
|
assertThat(resp.getConsent(), nullValue());
|
||||||
|
assertThat(resp.getIssuer(), not(nullValue()));
|
||||||
|
assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo"));
|
||||||
|
|
||||||
|
assertThat(resp.getExtensions(), not(nullValue()));
|
||||||
|
assertThat(resp.getExtensions().getAny().size(), is(1));
|
||||||
|
assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class));
|
||||||
|
Element el = (Element) resp.getExtensions().getAny().get(0);
|
||||||
|
assertThat(el.getLocalName(), is("KeyInfo"));
|
||||||
|
assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0"));
|
||||||
|
assertThat(el.hasAttribute("MessageSigningKeyId"), is(true));
|
||||||
|
assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE"));
|
||||||
|
|
||||||
|
assertThat(resp.getAssertions(), not(nullValue()));
|
||||||
|
assertThat(resp.getAssertions().size(), is(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaml20EncryptedAssertionsSignedTwoExtensionsReceivedWithRedirectBinding() throws Exception {
|
||||||
|
Element el;
|
||||||
|
|
||||||
|
InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response-two-extensions.xml");
|
||||||
|
SAMLParser parser = new SAMLParser();
|
||||||
|
|
||||||
|
Object parsedObject = parser.parse(st);
|
||||||
|
assertThat(parsedObject, instanceOf(ResponseType.class));
|
||||||
|
|
||||||
|
ResponseType resp = (ResponseType) parsedObject;
|
||||||
|
assertThat(resp.getSignature(), nullValue());
|
||||||
|
assertThat(resp.getConsent(), nullValue());
|
||||||
|
assertThat(resp.getIssuer(), not(nullValue()));
|
||||||
|
assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo"));
|
||||||
|
|
||||||
|
assertThat(resp.getExtensions(), not(nullValue()));
|
||||||
|
assertThat(resp.getExtensions().getAny().size(), is(2));
|
||||||
|
assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class));
|
||||||
|
el = (Element) resp.getExtensions().getAny().get(0);
|
||||||
|
assertThat(el.getLocalName(), is("KeyInfo"));
|
||||||
|
assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0"));
|
||||||
|
assertThat(el.hasAttribute("MessageSigningKeyId"), is(true));
|
||||||
|
assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE"));
|
||||||
|
assertThat(resp.getExtensions().getAny().get(1), instanceOf(Element.class));
|
||||||
|
el = (Element) resp.getExtensions().getAny().get(1);
|
||||||
|
assertThat(el.getLocalName(), is("ever"));
|
||||||
|
assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:what:1.0"));
|
||||||
|
assertThat(el.hasAttribute("what"), is(true));
|
||||||
|
assertThat(el.getAttribute("what"), is("ever"));
|
||||||
|
|
||||||
|
assertThat(resp.getAssertions(), not(nullValue()));
|
||||||
|
assertThat(resp.getAssertions().size(), is(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaml20PostLogoutRequest() throws Exception {
|
||||||
|
InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-signed-logout-request.xml");
|
||||||
|
SAMLParser parser = new SAMLParser();
|
||||||
|
|
||||||
|
Object parsedObject = parser.parse(st);
|
||||||
|
assertThat(parsedObject, instanceOf(LogoutRequestType.class));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/sales-post-enc/saml" ID="ID_0b43d444-d1a8-44a5-8caf-38e176489e1f" InResponseTo="ID_223d3591-22fb-4b3c-9e38-4719293b2d94" IssueInstant="2016-11-01T13:52:43.054Z" Version="2.0">
|
||||||
|
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8081/auth/realms/saml-demo</saml:Issuer>
|
||||||
|
<samlp:Extensions>
|
||||||
|
<kckey:KeyInfo xmlns:kckey="urn:keycloak:ext:key:1.0" MessageSigningKeyId="FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE"/>
|
||||||
|
<what:ever xmlns:what="urn:keycloak:ext:what:1.0" what="ever"/>
|
||||||
|
</samlp:Extensions>
|
||||||
|
<samlp:Status>
|
||||||
|
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
|
||||||
|
</samlp:Status>
|
||||||
|
<saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
|
||||||
|
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
|
||||||
|
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||||
|
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||||
|
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||||
|
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
|
||||||
|
<xenc:CipherData>
|
||||||
|
<xenc:CipherValue>
|
||||||
|
OkvZTx/ifYLef74rY0F9I8lbJaatgSEguo+zwh5JrYWcO09Ib2gtz5+z+67Is2+wk/OzKp154r8qAI5vY9AYvuXCslKL/wbcZ1UILL78F0T/iiUW3VpWy8Wvz5nezBFPRqot8WiFQykByjlBg1Z8XOts+uIdyqBBi/WjYeJGMaQ=
|
||||||
|
</xenc:CipherValue>
|
||||||
|
</xenc:CipherData>
|
||||||
|
</xenc:EncryptedKey>
|
||||||
|
</ds:KeyInfo>
|
||||||
|
<xenc:CipherData>
|
||||||
|
<xenc:CipherValue>
|
||||||
|
RW2eu9nP2Ez9hfRlug9xC+kFfVF3HZpEb4kIFH33gmVbzrQjPk0l67uXkwRjC82FZZ482QnHCBIqNFlAryds/zTa6wdRvFmhQnIM6WxoAl8TM+e9h8MoKkalMc8J/Qfp+WQ7/XdmCg2pp9VvUZTK+g0+G4aGuL+S5+ssZq4rl9k7LrSYyp6vj+djgvISZiz5hPYJCN/WY/gWXfVuLHSpu4CmZt8D2APtT3ax1WmGcuzStAfTW8q3MFIDNV59hkpFmDb+gvyLNbZ95cDYxofiPXaC5cOTftnSBp68Ay1eienqdttEDo4fyakszdvq128KwXkH9azCg6sqLxli6B8l2xdq41MeuJO54VqmOhhLxwKy42NtnJvK/NkNwttH4yMwDPpPbC4vOKCXxT2r2F7jjvJQNB2VFv+oiUAWSSc3fGQcc2uNlx9YQVuzTmjqc7fXAWCGgYoogC8AeNWni204bnBoVpFrEo3gzuOe2fFsddJIclglmTH1hWf31FXUHDO2nl/lT4puQVTo+I+d6jpiV+qdp823NDntRxljRlUJO2AzSTXuIIGtF5q5KWyEi9Nj93BCWa1Llcddkn3ZEZMvDwR4MacwUj8G8hwoH73VvT3jAiakjSpNEIqYCzofeejdfN/gEuuAUfe8uNbTu+gBS+iP3QJe3Pc0Fs/lKJzd3frPNj7xb83wpOf865EQQoOozhnRIKKcMReSjakr/Px5NNooeiJcWEreDagQO2TbwTnHg1kCNG3BAXV/2lV3XBU4afZBoUfxAzYWFOl6xFCAPzhQCPL1SFJp1VRADY/1MU2Kaje5AZoJ4jjph8+yspxBvjic1vC1uYRGW8LWRind9w4eVhCm0LfPiFRCpP+jKPQOJzcNH580/nIMFXPHHnLKv/It7Qex1unDv/QjkuCFFHR6SWJm4WBrwDek+MyOIvgT6o878Cu0Ps472QpoYBQ+7l2WoylWdG1lHZV1UiHPj7PLHPNAL4rbbN3U88fS6N9OJHegQTfcX0i/1KPk4IN/5Z+/15dHI658BINjRvI/6O1QqaTVZkqM8ORcoGpn6BjAiz5rRhjWpOCwlmT+VzOAp3IqACURS1X+txjWE2mfVjlHLJsvyGRDLv1dUR3IeStDAEfsjR/ruRgn5XTFpYaccB/u//DJonJr5A+KFiLbYl+sbbSVAoQCAiAdxKdUpKPx7C473UJ2nYQGby5H5xwboa0Uj0SnJLYWdQ0jvVvzWpWFVWATc4UqnaxdoUDAmewrM6cSSIAmQBB34orCunFbriK9Z4efZ7gB9erQ1fpi3z/IjQBoTEpOUUIPW/qMAApIDPVM6UV9PumW7RL9zKEP5PuWJoGGnKbWGP/b9G4vMFiWMaSNHBYYMI6OLH4WJ3E+4QBGh2vjjfQ0gobhaLgIerIwCQFYEdl9KddAjaflUEFXal9fIQ8Bz9L3rDhQE5AGBZL6ULZmJe3GnkN6Cc+UWAGyD5zv2rsCG2lvR5ox4UE2mFi6nBJbC5Vj5m9Sz1l0QpRwUkH2kD2QQ5iV6nNmQOcU/mz7ulxluf8+FBJJimYVqK8UkJ6+W6j8Eft9Q8fTpEuEVLxqTWGgOAEUBf87RWDU+iF3A+AxFGsJLc5RC+5BKNTEDlV2qDCjHT7b5wqBKJ3FHulOih9EenlZiI51m6kg5yyxnMdbhasvSh6Az8Mp/4lFo/wSA/mXxNhBrEEmRhFiIE5yYUEYIj5F8fH+93tIuWQqyhXIwCntEOdSSmoei9EYFzj8deXcEzVf8y/N6HQErZcJjyg34caOsfRcJYoxEiCm4icA/btWhdjUNT02B20qnxGFndO4CRUQlyDqTbyVD8LRLK9/95L9+5v9zojLle8xQe30dsxKn7r9TTJH8QQai5iam9lU1ik50lwTKpZb18k4rNdO5cnnYoHzCXeCg38YZxyFt9G7um/MxlID5Qd5Ywq6thDzL7WxvanKeRhCuJ2MTVV0EoJxZKIj9Yv0Ars9mZHkoHoP0ikcW8d5ciDj1Onnbj+XDcYI3FZj0Y2vToZvYi/7eLWi8EnSjaIQrr/AHnrmZK1w3Uicd691U6r3Y0UdnzQEl4Ub/l1uhSaGAg2oEdDxkOdZ3Frvf/C4nTEBmunPlNvnJjVFssdeVVXKLBOZ5eRiJjasHUKnTeJVwolvd/dBI+ypfw1+5ae/0upxd9/gV1lbwX9N2yOwqbxz24cKXZWvOFBAGc3+gQFu8RrF6NAeQ96PlkuRsiNOKPPtJT3JNrLGvVKY8g==
|
||||||
|
</xenc:CipherValue>
|
||||||
|
</xenc:CipherData>
|
||||||
|
</xenc:EncryptedData>
|
||||||
|
</saml:EncryptedAssertion>
|
||||||
|
</samlp:Response>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/sales-post-enc/saml" ID="ID_0b43d444-d1a8-44a5-8caf-38e176489e1f" InResponseTo="ID_223d3591-22fb-4b3c-9e38-4719293b2d94" IssueInstant="2016-11-01T13:52:43.054Z" Version="2.0">
|
||||||
|
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8081/auth/realms/saml-demo</saml:Issuer>
|
||||||
|
<samlp:Extensions>
|
||||||
|
<kckey:KeyInfo xmlns:kckey="urn:keycloak:ext:key:1.0" MessageSigningKeyId="FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE"/>
|
||||||
|
</samlp:Extensions>
|
||||||
|
<samlp:Status>
|
||||||
|
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
|
||||||
|
</samlp:Status>
|
||||||
|
<saml:EncryptedAssertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
|
||||||
|
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
|
||||||
|
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
|
||||||
|
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||||
|
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
|
||||||
|
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/>
|
||||||
|
<xenc:CipherData>
|
||||||
|
<xenc:CipherValue>
|
||||||
|
OkvZTx/ifYLef74rY0F9I8lbJaatgSEguo+zwh5JrYWcO09Ib2gtz5+z+67Is2+wk/OzKp154r8qAI5vY9AYvuXCslKL/wbcZ1UILL78F0T/iiUW3VpWy8Wvz5nezBFPRqot8WiFQykByjlBg1Z8XOts+uIdyqBBi/WjYeJGMaQ=
|
||||||
|
</xenc:CipherValue>
|
||||||
|
</xenc:CipherData>
|
||||||
|
</xenc:EncryptedKey>
|
||||||
|
</ds:KeyInfo>
|
||||||
|
<xenc:CipherData>
|
||||||
|
<xenc:CipherValue>
|
||||||
|
RW2eu9nP2Ez9hfRlug9xC+kFfVF3HZpEb4kIFH33gmVbzrQjPk0l67uXkwRjC82FZZ482QnHCBIqNFlAryds/zTa6wdRvFmhQnIM6WxoAl8TM+e9h8MoKkalMc8J/Qfp+WQ7/XdmCg2pp9VvUZTK+g0+G4aGuL+S5+ssZq4rl9k7LrSYyp6vj+djgvISZiz5hPYJCN/WY/gWXfVuLHSpu4CmZt8D2APtT3ax1WmGcuzStAfTW8q3MFIDNV59hkpFmDb+gvyLNbZ95cDYxofiPXaC5cOTftnSBp68Ay1eienqdttEDo4fyakszdvq128KwXkH9azCg6sqLxli6B8l2xdq41MeuJO54VqmOhhLxwKy42NtnJvK/NkNwttH4yMwDPpPbC4vOKCXxT2r2F7jjvJQNB2VFv+oiUAWSSc3fGQcc2uNlx9YQVuzTmjqc7fXAWCGgYoogC8AeNWni204bnBoVpFrEo3gzuOe2fFsddJIclglmTH1hWf31FXUHDO2nl/lT4puQVTo+I+d6jpiV+qdp823NDntRxljRlUJO2AzSTXuIIGtF5q5KWyEi9Nj93BCWa1Llcddkn3ZEZMvDwR4MacwUj8G8hwoH73VvT3jAiakjSpNEIqYCzofeejdfN/gEuuAUfe8uNbTu+gBS+iP3QJe3Pc0Fs/lKJzd3frPNj7xb83wpOf865EQQoOozhnRIKKcMReSjakr/Px5NNooeiJcWEreDagQO2TbwTnHg1kCNG3BAXV/2lV3XBU4afZBoUfxAzYWFOl6xFCAPzhQCPL1SFJp1VRADY/1MU2Kaje5AZoJ4jjph8+yspxBvjic1vC1uYRGW8LWRind9w4eVhCm0LfPiFRCpP+jKPQOJzcNH580/nIMFXPHHnLKv/It7Qex1unDv/QjkuCFFHR6SWJm4WBrwDek+MyOIvgT6o878Cu0Ps472QpoYBQ+7l2WoylWdG1lHZV1UiHPj7PLHPNAL4rbbN3U88fS6N9OJHegQTfcX0i/1KPk4IN/5Z+/15dHI658BINjRvI/6O1QqaTVZkqM8ORcoGpn6BjAiz5rRhjWpOCwlmT+VzOAp3IqACURS1X+txjWE2mfVjlHLJsvyGRDLv1dUR3IeStDAEfsjR/ruRgn5XTFpYaccB/u//DJonJr5A+KFiLbYl+sbbSVAoQCAiAdxKdUpKPx7C473UJ2nYQGby5H5xwboa0Uj0SnJLYWdQ0jvVvzWpWFVWATc4UqnaxdoUDAmewrM6cSSIAmQBB34orCunFbriK9Z4efZ7gB9erQ1fpi3z/IjQBoTEpOUUIPW/qMAApIDPVM6UV9PumW7RL9zKEP5PuWJoGGnKbWGP/b9G4vMFiWMaSNHBYYMI6OLH4WJ3E+4QBGh2vjjfQ0gobhaLgIerIwCQFYEdl9KddAjaflUEFXal9fIQ8Bz9L3rDhQE5AGBZL6ULZmJe3GnkN6Cc+UWAGyD5zv2rsCG2lvR5ox4UE2mFi6nBJbC5Vj5m9Sz1l0QpRwUkH2kD2QQ5iV6nNmQOcU/mz7ulxluf8+FBJJimYVqK8UkJ6+W6j8Eft9Q8fTpEuEVLxqTWGgOAEUBf87RWDU+iF3A+AxFGsJLc5RC+5BKNTEDlV2qDCjHT7b5wqBKJ3FHulOih9EenlZiI51m6kg5yyxnMdbhasvSh6Az8Mp/4lFo/wSA/mXxNhBrEEmRhFiIE5yYUEYIj5F8fH+93tIuWQqyhXIwCntEOdSSmoei9EYFzj8deXcEzVf8y/N6HQErZcJjyg34caOsfRcJYoxEiCm4icA/btWhdjUNT02B20qnxGFndO4CRUQlyDqTbyVD8LRLK9/95L9+5v9zojLle8xQe30dsxKn7r9TTJH8QQai5iam9lU1ik50lwTKpZb18k4rNdO5cnnYoHzCXeCg38YZxyFt9G7um/MxlID5Qd5Ywq6thDzL7WxvanKeRhCuJ2MTVV0EoJxZKIj9Yv0Ars9mZHkoHoP0ikcW8d5ciDj1Onnbj+XDcYI3FZj0Y2vToZvYi/7eLWi8EnSjaIQrr/AHnrmZK1w3Uicd691U6r3Y0UdnzQEl4Ub/l1uhSaGAg2oEdDxkOdZ3Frvf/C4nTEBmunPlNvnJjVFssdeVVXKLBOZ5eRiJjasHUKnTeJVwolvd/dBI+ypfw1+5ae/0upxd9/gV1lbwX9N2yOwqbxz24cKXZWvOFBAGc3+gQFu8RrF6NAeQ96PlkuRsiNOKPPtJT3JNrLGvVKY8g==
|
||||||
|
</xenc:CipherValue>
|
||||||
|
</xenc:CipherData>
|
||||||
|
</xenc:EncryptedData>
|
||||||
|
</saml:EncryptedAssertion>
|
||||||
|
</samlp:Response>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8081/auth/realms/saml-demo/protocol/saml" ID="ID_4790c6a3-4b9f-4c0a-a368-5c0e498544e4" IssueInstant="2016-11-01T14:36:43.194Z" Version="2.0">
|
||||||
|
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://localhost:8080/sales-post-enc/</saml:Issuer>
|
||||||
|
<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
|
||||||
|
<dsig:SignedInfo>
|
||||||
|
<dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
||||||
|
<dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
||||||
|
<dsig:Reference URI="#ID_4790c6a3-4b9f-4c0a-a368-5c0e498544e4">
|
||||||
|
<dsig:Transforms>
|
||||||
|
<dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
||||||
|
<dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
|
||||||
|
</dsig:Transforms>
|
||||||
|
<dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||||
|
<dsig:DigestValue>zeWNo5eav5tFOOCEJ1YU9eINkPnBSfixzAr8AOC4R4c=</dsig:DigestValue>
|
||||||
|
</dsig:Reference>
|
||||||
|
</dsig:SignedInfo>
|
||||||
|
<dsig:SignatureValue>
|
||||||
|
pyOiS1LsV/XR08zhcN6IqSYuKTDln4otmCvZxCc07ORP1C9jragu8V8rEE09qt/zBcdw7Arb8eLNNC6oCnrnMxuvzRInVTwt7T5K3t0UlzRWOb3HMElhcWFEgDzh6uKw5Cr45A01XNpojtJWCML/qU2Enyyy80FBlCJNcbzyLxE=
|
||||||
|
</dsig:SignatureValue>
|
||||||
|
<dsig:KeyInfo>
|
||||||
|
<dsig:KeyValue>
|
||||||
|
<dsig:RSAKeyValue>
|
||||||
|
<dsig:Modulus>
|
||||||
|
2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEik=
|
||||||
|
</dsig:Modulus>
|
||||||
|
<dsig:Exponent>AQAB</dsig:Exponent>
|
||||||
|
</dsig:RSAKeyValue>
|
||||||
|
</dsig:KeyValue>
|
||||||
|
</dsig:KeyInfo>
|
||||||
|
</dsig:Signature>
|
||||||
|
<saml:NameID xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">bburke</saml:NameID>
|
||||||
|
<samlp:SessionIndex>a3b2df1c-1095-487b-8b56-f62818c449e3</samlp:SessionIndex>
|
||||||
|
</samlp:LogoutRequest>
|
Loading…
Reference in a new issue