Search code examples
javaauthenticationrestlet

Fine-grained Authentication with RESTlet


I want to expose a resource using RESTlet with a fine-grained authentication. My ServerResource should be accessable via GET only for authenticated members (using BASIC Authentication). However, requests using POST should be available also for callers without any authentication.

In order to clearify: http://path/myapp/user should allow anyone to register using POST, but only registered members should be able to GET a list of all users.

I'm unfortunately not much into RESTlet and I only find examples using coarser authentication for whole Restlets or Routers.

So how do I enable optional authentication for resources and check them on a per-method level?

Thanks in advance!


Solution

  • To do basic authentication in RESTlet 2.0 (I assume you're using 2.0 since you mention ServerResource), you need to use a ChallengeAuthenticator. If this is configured with optional = true then authentication will only be requested if you invoke ChallengeAuthenticator.challenge().

    You can create your application with an authenticate() method, and call this whenever you need access to a resource to be secured:

    Application:

    package example;
    
    import org.restlet.*;
    import org.restlet.data.ChallengeScheme;
    import org.restlet.routing.Router;
    import org.restlet.security.*;
    
    public class ExampleApp extends Application {
    
        private ChallengeAuthenticator authenticatior;
    
        private ChallengeAuthenticator createAuthenticator() {
            Context context = getContext();
            boolean optional = true;
            ChallengeScheme challengeScheme = ChallengeScheme.HTTP_BASIC;
            String realm = "Example site";
    
            // MapVerifier isn't very secure; see docs for alternatives
            MapVerifier verifier = new MapVerifier();
            verifier.getLocalSecrets().put("user", "password".toCharArray());
    
            ChallengeAuthenticator auth = new ChallengeAuthenticator(context, optional, challengeScheme, realm, verifier) {
                @Override
                protected boolean authenticate(Request request, Response response) {
                    if (request.getChallengeResponse() == null) {
                        return false;
                    } else {
                        return super.authenticate(request, response);
                    }
                }
            };
    
            return auth;
        }
    
        @Override
        public Restlet createInboundRoot() {
            this.authenticatior = createAuthenticator();
    
            Router router = new Router();
            router.attach("/user", UserResource.class);
    
            authenticatior.setNext(router);
            return authenticatior;
        }
    
        public boolean authenticate(Request request, Response response) {
            if (!request.getClientInfo().isAuthenticated()) {
                authenticatior.challenge(response, false);
                return false;
            }
            return true;
        }
    
    }
    

    Resource:

    package example;
    
    import org.restlet.data.MediaType;
    import org.restlet.representation.EmptyRepresentation;
    import org.restlet.representation.Representation;
    import org.restlet.representation.StringRepresentation;
    import org.restlet.resource.ServerResource;
    
    public class UserResource extends ServerResource {
    
        @Override
        public Representation get() {
            ExampleApp app = (ExampleApp) getApplication();
            if (!app.authenticate(getRequest(), getResponse())) {
                // Not authenticated
                return new EmptyRepresentation();
            }
    
            // Generate list of users
            // ...
        }     
    
        @Override
        public Representation post(Representation entity) {
            // Handle post
            // ...
        }
    
    }