/******************************************************************************* * 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.demoOA.servlet.pvp2; import java.io.IOException; import java.security.KeyStore; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.joda.time.DateTime; import org.opensaml.common.SAMLObject; import org.opensaml.common.binding.BasicSAMLMessageContext; import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.binding.encoding.HTTPRedirectDeflateEncoder; import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameIDType; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.saml2.metadata.SingleSignOnService; import org.opensaml.saml2.metadata.impl.SingleSignOnServiceBuilder; import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; import org.opensaml.ws.transport.http.HttpServletResponseAdapter; import org.opensaml.xml.security.x509.KeyStoreX509CredentialAdapter; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egovernment.moa.id.demoOA.Configuration; import at.gv.egovernment.moa.id.demoOA.Constants; import at.gv.egovernment.moa.id.demoOA.exception.ConfigurationException; import at.gv.egovernment.moa.id.demoOA.utils.SAML2Utils; import at.gv.egovernment.moa.util.MiscUtil; /** * Servlet implementation class Authenticate */ public class SingleLogOut extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory .getLogger(SingleLogOut.class); /** * @see HttpServlet#HttpServlet() */ public SingleLogOut() { super(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { log.warn("PVP2 AuthenticationServlet can not be initialized.", e); } } DocumentBuilder builder; //generate AuthenticationRequest protected void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Configuration config = Configuration.getInstance(); config.initializePVP2Login(); String nameIDFormat = (String) request.getSession().getAttribute(Constants.SESSION_NAMEIDFORMAT); String nameID = (String) request.getSession().getAttribute(Constants.SESSION_NAMEID); if (MiscUtil.isEmpty(nameID) || MiscUtil.isEmpty(nameIDFormat)) { log.warn("No user information found. Single Log-Out not possible"); throw new ServletException("No user information found. Single Log-Out not possible"); } else log.info("Fount user information for user nameID: " + nameID + " , nameIDFormat: " + nameIDFormat + ". Build Single Log-Out request ..."); //invalidate local session request.getSession().invalidate(); //build Single LogOut request 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); 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(); 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; } } sloReq.setDestination(redirectEndpoint.getLocation()); //sign authentication request KeyStore keyStore = config.getPVP2KeyStore(); X509Credential authcredential = new KeyStoreX509CredentialAdapter( keyStore, config.getPVP2KeystoreAuthRequestKeyAlias(), config.getPVP2KeystoreAuthRequestKeyPassword().toCharArray()); Signature signer = SAML2Utils.createSAMLObject(Signature.class); signer.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); signer.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); signer.setSigningCredential(authcredential); sloReq.setSignature(signer); HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder(); HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter( response , true); BasicSAMLMessageContext context = new BasicSAMLMessageContext(); SingleSignOnService service = new SingleSignOnServiceBuilder() .buildObject(); service.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); service.setLocation(redirectEndpoint.getLocation()); context.setOutboundSAMLMessageSigningCredential(authcredential); context.setPeerEntityEndpoint(service); context.setOutboundSAMLMessage(sloReq); context.setOutboundMessageTransport(responseAdapter); encoder.encode(context); } catch (Exception e) { log.warn("Authentication Request can not be generated", e); throw new ServletException("Authentication Request can not be generated.", e); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } }