gov.nih.nci.security.authentication.loginmodules.CSMLoginModule.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.security.authentication.loginmodules.CSMLoginModule.java

Source

/*L
 *  Copyright Ekagra Software Technologies Ltd.
 *  Copyright SAIC, SAIC-Frederick
 *
 *  Distributed under the OSI-approved BSD 3-Clause License.
 *  See http://ncip.github.com/common-security-module/LICENSE.txt for details.
 */

/*
 * Created on Nov 15, 2004
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package gov.nih.nci.security.authentication.loginmodules;

/**
 *
 *<!-- LICENSE_TEXT_START -->
 *
 *The NCICB Common Security Module (CSM) Software License, Version 3.0 Copyright
 *2004-2005 Ekagra Software Technologies Limited ('Ekagra')
 *
 *Copyright Notice.  The software subject to this notice and license includes both
 *human readable source code form and machine readable, binary, object code form
 *(the 'CSM Software').  The CSM Software was developed in conjunction with the
 *National Cancer Institute ('NCI') by NCI employees and employees of Ekagra.  To
 *the extent government employees are authors, any rights in such works shall be
 *subject to Title 17 of the United States Code, section 105.    
 *
 *This CSM Software License (the 'License') is between NCI and You.  'You (or
 *'Your') shall mean a person or an entity, and all other entities that control,
 *are controlled by, or are under common control with the entity.  'Control' for
 *purposes of this definition means (i) the direct or indirect power to cause the
 *direction or management of such entity, whether by contract or otherwise, or
 *(ii) ownership of fifty percent (50%) or more of the outstanding shares, or
 *(iii) beneficial ownership of such entity.  
 *
 *This License is granted provided that You agree to the conditions described
 *below.  NCI grants You a non-exclusive, worldwide, perpetual, fully-paid-up,
 *no-charge, irrevocable, transferable and royalty-free right and license in its
 *rights in the CSM Software to (i) use, install, access, operate, execute, copy,
 *modify, translate, market, publicly display, publicly perform, and prepare
 *derivative works of the CSM Software; (ii) distribute and have distributed to
 *and by third parties the CSM Software and any modifications and derivative works
 *thereof; and (iii) sublicense the foregoing rights set out in (i) and (ii) to
 *third parties, including the right to license such rights to further third
 *parties.  For sake of clarity, and not by way of limitation, NCI shall have no
 *right of accounting or right of payment from You or Your sublicensees for the
 *rights granted under this License.  This License is granted at no charge to You.
 *
 *1.   Your redistributions of the source code for the Software must retain the
 *above copyright notice, this list of conditions and the disclaimer and
 *limitation of liability of Article 6 below.  Your redistributions in object code
 *form must reproduce the above copyright notice, this list of conditions and the
 *disclaimer of Article 6 in the documentation and/or other materials provided
 *with the distribution, if any.
 *2.   Your end-user documentation included with the redistribution, if any, must
 *include the following acknowledgment: 'This product includes software developed
 *by Ekagra and the National Cancer Institute.'  If You do not include such
 *end-user documentation, You shall include this acknowledgment in the Software
 *itself, wherever such third-party acknowledgments normally appear.
 *
 *3.   You may not use the names 'The National Cancer Institute', 'NCI' 'Ekagra
 *Software Technologies Limited' and 'Ekagra' to endorse or promote products
 *derived from this Software.  This License does not authorize You to use any
 *trademarks, service marks, trade names, logos or product names of either NCI or
 *Ekagra, except as required to comply with the terms of this License.
 *
 *4.   For sake of clarity, and not by way of limitation, You may incorporate this
 *Software into Your proprietary programs and into any third party proprietary
 *programs.  However, if You incorporate the Software into third party proprietary
 *programs, You agree that You are solely responsible for obtaining any permission
 *from such third parties required to incorporate the Software into such third
 *party proprietary programs and for informing Your sublicensees, including
 *without limitation Your end-users, of their obligation to secure any required
 *permissions from such third parties before incorporating the Software into such
 *third party proprietary software programs.  In the event that You fail to obtain
 *such permissions, You agree to indemnify NCI for any claims against NCI by such
 *third parties, except to the extent prohibited by law, resulting from Your
 *failure to obtain such permissions.
 *
 *5.   For sake of clarity, and not by way of limitation, You may add Your own
 *copyright statement to Your modifications and to the derivative works, and You
 *may provide additional or different license terms and conditions in Your
 *sublicenses of modifications of the Software, or any derivative works of the
 *Software as a whole, provided Your use, reproduction, and distribution of the
 *Work otherwise complies with the conditions stated in this License.
 *
 *6.   THIS SOFTWARE IS PROVIDED 'AS IS,' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 *(INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 *NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.  IN NO
 *EVENT SHALL THE NATIONAL CANCER INSTITUTE, EKAGRA, OR THEIR AFFILIATES BE LIABLE
 *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 *CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 *TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *<!-- LICENSE_TEXT_END -->
 *
 */

