/*******************************************************************************
* Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
* Frank Appel - replaced singletons and static fields (Bug 337787)
******************************************************************************/
package org.eclipse.rap.rwt.internal.service;
import javax.servlet.http.Cookie;
import org.eclipse.rap.rwt.internal.util.ParamCheck;
import org.eclipse.rap.rwt.service.UISession;
import org.eclipse.rap.rwt.service.SettingStore;
import org.eclipse.rap.rwt.service.SettingStoreFactory;
public 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 SettingStoreFactory factory;
public synchronized SettingStore getStore() {
UISession uiSession = ContextProvider.getUISession();
String storeId = getStoreId();
SettingStore result = ( SettingStore )uiSession.getAttribute( storeId );
if( result == null ) {
result = factory.createSettingStore( storeId );
uiSession.setAttribute( storeId, result );
}
return result;
}
public synchronized void register( SettingStoreFactory factory ) {
ParamCheck.notNull( factory, "factory" );
if( hasFactory() ) {
throw new IllegalStateException( "There is already a SettingStoreFactory registered." );
}
this.factory = factory;
}
public void deregisterFactory() {
if( !hasFactory() ) {
throw new IllegalStateException( "There is no SettingStoreFactory for deregistration." );
}
factory = null;
}
public synchronized boolean hasFactory() {
return factory != null;
}
private static String getStoreId() {
UISession uiSession = ContextProvider.getUISession();
// 1. storeId stored in session? (implies cookie exists)
String result = ( String )uiSession.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( ContextProvider.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
uiSession.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;
}
private synchronized static String createUniqueStoreId() {
long now = System.currentTimeMillis();
if( last == now ) {
instanceCount++;
} else {
last = now;
instanceCount = 0;
}
return String.valueOf( now ) + "_" + String.valueOf( instanceCount );
}
static boolean isValidCookieValue( String value ) {
int index = value.indexOf( '_' );
if( index != -1 ) {
try {
Long.parseLong( value.substring( 0, index ) );
Integer.parseInt( value.substring( index + 1 ) );
return true;
} catch( @SuppressWarnings( "unused" ) NumberFormatException nfe ) {
// Cookie format is invalid
}
}
return false;
}
}