/******************************************************************************* * Copyright (c), 2001, 2002 N2 Broadband, Inc.  All Rights Reserved.
 *
 * This module contains unpublished, confidential, proprietary
 * material.  The use and dissemination of this material are
 * governed by a license.  The above copyright notice does not
 * evidence any actual or intended publication of this material.
 *
 * Author:  Drake H. Henderson
 * Created:  11-12-01
 *
 ******************************************************************************/
package com.n2bb.security;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import javax.servlet.ServletContext;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.n2bb.user.UserBean;
import com.n2bb.util.DigestUtil;
import com.n2bb.util.N2bbException;
import com.n2bb.util.N2bbSettings;

/**
 * Manages roles for the UI framework.
 *
 * @version $Id: SecurityManager.java,v 1.4 2007/06/22 21:44:50 vjakobac Exp $
 * @see com.n2bb.user.UserManager
 */

public class SecurityManager {
    /*
    Implementation note:  this class manages roles and security framework users,
    that is, users as seen by container-managed authentication.
    See UserManager for management of application-level user data
    (including first name, last name, etc.)
    Upon startup, this class creates the N2BB Admin role,
    adds all J2EE roles to it, creates the admin user,
    and assigns gives it the admin role.
    */

    /*
    Note about role types:
    This class refers to two different kinds of roles.
    The first is the fine-grained J2EE role, which corresponds to a
    role-name entry in a security-constraint in web.xml.  An example
    might be "List Packages".

    The second kind of role is the "big role" or "N2 role",
    a functional role to which users are assigned.  An example would
    be "N2BB Admin" or "Report Viewer".  Each N2 role contains zero or
    more J2EE roles.  (N2BB Admin is a special role that contains all
    J2EE roles.)

    J2EE roles are assigned to N2 roles via the Edit Role screen.
    Each J2EE role appears in the UI as a single "System Permission".
    Permissions are grouped into "areas".  The content of these
    permission "areas" is configured in security.xml or other
    module-specific security config files.
    */

    private static final String NO_ROLE_DEFINED = "no role defined";

    // tables
    private static final String USER_TABLE = "users_realm";
    private static final String BIGROLE_TABLE = "bigRole";
    private static final String BIGROLE_ROLE_TABLE = "bigRole_role";
    private static final String BIGROLE_PERMISSION_TABLE = "TTV_BIGROLEPERMISSION";
    private static final String USER_BIGROLE_TABLE = "user_bigRole";
    private static final String USER_DATA = "user_data";

    private static Logger n2bbLog =Logger
	.getLogger(SecurityManager.class);
    private static SecurityManager secMgr;

    private static List moduleAreaLists =
            Collections.synchronizedList(new LinkedList());

    public static final String DEFAULT_SECURITY_FILE = "security.xml";

    private DataSource ds;

    /**
     * Constructor.
     */
    private SecurityManager() {
    }

    /**
     * Gets UI data source.
     *
     * @return UI data source.
     */
    private DataSource getDataSource() {
        return com.n2bb.util.PropertyManager.getUIDataSource();
    }

    /**
     * Gets the singleton instance of this class, which must be
     * <p/>
     * previously initialized.
     *
     * @return singleton instance of this class
     */
    public static SecurityManager getInstance() {
        n2bbLog.debug("enter");
        if (secMgr == null) {
            n2bbLog.error("security manager not properly initialized");
            n2bbLog.error("  please call init(dataSource, securityConfig) to initialize");
            return null;
        }
        return secMgr;
    }

    /**
     * Nulls out the singleton instance of this class.
     */
    public static void destroy() {
        n2bbLog.debug("enter");
        secMgr = null;
    }

    /**
     * Initializes security manager by loading in all xml files in a given
     * <p/>
     * directory.  If a file named <code>DEFAULT_SECURITY_FILE</code> is found,
     * <p/>
     * it is loaded first.
     *
     * @param servletContext servlet context
     * @param securityDir    directory to find security xml files
     * @return newly created servlet context
     */
    public synchronized static SecurityManager init(ServletContext servletContext,
                                                    String securityDir) {
        n2bbLog.debug("enter");
        if (secMgr != null) { // already initialized

            return secMgr;
        }

        secMgr = new SecurityManager();
        return secMgr;
    }