import gov.nih.nci.security.constants.Constants;
import gov.nih.nci.security.exceptions.CSConfigurationException;
import gov.nih.nci.security.exceptions.CSException;
import gov.nih.nci.security.exceptions.CSLoginException;
import gov.nih.nci.security.exceptions.internal.CSInternalConfigurationException;
import gov.nih.nci.security.exceptions.internal.CSInternalInsufficientAttributesException;
import gov.nih.nci.security.exceptions.internal.CSInternalLoginException;
import gov.nih.nci.security.util.ConfigurationHelper;
import gov.nih.nci.security.util.StringEncrypter;
import gov.nih.nci.security.util.StringUtilities;
import gov.nih.nci.security.util.StringEncrypter.EncryptionException;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

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

import org.apache.commons.configuration.DataConfiguration;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;

/**
 * This is the abstract base class which provides the common methods to be used for
 * the authentication services. The individual Login Modules should extend this class. This class 
 * retrieves the credentials from the {@link CallbackHandler} passed. It also stored the login configuration
 * data read from the JAAS policy files and stores it. These configurations are then used by the individual
 * Login Modules to connect to the respective credential providers and authenticate the user credentials
 *  
 * @author Kunal Modi (Ekagra Software Technologies Ltd.)
 *
 */
public abstract class CSMLoginModule implements LoginModule {

    private static final Logger log = Logger.getLogger(CSMLoginModule.class);

    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map sharedState;
    private Map options;

    private String userID;
    private char[] password;
    private boolean loginSuccessful = false;

