Search in sources :

Example 1 with ConfigurationException

use of org.keycloak.saml.common.exceptions.ConfigurationException in project keycloak by keycloak.

the class SamlProtocol method finishLogout.

@Override
public Response finishLogout(UserSessionModel userSession) {
    logger.debug("finishLogout");
    String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
    if (logoutBindingUri == null) {
        logger.error("Can't finish SAML logout as there is no logout binding set.  Please configure the logout service url in the admin console for your client applications.");
        return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_LOGOUT);
    }
    String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
    SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
    builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
    builder.destination(logoutBindingUri);
    builder.issuer(getResponseIssuer(realm));
    JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session);
    binding.relayState(logoutRelayState);
    String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
    boolean postBinding = isLogoutPostBindingForInitiator(userSession);
    if (signingAlgorithm != null) {
        SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
        String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
        if (canonicalization != null) {
            binding.canonicalizationMethod(canonicalization);
        }
        KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
        XmlKeyInfoKeyNameTransformer transformer = XmlKeyInfoKeyNameTransformer.from(userSession.getNote(SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER), SamlClient.DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
        String keyName = transformer.getKeyName(keys.getKid(), keys.getCertificate());
        binding.signatureAlgorithm(algorithm).signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
        boolean addExtension = (!postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
        if (addExtension) {
            // Only include extension if REDIRECT binding and signing whole SAML protocol message
            builder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));
        }
    }
    Response response;
    try {
        response = buildLogoutResponse(userSession, logoutBindingUri, builder, binding);
    } catch (ConfigurationException | ProcessingException | IOException e) {
        throw new RuntimeException(e);
    }
    if (logoutBindingUri != null) {
        event.detail(Details.REDIRECT_URI, logoutBindingUri);
    }
    event.event(EventType.LOGOUT).detail(Details.AUTH_METHOD, userSession.getAuthMethod()).client(session.getContext().getClient()).user(userSession.getUser()).session(userSession).detail(Details.USERNAME, userSession.getLoginUsername()).detail(Details.RESPONSE_MODE, postBinding ? SamlProtocol.SAML_POST_BINDING : SamlProtocol.SAML_REDIRECT_BINDING).detail(SamlProtocol.SAML_LOGOUT_REQUEST_ID, userSession.getNote(SAML_LOGOUT_REQUEST_ID)).success();
    return response;
}
Also used : SAML2LogoutResponseBuilder(org.keycloak.saml.SAML2LogoutResponseBuilder) XmlKeyInfoKeyNameTransformer(org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer) SignatureAlgorithm(org.keycloak.saml.SignatureAlgorithm) IOException(java.io.IOException) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) Response(javax.ws.rs.core.Response) CloseableHttpResponse(org.apache.http.client.methods.CloseableHttpResponse) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) KeyManager(org.keycloak.models.KeyManager) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 2 with ConfigurationException

use of org.keycloak.saml.common.exceptions.ConfigurationException in project keycloak by keycloak.

the class SamlProtocol method frontchannelLogout.

@Override
public Response frontchannelLogout(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
    ClientModel client = clientSession.getClient();
    SamlClient samlClient = new SamlClient(client);
    try {
        boolean postBinding = isLogoutPostBindingForClient(clientSession);
        String bindingUri = getLogoutServiceUrl(session, client, postBinding ? SAML_POST_BINDING : SAML_REDIRECT_BINDING, false);
        if (bindingUri == null) {
            logger.warnf("Failed to logout client %s, skipping this client.  Please configure the logout service url in the admin console for your client applications.", client.getClientId());
            return null;
        }
        NodeGenerator[] extensions = new NodeGenerator[] {};
        if (!postBinding) {
            if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
                KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
                String keyName = samlClient.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());
                extensions = new NodeGenerator[] { new KeycloakKeySamlExtensionGenerator(keyName) };
            }
        }
        LogoutRequestType logoutRequest = createLogoutRequest(bindingUri, clientSession, client, extensions);
        JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient, "true".equals(clientSession.getNote(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.get())));
        // If this session uses artifact binding, send an artifact instead of the LogoutRequest
        if ("true".equals(clientSession.getNote(JBossSAMLURIConstants.SAML_HTTP_ARTIFACT_BINDING.get())) && useArtifactForLogout(client)) {
            clientSession.setAction(CommonClientSessionModel.Action.LOGGING_OUT.name());
            return buildArtifactAuthenticatedResponse(clientSession, bindingUri, logoutRequest, binding);
        }
        Document samlDocument = SAML2Request.convert(logoutRequest);
        if (postBinding) {
            // This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add <samlp:Extensions> element
            return binding.postBinding(samlDocument).request(bindingUri);
        } else {
            logger.debug("frontchannel redirect binding");
            return binding.redirectBinding(samlDocument).request(bindingUri);
        }
    } catch (ConfigurationException | ProcessingException | IOException | ParsingException e) {
        throw new RuntimeException(e);
    }
}
Also used : LogoutRequestType(org.keycloak.dom.saml.v2.protocol.LogoutRequestType) IOException(java.io.IOException) KeycloakKeySamlExtensionGenerator(org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator) Document(org.w3c.dom.Document) ClientModel(org.keycloak.models.ClientModel) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) KeyManager(org.keycloak.models.KeyManager) NodeGenerator(org.keycloak.saml.SamlProtocolExtensionsAwareBuilder.NodeGenerator) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 3 with ConfigurationException

