diff options
Diffstat (limited to 'id/server/idserverlib/src/main/java/at/gv')
2 files changed, 450 insertions, 0 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 new file mode 100644 index 000000000..a59033177 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVP2XProtocol.java @@ -0,0 +1,396 @@ +/******************************************************************************* + * 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.net.MalformedURLException; +import java.net.URL; +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.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.AttributeQuery; +import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.LogoutResponse; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.ws.security.SecurityPolicyException; +import org.opensaml.xml.security.SecurityException; +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.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; +import at.gv.egiz.eaaf.core.exceptions.ProtocolNotActiveException; +import at.gv.egiz.eaaf.core.impl.gui.velocity.VelocityLogAdapter; +import at.gv.egiz.eaaf.core.impl.utils.HTTPUtils; +import at.gv.egiz.eaaf.modules.pvp2.exception.AttributQueryException; +import at.gv.egiz.eaaf.modules.pvp2.exception.NoMetadataInformationException; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.AbstractPVP2XProtocol; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.PVPSProfilePendingRequest; +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.message.PVPSProfileResponse; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.EAAFURICompare; +import at.gv.egiz.eaaf.modules.pvp2.sp.exception.AssertionValidationExeption; +import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; +import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; +import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; +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.protocols.pvp2x.config.PVPConfiguration; +import at.gv.egovernment.moa.id.storage.IAuthenticationSessionStoreage; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; + +@Controller +public class PVP2XProtocol extends AbstractPVP2XProtocol { + + @Autowired(required=true) AuthConfiguration moaAuthConfig; + @Autowired protected IAuthenticationSessionStoreage authenticatedSessionStorage; + + public static final String NAME = PVP2XProtocol.class.getName(); + public static final String PATH = "id_pvp2-sprofile"; + + + public static final List<String> DEFAULTREQUESTEDATTRFORINTERFEDERATION = Arrays.asList( + new String[] { + PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME + }); + + static { + new VelocityLogAdapter(); + + } + + public String getName() { + return NAME; + } + + public String getAuthProtocolIdentifier() { + return PATH; + } + + public PVP2XProtocol() { + super(); + } + + //PVP2.x metadata end-point + @RequestMapping(value = PVPConfiguration.PVP2_METADATA, method = {RequestMethod.POST, RequestMethod.GET}) + public void PVPMetadataRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + if (!moaAuthConfig.getAllowedProtocols().isPVP21Active()) { + Logger.info("PVP2.1 is deaktivated!"); + throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }, "PVP2.1 is deaktivated!"); + + } + + super.pvpMetadataRequest(req, resp); + + } + + //PVP2.x IDP POST-Binding end-point + @RequestMapping(value = PVPConfiguration.PVP2_IDP_POST, method = {RequestMethod.POST}) + public void PVPIDPPostRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + if (!moaAuthConfig.getAllowedProtocols().isPVP21Active()) { + Logger.info("PVP2.1 is deaktivated!"); + throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }, "PVP2.1 is deaktivated!"); + + } + + super.PVPIDPPostRequest(req, resp); + + } + + //PVP2.x IDP Redirect-Binding end-point + @RequestMapping(value = PVPConfiguration.PVP2_IDP_REDIRECT, method = {RequestMethod.GET}) + public void PVPIDPRedirecttRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { + if (!AuthConfigurationProviderFactory.getInstance().getAllowedProtocols().isPVP21Active()) { + Logger.info("PVP2.1 is deaktivated!"); + throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }, "PVP2.1 is deaktivated!"); + + } + + super.PVPIDPRedirecttRequest(req, resp); + + } + + + //PVP2.x IDP SOAP-Binding end-point + @RequestMapping(value = "/pvp2/soap", method = {RequestMethod.POST}) + public void PVPIDPSOAPRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { +// if (!authConfig.getAllowedProtocols().isPVP21Active()) { +// Logger.info("PVP2.1 is deaktivated!"); +// throw new ProtocolNotActiveException("auth.22", new java.lang.Object[] { NAME }, "PVP2.1 is deaktivated!"); +// +// } + + PVPSProfilePendingRequest pendingReq = null; + try { + //create pendingRequest object + pendingReq = applicationContext.getBean(PVPSProfilePendingRequest.class); + pendingReq.initialize(req, authConfig); + 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 EAAFURICompare(pvpBasicConfiguration.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); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, e.getMessage()); + + } catch (SecurityException e) { + String samlRequest = req.getParameter("SAMLRequest"); + Logger.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}, e.getMessage()); + + } catch (MOAIDException e) { + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw e; + + } catch (Throwable e) { + String samlRequest = req.getParameter("SAMLRequest"); + Logger.warn("Receive INVALID protocol request: " + samlRequest, e); + + //write revision log entries + if (pendingReq != null) + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.TRANSACTION_ERROR, pendingReq.getUniqueTransactionIdentifier()); + + throw new MOAIDException("pvp2.24", new Object[] {e.getMessage()}); + } + } + + + protected boolean childPreProcess(HttpServletRequest request, + HttpServletResponse response, PVPSProfilePendingRequest pendingReq) throws Throwable { + InboundMessage msg = pendingReq.getRequest(); + if (msg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)msg).getSamlRequest() instanceof AttributeQuery) + preProcessAttributQueryRequest(request, response, pendingReq); + + else if (msg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)msg).getSamlRequest() instanceof LogoutRequest) + preProcessLogOut(request, response, pendingReq); + + else if (msg instanceof PVPSProfileResponse && + ((PVPSProfileResponse)msg).getResponse() instanceof LogoutResponse) + preProcessLogOut(request, response, pendingReq); + + else + return false; + + + return true; + } + + /** + * PreProcess Single LogOut request + * @param request + * @param response + * @param msg + * @return + * @throws EAAFException + * @throws MOAIDException + */ + private void preProcessLogOut(HttpServletRequest request, + HttpServletResponse response, PVPSProfilePendingRequest pendingReq) throws EAAFException { + + InboundMessage inMsg = pendingReq.getRequest(); + PVPSProfileRequest msg; + if (inMsg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)inMsg).getSamlRequest() instanceof LogoutRequest) { + //preProcess single logout request from service provider + + msg = (PVPSProfileRequest) inMsg; + + EntityDescriptor metadata = msg.getEntityMetadata(metadataProvider); + if(metadata == null) { + throw new NoMetadataInformationException(); + } + + String oaURL = metadata.getEntityID(); + oaURL = StringEscapeUtils.escapeHtml(oaURL); + ISPConfiguration oa = authConfig.getServiceProviderConfiguration(oaURL); + + Logger.info("Dispatch PVP2 SingleLogOut: OAURL=" + oaURL + " Binding=" + msg.getRequestBinding()); + + pendingReq.setSPEntityId(oaURL); + pendingReq.setOnlineApplicationConfiguration(oa); + pendingReq.setBinding(msg.getRequestBinding()); + + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROTOCOL_PVP_REQUEST_SLO); + + + + } else if (inMsg instanceof PVPSProfileResponse && + ((PVPSProfileResponse)inMsg).getResponse() instanceof LogoutResponse) { + //preProcess single logour response from service provider + + LogoutResponse resp = (LogoutResponse) (((PVPSProfileResponse)inMsg).getResponse()); + + Logger.debug("PreProcess SLO Response from " + resp.getIssuer()); + +// List<String> allowedPublicURLPrefix = authConfig.getIDPPublicURLPrefixes(); +// AuthConfigurationProviderFactory.getInstance().getPublicURLPrefix(); + + boolean isAllowedDestination = false; + try { + isAllowedDestination = MiscUtil.isNotEmpty(authConfig.validateIDPURL(new URL(resp.getDestination()))); + + } catch (MalformedURLException e) { + Logger.info(resp.getDestination() + " is NOT valid. Reason: " + e.getMessage()); + + } + +// 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 EAAFException("Unsupported request"); + + + pendingReq.setRequest(inMsg); + pendingReq.setAction(PVPConstants.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, PVPSProfilePendingRequest pendingReq) throws Throwable { + PVPSProfileRequest moaRequest = ((PVPSProfileRequest)pendingReq.getRequest()); + AttributeQuery attrQuery = (AttributeQuery) moaRequest.getSamlRequest(); + moaRequest.setEntityID(attrQuery.getIssuer().getValue()); + + //validate destination + String destinaten = attrQuery.getDestination(); + if (!pvpBasicConfiguration.getIDPSSOSOAPService(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 + IOAAuthParameters oa = authConfig.getServiceProviderConfiguration(moaRequest.getEntityID(), IOAAuthParameters.class); + 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.setSPEntityId(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.setSSOSessionIdentifier(session.getSSOSessionID()); + + //write revisionslog entry + revisionsLogger.logEvent(pendingReq, MOAIDEventConstants.AUTHPROTOCOL_PVP_REQUEST_ATTRIBUTQUERY); + + + } + +} diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/metadata/PVPMetadataFilterChain.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/metadata/PVPMetadataFilterChain.java new file mode 100644 index 000000000..615a0eaa7 --- /dev/null +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/metadata/PVPMetadataFilterChain.java @@ -0,0 +1,54 @@ +/* + * 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.verification.metadata; + +import java.security.cert.CertificateException; + +import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.MetadataFilterChain; + +/** + * @author tlenz + * + */ +public class PVPMetadataFilterChain extends MetadataFilterChain { + + + /** + * @throws CertificateException + * + */ + public PVPMetadataFilterChain(String url, byte[] certificate) throws CertificateException { + addDefaultFilters(url, certificate); + } + + public void addDefaultFilters(String url, byte[] certificate) throws CertificateException { + addFilter(new MetadataSignatureFilter(url, certificate)); + + } + + + + + + +} |