/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 * Created on Feb 4, 2004
 */
package org.jboss.security.auth.spi;

import java.io.IOException;
import java.security.acl.Group;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

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

import org.jboss.security.SimpleGroup;


/**
 * <dl>
 * <dt><b>Title: </b><dd>Certificate Login Module that uses a properties file to store role information.</dd>
 * <p>
 * <dt><b>Description: </b><dd>This works just like the UsersRolesLoginModule, only without the users.properties file. 
 * In fact, all the role handling code was borrowed directly from that class.
 * </dd>
 * <p>
 * </dl>
 * @see org.jboss.security.auth.spi.BaseCertLoginModule

 * @author <a href="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
 * @author Scott.Stark@jboss.org
 * @version $Revision: 1.1.2.1 $
 */
public class CertRolesLoginModule extends BaseCertLoginModule
{
   /** The name of the properties resource containing user/roles */
   private String rolesRsrcName = "roles.properties";
   /** The roles.properties mappings */
   private Properties roles;

   /** Initialize this LoginModule.
    *@param options - the login module option map. Supported options include:
    *rolesProperties: The name of the properties resource containing user/roles
    The default is "roles.properties".
    */
   public void initialize(Subject subject, CallbackHandler callbackHandler,
      Map sharedState, Map options)
   {
      super.initialize(subject, callbackHandler, sharedState, options);
      if (log.isDebugEnabled())
         log.debug("enter: initialize(Subject, CallbackHandler, Map, Map)");

      try
      {
         String option = (String) options.get("rolesProperties");
         if (option != null)
            rolesRsrcName = option;
         // Load the properties file that contains the list of users and passwords
         loadRoles();
      }
      catch (Exception e)
      {
         // Note that although this exception isn't passed on, users or roles will be null
         // so that any call to login will throw a LoginException.
         super.log.error("Failed to load users/passwords/role files", e);
      }

      if (log.isDebugEnabled())
         log.debug("exit: initialize(Subject, CallbackHandler, Map, Map)");
   }

   public boolean login() throws LoginException
   {
      if (log.isDebugEnabled())
         log.debug("enter: login()");

      if (roles == null)
         throw new LoginException("Missing roles.properties file.");
      boolean wasSuccessful = super.login();

      if (log.isDebugEnabled())
         log.debug("exit: login()");

      return wasSuccessful;
   }

   /**
    * This method is pretty much straight from the UsersRolesLoginModule.
    * @see org.jboss.security.auth.spi.UsersRolesLoginModule#getRoleSets
    */
   protected Group[] getRoleSets() throws LoginException
   {
      if (log.isDebugEnabled())
         log.debug("enter: getRoleSets()");
      String targetUser = getUsername();
      Enumeration users = roles.propertyNames();
      SimpleGroup rolesGroup = new SimpleGroup("Roles");
      ArrayList groups = new ArrayList();
      groups.add(rolesGroup);
      while (users.hasMoreElements() && targetUser != null)
      {
         String user = (String) users.nextElement();
         String value = roles.getProperty(user);
         boolean userMatch = targetUser.equals(user);
         if (userMatch == true)
         {
            // Place these roles into the Default "Roles" group
            parseGroupMembers(rolesGroup, value);
         }
      }
      Group[] roleSets = new Group[groups.size()];
      groups.toArray(roleSets);
      if (log.isDebugEnabled())
         log.debug("exit: getRoleSets()");
      return roleSets;
   }

   /** Parse the comma delimited roles names given by value and add them to
    * group. The type of Principal created for each name is determined by
    * the createIdentity method.
    *
    * @see #createIdentity(String)
    * 
    * @param group - the Group to add the roles to.
    * @param roles - the comma delimited role names.
    */ 
   private void parseGroupMembers(Group group, String roles)
   {
      StringTokenizer tokenizer = new StringTokenizer(roles, ",");
      while (tokenizer.hasMoreTokens())
      {
         String token = tokenizer.nextToken();
         try
         {
            Principal p = createIdentity(token);
            group.addMember(p);
         }
         catch (Exception e)
         {
            log.warn("Failed to create principal for: "+token, e);
         }
      }
   }

   private void loadRoles() throws IOException
   {
      roles = UsersRolesLoginModule.loadProperties(rolesRsrcName, log);
   }

}
