/******************************************************************************* * 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.moduls; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.net.URI; import java.util.Date; import java.util.List; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.apache.velocity.app.VelocityEngine; import org.hibernate.Query; import org.hibernate.Session; import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.commons.db.MOASessionDBUtils; import at.gv.egovernment.moa.id.commons.db.dao.session.AuthenticatedSessionStore; import at.gv.egovernment.moa.id.commons.db.dao.session.InterfederationSessionStore; import at.gv.egovernment.moa.id.commons.db.dao.session.OldSSOSessionIDStore; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; 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.MiscUtil; public class SSOManager { private static final String HTMLTEMPLATESDIR = "htmlTemplates/"; private static final String HTMLTEMPLATEFULL = "slo_template.html"; private static final String SSOCOOKIE = "MOA_ID_SSO"; private static final String SSOINTERFEDERATION = "MOA_INTERFEDERATION_SSO"; private static final int DEFAULTSSOTIMEOUT = 15 * 60; // sec private static final int INTERFEDERATIONCOOKIEMAXAGE = 5 * 60;// sec private static SSOManager instance = null; private static int sso_timeout; public static SSOManager getInstance() { if (instance == null) { instance = new SSOManager(); try { sso_timeout = (int) AuthConfigurationProvider.getInstance().getTimeOuts().getMOASessionUpdated().longValue(); } catch (ConfigurationException e) { Logger.info("SSO Timeout can not be loaded from MOA-ID configuration. Use default Timeout with " + DEFAULTSSOTIMEOUT); sso_timeout = DEFAULTSSOTIMEOUT; } } return instance; } public void checkInterfederationIsRequested(HttpServletRequest httpReq, HttpServletResponse httpResp, IRequest protocolRequest) { String interIDP = httpReq.getParameter(MOAIDAuthConstants.INTERFEDERATION_IDP); if (MiscUtil.isNotEmpty(protocolRequest.getRequestedIDP())) { Logger.info("Protocolspecific preprocessing already set interfederation IDP " + protocolRequest.getRequestedIDP()); } if (protocolRequest instanceof RequestImpl) { //check if IDP is requested RequestImpl moaReq = (RequestImpl) protocolRequest; if (MiscUtil.isNotEmpty(interIDP)) { Logger.info("Receive SSO request for interfederation IDP " + interIDP); moaReq.setRequestedIDP(interIDP); } else { //check if IDP cookie is set String cookie = getValueFromCookie(httpReq, SSOINTERFEDERATION); if (MiscUtil.isNotEmpty(cookie)) { Logger.info("Receive SSO request for interfederated IDP from Cookie " + cookie); moaReq.setRequestedIDP(cookie); deleteCookie(httpReq, httpResp, SSOINTERFEDERATION); } } } else { Logger.warn("Request is not of type RequestImpl"); } } public void setInterfederationIDPCookie(HttpServletRequest httpReq, HttpServletResponse httpResp, String value) { setCookie(httpReq, httpResp, SSOINTERFEDERATION, value, INTERFEDERATIONCOOKIEMAXAGE); } public boolean isValidSSOSession(String ssoSessionID, IRequest protocolRequest) throws ConfigurationException { // search SSO Session if (ssoSessionID == null) { Logger.info("No SSO Session cookie found."); return false; } AuthenticatedSessionStore storedSession = AuthenticationSessionStoreage.isValidSessionWithSSOID(ssoSessionID, null); if (storedSession == null) return false; else { //check if session is out of lifetime Date now = new Date(); long maxSSOSessionTime = AuthConfigurationProvider.getInstance().getTimeOuts().getMOASessionCreated().longValue() * 1000; Date ssoSessionValidTo = new Date(storedSession.getCreated().getTime() + maxSSOSessionTime); if (now.after(ssoSessionValidTo)) { Logger.info("Found outdated SSO session information. Start reauthentication process ... "); return false; } //check if request starts an interfederated SSO session if (protocolRequest != null && protocolRequest instanceof RequestImpl && storedSession.isInterfederatedSSOSession() && !storedSession.isAuthenticated()) { if (MiscUtil.isEmpty(((RequestImpl) protocolRequest).getRequestedIDP())) { InterfederationSessionStore selectedIDP = AuthenticationSessionStoreage.searchInterfederatedIDPFORSSOWithMOASession(storedSession.getSessionid()); if (selectedIDP != null) { //no local SSO session exist -> request interfederated IDP ((RequestImpl) protocolRequest).setRequestedIDP(selectedIDP.getIdpurlprefix()); } else { Logger.warn("MOASession is marked as interfederated SSO session but no interfederated IDP is found. Switch to local authentication ..."); MOASessionDBUtils.delete(storedSession); } } return false; } return true; } } public String getMOASession(String ssoSessionID) { return AuthenticationSessionStoreage.getMOASessionSSOID(ssoSessionID); } public String existsOldSSOSession(String ssoId) { Logger.trace("Check that the SSOID has already been used"); Session session = MOASessionDBUtils.getCurrentSession(); List result; synchronized (session) { session.beginTransaction(); Query query = session.getNamedQuery("getSSOSessionWithOldSessionID"); query.setParameter("sessionid", ssoId); result = query.list(); // send transaction } Logger.trace("Found entries: " + result.size()); // Assertion requires an unique artifact if (result.size() == 0) { session.getTransaction().commit(); return null; } OldSSOSessionIDStore oldSSOSession = result.get(0); AuthenticatedSessionStore correspondingMoaSession = oldSSOSession.getMoasession(); if (correspondingMoaSession == null) { Logger.info("Get request with old SSO SessionID but no corresponding SSO Session is found."); return null; } String moasessionid = correspondingMoaSession.getSessionid(); session.getTransaction().commit(); return moasessionid; } public String createSSOSessionInformations(String moaSessionID, String OAUrl) { String newSSOId = Random.nextRandom(); if (MiscUtil.isEmpty(moaSessionID) || MiscUtil.isEmpty(OAUrl)) { Logger.warn("MoaSessionID or OAUrl are empty -> SSO is not enabled!"); return null; } return newSSOId; } public void setSSOSessionID(HttpServletRequest httpReq, HttpServletResponse httpResp, String ssoId) { setCookie(httpReq, httpResp, SSOCOOKIE, ssoId, sso_timeout); } public String getSSOSessionID(HttpServletRequest httpReq) { return getValueFromCookie(httpReq, SSOCOOKIE); } public void deleteSSOSessionID(HttpServletRequest httpReq, HttpServletResponse httpResp) { deleteCookie(httpReq, httpResp, SSOCOOKIE); } /** * @param entityID * @param request */ public boolean removeInterfederatedSSOIDP(String entityID, HttpServletRequest request) { String ssoSessionID = getSSOSessionID(request); if (MiscUtil.isNotEmpty(ssoSessionID)) { AuthenticatedSessionStore storedSession = AuthenticationSessionStoreage.isValidSessionWithSSOID(ssoSessionID, null); if (storedSession == null) return false; InterfederationSessionStore selectedIDP = AuthenticationSessionStoreage.searchInterfederatedIDPFORSSOWithMOASessionIDPID(storedSession.getSessionid(), entityID); if (selectedIDP != null) { //no local SSO session exist -> request interfederated IDP Logger.info("Delete interfederated IDP " + selectedIDP.getIdpurlprefix() + " from MOASession " + storedSession.getSessionid()); MOASessionDBUtils.delete(selectedIDP); } else { Logger.warn("MOASession is marked as interfederated SSO session but no interfederated IDP is found. Switch to local authentication ..."); } return true; } else return false; } public void printSingleLogOutInfo(VelocityContext context, HttpServletResponse httpResp) throws MOAIDException { try { Logger.trace("Initialize VelocityEngine..."); InputStream is = null; String pathLocation = null; try { String rootconfigdir = AuthConfigurationProvider.getInstance().getRootConfigFileDir(); pathLocation = rootconfigdir + HTMLTEMPLATESDIR + HTMLTEMPLATEFULL; File file = new File(new URI(pathLocation)); is = new FileInputStream(file); evaluateSLOTemplate(context, httpResp, is); } catch (Exception e) { Logger.warn("SLO Template is not found in configuration directory (" + pathLocation + "). Load template from project library ... "); try { pathLocation = "resources/templates/" + HTMLTEMPLATEFULL; is = Thread.currentThread() .getContextClassLoader() .getResourceAsStream(pathLocation); evaluateSLOTemplate(context, httpResp, is); } catch (Exception e1) { Logger.error("Single LogOut form can not created.", e); throw new MOAIDException("Create Single LogOut information FAILED.", null, e); } } finally { if (is != null) is.close(); } } catch (Exception e) { Logger.error("Single LogOut form can not created.", e); throw new MOAIDException("Create Single LogOut information FAILED.", null, e); } } private void evaluateSLOTemplate(VelocityContext context, HttpServletResponse httpResp, InputStream is) throws Exception { VelocityEngine engine = VelocityProvider.getClassPathVelocityEngine(); BufferedReader reader = new BufferedReader(new InputStreamReader(is )); //set default elements to velocity context context.put("contextpath", AuthConfigurationProvider.getInstance().getPublicURLPrefix()); StringWriter writer = new StringWriter(); //velocityEngine.evaluate(context, writer, "SLO_Template", reader); engine.evaluate(context, writer, "SLO Template", reader); httpResp.setContentType("text/html;charset=UTF-8"); httpResp.getOutputStream().write(writer.toString().getBytes("UTF-8")); } private String getValueFromCookie(HttpServletRequest httpReq, String cookieName) { Cookie[] cookies = httpReq.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { // funktioniert nicht, da Cookie seltsamerweise immer unsecure übertragen wird // (firefox) // if (cookie.getName().equals(SSOCOOKIE) && cookie.getSecure()) { if (cookie.getName().equals(cookieName)) { return cookie.getValue(); } } } return null; } private void setCookie(HttpServletRequest httpReq, HttpServletResponse httpResp, String cookieName, String cookieValue, int maxAge) { Cookie cookie = new Cookie(cookieName, cookieValue); cookie.setMaxAge(maxAge); cookie.setSecure(true); //TODO: could be a problem if the IDP is accessible from different contextPaths or Domains cookie.setPath(httpReq.getContextPath()); httpResp.addCookie(cookie); } private void deleteCookie(HttpServletRequest httpReq, HttpServletResponse httpResp, String cookieName) { setCookie(httpReq, httpResp, cookieName, "", 1); } }