com.sslexplorer.jdbc.JDBCSystemDatabase.java Source code

Java tutorial

Introduction

Here is the source code for com.sslexplorer.jdbc.JDBCSystemDatabase.java

Source

/*
*  SSL-Explorer
*
*  Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either version 2 of
*  the License, or (at your option) any later version.
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package com.sslexplorer.jdbc;

import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sslexplorer.boot.ContextHolder;
import com.sslexplorer.boot.SystemProperties;
import com.sslexplorer.boot.Util;
import com.sslexplorer.core.CoreEvent;
import com.sslexplorer.core.CoreEventConstants;
import com.sslexplorer.core.CoreListener;
import com.sslexplorer.core.CoreServlet;
import com.sslexplorer.navigation.DefaultFavorite;
import com.sslexplorer.navigation.Favorite;
import com.sslexplorer.security.AuthenticationModuleManager;
import com.sslexplorer.security.AuthenticationScheme;
import com.sslexplorer.security.DefaultAuthenticationScheme;
import com.sslexplorer.security.IpRestriction;
import com.sslexplorer.security.SystemDatabase;
import com.sslexplorer.security.User;
import com.sslexplorer.util.CIDRNetwork;

/**
 * Implementation of a {@link com.sslexplorer.security.SystemDatabase} that uses
 * a JDBC compliant database to store SSL-Explorer's basic configuration and
 * resources.
 */
public class JDBCSystemDatabase implements SystemDatabase, CoreListener {
    private static final Log log = LogFactory.getLog(JDBCSystemDatabase.class);
    private static final String LOCAL_HOST = "localhost";
    private static final String LOCAL_LOOP_BACK_IPV4 = "127.0.0.1";
    private static final String LOCAL_LOOP_BACK_IPV6 = "::1";
    private static final String LOCAL_LOOP_BACK_IPV6_EXT = "0:0:0:0:0:0:0:1";

    private final Collection<String> authorizedIPAddresses = new HashSet<String>();
    private final Collection<String> excludedIPAddresses = new HashSet<String>();
    private JDBCDatabaseEngine db;

