/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *  
 ***********************************************************************/

package com.novell.casa.authtoksvc;

import org.apache.log4j.Logger;

import java.io.*;
import java.util.*;

/**
 * EnabledSvcsConfig Class.
 * <p>
 * This class obtains and maintains configuration and policy information about
 * the services enabled to use Authentication Tokens.
 * 
 */
public final class EnabledSvcsConfig
{
   private static final Logger m_log = Logger.getLogger(EnabledSvcsConfig.class);

   private static final String m_authPolicyFileName = "auth.policy";
   private static final String m_authTokenSettingsFileName = "authtoken.settings";
   private static final String m_idenTokenSettingsFileName = "identoken.settings";

   private final boolean m_enabledSvcsOnly;

   // Default auth policy, authtoken, and identtoken configs.
   byte[]            m_defaultAuthPolicyData = null;
   AuthTokenConfig   m_defaultAuthTokenConfig = null;
   IdenTokenConfig   m_defaultIdenTokenConfig = null;


   private final Map<String,Map<Object, SvcConfigEntry>> m_hostsMap;

   /**
    * SvcConfigEntry Class.
    * <p>
    * This class is used to maintain the configuration and policy associated with an
    * enabled service.
    * 
    */
   private static final class SvcConfigEntry
   {
      protected final byte[]           m_authPolicyFileData;
      protected final AuthTokenConfig  m_authTokenConfig;
      protected final IdenTokenConfig  m_idenTokenConfig;

      /**
       * Constructor.
       *
       * @param authPolicyFileData  Authentication policy file data.
       * @param authTokenConfig     Authentication token configuration object.
       * @param idenTokenConfig     Identity token configuration object.
       */
      public SvcConfigEntry(byte[] authPolicyFileData,
                            AuthTokenConfig authTokenConfig,
                            IdenTokenConfig idenTokenConfig)
      {
         m_authPolicyFileData = authPolicyFileData;
         m_authTokenConfig = authTokenConfig;
         m_idenTokenConfig = idenTokenConfig;
      }
   }

