/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egovernment.moa.id.protocols.pvp2x; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; import org.joda.time.DateTime; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.AuthnRequest; import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; 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.SecurityException; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.SignableXMLObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; import at.gv.egovernment.moa.id.auth.exception.InvalidProtocolRequestException; import at.gv.egovernment.moa.id.auth.exception.ProtocolNotActiveException; import at.gv.egovernment.moa.id.auth.exception.WrongParametersException; import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityLogAdapter; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.data.IAuthenticationSession; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.id.moduls.NoPassivAuthenticationException; import at.gv.egovernment.moa.id.protocols.AbstractAuthProtocolModulController; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.MOAURICompare; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.PostBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.RedirectBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.SoapBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AssertionValidationExeption; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AttributQueryException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.InvalidAssertionConsumerServiceException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.MandateAttributesNotHandleAbleException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NameIDFormatNotSupportedException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NoMetadataInformationException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.PVP2Exception; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.SLOException; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.InboundMessage; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOAResponse; import at.gv.egovernment.moa.id.protocols.pvp2x.metadata.MOAMetadataProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.signer.IDPCredentialProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.CheckMandateAttributes; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; import at.gv.egovernment.moa.id.protocols.pvp2x.validation.AuthnRequestValidator; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.SAMLVerificationEngineSP; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.TrustEngineFactory; import at.gv.egovernment.moa.id.util.ErrorResponseUtils; import at.gv.egovernment.moa.id.util.HTTPUtils; import at.gv.egovernment.moa.id.util.ParamValidatorUtils; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; @Controller public class PVP2XProtocol extends AbstractAuthProtocolModulController { @Autowired IDPCredentialProvider pvpCredentials; @Autowired SAMLVerificationEngineSP samlVerificationEngine; @Autowired(required=true) private MOAMetadataProvider metadataProvider; public static final String NAME = PVP2XProtocol.class.getName(); public static final String PATH = "id_pvp2x"; public static final String REDIRECT = "Redirect"; public static final String POST = "Post"; public static final String SOAP = "Soap"; public static final String METADATA = "Metadata"; public static final String ATTRIBUTEQUERY = "AttributeQuery"; public static final String SINGLELOGOUT = "SingleLogOut"; public static final List DEFAULTREQUESTEDATTRFORINTERFEDERATION = Arrays.asList( new String[] { PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME }); static { new VelocityLogAdapter(); } public String getName() { return NAME; } public String getPath() { return PATH; } public PVP2XProtocol() { super(); } //PVP2.x metadata end-point @RequestMapping(value = "/pvp2/metadata", method = {RequestMethod.POST, RequestMethod.GET}) public void PVPMetadataRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException { if (!authConfig.getAllowedProtocols().isPVP21Active()) { Logger.info("PVP2.1 is deaktivated!"); throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }); } //create pendingRequest object PVPTargetConfiguration pendingReq = applicationContext.getBean(PVPTargetConfiguration.class); pendingReq.initialize(req); pendingReq.setModule(NAME); revisionsLogger.logEvent( pendingReq.getUniqueSessionIdentifier(), pendingReq.getUniqueTransactionIdentifier(), MOAIDEventConstants.TRANSACTION_IP, req.getRemoteAddr()); MetadataAction metadataAction = applicationContext.getBean(MetadataAction.class); metadataAction.processRequest(pendingReq, req, resp, null); } //PVP2.x IDP POST-Binding end-point @RequestMapping(value = "/pvp2/post", method = {RequestMethod.POST}) public void PVPIDPPostRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException { if (!authConfig.getAllowedProtocols().isPVP21Active()) { Logger.info("PVP2.1 is deaktivated!"); throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }); } try { //create pendingRequest object PVPTargetConfiguration pendingReq = applicationContext.getBean(PVPTargetConfiguration.class); pendingReq.initialize(req); pendingReq.setModule(NAME); revisionsLogger.logEvent(MOAIDEventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier()); revisionsLogger.logEvent(MOAIDEventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier()); revisionsLogger.logEvent( pendingReq.getUniqueSessionIdentifier(), pendingReq.getUniqueTransactionIdentifier(), MOAIDEventConstants.TRANSACTION_IP, req.getRemoteAddr()); //get POST-Binding decoder implementation InboundMessage msg = (InboundMessage) new PostBinding().decode( req, resp, metadataProvider, false, new MOAURICompare(PVPConfiguration.getInstance().getIDPSSOPostService(pendingReq.getAuthURL()))); pendingReq.setRequest(msg); //preProcess Message preProcess(req, resp, pendingReq); } catch (SecurityPolicyException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } catch (SecurityException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}); } catch (MOAIDException e) { throw e; } catch (Throwable e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new MOAIDException("pvp2.24", new Object[] {e.getMessage()}); } } //PVP2.x IDP Redirect-Binding end-point @RequestMapping(value = "/pvp2/redirect", method = {RequestMethod.GET}) public void PVPIDPRedirecttRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException { if (!AuthConfigurationProviderFactory.getInstance().getAllowedProtocols().isPVP21Active()) { Logger.info("PVP2.1 is deaktivated!"); throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }); } try { //create pendingRequest object PVPTargetConfiguration pendingReq = applicationContext.getBean(PVPTargetConfiguration.class); pendingReq.initialize(req); pendingReq.setModule(NAME); revisionsLogger.logEvent(MOAIDEventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier()); revisionsLogger.logEvent(MOAIDEventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier()); revisionsLogger.logEvent( pendingReq.getUniqueSessionIdentifier(), pendingReq.getUniqueTransactionIdentifier(), MOAIDEventConstants.TRANSACTION_IP, req.getRemoteAddr()); //get POST-Binding decoder implementation InboundMessage msg = (InboundMessage) new RedirectBinding().decode( req, resp, metadataProvider, false, new MOAURICompare(PVPConfiguration.getInstance().getIDPSSORedirectService(pendingReq.getAuthURL()))); pendingReq.setRequest(msg); //preProcess Message preProcess(req, resp, pendingReq); } catch (SecurityPolicyException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } catch (SecurityException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}); } catch (MOAIDException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.info("Receive INVALID protocol request: " + samlRequest); throw e; } catch (Throwable e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new MOAIDException("pvp2.24", new Object[] {e.getMessage()}); } } //PVP2.x IDP SOAP-Binding end-point @RequestMapping(value = "/pvp2/soap", method = {RequestMethod.POST}) public void PVPIDPSOAPRequest(HttpServletRequest req, HttpServletResponse resp) throws MOAIDException { if (!authConfig.getAllowedProtocols().isPVP21Active()) { Logger.info("PVP2.1 is deaktivated!"); throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }); } try { //create pendingRequest object PVPTargetConfiguration pendingReq = applicationContext.getBean(PVPTargetConfiguration.class); pendingReq.initialize(req); pendingReq.setModule(NAME); revisionsLogger.logEvent(MOAIDEventConstants.SESSION_CREATED, pendingReq.getUniqueSessionIdentifier()); revisionsLogger.logEvent(MOAIDEventConstants.TRANSACTION_CREATED, pendingReq.getUniqueTransactionIdentifier()); revisionsLogger.logEvent( pendingReq.getUniqueSessionIdentifier(), pendingReq.getUniqueTransactionIdentifier(), MOAIDEventConstants.TRANSACTION_IP, req.getRemoteAddr()); //get POST-Binding decoder implementation InboundMessage msg = (InboundMessage) new SoapBinding().decode( req, resp, metadataProvider, false, new MOAURICompare(PVPConfiguration.getInstance().getIDPSSOPostService(pendingReq.getAuthURL()))); pendingReq.setRequest(msg); //preProcess Message preProcess(req, resp, pendingReq); } catch (SecurityPolicyException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}); } catch (SecurityException e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}); } catch (MOAIDException e) { throw e; } catch (Throwable e) { String samlRequest = req.getParameter("SAMLRequest"); Logger.warn("Receive INVALID protocol request: " + samlRequest, e); throw new MOAIDException("pvp2.24", new Object[] {e.getMessage()}); } } private void preProcess(HttpServletRequest request, HttpServletResponse response, PVPTargetConfiguration pendingReq) throws Throwable { InboundMessage msg = pendingReq.getRequest(); if (MiscUtil.isEmpty(msg.getEntityID())) { throw new InvalidProtocolRequestException("pvp2.20", new Object[] {}); } if(!msg.isVerified()) { samlVerificationEngine.verify(msg, TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider)); msg.setVerified(true); } if (msg instanceof MOARequest && ((MOARequest)msg).getSamlRequest() instanceof AuthnRequest) preProcessAuthRequest(request, response, pendingReq); else if (msg instanceof MOARequest && ((MOARequest)msg).getSamlRequest() instanceof AttributeQuery) preProcessAttributQueryRequest(request, response, pendingReq); else if (msg instanceof MOARequest && ((MOARequest)msg).getSamlRequest() instanceof LogoutRequest) preProcessLogOut(request, response, pendingReq); else if (msg instanceof MOAResponse && ((MOAResponse)msg).getResponse() instanceof LogoutResponse) preProcessLogOut(request, response, pendingReq); else { Logger.error("Receive unsupported PVP21 message"); throw new MOAIDException("Unsupported PVP21 message", new Object[] {}); } revisionsLogger.logEvent(pendingReq.getOnlineApplicationConfiguration(), pendingReq, MOAIDEventConstants.AUTHPROTOCOL_TYPE, PATH); //switch to session authentication performAuthentication(request, response, pendingReq); } public boolean generateErrorMessage(Throwable e, HttpServletRequest request, HttpServletResponse response, IRequest protocolRequest) throws Throwable { if(protocolRequest == null) { throw e; } if(!(protocolRequest instanceof PVPTargetConfiguration) ) { throw e; } PVPTargetConfiguration pvpRequest = (PVPTargetConfiguration)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); ErrorResponseUtils errorUtils = ErrorResponseUtils.getInstance(); 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 = errorUtils.mapInternalErrorToExternalError(ex.getMessageId()); } else { statusCode.setValue(StatusCode.RESPONDER_URI); statusMessage.setMessage(StringEscapeUtils.escapeXml(e.getLocalizedMessage())); moaError = errorUtils.getResponseErrorCode(e); } if (MiscUtil.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(PVPConfiguration.getInstance().getIDPSSOMetadataService( 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 = pvpCredentials.getIDPAssertionSigningCredential(); encoder.encodeRespone(request, response, samlResponse, pvpRequest.getConsumerURL(), relayState, signCred, protocolRequest); return true; } public boolean validate(HttpServletRequest request, HttpServletResponse response, IRequest pending) { return true; } /** * PreProcess Single LogOut request * @param request * @param response * @param msg * @return * @throws MOAIDException */ private void preProcessLogOut(HttpServletRequest request, HttpServletResponse response, PVPTargetConfiguration pendingReq) throws MOAIDException { InboundMessage inMsg = pendingReq.getRequest(); MOARequest msg; if (inMsg instanceof MOARequest && ((MOARequest)inMsg).getSamlRequest() instanceof LogoutRequest) { //preProcess single logout request from service provider msg = (MOARequest) inMsg; EntityDescriptor metadata = msg.getEntityMetadata(metadataProvider); if(metadata == null) { throw new NoMetadataInformationException(); } String oaURL = metadata.getEntityID(); oaURL = StringEscapeUtils.escapeHtml(oaURL); IOAAuthParameters oa = authConfig.getOnlineApplicationParameter(oaURL); Logger.info("Dispatch PVP2 SingleLogOut: OAURL=" + oaURL + " Binding=" + msg.getRequestBinding()); pendingReq.setOAURL(oaURL); pendingReq.setOnlineApplicationConfiguration(oa); pendingReq.setBinding(msg.getRequestBinding()); revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROTOCOL_PVP_REQUEST_SLO); } else if (inMsg instanceof MOAResponse && ((MOAResponse)inMsg).getResponse() instanceof LogoutResponse) { //preProcess single logour response from service provider LogoutResponse resp = (LogoutResponse) (((MOAResponse)inMsg).getResponse()); Logger.debug("PreProcess SLO Response from " + resp.getIssuer()); List allowedPublicURLPrefix = AuthConfigurationProviderFactory.getInstance().getPublicURLPrefix(); boolean isAllowedDestination = false; for (String prefix : allowedPublicURLPrefix) { if (resp.getDestination().startsWith( prefix)) { isAllowedDestination = true; break; } } if (!isAllowedDestination) { Logger.warn("PVP 2.1 single logout response destination does not match to IDP URL"); throw new AssertionValidationExeption("PVP 2.1 single logout response destination does not match to IDP URL", null); } //TODO: check if relayState exists inMsg.getRelayState(); } else throw new MOAIDException("Unsupported request", new Object[] {}); pendingReq.setRequest(inMsg); pendingReq.setAction(SINGLELOGOUT); //Single LogOut Request needs no authentication pendingReq.setNeedAuthentication(false); //set protocol action, which should be executed pendingReq.setAction(SingleLogOutAction.class.getName()); } /** * PreProcess AttributeQuery request * @param request * @param response * @param pendingReq * @throws Throwable */ private void preProcessAttributQueryRequest(HttpServletRequest request, HttpServletResponse response, PVPTargetConfiguration pendingReq) throws Throwable { MOARequest moaRequest = ((MOARequest)pendingReq.getRequest()); AttributeQuery attrQuery = (AttributeQuery) moaRequest.getSamlRequest(); moaRequest.setEntityID(attrQuery.getIssuer().getValue()); //validate destination String destinaten = attrQuery.getDestination(); if (!PVPConfiguration.getInstance().getIDPAttributeQueryService(HTTPUtils.extractAuthURLFromRequest(request)).equals(destinaten)) { Logger.warn("AttributeQuery destination does not match IDP AttributeQueryService URL"); throw new AttributQueryException("AttributeQuery destination does not match IDP AttributeQueryService URL", null); } //check if Issuer is an interfederation IDP // check parameter if (!ParamValidatorUtils.isValidOA(moaRequest.getEntityID())) throw new WrongParametersException("StartAuthentication", PARAM_OA, "auth.12"); IOAAuthParameters oa = authConfig.getOnlineApplicationParameter(moaRequest.getEntityID()); if (!oa.isInderfederationIDP()) { Logger.warn("AttributeQuery requests are only allowed for interfederation IDPs."); throw new AttributQueryException("AttributeQuery requests are only allowed for interfederation IDPs.", null); } if (!oa.isOutboundSSOInterfederationAllowed()) { Logger.warn("Interfederation IDP " + oa.getPublicURLPrefix() + " does not allow outgoing SSO interfederation."); throw new AttributQueryException("Interfederation IDP does not allow outgoing SSO interfederation.", null); } //check active MOASession String nameID = attrQuery.getSubject().getNameID().getValue(); IAuthenticationSession session = authenticatedSessionStorage.getSessionWithUserNameID(nameID); if (session == null) { Logger.warn("AttributeQuery nameID does not match to an active single sign-on session."); throw new AttributQueryException("auth.31", null); } //set preProcessed information into pending-request pendingReq.setRequest(moaRequest); pendingReq.setOAURL(moaRequest.getEntityID()); pendingReq.setOnlineApplicationConfiguration(oa); pendingReq.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI); //Attribute-Query Request needs authentication, because session MUST be already authenticated pendingReq.setNeedAuthentication(false); //set protocol action, which should be executed after authentication pendingReq.setAction(AttributQueryAction.class.getName()); //add moasession pendingReq.setInternalSSOSessionIdentifier(session.getSessionID()); //write revisionslog entry revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROTOCOL_PVP_REQUEST_ATTRIBUTQUERY); } /** * PreProcess Authn request * @param request * @param response * @param pendingReq * @throws Throwable */ private void preProcessAuthRequest(HttpServletRequest request, HttpServletResponse response, PVPTargetConfiguration pendingReq) throws Throwable { MOARequest moaRequest = ((MOARequest)pendingReq.getRequest()); SignableXMLObject samlReq = moaRequest.getSamlRequest(); if(!(samlReq instanceof AuthnRequest)) { throw new MOAIDException("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) { Logger.warn("Unsupported request: No IssueInstant Attribute found."); throw new AuthnRequestValidatorException("Unsupported request: No IssueInstant Attribute found.", new Object[] {}); } if (authnRequest.getIssueInstant().minusMinutes(MOAIDAuthConstants.TIME_JITTER).isAfterNow()) { Logger.warn("Unsupported request: No IssueInstant DateTime is not valid anymore."); throw new AuthnRequestValidatorException("Unsupported request: No IssueInstant DateTime is not valid anymore.", new Object[] {}); } //parse AssertionConsumerService AssertionConsumerService consumerService = null; if (MiscUtil.isNotEmpty(authnRequest.getAssertionConsumerServiceURL()) && MiscUtil.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()); Logger.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); IOAAuthParameters oa = authConfig.getOnlineApplicationParameter(oaURL); Logger.info("Dispatch PVP2 AuthnRequest: OAURL=" + oaURL + " Binding=" + consumerService.getBinding()); pendingReq.setOAURL(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, MOAIDEventConstants.AUTHPROTOCOL_PVP_REQUEST_AUTHREQUEST); } }