Thursday, July 22, 2010

A JAAS LoginModule for ActiveMQ

In order to let ActiveMQ to use the existing authentication repository, I created a JAAS plugin for ActiveMQ serve the purpose.

1.  change <ActiveMQ_HOME>/conf/activemq.xml to enable JAAS authentication.

<!-- comment out simpleAuthenticationPlugin-->
 <jaasAuthenticationPlugin configuration="activemq-domain" />

2. change <ActiveMQ_HOME>/conf/loging.conf, add login module definition.
activemq-domain {
    com.zeon.auth.CmdJdbcLoginModule Sufficient
                                debug=true
                                com.zeon.db.url="jdbc:mysql://localhost:3306/userdb"
                                com.zeon.db.driver="com.mysql.jdbc.Driver"
                                com.zeon.db.username="user"
                                com.zeon.db.password="pass";
                org.apache.activemq.jaas.PropertiesLoginModule Sufficient
        debug=true
        org.apache.activemq.jaas.properties.user="users.properties"
        org.apache.activemq.jaas.properties.group="groups.properties";
};

3. finally, the class that extended from javax.security.auth.spi.LoginModule

package com.zeon.sync.auth;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

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.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import org.apache.activemq.jaas.GroupPrincipal;
import org.apache.activemq.jaas.UserPrincipal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.zeon.jdbc.JDBCException;
import com.zeon.jdbc.JDBCUtils;
import com.zeon.jdbc.UniqueResultHandler;

public class CmdJdbcLoginModule implements LoginModule {

    private static final Log log = LogFactory.getLog(CmdJdbcLoginModule.class);
    private static final String[] ROLE = {"hs", "users"};
    private Subject subject;
    private CallbackHandler callbackHandler;
    private boolean debug;
    private String user;
    private Set principals;
   
    private JDBCUtils jdbcUtils;

    public CmdJdbcLoginModule() {
        principals = new HashSet();
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler,
            Map sharedState, Map options) {
        this.subject = subject;
        this.callbackHandler = callbackHandler;
       
        debug = "true".equalsIgnoreCase((String) options.get("debug"));
        String url = (new StringBuilder()).append(
                (String) options.get("com.hs.db.url"))
                .append("").toString();
        String username = (new StringBuilder()).append(
                (String) options.get("com.hs.db.username"))
                .append("").toString();
        String password = (new StringBuilder()).append((String)options
                .get("com.zeon.db.password")).append("").toString();
        String driver = (new StringBuilder()).append((String)options
                .get("com.zeon.db.driver")).append("").toString();
       
        Properties props = new Properties();
        props.setProperty("jdbc.user", username);
        props.setProperty("jdbc.password", password);
        props.setProperty("jdbc.driver", driver);
        props.setProperty("jdbc.url", url);

        try {
            jdbcUtils = new JDBCUtils(props);
        } catch (SQLException e) {
            log.error("connect to db failed.", e);
        }
        if (debug)
            log.debug((new StringBuilder()).append("Initialized debug=")
                    .append(debug));
    }

    public boolean login() throws LoginException {
        Callback callbacks[] = new Callback[2];
        callbacks[0] = new NameCallback("Username: ");
        callbacks[1] = new PasswordCallback("Password: ", false);
        try {
            callbackHandler.handle(callbacks);
        } catch (IOException ioe) {
            throw new LoginException(ioe.getMessage());
        } catch (UnsupportedCallbackException uce) {
            throw new LoginException((new StringBuilder()).append(
                    uce.getMessage()).append(
                    " not available to obtain information from user")
                    .toString());
        }
        user = ((NameCallback) callbacks[0]).getName();
        char tmpPassword[] = ((PasswordCallback) callbacks[1]).getPassword();
        if (tmpPassword == null)
            tmpPassword = new char[0];
        String password = null;
        try {
            password = findPassword(user);
            log.debug("=============the password for user " + user + " is " + password);
        } catch (JDBCException e) {
            throw new FailedLoginException("DB access failed.");
        }
        if (password == null)
            throw new FailedLoginException("User does exist");
        if (!password.equals(new String(tmpPassword)))
            throw new FailedLoginException("Password does not match");
        if (debug)
            log.debug((new StringBuilder()).append("login ").append(user)
                    .toString());
        return true;
    }

    @SuppressWarnings("unchecked")
    private String findPassword(String user) throws JDBCException {
        String sql = "select JMSpassword from DoctorSyncMap where DoctorID='" + user + "'";
        String result = (String)jdbcUtils.query(sql, new UniqueResultHandler() {

            @Override
            public Object handle(ResultSet rs) throws JDBCException {
                String result = "";
                try {
                    if (rs.next()) {
                        result = rs.getString(1);
                    }
                } catch (Exception e) {
                    log.error("query db failed.", e);
                    throw new JDBCException(e);
                }
                return result;
            }
           
        });
        return result;
    }

    @SuppressWarnings("unchecked")
    public boolean commit() throws LoginException {
        principals.add(new UserPrincipal(user));
        for (String r : ROLE) {
            principals.add(new GroupPrincipal(r));
        }
        subject.getPrincipals().addAll(principals);
        log.debug("commit");
        return true;
    }

    public boolean abort() throws LoginException {
        user = null;
        if (debug)
            log.debug("abort");
        return true;
    }

    public boolean logout() throws LoginException {
        subject.getPrincipals().removeAll(principals);
        principals.clear();
        if (debug)
            log.debug("logout");
        return true;
    }

}

No comments:

Google+