localdomain.localhost.CasInitializer.java Source code

Java tutorial

Introduction

Here is the source code for localdomain.localhost.CasInitializer.java

Source

/*
 * Copyright 2010-2013, the original author or authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package localdomain.localhost;

import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;
import org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.PasswordEncoder;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.ReflectionUtils;

import javax.sql.DataSource;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.NoSuchElementException;

/**
 * <p>
 * Initialize the application creating a table for {@link SearchModeSearchDatabaseAuthenticationHandler} if needed
 * and populating it with a user "demo/mode" if no user exist in the database.
 * </p>
 * <p>
 * Don't use SLF4J {@link Logger} but directly {@code System.out} because by default, CAS configures log4j at
 * {@code ERROR} level.
 * </p>
 *
 * @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
 */
@ManagedResource
public class CasInitializer implements InitializingBean, ApplicationContextAware {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    final Field tableUsersField = ReflectionUtils.findField(SearchModeSearchDatabaseAuthenticationHandler.class,
            "tableUsers");
    private final Field passwordEncoderField = ReflectionUtils
            .findField(AbstractUsernamePasswordAuthenticationHandler.class, "passwordEncoder");
    private final Field dataSourceField = ReflectionUtils
            .findField(AbstractJdbcUsernamePasswordAuthenticationHandler.class, "dataSource");
    private final Field fieldUserField = ReflectionUtils
            .findField(SearchModeSearchDatabaseAuthenticationHandler.class, "fieldUser");
    private final Field fieldPasswordField = ReflectionUtils
            .findField(SearchModeSearchDatabaseAuthenticationHandler.class, "fieldPassword");
    @NotNull
    private ApplicationContext applicationContext;

    {
        passwordEncoderField.setAccessible(true);
        dataSourceField.setAccessible(true);
        fieldUserField.setAccessible(true);
        fieldPasswordField.setAccessible(true);
        tableUsersField.setAccessible(true);
    }

    private DataSource dataSource;
    private String fieldUser;
    private String fieldPassword;
    private String tableUsers;
    private PasswordEncoder passwordEncoder;
    private SearchModeSearchDatabaseAuthenticationHandler authenticationHandler;

    public static void rollbackQuietly(Connection cnn) {
        if (cnn == null)
            return;
        try {
            cnn.rollback();
        } catch (Exception e) {
            // ignore
        }
    }

    public static void closeQuietly(Statement stmt) {
        if (stmt == null)
            return;
        try {
            stmt.close();
        } catch (Exception e) {
            // ignore
        }
    }

    public static void closeQuietly(Connection cnn) {
        if (cnn == null)
            return;
        try {
            cnn.close();
        } catch (Exception e) {
            // ignore
        }
    }

    public static void closeQuietly(ResultSet rst) {
        if (rst == null)
            return;
        try {
            rst.close();
        } catch (Exception e) {
            // ignore
        }
    }

    public static void closeQuietly(Statement stmt, ResultSet rst) {
        closeQuietly(rst);
        closeQuietly(stmt);
    }

    public static void closeQuietly(Connection cnn, Statement stmt, ResultSet rst) {
        closeQuietly(rst);
        closeQuietly(stmt);
        closeQuietly(cnn);
    }

    public static void closeQuietly(Connection cnn, Statement stmt) {
        closeQuietly(stmt);
        closeQuietly(cnn);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.err.println(
                "##############################################################################################");
        System.err.println(
                "##############################################################################################");
        System.err.println(
                "#                                                                                            #");
        System.err.println(
                "# CLOUDBEES JASIG CAS SSO SERVER CLICKSTART                                                  #");
        System.err.println(
                "#                                                                                            #");
        System.err.println(
                "# See documentation at https://github.com/CloudBees-community/jasig-cas-clickstart/wiki      #");
        System.err.println(
                "#                                                                                            #");
        System.err.println(
                "##############################################################################################");
        System.err.println(
                "##############################################################################################");

        try {
            authenticationHandler = applicationContext.getBean(SearchModeSearchDatabaseAuthenticationHandler.class);
        } catch (NoSuchBeanDefinitionException e) {
            String msg = "No Spring bean of type " + SearchModeSearchDatabaseAuthenticationHandler.class
                    + " found, initializer can not run";
            logger.warn(msg);
            throw new IllegalStateException(msg, e);
        }

        dataSource = (DataSource) ReflectionUtils.getField(dataSourceField, authenticationHandler);
        fieldUser = (String) ReflectionUtils.getField(fieldUserField, authenticationHandler);
        fieldPassword = (String) ReflectionUtils.getField(fieldPasswordField, authenticationHandler);
        tableUsers = (String) ReflectionUtils.getField(tableUsersField, authenticationHandler);
        passwordEncoder = (PasswordEncoder) ReflectionUtils.getField(passwordEncoderField, authenticationHandler);

        final Connection cnn = dataSource.getConnection();
        Statement stmt = null;
        ResultSet rst = null;

        try {
            logger.info("Create table " + tableUsers + " if not exist");
            stmt = cnn.createStatement();
            String createTableDdl = "CREATE TABLE IF NOT EXISTS `" + tableUsers + "` (" + " `" + fieldUser
                    + "` varchar(255) DEFAULT NULL,\n" + " `" + fieldPassword + "` varchar(255) DEFAULT NULL,\n"
                    + " PRIMARY KEY (`" + fieldUser + "`)\n" + ")";
            stmt.execute(createTableDdl);
            closeQuietly(stmt);

            logger.info("Validate table columns");
            stmt = cnn.createStatement();
            String sqlCheckDatabaseTable = "select `" + fieldUser + "`,`" + fieldPassword + "`" + " from `"
                    + tableUsers + "` where 0=1";
            try {
                stmt.execute(sqlCheckDatabaseTable);
            } catch (SQLException e) {
                throw new IllegalStateException("Invalid table structure:" + sqlCheckDatabaseTable, e);
            }
            closeQuietly(stmt);

            stmt = cnn.createStatement();
            String sqlCountUsers = "select count(*) from `" + tableUsers + "`";
            rst = stmt.executeQuery(sqlCountUsers);
            rst.next();
            int usersCount = rst.getInt(1);
            closeQuietly(stmt, rst);

            if (usersCount == 0) {
                insertUser("demo", "mode", cnn);
            }

            if (!cnn.getAutoCommit()) {
                cnn.commit();
            }
            logger.info("CAS CLICKSTART INITIALIZED");

        } finally {
            closeQuietly(cnn, stmt, rst);
        }

    }

