Francesco
Francesco

Reputation: 1792

Wildfly, JAAS and SecurityContext

I'm still playin with Wildfly-9.0.1.Final and JAAS (see my previous question Wildfly and JAAS login module) in a web application that use a BASIC auth-method. While my custom login module works I got some problems about authorization. I use a RESTeasy RESTFul web service with annotation to test, here is the code:

package it.bytebear.web.mongo;

import it.bytebear.web.mongo.jaas.MongoModuleCallbackHandler;
import it.bytebear.web.mongo.model.User;

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Path("/service")
Stateless
ublic class UserServices {

    private Logger log = LoggerFactory.getLogger(UserServices.class);

    @GET
    @Path("/userA")
    @RolesAllowed({ "userA" })
    public Response postUserA() {
        return Response.ok("You're user A.", MediaType.TEXT_HTML).build();
    }

    @GET
    @Path("/userB")
    @RolesAllowed({ "userB" })
    public Response postUserB() {
        return Response.ok("You're user B.", MediaType.TEXT_HTML).build();
    }

    @GET
    @Path("/userC")
    @RolesAllowed({ "userC" })
    public Response postUserC() {
        return Response.ok("You're user C.", MediaType.TEXT_HTML).build();
    }

    @POST
    @Path("/login")
    @PermitAll
    @Consumes(MediaType.APPLICATION_JSON)
    // @Consumes("application/x-authc-username-password+json")
    public Response login(User userCredentials) {
        log.info("logging in.");
        try {
            MongoModuleCallbackHandler handler = new MongoModuleCallbackHandler();
            handler.setUsername(userCredentials.getUserName());
            handler.setPassword(userCredentials.getPassword().toCharArray());
            LoginContext loginContext = new LoginContext("MongoLoginRealm", handler);
            loginContext.login();
            Subject subject = loginContext.getSubject();
            List<String> roles = new ArrayList<String>();
            for (Principal p : subject.getPrincipals()) {
                roles.add(p.getName());
            }
            String[] userCredentialsRoles = new String[roles.size()];
            roles.toArray(userCredentialsRoles);
            userCredentials.setRoles(userCredentialsRoles);
            return Response.ok().entity(userCredentials)
                    .type(MediaType.APPLICATION_JSON_TYPE).build();
        } catch (Exception e) {
            log.error("login fails.", e);
            return Response.status(Status.FORBIDDEN).entity("Not logged")
                    .type(MediaType.APPLICATION_JSON_TYPE).build();
        }
    }

    @GET
    @Path("/logout")
    @PermitAll
    public Response logout(Request req) {
        return Response.ok().build();
    }

    @POST
    @Path("/test")
    @PermitAll
    public Response test(@Context SecurityContext ctx) {
        Principal p = ctx.getUserPrincipal();
        return Response.status(Status.OK).entity(p).build();
    }
}

My login module is correctly invoked and generate a subjec with a Group named Roles containing a Principal named userA, but when I try to access .../service/userA I always get a 403 error. I use test method to check subject but ctx.getUserPrincipal() always return null. I miss how LoginModule and SecurityContext works, how SecurityContext knows about a Subject? More important: I'd like to learn more, link to resources and docs will be appreciated.

UPDATE: In my web.xml I'm using RESTEasy security:

...
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
...

Am I messing up EJB security with RESTEasy security?

Upvotes: 0

Views: 4697

Answers (1)

kwart
kwart

Reputation: 3164

Don't implement, configure

I suggest to avoid doing all the JAAS handling programmatically. Just use the application server configuration and security subsystem will take care of all the associations for you.

RestEasy role-based security

RestEasy implements Role-based security. It has to be enabled in application's context parameter "resteasy.role.based.security" in web.xml.

<context-param>
    <param-name>resteasy.role.based.security</param-name>
    <param-value>true</param-value>
</context-param>

If you don't use this parameter, then only the security constraints (in web.xml) are available for you for authorization configuration.

Example

You can take some inspiration from my sample app on GitHub used for basic security testing. There is also a Java package with REST resources.

Still not convinced?

Take a look at following code in the security subsystem implementation of WildFly:

Upvotes: 2

Related Questions