Enforcing multiple levels of page access in Wicket

In Wicket when you want to require user login for some pages, you typically inherit the pages from a superclass called say ProtectedPage and then use a wicket SimplePageAuthorizationStrategy to protect these pages as follows:

getSecuritySettings().setAuthorizationStrategy(new SimplePageAuthorizationStrategy(ProtectedPage.class, LoginPage.class) {
  @Override
  protected boolean isAuthorized() {
    return ((ApplicationSession) Session.get()).getLoggedInUser() != null;
  }
});

This will cause any access to a protected page by a non-logged-in user to be redirected to the login page first.

In the real world, its likely however, that your application will also require administrator privileges for access to certain pages. To do this, you need to use a wicket CompoundAuthorizationStrategy as follows:

  CompoundAuthorizationStrategy pageAuthorizationStrategy = new CompoundAuthorizationStrategy();
  pageAuthorizationStrategy.add(new SimplePageAuthorizationStrategy(AdminPage.class, AccessDeniedPage.class) {
    @Override
     protected boolean isAuthorized() {
       return (((ApplicationSession) Session.get()).getLoggedInUser() != null) && (((ApplicationSession) Session.get()).getLoggedInUser().isAdministrator());
     }
   });
  pageAuthorizationStrategy.add(new SimplePageAuthorizationStrategy(ProtectedPage.class, LoginPage.class) {
    @Override
     protected boolean isAuthorized() {
       return ((ApplicationSession) Session.get()).getLoggedInUser() != null;
     }
   });
   getSecuritySettings().setAuthorizationStrategy(pageAuthorizationStrategy);

The above requires the user to be logged in to access any page inherited from ProtectedPage and to be both logged in AND be an administrator to access any page inherited from AdminPage.

Thanks to selckin for the tip!

Embedded Jetty instead of Tomcat

I recently used embedded Jetty for a small REST server project and it was a revelation. It took exactly no time to get it running and now I can deploy web-apps just by starting a runnable jar. That’s reason enough – no more Tomcat with all the complexity of sharing a single server amongst multiple apps, but, even more importantly, its wonderful for development. No more fooling around with Tomcat in Eclipse – just debug web-apps directly as a Java application.

We run all of our apps as multiple instances behind a load-balancer (HAProxy), so its convenient to run each app on a separate port and let the load-balancer handle the forwarding from port 80.

JDBC vs IBATIS vs Hibernate

Christoph has spent the last couple of weeks refactoring a web project to use IBATIS instead of Hibernate. I also refactored a smaller project to use IBATIS instead of JDBC. Here’s our take on the issues:

If you like JDBC and PreparedStatements, you’ll feel comfortable with IBATIS. It doesn’t force any major changes on you and reduces the lines of boilerplate code you’ll need for your database access. It has other benefits as well, such as automatically handling boolean and date mappings which are tedious and error-prone in JDBC.

We had successfully used Hibernate in a number of other projects, but in this particular project, Hibernate turned out to be an awkward fit. The problem was that we already had a database model which we couldn’t change and furthermore, we were porting an existing code base. This combination proved overly complex with Hibernate and we eventually redid the DAO in IBATIS, resulting in a much simpler port.

In summary, if you’re having difficulties integrating Hibernate into your application, consider using IBATIS instead – you get many of the benefits of ORM without the paradigm shifts which Hibernate implies.

LDAPAuthentication with Active Directory

Here’s something I had to piece together myself from various code fragments. Its a class to authenticate a user in Windows Active Directory environment using LDAP. It first locates the domain controllers (DNS lookup of SRV records for _ldap._tcp.domain), parses out the server part and then tries to authenticate the user against a domain controller.

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.naming.AuthenticationException;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

import com.sun.jndi.ldap.LdapCtxFactory;

/**
* LDAPAuthentication class for authenticating Microsoft Active Directory users
*
* If the user or password is wrong, you’ll get an AuthenticationException If
* none of the domain controllers are reachable, you’ll get a
* CommunicationException. If a domain controller cannot be located (via DNS)
* you’ll get a NamingException.
*
* @author Roger Armstrong, Armstrong Consulting GmbH
*
*/
public class LDAPAuthentication {
public static void authenticateUser(String user, String password, String domain) throws AuthenticationException, NamingException {
List ldapServers = findLDAPServersInWindowsDomain(domain);
if (ldapServers.isEmpty())
throw new NamingException(“Can’t locate an LDAP server (try nslookup type=SRV _ldap._tcp.” + domain + “)”);

Hashtable props = new Hashtable();
String principalName = user + “@” + domain;
props.put(Context.SECURITY_PRINCIPAL, principalName);
props.put(Context.SECURITY_CREDENTIALS, password);
Integer count = 0;
for (String ldapServer : ldapServers) {
try {
count++;
LdapCtxFactory.getLdapCtxInstance(“ldap://” + ldapServer, props);
return;
} catch (CommunicationException e) { // this is what’ll happen if one of the domain controllers is unreachable
if (count.equals(ldapServers.size())) {
// we’ve got no more servers to try, so throw the CommunicationException to indicate that we failed to reach an LDAP server
throw e;
}
}
}
}

private static List findLDAPServersInWindowsDomain(String domain) throws NamingException {
List servers = new ArrayList();
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, “com.sun.jndi.dns.DnsContextFactory”);
env.put(“java.naming.provider.url”, “dns:”);
DirContext ctx = new InitialDirContext(env);
Attributes attributes = ctx.getAttributes(“_ldap._tcp.” + domain, new String[] { “SRV” }); // that’s how Windows domain controllers are registered in DNS
Attribute a = attributes.get(“SRV”);
for (int i = 0; i < a.size(); i++) { String srvRecord = a.get(i).toString(); // each SRV record is in the format "0 100 389 dc1.company.com." // priority weight port server (space separated) servers.add(srvRecord.split(" ")[3]); } ctx.close(); return servers; } } [/sourcecode]