From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../idp/PVP2SProfileIDPSpringResourceProvider.java | 30 ++ .../idp/api/builder/ISubjectNameIdGenerator.java | 21 + .../InvalidAssertionConsumerServiceException.java | 30 ++ .../InvalidAssertionEncryptionException.java | 18 + .../pvp2/idp/exception/RequestDeniedException.java | 21 + .../idp/exception/ResponderErrorException.java | 26 + .../exception/SAMLRequestNotSignedException.java | 26 + .../idp/exception/SAMLRequestNotSupported.java | 22 + .../exception/UnprovideableAttributeException.java | 19 + .../pvp2/idp/impl/AbstractPVP2XProtocol.java | 537 +++++++++++++++++++++ .../pvp2/idp/impl/AuthenticationAction.java | 154 ++++++ .../eaaf/modules/pvp2/idp/impl/MetadataAction.java | 99 ++++ .../pvp2/idp/impl/PVPSProfilePendingRequest.java | 103 ++++ .../pvp2/idp/impl/builder/AuthResponseBuilder.java | 129 +++++ .../idp/impl/builder/PVP2AssertionBuilder.java | 448 +++++++++++++++++ 15 files changed, 1683 insertions(+) create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/PVP2SProfileIDPSpringResourceProvider.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/api/builder/ISubjectNameIdGenerator.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionConsumerServiceException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionEncryptionException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/RequestDeniedException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/ResponderErrorException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSignedException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSupported.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/UnprovideableAttributeException.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/MetadataAction.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/PVPSProfilePendingRequest.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java create mode 100644 eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java (limited to 'eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv') diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/PVP2SProfileIDPSpringResourceProvider.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/PVP2SProfileIDPSpringResourceProvider.java new file mode 100644 index 00000000..7a9ac92b --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/PVP2SProfileIDPSpringResourceProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import at.gv.egiz.components.spring.api.SpringResourceProvider; + +public class PVP2SProfileIDPSpringResourceProvider implements SpringResourceProvider { + + @Override + public String getName() { + return "EAAF PVP2 S-Profile IDP SpringResourceProvider"; + } + + @Override + public String[] getPackagesToScan() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Resource[] getResourcesToLoad() { + ClassPathResource sl20AuthConfig = new ClassPathResource("/eaaf_pvp_idp.beans.xml", PVP2SProfileIDPSpringResourceProvider.class); + + return new Resource[] {sl20AuthConfig}; + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/api/builder/ISubjectNameIdGenerator.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/api/builder/ISubjectNameIdGenerator.java new file mode 100644 index 00000000..ac999ffc --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/api/builder/ISubjectNameIdGenerator.java @@ -0,0 +1,21 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.api.builder; + +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public interface ISubjectNameIdGenerator { + + /** + * Generates a SAML2 subjectNameId from authentication data + * + * @param authData Authentication data for the current pending request + * @param spConfig Service provider configuration + * @return Pair of subjectNameId and NameIdFormat + * @throws PVP2Exception + */ + public Pair generateSubjectNameId(IAuthData authData, ISPConfiguration spConfig) throws PVP2Exception; +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionConsumerServiceException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionConsumerServiceException.java new file mode 100644 index 00000000..d9ffa2f2 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionConsumerServiceException.java @@ -0,0 +1,30 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class InvalidAssertionConsumerServiceException extends PVP2Exception { + + public InvalidAssertionConsumerServiceException(int idx) { + super("pvp2.00", new Object[]{idx}); + this.statusCodeValue = StatusCode.REQUESTER_URI; + } + + /** + * + */ + public InvalidAssertionConsumerServiceException(String wrongURL) { + super("pvp2.23", new Object[]{wrongURL}); + this.statusCodeValue = StatusCode.REQUESTER_URI; + + } + + /** + * + */ + private static final long serialVersionUID = 7861790149343943091L; + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionEncryptionException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionEncryptionException.java new file mode 100644 index 00000000..d0b6feb9 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/InvalidAssertionEncryptionException.java @@ -0,0 +1,18 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class InvalidAssertionEncryptionException extends PVP2Exception { + + private static final long serialVersionUID = 6513388841485355549L; + + public InvalidAssertionEncryptionException() { + super("pvp2.16", new Object[]{}); + this.statusCodeValue = StatusCode.RESPONDER_URI; + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/RequestDeniedException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/RequestDeniedException.java new file mode 100644 index 00000000..5abd6dbe --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/RequestDeniedException.java @@ -0,0 +1,21 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class RequestDeniedException extends PVP2Exception { + + public RequestDeniedException() { + super("pvp2.14", null); + this.statusCodeValue = StatusCode.REQUEST_DENIED_URI; + } + + /** + * + */ + private static final long serialVersionUID = 4415896615794730553L; + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/ResponderErrorException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/ResponderErrorException.java new file mode 100644 index 00000000..f7145458 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/ResponderErrorException.java @@ -0,0 +1,26 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class ResponderErrorException extends PVP2Exception { + + /** + * + */ + private static final long serialVersionUID = -425416760138285446L; + + public ResponderErrorException(String messageId, Object[] parameters, + Throwable wrapped) { + super(messageId, parameters, wrapped); + this.statusCodeValue = StatusCode.RESPONDER_URI; + } + + public ResponderErrorException(String messageId, Object[] parameters) { + super(messageId, parameters); + this.statusCodeValue = StatusCode.RESPONDER_URI; + } +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSignedException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSignedException.java new file mode 100644 index 00000000..364fdbf0 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSignedException.java @@ -0,0 +1,26 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class SAMLRequestNotSignedException extends PVP2Exception { + + public SAMLRequestNotSignedException() { + super("pvp2.07", null); + this.statusCodeValue = StatusCode.REQUESTER_URI; + } + + public SAMLRequestNotSignedException(Throwable e) { + super("pvp2.07", null, e); + this.statusCodeValue = StatusCode.REQUESTER_URI; + } + + /** + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSupported.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSupported.java new file mode 100644 index 00000000..b370a7be --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/SAMLRequestNotSupported.java @@ -0,0 +1,22 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + + +public class SAMLRequestNotSupported extends PVP2Exception { + + public SAMLRequestNotSupported() { + super("pvp2.09", null); + this.statusCodeValue = StatusCode.REQUEST_UNSUPPORTED_URI; + } + + /** + * + */ + private static final long serialVersionUID = 1244883178458802767L; + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/UnprovideableAttributeException.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/UnprovideableAttributeException.java new file mode 100644 index 00000000..5dea922c --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/exception/UnprovideableAttributeException.java @@ -0,0 +1,19 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.exception; + +import org.opensaml.saml2.core.StatusCode; + +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; + +public class UnprovideableAttributeException extends PVP2Exception { + /** + * + */ + private static final long serialVersionUID = 3972197758163647157L; + + public UnprovideableAttributeException(String attributeName) { + super("pvp2.10", new Object[] {attributeName}); + this.statusCodeValue = StatusCode.UNKNOWN_ATTR_PROFILE_URI; + } +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java new file mode 100644 index 00000000..93ffa789 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AbstractPVP2XProtocol.java @@ -0,0 +1,537 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl; + +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.Status; +import org.opensaml.saml2.core.StatusCode; +import org.opensaml.saml2.core.StatusMessage; +import org.opensaml.saml2.core.impl.AuthnRequestImpl; +import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.AttributeConsumingService; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.ws.security.SecurityPolicyException; +import org.opensaml.xml.security.x509.X509Credential; +import org.opensaml.xml.signature.SignableXMLObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import at.gv.egiz.components.eventlog.api.EventConstants; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.idp.IModulInfo; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; +import at.gv.egiz.eaaf.core.exceptions.AuthnRequestValidatorException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; +import at.gv.egiz.eaaf.core.exceptions.NoPassivAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.SLOException; +import at.gv.egiz.eaaf.core.impl.idp.controller.AbstractAuthProtocolModulController; +import at.gv.egiz.eaaf.modules.pvp2.PVPEventConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.binding.IEncoder; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.InvalidPVPRequestException; +import at.gv.egiz.eaaf.modules.pvp2.exception.NameIDFormatNotSupportedException; +import at.gv.egiz.eaaf.modules.pvp2.exception.NoMetadataInformationException; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.InvalidAssertionConsumerServiceException; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.PostBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.SoapBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PVPSProfileRequest; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EAAFURICompare; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.AuthnRequestValidator; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.SAMLVerificationEngine; + +public abstract class AbstractPVP2XProtocol extends AbstractAuthProtocolModulController implements IModulInfo { + private static final Logger log = LoggerFactory.getLogger(AbstractPVP2XProtocol.class); + + @Autowired(required=true) protected IPVP2BasicConfiguration pvpBasicConfiguration; + @Autowired(required=true) protected IPVPMetadataProvider metadataProvider; + @Autowired(required=true) protected SAMLVerificationEngine samlVerificationEngine; + + private AbstractCredentialProvider pvpIDPCredentials; + + /** + * Sets a specific credential provider for PVP S-Profile IDP component. + * @param pvpIDPCredentials credential provider + */ + public void setPvpIDPCredentials(AbstractCredentialProvider pvpIDPCredentials) { + this.pvpIDPCredentials = pvpIDPCredentials; + + } + + public boolean generateErrorMessage(Throwable e, + HttpServletRequest request, HttpServletResponse response, + IRequest protocolRequest) throws Throwable { + + if(protocolRequest == null) { + throw e; + } + + if(!(protocolRequest instanceof PVPSProfilePendingRequest) ) { + throw e; + } + PVPSProfilePendingRequest pvpRequest = (PVPSProfilePendingRequest)protocolRequest; + + Response samlResponse = + SAML2Utils.createSAMLObject(Response.class); + Status status = SAML2Utils.createSAMLObject(Status.class); + StatusCode statusCode = SAML2Utils.createSAMLObject(StatusCode.class); + StatusMessage statusMessage = SAML2Utils.createSAMLObject(StatusMessage.class); + + String moaError = null; + + if(e instanceof NoPassivAuthenticationException) { + statusCode.setValue(StatusCode.NO_PASSIVE_URI); + statusMessage.setMessage(StringEscapeUtils.escapeXml(e.getLocalizedMessage())); + + } else if (e instanceof NameIDFormatNotSupportedException) { + statusCode.setValue(StatusCode.INVALID_NAMEID_POLICY_URI); + statusMessage.setMessage(StringEscapeUtils.escapeXml(e.getLocalizedMessage())); + + } else if (e instanceof SLOException) { + //SLOExecpetions only occurs if session information is lost + return false; + + } else if(e instanceof PVP2Exception) { + PVP2Exception ex = (PVP2Exception) e; + statusCode.setValue(ex.getStatusCodeValue()); + String statusMessageValue = ex.getStatusMessageValue(); + if(statusMessageValue != null) { + statusMessage.setMessage(StringEscapeUtils.escapeXml(statusMessageValue)); + } + moaError = statusMessager.mapInternalErrorToExternalError(ex.getErrorId()); + + } else { + statusCode.setValue(StatusCode.RESPONDER_URI); + statusMessage.setMessage(StringEscapeUtils.escapeXml(e.getLocalizedMessage())); + moaError = statusMessager.getResponseErrorCode(e); + } + + + if (StringUtils.isNotEmpty(moaError)) { + StatusCode moaStatusCode = SAML2Utils.createSAMLObject(StatusCode.class); + moaStatusCode.setValue(moaError); + statusCode.setStatusCode(moaStatusCode); + } + + status.setStatusCode(statusCode); + if(statusMessage.getMessage() != null) { + status.setStatusMessage(statusMessage); + } + samlResponse.setStatus(status); + String remoteSessionID = SAML2Utils.getSecureIdentifier(); + samlResponse.setID(remoteSessionID); + + samlResponse.setIssueInstant(new DateTime()); + Issuer nissuer = SAML2Utils.createSAMLObject(Issuer.class); + nissuer.setValue(pvpBasicConfiguration.getIDPEntityId(pvpRequest.getAuthURL())); + nissuer.setFormat(NameID.ENTITY); + samlResponse.setIssuer(nissuer); + + IEncoder encoder = null; + + if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { + encoder = applicationContext.getBean("PVPRedirectBinding", RedirectBinding.class); + + } else if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) { + encoder = applicationContext.getBean("PVPPOSTBinding", PostBinding.class); + + } else if (pvpRequest.getBinding().equals(SAMLConstants.SAML2_SOAP11_BINDING_URI)) { + encoder = applicationContext.getBean("PVPSOAPBinding", SoapBinding.class); + } + + if(encoder == null) { + // default to redirect binding + encoder = new RedirectBinding(); + } + + String relayState = null; + if (pvpRequest.getRequest() != null) + relayState = pvpRequest.getRequest().getRelayState(); + + X509Credential signCred = pvpIDPCredentials.getIDPAssertionSigningCredential(); + + encoder.encodeRespone(request, response, samlResponse, pvpRequest.getConsumerURL(), + relayState, signCred, protocolRequest); + return true; + } + + public boolean validate(HttpServletRequest request, + HttpServletResponse response, IRequest pending) { + + return true; + } + + protected void pvpMetadataRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + //create pendingRequest object + PVPSProfilePendingRequest pendingReq = applicationContext.getBean(PVPSProfilePendingRequest.class); + pendingReq.initialize(req, authConfig); + pendingReq.setModule(getName()); + + revisionsLogger.logEvent( + pendingReq.getUniqueSessionIdentifier(), + pendingReq.getUniqueTransactionIdentifier(), + EventConstants.TRANSACTION_IP, + req.getRemoteAddr()); + + MetadataAction metadataAction = applicationContext.getBean(MetadataAction.class); + metadataAction.processRequest(pendingReq, + req, resp, null); + + } + + protected void PVPIDPPostRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + PVPSProfilePendingRequest pendingReq = null; + + try { + //create pendingRequest object + pendingReq = applicationContext.getBean(PVPSProfilePendingRequest.class); + pendingReq.initialize(req, authConfig); + pendingReq.setModule(getName()); + + revisionsLogger.logEvent(EventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier()); + revisionsLogger.logEvent(EventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier()); + revisionsLogger.logEvent( + pendingReq.getUniqueSessionIdentifier(), + pendingReq.getUniqueTransactionIdentifier(), + EventConstants.TRANSACTION_IP, + req.getRemoteAddr()); + + //get POST-Binding decoder implementation + InboundMessage msg = (InboundMessage) new PostBinding().decode( + req, resp, metadataProvider, false, + new EAAFURICompare(pvpBasicConfiguration.getIDPSSOPostService(pendingReq.getAuthURL()))); + pendingReq.setRequest(msg); + + //preProcess Message + preProcess(req, resp, pendingReq); + + } catch (SecurityPolicyException e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, e.getMessage()); + + } catch (SecurityException e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}, e.getMessage()); + + } catch (EAAFException e) { + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw e; + + } catch (Throwable e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new EAAFException("pvp2.24", new Object[] {e.getMessage()}, e.getMessage(), e); + } + } + + protected void PVPIDPRedirecttRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + PVPSProfilePendingRequest pendingReq = null; + try { + //create pendingRequest object + pendingReq = applicationContext.getBean(PVPSProfilePendingRequest.class); + pendingReq.initialize(req, authConfig); + pendingReq.setModule(getName()); + + revisionsLogger.logEvent(EventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier()); + revisionsLogger.logEvent(EventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier()); + revisionsLogger.logEvent( + pendingReq.getUniqueSessionIdentifier(), + pendingReq.getUniqueTransactionIdentifier(), + EventConstants.TRANSACTION_IP, + req.getRemoteAddr()); + + //get POST-Binding decoder implementation + InboundMessage msg = (InboundMessage) new RedirectBinding().decode( + req, resp, metadataProvider, false, + new EAAFURICompare(pvpBasicConfiguration.getIDPSSORedirectService(pendingReq.getAuthURL()))); + pendingReq.setRequest(msg); + + //preProcess Message + preProcess(req, resp, pendingReq); + + } catch (SecurityPolicyException e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, e.getMessage()); + + } catch (SecurityException e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}, e.getMessage()); + + } catch (EAAFException e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.info("Receive INVALID protocol request: " + samlRequest); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw e; + + } catch (Throwable e) { + String samlRequest = req.getParameter("SAMLRequest"); + log.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, EventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new EAAFException("pvp2.24", new Object[] {e.getMessage()}, e.getMessage(), e); + } + } + + + + /** + * + * + * @param request + * @param response + * @param msg + * @return true if preprocess can handle this request type, otherwise false + * @throws Throwable + */ + abstract protected boolean childPreProcess(HttpServletRequest request, + HttpServletResponse response, PVPSProfilePendingRequest pendingReq) throws Throwable; + + protected void preProcess(HttpServletRequest request, + HttpServletResponse response, PVPSProfilePendingRequest pendingReq) throws Throwable { + + InboundMessage msg = pendingReq.getRequest(); + + if (StringUtils.isEmpty(msg.getEntityID())) { + throw new InvalidProtocolRequestException("pvp2.20", new Object[] {}, "EntityId is null or empty"); + + } + + if(!msg.isVerified()) { + samlVerificationEngine.verify(msg, + TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider)); + msg.setVerified(true); + + } + + + if (msg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)msg).getSamlRequest() instanceof AuthnRequest) + preProcessAuthRequest(request, response, pendingReq); + + else if (childPreProcess(request, response, pendingReq)) + log.debug("Find protocol handler in child implementation"); + + else { + log.error("Receive unsupported PVP21 message"); + throw new InvalidPVPRequestException("Unsupported PVP21 message", new Object[] {}); + } + + revisionsLogger.logEvent(pendingReq, IRevisionLogger.AUTHPROTOCOL_TYPE, getAuthProtocolIdentifier()); + + //switch to session authentication + performAuthentication(request, response, pendingReq); + } + + + /** + * PreProcess Authn request + * @param request + * @param response + * @param pendingReq + * @throws Throwable + */ + private void preProcessAuthRequest(HttpServletRequest request, + HttpServletResponse response, PVPSProfilePendingRequest pendingReq) throws Throwable { + + PVPSProfileRequest moaRequest = ((PVPSProfileRequest)pendingReq.getRequest()); + SignableXMLObject samlReq = moaRequest.getSamlRequest(); + + if(!(samlReq instanceof AuthnRequest)) { + throw new InvalidPVPRequestException("Unsupported request", new Object[] {}); + } + + EntityDescriptor metadata = moaRequest.getEntityMetadata(metadataProvider); + if(metadata == null) { + throw new NoMetadataInformationException(); + } + SPSSODescriptor spSSODescriptor = metadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS); + + AuthnRequest authnRequest = (AuthnRequest)samlReq; + + if (authnRequest.getIssueInstant() == null) { + log.warn("Unsupported request: No IssueInstant Attribute found."); + throw new AuthnRequestValidatorException("Unsupported request: No IssueInstant Attribute found.", new Object[] {}, + "Unsupported request: No IssueInstant Attribute found", pendingReq); + + } + + if (authnRequest.getIssueInstant().minusMinutes(EAAFConstants.ALLOWED_TIME_JITTER).isAfterNow()) { + log.warn("Unsupported request: No IssueInstant DateTime is not valid anymore."); + throw new AuthnRequestValidatorException("Unsupported request: No IssueInstant DateTime is not valid anymore.", new Object[] {}, + "Unsupported request: No IssueInstant DateTime is not valid anymore.", pendingReq); + + } + + //parse AssertionConsumerService + AssertionConsumerService consumerService = null; + if (StringUtils.isNotEmpty(authnRequest.getAssertionConsumerServiceURL()) && + StringUtils.isNotEmpty(authnRequest.getProtocolBinding())) { + //use AssertionConsumerServiceURL from request + + //check requested AssertionConsumingService URL against metadata + List metadataAssertionServiceList = spSSODescriptor.getAssertionConsumerServices(); + for (AssertionConsumerService service : metadataAssertionServiceList) { + if (authnRequest.getProtocolBinding().equals(service.getBinding()) + && authnRequest.getAssertionConsumerServiceURL().equals(service.getLocation())) { + consumerService = SAML2Utils.createSAMLObject(AssertionConsumerService.class); + consumerService.setBinding(authnRequest.getProtocolBinding()); + consumerService.setLocation(authnRequest.getAssertionConsumerServiceURL()); + log.debug("Requested AssertionConsumerServiceURL is valid."); + } + } + + if (consumerService == null) { + throw new InvalidAssertionConsumerServiceException(authnRequest.getAssertionConsumerServiceURL()); + + } + + + } else { + //use AssertionConsumerServiceIndex and select consumerService from metadata + Integer aIdx = authnRequest.getAssertionConsumerServiceIndex(); + int assertionidx = 0; + + if(aIdx != null) { + assertionidx = aIdx.intValue(); + + } else { + assertionidx = SAML2Utils.getDefaultAssertionConsumerServiceIndex(spSSODescriptor); + + } + consumerService = spSSODescriptor.getAssertionConsumerServices().get(assertionidx); + + if (consumerService == null) { + throw new InvalidAssertionConsumerServiceException(aIdx); + + } + } + + + //select AttributeConsumingService from request + AttributeConsumingService attributeConsumer = null; + Integer aIdx = authnRequest.getAttributeConsumingServiceIndex(); + int attributeIdx = 0; + + if(aIdx != null) { + attributeIdx = aIdx.intValue(); + } + + if (spSSODescriptor.getAttributeConsumingServices() != null && + spSSODescriptor.getAttributeConsumingServices().size() > 0) { + attributeConsumer = spSSODescriptor.getAttributeConsumingServices().get(attributeIdx); + } + + //validate AuthnRequest + AuthnRequestImpl authReq = (AuthnRequestImpl) samlReq; + AuthnRequestValidator.validate(authReq); + +// String useMandate = request.getParameter(PARAM_USEMANDATE); +// if(useMandate != null) { +// if(useMandate.equals("true") && attributeConsumer != null) { +// if(!CheckMandateAttributes.canHandleMandate(attributeConsumer)) { +// throw new MandateAttributesNotHandleAbleException(); +// } +// } +// } + + String oaURL = moaRequest.getEntityMetadata(metadataProvider).getEntityID(); + oaURL = StringEscapeUtils.escapeHtml(oaURL); + ISPConfiguration oa = authConfig.getServiceProviderConfiguration(oaURL); + + log.info("Dispatch PVP2 AuthnRequest: OAURL=" + oaURL + " Binding=" + consumerService.getBinding()); + + pendingReq.setSPEntityId(oaURL); + pendingReq.setOnlineApplicationConfiguration(oa); + pendingReq.setBinding(consumerService.getBinding()); + pendingReq.setRequest(moaRequest); + pendingReq.setConsumerURL(consumerService.getLocation()); + + //parse AuthRequest + pendingReq.setPassiv(authReq.isPassive()); + pendingReq.setForce(authReq.isForceAuthn()); + + //AuthnRequest needs authentication + pendingReq.setNeedAuthentication(true); + + //set protocol action, which should be executed after authentication + pendingReq.setAction(AuthenticationAction.class.getName()); + + //write revisionslog entry + revisionsLogger.logEvent(pendingReq, PVPEventConstants.AUTHPROTOCOL_PVP_REQUEST_AUTHREQUEST); + + } + + @PostConstruct + private void verifyInitialization() { + if (pvpIDPCredentials == null) { + log.error("No SAML2 credentialProvider injected!"); + throw new RuntimeException("No SAML2 credentialProvider injected!"); + + } + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java new file mode 100644 index 00000000..adcff465 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/AuthenticationAction.java @@ -0,0 +1,154 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.joda.time.DateTime; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.Assertion; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.ws.message.encoder.MessageEncodingException; +import org.opensaml.xml.security.SecurityException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.IAction; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.data.SLOInformationImpl; +import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.binding.IEncoder; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.BindingNotSupportedException; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.ResponderErrorException; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder.AuthResponseBuilder; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder.PVP2AssertionBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.PostBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.binding.RedirectBinding; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PVPSProfileRequest; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; + +@Service("PVPAuthenticationRequestAction") +public class AuthenticationAction implements IAction { + private static final Logger log = LoggerFactory.getLogger(AuthenticationAction.class); + + private static final String CONFIG_PROPERTY_PVP2_ENABLE_ENCRYPTION = "protocols.pvp2.assertion.encryption.active"; + + @Autowired(required=true) private IPVPMetadataProvider metadataProvider; + @Autowired(required=true) ApplicationContext springContext; + @Autowired(required=true) IConfiguration authConfig; + @Autowired(required=true) PVP2AssertionBuilder assertionBuilder; + @Autowired(required=true) IPVP2BasicConfiguration pvpBasicConfiguration; + + private AbstractCredentialProvider pvpIDPCredentials; + + /** + * Sets a specific credential provider for PVP S-Profile IDP component. + * @param pvpIDPCredentials credential provider + */ + public void setPvpIDPCredentials(AbstractCredentialProvider pvpIDPCredentials) { + this.pvpIDPCredentials = pvpIDPCredentials; + + } + + public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, + HttpServletResponse httpResp, IAuthData authData) throws ResponderErrorException { + PVPSProfilePendingRequest pvpRequest = (PVPSProfilePendingRequest) req; + try { + //get basic information + PVPSProfileRequest moaRequest = (PVPSProfileRequest) pvpRequest.getRequest(); + AuthnRequest authnRequest = (AuthnRequest) moaRequest.getSamlRequest(); + EntityDescriptor peerEntity = moaRequest.getEntityMetadata(metadataProvider); + + AssertionConsumerService consumerService = + SAML2Utils.createSAMLObject(AssertionConsumerService.class); + consumerService.setBinding(pvpRequest.getBinding()); + consumerService.setLocation(pvpRequest.getConsumerURL()); + + DateTime date = new DateTime(); + SLOInformationImpl sloInformation = new SLOInformationImpl(); + String issuerEntityID = pvpBasicConfiguration.getIDPEntityId(pvpRequest.getAuthURL()); + + //build Assertion + Assertion assertion = assertionBuilder.buildAssertion(issuerEntityID, pvpRequest, authnRequest, authData, + peerEntity, date, consumerService, sloInformation); + + Response authResponse = AuthResponseBuilder.buildResponse( + metadataProvider, issuerEntityID, authnRequest, + date, assertion, authConfig.getBasicMOAIDConfigurationBoolean( + CONFIG_PROPERTY_PVP2_ENABLE_ENCRYPTION, true)); + + IEncoder binding = null; + + if (consumerService.getBinding().equals( + SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { + binding = springContext.getBean("PVPRedirectBinding", RedirectBinding.class); + + } else if (consumerService.getBinding().equals( + SAMLConstants.SAML2_POST_BINDING_URI)) { + binding = springContext.getBean("PVPPOSTBinding", PostBinding.class); + + } + + if (binding == null) { + throw new BindingNotSupportedException(consumerService.getBinding()); + } + + binding.encodeRespone(httpReq, httpResp, authResponse, + consumerService.getLocation(), moaRequest.getRelayState(), + pvpIDPCredentials.getIDPAssertionSigningCredential(), req); + + //set protocol type + sloInformation.setProtocolType(req.requestedModule()); + sloInformation.setSpEntityID(req.getServiceProviderConfiguration().getUniqueIdentifier()); + return sloInformation; + + } catch (MessageEncodingException e) { + log.error("Message Encoding exception", e); + throw new ResponderErrorException("pvp2.01", null, e); + + } catch (SecurityException e) { + log.error("Security exception", e); + throw new ResponderErrorException("pvp2.01", null, e); + + } catch (EAAFException e) { + log.error("Response generation error", e); + throw new ResponderErrorException("pvp2.01", null, e); + + } + + } + + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, + HttpServletResponse httpResp) { + return true; + } + + public String getDefaultActionName() { + return "PVPAuthenticationRequestAction"; + + } + + @PostConstruct + private void verifyInitialization() { + if (pvpIDPCredentials == null) { + log.error("No SAML2 credentialProvider injected!"); + throw new RuntimeException("No SAML2 credentialProvider injected!"); + + } + } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/MetadataAction.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/MetadataAction.java new file mode 100644 index 00000000..fa871597 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/MetadataAction.java @@ -0,0 +1,99 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.IAction; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; +import at.gv.egiz.eaaf.core.api.logging.IRevisionLogger; +import at.gv.egiz.eaaf.modules.pvp2.PVPEventConstants; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataBuilderConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataConfigurationFactory; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2MetadataException; +import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PVPMetadataBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; + +@Service("pvpMetadataService") +public class MetadataAction implements IAction { + + private static final Logger log = LoggerFactory.getLogger(MetadataAction.class); + + @Autowired private IRevisionLogger revisionsLogger; + @Autowired private PVPMetadataBuilder metadatabuilder; + @Autowired private IPVPMetadataConfigurationFactory configFactory; + + private AbstractCredentialProvider pvpIDPCredentials; + + /** + * Sets a specific credential provider for PVP S-Profile IDP component. + * @param pvpIDPCredentials credential provider + */ + public void setPvpIDPCredentials(AbstractCredentialProvider pvpIDPCredentials) { + this.pvpIDPCredentials = pvpIDPCredentials; + + } + + public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, + HttpServletResponse httpResp, IAuthData authData) throws PVP2MetadataException { + try { + revisionsLogger.logEvent(req, PVPEventConstants.AUTHPROTOCOL_PVP_METADATA); + + //build metadata + IPVPMetadataBuilderConfiguration metadataConfig = + configFactory.generateMetadataBuilderConfiguration( + req.getAuthURLWithOutSlash(), + pvpIDPCredentials); + + ; + + String metadataXML = metadatabuilder.buildPVPMetadata(metadataConfig); + log.debug("METADATA: " + metadataXML); + + byte[] content = metadataXML.getBytes("UTF-8"); + httpResp.setStatus(HttpServletResponse.SC_OK); + httpResp.setContentLength(content.length); + httpResp.setContentType(MediaType.APPLICATION_XML_VALUE); + httpResp.getOutputStream().write(content); + return null; + + } catch (Exception e) { + log.error("Failed to generate metadata", e); + throw new PVP2MetadataException("pvp2.13", null); + } + } + + public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, + HttpServletResponse httpResp) { + return false; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() + */ + @Override + public String getDefaultActionName() { + return "IDP - PVP Metadata action"; + } + + @PostConstruct + private void verifyInitialization() { + if (pvpIDPCredentials == null) { + log.error("No SAML2 credentialProvider injected!"); + throw new RuntimeException("No SAML2 credentialProvider injected!"); + + } + } + + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/PVPSProfilePendingRequest.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/PVPSProfilePendingRequest.java new file mode 100644 index 00000000..06c64b84 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/PVPSProfilePendingRequest.java @@ -0,0 +1,103 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; + +@Component("PVPSProfilePendingRequest") +@Scope(value = BeanDefinition.SCOPE_PROTOTYPE) +public class PVPSProfilePendingRequest extends RequestImpl { + private static final long serialVersionUID = 4889919265919638188L; + + InboundMessage request; + String binding; + String consumerURL; + + public InboundMessage getRequest() { + return request; + } + + public void setRequest(InboundMessage request) { + this.request = request; + } + + public String getBinding() { + return binding; + } + + public void setBinding(String binding) { + this.binding = binding; + } + + public String getConsumerURL() { + return consumerURL; + } + + public void setConsumerURL(String consumerURL) { + this.consumerURL = consumerURL; + + } + +// /* (non-Javadoc) +// * @see at.gv.egovernment.moa.id.moduls.RequestImpl#getRequestedAttributes() +// */ +// @Override +// public Collection getRequestedAttributes(MetadataProvider metadataProvider) { +// +// Map reqAttr = new HashMap(); +// for (String el : PVP2XProtocol.DEFAULTREQUESTEDATTRFORINTERFEDERATION) +// reqAttr.put(el, ""); +// +// try { +// SPSSODescriptor spSSODescriptor = getRequest().getEntityMetadata(metadataProvider).getSPSSODescriptor(SAMLConstants.SAML20P_NS); +// if (spSSODescriptor.getAttributeConsumingServices() != null && +// spSSODescriptor.getAttributeConsumingServices().size() > 0) { +// +// Integer aIdx = null; +// if (getRequest() instanceof MOARequest && +// ((MOARequest)getRequest()).getSamlRequest() instanceof AuthnRequestImpl) { +// AuthnRequestImpl authnRequest = (AuthnRequestImpl)((MOARequest)getRequest()).getSamlRequest(); +// aIdx = authnRequest.getAttributeConsumingServiceIndex(); +// +// } else { +// Logger.error("MOARequest is NOT of type AuthnRequest"); +// } +// +// int idx = 0; +// +// AttributeConsumingService attributeConsumingService = null; +// +// if (aIdx != null) { +// idx = aIdx.intValue(); +// attributeConsumingService = spSSODescriptor +// .getAttributeConsumingServices().get(idx); +// +// } else { +// List attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices(); +// for (AttributeConsumingService el : attrConsumingServiceList) { +// if (el.isDefault()) +// attributeConsumingService = el; +// } +// } +// +// for ( RequestedAttribute attr : attributeConsumingService.getRequestAttributes()) +// reqAttr.put(attr.getName(), ""); +// } +// +// //return attributQueryBuilder.buildSAML2AttributeList(this.getOnlineApplicationConfiguration(), reqAttr.keySet().iterator()); +// return reqAttr.keySet(); +// +// } catch (NoMetadataInformationException e) { +// Logger.warn("NO metadata found for Entity " + getRequest().getEntityID()); +// return null; +// +// } +// +// } + +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java new file mode 100644 index 00000000..34a28f72 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/AuthResponseBuilder.java @@ -0,0 +1,129 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder; + +import java.util.ArrayList; +import java.util.List; + +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.Assertion; +import org.opensaml.saml2.core.EncryptedAssertion; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.RequestAbstractType; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.encryption.Encrypter; +import org.opensaml.saml2.encryption.Encrypter.KeyPlacement; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.saml2.metadata.provider.MetadataProvider; +import org.opensaml.security.MetadataCredentialResolver; +import org.opensaml.security.MetadataCriteria; +import org.opensaml.xml.encryption.EncryptionException; +import org.opensaml.xml.encryption.EncryptionParameters; +import org.opensaml.xml.encryption.KeyEncryptionParameters; +import org.opensaml.xml.security.CriteriaSet; +import org.opensaml.xml.security.SecurityException; +import org.opensaml.xml.security.credential.UsageType; +import org.opensaml.xml.security.criteria.EntityIDCriteria; +import org.opensaml.xml.security.criteria.UsageCriteria; +import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory; +import org.opensaml.xml.security.x509.X509Credential; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.modules.pvp2.PVPConstants; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.InvalidAssertionEncryptionException; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; + +/** + * @author tlenz + * + */ +public class AuthResponseBuilder { + + private static final Logger log = LoggerFactory.getLogger(AuthResponseBuilder.class); + + public static Response buildResponse(MetadataProvider metadataProvider, String issuerEntityID, RequestAbstractType req, DateTime date, Assertion assertion, boolean enableEncryption) throws InvalidAssertionEncryptionException { + Response authResponse = SAML2Utils.createSAMLObject(Response.class); + + Issuer nissuer = SAML2Utils.createSAMLObject(Issuer.class); + + nissuer.setValue(issuerEntityID); + nissuer.setFormat(NameID.ENTITY); + authResponse.setIssuer(nissuer); + authResponse.setInResponseTo(req.getID()); + + //set responseID + String remoteSessionID = SAML2Utils.getSecureIdentifier(); + authResponse.setID(remoteSessionID); + + + //SAML2 response required IssueInstant + authResponse.setIssueInstant(date); + + authResponse.setStatus(SAML2Utils.getSuccessStatus()); + + //check, if metadata includes an encryption key + MetadataCredentialResolver mdCredResolver = + new MetadataCredentialResolver(metadataProvider); + + CriteriaSet criteriaSet = new CriteriaSet(); + criteriaSet.add( new EntityIDCriteria(req.getIssuer().getValue()) ); + criteriaSet.add( new MetadataCriteria(SPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS) ); + criteriaSet.add( new UsageCriteria(UsageType.ENCRYPTION) ); + + X509Credential encryptionCredentials = null; + try { + encryptionCredentials = (X509Credential) mdCredResolver.resolveSingle(criteriaSet); + + } catch (SecurityException e2) { + log.warn("Can not extract the Assertion Encryption-Key from metadata", e2); + throw new InvalidAssertionEncryptionException(); + + } + + if (encryptionCredentials != null && enableEncryption) { + //encrypt SAML2 assertion + + try { + + EncryptionParameters dataEncParams = new EncryptionParameters(); + dataEncParams.setAlgorithm(PVPConstants.DEFAULT_SYM_ENCRYPTION_METHODE); + + List keyEncParamList = new ArrayList(); + KeyEncryptionParameters keyEncParam = new KeyEncryptionParameters(); + + keyEncParam.setEncryptionCredential(encryptionCredentials); + keyEncParam.setAlgorithm(PVPConstants.DEFAULT_ASYM_ENCRYPTION_METHODE); + KeyInfoGeneratorFactory kigf = Configuration.getGlobalSecurityConfiguration() + .getKeyInfoGeneratorManager().getDefaultManager() + .getFactory(encryptionCredentials); + keyEncParam.setKeyInfoGenerator(kigf.newInstance()); + keyEncParamList.add(keyEncParam); + + Encrypter samlEncrypter = new Encrypter(dataEncParams, keyEncParamList); + //samlEncrypter.setKeyPlacement(KeyPlacement.INLINE); + samlEncrypter.setKeyPlacement(KeyPlacement.PEER); + + EncryptedAssertion encryptAssertion = null; + + encryptAssertion = samlEncrypter.encrypt(assertion); + + authResponse.getEncryptedAssertions().add(encryptAssertion); + + } catch (EncryptionException e1) { + log.warn("Can not encrypt the PVP2 assertion", e1); + throw new InvalidAssertionEncryptionException(); + + } + + } else { + authResponse.getAssertions().add(assertion); + + } + + return authResponse; + } +} diff --git a/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java new file mode 100644 index 00000000..7369da15 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_idp/src/main/java/at/gv/egiz/eaaf/modules/pvp2/idp/impl/builder/PVP2AssertionBuilder.java @@ -0,0 +1,448 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.idp.impl.builder; + +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.Assertion; +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.core.AttributeQuery; +import org.opensaml.saml2.core.AttributeStatement; +import org.opensaml.saml2.core.Audience; +import org.opensaml.saml2.core.AudienceRestriction; +import org.opensaml.saml2.core.AuthnContext; +import org.opensaml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.AuthnStatement; +import org.opensaml.saml2.core.Conditions; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.RequestedAuthnContext; +import org.opensaml.saml2.core.Subject; +import org.opensaml.saml2.core.SubjectConfirmation; +import org.opensaml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml2.core.impl.AuthnRequestImpl; +import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.AttributeConsumingService; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.NameIDFormat; +import org.opensaml.saml2.metadata.RequestedAttribute; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.Base64Utils; + +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.data.ILoALevelMapper; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.slo.SLOInformationInterface; +import at.gv.egiz.eaaf.core.exceptions.UnavailableAttributeException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; +import at.gv.egiz.eaaf.core.impl.utils.Random; +import at.gv.egiz.eaaf.modules.pvp2.PVPConstants; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; +import at.gv.egiz.eaaf.modules.pvp2.exception.QAANotSupportedException; +import at.gv.egiz.eaaf.modules.pvp2.idp.api.builder.ISubjectNameIdGenerator; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.ResponderErrorException; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.UnprovideableAttributeException; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.PVPSProfilePendingRequest; +import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PVPAttributeBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.QAALevelVerifier; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.SAML2Utils; + +@Service("PVP2AssertionBuilder") +public class PVP2AssertionBuilder implements PVPConstants { + + private static final Logger log = LoggerFactory.getLogger(PVP2AssertionBuilder.class); + @Autowired private ILoALevelMapper loaLevelMapper; + @Autowired private ISubjectNameIdGenerator subjectNameIdGenerator; + + + /** + * Build a PVP assertion as response for a SAML2 AttributeQuery request + * + * @param issuerEntityID EnitiyID, which should be used for this IDP response + * @param attrQuery AttributeQuery request from Service-Provider + * @param attrList List of PVP response attributes + * @param now Current time + * @param validTo ValidTo time of the assertion + * @param qaaLevel QAA level of the authentication + * @param sessionIndex SAML2 SessionIndex, which should be included * + * @return PVP 2.1 Assertion + * @throws PVP2Exception + */ + public Assertion buildAssertion(String issuerEntityID, AttributeQuery attrQuery, + List attrList, DateTime now, DateTime validTo, String qaaLevel, String sessionIndex) throws PVP2Exception { + + AuthnContextClassRef authnContextClassRef = SAML2Utils.createSAMLObject(AuthnContextClassRef.class); + authnContextClassRef.setAuthnContextClassRef(qaaLevel); + + NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); + subjectNameID.setFormat(attrQuery.getSubject().getNameID().getFormat()); + subjectNameID.setValue(attrQuery.getSubject().getNameID().getValue()); + + SubjectConfirmationData subjectConfirmationData = null; + + return buildGenericAssertion(issuerEntityID, attrQuery.getIssuer().getValue(), now, + authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex, + validTo); + } + + + /** + * Build a PVP 2.1 assertion as response of a SAML2 AuthnRequest + * + * @param issuerEntityID EnitiyID, which should be used for this IDP response + * @param pendingReq Current processed pendingRequest DAO + * @param authnRequest Current processed PVP AuthnRequest + * @param authData AuthenticationData of the user, which is already authenticated + * @param peerEntity SAML2 EntityDescriptor of the service-provider, which receives the response + * @param date TimeStamp + * @param assertionConsumerService SAML2 endpoint of the service-provider, which should be used + * @param sloInformation Single LogOut information DAO + * @return + * @throws PVP2Exception + */ + public Assertion buildAssertion(String issuerEntityID, PVPSProfilePendingRequest pendingReq, AuthnRequest authnRequest, + IAuthData authData, EntityDescriptor peerEntity, DateTime date, + AssertionConsumerService assertionConsumerService, SLOInformationInterface sloInformation) + throws PVP2Exception { + + RequestedAuthnContext reqAuthnContext = authnRequest + .getRequestedAuthnContext(); + + AuthnContextClassRef authnContextClassRef = SAML2Utils + .createSAMLObject(AuthnContextClassRef.class); + + ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration(); + + if (reqAuthnContext == null) { + authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel()); + + } else { + + boolean eIDAS_qaa_found = false; + + List reqAuthnContextClassRefIt = reqAuthnContext + .getAuthnContextClassRefs(); + + if (reqAuthnContextClassRefIt.size() == 0) { + QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(), EAAFConstants.EIDAS_QAA_HIGH); + + eIDAS_qaa_found = true; + authnContextClassRef.setAuthnContextClassRef(EAAFConstants.EIDAS_QAA_HIGH); + + } else { + for (AuthnContextClassRef authnClassRef : reqAuthnContextClassRefIt) { + String qaa_uri = authnClassRef.getAuthnContextClassRef(); + + if (!qaa_uri.trim().startsWith(EAAFConstants.EIDAS_QAA_PREFIX)) { + if (loaLevelMapper != null) { + log.debug("Find no eIDAS LoA. Start mapping process ... " ); + qaa_uri = loaLevelMapper.mapToeIDASLoA(qaa_uri.trim()); + + } else + log.debug("AuthnRequest contains no eIDAS LoA. NO LoA mapper FOUND, ignore " + + "'" + qaa_uri.trim() + "'"); + + } + + if (qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_HIGH) + || qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_SUBSTANTIAL) + || qaa_uri.trim().equals(EAAFConstants.EIDAS_QAA_LOW)) { + + if (authData.isForeigner()) { + QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(), oaParam.getMinimumLevelOfAssurence()); + + eIDAS_qaa_found = true; + authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel()); + + } else { + + QAALevelVerifier.verifyQAALevel(authData.getEIDASQAALevel(), + qaa_uri.trim()); + + eIDAS_qaa_found = true; + authnContextClassRef.setAuthnContextClassRef(authData.getEIDASQAALevel()); + + } + break; + } + } + } + + if (!eIDAS_qaa_found) + throw new QAANotSupportedException(EAAFConstants.EIDAS_QAA_HIGH); + + } + + + + SPSSODescriptor spSSODescriptor = peerEntity + .getSPSSODescriptor(SAMLConstants.SAML20P_NS); + + //add Attributes to Assertion + List attrList = new ArrayList(); + if (spSSODescriptor.getAttributeConsumingServices() != null && + spSSODescriptor.getAttributeConsumingServices().size() > 0) { + + Integer aIdx = authnRequest.getAttributeConsumingServiceIndex(); + int idx = 0; + + AttributeConsumingService attributeConsumingService = null; + if (aIdx != null) { + idx = aIdx.intValue(); + attributeConsumingService = spSSODescriptor + .getAttributeConsumingServices().get(idx); + + } else { + List attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices(); + for (AttributeConsumingService el : attrConsumingServiceList) { + if (el.isDefault()) + attributeConsumingService = el; + } + } + + /* + * TODO: maybe use first AttributeConsumingService if no is selected + * in request or on service is marked as default + * + */ + if (attributeConsumingService == null ) { + List attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices(); + if (attrConsumingServiceList != null && !attrConsumingServiceList.isEmpty()) + attributeConsumingService = attrConsumingServiceList.get(0); + + } + + + if (attributeConsumingService != null) { + Iterator it = attributeConsumingService + .getRequestAttributes().iterator(); + while (it.hasNext()) { + RequestedAttribute reqAttribut = it.next(); + try { + Attribute attr = PVPAttributeBuilder.buildAttribute( + reqAttribut.getName(), oaParam, authData); + if (attr == null) { + if (reqAttribut.isRequired()) { + throw new UnprovideableAttributeException( + reqAttribut.getName()); + } + } else { + attrList.add(attr); + } + + } catch (UnavailableAttributeException e) { + log.info( + "Attribute generation for " + + reqAttribut.getFriendlyName() + " not possible."); + if (reqAttribut.isRequired()) { + throw new UnprovideableAttributeException( + reqAttribut.getName()); + } + + + } catch (PVP2Exception e) { + log.info( + "Attribute generation failed! for " + + reqAttribut.getFriendlyName()); + if (reqAttribut.isRequired()) { + throw new UnprovideableAttributeException( + reqAttribut.getName()); + } + + } catch (Exception e) { + log.warn( + "General Attribute generation failed! for " + + reqAttribut.getFriendlyName(), e); + if (reqAttribut.isRequired()) { + throw new UnprovideableAttributeException( + reqAttribut.getName()); + } + + } + } + } + } + + //generate subjectNameId + NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); + Pair subjectNameIdPair = subjectNameIdGenerator.generateSubjectNameId(authData, oaParam); + subjectNameID.setValue(subjectNameIdPair.getFirst()); + subjectNameID.setNameQualifier(subjectNameIdPair.getSecond()); + + //get NameIDFormat from request + String nameIDFormat = NameID.TRANSIENT; + AuthnRequest authnReq = (AuthnRequestImpl) authnRequest; + if (authnReq.getNameIDPolicy() != null && + StringUtils.isNotEmpty(authnReq.getNameIDPolicy().getFormat())) { + nameIDFormat = authnReq.getNameIDPolicy().getFormat(); + + } else { + //get NameIDFormat from metadata + List metadataNameIDFormats = spSSODescriptor.getNameIDFormats(); + + if (metadataNameIDFormats != null) { + + for (NameIDFormat el : metadataNameIDFormats) { + if (NameID.PERSISTENT.equals(el.getFormat())) { + nameIDFormat = NameID.PERSISTENT; + break; + + } else if (NameID.TRANSIENT.equals(el.getFormat()) || + NameID.UNSPECIFIED.equals(el.getFormat())) + break; + + } + } + } + + if (NameID.TRANSIENT.equals(nameIDFormat) || NameID.UNSPECIFIED.equals(nameIDFormat)) { + String random = Random.nextHexRandom32(); + String nameID = subjectNameID.getValue(); + + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] hash = md.digest((nameID + random).getBytes("ISO-8859-1")); + subjectNameID.setValue(Base64Utils.encodeToString(hash)); + subjectNameID.setNameQualifier(null); + subjectNameID.setFormat(NameID.TRANSIENT); + + } catch (Exception e) { + log.warn("PVP2 subjectNameID error", e); + throw new ResponderErrorException("pvp2.13", null, e); + } + + } else + subjectNameID.setFormat(nameIDFormat); + + + String sessionIndex = null; + + //if request is a reauthentication and NameIDFormat match reuse old session information + if (StringUtils.isNotEmpty(authData.getNameID()) && + StringUtils.isNotEmpty(authData.getNameIDFormat()) && + nameIDFormat.equals(authData.getNameIDFormat())) { + subjectNameID.setValue(authData.getNameID()); + sessionIndex = authData.getSessionIndex(); + + } + + // + if (StringUtils.isEmpty(sessionIndex)) + sessionIndex = SAML2Utils.getSecureIdentifier(); + + SubjectConfirmationData subjectConfirmationData = SAML2Utils + .createSAMLObject(SubjectConfirmationData.class); + subjectConfirmationData.setInResponseTo(authnRequest.getID()); + subjectConfirmationData.setNotOnOrAfter(new DateTime(authData.getSsoSessionValidTo().getTime())); +// subjectConfirmationData.setNotBefore(date); + + //set 'recipient' attribute in subjectConformationData + subjectConfirmationData.setRecipient(assertionConsumerService.getLocation()); + + //set IP address of the user machine as 'Address' attribute in subjectConformationData + String usersIPAddress = pendingReq.getGenericData( + RequestImpl.DATAID_REQUESTER_IP_ADDRESS, String.class); + if (StringUtils.isNotEmpty(usersIPAddress)) + subjectConfirmationData.setAddress(usersIPAddress); + + //set SLO information + sloInformation.setUserNameIdentifier(subjectNameID.getValue()); + sloInformation.setNameIDFormat(subjectNameID.getFormat()); + sloInformation.setSessionIndex(sessionIndex); + + return buildGenericAssertion(issuerEntityID, peerEntity.getEntityID(), date, authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex, subjectConfirmationData.getNotOnOrAfter()); + } + + /** + * + * @param issuer IDP EntityID + * @param entityID Service Provider EntityID + * @param date + * @param authnContextClassRef + * @param attrList + * @param subjectNameID + * @param subjectConfirmationData + * @param sessionIndex + * @param isValidTo + * @return + * @throws ConfigurationException + */ + + public Assertion buildGenericAssertion(String issuer, String entityID, DateTime date, + AuthnContextClassRef authnContextClassRef, List attrList, + NameID subjectNameID, SubjectConfirmationData subjectConfirmationData, + String sessionIndex, DateTime isValidTo) throws ResponderErrorException { + Assertion assertion = SAML2Utils.createSAMLObject(Assertion.class); + + AuthnContext authnContext = SAML2Utils + .createSAMLObject(AuthnContext.class); + authnContext.setAuthnContextClassRef(authnContextClassRef); + + AuthnStatement authnStatement = SAML2Utils + .createSAMLObject(AuthnStatement.class); + + authnStatement.setAuthnInstant(date); + authnStatement.setSessionIndex(sessionIndex); + authnStatement.setAuthnContext(authnContext); + + assertion.getAuthnStatements().add(authnStatement); + + AttributeStatement attributeStatement = SAML2Utils + .createSAMLObject(AttributeStatement.class); + attributeStatement.getAttributes().addAll(attrList); + if (attributeStatement.getAttributes().size() > 0) { + assertion.getAttributeStatements().add(attributeStatement); + } + + Subject subject = SAML2Utils.createSAMLObject(Subject.class); + subject.setNameID(subjectNameID); + + SubjectConfirmation subjectConfirmation = SAML2Utils + .createSAMLObject(SubjectConfirmation.class); + subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER); + subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); + + subject.getSubjectConfirmations().add(subjectConfirmation); + + Conditions conditions = SAML2Utils.createSAMLObject(Conditions.class); + AudienceRestriction audienceRestriction = SAML2Utils + .createSAMLObject(AudienceRestriction.class); + Audience audience = SAML2Utils.createSAMLObject(Audience.class); + + audience.setAudienceURI(entityID); + audienceRestriction.getAudiences().add(audience); + conditions.setNotBefore(date); + conditions.setNotOnOrAfter(isValidTo); + + conditions.getAudienceRestrictions().add(audienceRestriction); + + assertion.setConditions(conditions); + + Issuer issuerObj = SAML2Utils.createSAMLObject(Issuer.class); + + if (issuer.endsWith("/")) + issuer = issuer.substring(0, issuer.length()-1); + issuerObj.setValue(issuer); + issuerObj.setFormat(NameID.ENTITY); + + assertion.setIssuer(issuerObj); + assertion.setSubject(subject); + assertion.setID(SAML2Utils.getSecureIdentifier()); + assertion.setIssueInstant(date); + + return assertion; + } +} -- cgit v1.2.3