views:

599

answers:

1

Hi,

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!

+5  A: 

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
        // ...
    }

}
sea36
First of all thanks for your answer, this looks promising. However, I have some problems getting your code to work. For instance, there is no `getSubject()` method for ClientInfo (I'm using 2.0m7). Also, I'm not sure if your `authenticate()` method is correct?
PartlyCloudy
I was using an earlier snapshot; I've updated the examples to work with 2.0m7.
sea36
Thanks, again, now the code compiles and POST is always available. Unfortunately, GET is never. No matter if I provide no, wrong or correct BASIC credentials, I always get a 401.
PartlyCloudy
I've tried it again and the example works for me using RESTlet 2.0m7. Are you running the example, or trying to adapt it to your code?
sea36
Ok, I'm sorry, your code works fine! Thank you very much for your sustainable help!
PartlyCloudy
Thank you for the example, it was very useful!Just one question: why are you overriding the authenticator to check if the challengeResponse is null?
artgon