Search in sources :

Example 1 with SSOValidatorResponse

use of org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse in project syncope by apache.

the class SAML2ReaderWriter method validate.

public SSOValidatorResponse validate(final Response samlResponse, final SAML2IdPEntity idp, final String assertionConsumerURL, final String requestId, final String spEntityID) throws WSSecurityException {
    // validate the SAML response and, if needed, decrypt the provided assertion(s)
    Merlin crypto = new Merlin();
    crypto.setKeyStore(loader.getKeyStore());
    crypto.setTrustStore(idp.getTrustStore());
    SAMLProtocolResponseValidator protocolValidator = new SAMLProtocolResponseValidator();
    protocolValidator.setKeyInfoMustBeAvailable(true);
    protocolValidator.validateSamlResponse(samlResponse, crypto, callbackHandler);
    SAMLSSOResponseValidator ssoResponseValidator = new SAMLSSOResponseValidator();
    ssoResponseValidator.setAssertionConsumerURL(assertionConsumerURL);
    ssoResponseValidator.setIssuerIDP(idp.getId());
    ssoResponseValidator.setRequestId(requestId);
    ssoResponseValidator.setSpIdentifier(spEntityID);
    SSOValidatorResponse validatorResponse = ssoResponseValidator.validateSamlResponse(samlResponse, idp.getBindingType() == SAML2BindingType.POST);
    if (LOG.isDebugEnabled()) {
        try {
            StringWriter writer = new StringWriter();
            write(writer, samlResponse, false);
            writer.close();
            LOG.debug("SAML response with decrypted assertions: {}", writer.toString());
        } catch (Exception e) {
            LOG.error("Could not log the SAML response with decrypted assertions", e);
        }
    }
    return validatorResponse;
}
Also used : SAMLProtocolResponseValidator(org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator) SAMLSSOResponseValidator(org.apache.cxf.rs.security.saml.sso.SAMLSSOResponseValidator) StringWriter(java.io.StringWriter) Merlin(org.apache.wss4j.common.crypto.Merlin) WSSecurityException(org.apache.wss4j.common.ext.WSSecurityException) XMLStreamException(javax.xml.stream.XMLStreamException) SecurityException(org.opensaml.security.SecurityException) SignatureException(java.security.SignatureException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) TransformerException(javax.xml.transform.TransformerException) DataFormatException(java.util.zip.DataFormatException) TransformerConfigurationException(javax.xml.transform.TransformerConfigurationException) IOException(java.io.IOException) SSOValidatorResponse(org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse)

Example 2 with SSOValidatorResponse

use of org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse in project syncope by apache.

the class SAML2SPLogic method validateLoginResponse.