    /**
     * Creates admin role and user.
     *
     * @throws N2bbException
     * @throws SQLException
     */
    private void createAdminRoleAndUser() throws N2bbException, SQLException {
        createAdminRole();
        createAdminUser();
    }

    /**
     * Creates the default admin role.
     */
    private void createAdminRole() throws N2bbException {
        if (roleExists(N2bbSettings.N2BB_ADMIN_ROLE)) {
            return;
        }
        n2bbLog.info("admin role does not exist; creating...");
        saveRole(N2bbSettings.N2BB_ADMIN_ROLE, "Default System Administrator", new Vector());
    }

    /**
     * Creates the default admin user.
     */
    private void createAdminUser() throws N2bbException, SQLException {
        if (userExists(N2bbSettings.ADMIN_USER)) {
            return;
        }
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            n2bbLog.info("admin user does not exist; creating...");
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            createUser(N2bbSettings.ADMIN_USER, "administrator", "A", N2bbSettings.N2BB_ADMIN_ROLE, con);
            pstmt = con.prepareStatement("insert into " + USER_DATA + " (user_name, firstName, lastName, email, phone) values(?, ?, ?, ?, ?)");
            pstmt.setString(1, N2bbSettings.ADMIN_USER);
            pstmt.setString(2, "Super");
            pstmt.setString(3, "User");
            pstmt.setString(4, "admin@n2bb.com");
            pstmt.setString(5, "");
            pstmt.executeUpdate();
        }
        finally {
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Gives the admin role all permissions from all modules.
     */
    private void insertAdminRoles() throws N2bbException {
        List allWebXMLRoles = new LinkedList();
        for (Iterator it1 = moduleAreaLists.iterator(); it1.hasNext();) {
            ModuleAreaList mal = (ModuleAreaList) it1.next();
            List areaPermissions = mal.getAreaPermissionLists();
            for (Iterator it2 = areaPermissions.iterator(); it2.hasNext();) {
                AreaPermissionList apl = (AreaPermissionList) it2.next();
                List permissions = apl.getPermissions();
                for (Iterator it3 = permissions.iterator(); it3.hasNext();) {
                    AreaPermissionList.Permission perm = (AreaPermissionList.Permission) it3.next();
                    n2bbLog.debug("xml role... " + perm.getWebXMLRole());
                    allWebXMLRoles.add(perm.getWebXMLRole());
                }
            }
        }
        updateRole(N2bbSettings.N2BB_ADMIN_ROLE, "Default System Administrator", allWebXMLRoles);
    }

    /**
     * Loads roles from a config file.
     */
    private void loadRoles(InputStream securityConfigXml)
            throws ParserConfigurationException, SAXException, IOException, N2bbException {
        loadRoleDisplayList(getXMLDocument(securityConfigXml));
    }

    private Document getXMLDocument(InputStream xml)
            throws ParserConfigurationException, SAXException, IOException {
        return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xml);
    }

    /**
     * Loads security roles from XML doc.
     *
     * @param secDoc XML doc containing roles
     * @throws N2bbException
     */
    private void loadRoleDisplayList(Document secDoc) throws N2bbException {
        n2bbLog.debug("enter");
        String module = null;
        try {
            Element security = secDoc.getDocumentElement();
            module = security.getAttribute("display");
            if (isEmpty(module)) {
                n2bbLog.warn("module has no display name in security xml file");
                module = "";
            }
            else {
                n2bbLog.info("processing module... '" + module + "'");
            }
            NodeList areas = security.getChildNodes();
            List areaPermissions = getAreaPermissions(areas);
            n2bbLog.info("permissions list initialized for module... '" + module + "'");
            moduleAreaLists.add(new ModuleAreaList(module, areaPermissions));
        }
        catch (Exception e) {
            n2bbLog.error("error loading permissions for module... '" + module + "'");
            n2bbLog.error(e.getMessage(), e);
            throw new N2bbException("error.other");
        }
    }

