/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.security;

import java.io.IOException;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.venice.except.AccessError;

/**
 * The security monitor "below the root" of the security monitor tree, which defines the three roles
 * which are "outside the standard scopes."  Calls delegated to this instance of <CODE>SecurityMonitor</CODE>
 * generally fail or return <CODE>null</CODE>, except when returning the three defined roles.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
public class PrimordialSecurityMonitor implements SecurityMonitor
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static PrimordialSecurityMonitor self = null;

  private static Category logger = Category.getInstance(PrimordialSecurityMonitor.class);

  // default human readable names
  private static final String NAME_NOT_IN_LIST = "(not in list)";
  private static final String NAME_NO_ACCESS = "No Access";
  private static final String NAME_UNRESTRICTED = "'Unrestricted' User";

  /**
   * The programmatic name of the "not in list" defined role.
   *
   * @see ScopeInfo#L_NOT_THERE
   */
  public static final String SYM_NOT_IN_LIST = "NotInList";

  /**
   * The programmatic name of the "no access" defined role.
   *
   * @see ScopeInfo#L_NO_ACCESS
   */
  public static final String SYM_NO_ACCESS = "NoAccess";

  /**
   * The programmatic name of the "unrestricted user" defined role.
   *
   * @see ScopeInfo#L_UNRESTRICTED
   */
  public static final String SYM_UNRESTRICTED = "UnrestrictedUser";

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private Role not_in_list;              // the "not in list" role
  private Role no_access;                // the "no access" role
  private Role unrestricted_user;        // the "unrestricted user" role

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  /**
   * Creates a new instance of this object.  Only one is ever created.
   */
  private PrimordialSecurityMonitor()
  {
    String name_not_in_list = null, name_no_access = null, name_unrestricted_user = null;

    try
    { // load the three initial role names
      Properties init_props = new Properties();
      init_props.load(getClass().getResourceAsStream("sm.properties"));
      name_not_in_list = init_props.getProperty("role.NotInList.name",NAME_NOT_IN_LIST);
      name_no_access = init_props.getProperty("role.NoAccess.name",NAME_NO_ACCESS);
      name_unrestricted_user = init_props.getProperty("role.UnrestrictedUser.name",NAME_UNRESTRICTED);

    } // end try
    catch (IOException e)
    { // on error, revert to the internal names
      logger.warn("Error loading default names",e);
      name_not_in_list = NAME_NOT_IN_LIST;
      name_no_access = NAME_NO_ACCESS;
      name_unrestricted_user = NAME_UNRESTRICTED;

    } // end catch

    // Create the actual roles.
    this.not_in_list = Role.create(ScopeInfo.L_NOT_THERE,name_not_in_list,SYM_NOT_IN_LIST);
    this.no_access = Role.create(ScopeInfo.L_NO_ACCESS,name_no_access,SYM_NO_ACCESS);
    this.unrestricted_user = Role.create(ScopeInfo.L_UNRESTRICTED,name_unrestricted_user,SYM_UNRESTRICTED);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface SecurityMonitor
   *--------------------------------------------------------------------------------
   */

  /**
   * Tests a specified permission within this security monitor.  If the supplied security level is less
   * than the defined security level for this permission, an exception is thrown.
   * (<CODE>PrimordialSecurityMonitor</CODE> always throws this exception.)
   *
   * @param symbol The programmatic symbol of the permission to test.
   * @param level The security level to supply for the test.
   * @param errormsg The error message to be thrown if the test fails.  May be <CODE>null</CODE>, in
   *                 which case the security monitor will throw an appropriate default, if applicable.
   * @return <CODE>true</CODE> if the specified permission was defined and satisfied; <CODE>false</CODE>
   *         if the specified permission was not defined within this security monitor.
   * @exception com.silverwrist.venice.except.AccessError If the specified permission was defined but
   *            not satisfied.  (Always thrown by <CODE>PrimordialSecurityMonitor</CDOE>.)
   * @see #testPermission(java.lang.String,int)
   */
  public boolean testPermission(String symbol, int level, String errormsg) throws AccessError
  {
    if (symbol==null)
      throw new NullPointerException("testPermission() got null symbol");
    logger.error("testPermission: symbol \"" + symbol + "\" is not a defined permission test");
    throw new AccessError("Undefined internal permission test \"" + symbol + "\"");

  } // end testPermission

  /**
   * Tests a specified permission within this security monitor.  If the supplied security level is greater
   * than or equal to the defined security level for this permission, <CODE>true</CODE> is returned.
   * (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>false</CODE>.)
   *
   * @param symbol The programmatic symbol of the permission to test.
   * @param level The security level to supply for the test.
   * @return <CODE>true</CODE> if the specified permission was defined and satisfied; <CODE>false</CODE>
   *         if the specified permission was not defined within this security monitor, or was defined but
   *         not satisfied.  (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>false</CODE>.)
   * @see #testPermission(java.lang.String,int,java.lang.String)
   */
  public boolean testPermission(String symbol, int level)
  {
    if (symbol==null)
      throw new NullPointerException("testPermission() got null symbol");
    logger.error("testPermission: symbol \"" + symbol + "\" is not a defined permission test");
    return false;

  } // end testPermission

  /**
   * Determines whether this <CODE>SecurityMonitor</CODE> defines the specified permission symbol.
   * (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>false</CODE>.)
   *
   * @param symbol The programmatic symbol of the permission to look up.
   * @param no_follow If this is <CODE>true</CODE>, we look only in this <CODE>SecurityMonitor</CODE>
   *                  for the specified permission, and not in any "parent" <CODE>SecurityMonitor</CODE>
   *                  objects.
   * @return <CODE>true</CODE> if this is a defined permission symbol, <CODE>false</CODE> if not.
   *         (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>false</CODE>.)
   */
  public boolean permissionDefined(String symbol, boolean no_follow)
  {
    if (symbol==null)
      throw new NullPointerException("permissionDefined() got null symbol");
    return false;  // primordial monitor defines no permissions

  } // end permissionDefined

  /**
   * Returns a defined list of roles for this <CODE>SecurityMonitor</CODE>.
   * (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   *
   * @param symbol The programmatic symbol of the role list to look up.
   * @return The associated role list, or <CODE>null</CODE> if the role list name was not defined.
   *         (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   * @see Role
   */
  public List getRoleList(String symbol)
  {
    if (symbol==null)
      throw new NullPointerException("getRoleList() got null symbol");
    logger.error("getRoleList: symbol \"" + symbol + "\" is not a defined role list");
    return null;

  } // end getRoleList

  /**
   * Returns a defined role for this <CODE>SecurityMonitor</CODE>.
   *
   * @param symbol The programmatic symbol of the role to look up.
   * @return The associated role, or <CODE>null</CODE> if the role name was not defined.
   * @see Role
   */
  public Role getRole(String symbol)
  {
    if (symbol==null)
      throw new NullPointerException("getRole() got null symbol");
    if (symbol.equals(SYM_NOT_IN_LIST))
      return not_in_list;
    if (symbol.equals(SYM_NO_ACCESS))
      return no_access;
    if (symbol.equals(SYM_UNRESTRICTED))
      return unrestricted_user;
    logger.error("getRole: symbol \"" + symbol + "\" is not a defined role");
    return null;

  } // end getRole

  /**
   * Returns the defined role that has the given level.
   *
   * @param level The level value to look up.
   * @return The associated role, or <CODE>null</CODE> if no such role could be found.
   * @see Role
   */
  public Role getRoleForLevel(int level)
  {
    if (level==ScopeInfo.L_NOT_THERE)
      return not_in_list;
    if (level==ScopeInfo.L_NO_ACCESS)
      return no_access;
    if (level==ScopeInfo.L_UNRESTRICTED)
      return unrestricted_user;
    logger.error("getRoleForLevel: no role for level " + level);
    return null;

  } // end getRoleForLevel

  /**
   * Returns a defined default role for this <CODE>SecurityMonitor</CODE>.
   * (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   *
   * @param symbol The programmatic symbol of the default role to look up.
   * @return The associated role, or <CODE>null</CODE> if the default role name was not defined.
   *         (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   * @see Role
   */
  public Role getDefaultRole(String symbol)
  {
    logger.error("getDefaultRole: symbol \"" + symbol + "\" has no default role");
    return null;

  } // end getDefaultRole

  /**
   * Returns the <CODE>ScopeInfo</CODE> associated with this <CODE>SecurityMonitor</CODE>.
   * (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   *
   * @return The <CODE>ScopeInfo</CODE> associated with this <CODE>SecurityMonitor</CODE>.
   *         (<CODE>PrimordialSecurityMonitor</CODE> always returns <CODE>null</CODE>.)
   * @see ScopeInfo
   */
  public ScopeInfo getScopeInfo()
  {
    return null;  // this security monitor HAS no scope

  } // end getScopeInfo

  /**
   * Returns the identifier for this <CODE>SecurityMonitor</CODE>. (<CODE>PrimordialSecurityMonitor</CODE>
   * always returns <CODE>null</CODE>.)
   *
   * @return The identifier for this <CODE>SecurityMonitor</CODE>. (<CODE>PrimordialSecurityMonitor</CODE>
   *         always returns <CODE>null</CODE>.)
   */
  public String getID()
  {
    return null;  // this security monitor HAS no ID

  } // end getID

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Returns the singleton instance of the <CODE>PrimordialSecurityMonitor</CODE>.
   *
   * @return The singleton instance of the <CODE>PrimordialSecurityMonitor</CODE>.
   */
  public static synchronized SecurityMonitor get()
  {
    if (self==null)
      self = new PrimordialSecurityMonitor();
    return self;

  } // end get

} // end class PrimordialSecurityMonitor