    protected void insertUser(@NotNull String username, @NotNull String clearTextPassword, Connection cnn)
            throws SQLException {
        logger.info("Create user '" + username + "'");
        PreparedStatement stmt = null;
        try {
            stmt = cnn.prepareStatement("insert into `" + tableUsers + "` " + "(`" + fieldUser + "`, `"
                    + fieldPassword + "`) values (?, ?)");
            stmt.setString(1, username);
            stmt.setString(2, passwordEncoder.encode(clearTextPassword));
            int rowCount = stmt.executeUpdate();
            if (rowCount != 1) {
                logger.warn("More/less (" + rowCount + ") than 1 row inserted for username '" + username + "'");
            }
        } finally {
            closeQuietly(stmt);
        }
    }

    @ManagedOperation
    public void createUser(@NotNull String username, @NotNull String clearTextPassword) {
        Connection cnn = null;
        try {
            cnn = dataSource.getConnection();
            cnn.setAutoCommit(false);
            insertUser(username, clearTextPassword, cnn);
            cnn.commit();
        } catch (RuntimeException e) {
            rollbackQuietly(cnn);
            String msg = "Exception creating '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } catch (SQLException e) {
            rollbackQuietly(cnn);
            String msg = "Exception creating '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } finally {
            closeQuietly(cnn);
        }
    }

    @ManagedOperation
    public void deleteUser(@NotNull String username) {
        Connection cnn = null;
        PreparedStatement stmt = null;
        try {
            cnn = dataSource.getConnection();
            cnn.setAutoCommit(false);
            stmt = cnn.prepareStatement("delete from `" + tableUsers + "` where `" + fieldUser + "` like ?");
            stmt.setString(1, username);
            int rowCount = stmt.executeUpdate();
            if (rowCount == 0) {
                throw new NoSuchElementException("No user '" + username + "' found");
            } else if (rowCount == 1) {
                logger.info("User '" + username + "' was deleted");
            } else {
                new IllegalArgumentException(
                        "More (" + rowCount + ") than 1 row deleted for username '" + username + "', rollback");
            }
            logger.info("User '" + username + "' deleted");
            cnn.commit();
        } catch (RuntimeException e) {
            rollbackQuietly(cnn);
            String msg = "Exception deleting user '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } catch (SQLException e) {
            rollbackQuietly(cnn);

            String msg = "Exception deleting user '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } finally {
            closeQuietly(cnn, stmt);
        }
    }

    @ManagedOperation
    public void changeUserPassword(@NotNull String username, @NotNull String clearTextPassword) {
        Connection cnn = null;
        PreparedStatement stmt = null;
        try {
            cnn = dataSource.getConnection();
            cnn.setAutoCommit(false);
            stmt = cnn.prepareStatement(
                    "update `" + tableUsers + "` set `" + fieldPassword + "` = ? where `" + fieldUser + "` like ?");
            stmt.setString(1, passwordEncoder.encode(clearTextPassword));
            stmt.setString(2, username);
            int rowCount = stmt.executeUpdate();
            if (rowCount == 0) {
                throw new NoSuchElementException("No user '" + username + "' found");
            } else if (rowCount == 1) {
                logger.info("Password of user '" + username + "' was updated");
            } else {
                new IllegalArgumentException(
                        "More (" + rowCount + ") than 1 row deleted for username '" + username + "', rollback");
            }
            logger.info("User '" + username + "' deleted");
            cnn.commit();
        } catch (RuntimeException e) {
            rollbackQuietly(cnn);
            String msg = "Exception changing password for user '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } catch (SQLException e) {
            rollbackQuietly(cnn);
            String msg = "Exception changing password for user '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } finally {
            closeQuietly(cnn, stmt);
        }
    }

    @ManagedOperation
    public void checkUserPassword(@NotNull String username, @NotNull String clearTextPassword) {
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials();
        credentials.setUsername(username);
        credentials.setPassword(clearTextPassword);
        boolean authenticated;
        try {
            authenticated = authenticationHandler.authenticate(credentials);

        } catch (AuthenticationException e) {
            String msg = "Exception authenticating login/password for username '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        } catch (RuntimeException e) {
            String msg = "Invalid login/password for username '" + username + "': " + e;
            logger.warn(msg, e);
            throw new RuntimeException(msg);
        }
        if (!authenticated) {
            String msg = "Invalid login/password for username '" + username + "'";
            logger.warn(msg);
            throw new RuntimeException(msg);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}