diff options
Diffstat (limited to 'id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java')
-rw-r--r-- | id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java | 424 |
1 files changed, 339 insertions, 85 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java index e81b23d41..6527f03b5 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java @@ -24,6 +24,7 @@ package at.gv.egovernment.moa.id.protocols.pvp2x; import iaik.pkcs.pkcs11.objects.Object; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -31,11 +32,17 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.xml.transform.TransformerException; 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.RequestAbstractType; +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; @@ -45,30 +52,49 @@ 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.saml2.metadata.SingleLogoutService; +import org.opensaml.xml.io.MarshallingException; +import org.opensaml.xml.signature.SignableXMLObject; + +import edu.emory.mathcs.backport.java.util.Arrays; import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; +import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.auth.exception.ProtocolNotActiveException; +import at.gv.egovernment.moa.id.auth.exception.WrongParametersException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; +import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.moduls.IModulInfo; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.moduls.NoPassivAuthenticationException; +import at.gv.egovernment.moa.id.moduls.RequestImpl; +import at.gv.egovernment.moa.id.moduls.RequestStorage; +import at.gv.egovernment.moa.id.moduls.SSOManager; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IDecoder; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder; -import at.gv.egovernment.moa.id.protocols.pvp2x.binding.MOARequest; +import at.gv.egovernment.moa.id.protocols.pvp2x.binding.SoapBinding; +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.binding.PostBinding; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.RedirectBinding; -import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AuthnRequestValidatorException; +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.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.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.SAMLVerificationEngine; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.TrustEngineFactory; +import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; +import at.gv.egovernment.moa.id.util.ParamValidatorUtils; import at.gv.egovernment.moa.id.util.VelocityLogAdapter; import at.gv.egovernment.moa.logging.Logger; @@ -81,18 +107,29 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { 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"; private static List<IDecoder> decoder = new ArrayList<IDecoder>(); private static HashMap<String, IAction> actions = new HashMap<String, IAction>(); + @SuppressWarnings("unchecked") + public static final List<String> DEFAULTREQUESTEDATTRFORINTERFEDERATION = Arrays.asList( + new String[] { + PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME + }); + static { decoder.add(new PostBinding()); decoder.add(new RedirectBinding()); + decoder.add(new SoapBinding()); actions.put(REDIRECT, new AuthenticationAction()); actions.put(POST, new AuthenticationAction()); actions.put(METADATA, new MetadataAction()); + actions.put(ATTRIBUTEQUERY, new AttributQueryAction()); + actions.put(SINGLELOGOUT, new SingleLogOutAction()); //TODO: insert getArtifact action @@ -133,7 +170,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { public PVP2XProtocol() { super(); } - + public IRequest preProcess(HttpServletRequest request, HttpServletResponse response, String action) throws MOAIDException { @@ -147,6 +184,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { if(METADATA.equals(action)) { return new PVPTargetConfiguration(); + } IDecoder decoder = findDecoder(action, request); @@ -154,100 +192,67 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { return null; } try { - PVPTargetConfiguration config = new PVPTargetConfiguration(); - - MOARequest moaRequest = decoder.decodeRequest(request, response); - - RequestAbstractType samlReq = moaRequest.getSamlRequest(); - - //String xml = PrettyPrinter.prettyPrint(SAML2Utils.asDOMDocument(samlReq)); - //Logger.info("SAML : " + xml); + InboundMessage msg = (InboundMessage) decoder.decode(request, response); - if(!moaRequest.isVerified()) { + if(!msg.isVerified()) { SAMLVerificationEngine engine = new SAMLVerificationEngine(); - engine.verifyRequest(samlReq, TrustEngineFactory.getSignatureKnownKeysTrustEngine()); - moaRequest.setVerified(true); - } - - if(!(samlReq instanceof AuthnRequest)) { - throw new MOAIDException("Unsupported request", new Object[] {}); - } - - EntityDescriptor metadata = moaRequest.getEntityMetadata(); - if(metadata == null) { - throw new NoMetadataInformationException(); - } - SPSSODescriptor spSSODescriptor = metadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS); - - AuthnRequest authnRequest = (AuthnRequest)samlReq; - - Integer aIdx = authnRequest.getAssertionConsumerServiceIndex(); - int assertionidx = 0; - - if(aIdx != null) { - assertionidx = aIdx.intValue(); + engine.verify(msg, TrustEngineFactory.getSignatureKnownKeysTrustEngine()); + msg.setVerified(true); - } else { - assertionidx = SAML2Utils.getDefaultAssertionConsumerServiceIndex(spSSODescriptor); } - aIdx = authnRequest.getAttributeConsumingServiceIndex(); - int attributeIdx = 0; - - if(aIdx != null) { - attributeIdx = aIdx.intValue(); - } + if (msg instanceof MOARequest && + ((MOARequest)msg).getSamlRequest() instanceof AuthnRequest) + return preProcessAuthRequest(request, response, (MOARequest) msg); - AssertionConsumerService consumerService = spSSODescriptor.getAssertionConsumerServices().get(assertionidx); + else if (msg instanceof MOARequest && + ((MOARequest)msg).getSamlRequest() instanceof AttributeQuery) + return preProcessAttributQueryRequest(request, response, (MOARequest) msg); + + else if (msg instanceof MOARequest && + ((MOARequest)msg).getSamlRequest() instanceof LogoutRequest) + return preProcessLogOut(request, response, (MOARequest) msg); - AttributeConsumingService attributeConsumer = null; + else if (msg instanceof MOARequest && + ((MOARequest)msg).getSamlRequest() instanceof LogoutResponse) + return preProcessLogOut(request, response, (MOARequest) msg); - if (spSSODescriptor.getAttributeConsumingServices() != null && - spSSODescriptor.getAttributeConsumingServices().size() > 0) { - attributeConsumer = spSSODescriptor.getAttributeConsumingServices().get(attributeIdx); - } + else if (msg instanceof MOAResponse) { + //load service provider AuthRequest from session + + IRequest obj = RequestStorage.getPendingRequest(msg.getRelayState()); + if (obj instanceof RequestImpl) { + RequestImpl iReqSP = (RequestImpl) obj; + + MOAResponse processedMsg = preProcessAuthResponse((MOAResponse) msg); + + if ( processedMsg != null ) { + iReqSP.setInterfederationResponse(processedMsg); + + } else { + Logger.info("Receive NO valid SSO session from " + msg.getEntityID() + +". Switch to local authentication process ..."); - String oaURL = moaRequest.getEntityMetadata().getEntityID(); - String binding = consumerService.getBinding(); -// String entityID = moaRequest.getEntityMetadata().getEntityID(); - - Logger.info("Dispatch PVP2 Request: OAURL=" + oaURL + " Binding=" + binding); - - oaURL = StringEscapeUtils.escapeHtml(oaURL); - - config.setOAURL(oaURL); - config.setBinding(binding); - config.setRequest(moaRequest); - config.setConsumerURL(consumerService.getLocation()); + SSOManager ssomanager = SSOManager.getInstance(); + ssomanager.removeInterfederatedSSOIDP(msg.getEntityID(), request); + + iReqSP.setRequestedIDP(null); - String useMandate = request.getParameter(PARAM_USEMANDATE); - if(useMandate != null) { - if(useMandate.equals("true") && attributeConsumer != null) { - if(!CheckMandateAttributes.canHandleMandate(attributeConsumer)) { - throw new MandateAttributesNotHandleAbleException(); } + + return iReqSP; + } - } - //validate AuthnRequest - try { - AuthnRequestValidator.validate((AuthnRequestImpl) samlReq); - - } catch (AuthnRequestValidatorException e) { - if (generateErrorMessage(e, request, response, config)) { - throw new AuthnRequestValidatorException(e.getMessage(), - new Object[] {}, config); - - } else { - throw new MOAIDException(e.getMessage(), new Object[] {}); - - } + Logger.error("Stored PVP21 authrequest from service provider has an unsuppored type."); + return null; + + } else { + Logger.error("Receive unsupported PVP21 message"); + throw new MOAIDException("Unsupported PVP21 message", new Object[] {}); } - - //request.getSession().setAttribute(PARAM_OA, oaURL); - return config; } catch (PVP2Exception e) { throw e; @@ -285,6 +290,10 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { } 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; @@ -293,6 +302,7 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { if(statusMessageValue != null) { statusMessage.setMessage(StringEscapeUtils.escapeXml(statusMessageValue)); } + } else { statusCode.setValue(StatusCode.RESPONDER_URI); @@ -306,16 +316,27 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { samlResponse.setStatus(status); String remoteSessionID = SAML2Utils.getSecureIdentifier(); samlResponse.setID(remoteSessionID); - + + samlResponse.setIssueInstant(new DateTime()); + Issuer nissuer = SAML2Utils.createSAMLObject(Issuer.class); + nissuer.setValue(PVPConfiguration.getInstance().getIDPPublicPath()); + nissuer.setFormat(NameID.ENTITY); + samlResponse.setIssuer(nissuer); + IEncoder encoder = null; - if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { + if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { encoder = new RedirectBinding(); - } else if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_ARTIFACT_BINDING_URI)) { + + } else if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_ARTIFACT_BINDING_URI)) { // TODO: not supported YET!! //binding = new ArtifactBinding(); + } else if(pvpRequest.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) { encoder = new PostBinding(); + + } else if (pvpRequest.getBinding().equals(SAMLConstants.SAML2_SOAP11_BINDING_URI)) { + encoder = new SoapBinding(); } if(encoder == null) { @@ -340,8 +361,10 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { HttpServletResponse response) { if(request.getParameter("SAMLRequest") != null && request.getMethod().equals("GET")) { return getAction(REDIRECT); + } else if(request.getParameter("SAMLRequest") != null && request.getMethod().equals("POST")) { return getAction(POST); + } if(METADATA.equals(request.getParameter("action"))) { @@ -356,4 +379,235 @@ public class PVP2XProtocol implements IModulInfo, MOAIDAuthConstants { return true; } + + /** + * PreProcess Single LogOut request + * @param request + * @param response + * @param msg + * @return + * @throws MOAIDException + */ + private IRequest preProcessLogOut(HttpServletRequest request, + HttpServletResponse response, MOARequest msg) throws MOAIDException { + + PVPTargetConfiguration config = new PVPTargetConfiguration(); + + if (((MOARequest)msg).getSamlRequest() instanceof LogoutRequest) { + //preProcess single logout request from service provider + + EntityDescriptor metadata = msg.getEntityMetadata(); + if(metadata == null) { + throw new NoMetadataInformationException(); + } + + + + String oaURL = metadata.getEntityID(); + oaURL = StringEscapeUtils.escapeHtml(oaURL); + + Logger.info("Dispatch PVP2 SingleLogOut: OAURL=" + oaURL + " Binding=" + msg.getRequestBinding()); + + config.setOAURL(oaURL); + config.setBinding(msg.getRequestBinding()); + + + } else if (((MOARequest)msg).getSamlRequest() instanceof LogoutResponse) { + //preProcess single logour response from service provider + + LogoutResponse resp = (LogoutResponse) (((MOARequest)msg).getSamlRequest()); + + Logger.debug("PreProcess SLO Response from " + resp.getIssuer()); + + if (!resp.getDestination().startsWith( + PVPConfiguration.getInstance().getIDPPublicPath())) { + 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 + msg.getRelayState(); + + + } else + throw new MOAIDException("Unsupported request", new Object[] {}); + + + config.setRequest(msg); + config.setAction(SINGLELOGOUT); + return config; + } + + /** + * PreProcess AttributeQuery request + * @param request + * @param response + * @param moaRequest + * @return + * @throws Throwable + */ + private IRequest preProcessAttributQueryRequest(HttpServletRequest request, + HttpServletResponse response, MOARequest moaRequest) throws Throwable { + + AttributeQuery attrQuery = (AttributeQuery) moaRequest.getSamlRequest(); + moaRequest.setEntityID(attrQuery.getIssuer().getValue()); + + //validate destination + String destinaten = attrQuery.getDestination(); + if (!PVPConfiguration.getInstance().getIDPAttributeQueryService().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"); + + OAAuthParameter oa = AuthConfigurationProvider.getInstance().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); + + } + + PVPTargetConfiguration config = new PVPTargetConfiguration(); + config.setRequest(moaRequest); + config.setOAURL(moaRequest.getEntityID()); + config.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI); + + return config; + } + + /** + * PreProcess Authn request + * @param request + * @param response + * @param moaRequest + * @return + * @throws Throwable + */ + private IRequest preProcessAuthRequest(HttpServletRequest request, + HttpServletResponse response, MOARequest moaRequest) throws Throwable { + + SignableXMLObject samlReq = moaRequest.getSamlRequest(); + + if(!(samlReq instanceof AuthnRequest)) { + throw new MOAIDException("Unsupported request", new Object[] {}); + } + + EntityDescriptor metadata = moaRequest.getEntityMetadata(); + if(metadata == null) { + throw new NoMetadataInformationException(); + } + SPSSODescriptor spSSODescriptor = metadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS); + + AuthnRequest authnRequest = (AuthnRequest)samlReq; + + Integer aIdx = authnRequest.getAssertionConsumerServiceIndex(); + int assertionidx = 0; + + if(aIdx != null) { + assertionidx = aIdx.intValue(); + + } else { + assertionidx = SAML2Utils.getDefaultAssertionConsumerServiceIndex(spSSODescriptor); + } + + aIdx = authnRequest.getAttributeConsumingServiceIndex(); + int attributeIdx = 0; + + if(aIdx != null) { + attributeIdx = aIdx.intValue(); + } + + AssertionConsumerService consumerService = spSSODescriptor.getAssertionConsumerServices().get(assertionidx); + + AttributeConsumingService attributeConsumer = null; + + if (spSSODescriptor.getAttributeConsumingServices() != null && + spSSODescriptor.getAttributeConsumingServices().size() > 0) { + attributeConsumer = spSSODescriptor.getAttributeConsumingServices().get(attributeIdx); + } + + PVPTargetConfiguration config = new PVPTargetConfiguration(); + + String oaURL = moaRequest.getEntityMetadata().getEntityID(); + String binding = consumerService.getBinding(); + + Logger.info("Dispatch PVP2 AuthnRequest: OAURL=" + oaURL + " Binding=" + binding); + + oaURL = StringEscapeUtils.escapeHtml(oaURL); + + config.setOAURL(oaURL); + config.setBinding(binding); + config.setRequest(moaRequest); + config.setConsumerURL(consumerService.getLocation()); + + //parse AuthRequest + AuthnRequestImpl authReq = (AuthnRequestImpl) samlReq; + config.setPassiv(authReq.isPassive()); + config.setForce(authReq.isForceAuthn()); + + //validate AuthnRequest + 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(); + } + } + } + + return config; + } + + /** + * PreProcess AuthResponse and Assertion + * @param msg + */ + private MOAResponse preProcessAuthResponse(MOAResponse msg) { + Logger.debug("Start PVP21 assertion processing... "); + Response samlResp = msg.getResponse(); + + try { + if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { + + //validate PVP 2.1 assertion + SAMLVerificationEngine.validateAssertion(samlResp, true); + + msg.setSAMLMessage(SAML2Utils.asDOMDocument(samlResp).getDocumentElement()); + return msg; + + } else if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.NO_PASSIVE_URI)) { + Logger.info("Interfederation IDP has no valid Single Sign-On session. Starting local authentication ..."); + + } + + } catch (IOException e) { + Logger.warn("Interfederation response marshaling FAILED.", e); + + } catch (MarshallingException e) { + Logger.warn("Interfederation response marshaling FAILED.", e); + + } catch (TransformerException e) { + Logger.warn("Interfederation response marshaling FAILED.", e); + + } catch (AssertionValidationExeption e) { + //error is already logged, to nothing + } + + return null; + } } |