use of org.keycloak.dom.saml.v2.protocol.ArtifactResponseType in project keycloak by keycloak.
the class SamlProtocolUtilsTest method testBuildArtifactResponse.
@Test
public void testBuildArtifactResponse() throws ConfigurationException, ProcessingException, ParsingException {
ResponseType response = new SAML2LoginResponseBuilder().requestID(IDGenerator.create("ID_")).destination("http://localhost:8180/auth/realms/demo/broker/saml-broker/endpoint").issuer("http://saml.idp/saml").assertionExpiration(1000000).subjectExpiration(1000000).requestIssuer("http://localhost:8180/auth/realms/demo").nameIdentifier(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get(), "a@b.c").authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get()).sessionIndex("idp:" + UUID.randomUUID()).buildModel();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
SAMLResponseWriter writer = new SAMLResponseWriter(StaxUtil.getXMLStreamWriter(bos));
writer.write(response);
Document responseDoc = DocumentUtil.getDocument(new ByteArrayInputStream(bos.toByteArray()));
ArtifactResponseType artifactResponseType = SamlProtocolUtils.buildArtifactResponse(responseDoc);
Document doc = SamlProtocolUtils.convert(artifactResponseType);
String artifactResponse = DocumentUtil.asString(doc);
assertThat(artifactResponse, containsString("samlp:ArtifactResponse"));
assertThat(artifactResponse, containsString("samlp:Response"));
assertThat(artifactResponse, containsString("saml:Assertion"));
assertThat(artifactResponse.indexOf("samlp:ArtifactResponse"), lessThan(artifactResponse.indexOf("samlp:Response")));
assertThat(artifactResponse.indexOf("samlp:Response"), lessThan(artifactResponse.indexOf("saml:Assertion")));
assertThat(artifactResponse.split("\\Q<saml:Issuer>http://saml.idp/saml</saml:Issuer>\\E").length, is(4));
assertThat(artifactResponse.split("\\Q<samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/>\\E").length, is(3));
}
use of org.keycloak.dom.saml.v2.protocol.ArtifactResponseType in project keycloak by keycloak.
the class SamlService method emptyArtifactResponseMessage.
private Response emptyArtifactResponseMessage(ArtifactResolveType artifactResolveMessage, ClientModel clientModel, URI responseStatusCode) throws ProcessingException, ConfigurationException {
ArtifactResponseType artifactResponse = SamlProtocolUtils.buildArtifactResponse(null, SAML2NameIDBuilder.value(RealmsResource.realmBaseUrl(session.getContext().getUri()).build(realm.getName()).toString()).build(), responseStatusCode);
Document artifactResponseDocument;
try {
artifactResponseDocument = SamlProtocolUtils.convert(artifactResponse);
} catch (ParsingException | ConfigurationException | ProcessingException e) {
logger.errorf("Failed to obtain document from ArtifactResponse: %s.", artifactResponse);
throw new ProcessingException(Errors.INVALID_SAML_ARTIFACT_RESPONSE, e);
}
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
}
use of org.keycloak.dom.saml.v2.protocol.ArtifactResponseType in project keycloak by keycloak.
the class SamlService method artifactResolve.
/**
* Takes an artifact resolve message and returns the artifact response, if the artifact is found belonging to a session
* of the issuer.
* @param artifactResolveMessage The artifact resolve message sent by the client
* @param artifactResolveHolder the document containing the artifact resolve message sent by the client
* @return a Response containing the SOAP message with the ArifactResponse
* @throws ParsingException
* @throws ConfigurationException
* @throws ProcessingException
*/
public Response artifactResolve(ArtifactResolveType artifactResolveMessage, SAMLDocumentHolder artifactResolveHolder) throws ParsingException, ConfigurationException, ProcessingException {
logger.debug("Received artifactResolve message for artifact " + artifactResolveMessage.getArtifact() + "\n" + "Message: \n" + DocumentUtil.getDocumentAsString(artifactResolveHolder.getSamlDocument()));
// Artifact from resolve request
String artifact = artifactResolveMessage.getArtifact();
if (artifact == null) {
logger.errorf("Artifact to resolve was null");
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
}
ArtifactResolver artifactResolver = getArtifactResolver(artifact);
if (artifactResolver == null) {
logger.errorf("Cannot find ArtifactResolver for artifact %s", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
}
// Obtain details of session that issued artifact and check if it corresponds to issuer of Resolve message
SamlArtifactSessionMappingModel sessionMapping = getArtifactSessionMappingStore().get(artifact);
if (sessionMapping == null) {
logger.errorf("No data stored for artifact %s", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
UserSessionModel userSessionModel = session.sessions().getUserSession(realm, sessionMapping.getUserSessionId());
if (userSessionModel == null) {
logger.errorf("UserSession with id: %s, that corresponds to artifact: %s does not exist.", sessionMapping.getUserSessionId(), artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
AuthenticatedClientSessionModel clientSessionModel = userSessionModel.getAuthenticatedClientSessions().get(sessionMapping.getClientSessionId());
if (clientSessionModel == null) {
logger.errorf("ClientSession with id: %s, that corresponds to artifact: %s and UserSession: %s does not exist.", sessionMapping.getClientSessionId(), artifact, sessionMapping.getUserSessionId());
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
ClientModel clientModel = getAndCheckClientModel(sessionMapping.getClientSessionId(), artifactResolveMessage.getIssuer().getValue());
SamlClient samlClient = new SamlClient(clientModel);
// Check signature within ArtifactResolve request if client requires it
if (samlClient.requiresClientSignature()) {
try {
SamlProtocolUtils.verifyDocumentSignature(clientModel, artifactResolveHolder.getSamlDocument());
} catch (VerificationException e) {
SamlService.logger.error("request validation failed", e);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
}
// Obtain artifactResponse from clientSessionModel
String artifactResponseString;
try {
artifactResponseString = artifactResolver.resolveArtifact(clientSessionModel, artifact);
} catch (ArtifactResolverProcessingException e) {
logger.errorf(e, "Failed to resolve artifact: %s.", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
// Artifact is successfully resolved, we can remove session mapping from storage
getArtifactSessionMappingStore().remove(artifact);
Document artifactResponseDocument = null;
ArtifactResponseType artifactResponseType = null;
try {
SAMLDataMarshaller marshaller = new SAMLDataMarshaller();
artifactResponseType = marshaller.deserialize(artifactResponseString, ArtifactResponseType.class);
artifactResponseDocument = SamlProtocolUtils.convert(artifactResponseType);
} catch (ParsingException | ConfigurationException | ProcessingException e) {
logger.errorf(e, "Failed to obtain document from ArtifactResponseString: %s.", artifactResponseString);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
// If clientSession is in LOGGING_OUT action, now we can move it to LOGGED_OUT
if (CommonClientSessionModel.Action.LOGGING_OUT.name().equals(clientSessionModel.getAction())) {
clientSessionModel.setAction(CommonClientSessionModel.Action.LOGGED_OUT.name());
// If Keycloak sent LogoutResponse we need to also remove UserSession
if (artifactResponseType.getAny() instanceof StatusResponseType && artifactResponseString.contains(JBossSAMLConstants.LOGOUT_RESPONSE.get())) {
if (!UserSessionModel.State.LOGGED_OUT_UNCONFIRMED.equals(userSessionModel.getState())) {
logger.warnf("Keycloak issued LogoutResponse for clientSession %s, however user session %s was not in LOGGED_OUT_UNCONFIRMED state.", clientSessionModel.getId(), userSessionModel.getId());
}
AuthenticationManager.finishUnconfirmedUserSession(session, realm, userSessionModel);
}
}
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
}
use of org.keycloak.dom.saml.v2.protocol.ArtifactResponseType in project keycloak by keycloak.
the class SAMLResponseWriter method write.
public void write(ArtifactResponseType response) throws ProcessingException {
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.ARTIFACT_RESPONSE.get(), JBossSAMLURIConstants.PROTOCOL_NSURI.get());
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, JBossSAMLURIConstants.PROTOCOL_NSURI.get());
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, JBossSAMLURIConstants.ASSERTION_NSURI.get());
StaxUtil.writeDefaultNameSpace(writer, JBossSAMLURIConstants.ASSERTION_NSURI.get());
writeBaseAttributes(response);
NameIDType issuer = response.getIssuer();
if (issuer != null) {
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();
if (status != null) {
write(status);
}
Object anyObj = response.getAny();
if (anyObj instanceof AuthnRequestType) {
AuthnRequestType authn = (AuthnRequestType) anyObj;
SAMLRequestWriter requestWriter = new SAMLRequestWriter(writer);
requestWriter.write(authn);
} else if (anyObj instanceof LogoutRequestType) {
LogoutRequestType logoutRequestType = (LogoutRequestType) anyObj;
SAMLRequestWriter requestWriter = new SAMLRequestWriter(writer);
requestWriter.write(logoutRequestType);
} else if (anyObj instanceof ResponseType) {
ResponseType rt = (ResponseType) anyObj;
write(rt);
} else if (anyObj instanceof StatusResponseType) {
StatusResponseType rt = (StatusResponseType) anyObj;
write(rt, new QName(PROTOCOL_NSURI.get(), JBossSAMLConstants.LOGOUT_RESPONSE.get(), "samlp"));
}
StaxUtil.writeEndElement(writer);
StaxUtil.flush(writer);
}
use of org.keycloak.dom.saml.v2.protocol.ArtifactResponseType in project keycloak by keycloak.
the class SamlDocumentStepBuilder method saml2Object2String.
public static String saml2Object2String(final SAML2Object transformed) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
XMLStreamWriter xmlStreamWriter = StaxUtil.getXMLStreamWriter(bos);
if (transformed instanceof AuthnRequestType) {
new SAMLRequestWriter(xmlStreamWriter).write((AuthnRequestType) transformed);
} else if (transformed instanceof LogoutRequestType) {
new SAMLRequestWriter(xmlStreamWriter).write((LogoutRequestType) transformed);
} else if (transformed instanceof ArtifactResolveType) {
new SAMLRequestWriter(xmlStreamWriter).write((ArtifactResolveType) transformed);
} else if (transformed instanceof AttributeQueryType) {
new SAMLRequestWriter(xmlStreamWriter).write((AttributeQueryType) transformed);
} else if (transformed instanceof ResponseType) {
new SAMLResponseWriter(xmlStreamWriter).write((ResponseType) transformed);
} else if (transformed instanceof ArtifactResponseType) {
new SAMLResponseWriter(xmlStreamWriter).write((ArtifactResponseType) transformed);
} else if (transformed instanceof StatusResponseType) {
new SAMLResponseWriter(xmlStreamWriter).write((StatusResponseType) transformed, SAMLProtocolQNames.LOGOUT_RESPONSE.getQName("samlp"));
} else {
Assert.assertNotNull("Unknown type: <null>", transformed);
Assert.fail("Unknown type: " + transformed.getClass().getName());
}
return new String(bos.toByteArray(), GeneralConstants.SAML_CHARSET);
} catch (ProcessingException ex) {
throw new RuntimeException(ex);
}
}
Aggregations