    /**
     * Gets list of permissions for all areas in a list.
     *
     * @param areas list of areas
     * @return all permissions for the list of areas
     */
    private List getAreaPermissions(NodeList areas) {
        n2bbLog.debug("enter");
        List areaPermissions = new LinkedList();
        for (int i = 0; i < areas.getLength(); i++) {
            if (!areas.item(i).hasChildNodes()) {
                continue; // will filter out text nodes...
            }
            String areaDis = ((Element) areas.item(i)).getAttribute("display");
            if (isEmpty(areaDis)) {
                continue;
            }
            else {
                n2bbLog.info("processing area... '" + areaDis + "'");
            }
            NodeList roles = areas.item(i).getChildNodes();
            AreaPermissionList permList = new AreaPermissionList(areaDis);
            for (int j = 0; j < roles.getLength(); j++) {
                if (!(roles.item(j) instanceof Element)) {
                    continue;
                }
                Element role = (Element) roles.item(j);
                String webXMLRole = role.getAttribute("webXMLRole");
                String roleDis = role.getAttribute("display");
                if (isEmpty(webXMLRole) || isEmpty(roleDis)) {
                    continue;
                }
                n2bbLog.info("adding permission... " + roleDis + " with webXMLRole... " + webXMLRole);
                permList.addPermission(roleDis, webXMLRole);
            } // end roles for loop
            if (!permList.isEmpty()) {
                areaPermissions.add(permList);
            }
        } // end areas for loop
        return areaPermissions;
    }

    private boolean isEmpty(String s) {
        return s == null || s.trim().equals("");
    }

