/* * 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.configuration.auth.pvp2.servlets; import java.io.IOException; import java.security.NoSuchAlgorithmException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.opensaml.common.SAMLObject; import org.opensaml.common.SignableSAMLObject; import org.opensaml.common.binding.BasicSAMLMessageContext; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.binding.decoding.HTTPPostDecoder; import org.opensaml.saml2.binding.decoding.HTTPRedirectDeflateDecoder; import org.opensaml.saml2.binding.security.SAML2AuthnRequestsSignedRule; import org.opensaml.saml2.binding.security.SAML2HTTPRedirectDeflateSignatureRule; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.saml2.core.RequestAbstractType; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.StatusResponseType; import org.opensaml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.ws.message.decoder.MessageDecodingException; import org.opensaml.ws.security.SecurityPolicyException; import org.opensaml.ws.security.SecurityPolicyResolver; import org.opensaml.ws.security.provider.BasicSecurityPolicy; import org.opensaml.ws.security.provider.StaticSecurityPolicyResolver; import org.opensaml.ws.transport.http.HttpServletRequestAdapter; import org.opensaml.xml.XMLObject; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.SecurityException; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.AbstractSignableXMLObject; import org.opensaml.xml.signature.SignableXMLObject; import org.opensaml.xml.validation.ValidationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egovernment.moa.id.config.webgui.exception.ConfigurationException; import at.gv.egovernment.moa.id.configuration.Constants; import at.gv.egovernment.moa.id.configuration.auth.AuthenticatedUser; import at.gv.egovernment.moa.id.configuration.auth.AuthenticationManager; import at.gv.egovernment.moa.id.configuration.auth.pvp2.PVP2Utils; import at.gv.egovernment.moa.id.configuration.exception.PVP2Exception; import at.gv.egovernment.moa.id.configuration.exception.SLOException; import at.gv.egovernment.moa.id.configuration.helper.LanguageHelper; import at.gv.egovernment.moa.util.MiscUtil; /** * @author tlenz * */ public class SLOFrontChannelServlet extends SLOBasicServlet { private static final long serialVersionUID = -6280199681356977759L; private static final Logger log = LoggerFactory .getLogger(SLOFrontChannelServlet.class); /** * @throws ConfigurationException */ public SLOFrontChannelServlet() throws ConfigurationException { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { if (MiscUtil.isNotEmpty(request.getParameter(Constants.REQUEST_USERSLO))) { // process user initiated single logout process final Object authUserObj = request.getSession().getAttribute(Constants.SESSION_AUTH); if (authUserObj == null) { log.warn("No user information found. Single Log-Out not possible"); buildErrorMessage(request, response); } final AuthenticatedUser authUser = (AuthenticatedUser) authUserObj; final String nameIDFormat = authUser.getNameIDFormat(); final String nameID = authUser.getNameID(); // remove user final AuthenticationManager authManager = AuthenticationManager.getInstance(); authManager.removeActiveUser(authUser); if (MiscUtil.isEmpty(nameID) || MiscUtil.isEmpty(nameIDFormat)) { log.warn("No user information found. Single Log-Out not possible"); buildErrorMessage(request, response); } else { log.info("Fount user information for user nameID: " + nameID + " , nameIDFormat: " + nameIDFormat + ". Build Single Log-Out request ..."); } // build SLO request to IDP final LogoutRequest sloReq = createLogOutRequest(nameID, nameIDFormat, request); request.getSession().setAttribute(Constants.SESSION_PVP2REQUESTID, sloReq.getID()); // send message sendMessage(request, response, sloReq, null); } else { // process PVP 2.1 single logout process final HTTPRedirectDeflateDecoder decode = new HTTPRedirectDeflateDecoder( new BasicParserPool()); final BasicSAMLMessageContext messageContext = new BasicSAMLMessageContext<>(); messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request)); messageContext.setMetadataProvider(getConfig().getMetaDataProvier()); final SAML2HTTPRedirectDeflateSignatureRule signatureRule = new SAML2HTTPRedirectDeflateSignatureRule( PVP2Utils.getTrustEngine(getConfig())); final SAML2AuthnRequestsSignedRule signedRole = new SAML2AuthnRequestsSignedRule(); final BasicSecurityPolicy policy = new BasicSecurityPolicy(); policy.getPolicyRules().add(signatureRule); policy.getPolicyRules().add(signedRole); final SecurityPolicyResolver resolver = new StaticSecurityPolicyResolver( policy); messageContext.setSecurityPolicyResolver(resolver); messageContext.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); decode.decode(messageContext); signatureRule.evaluate(messageContext); processMessage(request, response, messageContext.getInboundMessage(), messageContext.getRelayState()); } } catch (final SLOException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final ConfigurationException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final PVP2Exception e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final SecurityPolicyException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final MessageDecodingException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final SecurityException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final NoSuchAlgorithmException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { final HTTPPostDecoder decode = new HTTPPostDecoder(new BasicParserPool()); final BasicSAMLMessageContext messageContext = new BasicSAMLMessageContext<>(); messageContext.setInboundMessageTransport(new HttpServletRequestAdapter(request)); decode.decode(messageContext); PVP2Utils.validateSignature((SignableXMLObject) messageContext.getInboundMessage(), getConfig()); processMessage(request, response, messageContext.getInboundMessage(), messageContext.getRelayState()); } catch (final MessageDecodingException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final SecurityException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final ValidationException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final ConfigurationException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final PVP2Exception e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } catch (final NoSuchAlgorithmException e) { log.error("Single LogOut processing error.", e); buildErrorMessage(request, response); } } private void buildErrorMessage(HttpServletRequest request, HttpServletResponse response) { request.getSession().setAttribute(Constants.SESSION_SLOERROR, LanguageHelper.getErrorString("webpages.slo.error", request)); // check response destination String serviceURL = getConfig().getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) { serviceURL = serviceURL + "/"; } String redirectURL = serviceURL + Constants.SERVLET_LOGOUT; redirectURL = response.encodeRedirectURL(redirectURL); response.setContentType("text/html"); response.setStatus(302); response.addHeader("Location", redirectURL); } private void processMessage(HttpServletRequest request, HttpServletResponse response, XMLObject xmlObject, String relayState) throws ConfigurationException, PVP2Exception, NoSuchAlgorithmException { if (xmlObject instanceof LogoutRequest) { final LogoutResponse sloResp = processLogOutRequest((LogoutRequest) xmlObject, request); sendMessage(request, response, sloResp, relayState); } else if (xmlObject instanceof LogoutResponse) { final LogoutResponse sloResp = (LogoutResponse) xmlObject; final String reqID = (String) request.getSession().getAttribute(Constants.SESSION_PVP2REQUESTID); request.getSession().setAttribute(Constants.SESSION_PVP2REQUESTID, null); validateLogOutResponse(sloResp, reqID, request, response); } } private void sendMessage(HttpServletRequest request, HttpServletResponse response, RequestAbstractType sloReq, String relayState) throws ConfigurationException, PVP2Exception { final SingleLogoutService sloService = findIDPFrontChannelSLOService(); sloReq.setDestination(sloService.getLocation()); sendMessage(request, response, sloReq, sloService, relayState); } private void sendMessage(HttpServletRequest request, HttpServletResponse response, StatusResponseType sloReq, String relayState) throws ConfigurationException, PVP2Exception { final SingleLogoutService sloService = findIDPFrontChannelSLOService(); sloReq.setDestination(sloService.getLocation()); sendMessage(request, response, sloReq, sloService, relayState); } private void sendMessage(HttpServletRequest request, HttpServletResponse response, SignableSAMLObject sloReq, SingleLogoutService sloService, String relayState) throws ConfigurationException, PVP2Exception { final X509Credential authcredential = PVP2Utils.signMessage((AbstractSignableXMLObject) sloReq, getConfig()); if (sloService.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) { PVP2Utils.postBindingEncoder(request, response, sloReq, authcredential, sloService.getLocation(), relayState); } else if (sloService.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { PVP2Utils.redirectBindingEncoder(request, response, sloReq, authcredential, sloService.getLocation(), relayState); } } }