   /**
    * Constructor.
    *
    * @param svcConfigPath    Path to the service configuration folder.
    * @param enabledSvcsOnly  True if configured to only provide authentication for
    *                         configured services.
    * @throws Exception
    */
   public EnabledSvcsConfig(String svcConfigPath,
                            boolean enabledSvcsOnly) throws Exception
   {
      m_log.debug("Constructor()-");
      m_log.debug("Constructor()- SvcConfigPath = " + svcConfigPath);

      // Remember the enabledSvcsOnly setting
      m_enabledSvcsOnly = enabledSvcsOnly;

      // Create a map to keep track of the enabled services and their configuration
      // for each configured host.
      m_hostsMap = new HashMap<String, Map<Object, SvcConfigEntry>>();

      // Get access to the configuration folder for the service
      File configFolder = new File(svcConfigPath);
      try
      {
         // Try to obtain the default authentication policy
         FileInputStream inStream = null;
         try
         {
            File f = new File(configFolder, m_authPolicyFileName);
            m_defaultAuthPolicyData = new byte[(int) f.length()];
            inStream = new FileInputStream(f);
            int bytesRead = inStream.read(m_defaultAuthPolicyData);
            if (bytesRead != m_defaultAuthPolicyData.length)
            {
               m_log.warn("Constructor()- Error reading default policy file");
            }
         }
         catch (SecurityException e)
         {
            m_log.warn("Constructor()- SecurityException accessing " + configFolder + File.separator + m_authPolicyFileName + " Exception=" + e.toString());
         }
         catch (FileNotFoundException e)
         {
            m_log.debug("Constructor()- File " + configFolder + File.separator + m_authPolicyFileName + " not found");
         }
         catch (IOException e)
         {
            m_log.warn("Constructor()- IOException reading " + configFolder + File.separator + m_authPolicyFileName + " Exception=" + e.toString());
         }
         finally
         {
            if (inStream != null)
            {
               try
               {
                  inStream.close();
               }
               catch (IOException e)
               {
                  // Do nothing
               }
            }
         }

         // Try to obtain the default authentication token settings
         try
         {
            m_defaultAuthTokenConfig = new AuthTokenConfig(configFolder + File.separator + m_authTokenSettingsFileName);
         }
         catch (Exception e)
         {
            // Not able to create authentication token configuration using the default
            // file. Create one using default parameters.
            m_defaultAuthTokenConfig = new AuthTokenConfig();
         }

         // Try to obtain the default identity token settings
         try
         {
            m_defaultIdenTokenConfig = new IdenTokenConfig(configFolder + File.separator + m_idenTokenSettingsFileName);
         }
         catch (Exception e)
         {
            // Not able to create identity token configuration using the default
            // file. Create one using default parameters.
            m_defaultIdenTokenConfig = new IdenTokenConfig();
         }

         // Now go through the configured hosts. Note that the services config folder
         // contains folders for each host for which there are enabled services. The folders
         // in the services config folder must match the DNS name of the hosts where
         // the enabled services reside except in the localhost case.
         File servicesConfigFolder = new File(svcConfigPath, "enabled_services");
         try
         {
            String[] servicesConfigFolderObjs = servicesConfigFolder.list();
            if (servicesConfigFolderObjs != null)
            {
               for (int i = 0; i < servicesConfigFolderObjs.length; i++)
               {
                  // Check if we are dealing with a file or a folder
                  File hostFolder = new File(servicesConfigFolder, servicesConfigFolderObjs[i]);
                  try
                  {
                     if (hostFolder.isDirectory())
                     {
                        m_log.debug("Constructor()- Host folder " + hostFolder + " is directory");

                        // Now go through the services configured for this host
                        String[] hostFolderObjs = hostFolder.list();
                        if (hostFolderObjs != null)
                        {
                           // Create a Map object to hold the service configurations for this host
                           Map<Object, SvcConfigEntry> enabledSvcsConfigMap = new HashMap<Object, SvcConfigEntry>();

                           for (int ii = 0; ii < hostFolderObjs.length; ii++)
                           {
                              // Check if we are dealing with a file or a folder
                              File serviceFolder = new File(hostFolder, hostFolderObjs[ii]);
                              m_log.debug("Constructor()- Service folder " + serviceFolder);
                              try
                              {
                                 if (serviceFolder.isDirectory())
                                 {
                                    m_log.debug("Constructor()- Service folder " + serviceFolder + " is directory");

                                    // We are dealing with a folder, remember that the folder name matches the name
                                    // of the enabled service. Check and see if there are authentication policy and
                                    // authtoken and identoken setting files configured for it.
                                    byte[] authPolicyData = null;
                                    AuthTokenConfig authTokenConfig = null;
                                    IdenTokenConfig idenTokenConfig = null;
                                    inStream = null;

                                    try
                                    {
                                       File policyFile = new File(serviceFolder, m_authPolicyFileName);
                                       authPolicyData = new byte[(int) policyFile.length()];
                                       inStream = new FileInputStream(policyFile);
                                       int bytesRead = inStream.read(authPolicyData);
                                       if (bytesRead != authPolicyData.length)
                                       {
                                          m_log.warn("Constructor()- Error reading policy file for " + servicesConfigFolderObjs[i] + " " + hostFolderObjs[ii]);
                                       }
                                    }
                                    catch (SecurityException e)
                                    {
                                       m_log.warn("Constructor()- SecurityException accessing " + serviceFolder + File.separator + m_authPolicyFileName + " Exception=" + e.toString());
                                    }
                                    catch (FileNotFoundException e)
                                    {
                                       m_log.warn("Constructor()- No authentication policy file for " + serviceFolder);
                                    }
                                    catch (IOException e)
                                    {
                                       m_log.warn("Constructor()- IOException reading " + serviceFolder + File.separator + m_authPolicyFileName + " Exception=" + e.toString());
                                    }
                                    finally
                                    {
                                       if (inStream != null)
                                       {
                                          try
                                          {
                                             inStream.close();
                                          }
                                          catch (IOException e)
                                          {
                                             // Do nothing
                                          }
                                       }
                                    }

                                    try
                                    {
                                       authTokenConfig = new AuthTokenConfig(serviceFolder + File.separator + m_authTokenSettingsFileName);
                                    }
                                    catch (Exception e)
                                    {
                                       m_log.debug("Constructor()- Exception accessing " + serviceFolder + File.separator + m_authTokenSettingsFileName + " Exception=" + e.toString());
                                    }

                                    try
                                    {
                                       idenTokenConfig = new IdenTokenConfig(serviceFolder + File.separator + m_idenTokenSettingsFileName);
                                    }
                                    catch (Exception e)
                                    {
                                       m_log.debug("Constructor()- Exception accessing " + serviceFolder + File.separator + m_idenTokenSettingsFileName + " Exception=" + e.toString());
                                    }

                                    // Make sure that we have a policy file
                                    if ((authPolicyData != null && authPolicyData.length != 0)
                                        || (m_defaultAuthPolicyData != null && m_defaultAuthPolicyData.length != 0))
                                    {
                                       // Instantiate SvcConfigEntry for this service and place it in our map
                                       SvcConfigEntry svcConfigEntry = new SvcConfigEntry((authPolicyData != null && authPolicyData.length != 0) ? authPolicyData : m_defaultAuthPolicyData,
                                                                                          (authTokenConfig != null) ? authTokenConfig : m_defaultAuthTokenConfig,
                                                                                          (idenTokenConfig != null) ? idenTokenConfig : m_defaultIdenTokenConfig);

                                       // Add this entry to our map
                                       m_log.debug("Constructor()- Adding entry in map for " + servicesConfigFolderObjs[i] + " " + hostFolderObjs[ii]);
                                       enabledSvcsConfigMap.put(hostFolderObjs[ii], svcConfigEntry);
                                    }
                                    else
                                    {
                                       m_log.warn("Constructor()- Unable to enable " + servicesConfigFolderObjs[i] + " " + hostFolderObjs[ii] + " due to no configured authentication policy");
                                    }
                                 }
                              }
                              catch (SecurityException e)
                              {
                                 m_log.warn("Constructor()- SecurityException accessing " + serviceFolder + " Exception=" + e.toString());
                              }

                              // Add this hosts enabled services configuration map to the hosts map
                              m_hostsMap.put(servicesConfigFolderObjs[i].toUpperCase(), enabledSvcsConfigMap);
                           }
                        }
                        else
                        {
                           m_log.debug("Constructor()- No services configured for " + hostFolder);
                        }
                     }
                  }
                  catch (SecurityException e)
                  {
                     m_log.warn("Constructor()- SecurityException accessing " + hostFolder + " Exception=" + e.toString());
                  }
               }
            }
            else
            {
               m_log.warn("Constructor()- Unable to obtain services folder " + servicesConfigFolder + " objects");
            }
         }
         catch (SecurityException e)
         {
            m_log.warn("Constructor()- SecurityException accessing " + servicesConfigFolder + " Exception=" + e.toString());
         }
      }
      catch (SecurityException e)
      {
         m_log.warn("Constructor()- SecurityException accessing " + configFolder + " Exception=" + e.toString());
      }
   }     