    /**
     * Constructor
     */
    public JDBCSystemDatabase() {
        initialiseIpRestrictionCache();
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.core.Database#open(com.sslexplorer.core.CoreServlet)
     */
    public void open(CoreServlet controllingServlet) throws Exception {
        String dbName = SystemProperties.get("sslexplorer.systemDatabase.jdbc.dbName", "explorer_configuration");
        controllingServlet.addDatabase(dbName, ContextHolder.getContext().getDBDirectory());
        String jdbcUser = SystemProperties.get("sslexplorer.jdbc.username", "sa");
        String jdbcPassword = SystemProperties.get("sslexplorer.jdbc.password", "");
        String vendorDB = SystemProperties.get("sslexplorer.jdbc.vendorClass",
                "com.sslexplorer.jdbc.hsqldb.HSQLDBDatabaseEngine");

        if (log.isInfoEnabled()) {
            log.info("System database is being opened...");
            log.info("JDBC vendor class implementation is " + vendorDB);
        }

        db = (JDBCDatabaseEngine) Class.forName(vendorDB).newInstance();
        db.init("systemDatabase", dbName, jdbcUser, jdbcPassword, null);

        File upgradeDir = new File("install/upgrade");
        DBUpgrader upgrader = new DBUpgrader(ContextHolder.getContext().getVersion(), db,
                ContextHolder.getContext().getDBDirectory(), upgradeDir);
        upgrader.upgrade();

        CoreServlet.getServlet().addCoreListener(this);
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.boot.Database#close()
     */
    public void close() throws Exception {
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.boot.Database#cleanup()
     */
    public void cleanup() throws Exception {
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.core.CoreListener#coreEvent(com.sslexplorer.core.CoreEvent)
     */
    public void coreEvent(CoreEvent evt) {
        if (evt.getId() == CoreEventConstants.USER_REMOVED) {
            User user = (User) evt.getParameter();

            // LDP - Fix as null user might be passed?
            if (user == null)
                return;

            try {
                removeUser(user.getPrincipalName());
            } catch (Exception e) {
                log.error("Failed to remove user from system database.", e);
            }
        }
    }

    private void initialiseIpRestrictionCache() {
        synchronized (authorizedIPAddresses) {
            authorizedIPAddresses.clear();
            authorizedIPAddresses.addAll(getAlwaysAuthorizedIpAddresses());
            excludedIPAddresses.clear();
        }
    }

    private static Collection<String> getAlwaysAuthorizedIpAddresses() {
        String property = SystemProperties.get("sslexplorer.iprestrictions.allow", "");
        Collection<String> propertyAsCollection = property.length() == 0 ? Collections.<String>emptyList()
                : Arrays.asList(property.split(","));

        /* BPS - It is possible to spoof localhost, although most firewalls should prevent this happening,
         * do we really want to just allow localhost?
         */
        Collection<String> alwaysAuthorized = new HashSet<String>();
        alwaysAuthorized.add(LOCAL_HOST);
        alwaysAuthorized.add(LOCAL_LOOP_BACK_IPV4);
        alwaysAuthorized.add(LOCAL_LOOP_BACK_IPV6);
        alwaysAuthorized.add(LOCAL_LOOP_BACK_IPV6_EXT);
        alwaysAuthorized.addAll(propertyAsCollection);
        return alwaysAuthorized;
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#verifyIPAddress(java.lang.String)
     */
    public boolean verifyIPAddress(String ipAddress) throws Exception {
        // Check if the state of this ip address is cached
        if (authorizedIPAddresses.contains(ipAddress)) {
            return true;
        } else if (excludedIPAddresses.contains(ipAddress)) {
            return false;
        }

        IpRestriction[] ipRestrictions = getIpRestrictions();
        return verifyIPAddress(ipAddress, ipRestrictions);
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#verifyIPAddress(java.lang.String, com.sslexplorer.security.IpRestriction[])
     */
    public boolean verifyIPAddress(String ipAddress, IpRestriction[] ipRestrictions) throws Exception {
        synchronized (authorizedIPAddresses) {
            boolean isValid = isAddressValid(ipAddress, ipRestrictions);
            if (isValid) { // Cache IP addresses to stop us doing too many lookups
                authorizedIPAddresses.add(ipAddress);
            } else {
                excludedIPAddresses.add(ipAddress);
            }
            return isValid;
        }
    }

    /**
     * @param ipAddress
     * @param ipRestrictions
     * @return boolean
     * @throws Exception
     */
    private boolean isAddressValid(String ipAddress, IpRestriction[] ipRestrictions) throws Exception {
        /* BPS - It is possible to spoof localhost, although most firewalls should prevent this happening,
         * do we really want to just allow localhost?
         */

        if (LOCAL_LOOP_BACK_IPV4.equals(ipAddress) || LOCAL_LOOP_BACK_IPV6.equals(ipAddress)
                || LOCAL_LOOP_BACK_IPV6_EXT.equals(ipAddress) || LOCAL_HOST.equalsIgnoreCase(ipAddress)) {
            return true;
        }

        if (ipAddress.length() == 0) {
            return true;
        }

        // No restrictions at all. Allow. Should only happen on bad upgrades
        if (ipRestrictions.length == 0) {
            return true;
        }

        /* Get the last matching state. We use the IpRestriction type
           constants as a convenience while iterating over the 
           restrictions, nothing more */
        int state = -1;
        for (int i = 0; i < ipRestrictions.length; i++) {
            if (matchesAddress(ipRestrictions[i], ipAddress)) {
                state = ipRestrictions[i].getAllowed() ? IpRestriction.ALLOWED : IpRestriction.DENIED;
            }
        }
        return state == IpRestriction.ALLOWED;
    }

    /**
     * @param restriction
     * @param ipAddress
     * @return boolean
     */
    private boolean matchesAddress(IpRestriction restriction, String ipAddress) {
        String ipRestrictionAddress = restriction.getAddress();

        if (isIpAddressMatch(ipAddress, ipRestrictionAddress)) {
            return true;
        } else if (restriction.isWildcardMatch() && isIpAddressWildcardMatch(ipAddress, ipRestrictionAddress)) {
            return true;
        } else if (isCIDRMatch(ipAddress, ipRestrictionAddress)) {
            return true;
        }

        return false;
    }

    /**
     * @param toCheck
     * @param ipRestrictionAddress
     * @return boolean
     */
    private static boolean isIpAddressMatch(String toCheck, String ipRestrictionAddress) {
        return toCheck.equals(ipRestrictionAddress);
    }

    /**
     * @param toCheck
     * @param ipRestrictionAddress
     * @return boolean
     */
    private static boolean isIpAddressWildcardMatch(String toCheck, String ipRestrictionAddress) {
        String regex = Util.parseSimplePatternToRegExp(ipRestrictionAddress);
        return Pattern.matches(regex, toCheck);
    }

    /**
     * @param toCheck
     * @param ipRestrictionAddress
     * @return boolean
     */
    private static boolean isCIDRMatch(String toCheck, String ipRestrictionAddress) {
        try {
            CIDRNetwork network = new CIDRNetwork(ipRestrictionAddress);
            return network.isValidAddressForNetwork(toCheck);
        } catch (Exception e) {
            return false;
        }
    }

    public void addIpRestriction(String addressPattern, int type) throws Exception {
        IpRestriction[] r = getIpRestrictions();
        int priority = (r == null || r.length == 0 ? 10 : r[r.length - 1].getPriority()) + 10;
        JDBCPreparedStatement ps = db.getStatement("addIpRestriction.insert");
        try {
            ps.setString(1, addressPattern);
            ps.setInt(2, type);
            ps.setInt(3, priority);
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
            initialiseIpRestrictionCache();
        }
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#updateIpRestriction(com.sslexplorer.security.IpRestriction)
     */
    public void updateIpRestriction(IpRestriction restriction) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("updateIpRestriction.update");
        try {
            ps.setString(1, restriction.getAddress());
            ps.setInt(2, restriction.getType());
            ps.setInt(3, restriction.getPriority());
            ps.setInt(4, restriction.getID());
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
            initialiseIpRestrictionCache();
        }
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#swapIpRestrictions(com.sslexplorer.security.IpRestriction, com.sslexplorer.security.IpRestriction)
     */
    public void swapIpRestrictions(IpRestriction restriction1, IpRestriction restriction2) throws Exception {
        if (restriction1.isDefault() || restriction2.isDefault()) {
            throw new IllegalArgumentException("You may not move the default Ip restriction.");
        }
        int priority2 = restriction2.getPriority();
        restriction2.setPriority(restriction1.getPriority());
        restriction1.setPriority(priority2);
        updateIpRestriction(restriction1);
        updateIpRestriction(restriction2);
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getIpRestrictions()
     */
    public IpRestriction[] getIpRestrictions() throws Exception {
        JDBCPreparedStatement statement = db.getStatement("getIpRestrictions.select");
        Collection<IpRestriction> restrictions = new ArrayList<IpRestriction>();
        ResultSet resultSet = null;

        try {
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                restrictions.add(buildIpRestriction(resultSet));
            }
        } finally {
            JDBCUtil.cleanup(resultSet);
            statement.releasePreparedStatement();
        }

        return restrictions.toArray(new IpRestriction[restrictions.size()]);
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getIpRestriction(int)
     */
    public IpRestriction getIpRestriction(int id) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getIpRestriction.select");
        ps.setInt(1, id);
        try {
            ResultSet rs = ps.executeQuery();
            try {
                if (rs.next()) {
                    return buildIpRestriction(rs);
                }
            } finally {
                rs.close();
            }
        } finally {
            ps.releasePreparedStatement();
        }
        return null;
    }

    /**
     * @param resultSet
     * @return IpRestriction
     * @throws SQLException
     */
    private static IpRestriction buildIpRestriction(ResultSet resultSet) throws SQLException {
        return new IpRestriction(resultSet.getInt("restriction_id"), resultSet.getString("address"),
                resultSet.getInt("type"), resultSet.getInt("priority"));
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#removeIpRestriction(int)
     */
    public void removeIpRestriction(int id) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("removeIpRestriction.delete");
        try {
            ps.setInt(1, id);
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
            initialiseIpRestrictionCache();
        }
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#addFavorite(int, int, java.lang.String)
     */
    public void addFavorite(int type, int favoriteKey, String username) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("addFavorite.insert");
        try {
            ps.setInt(1, type);
            ps.setString(2, username == null ? "" : username);
            ps.setInt(3, favoriteKey);
            ps.execute();
            ps.reset();
        } finally {
            ps.releasePreparedStatement();
        }
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#removeFavorite(int, int, java.lang.String)
     */
    public void removeFavorite(int type, int favoriteKey, String username) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("removeFavorite.delete");
        try {
            ps.setInt(1, type);
            ps.setString(2, username == null ? "" : username);
            ps.setInt(3, favoriteKey);
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
        }

    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getFavorites(int, com.sslexplorer.security.User)
     */
    public List<Favorite> getFavorites(int type, User user) throws Exception {
        JDBCPreparedStatement ps = null;
        if (type == -1) {
            ps = db.getStatement("getFavorites.selectAllForUser");
            ps.setString(1, user == null ? "" : user.getPrincipalName());
        } else {
            ps = db.getStatement("getFavorites.selectTypeForUser");
            ps.setString(1, user == null ? "" : user.getPrincipalName());
            ps.setInt(2, type);
        }
        try {
            Vector<Favorite> v = new Vector<Favorite>();
            ResultSet rs = ps.executeQuery();
            try {
                while (rs.next()) {

                    v.add(new DefaultFavorite(rs.getInt("favorite_id"), type,
                            user == null ? null : user.getPrincipalName(), rs.getInt("favorite_key")));
                }
            } finally {
                rs.close();
            }
            return v;
        } finally {
            ps.releasePreparedStatement();
        }

    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getFavorite(int, com.sslexplorer.security.User, java.lang.String)
     */
    public Favorite getFavorite(int type, User user, int resourceId) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getFavorite.select");
        ps.setString(1, user == null ? "" : user.getPrincipalName());
        ps.setInt(2, type);
        ps.setInt(3, resourceId);
        try {
            ResultSet rs = ps.executeQuery();
            try {
                if (rs.next()) {
                    return new DefaultFavorite(rs.getInt("favorite_id"), type,
                            user == null ? null : user.getPrincipalName(), rs.getInt("favorite_key"));
                }
            } finally {
                rs.close();
            }
        } finally {
            ps.releasePreparedStatement();
        }
        return null;
    }

    // Authentication Schemes

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getAuthenticationSchemeSequences()
     */
    public List<AuthenticationScheme> getAuthenticationSchemeSequences() throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getAuthenticationSchemeSequences.select");
        try {
            ResultSet rs = ps.executeQuery();
            try {
                List<AuthenticationScheme> sequences = new ArrayList<AuthenticationScheme>();
                while (rs.next()) {
                    AuthenticationScheme sequence = buildAuthenticationSchemeSequence(rs);
                    if (sequence != null) {
                        sequences.add(sequence);
                    }
                }
                return sequences;
            } finally {
                rs.close();
            }
        } finally {
            ps.releasePreparedStatement();
        }
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getAuthenticationSchemeSequence(int)
     */
    public AuthenticationScheme getAuthenticationSchemeSequence(int id) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getAuthenticationSchemeSequence.select");
        ResultSet rs = null;
        try {
            ps.setInt(1, id);
            rs = ps.executeQuery();
            if (rs.next()) {
                return buildAuthenticationSchemeSequence(rs);
            }
        } finally {
            JDBCUtil.cleanup(rs);
            ps.releasePreparedStatement();
        }
        return null;
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#getAuthenticationSchemeSequence(java.lang.String, int)
     */
    public AuthenticationScheme getAuthenticationSchemeSequence(String name, int realmID) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getAuthenticationSchemeSequence.select.byName");
        ResultSet rs = null;
        try {
            ps.setInt(1, realmID);
            ps.setString(2, name);
            rs = ps.executeQuery();
            if (rs.next()) {
                return buildAuthenticationSchemeSequence(rs);
            }
        } finally {
            JDBCUtil.cleanup(rs);
            ps.releasePreparedStatement();
        }
        return null;
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#createAuthenticationSchemeSequence(int, java.lang.String, java.lang.String, java.lang.String[], boolean, int)
     */
    public AuthenticationScheme createAuthenticationSchemeSequence(int realmID, String name, String description,
            String[] modules, boolean enabled, int priority) throws Exception {
        Calendar calendar = Calendar.getInstance();
        String timestamp = db.formatTimestamp(calendar);
        JDBCPreparedStatement ps = db.getStatement("createAuthenticationSchemeSequence.insert");
        try {
            ps.setString(1, name);
            ps.setString(2, description);
            ps.setString(3, timestamp);
            ps.setString(4, timestamp);
            ps.setInt(5, enabled ? 1 : 0);
            ps.setInt(6, priority);
            ps.setInt(7, realmID);
            ps.execute();
            int id = db.getLastInsertId(ps, "createAuthenticationSchemeSequence.lastInsertId");
            AuthenticationScheme sequence = getAuthenticationSchemeSequence(id);
            updateAuthenticationSequence(sequence, modules);
            return sequence;
        } finally {
            ps.releasePreparedStatement();
        }
    }

    private void updateAuthenticationSequence(AuthenticationScheme sequence, String[] modules) throws Exception {
        for (int index = 0; index < modules.length; index++) {
            sequence.addModule(modules[index]);
        }
        updateSequence(sequence);
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#updateAuthenticationSchemeSequence(com.sslexplorer.security.AuthenticationScheme)
     */
    public void updateAuthenticationSchemeSequence(AuthenticationScheme sequence) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("updateAuthenticationSchemeSequence.update");
        try {
            ps.setString(1, sequence.getResourceName());
            ps.setString(2, sequence.getResourceDescription());
            Calendar now = Calendar.getInstance();
            ps.setString(3, db.formatTimestamp(now));
            ps.setInt(4, sequence.getEnabled() ? 1 : 0);
            ps.setInt(5, sequence.getResourceId());
            ps.execute();
            updateSequence(sequence);
        } finally {
            ps.releasePreparedStatement();
        }
    }

    /**
     * @param sequence
     * @throws Exception
     */
    private void updateSequence(AuthenticationScheme sequence) throws Exception {
        deleteAuthenticationSequence(sequence.getResourceId());
        updateAuthentictionSequence(sequence);
    }

    private void updateAuthentictionSequence(AuthenticationScheme sequence) throws Exception {
        JDBCPreparedStatement statement = db.getStatement("updateSequence.insert");
        try {
            int seq = 0;
            for (Iterator itr = sequence.modules(); itr.hasNext();) {
                String module = (String) itr.next();
                statement.setInt(1, sequence.getResourceId());
                statement.setString(2, module);
                statement.setInt(3, seq);
                statement.execute();
                seq += 10;
                statement.reset();
            }
        } finally {
            statement.releasePreparedStatement();
        }
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#moveAuthenticationSchemeUp(com.sslexplorer.security.AuthenticationScheme, java.util.List)
     */
    public void moveAuthenticationSchemeUp(AuthenticationScheme scheme, List<AuthenticationScheme> schemes)
            throws Exception {
        int indexOf = schemes.indexOf(scheme);
        if (indexOf == 0)
            throw new IllegalStateException("Scheme is already set to highest priority");

        int schemeAboveIndex = indexOf - 1;
        AuthenticationScheme schemeAbove = schemes.get(schemeAboveIndex);
        updateAuthenticationSchemePriority(scheme, schemeAbove.getPriorityInt());
        updateAuthenticationSchemePriority(schemeAbove, scheme.getPriorityInt());
    }

    /* (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#moveAuthenticationSchemeDown(com.sslexplorer.security.AuthenticationScheme, java.util.List)
     */
    public void moveAuthenticationSchemeDown(AuthenticationScheme scheme, List<AuthenticationScheme> schemes)
            throws Exception {
        int indexOf = schemes.indexOf(scheme);
        if (indexOf == schemes.size() - 1)
            throw new IllegalStateException("Scheme is already set to lowest priority");

        int schemeBelowIndex = indexOf + 1;
        AuthenticationScheme schemeBelow = schemes.get(schemeBelowIndex);
        updateAuthenticationSchemePriority(scheme, schemeBelow.getPriorityInt());
        updateAuthenticationSchemePriority(schemeBelow, scheme.getPriorityInt());
    }

    private void updateAuthenticationSchemePriority(AuthenticationScheme scheme, int priority) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("updateAuthenticationSchemeSequence.update.priority");
        try {
            ps.setInt(1, priority);
            ps.setInt(2, scheme.getResourceId());
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
        }
    }

    /*
     * (non-Javadoc)
     * @see com.sslexplorer.security.SystemDatabase#deleteAuthenticationSchemeSequence(java.lang.String)
     */
    public void deleteAuthenticationSchemeSequence(int id) throws Exception {
        AuthenticationScheme scheme = getAuthenticationSchemeSequence(id);
        if (scheme == null) {
            throw new Exception("No authentication scheme with " + id + ".");
        }
        deleteAuthenticationScheme(id);
        deleteAuthenticationSequence(id);
    }

    private void deleteAuthenticationScheme(int id) throws SQLException, ClassNotFoundException {
        JDBCPreparedStatement ps = db.getStatement("deleteAuthenticationSchemeSequence.delete.authSchemes");
        try {
            ps.setInt(1, id);
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
        }
    }

    private void deleteAuthenticationSequence(int id) throws SQLException, ClassNotFoundException {
        JDBCPreparedStatement ps;
        ps = db.getStatement("deleteAuthenticationSchemeSequence.delete.authSequence");
        try {
            ps.setInt(1, id);
            ps.execute();
        } finally {
            ps.releasePreparedStatement();
        }
    }

    /**
     * @param resultSet
     * @return AuthenticationScheme
     * @throws Exception
     */
    private AuthenticationScheme buildAuthenticationSchemeSequence(ResultSet resultSet) throws Exception {
        Calendar created = JDBCUtil.getCalendar(resultSet, "date_created");
        Calendar amended = JDBCUtil.getCalendar(resultSet, "date_amended");
        boolean enabled = resultSet.getInt("enabled") == 1;
        AuthenticationScheme seq = new DefaultAuthenticationScheme(resultSet.getInt("realm_id"),
                resultSet.getInt("resource_id"), resultSet.getString("resource_name"),
                resultSet.getString("resource_description"), created, amended, enabled,
                resultSet.getInt("priority"));
        return isSchemeAvailable(seq) ? seq : null;
    }

    private boolean isSchemeAvailable(AuthenticationScheme scheme) throws Exception {
        JDBCPreparedStatement statement = db.getStatement("buildAuthenticationSchemeSequences.select.scheme");
        ResultSet resultSet = null;
        boolean available = true;
        try {
            statement.setInt(1, scheme.getResourceId());
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                String moduleId = resultSet.getString("module_id");
                if (!AuthenticationModuleManager.getInstance().isRegistered(moduleId)) {
                    available = false;
                }
                scheme.addModule(moduleId);
            }
        } finally {
            JDBCUtil.cleanup(resultSet);
            statement.releasePreparedStatement();
        }
        return available;
    }

    /**
     * @param username
     * @throws Exception
     */
    protected void removeUser(String username) throws Exception {
        String[] statements = { "removeUser.delete.explorerProperties", "removeUser.delete.propertyProfiles" };
        for (int i = 0; i < statements.length; i++) {
            JDBCPreparedStatement ps = db.getStatement(statements[i]);
            try {
                ps.setString(1, username);
                ps.execute();
            } finally {
                ps.releasePreparedStatement();
            }
        }
    }

    public List<AuthenticationScheme> getAuthenticationSchemeSequences(int realmID) throws Exception {
        JDBCPreparedStatement ps = db.getStatement("getAuthenticationSchemeSequences.realm.select");
        try {
            ps.setInt(1, realmID);
            ResultSet rs = ps.executeQuery();
            try {
                List<AuthenticationScheme> sequences = new ArrayList<AuthenticationScheme>();
                while (rs.next()) {
                    AuthenticationScheme sequence = buildAuthenticationSchemeSequence(rs);
                    if (sequence != null) {
                        sequences.add(sequence);
                    }
                }
                return sequences;
            } finally {
                rs.close();
            }
        } finally {
            ps.releasePreparedStatement();
        }
    }
}