/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.security.server; import java.security.Principal; import java.util.Arrays; import javax.security.auth.Subject; import javax.servlet.http.Cookie; import org.osgi.service.log.LogService; import org.eclipse.equinox.log.Logger; import org.eclipse.riena.communication.core.hooks.IServiceHook; import org.eclipse.riena.communication.core.hooks.ServiceContext; import org.eclipse.riena.core.Log4r; import org.eclipse.riena.core.RienaStatus; import org.eclipse.riena.core.cache.IGenericObjectCache; import org.eclipse.riena.core.wire.InjectService; import org.eclipse.riena.security.common.ISubjectHolder; import org.eclipse.riena.security.common.NotAuthorizedFailure; import org.eclipse.riena.security.common.session.ISessionHolder; import org.eclipse.riena.security.common.session.Session; import org.eclipse.riena.security.server.session.ISessionService; /** * This Service Hook deals with security issues of a web-service invocation. It * reads the cookies and set SessionHolder, PrincipalLocationHolder and * PrincipalHolder. It also sets "Set-Cookie" on return, when the session has * changed. */ public class SecurityServiceHook implements IServiceHook { /** <code>SESSIONID</code> */ public static final String SESSIONID = "ssoid"; //$NON-NLS-1$ /** <code>SSOID</code> used as Cookie name for the ssoid */ public final static String SSOID = "x-compeople-ssoid"; //$NON-NLS-1$ /** * <code>PRINCIPAL</code> the name of the id, under which the principal is * stored in the current message context * */ public static final String PRINCIPAL = "principal"; //$NON-NLS-1$ /** <code>SET_SESSION</code> */ public static final String SET_SESSION = "set-ssoid"; //$NON-NLS-1$ private static final String RIENA_SECURE_WEBSERVICES_PROPERTY = "riena.secure.webservices"; //$NON-NLS-1$ private IGenericObjectCache<String, Principal[]> principalCache; private ISessionService sessionService; private ISubjectHolder subjectHolder; private ISessionHolder sessionHolder; private final boolean requiresSSOIDbyDefault = false; private static final Logger LOGGER = Log4r.getLogger(Activator.getDefault(), SecurityServiceHook.class); public SecurityServiceHook() { super(); if (!requiresSSOIDbyDefault) { LOGGER.log(LogService.LOG_INFO, "Stage " + RienaStatus.getStage() //$NON-NLS-1$ + ": defining ALL WEBSERVICES in this Webapp as unsecure (SSOID is not required)."); //$NON-NLS-1$ } } @InjectService(useFilter = "(cache.type=PrincipalCache)") public void bind(final IGenericObjectCache<String, Principal[]> principalCache) { this.principalCache = principalCache; } public void unbind(final IGenericObjectCache<String, Principal[]> principalCache) { if (this.principalCache == principalCache) { this.principalCache = null; } } @InjectService(useRanking = true) public void bind(final ISessionService sessionService) { this.sessionService = sessionService; } public void unbind(final ISessionService sessionService) { if (this.sessionService == sessionService) { this.sessionService = null; } } @InjectService(useRanking = true) public void bind(final ISubjectHolder subjectHolder) { this.subjectHolder = subjectHolder; } public void unbind(final ISubjectHolder subjectHolder) { if (this.subjectHolder == subjectHolder) { this.subjectHolder = null; } } @InjectService(useRanking = true) public void bind(final ISessionHolder sessionHolder) { this.sessionHolder = sessionHolder; } public void unbind(final ISessionHolder sessionHolder) { if (this.sessionHolder == sessionHolder) { this.sessionHolder = null; } } public void beforeService(final ServiceContext callback) { final boolean requiresSSOID = requiresSSOIDbyDefault; // first extract the cookies final Cookie[] cookies = callback.getCookies(); String ssoid = null; if (cookies != null) { for (final Cookie cookie : cookies) { if (cookie.getName().equals(SSOID)) { ssoid = cookie.getValue(); } } } if (ssoid != null && ssoid.length() == 0) { ssoid = null; } LOGGER.log(LogService.LOG_DEBUG, "before Service ssoid = " + ssoid); //$NON-NLS-1$ if (ssoid == null && requiresSSOID) { LOGGER.log(LogService.LOG_ERROR, "error in call to webservice {" + callback.getInterfaceName() //$NON-NLS-1$ + "} since it is not in the list of webservices that do not require a session but SSOID=null !!!"); //$NON-NLS-1$ if (Boolean.valueOf(System.getProperty(RIENA_SECURE_WEBSERVICES_PROPERTY, Boolean.TRUE.toString()))) { throw new NotAuthorizedFailure("call to webservice " + callback.getInterfaceName() //$NON-NLS-1$ + " failed, no valid session was given but is required."); //$NON-NLS-1$ } } // check the ssoid in the session service potentially with a webservice call // note: ssoid and plid are not set if (ssoid != null) { Principal[] principals = principalCache.get(ssoid); if (principals == null) { principals = sessionService.findPrincipals(new Session(ssoid)); LOGGER.log(LogService.LOG_DEBUG, "sessionService found principal = " + Arrays.toString(principals)); //$NON-NLS-1$ if (principals == null && requiresSSOID) { LOGGER.log(LogService.LOG_ERROR, "ssoid {" + ssoid //$NON-NLS-1$ + "} found in request but SessionService could not find a Principal."); //$NON-NLS-1$ throw new NotAuthorizedFailure("call to webservice with invalid ssoid"); //$NON-NLS-1$ } if (principals != null) { principalCache.put(ssoid, principals); } } else { LOGGER.log(LogService.LOG_DEBUG, "found principal in cache = " + Arrays.toString(principals)); //$NON-NLS-1$ } if (principals != null) { final Subject subject = new Subject(); for (final Principal p : principals) { subject.getPrincipals().add(p); } subjectHolder.setSubject(subject); callback.setProperty("riena.subject", subject); //$NON-NLS-1$ } } // set ssoid and plid in the sessionholder and the ssoid as attribute if (ssoid != null) { final Session beforeSession = new Session(ssoid); sessionHolder.setSession(beforeSession); callback.setProperty("de.compeople.ssoid", beforeSession); //$NON-NLS-1$ } } public void afterService(final ServiceContext context) { final Session afterSession = sessionHolder.getSession(); final Session beforeSession = (Session) context.getProperty("de.compeople.ssoid"); //$NON-NLS-1$ String ssoid = null; if (afterSession != null) { ssoid = afterSession.getSessionId(); } if (beforeSession != null) { LOGGER.log(LogService.LOG_DEBUG, "afterService after_ssoid=" + ssoid + " before_ssoid=" //$NON-NLS-1$ //$NON-NLS-2$ + beforeSession.getSessionId()); } LOGGER.log(LogService.LOG_DEBUG, "afterService compare session instance before=" + beforeSession + " after=" //$NON-NLS-1$ //$NON-NLS-2$ + afterSession); if (beforeSession != afterSession || (beforeSession != null && afterSession != null && !(beforeSession.getSessionId().equals(ssoid)))) { if (ssoid == null || ssoid.equals("0")) { //$NON-NLS-1$ // delete cookie final Cookie cookie = new Cookie(SSOID, ""); //$NON-NLS-1$ cookie.setPath("/"); //$NON-NLS-1$ context.addCookie(cookie); LOGGER.log(LogService.LOG_DEBUG, "setting cookie to '0'"); //$NON-NLS-1$ } else { final Cookie cookie = new Cookie(SSOID, ssoid); cookie.setPath("/"); //$NON-NLS-1$ context.addCookie(cookie); if (beforeSession != null && !(beforeSession.getSessionId().equals("0"))) { //$NON-NLS-1$ LOGGER.log(LogService.LOG_WARNING, "CHANGING cookie setting from '" + beforeSession.getSessionId() //$NON-NLS-1$ + "' to '" + ssoid + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } else { LOGGER.log(LogService.LOG_DEBUG, "setting cookie to '" + ssoid + "'"); //$NON-NLS-1$ //$NON-NLS-2$ } } } else { LOGGER.log(LogService.LOG_DEBUG, "doing nothing in afterService"); //$NON-NLS-1$ } sessionHolder.setSession(null); subjectHolder.setSubject(null); } }