   /**
    * Returns true if the specified service has been enabled to use authentication
    * tokens.
    *
    * @param hostName      Name of host where the service resides.
    * @param serviceName   Name of the service.
    * @return              True if the service is enabled.
    */
   public final boolean svcEnabled(String hostName, String serviceName)
   {
      // Always return try if m_enabledSvcsOnly is configured "false" else
      // check the enabled svcs configuration.
      if (m_enabledSvcsOnly == false)
      {
         return true;
      }
      else
      {
         // First try to obtain the Map of enabled services for the host
         Map<Object, SvcConfigEntry> enabledSvcsConfigMap = m_hostsMap.get(hostName.toUpperCase());
         if (enabledSvcsConfigMap != null)
         {
            return enabledSvcsConfigMap.containsKey(serviceName);
         }
         else
         {
            return false;
         }
      }
   }

   /**
    * Returns the data associated with the authentication policy file
    * associated with the specified service.
    *
    * @param hostName      Name of host where the service resides.
    * @param serviceName   Name of the service.
    * @return              Authentication policy data for the service.
    */
   public final byte[] getAuthPolicyFileDataForSvc(String hostName, String serviceName)
   {
      byte[]   authPolicyData = null;

      // First try to obtain the Map of enabled services for the host
      Map<Object, SvcConfigEntry> enabledSvcsConfigMap = m_hostsMap.get(hostName.toUpperCase());
      if (enabledSvcsConfigMap != null)
      {
         // Retrieve SvcConfigEntry for the service from the map for the host
         SvcConfigEntry svcConfigEntry = enabledSvcsConfigMap.get(serviceName);
         if (svcConfigEntry != null)
         {
            authPolicyData = svcConfigEntry.m_authPolicyFileData;
         }
      }

      // If m_enabledSvcsOnly is configured "false" and if no authentication policy
      // data was found for this service then return the default authentication policy
      // data.
      if (authPolicyData == null
          && m_enabledSvcsOnly == false)
      {
         authPolicyData = m_defaultAuthPolicyData;
      }

      return authPolicyData;
   }

