Java tutorial
/* * 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.io.Serializable; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.SerializationUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.opensaml.common.SAMLObject; import org.opensaml.common.binding.BasicSAMLMessageContext; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.saml2.core.RequestAbstractType; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusResponseType; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.saml2.metadata.impl.SingleLogoutServiceBuilder; import org.opensaml.ws.message.encoder.MessageEncodingException; import org.opensaml.ws.soap.common.SOAPException; import org.opensaml.xml.XMLObject; import org.opensaml.xml.security.SecurityException; import org.opensaml.xml.security.x509.X509Credential; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.auth.servlet.RedirectServlet; import at.gv.egovernment.moa.id.commons.db.MOASessionDBUtils; import at.gv.egovernment.moa.id.commons.db.dao.session.AssertionStore; import at.gv.egovernment.moa.id.commons.db.dao.session.InterfederationSessionStore; import at.gv.egovernment.moa.id.commons.db.dao.session.OASessionStore; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.SLOInformationContainer; import at.gv.egovernment.moa.id.data.SLOInformationImpl; import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.moduls.AuthenticationManager; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.moduls.SSOManager; import at.gv.egovernment.moa.id.opemsaml.MOAStringRedirectDeflateEncoder; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IEncoder; 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.builder.SingleLogOutBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.BindingNotSupportedException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.SLOException; 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.signer.CredentialProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.MOASAMLSOAPClient; import at.gv.egovernment.moa.id.storage.AssertionStorage; import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; import at.gv.egovernment.moa.id.util.Random; import at.gv.egovernment.moa.id.util.VelocityProvider; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MessageProvider; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moa.util.URLEncoder; /** * @author tlenz * */ public class SingleLogOutAction implements IAction { /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#processRequest(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.data.IAuthData) */ @Override public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { PVPTargetConfiguration pvpReq = (PVPTargetConfiguration) req; if (pvpReq.getRequest() instanceof MOARequest && ((MOARequest) pvpReq.getRequest()).getSamlRequest() instanceof LogoutRequest) { Logger.debug("Process Single LogOut request"); MOARequest samlReq = (MOARequest) pvpReq.getRequest(); LogoutRequest logOutReq = (LogoutRequest) samlReq.getSamlRequest(); AuthenticationSession session = AuthenticationSessionStoreage.searchMOASessionWithNameIDandOAID( logOutReq.getIssuer().getValue(), logOutReq.getNameID().getValue()); if (session == null) { Logger.warn("Can not find active SSO session with nameID " + logOutReq.getNameID().getValue() + " and OA " + logOutReq.getIssuer().getValue()); Logger.info("Search active SSO session with SSO session cookie"); SSOManager ssomanager = SSOManager.getInstance(); String ssoID = ssomanager.getSSOSessionID(httpReq); if (MiscUtil.isEmpty(ssoID)) { Logger.warn("Can not find active Session. Single LogOut not possible!"); SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); //LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq, StatusCode.RESPONDER_URI); LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, pvpReq, null); Logger.info("Sending SLO success message to requester ..."); SingleLogOutBuilder.sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); return null; } else { String moasession = ssomanager.getMOASession(ssoID); try { session = AuthenticationSessionStoreage.getSession(moasession); } catch (MOADatabaseException e) { Logger.warn("Can not find active Session. Single LogOut not possible!"); SingleLogoutService sloService = SingleLogOutBuilder.getResponseSLODescriptor(pvpReq); //LogoutResponse message = SingleLogOutBuilder.buildSLOErrorResponse(sloService, pvpReq, StatusCode.RESPONDER_URI); LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, pvpReq, null); Logger.info("Sending SLO success message to requester ..."); SingleLogOutBuilder.sendFrontChannelSLOMessage(sloService, message, httpReq, httpResp, samlReq.getRelayState()); return null; } } } AuthenticationManager authManager = AuthenticationManager.getInstance(); authManager.performSingleLogOut(httpReq, httpResp, session, pvpReq); } else if (pvpReq.getRequest() instanceof MOAResponse && ((MOAResponse) pvpReq.getRequest()).getResponse() instanceof LogoutResponse) { Logger.debug("Process Single LogOut response"); LogoutResponse logOutResp = (LogoutResponse) ((MOAResponse) pvpReq.getRequest()).getResponse(); Transaction tx = null; try { String relayState = pvpReq.getRequest().getRelayState(); if (MiscUtil.isEmpty(relayState)) { Logger.warn( "SLO Response from " + logOutResp.getIssuer().getValue() + " has no SAML2 RelayState."); throw new SLOException("pvp2.19", null); } Session session = MOASessionDBUtils.getCurrentSession(); boolean storageSuccess = false; int counter = 0; //TODO: add counter to prevent deadlock while (!storageSuccess) { tx = session.beginTransaction(); List result; Query query = session.getNamedQuery("getAssertionWithArtifact"); query.setParameter("artifact", relayState); result = query.list(); Logger.trace("Found entries: " + result.size()); //Assertion requires an unique artifact if (result.size() != 1) { Logger.trace("No entries found."); throw new MOADatabaseException("No sessioninformation found with this ID"); } AssertionStore element = (AssertionStore) result.get(0); Object data = SerializationUtils.deserialize(element.getAssertion()); if (data instanceof SLOInformationContainer) { SLOInformationContainer sloContainer = (SLOInformationContainer) data; //check status SingleLogOutBuilder.checkStatusCode(sloContainer, logOutResp); if (sloContainer.hasFrontChannelOA()) { try { //some response are open byte[] serializedSLOContainer = SerializationUtils .serialize((Serializable) sloContainer); element.setAssertion(serializedSLOContainer); element.setType(sloContainer.getClass().getName()); session.saveOrUpdate(element); tx.commit(); //sloContainer could be stored to database storageSuccess = true; } catch (HibernateException e) { tx.rollback(); counter++; Logger.debug( "SLOContainter could not stored to database. Wait some time and restart storage process ... "); java.util.Random rand = new java.util.Random(); try { Thread.sleep(rand.nextInt(20) * 10); } catch (InterruptedException e1) { Logger.warn("Thread could not stopped. ReStart storage process immediately", e1); } } } else { //last response received. try { session.delete(element); tx.commit(); } catch (HibernateException e) { tx.rollback(); Logger.error("SLOContainter could not deleted from database. "); } storageSuccess = true; String redirectURL = null; if (sloContainer.getSloRequest() != null) { //send SLO response to SLO request issuer SingleLogoutService sloService = SingleLogOutBuilder .getResponseSLODescriptor(sloContainer.getSloRequest()); LogoutResponse message = SingleLogOutBuilder.buildSLOResponseMessage(sloService, sloContainer.getSloRequest(), sloContainer.getSloFailedOAs()); redirectURL = SingleLogOutBuilder.getFrontChannelSLOMessageURL(sloService, message, httpReq, httpResp, sloContainer.getSloRequest().getRequest().getRelayState()); } else { //print SLO information directly redirectURL = AuthConfigurationProvider.getInstance().getPublicURLPrefix() + "/idpSingleLogout"; String artifact = Random.nextRandom(); String statusCode = null; if (sloContainer.getSloFailedOAs() == null || sloContainer.getSloFailedOAs().size() == 0) statusCode = SLOSTATUS_SUCCESS; else statusCode = SLOSTATUS_ERROR; AssertionStorage.getInstance().put(artifact, statusCode); redirectURL = addURLParameter(redirectURL, PARAM_SLOSTATUS, artifact); } //redirect to Redirect Servlet String url = AuthConfigurationProvider.getInstance().getPublicURLPrefix() + "/RedirectServlet"; url = addURLParameter(url, RedirectServlet.REDIRCT_PARAM_URL, URLEncoder.encode(redirectURL, "UTF-8")); url = httpResp.encodeRedirectURL(url); httpResp.setContentType("text/html"); httpResp.setStatus(302); httpResp.addHeader("Location", url); } } else { Logger.warn("Sessioninformation Cast-Exception by using Artifact=" + relayState); throw new MOADatabaseException("Sessioninformation Cast-Exception"); } } } catch (MOADatabaseException e) { Logger.error("MOA AssertionDatabase ERROR", e); throw new SLOException("pvp2.19", null); } catch (UnsupportedEncodingException e) { Logger.error("Finale SLO redirct not possible.", e); throw new AuthenticationException("pvp2.13", new Object[] {}); } finally { if (tx != null && !tx.wasCommitted()) { tx.commit(); } } } else { Logger.error("Process SingleLogOutAction but request is NOT of type LogoutRequest or LogoutResponse."); throw new MOAIDException("pvp2.13", null); } return null; } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#needAuthentication(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) { return false; } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#getDefaultActionName() */ @Override public String getDefaultActionName() { return PVP2XProtocol.SINGLELOGOUT; } protected static String addURLParameter(String url, String paramname, String paramvalue) { String param = paramname + "=" + paramvalue; if (url.indexOf("?") < 0) return url + "?" + param; else return url + "&" + param; } }