@PreAuthorize("hasRole('" + StandardEntitlement.ANONYMOUS + "')")
public SAML2LoginResponseTO validateLoginResponse(final SAML2ReceivedResponseTO response) {
    check();
    // 1. first checks for the provided relay state
    if (response.getRelayState() == null) {
        throw new IllegalArgumentException("No Relay State was provided");
    }
    Boolean useDeflateEncoding = false;
    String requestId = null;
    if (!IDP_INITIATED_RELAY_STATE.equals(response.getRelayState())) {
        JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
        if (!relayState.verifySignatureWith(jwsSignatureVerifier)) {
            throw new IllegalArgumentException("Invalid signature found in Relay State");
        }
        useDeflateEncoding = Boolean.valueOf(relayState.getJwtClaims().getClaim(JWT_CLAIM_IDP_DEFLATE).toString());
        requestId = relayState.getJwtClaims().getSubject();
        Long expiryTime = relayState.getJwtClaims().getExpiryTime();
        if (expiryTime == null || (expiryTime * 1000L) < new Date().getTime()) {
            throw new IllegalArgumentException("Relay State is expired");
        }
    }
    // 2. parse the provided SAML response
    if (response.getSamlResponse() == null) {
        throw new IllegalArgumentException("No SAML Response was provided");
    }
    Response samlResponse;
    try {
        XMLObject responseObject = saml2rw.read(useDeflateEncoding, response.getSamlResponse());
        if (!(responseObject instanceof Response)) {
            throw new IllegalArgumentException("Expected " + Response.class.getName() + ", got " + responseObject.getClass().getName());
        }
        samlResponse = (Response) responseObject;
    } catch (Exception e) {
        LOG.error("While parsing AuthnResponse", e);
        SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
        sce.getElements().add(e.getMessage());
        throw sce;
    }
    // 3. validate the SAML response and, if needed, decrypt the provided assertion(s)
    if (samlResponse.getIssuer() == null || samlResponse.getIssuer().getValue() == null) {
        throw new IllegalArgumentException("The SAML Response must contain an Issuer");
    }
    final SAML2IdPEntity idp = getIdP(samlResponse.getIssuer().getValue());
    if (idp.getConnObjectKeyItem() == null) {
        throw new IllegalArgumentException("No mapping provided for SAML 2.0 IdP '" + idp.getId() + "'");
    }
    if (IDP_INITIATED_RELAY_STATE.equals(response.getRelayState()) && !idp.isSupportUnsolicited()) {
        throw new IllegalArgumentException("An unsolicited request is not allowed for idp: " + idp.getId());
    }
    SSOValidatorResponse validatorResponse = null;
    try {
        validatorResponse = saml2rw.validate(samlResponse, idp, getAssertionConsumerURL(response.getSpEntityID(), response.getUrlContext()), requestId, response.getSpEntityID());
    } catch (Exception e) {
        LOG.error("While validating AuthnResponse", e);
        SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
        sce.getElements().add(e.getMessage());
        throw sce;
    }
    // 4. prepare the result: find matching user (if any) and return the received attributes
    final SAML2LoginResponseTO responseTO = new SAML2LoginResponseTO();
    responseTO.setIdp(idp.getId());
    responseTO.setSloSupported(idp.getSLOLocation(idp.getBindingType()) != null);
    Assertion assertion = validatorResponse.getOpensamlAssertion();
    NameID nameID = assertion.getSubject().getNameID();
    if (nameID == null) {
        throw new IllegalArgumentException("NameID not found");
    }
    String keyValue = null;
    if (StringUtils.isNotBlank(nameID.getValue()) && idp.getConnObjectKeyItem().getExtAttrName().equals("NameID")) {
        keyValue = nameID.getValue();
    }
    if (assertion.getConditions().getNotOnOrAfter() != null) {
        responseTO.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter().toDate());
    }
    assertion.getAuthnStatements().forEach(authnStmt -> {
        responseTO.setSessionIndex(authnStmt.getSessionIndex());
        responseTO.setAuthInstant(authnStmt.getAuthnInstant().toDate());
        if (authnStmt.getSessionNotOnOrAfter() != null) {
            responseTO.setNotOnOrAfter(authnStmt.getSessionNotOnOrAfter().toDate());
        }
    });
    for (AttributeStatement attrStmt : assertion.getAttributeStatements()) {
        for (Attribute attr : attrStmt.getAttributes()) {
            if (!attr.getAttributeValues().isEmpty()) {
                String attrName = attr.getFriendlyName() == null ? attr.getName() : attr.getFriendlyName();
                if (attrName.equals(idp.getConnObjectKeyItem().getExtAttrName())) {
                    if (attr.getAttributeValues().get(0) instanceof XSString) {
                        keyValue = ((XSString) attr.getAttributeValues().get(0)).getValue();
                    } else if (attr.getAttributeValues().get(0) instanceof XSAny) {
                        keyValue = ((XSAny) attr.getAttributeValues().get(0)).getTextContent();
                    }
                }
                AttrTO attrTO = new AttrTO();
                attrTO.setSchema(attrName);
                attr.getAttributeValues().stream().filter(value -> value.getDOM() != null).forEachOrdered(value -> {
                    attrTO.getValues().add(value.getDOM().getTextContent());
                });
                responseTO.getAttrs().add(attrTO);
            }
        }
    }
    final List<String> matchingUsers = keyValue == null ? Collections.<String>emptyList() : userManager.findMatchingUser(keyValue, idp.getKey());
    LOG.debug("Found {} matching users for {}", matchingUsers.size(), keyValue);
    String username;
    if (matchingUsers.isEmpty()) {
        if (idp.isCreateUnmatching()) {
            LOG.debug("No user matching {}, about to create", keyValue);
            username = AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(), () -> userManager.create(idp, responseTO, nameID.getValue()));
        } else if (idp.isSelfRegUnmatching()) {
            responseTO.setNameID(nameID.getValue());
            UserTO userTO = new UserTO();
            userManager.fill(idp.getKey(), responseTO, userTO);
            responseTO.getAttrs().clear();
            responseTO.getAttrs().addAll(userTO.getPlainAttrs());
            responseTO.getAttrs().addAll(userTO.getVirAttrs());
            if (StringUtils.isNotBlank(userTO.getUsername())) {
                responseTO.setUsername(userTO.getUsername());
            }
            responseTO.setSelfReg(true);
            return responseTO;
        } else {
            throw new NotFoundException("User matching the provided value " + keyValue);
        }
    } else if (matchingUsers.size() > 1) {
        throw new IllegalArgumentException("Several users match the provided value " + keyValue);
    } else {
        if (idp.isUpdateMatching()) {
            LOG.debug("About to update {} for {}", matchingUsers.get(0), keyValue);
            username = AuthContextUtils.execWithAuthContext(AuthContextUtils.getDomain(), () -> userManager.update(matchingUsers.get(0), idp, responseTO));
        } else {
            username = matchingUsers.get(0);
        }
    }
    responseTO.setUsername(username);
    responseTO.setNameID(nameID.getValue());
    // 5. generate JWT for further access
    Map<String, Object> claims = new HashMap<>();
    claims.put(JWT_CLAIM_IDP_ENTITYID, idp.getId());
    claims.put(JWT_CLAIM_NAMEID_FORMAT, nameID.getFormat());
    claims.put(JWT_CLAIM_NAMEID_VALUE, nameID.getValue());
    claims.put(JWT_CLAIM_SESSIONINDEX, responseTO.getSessionIndex());
    byte[] authorities = null;
    try {
        authorities = ENCRYPTOR.encode(POJOHelper.serialize(authDataAccessor.getAuthorities(responseTO.getUsername())), CipherAlgorithm.AES).getBytes();
    } catch (Exception e) {
        LOG.error("Could not fetch authorities", e);
    }
    Pair<String, Date> accessTokenInfo = accessTokenDataBinder.create(responseTO.getUsername(), claims, authorities, true);
    responseTO.setAccessToken(accessTokenInfo.getLeft());
    responseTO.setAccessTokenExpiryTime(accessTokenInfo.getRight());
    return responseTO;
}
Also used : SAMLVersion(org.opensaml.saml.common.SAMLVersion) XSAny(org.opensaml.core.xml.schema.XSAny) JwsJwtCompactConsumer(org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer) SyncopeClientException(org.apache.syncope.common.lib.SyncopeClientException) Date(java.util.Date) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize) AuthnRequest(org.opensaml.saml.saml2.core.AuthnRequest) Autowired(org.springframework.beans.factory.annotation.Autowired) SAML2ReaderWriter(org.apache.syncope.core.logic.saml2.SAML2ReaderWriter) KeyDescriptor(org.opensaml.saml.saml2.metadata.KeyDescriptor) SAML2IdP(org.apache.syncope.core.persistence.api.entity.SAML2IdP) KeyInfoGenerator(org.opensaml.xmlsec.keyinfo.KeyInfoGenerator) StringUtils(org.apache.commons.lang3.StringUtils) AuthnRequestBuilder(org.opensaml.saml.saml2.core.impl.AuthnRequestBuilder) LogoutRequest(org.opensaml.saml.saml2.core.LogoutRequest) Attribute(org.opensaml.saml.saml2.core.Attribute) AuthnContextComparisonTypeEnumeration(org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration) Pair(org.apache.commons.lang3.tuple.Pair) SAML2ReceivedResponseTO(org.apache.syncope.common.lib.to.SAML2ReceivedResponseTO) AttributeStatement(org.opensaml.saml.saml2.core.AttributeStatement) Map(java.util.Map) RequestedAuthnContext(org.opensaml.saml.saml2.core.RequestedAuthnContext) SAML2IdPDAO(org.apache.syncope.core.persistence.api.dao.SAML2IdPDAO) AuthContextUtils(org.apache.syncope.core.spring.security.AuthContextUtils) XSString(org.opensaml.core.xml.schema.XSString) Method(java.lang.reflect.Method) Triple(org.apache.commons.lang3.tuple.Triple) Response(org.opensaml.saml.saml2.core.Response) AssertionConsumerServiceBuilder(org.opensaml.saml.saml2.metadata.impl.AssertionConsumerServiceBuilder) RandomBasedGenerator(com.fasterxml.uuid.impl.RandomBasedGenerator) AssertionConsumerService(org.opensaml.saml.saml2.metadata.AssertionConsumerService) NameIDFormat(org.opensaml.saml.saml2.metadata.NameIDFormat) Resource(javax.annotation.Resource) AccessTokenDataBinder(org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder) AuthnContextClassRef(org.opensaml.saml.saml2.core.AuthnContextClassRef) SSOValidatorResponse(org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse) NotFoundException(org.apache.syncope.core.persistence.api.dao.NotFoundException) StandardCharsets(java.nio.charset.StandardCharsets) IssuerBuilder(org.opensaml.saml.saml2.core.impl.IssuerBuilder) SPSSODescriptor(org.opensaml.saml.saml2.metadata.SPSSODescriptor) List(java.util.List) Issuer(org.opensaml.saml.saml2.core.Issuer) NameIDFormatBuilder(org.opensaml.saml.saml2.metadata.impl.NameIDFormatBuilder) EntityDescriptor(org.opensaml.saml.saml2.metadata.EntityDescriptor) AuthnContextClassRefBuilder(org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder) AbstractBaseBean(org.apache.syncope.common.lib.AbstractBaseBean) AuthnContext(org.opensaml.saml.saml2.core.AuthnContext) StandardEntitlement(org.apache.syncope.common.lib.types.StandardEntitlement) POJOHelper(org.apache.syncope.core.provisioning.api.serialization.POJOHelper) AttrTO(org.apache.syncope.common.lib.to.AttrTO) SAML2RequestTO(org.apache.syncope.common.lib.to.SAML2RequestTO) SAML2BindingType(org.apache.syncope.common.lib.types.SAML2BindingType) LogoutResponse(org.opensaml.saml.saml2.core.LogoutResponse) HashMap(java.util.HashMap) NameIDPolicyBuilder(org.opensaml.saml.saml2.core.impl.NameIDPolicyBuilder) StatusCode(org.opensaml.saml.saml2.core.StatusCode) SPSSODescriptorBuilder(org.opensaml.saml.saml2.metadata.impl.SPSSODescriptorBuilder) EntityDescriptorBuilder(org.opensaml.saml.saml2.metadata.impl.EntityDescriptorBuilder) SingleLogoutServiceBuilder(org.opensaml.saml.saml2.metadata.impl.SingleLogoutServiceBuilder) SAML2LoginResponseTO(org.apache.syncope.common.lib.to.SAML2LoginResponseTO) Assertion(org.opensaml.saml.saml2.core.Assertion) OutputStreamWriter(java.io.OutputStreamWriter) ClientExceptionType(org.apache.syncope.common.lib.types.ClientExceptionType) SAML2IdPCache(org.apache.syncope.core.logic.saml2.SAML2IdPCache) XMLObject(org.opensaml.core.xml.XMLObject) SAMLConstants(org.opensaml.saml.common.xml.SAMLConstants) CipherAlgorithm(org.apache.syncope.common.lib.types.CipherAlgorithm) OutputStream(java.io.OutputStream) KeyDescriptorBuilder(org.opensaml.saml.saml2.metadata.impl.KeyDescriptorBuilder) Encryptor(org.apache.syncope.core.spring.security.Encryptor) SingleLogoutService(org.opensaml.saml.saml2.metadata.SingleLogoutService) DateTime(org.joda.time.DateTime) SessionIndexBuilder(org.opensaml.saml.saml2.core.impl.SessionIndexBuilder) SAML2UserManager(org.apache.syncope.core.logic.saml2.SAML2UserManager) LogoutRequestBuilder(org.opensaml.saml.saml2.core.impl.LogoutRequestBuilder) AuthDataAccessor(org.apache.syncope.core.spring.security.AuthDataAccessor) AccessTokenDAO(org.apache.syncope.core.persistence.api.dao.AccessTokenDAO) JwsSignatureVerifier(org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier) NameIDBuilder(org.opensaml.saml.saml2.core.impl.NameIDBuilder) ResourceUtils(org.springframework.util.ResourceUtils) SessionIndex(org.opensaml.saml.saml2.core.SessionIndex) URLEncoder(java.net.URLEncoder) Component(org.springframework.stereotype.Component) X509KeyInfoGeneratorFactory(org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory) RequestedAuthnContextBuilder(org.opensaml.saml.saml2.core.impl.RequestedAuthnContextBuilder) NameIDType(org.opensaml.saml.saml2.core.NameIDType) Generators(com.fasterxml.uuid.Generators) NameIDPolicy(org.opensaml.saml.saml2.core.NameIDPolicy) UserTO(org.apache.syncope.common.lib.to.UserTO) Collections(java.util.Collections) NameID(org.opensaml.saml.saml2.core.NameID) SAML2IdPEntity(org.apache.syncope.core.logic.saml2.SAML2IdPEntity) Attribute(org.opensaml.saml.saml2.core.Attribute) HashMap(java.util.HashMap) AttrTO(org.apache.syncope.common.lib.to.AttrTO) NotFoundException(org.apache.syncope.core.persistence.api.dao.NotFoundException) XSString(org.opensaml.core.xml.schema.XSString) XSAny(org.opensaml.core.xml.schema.XSAny) JwsJwtCompactConsumer(org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer) SSOValidatorResponse(org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse) SAML2LoginResponseTO(org.apache.syncope.common.lib.to.SAML2LoginResponseTO) NameID(org.opensaml.saml.saml2.core.NameID) SyncopeClientException(org.apache.syncope.common.lib.SyncopeClientException) Assertion(org.opensaml.saml.saml2.core.Assertion) XMLObject(org.opensaml.core.xml.XMLObject) XSString(org.opensaml.core.xml.schema.XSString) Date(java.util.Date) SyncopeClientException(org.apache.syncope.common.lib.SyncopeClientException) NotFoundException(org.apache.syncope.core.persistence.api.dao.NotFoundException) Response(org.opensaml.saml.saml2.core.Response) SSOValidatorResponse(org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse) LogoutResponse(org.opensaml.saml.saml2.core.LogoutResponse) SAML2IdPEntity(org.apache.syncope.core.logic.saml2.SAML2IdPEntity) AttributeStatement(org.opensaml.saml.saml2.core.AttributeStatement) UserTO(org.apache.syncope.common.lib.to.UserTO) XMLObject(org.opensaml.core.xml.XMLObject) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Aggregations

SSOValidatorResponse (org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse)2 Generators (com.fasterxml.uuid.Generators)1 RandomBasedGenerator (com.fasterxml.uuid.impl.RandomBasedGenerator)1 IOException (java.io.IOException)1 OutputStream (java.io.OutputStream)1 OutputStreamWriter (java.io.OutputStreamWriter)1 StringWriter (java.io.StringWriter)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 Method (java.lang.reflect.Method)1 URLEncoder (java.net.URLEncoder)1 StandardCharsets (java.nio.charset.StandardCharsets)1 InvalidKeyException (java.security.InvalidKeyException)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 SignatureException (java.security.SignatureException)1 Collections (java.util.Collections)1 Date (java.util.Date)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 DataFormatException (java.util.zip.DataFormatException)1