pl.edu.icm.unity.saml.SAMLResponseValidatorUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2014 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.saml;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
import eu.emi.security.authn.x509.X509Credential;
import eu.unicore.samly2.SAMLBindings;
import eu.unicore.samly2.assertion.AssertionParser;
import eu.unicore.samly2.assertion.AttributeAssertionParser;
import eu.unicore.samly2.attrprofile.ParsedAttribute;
import eu.unicore.samly2.exceptions.SAMLValidationException;
import eu.unicore.samly2.messages.SAMLVerifiableElement;
import eu.unicore.samly2.trust.SamlTrustChecker;
import eu.unicore.samly2.validators.AssertionValidator;
import eu.unicore.samly2.validators.ReplayAttackChecker;
import eu.unicore.samly2.validators.SSOAuthnResponseValidator;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.authn.RemoteAuthenticationException;
import pl.edu.icm.unity.engine.api.authn.remote.RemoteAttribute;
import pl.edu.icm.unity.engine.api.authn.remote.RemoteGroupMembership;
import pl.edu.icm.unity.engine.api.authn.remote.RemoteIdentity;
import pl.edu.icm.unity.engine.api.authn.remote.RemotelyAuthenticatedInput;
import pl.edu.icm.unity.saml.sp.config.AdditionalCredential;
import pl.edu.icm.unity.saml.sp.config.SAMLSPConfiguration;
import pl.edu.icm.unity.saml.sp.config.TrustedIdPConfiguration;
import xmlbeans.org.oasis.saml2.assertion.AssertionDocument;
import xmlbeans.org.oasis.saml2.assertion.AssertionType;
import xmlbeans.org.oasis.saml2.assertion.AuthnContextType;
import xmlbeans.org.oasis.saml2.assertion.AuthnStatementType;
import xmlbeans.org.oasis.saml2.assertion.NameIDType;
import xmlbeans.org.oasis.saml2.protocol.ResponseDocument;
/**
* Class used to validate SAML responses. Used by ECP and SP subsystems.
* The code validates the SAML response and performs initial parsing, so the output can be directly
* feed to translation profile engine.
*
* @author K. Benedyczak
*/
public class SAMLResponseValidatorUtil
{
private static final Logger log = Log.getLogger(Log.U_SERVER_SAML, SAMLResponseValidatorUtil.class);
public static final String AUTHN_CONTEXT_CLASS_REF_ATTR = "authnContextClassRef";
private SAMLSPConfiguration spConfiguration;
private ReplayAttackChecker replayAttackChecker;
private String responseConsumerAddress;
public SAMLResponseValidatorUtil(SAMLSPConfiguration spConfiguration,
ReplayAttackChecker replayAttackChecker, String responseConsumerAddress)
{
this.spConfiguration = spConfiguration;
this.replayAttackChecker = replayAttackChecker;
this.responseConsumerAddress = responseConsumerAddress;
}
public RemotelyAuthenticatedInput verifySAMLResponse(ResponseDocument responseDocument,
SAMLVerifiableElement verifiableResponse, String requestId, SAMLBindings binding, String groupAttribute,
TrustedIdPConfiguration idp, SamlTrustChecker trustChecker) throws RemoteAuthenticationException
{
SSOAuthnResponseValidator validator = validate(responseDocument, verifiableResponse, requestId, binding,
groupAttribute, idp, trustChecker, spConfiguration.requesterCredential,
spConfiguration.additionalCredential);
return convertAssertion(responseDocument, validator, groupAttribute, idp);
}
private SSOAuthnResponseValidator validate(ResponseDocument responseDocument,
SAMLVerifiableElement verifiableResponse, String requestId, SAMLBindings binding, String groupAttribute,
TrustedIdPConfiguration idp, SamlTrustChecker trustChecker, X509Credential credential,
Optional alternativeCredential) throws RemoteAuthenticationException
{
SSOAuthnResponseValidator validator = getValidator(credential, trustChecker, binding, requestId);
try
{
validator.validate(responseDocument, verifiableResponse);
} catch (SAMLValidationException e)
{
if (alternativeCredential.isPresent())
{
log.warn("SAML response validation using main credential failed: ", e);
log.info("Try validate SAML response using alternative credential");
return validateUsingAdditionalCredential(responseDocument, verifiableResponse, requestId, binding, groupAttribute, idp,
trustChecker, alternativeCredential.get().credential);
}
throw new RemoteAuthenticationException(
"The SAML response is either invalid or is issued " + "by an untrusted identity provider.", e);
}
return validator;
}
private SSOAuthnResponseValidator validateUsingAdditionalCredential(ResponseDocument responseDocument,
SAMLVerifiableElement verifiableResponse, String requestId, SAMLBindings binding, String groupAttribute,
TrustedIdPConfiguration idp, SamlTrustChecker trustChecker, X509Credential credential) throws RemoteAuthenticationException
{
SSOAuthnResponseValidator validator = getValidator(credential, trustChecker, binding, requestId);
try
{
validator.validate(responseDocument, verifiableResponse);
} catch (SAMLValidationException e)
{
throw new RemoteAuthenticationException(
"The SAML response is either invalid or is issued " + "by an untrusted identity provider.", e);
}
return validator;
}
private SSOAuthnResponseValidator getValidator(X509Credential credential, SamlTrustChecker trustChecker,
SAMLBindings binding, String requestId)
{
PrivateKey decryptKey = credential == null ? null : credential.getKey();
return new SSOAuthnResponseValidator(spConfiguration.requesterSamlId, responseConsumerAddress, requestId,
AssertionValidator.DEFAULT_VALIDITY_GRACE_PERIOD, trustChecker, replayAttackChecker, binding,
decryptKey);
}
RemotelyAuthenticatedInput convertAssertion(ResponseDocument responseDocument,
SSOAuthnResponseValidator validator, String groupA, TrustedIdPConfiguration idp) throws RemoteAuthenticationException
{
xmlbeans.org.oasis.saml2.protocol.ResponseType resp = responseDocument.getResponse();
NameIDType issuer = resp.getIssuer();
RemotelyAuthenticatedInput input = new RemotelyAuthenticatedInput(issuer.getStringValue());
input.setIdentities(getAuthenticatedIdentities(validator));
List remoteAttributes = getAttributes(validator);
remoteAttributes.add(getAuthnContextClassAttribute(validator));
input.setAttributes(remoteAttributes);
input.setRawAttributes(input.getAttributes());
input.setGroups(getGroups(remoteAttributes, groupA));
addSessionParticipants(validator, issuer, input, idp);
return input;
}
private List getAuthenticatedIdentities(SSOAuthnResponseValidator validator)
{
List authnAssertions = validator.getAuthNAssertions();
List ret = new ArrayList<>(authnAssertions.size());
for (int i=0; i authnAssertions = validator.getAuthNAssertions();
for (int i=0; i getAttributes(SSOAuthnResponseValidator validator) throws RemoteAuthenticationException
{
List assertions = validator.getAttributeAssertions();
List ret = new ArrayList<>(assertions.size());
for (AssertionDocument assertion: assertions)
{
AttributeAssertionParser parser = new AttributeAssertionParser(assertion);
List parsedAttrs;
try
{
parsedAttrs = parser.getAttributes();
} catch (SAMLValidationException e)
{
throw new RemoteAuthenticationException("Problem retrieving attributes from the SAML data", e);
}
for (ParsedAttribute pa: parsedAttrs)
{
List strValues = pa.getStringValues();
ret.add(new RemoteAttribute(pa.getName(),
(Object[]) strValues.toArray(new String[strValues.size()])));
}
}
return ret;
}
private RemoteAttribute getAuthnContextClassAttribute(SSOAuthnResponseValidator validator)
{
List assertions = validator.getAuthNAssertions();
List values = new ArrayList<>();
for (AssertionDocument assertion: assertions)
{
AssertionParser parser = new AssertionParser(assertion);
AuthnStatementType[] authnStatements = parser.getXMLBean().getAuthnStatementArray();
for (AuthnStatementType statement: authnStatements)
{
AuthnContextType authnContext = statement.getAuthnContext();
if (authnContext != null)
{
String authnContextClassRef = authnContext.getAuthnContextClassRef();
if (authnContextClassRef != null)
values.add(authnContextClassRef);
}
}
}
return new RemoteAttribute(AUTHN_CONTEXT_CLASS_REF_ATTR,
(Object[]) values.toArray(new String[values.size()]));
}
private List getGroups(List remoteAttributes, String groupA)
{
List ret = new ArrayList<>();
if (groupA == null)
return ret;
for (RemoteAttribute ra: remoteAttributes)
{
if (ra.getName().equals(groupA))
{
for (Object value: ra.getValues())
{
ret.add(new RemoteGroupMembership((String) value));
}
}
}
return ret;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy