/********************************************************************
 * Copyright © 2004 VHA. All rights reserved
 ********************************************************************/
package gov.va.med.fw.security.jaas;

import java.util.ArrayList;
import java.util.List;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.beans.BeansException;

import gov.va.med.fw.security.SecurityService;
import gov.va.med.fw.service.ServiceException;
import gov.va.med.fw.service.config.SingletonApplicationContext;

/**
 * JAAS Login module implementation. This login module delegate the actual
 * authentication call to the security service. This class gets the service
 * target name from the module option named "seviceTarget". If no such option is
 * specified, it uses the default name of "securityServiceTarget".
 * 
 * @author DNS   mansog
 * @datetime Mar 22, 2005
 */
public class LoginModuleImpl extends AbstractUserPasswordLoginModule {
    protected transient Log logger = LogFactory.getLog(getClass());    
    
    //module option name for service target 
    private static final String OPTION_SERVICE_TARGET = "serviceTarget";
    //default security service target.
    private static final String DEFAULT_SECURITY_SERVICE_TARGET = "securityService";
    
    /*
     * Perform the user authentication by delegating a request to
     * SecurityService.
     * 
     * @see gov.vha.edb.service.impl.AbstractLoginModule#authenticate(java.lang.String,
     *      char[])
     */
    protected List authenticate(String username, char[] password)
            throws LoginException {
        SecurityService ss;
        try {
            ss = getSecurityService();
        } catch (ServiceException e) {
            throw new LoginException(
                    "Unable to get a reference to Security Service."
                            + " Reason: " + e.toString());
        }
        List principals = new ArrayList();
        Subject subject = ss.authenticate(username, password);
        principals.addAll(subject.getPrincipals());
        return principals;
    }

    /**
     * Get the SecurityService Component from spring context.
     * 
     * @return
     * @throws ServiceException
     */
    private SecurityService getSecurityService() throws ServiceException {
       try {
          String serviceName = getSecurityServiceBeanTargetName();
          debugIfOn("Using service target " + serviceName);
          SingletonApplicationContext locator = SingletonApplicationContext.getInstance();
          ApplicationContext ctx = locator.getSingletonContext();
          return (SecurityService)ctx.getBean(serviceName);
       }
       catch( BeansException e ) {
          throw new ServiceException( "Failed to get a security service", e );
       }
    }

    /**
     * Return the name of SecurityService target. Subclasses can over ride this
     * to provide a different target name. This method gets the service target
     * name from the module option named "seviceTarget". If no such option is
     * specified, it uses the default name of "securityServiceTarget".
     * 
     * @return target name of the security service
     */
    protected String getSecurityServiceBeanTargetName() {
        Object serviceTarget = getOption(OPTION_SERVICE_TARGET);
        String serviceTargetName = serviceTarget != null ? serviceTarget
                .toString() : DEFAULT_SECURITY_SERVICE_TARGET;
        return serviceTargetName;
    }

    /* 
     * Method to debug the log messages if module debug option is true.
     * @see gov.va.med.fw.security.jaas.AbstractUserPasswordLoginModule#logDebug(java.lang.String)
     */
    protected void logDebug(String msg) {
        logger.debug(msg);
    }
}