Atom Feed SITE FEED   ADD TO GOOGLE READER

Type safety bites me right in the ass!

Premise:
I'm doing some J2EE boilerplate coding to get our authentication system working again. To get started, here's the API of interest.
public class HTTPSession {
public void setAttribute(String name, Object value);
public Object getAttribute(String name);
.....
}

public class UsernamePasswordHandler {
public UsernamePasswordHandler(String username, char[] password);
public UsernamePasswordHandler(String username, Object credential);
}


So far, so good. Now to keep the user authenticated between HTTP requests, I keep their username and password in the user's session. This might be a bad practice, but I couldn't come up with a reasonable alternative. I need to access login on every request, since JBoss authenticates against the calling thread and I'm not guaranteed to get the same thread for different HTTP requests by the same user.

The seemingly harmless code
    private static LoginContext tryLoginToThread(HttpServletRequest request) throws LoginException{
String principal = (String)request.getSession().getAttribute("principal");
Object credential = request.getSession().getAttribute("credential");
UsernamePasswordHandler userLogin = new UsernamePasswordHandler(principal, credential);
LoginContext loginContext = new LoginContext("client-login", userLogin);
loginContext.login();
return loginContext;
}


The problem?
When I run this, my login() method doesn't do anything, but it's not supposed to. It doesn't throw an exception if login fails because in JBoss your login isn't actually authorized until necessary. This isn't a horrible design but it did provide some nice indirection in debugging this problem.

The problem?
Here's a bit of the implementation of JBoss' UsernamePasswordHandler class:
    if(password == null && credential != null) {
password = credential.toString().toCharArray();
}


The problem!
The password I'm using isn't what I think it is. It's supposed to be riders05cfl but in reality its [C@92d342. That's what you get when you call "riders05cfl".toCharArray().toString().
Although I was calling the constructor with a String and a char[], the compiler didn't call UsernamePasswordHandler(String,char[]). Since it didn't know my credentials object was a character array, it called the other constructor: UsernamePasswordHandler(String,Object). By using the wrong constructor, my password is silently changed from "riders05cfl" to "C@92d342" and it sucks to be me. For reference, here's the fixed code. Note the type of the credentials object.
    private static LoginContext tryLoginToThread(HttpServletRequest request) throws LoginException{
String principal = (String)request.getSession().getAttribute("principal");
char[] credential = (char[])request.getSession().getAttribute("credential");
UsernamePasswordHandler userLogin = new UsernamePasswordHandler(principal, credential);
LoginContext loginContext = new LoginContext("client-login", userLogin);
loginContext.login();
return loginContext;
}


Yuck. The moral of the story:
it doesn't matter what the object is, it only matters what the compiler thinks it is.