2 Replies Latest reply on Oct 29, 2015 6:21 AM by sevage

    Wildfly JAAS authorization

    fpezzati

      Hello,

       

      I'm trying to understand how Wildfly and JAAS works together. I made a simple pure JAAS custom LoginModule using some custom Principals too. I can login succesfully but I can't get Subject Roles working as expected. When I check for SecurityContext getUserPrincipal I always get back a null value. I'm using BASIC authentication. Here is my custom login module:

       

      package it.bytebear.jaas.mongo.module;

       

      import java.io.IOException;

      import java.security.Principal;

      import java.util.Map;

       

      import javax.inject.Inject;

      import javax.security.auth.Subject;

      import javax.security.auth.callback.Callback;

      import javax.security.auth.callback.CallbackHandler;

      import javax.security.auth.callback.NameCallback;

      import javax.security.auth.callback.PasswordCallback;

      import javax.security.auth.callback.UnsupportedCallbackException;

      import javax.security.auth.login.LoginException;

      import javax.security.auth.spi.LoginModule;

       

      import org.bson.Document;

      import org.slf4j.Logger;

      import org.slf4j.LoggerFactory;

       

      import com.mongodb.client.FindIterable;

       

      public class MongoLoginModule implements LoginModule {

       

        @Inject

        protected MongoDB mongoDb;

        protected Subject subject;

        protected Principal identity;

        protected boolean loginOk;

       

        private CallbackHandler callbackHandler;

        private Map sharedState;

        private Map options;

       

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

       

        public boolean abort() throws LoginException {

        log.info("abort!");

        subject = null;

        return true;

        }

       

        public boolean commit() throws LoginException {

        // TODO Auto-generated method stub

        log.info("commit!");

        try {

        if(loginOk) {

        UserGroup userGroup = new UserGroup("Roles");

        userGroup.addMember(new RolePrincipal("userA"));

        subject.getPrincipals().add(userGroup);

        subject.getPublicCredentials().add(userGroup);

        return true;

        }

        } catch (Throwable e) {

        log.error("Error while committing.", e);

        }

        return false;

        }

       

        public void initialize(Subject subject, CallbackHandler callbackHandler,

        Map<String, ?> sharedState, Map<String, ?> options) {

        log.info("Initializing MongoLoginModule.");

        this.subject = subject;

        this.callbackHandler = callbackHandler;

        this.sharedState = sharedState;

        this.options = options;

        }

       

        public boolean login() throws LoginException {

        log.info("login requested.");

        NameCallback nameCallback = new NameCallback("username:");

        PasswordCallback passwordCallback = new PasswordCallback("password:", false);

        try {

        callbackHandler.handle(new Callback[]{nameCallback, passwordCallback});

        String username = nameCallback.getName();

        String password = new String(passwordCallback.getPassword());

        log.info("check credentials for: "+username);

        if(username.equals("jim") && password.equals("jim")) {

        loginOk = true;

        identity = new UserPrincipal(username);

        subject.getPrincipals().add(identity);

        subject.getPublicCredentials().add(identity);

        return true;

        }

        } catch (IOException e) {

        e.printStackTrace();

        } catch (UnsupportedCallbackException e) {

        e.printStackTrace();

        }

       

        return false;

        }

       

       

        public boolean logout() throws LoginException {

        if(subject != null && identity != null) {

        subject.getPrincipals().remove(identity);

        return true;

        }

        return false;

        }

       

        public Document getUserByName(String userName) {

        FindIterable<Document> results = mongoDb.getCollection().find(new Document("username", userName));

        return results.iterator().next();

        }

       

        public void getRoles() {

      // FindIterable<Document> results = mongoDb.getCollection().find(new Document("username", userName));

      // results.iterator().next().get

        }

      }

       

      And here is the RESTFul service I use to test my login module:

       

      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.MediaType;

      import javax.ws.rs.core.Request;

      import javax.ws.rs.core.Response;

      import javax.ws.rs.core.Response.Status;

       

      import org.slf4j.Logger;

      import org.slf4j.LoggerFactory;

       

       

      @Path("/service")

      @Stateless

      public 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();

        }

      }

       

      As I succesfull login (I can debug my login module), I can't get resource at URL http://localhost:8080/MongoWebTest/services/service/userA I always get a 403 error. That URL is allowed to users who got the role "userA" and I put a Principal named "userA" into the "Roles" group of the subject. What am I missing? Do you have some docs where I can learn more about authentication and authorization with JAAS and Wildfly? Thanks.

        • 1. Re: Wildfly JAAS authorization
          sevage

          Hello!

          While ago I had a similar issue. I had to dynamically change the Principal based on context data in EJB invocation. What worked for me was setting the authenticated subject in the security context:

           

          SecurityContextAssociation.getSecurityContext().getSubjectInfo().setAuthenticatedSubject(s);

           

          and then when You need to check the principal:

           

          SecurityContext securityContext = SecurityContextAssociation.getSecurityContext();

          if (securityContext.getSubjectInfo().getAuthenticatedSubject() != null)

            {

             for (Principal p : securityContext.getSubjectInfo().getAuthenticatedSubject().getPrincipals())

            { ... }

          }

           

          Hope it will help You.

          • 2. Re: Wildfly JAAS authorization
            fpezzati

            Hello Andrzej,

             

            I'm using RESTeasy security in my web.xml:

             

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


            am I messing EJB security with RESTEasy security? Thanks for your reply.

            • 3. Re: Wildfly JAAS authorization
              sevage

              My experience with Wildfly is rather basic as I began the journey to migrate from Jboss 6.0 to WF 9.0.1 two weeks ago.

              As I read about the SecurityContext it is ThreadLocal based, so I guess that if You authenticate the subject in the commit method it should work.

              I don't have the experience with the REST services, but authenticating the subject in a way I wrote should not be a problem.

               

              Let me know if that worked.