Blog do projektu Open Source JavaHotel

niedziela, 21 kwietnia 2013

New version of JSPWiki in the clouds

Problem
I realized that my humble JSPWiki implementation for Google App Engine is under spam attack. Nasty anonymous user is constantly adding spam message to the main page. I also realized that ACL (Access Control List for pages) does not work in my implementation so I was unable to restrict access to this page.
Solution
After spending some time I enabled ACL for page access. So one can add to the pages stuff like [{ALLOW view SocialCommittee}] and the access is limited. The problem was awkward binding ACL list with the 'WikiPage' class I found in the standard version. But now it seems working, I restricted access to the 'Main' page and although the page was under spam attach all the time the spam message was not added. I also put the spam address to the blacklist (using solution described here) and the battle with spam is over and victorious for the time being.

piątek, 19 kwietnia 2013

Jython framework and security

Introduction
I added security to my Jython/UI MVP framework. Full source code is available here. Current Google App Engine version is available here. Another version of an application (without security enabled) is available here. Also not Google App Engine (tested with Tomcat and Glassfish) has been created. Current documentation is available here.
Authentication and Shiro
Authentication is implemented using Apache Shiro. Only standalone API is used now. I found this framework very useful - it fulfills what is needed and is very simple at the same time. In sample application the simplest authentication realm is used but the doors is open to implement more sophisticated authentication environment.
Authentication and two modes
It is controlled by Authenticate property in app.properties file (example).
  • Authenticate=Y : Restricted mode, access to all pages requires authentication token. Cannot get access without being authenticated.
  • Authenticate=N (default) : Not restricted mode. Access for not authenticated users to some pages is possible.
Login enforcement
Starting pages (specified by 'start' http query argument) enforcing login page are specified in app.properties file. Parameter 'Login' should contain list of starting page requiring login window. All starting pages not specified there are accessible without authentication (only if not restricted mode is enabled).
Authentication token
After successful login authentication token is returned to the client. From now on all access to the server  should be performed by using this token. Any password (with except to the login page) is stored at the client side just making all stuff more secure.
Authentication token and server
Unfortunately, this solution does not come cheap. It is related to the fact that the purpose is to have server side stateless, just ready to run in cloud environment. So the solution cannot rely on Shiro session  because next request can be executed in a fresh instance of JVM just having previous session invalidated.  The problem is described here in Shiro documentation. So both user name and password should be stored at the server side after user authentication ready to authenticate again against the Shiro session. Credentials are stored in a cache and also in backing persistent storage in case of being removed from cache. Implementation of cache and persistent storage is different for Google App Engine and not Google App Engine implementation. The simple algorithm for caching credentials is common.

public class SecuritySessionStore implements ISecuritySessionCache {

    private final ISecuritySessionMemCache iMemCache;
    private final ISecuritySessionPersistent iPersistent;

    private static final Logger log = Logger
            .getLogger(SecuritySessionStore.class.getName());

    @Inject
    public SecuritySessionStore(ISecuritySessionMemCache iMemCache,
            ISecuritySessionPersistent iPersistent) {
        this.iMemCache = iMemCache;
        this.iPersistent = iPersistent;
    }

    @Override
    public Object get(String key) throws InvalidClassException {
        Object val = iMemCache.get(key);
        if (val != null)
            return val;
        val = iPersistent.get(key);
        if (val == null)
            return null;
        // add value to cache again
        log.log(Level.FINE, LogMess.getMessN(ILogMess.PUTINCACHEAGAIN, key));
        iMemCache.put(key, val);
        return val;
    }

    @Override
    public void put(String key, Object o) {
        iMemCache.put(key, o);
        iPersistent.put(key, o);
    }

    @Override
    public void remove(String key) {
        iMemCache.remove(key);
        iPersistent.remove(key);
    }

}
By virtue of Guice the proper implementation of ISecuritySessionMemCache and ISecuritySessionPersitent is injected.
Authorization and Jexl
Three types of authorization is used. User, role and permission. Sometimes authorization rule can be complicated (for instance: give access to the user identified by name or the user having the role). So I decided to user Jexl for creating boolean expression evaluating to True (is authorized) or False (is not authorized).
To create an expression three methods are defined: sec.u (current user is the user authorized), sec.r (current user has the role) and sec.p (current user has permission). Using this three methods even complicated expression can be created.
Example:
Only 'darkhelmet' user is authorized and all user belonging to 'shwartz' role. Jexl expression: sec.u('dearkhelmer) or sec.r('schwartz')
Two types of authorization
Static way, restrict access in the xml dialog definition. Two attributes : 'readonly' and 'hidden' can be enriched with security expression. If expression evaluates to True for current user then attribute is enabled otherwise is disabled. This attributes can be attached to fields in the form, columns in the list and buttons. If 'hidden' is enabled then simple the element is not visible and not accessible. If 'readonly' is enabled then the element is visible but cannot be modified or clicked (in case of button).
Dynamic way, from 'jython' code. Authorization can be also enforced in 'jython' code at the server side just allowing to apply any logic to authorization rule. Examples:
from guice import ServiceInjector

def dialogaction(action,var) :
  iSec = ServiceInjector.constructSecurity()
  token = var["SECURITY_TOKEN"]
  ok = iSec.isAuthorized(token,"sec.u('darkhelmet')")
  var["OK"] = ok
Tests
 Both types of tests: GUI testing using Boa/Selenium framework (test cases) and Junit test cases have been created for security enhancement.
Future, next steps
It is the first version of security enhancement. Further development:

  • 'Hidden'. Current version simple applies 'hidden' attribute to HTML element. It is not rendered but is visible in HTML code. Future version will contain 'superhidden' attribute. Such a field will not be included in the HTML code.
  • Current version stored password at the server side in plain text. It should be encoded in real production code.
  • More attributes covered with authorization.