1
votes

I'm playing with Wildfly-9.0.1.Final and JAAS but I'm not having so much fun.. I implemented my custom login module:

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!");
    if(loginOk) {
        UserGroup userGroup = new UserGroup("Roles");
        userGroup.addMember(new RolePrincipal("userA"));
        subject.getPrincipals().add(userGroup);
        subject.getPublicCredentials().add(userGroup);
        return true;
    }
    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
}

It's not perfect but it's enought for now. This pure JAAS login module is a module in my Wildfly. I configure the security domain this way:

<security-domain name="MongoLoginRealm" cache-type="default">
    <authentication>
        <login-module code="it.bytebear.jaas.mongo.module.MongoLoginModule" flag="required" module="login.mongodb">
            <module-option name="mongodb.uri" value="mongodb://localhost:21017/test?collection"/>
        </login-module>
    </authentication>
</security-domain>

I implemented some RESTful web service to do some test. I'm only posting the relevant code:

...

@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());
        }
        userCredentials.setRoles((String[]) roles.toArray());
        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();
    }
}
...

In web.xml auth-method is BASIC and realm-name is MongoLoginRealm, the same used in jboss-web.xml and when instantiating LoginContext. When I invoke the login method I got this exception:

22:39:49,421 ERROR [it.bytebear.web.mongo.UserServices] (default task-1) login fails.: javax.security.auth.login.LoginException: impossibile trovare la classe Login
Module: it.bytebear.jaas.mongo.module.MongoLoginModule from [Module "deployment.MongoWebTest.war:main" from Service Module Loader]
    at javax.security.auth.login.LoginContext.invoke(LoginContext.java:822)
    at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:698)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:696)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:695)
    at javax.security.auth.login.LoginContext.login(LoginContext.java:594)
    at it.bytebear.web.mongo.UserServices.login(UserServices.java:66)

My login module is never been executed. What am I missing?

UPDATE: Moving the login module .jar into the web application it works. Maybe there is something wrong in my Wildfly. Here is my module structure:

%wildfly_home%/modules/system/layers/base/login/mongodb/main/module.xml             
%wildfly_home%/modules/system/layers/base/login/mongodb/main/mongo-java-driver-3.0.3.jar
%wildfly_home%/modules/system/layers/base/login/mongodb/main/MongoLoginModule.jar

and this is the module.xml:

<module xmlns="urn:jboss:module:1.1" name="login.mongodb">
    <resources>
        <resource-root path="MongoLoginModule.jar"/>
        <resource-root path="mongo-java-driver-3.0.3.jar"/>
    </resources>
    <dependencies>
        <module name="org.apache.log4j"/>
        <module name="javax.api"/>
        <module name="org.slf4j"/>
    </dependencies>
</module>

I have to figure out why the login module isn't reachable as Wildfly module.

2
How did you packaged and installed the server module ("login.mongodb") which contains your custom LoginModule? You could try at the first step to avoid using server modules and include the custom login module within your application WAR (in such case remove the module attribute from security domain configuration in standalone.xml). - kwart
I did the test you suggest just before reading your comment. I include the login module jar in the web application and removed module attribute from my standalone.xml login-module node. It works, now I have to figure out why the login module isn't reachable as Wildfly module. I updated my question. - Francesco

2 Answers

3
votes

It's probably an issue (or feature) of the WildFly. I've reported it as WildFly JIRA WFLY-5569.

You can still use standard servlet authentication which works correctly.

Add WEB-INF/jboss-web.xml file to your deployment:

<jboss-web>
    <security-domain>MongoLoginRealm</security-domain>
</jboss-web>

Adjust your code to use HttpServletRequest.login(String, String) method

@Context
private HttpServletRequest req;

@POST
@Path("/login")
@PermitAll
@Consumes(MediaType.APPLICATION_JSON)
public Response login(User userCredentials) {
    try {
        req.login(userCredentials.getUserName(),
            userCredentials.getPassword());
        Subject subject = org.jboss.security.SecurityContextAssociation.getSubject();
        Optional<Group> rolesGroup = subject.getPrincipals(Group.class).stream().filter(p -> "Roles".equals(p.getName()))
                .findFirst();
        if (rolesGroup.isPresent()) {
            List<String> roleNames = Collections.list(rolesGroup.get().members()).stream().map(p -> p.getName())
                    .collect(Collectors.toList());
            // ...
        } else {
            // ...
        }
    } catch (ServletException e) {
        log.error("login fails.", e);
        return Response.status(Status.FORBIDDEN).entity("Not logged")
            .type(MediaType.APPLICATION_JSON_TYPE).build();
    }
}
1
votes

My application can't find the module because I forgot to add Dependency to it's MANIFEST or to declare login.mongodb as a global module in Wildfly. Thanks @kwart for your suggestion and your answer, you point me to the right direction.