    /**
     * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
     */
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.sharedState = sharedState;
        this.options = options;
    }

    /**
     * Retrieves the user credentials from the CallBacks and tries to validate 
     * them against the database. It retrieves userID and password from the 
     * CallbackHandler. It uses helper class to perform the actual authentication 
     * operations and access the user record. This method returns a true if
     * the user authentication was sucessful else it throws a Login Exception.
     * @throws LoginException 
     * @see javax.security.auth.spi.LoginModule#login()
     */
    public boolean login() throws LoginException, CSInternalLoginException, CSInternalConfigurationException {
        if (callbackHandler == null) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in obtaining the CallBack Handler |");
            throw new LoginException("Error in obtaining Callback Handler");
        }
        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("userid: ");
        callbacks[1] = new PasswordCallback("password: ", false);

        try {
            callbackHandler.handle(callbacks);
            userID = ((NameCallback) callbacks[0]).getName();
            char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();

            if (tmpPassword == null) {
                // treat a NULL password as an empty password
                tmpPassword = new char[0];
            }
            password = new char[tmpPassword.length];
            System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
            ((PasswordCallback) callbacks[1]).clearPassword();
        } catch (java.io.IOException e) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in creating the CallBack Handler |"
                        + e.getMessage());
            throw new LoginException("Error in Creating the CallBack Handler");
        } catch (UnsupportedCallbackException e) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in creating the CallBack Handler |"
                        + e.getMessage());
            throw new LoginException("Error in Creating the CallBack Handler");
        }
        if (isFirstTimeLogin(options, userID)) {
            loginSuccessful = false;
            password = null;
            throw new FailedLoginException("User logging in first time, Password should be changed ");
        }
        DataConfiguration config;
        try {
            config = ConfigurationHelper.getConfiguration();
        } catch (CSConfigurationException e) {
            // TODO Auto-generated catch block
            throw new CSInternalConfigurationException("Exception while reading config data!!");
        }

        if (isPasswordExpired(options, userID)) {
            loginSuccessful = false;
            userID = null;
            password = null;

            throw new CredentialExpiredException("User password expired, Ceate new password");
        }

        try {
            //now validate user
            if (validate(options, userID, password, subject)) {
                if (isActive(options, userID))
                    loginSuccessful = true;
                else {
                    loginSuccessful = false;
                    password = null;
                    throw new AccountExpiredException("User is not active, Contact the system administrator");
                }
            } else {
                // clear the values         
                loginSuccessful = false;
                userID = null;
                password = null;

                throw new LoginException("Invalid Login Credentials");
            }
        } catch (FailedLoginException fle) {
            if (log.isDebugEnabled())
                if (log.isDebugEnabled())
                    log.debug("Authentication|||login|Failure| Invalid Login Credentials |" + fle.getMessage());
            throw new LoginException("Invalid Login Credentials");
        }
        if (log.isDebugEnabled())
            log.debug("Authentication|||login|Success| Authentication is " + loginSuccessful + "|");
        return loginSuccessful;
    }

    public boolean changePassword(String newPassword) throws LoginException, CSInternalLoginException,
            CSInternalConfigurationException, CSConfigurationException {
        if (callbackHandler == null) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in obtaining the CallBack Handler |");
            throw new LoginException("Error in obtaining Callback Handler");
        }
        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("userid: ");
        callbacks[1] = new PasswordCallback("password: ", false);

        try {
            callbackHandler.handle(callbacks);
            userID = ((NameCallback) callbacks[0]).getName();
            char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();

            if (tmpPassword == null) {
                // treat a NULL password as an empty password
                tmpPassword = new char[0];
            }
            password = new char[tmpPassword.length];
            System.arraycopy(tmpPassword, 0, password, 0, tmpPassword.length);
            ((PasswordCallback) callbacks[1]).clearPassword();
        } catch (java.io.IOException e) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in creating the CallBack Handler |"
                        + e.getMessage());
            throw new LoginException("Error in Creating the CallBack Handler");
        } catch (UnsupportedCallbackException e) {
            if (log.isDebugEnabled())
                log.debug("Authentication|||login|Failure| Error in creating the CallBack Handler |"
                        + e.getMessage());
            throw new LoginException("Error in Creating the CallBack Handler");
        }

        try {
            //now validate user
            if (validate(options, userID, password, subject)) {
                DataConfiguration config = ConfigurationHelper.getConfiguration();
                String encryptedPassword = new String(password);
                encryptedPassword = StringUtilities.initTrimmedString(encryptPassword(encryptedPassword, "YES"));
                if (encryptedPassword.equals(encryptPassword(newPassword, "YES"))) {
                    throw new LoginException("The password should be different from the previous passwords");
                }
                if (passwordMatchs(options, userID, newPassword,
                        Integer.parseInt(config.getString("PASSWORD_MATCH_NUM")))) {
                    throw new LoginException("The password should be different from the previous passwords");
                } else {
                    changePassword(options, userID, newPassword);
                    if (isFirstTimeLogin(options, userID))
                        resetFirstTimeLogin(options, userID);

                    insertIntoPasswordHistory(options, userID, password);
                    updatePasswordExpiryDate(options, userID, DateUtils.addDays(Calendar.getInstance().getTime(),
                            Integer.parseInt(config.getString("PASSWORD_EXPIRY_DAYS"))));
                }
            } else {
                // clear the values         
                loginSuccessful = false;
                userID = null;
                password = null;

                throw new FailedLoginException("Invalid Login Credentials");
            }
        } catch (FailedLoginException fle) {
            if (log.isDebugEnabled())
                if (log.isDebugEnabled())
                    log.debug("Authentication|||login|Failure| Invalid Login Credentials |" + fle.getMessage());
            throw new LoginException("Invalid Login Credentials");
        }
        if (log.isDebugEnabled())
            log.debug("Authentication|||login|Success| Authentication is " + loginSuccessful + "|");
        return loginSuccessful;
    }

    private static String encryptPassword(String encryptedPassword, String encryptionEnabled) {
        if (!StringUtilities.isBlank(encryptionEnabled) && encryptionEnabled.equalsIgnoreCase(Constants.YES)) {
            StringEncrypter se;
            try {
                se = new StringEncrypter();
                encryptedPassword = se.encrypt(new String(encryptedPassword));
            } catch (EncryptionException e) {
                e.printStackTrace();
            }
        }
        return encryptedPassword;
    }

    /**
     * @see javax.security.auth.spi.LoginModule#commit()
     */
    public boolean commit() throws LoginException {
        return true;
    }

    /**
     * @see javax.security.auth.spi.LoginModule#abort()
     */
    public boolean abort() throws LoginException {
        return true;
    }

    /**
     * @see javax.security.auth.spi.LoginModule#logout()
     */
    public boolean logout() throws LoginException {
        subject.getPrincipals().clear();
        loginSuccessful = false;
        userID = null;
        password = null;

        return true;
    }

    /**
     * This is an internal method which is to be implemented by individual login
     * modules. This method accepts the user credentials then performs the actual 
     * authentication against the individual credential providers.
     * @param user the user entered user name provided by the calling application
     * @param password the user entered password provided by the calling application
     * @return TRUE if the authentication was sucessful using the provided user 
     *             credentials and FALSE if the authentication fails
     * @throws CSException if the login has failed for any reasons
     * @throws CSLoginException 
     * @throws CSInternalConfigurationException 
     * @throws CSInternalLoginException 
     * @throws CSInternalInsufficientAttributesException 
     */
    protected abstract boolean validate(Map options, String user, char[] password, Subject subject)
            throws CSInternalConfigurationException, CSInternalLoginException,
            CSInternalInsufficientAttributesException;

    protected abstract boolean isPasswordExpired(Map options, String user) throws CSInternalConfigurationException;

    protected abstract boolean isFirstTimeLogin(Map options, String user) throws CSInternalConfigurationException;

    protected abstract boolean changePassword(Map options, String user, String password)
            throws CSInternalConfigurationException;

    protected abstract boolean insertIntoPasswordHistory(Map options, String user, char[] password)
            throws CSInternalConfigurationException;

    protected abstract boolean resetFirstTimeLogin(Map options, String user)
            throws CSInternalConfigurationException;

    protected abstract boolean passwordMatchs(Map options, String user, String newPassword, int passwordNum)
            throws CSInternalConfigurationException;

    protected abstract boolean updatePasswordExpiryDate(Map options, String user, Date expiryDate)
            throws CSInternalConfigurationException;

    protected abstract boolean isActive(Map options, String user) throws CSInternalConfigurationException;

}