/* * 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.security.NoSuchAlgorithmException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.joda.time.DateTime; import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import org.opensaml.common.xml.SAMLConstants; 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.NameIDType; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; 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.config.ConfigurationProvider; 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.id.configuration.utils.SAML2Utils; import at.gv.egovernment.moa.util.MiscUtil; /** * @author tlenz * */ public class SLOBasicServlet extends HttpServlet { private static final long serialVersionUID = -4547240664871845098L; private static final Logger log = LoggerFactory .getLogger(SLOBasicServlet.class); private ConfigurationProvider config; public SLOBasicServlet() throws ConfigurationException { config = ConfigurationProvider.getInstance(); config.initializePVP2Login(); } protected LogoutRequest createLogOutRequest(String nameID, String nameIDFormat, HttpServletRequest request) throws SLOException { try { LogoutRequest sloReq = SAML2Utils.createSAMLObject(LogoutRequest.class); SecureRandomIdentifierGenerator gen = new SecureRandomIdentifierGenerator(); sloReq.setID(gen.generateIdentifier()); sloReq.setIssueInstant(new DateTime()); NameID name = SAML2Utils.createSAMLObject(NameID.class); Issuer issuer = SAML2Utils.createSAMLObject(Issuer.class); String serviceURL = config.getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) serviceURL = serviceURL + "/"; name.setValue(serviceURL); issuer.setValue(serviceURL); issuer.setFormat(NameIDType.ENTITY); sloReq.setIssuer(issuer); NameID userNameID = SAML2Utils.createSAMLObject(NameID.class); sloReq.setNameID(userNameID); userNameID.setFormat(nameIDFormat); userNameID.setValue(nameID); return sloReq; } catch (NoSuchAlgorithmException e) { log.warn("Single LogOut request createn FAILED. ", e); throw new SLOException(); } } protected LogoutResponse processLogOutRequest(LogoutRequest sloReq, HttpServletRequest request) throws NoSuchAlgorithmException { //check response destination String serviceURL = config.getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) serviceURL = serviceURL + "/"; String responseDestination = sloReq.getDestination(); if (MiscUtil.isEmpty(responseDestination) || !responseDestination.startsWith(serviceURL)) { log.warn("PVPResponse destination does not match requested destination"); return createSLOResponse(sloReq, StatusCode.REQUESTER_URI, request); } AuthenticationManager authManager = AuthenticationManager.getInstance(); if (authManager.isActiveUser(sloReq.getNameID().getValue())) { AuthenticatedUser authUser = authManager.getActiveUser(sloReq.getNameID().getValue()); log.info("User " + authUser.getGivenName() + " " + authUser.getFamilyName() + " with nameID:" + authUser.getNameID() + " get logged out by Single LogOut request."); authManager.removeActiveUser(authUser); HttpSession session = request.getSession(false); if (session != null) session.invalidate(); return createSLOResponse(sloReq, StatusCode.SUCCESS_URI, request); } else { log.debug("Single LogOut not possible! User with nameID:" + sloReq.getNameID().getValue() + " is not found."); return createSLOResponse(sloReq, StatusCode.SUCCESS_URI, request); } } protected LogoutResponse createSLOResponse(LogoutRequest sloReq, String statusCodeURI, HttpServletRequest request) throws NoSuchAlgorithmException { LogoutResponse sloResp = SAML2Utils.createSAMLObject(LogoutResponse.class); SecureRandomIdentifierGenerator gen = new SecureRandomIdentifierGenerator(); sloResp.setID(gen.generateIdentifier()); sloResp.setInResponseTo(sloReq.getID()); sloResp.setIssueInstant(new DateTime()); NameID name = SAML2Utils.createSAMLObject(NameID.class); Issuer issuer = SAML2Utils.createSAMLObject(Issuer.class); String serviceURL = config.getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) serviceURL = serviceURL + "/"; name.setValue(serviceURL); issuer.setValue(serviceURL); issuer.setFormat(NameIDType.ENTITY); sloResp.setIssuer(issuer); Status status = SAML2Utils.createSAMLObject(Status.class); sloResp.setStatus(status); StatusCode statusCode = SAML2Utils.createSAMLObject(StatusCode.class); statusCode.setValue(statusCodeURI); status.setStatusCode(statusCode ); return sloResp; } protected void validateLogOutResponse(LogoutResponse sloResp, String reqID, HttpServletRequest request, HttpServletResponse response) throws PVP2Exception { //ckeck InResponseTo matchs requestID if (MiscUtil.isEmpty(reqID)) { log.info("NO Sigle LogOut request ID"); throw new PVP2Exception("NO Sigle LogOut request ID"); } if (!reqID.equals(sloResp.getInResponseTo())) { log.warn("SLORequestID does not match SLO Response ID!"); throw new PVP2Exception("SLORequestID does not match SLO Response ID!"); } //check response destination String serviceURL = config.getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) serviceURL = serviceURL + "/"; String responseDestination = sloResp.getDestination(); if (MiscUtil.isEmpty(responseDestination) || !responseDestination.startsWith(serviceURL)) { log.warn("PVPResponse destination does not match requested destination"); throw new PVP2Exception("SLO response destination does not match requested destination"); } request.getSession().invalidate(); if (sloResp.getStatus().getStatusCode().getValue().equals(StatusCode.PARTIAL_LOGOUT_URI)) { log.warn("Single LogOut process is not completed."); request.getSession().setAttribute(Constants.SESSION_SLOERROR, LanguageHelper.getErrorString("webpages.slo.error", request)); } else if (sloResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { if (sloResp.getStatus().getStatusCode().getStatusCode() != null && !sloResp.getStatus().getStatusCode().getStatusCode().equals(StatusCode.PARTIAL_LOGOUT_URI)) { log.info("Single LogOut process complete."); request.getSession().setAttribute(Constants.SESSION_SLOSUCCESS, LanguageHelper.getErrorString("webpages.slo.success", request)); } else { log.warn("Single LogOut process is not completed."); request.getSession().setAttribute(Constants.SESSION_SLOERROR, LanguageHelper.getErrorString("webpages.slo.error", request)); } } else { log.warn("Single LogOut response sends an unsupported statustype " + sloResp.getStatus().getStatusCode().getValue()); request.getSession().setAttribute(Constants.SESSION_SLOERROR, LanguageHelper.getErrorString("webpages.slo.error", request)); } String redirectURL = serviceURL + Constants.SERVLET_LOGOUT; redirectURL = response.encodeRedirectURL(redirectURL); response.setContentType("text/html"); response.setStatus(302); response.addHeader("Location", redirectURL); } protected SingleLogoutService findIDPFrontChannelSLOService() throws ConfigurationException, SLOException { String entityname = config.getPVP2IDPMetadataEntityName(); if (MiscUtil.isEmpty(entityname)) { log.info("No IDP EntityName configurated"); throw new ConfigurationException("No IDP EntityName configurated"); } //get IDP metadata from metadataprovider HTTPMetadataProvider idpmetadata = config.getMetaDataProvier(); try { EntityDescriptor idpEntity = idpmetadata.getEntityDescriptor(entityname); if (idpEntity == null) { log.info("IDP EntityName is not found in IDP Metadata"); throw new ConfigurationException("IDP EntityName is not found in IDP Metadata"); } //select authentication-service url from metadata SingleLogoutService redirectEndpoint = null; for (SingleLogoutService sss : idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleLogoutServices()) { //Get the service address for the binding you wish to use if (sss.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) redirectEndpoint = sss; else if (sss.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI) && redirectEndpoint == null) redirectEndpoint = sss; } if (redirectEndpoint == null) { log.warn("Single LogOut FAILED: IDP implements no frontchannel SLO service."); throw new SLOException("Single LogOut FAILED: IDP implements no frontchannel SLO service."); } return redirectEndpoint; } catch (MetadataProviderException e) { log.info("IDP EntityName is not found in IDP Metadata", e); throw new ConfigurationException("IDP EntityName is not found in IDP Metadata"); } } protected ConfigurationProvider getConfig() { return config; } }