    /**
     * Gets the list of roles.
     *
     * @return role list
     */
    public Vector getRoleMap() throws N2bbException {
        n2bbLog.debug("enter");
        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;
        Vector roles = new Vector();
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            stmt = con.createStatement();



            // get the n2 roles

            String query = "select * from " + BIGROLE_TABLE;
            rs = stmt.executeQuery(query);
            while (rs.next()) {
                RoleBean rb = new RoleBean();
                rb.setN2RoleName(rs.getString(1));
                rb.setDescription(rs.getString(2));
                rb.setUserCount(getUserCount(rb.getN2RoleName(), con));
                roles.add(rb);
            }
            rs.close();
            stmt.close();
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(stmt);
            closeConnection(con);
        }
        return roles;
    }

    /**
     * Gets number of users in an N2 role.
     */
    int getUserCount(String bigRole, Connection con) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String query = "select count(*) " +
                    " from " + USER_BIGROLE_TABLE +
                    " where bigRole = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, bigRole);
            ResultSet rs = pstmt.executeQuery();
            rs.next();
            return rs.getInt(1);
        }
        finally {
            closeStatement(pstmt);
        }
    }

    /**
     * Saves an N2 role.
     *
     * @param name        role name
     * @param description role description
     * @param roles       J2EE roles (permissions) belonging to this role
     */
    public void saveRole(String name, String description, List roles) throws N2bbException {
        n2bbLog.debug("enter n2 role... " + name);
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }



            // check duplicate role name

            if (roleExists(name)) {
                n2bbLog.warn("n2 role already exists... " + name);
                throw new N2bbException("error.securityManager.duplicateN2RoleName");
            }
            try {
                con.setAutoCommit(false);



                // insert n2 role name into permissions table

                pstmt = con.prepareStatement("insert into  " + BIGROLE_TABLE + "  (bigrole, description) values(?, ?)");
                pstmt.setString(1, name);
                pstmt.setString(2, description);
                pstmt.executeUpdate();
                pstmt.close();



                // map n2 role name to granular role names

                pstmt = con.prepareStatement("insert into  " + BIGROLE_ROLE_TABLE + " (bigrole, role) values(?,?)");
                pstmt.setString(1, name);
                for (Iterator iterator = roles.iterator(); iterator.hasNext();) {
                    String role = (String) iterator.next();
                    n2bbLog.debug("role... " + role);
                    pstmt.setString(2, role);
                    pstmt.executeUpdate();
                }



                // every role gets

                n2bbLog.debug("base role... " + N2bbRoleConsts.baseRole);
                pstmt.setString(2, N2bbRoleConsts.baseRole);
                pstmt.executeUpdate();
                pstmt.close();
                con.commit();
                con.setAutoCommit(true);
            }
            catch (Exception e) {
                n2bbLog.error(e.getMessage(), e);
                con.rollback();
                con.setAutoCommit(true);
                throw new N2bbException("error.other");
            }
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Deletes a "big" role.
     *
     * @param n2RoleName "big" role name
     */
    public void deleteRole(String n2RoleName) throws N2bbException {
        n2bbLog.debug("n2 role... " + n2RoleName);
        Connection con = null;
        PreparedStatement pstmt = null;
        String query = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }

           try {
                con.setAutoCommit(false);
                // delete n2 role to Role Permissions  mappings
                query = "delete from " + BIGROLE_PERMISSION_TABLE + " where bigRole=?";
                pstmt = con.prepareStatement(query);
                pstmt.setString(1, n2RoleName);
                pstmt.executeUpdate();
                pstmt.close();
                n2bbLog.debug("deleted from TTV_BIGROLEPERMISSION");
                // delete  Role from BIGROLE table
                query = "delete from " + BIGROLE_TABLE + " where bigRole=?";
                pstmt = con.prepareStatement(query);
                pstmt.setString(1, n2RoleName);
                pstmt.executeUpdate();
                pstmt.close();
                n2bbLog.debug("deleted from BIGROLE");
                con.commit();
                con.setAutoCommit(true);
            }
            catch (Exception e) {
                n2bbLog.error(e.getMessage(), e);
                con.rollback();
                con.setAutoCommit(true);
                throw new N2bbException("error.other");
            }
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Updates an N2 role.
     *
     * @param name        N2 role name
     * @param description description
     * @param roles       J2EE roles to assign to the N2 role
     */
    public void updateRole(String name, String description, List roles) throws N2bbException {
        n2bbLog.debug("role... " + name);
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            try {
                con.setAutoCommit(false);

               // update n2 role

                String query = "update " + BIGROLE_TABLE + " set description=? where bigRole=?";
                pstmt = con.prepareStatement(query);
                pstmt.setString(1, description);
                pstmt.setString(2, name);
                pstmt.executeUpdate();
                pstmt.close();



                // delete n2 role to role mappings

                query = "delete from " + BIGROLE_ROLE_TABLE + " where bigRole=?";
                pstmt = con.prepareStatement(query);
                pstmt.setString(1, name);
                pstmt.executeUpdate();
                pstmt.close();



                // map n2 role to new roles

                roles = new ArrayList(roles); // in case original list is fixed-size
                roles.add(N2bbRoleConsts.baseRole);
                query = "insert into " + BIGROLE_ROLE_TABLE + " (bigrole, role)values (?,?)";
                pstmt = con.prepareStatement(query);
                pstmt.setString(1, name);
                for (Iterator iterator = roles.iterator(); iterator.hasNext();) {
                    String role = (String) iterator.next();
                    n2bbLog.info("mapping " + name + " to " + role);
                    pstmt.setString(2, role);
                    pstmt.executeUpdate();
                }
                con.commit();
                con.setAutoCommit(true);
            }
            catch (Exception e) {
                n2bbLog.error(e.getMessage(), e);
                con.rollback();
                con.setAutoCommit(true);
                throw new N2bbException("error.other");
            }
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Gets a role for editing.
     *
     * @param name role name
     * @return role form, with name, description, and permissions set
     */
    public RoleForm getRole(String name) throws N2bbException {
        n2bbLog.debug("enter n2 role... " + name);
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }



            // get the role's description

            String query = "select description from " + BIGROLE_TABLE + " where bigRole=?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            String description = "";
            if (rs.next()) {
                description = rs.getString(1);
            }
            rs.close();
            pstmt.close();



            // get roles for the n2 role

            query = "select role from " + BIGROLE_ROLE_TABLE + " where bigRole=?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            List permissions = new Vector();
            while (rs.next()) {
                permissions.add(rs.getString(1));
            }
            rs.close();
            pstmt.close();
            RoleForm form = new RoleForm();
            form.setN2RoleName(name);
            form.setDescription(description);
            form.setPermissions((String[]) permissions.toArray(new String[0]));
            return form;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Checks if a role exists.
     *
     * @param name role name
     * @return true if role exists.
     */
    private boolean roleExists(String name) throws N2bbException {
        n2bbLog.debug("enter n2 role... " + name);
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        boolean bExists = false;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            String query = "select * from " + BIGROLE_TABLE + " where bigRole=?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            bExists = rs.next();
            rs.close();
            pstmt.close();
            return bExists;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Gets module-specific permission areas.
     */
    public List getModuleAreaLists() {
        return moduleAreaLists;
    }

    /**
     * Gets N2 roles names.
     */
    public Vector getRoleNames() throws N2bbException {
        n2bbLog.debug("enter");
        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;
        Vector n2RoleVector = new Vector();
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            stmt = con.createStatement();



            // get the n2 roles

            String query = "select bigRole from " + BIGROLE_TABLE;
            rs = stmt.executeQuery(query);
            while (rs.next()) {
                n2RoleVector.addElement(rs.getString(1));
            }
            rs.close();
            stmt.close();
            return n2RoleVector;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(stmt);
            closeConnection(con);
        }
    }

    /**
     * Creates user.
     *
     * @param name   user name
     * @param pass   unencrypted password
     * @param n2Role "big" role for user
     * @param con    database connection
     */
    public void createUser(String name, String pass, String status, String n2Role,
                           Connection con) throws N2bbException {
        /* A connection is passed in so that individual apps

          can control rollbacks/commits of data */

        n2bbLog.debug("enter user... " + name);
        PreparedStatement pstmt = null;
        try {
            // insert user name and password into USER_TABLE

            n2bbLog.debug("insert user, password, and status");
            pstmt = con.prepareStatement("insert into " + USER_TABLE + " (user_name, user_pass, password_modified_date, status)values(?, ?, ?, ?)");
            pstmt.setString(1, name);

            // pstmt.setString(2, RealmBase.Digest(pass, "SHA"));
            pstmt.setString(2, DigestUtil.Digest(pass, "SHA"));
            pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
            int istat = 0;
            if (status.equalsIgnoreCase("Active") || status.equalsIgnoreCase("A")) {
                istat = 1;
            }
            pstmt.setInt(4, istat);
            pstmt.executeUpdate();
            pstmt.close();



            // insert user name and n2 role into USER_PERMISSIONS_TABLE

            n2bbLog.debug("insert user and n2 role");
            pstmt = con.prepareStatement("insert into " + USER_BIGROLE_TABLE + "(user_name, bigrole) values(?, ?)");
            pstmt.setString(1, name);
            pstmt.setString(2, n2Role);
            pstmt.executeUpdate();
            pstmt.close();
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
        }
    }

    /**
     * Updates a user in the security table, keeping the old password.
     */
    public void updateUser(String username, String status, String n2Role,
                           Connection con) throws N2bbException {
        UserSecurityData securityData =
                getUserSecurityData(username);

        // note: password is encrypted

        String password = securityData.getPassword();
        updateUser(username, "temp", status, n2Role, con);

        // don't re-encrypt password

        setPassword(username, password, false, con);
    }

    /**
     * Updates a user in the security table, setting a new password.
     */
    public void updateUser(String name, String pass, String status, String n2Role,
                           Connection con) throws N2bbException {
        /* Implementation note:

           Since changing n2Role involves updating user-role table,

           it's easiest just to delete and re-create user. */

        deleteUser(name, con);
        createUser(name, pass, status, n2Role, con);
    }

    /**
     * ************************************
     * <p/>
     * deletes user within framework tables - a connection is passed in so that individual
     * <p/>
     * apps can control rollbacks/commits of data
     * <p/>
     * *************************************
     */
    public void deleteUser(String name, Connection con) throws N2bbException {
        n2bbLog.debug("enter user... " + name);
        PreparedStatement pstmt = null;
        try {
            // delete user role entries

            n2bbLog.debug("delete user role mappings");
            String query = "delete from " + USER_BIGROLE_TABLE + " where user_name=?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            pstmt.executeUpdate();
            pstmt.close();



            // delete user entry

            n2bbLog.debug("delete user");
            pstmt = con.prepareStatement("delete from " + USER_TABLE + " where user_name=?");
            pstmt.setString(1, name);
            pstmt.executeUpdate();
            pstmt.close();
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
        }
    }

    /**
     * Gets the password, status, and role for a user.
     *
     * @param name username
     * @return security data
     */
    public UserSecurityData getUserSecurityData(String name) throws N2bbException {
        n2bbLog.debug("enter");
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }



            // get password and status

            String query = "select user_pass, status from " + USER_TABLE;
            query += " where user_name = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            if (!rs.next()) {
                n2bbLog.error("user not found");
                throw new N2bbException("error.securityManager.userNotFound");
            }
            UserSecurityData userSecurityData = new UserSecurityData(name, rs.getString(1),
                    getStatusString(rs.getInt(2)));
            rs.close();
            pstmt.close();



            // get the role

            query = "select bigRole from " + USER_BIGROLE_TABLE;
            query += " where user_name = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            userSecurityData.setRole(rs.next() ? rs.getString(1) : NO_ROLE_DEFINED);
            if (rs.next()) {
                n2bbLog.warn("more than one n2 role assigned to user... " + name);
            }
            return userSecurityData;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * Checks if a user exists.
     *
     * @param name user name
     * @return true if user exists
     */
    public boolean userExists(String name) throws N2bbException {
        n2bbLog.debug("enter user... " + name);
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        boolean bExists = false;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            String query = "select * from " + USER_TABLE + " where user_name = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, name);
            rs = pstmt.executeQuery();
            bExists = rs.next();
            rs.close();
            pstmt.close();
            return bExists;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
    }
    /**
     * Checks the existence of users for the given role
     ** @param name user name
     * @return true if user exists
     */
    public boolean userExistsForRole(String roleName) throws N2bbException {
        n2bbLog.debug("userExistsForRole enter " + roleName);
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        boolean bExists = false;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            String query = "select * from " + USER_BIGROLE_TABLE + " where bigrole = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, roleName);
            rs = pstmt.executeQuery();
            bExists = rs.next();
            rs.close();
            pstmt.close();
            return bExists;
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
    }
    /**
     * Checks if a user exists.
     *
     * @param name user name
     * @return true if user exists
     */
    public Set<UserBean> getUserData(String roleName) throws N2bbException {
        n2bbLog.debug("enter user... " + roleName);
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        Set<UserBean> userBeanSet = new HashSet<UserBean>();
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            String query = "SELECT  USER_BIGROLE.USER_NAME,FIRSTNAME,LASTNAME,EMAIL,PHONE,USER_DATA.CREATEDATE,USER_DATA.UPDATEDATE,EMPLOYEEID,DEPARTMENT,LOCATION,EXTENSION FROM USER_BIGROLE , USER_DATA WHERE BIGROLE =? AND USER_BIGROLE.USER_NAME = USER_DATA.USER_NAME ORDER BY UPPER(FIRSTNAME)" ;
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, roleName);
            rs = pstmt.executeQuery();
            while (rs.next()){
            	UserBean userBean = new UserBean();
            	userBean.setUserName(rs.getString("USER_NAME"));
            	userBean.setFirstName(rs.getString("FIRSTNAME"));
            	userBean.setLastName(rs.getString("LASTNAME"));
            	userBean.setEmail(rs.getString("EMAIL"));
            	// To add all the columns to the 'User Bean' modify the UserBean for getter and setter methods
            	userBeanSet.add(userBean);
            }
            rs.close();
            pstmt.close();

        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeResultSet(rs);
            closeStatement(pstmt);
            closeConnection(con);
        }
        return userBeanSet;
    }

    /**
     * Toggles a user's status.
     *
     * @param userName user name
     */
    public void toggleUserStatus(String userName) throws N2bbException {
        n2bbLog.debug("enter user... " + userName);
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            String query = "select status from " + USER_TABLE +
                    " where user_name = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, userName);
            ResultSet rs = pstmt.executeQuery();
            if (!rs.next()) {
                n2bbLog.error("user not found");
                throw new N2bbException("error.securityManager.userNotFound");
            }
            int newStatus = (rs.getInt(1) == 1) ? 0 : 1;
            query = "update " + USER_TABLE + " set status = ? where user_name = ?";
            pstmt = con.prepareStatement(query);
            pstmt.setInt(1, newStatus);
            pstmt.setString(2, userName);
            pstmt.executeUpdate();
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
            closeConnection(con);
        }
    }

    /**
     * sets a user's password
     */
    public void setPassword(String userName, String pass, boolean encrypt) throws N2bbException {
        n2bbLog.debug("enter user... " + userName);
        Connection con = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            setPassword(userName, pass, encrypt, con);
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeConnection(con);
        }
    }

    /**
     * Changes user's password after it has expired.
     *
     * @param username    username
     * @param newPassword new password, must be different from existing one
     * @throws IdenticalPasswordException if new password is same as old
     * @throws N2bbException              if any other error occurs
     */
    public void changeExpiredPassword(String username, String newPassword)
            throws IdenticalPasswordException, N2bbException {
        Connection con = null;
        try {
            con = getDataSource().getConnection();
            if (con == null) {
                throw new N2bbException("error.userManager.databaseNoConnection");
            }
            if (isPasswordSame(username, newPassword, con)) {
                throw new IdenticalPasswordException();
            }
            setPassword(username, newPassword, true, con);
        }
        catch (SQLException e) {
            throw wrappedException(e);
        }
        finally {
            closeConnection(con);
        }
    }

    /**
     * Checks if the supplied password is the same as the existing one.
     */
    private boolean isPasswordSame(String userName, String password,
                                   Connection con) throws N2bbException {
        String query = "select USER_NAME from " + USER_TABLE +
                " where user_name = ? " +
                " and   user_pass = ?";
        PreparedStatement pstmt = null;
        boolean different = false;
        try {
            pstmt = con.prepareStatement(query);
            pstmt.setString(1, userName);
            pstmt.setString(2, DigestUtil.Digest(password, "SHA"));
            different = pstmt.executeQuery().next();
        }
        catch (SQLException e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
        }
        return different;
    }

    /**
     * Sets a user's password.
     *
     * @param userName user name
     * @param pass     unencrypted password
     * @param encrypt
     */
    public void setPassword(String userName, String pass, boolean encrypt,
                            Connection con) throws N2bbException {
        n2bbLog.debug("enter user... " + userName);
        PreparedStatement pstmt = null;
        try {
            String query =
                    "update " + USER_TABLE +
                    " set user_pass = ?," +
                    "     PASSWORD_MODIFIED_DATE = ? " +
                    " where user_name = ?";
            pstmt = con.prepareStatement(query);



            // pstmt.setString(1, RealmBase.Digest(pass, "SHA"));

            if (encrypt) {
                pstmt.setString(1, DigestUtil.Digest(pass, "SHA"));
            }
            else {
                pstmt.setString(1, pass);
            }
            pstmt.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
            pstmt.setString(3, userName);
            pstmt.executeUpdate();
            pstmt.close();
        }
        catch (Exception e) {
            throw wrappedException(e);
        }
        finally {
            closeStatement(pstmt);
        }
    }

    /**
     * Wraps an exception in an N2BBException, unless it's already
     * <p/>
     * an N2BBException.
     *
     * @param e exception
     * @return wrapped exception
     */
    private N2bbException wrappedException(Exception e) {
        if (e instanceof N2bbException) {
            n2bbLog.error("n2bb exception - message... " + e.getMessage(), e);
            return (N2bbException) e;
        }
        else if (e instanceof SQLException) {
            n2bbLog.error("SQLException - message... " + e.getMessage(), e);
            return new N2bbException("error.userManager.databaseAccessError");
        }
        else {
            n2bbLog.error(e.getMessage(), e);
            return new N2bbException("error.other");
        }
    }

    /**
     * Gets the status string for a status code.
     *
     * @param statusCode status code as stored in database
     * @return A for active, I for inactive
     */
    private static String getStatusString(int statusCode) {
        return (statusCode == 1) ? "A" : "I";
    }

    private void closeConnection(Connection con) {
        if (con != null) {
            try {
                con.close();
            }
            catch (SQLException e) {
            }
        }
    }

    private void closeStatement(Statement statement) {
        if (statement != null) {
            try {
                statement.close();
            }
            catch (SQLException e) {
            }
        }
    }

    private void closeResultSet(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            }
            catch (SQLException e) {
            }
        }
    }

}