use of org.keycloak.saml.common.exceptions.ConfigurationException 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);
}
Also used : ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) ArtifactResponseType(org.keycloak.dom.saml.v2.protocol.ArtifactResponseType) Document(org.w3c.dom.Document) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 4 with ConfigurationException

use of org.keycloak.saml.common.exceptions.ConfigurationException in project keycloak by keycloak.

the class SamlService method artifactResolutionService.

/**
 * Handles SOAP messages. Chooses the correct response path depending on whether the message is of type ECP or Artifact
 * @param inputStream the data of the request.
 * @return The response to the SOAP message
 */
@POST
@Path(ARTIFACT_RESOLUTION_SERVICE_PATH)
@NoCache
@Consumes({ "application/soap+xml", MediaType.TEXT_XML })
public Response artifactResolutionService(InputStream inputStream) {
    Document soapBodyContents = Soap.extractSoapMessage(inputStream);
    ArtifactResolveType artifactResolveType = null;
    SAMLDocumentHolder samlDocumentHolder = null;
    try {
        samlDocumentHolder = SAML2Request.getSAML2ObjectFromDocument(soapBodyContents);
        if (samlDocumentHolder.getSamlObject() instanceof ArtifactResolveType) {
            logger.debug("Received artifact resolve message");
            artifactResolveType = (ArtifactResolveType) samlDocumentHolder.getSamlObject();
        }
    } catch (Exception e) {
        logger.errorf("Artifact resolution endpoint obtained request that contained no " + "ArtifactResolve message: %s", DocumentUtil.asString(soapBodyContents));
        return Soap.createFault().reason("").detail("").build();
    }
    if (artifactResolveType == null) {
        logger.errorf("Artifact resolution endpoint obtained request that contained no " + "ArtifactResolve message: %s", DocumentUtil.asString(soapBodyContents));
        return Soap.createFault().reason("").detail("").build();
    }
    try {
        return artifactResolve(artifactResolveType, samlDocumentHolder);
    } catch (Exception e) {
        try {
            return emptyArtifactResponseMessage(artifactResolveType, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
        } catch (ConfigurationException | ProcessingException configurationException) {
            String reason = "An error occurred while trying to return the artifactResponse";
            String detail = e.getMessage();
            if (detail == null) {
                detail = "";
            }
            logger.errorf("Failure during ArtifactResolve reason: %s, detail: %s", reason, detail);
            return Soap.createFault().reason(reason).detail(detail).build();
        }
    }
}
Also used : ArtifactResolveType(org.keycloak.dom.saml.v2.protocol.ArtifactResolveType) SAMLDocumentHolder(org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder) Document(org.w3c.dom.Document) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) IOException(java.io.IOException) URISyntaxException(java.net.URISyntaxException) VerificationException(org.keycloak.common.VerificationException) NotFoundException(javax.ws.rs.NotFoundException) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) NoCache(org.jboss.resteasy.annotations.cache.NoCache)

Example 5 with ConfigurationException

use of org.keycloak.saml.common.exceptions.ConfigurationException 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);
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) Document(org.w3c.dom.Document) SAMLDataMarshaller(org.keycloak.broker.saml.SAMLDataMarshaller) StatusResponseType(org.keycloak.dom.saml.v2.protocol.StatusResponseType) SamlArtifactSessionMappingModel(org.keycloak.models.SamlArtifactSessionMappingModel) ClientModel(org.keycloak.models.ClientModel) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) VerificationException(org.keycloak.common.VerificationException) ArtifactResponseType(org.keycloak.dom.saml.v2.protocol.ArtifactResponseType) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Aggregations

ConfigurationException (org.keycloak.saml.common.exceptions.ConfigurationException)24 ProcessingException (org.keycloak.saml.common.exceptions.ProcessingException)20 ParsingException (org.keycloak.saml.common.exceptions.ParsingException)14 Document (org.w3c.dom.Document)14 AuthnRequestType (org.keycloak.dom.saml.v2.protocol.AuthnRequestType)8 IOException (java.io.IOException)7 Element (org.w3c.dom.Element)6 ResponseType (org.keycloak.dom.saml.v2.protocol.ResponseType)5 StatusResponseType (org.keycloak.dom.saml.v2.protocol.StatusResponseType)5 SAML2Request (org.keycloak.saml.processing.api.saml.v2.request.SAML2Request)5 SAMLDocumentHolder (org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder)5 ParserConfigurationException (javax.xml.parsers.ParserConfigurationException)4 Response (javax.ws.rs.core.Response)3 Test (org.junit.Test)3 VerificationException (org.keycloak.common.VerificationException)3 ClientModel (org.keycloak.models.ClientModel)3 SignatureAlgorithm (org.keycloak.saml.SignatureAlgorithm)3 JBossSAMLURIConstants (org.keycloak.saml.common.constants.JBossSAMLURIConstants)3 SAML2Response (org.keycloak.saml.processing.api.saml.v2.response.SAML2Response)3 ByteArrayInputStream (java.io.ByteArrayInputStream)2