Search code examples
javaplayframeworkdeadboltdeadbolt-2

Deadbolt 2 and Play 2.4.x: Null pointer exception on unaccessible pages


I'm trying to implement a simple autentication system in Play Framework 2.4.x using Deadbolt 2.

I've followed this guide written by Chaloner and I've implemented my deadbolt handler as you can see here:

public class MyDeadboltHandler extends AbstractDeadboltHandler {

    public F.Promise<Optional<Result>> beforeAuthCheck(Http.Context context) {
        // returning null means that everything is OK.  Return a real result if you want a redirect to a login page or
        // somewhere else
        return F.Promise.promise(Optional::empty);
    }

    public F.Promise<Optional<Subject>> getSubject(Http.Context context) {
        // in a real application, the user name would probably be in the session following a login process
        User user = new User("MyUser", "[email protected]");
        return F.Promise.promise(() -> Optional.ofNullable(user));
    }

    public F.Promise<Optional<DynamicResourceHandler>> getDynamicResourceHandler(Http.Context context) {
        return F.Promise.promise(() -> Optional.of(new MyDynamicResourceHandler()));
    }

    @Override
    public F.Promise<Result> onAuthFailure(final Http.Context context, final String content) {
        // you can return any result from here - forbidden, etc
        return F.Promise.promise(() -> Controller.redirect(routes.Application.index()));
    }
}

My main controller is this:

public class Application extends Controller {

    public Result index() {
        return ok(index.render());
    }

    @SubjectPresent
    public Result denyAccess(){
        return ok(notAllowed.render());
    }

    public Result permitAccess(){
        return ok(allowed.render());
    }

    public Result errorPage(){
        return ok(errorPage.render());
    }
}

The problem appears when I try to access the page rendered by the action denyAccess. In this case I get a NullPointerException on page without a stack trace I can read, as you can see in the following image.

enter image description here

It seems that the method onAuthFailure is never invoked even when I try to access the controller denyAccess.

You can see the complete project here on my github page, it's very short and I think it can help you to understand the problem.

Thank you for your help.


Solution

  • The problem lies in your implementation of HandlerCache:

    @Singleton
    public class MyHandlerCache implements HandlerCache {
    
        private final Map<String, DeadboltHandler> handlers = new HashMap<>();
    
        public MyHandlerCache() {
            handlers.put("DEFAULT_KEY", new MyDeadboltHandler());
        }
    
        @Override
        public DeadboltHandler apply(final String key) {
            return handlers.get(key);
        }
    
        @Override
        public DeadboltHandler get() {
            return handlers.get("DEFAULT_KEY");
        }
    }
    

    By default, the default handler key name is defined by be.objectify.deadbolt.java.ConfigKeys.DEFAULT_HANDLER_KEY but in MyHandlerCache you use "DEFAULT_KEY". However, when this method is called:

    public DeadboltHandler apply(final String key) {
        return handlers.get(key);
    }
    

    it will receive be.objectify.deadbolt.java.ConfigKeys.DEFAULT_HANDLER_KEY as a key and return null.

    I'll make sure this is logged better and made clear in the documentation. To fix your implementation, replace "DEFAULT_KEY" with be.objectify.deadbolt.java.ConfigKeys.DEFAULT_HANDLER_KEY:

    @Singleton
    public class MyHandlerCache implements HandlerCache {
    
        private final Map<String, DeadboltHandler> handlers = new HashMap<>();
    
        public MyHandlerCache() {
            handlers.put(ConfigKeys.DEFAULT_HANDLER_KEY, new MyDeadboltHandler());
        }
    
        @Override
        public DeadboltHandler apply(final String key) {
            return handlers.get(key);
        }
    
        @Override
        public DeadboltHandler get() {
            return handlers.get(ConfigKeys.DEFAULT_HANDLER_KEY);
        }
    }