/*******************************************************************************
* Copyright (c) 2002, 2009 Innoopract Informationssysteme GmbH.
* 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.rwt.internal.service;
import javax.servlet.http.Cookie;
import org.eclipse.rwt.RWT;
import org.eclipse.rwt.internal.util.ParamCheck;
import org.eclipse.rwt.service.*;
/**
* An SettingStoreManager manages ISettingStores with the help of a
* registered {@link ISettingStoreFactory}.
*/
public final class SettingStoreManager {
private static final String COOKIE_NAME = "settingStore";
private static final int COOKIE_MAX_AGE_SEC = 3600 * 24 * 90; // 3 months
private static long last = System.currentTimeMillis();
private static int instanceCount;
private static ISettingStoreFactory factory = null;
public synchronized static ISettingStore getStore() {
ISessionStore session = ContextProvider.getSession();
String storeId = getStoreId();
ISettingStore result
= ( ISettingStore )session.getAttribute( storeId );
if( result == null ) {
result = factory.createSettingStore( storeId );
session.setAttribute( storeId, result );
}
return result;
}
public synchronized static void register(
final ISettingStoreFactory factory )
{
ParamCheck.notNull( factory, "factory" );
if( factory != null && SettingStoreManager.factory != null ) {
String msg = "There is already an ISettingStoreFactory registered.";
throw new IllegalStateException( msg );
}
SettingStoreManager.factory = factory;
}
public synchronized static boolean hasFactory() {
return SettingStoreManager.factory != null;
}
//////////////////
// helping methods
private static synchronized String createUniqueStoreId() {
long now = System.currentTimeMillis();
if( last == now ) {
instanceCount++;
} else {
last = now;
instanceCount = 0;
}
return String.valueOf( now ) + "_" + String.valueOf( instanceCount );
}
private static String getStoreId() {
ISessionStore session = ContextProvider.getSession();
// 1. storeId stored in session? (implies cookie exists)
String result = ( String )session.getAttribute( COOKIE_NAME );
if( result == null ) {
// 2. storeId stored in cookie?
result = getStoreIdFromCookie();
if( result == null ) {
// 3. create new storeId
result = createUniqueStoreId();
}
// (2+3) do refresh cookie, to ensure it expires in COOKIE_MAX_AGE_SEC
Cookie cookie = new Cookie( COOKIE_NAME, result );
cookie.setSecure( RWT.getRequest().isSecure() );
cookie.setMaxAge( COOKIE_MAX_AGE_SEC );
ContextProvider.getResponse().addCookie( cookie );
// (2+3) update storeId stored in session
// Note: This attribute must be checked for validity to prevent attacks
// like http://www.owasp.org/index.php/Cross-User_Defacement
session.setAttribute( COOKIE_NAME, result );
}
return result;
}
private static String getStoreIdFromCookie() {
String result = null;
Cookie[] cookies = ContextProvider.getRequest().getCookies();
if( cookies != null ) {
for( int i = 0; result == null && i < cookies.length; i++ ) {
Cookie cookie = cookies[ i ];
if( COOKIE_NAME.equals( cookie.getName() ) ) {
String value = cookie.getValue();
// Validate cookies to prevent cookie manipulation and related attacks
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=275380
if( isValidCookieValue( value ) ) {
result = value;
}
}
}
}
return result;
}
static boolean isValidCookieValue( final String value ) {
boolean result = false;
try {
int index = value.indexOf( '_' );
if( index != -1 ) {
Long.parseLong( value.substring( 0, index ) );
Integer.parseInt( value.substring( index + 1 ) );
result = true;
}
} catch( Exception e ) {
// Cookie format is invalid
}
return result;
}
}