0

I have the following routes:

/projects/{projectName}

and

/projects/{projectName}/Wall/{wallName}

Now I'd like to have that all GETs be allowed but PUT, POST, DELETE should only be allowed by project members i.e. users members of that project. I have a special class that given a user id and project name I can get the status of the user's membership - something like MyEnroler.getRole(userId, projectName) - where the userId is part of the request header and the projectName is taken from the URI.

I've tried a number of things but doesn't work. Here's the idea:

public class RoleMethodAuthorizer extends Authorizer {
    @Override
    protected boolean authorize(Request req, Response resp) {
        //If it's a get request then no need for further authorization. 
        if(req.getMethod().equals(Method.GET))
            return true;
        else
        {                               
            String authorEmail = req.getClientInfo().getUser().getIdentifier();
            String projectName = req.getAttributes().get("project").toString();

            Role userRole = MyEnroler.getRole(authorEmail, projectName);

            //forbid updates to resources if done by non-members of project
            if(userRole.equals(MyEnroler.NON_MEMBER))
                return false;
            //for everybody else, return true
            return true;
        }


    }

}

Now simply doing the following completely fails when creating inbound root in the Application:

Router projectRouter = new Router(getContext());
RoleMethodAuthorizer rma = new RoleMethodAuthorizer();
//Guard declaration here. Then setNext Restlet
guard.setNext(projectRouter);
projectRouter.attach("/projects/{project}",rma);

Router wallRouter = new Router(getContext());
wallRouter.attach("/Wall/{wallName}", WallResource.class);

rma.setNext(wallRouter);

//return guard;

So a request to /projects/stackoverflow/Wall/restlet fails. The URL is never found. I'm guessing since it's trying to match it with the projectRouter. Well I tried the various modes (MODE_BEST_MATCH or MODE_FIRST/NEXT_MATCH) to no avail.

Nothing seems to work. Conceptually this should work. I'm only intercepting a call and just being transparent to the request, but don't know how things are working on the inside.

I could move the authorizer just after the guard, but I'd lose access to the request attribute of projectName - I don't wish to parse the URL myself to search for the projectName since the URL pattern could change and would break the functionality - i.e. require 2 changes instead of one.

Any ideas how to achieve this?

PhD
  • 11,202
  • 14
  • 64
  • 112

1 Answers1

2

I would use the standard RoleAuthorizer class to supply the list of allowed roles, along with your custom enroller probably split into two I would then add a custom Filter class that does something like this to call your Enrolers.

protected int beforeHandle(final Request request, final Response response) throws ResourceException {
    final String projectName = (String) request.getAttributes().get("projectName");
    // Check that a projectName is supplied, should not have got this far otherwise but lets check.
    if (projectName == null || projectName.isEmpty()) {
        throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
    }

    if (Method.GET.equals(request.getMethod())){
        new ReadEnroler(projectName).enrole(request.getClientInfo());
    }else{
        new MutateEnroler(projectName).enrole(request.getClientInfo());
    }

    return super.beforeHandle(request, response);
}

the enrolers would then set the appropriate values in the clientInfo.getRoles() Collection when enrole was called.

Caleryn
  • 1,084
  • 3
  • 18
  • 23