   /**
    * Returns the authentication token configuration associated with the
    * specified service.
    *
    * @param hostName      Name of host where the service resides.
    * @param serviceName   Name of the service.
    * @return Authentication token configuration object for the service.
    */
   public final AuthTokenConfig getAuthTokenConfig(String hostName, String serviceName)
   {
      AuthTokenConfig   authTokenConfig = null;

      // First try to obtain the Map of enabled services for the host
      Map<Object, SvcConfigEntry> enabledSvcsConfigMap = m_hostsMap.get(hostName.toUpperCase());
      if (enabledSvcsConfigMap != null)
      {
         // Retrieve SvcConfigEntry for the service from the map for the host
         SvcConfigEntry svcConfigEntry = enabledSvcsConfigMap.get(serviceName);
         if (svcConfigEntry != null)
         {
            authTokenConfig = svcConfigEntry.m_authTokenConfig;
         }
      }

      // If m_enabledSvcsOnly is configured "false" and if no AuthTokenConfig
      // was found for this service then return the default AuthTokenConfig.
      if (authTokenConfig == null
          && m_enabledSvcsOnly == false)
      {
         authTokenConfig = m_defaultAuthTokenConfig;
      }

      return authTokenConfig;
   }

   /**
    * Returns the identity token configuration associated with the
    * specified service.
    *
    * @param hostName      Name of host where the service resides.
    * @param serviceName   Name of the service.
    * @return Identity token configuration object for the specified service.
    */
   public final IdenTokenConfig getIdenTokenConfig(String hostName, String serviceName)
   {
      IdenTokenConfig   idenTokenConfig = null;

      // First try to obtain the Map of enabled services for the host
      Map<Object, SvcConfigEntry> enabledSvcsConfigMap = m_hostsMap.get(hostName.toUpperCase());
      if (enabledSvcsConfigMap != null)
      {
         // Retrieve SvcConfigEntry for the service from the map for the host
         SvcConfigEntry svcConfigEntry = enabledSvcsConfigMap.get(serviceName);
         if (svcConfigEntry != null)
         {
            idenTokenConfig = svcConfigEntry.m_idenTokenConfig;
         }
      }

      // If m_enabledSvcsOnly is configured "false" and if no IdenTokenConfig
      // was found for this service then return the default IdenTokenConfig.
      if (idenTokenConfig == null
          && m_enabledSvcsOnly == false)
      {
         idenTokenConfig = m_defaultIdenTokenConfig;
      }

      return idenTokenConfig;
   }
}