/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package javax.security.auth.message.config;

import java.security.Permission;
import java.security.Security;
import java.security.SecurityPermission;

import java.util.Map;
// for @see
import java.util.Properties;

import javax.security.auth.message.*;

/**
 * This class is used to obtain <code>AuthConfigProvider</code> objects 
 * that can be used to obtain authentication context configuration objects,
 * i.e., <code>ClientAuthConfig</code> and <code>ServerAuthConfig</code>
 * objects.
 * <p>Authentication context configuration objects are used to obtain
 * authentication context objects. Authentication context objects,
 * i.e., <code>ClientAuthContext</code> and <code>ServerAuthContex</code>
 * objects encapsulate authentication modules. Authentication modules are 
 * pluggable components that perform security-related processing of
 * request and response messages.
 *
 * <p> Callers do not operate on modules directly.
 * Instead they rely on an authentication context to manage the 
 * invocation of modules.  A caller obtains an authentication context
 * by calling the <code>getAuthContext</code> method on a 
 * <code>ClientAuthConfig</code> or <code>ServerAuthConfig</code> 
 * obtained from an AuthConfigProvider.
 *
 * <p> The following represents a typical sequence of calls for obtaining
 * a client authentication context, and then using it to secure 
 * a request.
 * <ol>
 * <li>AuthConfigFactory factory = AuthConfigFactory.getFactory();
 * <li>AuthConfigProvider provider = factory.getConfigProvider(layer,appID,listener);
 * <li>ClientAuthConfig config = provider.getClientAuthConfig(layer,appID,cbh)
 * <li>String authContextID = config.getAuthContextID(messageInfo);
 * <li>ClientAuthContext context = config.getAuthContext(authContextID,subject,properties);
 * <li>context.secureRequest(messageInfo,subject);
 * </ol>
 *
 * <p> A system-wide AuthConfigFactory implementation can be set by invoking
 * <code>setFactory</code>, and retrieved via <code>getFactory</code>.
 *
 * <p> Every implementation of this abstract class must offer a public,
 * zero argument constructor. This constructor must support the construction
 * and registration (including self-registration) of AuthConfigProviders 
 * from a persistent declarative representation.
 * For example, a factory implementation class could interpret the contents
 * of a file containing a sequence of configuration entries, with one entry per
 * AuthConfigProvider, and with each entry representing:
 * <ul>
 * <li> the fully qualified name of the provider implementation class (or null)
 * <li> the list of provider initialization properties (which could be empty)
 * </ul>
 * Any provider initialization properties must be specified in a 
 * form that can be passed to the provider constructor within a Map of key, 
 * value pairs, and where all keys and values within the Map are of type 
 * String.
 * <p>
 * The entry syntax must also provide for the optional inclusion of information
 * sufficient to define a RegistrationContext. This information would only 
 * be present when the factory will register the provider. 
 * For example, each entry could provide for the inclusion of one or more
 * RegistrationContext objects of the following form: 
 * <ul>
 * <li> the message layer name (or null)
 * <li> the application context identifier (or null)
 * <li> the registration description (or null)
 * </ul>
 * When a RegistrationContext is not included, the factory must make it 
 * convenient for the provider to self-register with the factory during 
 * the provider construction (see 
 * <code>registerConfigProvider(AuthConfigProvider provider, ...)</code>).
 * <P> An AuthConfigFactory implementation is free to choose is own 
 * persistent declarative syntax as long as it conforms to the requirements 
 * defined by this class.
 *
 * @version %I%, %G%
 *
 * @see ClientAuthContext
 * @see ServerAuthContext
 * @see ClientAuthConfig
 * @see ServerAuthConfig
 * @see Properties
 */
public abstract class AuthConfigFactory {

    private static AuthConfigFactory factory = null;

    /**
     * the name of the Security property used to define the
     * default AuthConfigFactory implementation class.
     */
    public static final String DEFAULT_FACTORY_SECURITY_PROPERTY = 
    "authconfigprovider.factory";

    // XXX verify that key need not include java.security.
 
    private static SecurityPermission getPerm = 
	new SecurityPermission
    ("getProperty."+DEFAULT_FACTORY_SECURITY_PROPERTY);

    private static SecurityPermission setPerm = 
	new SecurityPermission
    ("setProperty."+DEFAULT_FACTORY_SECURITY_PROPERTY);

    /**
     * utility method to check for permission to operate on the factory.
     *
     * @exception SecurityException if the caller does not have
     *		permission to operate on the factory.
     */
    private static void checkPermission(Permission p) 
	throws SecurityException {
	SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
	    sm.checkPermission(p);
	}
    }

    /**
     * Get the system-wide AuthConfigFactory implementation.
     *
     * <p> If a non-null system-wide factory instance is defined at the time 
     * of the call,
     * e.g., with <code>setFactory</code>, it will be returned. Otherwise, an
     * attempt will be made to construct an instance of the default
     * AuthConfigFactory implementation class. The fully qualified class name
     * of the default factory implementation class is obtained from the
     * value of the "authconfigprovider.factory" security property. When an
     * instance of the default factory implementation class is successfully
     * constructed by this method, this method will set it as the system-wide 
     * factory instance.
     * 
     * <p> The absolute pathname of the Java security properties file is
     * &lt;JAVA_HOME&gt;/lib/security/java.security, where &lt;JAVA_HOME&gt;
     * refers to the directory where the JDK was installed.
     *
     * @return the non-null system-wide AuthConfigFactory instance set at the 
     *          time of the call, or if that value was null, the value of the
     *          system-wide factory instance established by this method. 
     *          This method returns null when the system-wide factory was not 
     *          defined when this method was called and no default factory name
     *          was defined via the security property. 
     *
     * @exception AuthException if an error occurred during the class loading, 
     *          or construction of the default AuthConfigFactory
     *          implementation class.
     *
     * @exception SecurityException if the caller does not have permission
     *		to retrieve the factory, or set it as the system-wide 
     *          instance.
     *
     */
    public static synchronized AuthConfigFactory getFactory() {
	checkPermission(getPerm);
	if (AuthConfigFactory.factory == null) {
	    final String className = Security.getProperty
		(DEFAULT_FACTORY_SECURITY_PROPERTY);
	    if (className != null) {
		checkPermission(setPerm);
		try {
		    AuthConfigFactory.factory = (AuthConfigFactory)
			java.security.AccessController.doPrivileged
			(new java.security.PrivilegedExceptionAction() {
				public Object run() 
				    throws ClassNotFoundException,
					   InstantiationException,
				           IllegalAccessException {
				    ClassLoader loader =
					Thread.currentThread().
					getContextClassLoader();
				    
				    Class clazz = Class.forName
					(className,true,loader);

				    return clazz.newInstance();
				}
			    });
		} catch (java.security.PrivilegedActionException pae) {
		    throw (SecurityException)
			new SecurityException().initCause(pae.getException());
		} 
	    }
	} 
	return AuthConfigFactory.factory;
    }

    /**
     * Set the system-wide AuthConfigFactory implementation.
     *
     * <p> If an implementation was set previously, it will be replaced.
     * <p> listeners are not notified of a change to the registered factory.
     *
     * @param factory the AuthConfigFactory instance, which may be null. 
     *
     * @exception SecurityException if the caller does not have
     *		permission to set the factory.
     */
    public static synchronized void setFactory(AuthConfigFactory factory) {
	checkPermission(setPerm);
	AuthConfigFactory.factory = factory;
    }

    /**
     * Get a registered AuthConfigProvider from the factory.
     *
     * Get the provider of ServerAuthConfig and/or 
     * ClientAuthConfig objects registered for the identified message 
     * layer and application context.
     *
     * <p>All factories shall employ the following precedence rules to select
     * the registered AuthConfigProvider that matches the layer and appContext 
     * arguments:
     *<ul>
     * <li> The provider that is specifically registered for both the 
     * corresponding message layer and appContext 
     * shall be selected.
     * <li> if no provider is selected according to the preceding rule,
     * the provider specifically registered for the 
     * corresponding appContext and for all message layers
     * shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the provider specifically registered for the
     * corresponding message layer and for all appContexts
     * shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the provider registered for all message layers and for all 
     * appContexts shall be selected.
     * <li> if no provider is selected according to the preceding rules,
     * the factory shall terminate its search for a registered provider.
     *</ul>
     * <p>
     * The above precedence rules apply equivalently to registrations created
     * with a null or non-null <code>className</code> argument.
     *
     * @param layer a String identifying the message layer
     *		for which the registered AuthConfigProvider is
     *          to be returned. This argument may NOT be null.
     *
     * @param appContext a String that identifys the application messaging 
     *          context for which the registered AuthConfigProvider
     *          is to be returned. This argument may NOT be null.
     *
     * @param listener the RegistrationListener whose 
     *          <code>notify</code> method is to be invoked 
     *          if the corresponding registration is unregistered or 
     *          replaced. The value of this argument may be null.
     *
     * @return the implementation of the AuthConfigProvider interface 
     *          registered at the factory for the layer and appContext
     *          or null if no AuthConfigProvider is selected. An argument
     *          listener is attached even if the return value is null.
     */
    public abstract AuthConfigProvider 
    getConfigProvider(String layer, String appContext,
		      RegistrationListener listener);

    /**
     * Registers within the factory and records within the factory's  
     * persistent declarative representation of provider registrations
     * a provider of ServerAuthConfig and/or ClientAuthConfig objects for a
     * message layer and application context identifier.
     * 
     * This method typically constructs an instance of the provider
     * before registering it with the factory. Factories may extend or modify 
     * the persisted registrations of existing provider instances, if 
     * those instances were registered with ClassName and 
     * properties arguments equivalent to those passed in the current call. 
     * <P>
     * This method employs the two argument constructor required to
     * be supported by every implementation of the AuthConfigProvider 
     * interface, and this method must pass a null value for the factory 
     * argument of the constructor.
     * <code>AuthConfigProviderImpl AuthConfigProviderImpl(Map properties, 
     * AuthConfigFactory factory)</code> 
     * Instance(properties)</code> 
     *
     * <P>At most one registration may exist within the factory for a 
     * given combination of message layer 
     * and appContext. Any pre-existing
     * registration with identical values for layer and appContext is replaced 
     * by a subsequent registration. When replacement occurs, the registration
     * identifier, layer, and appContext identifier remain unchanged,
     * and the AuthConfigProvider (with initialization properties) and 
     * description are replaced. 
     *
     *<p>Within the lifetime of its Java process, a factory must assign unique
     * registration identifiers to registrations, and must never 
     * assign a previously used registration identifier to a registration 
     * whose message layer and or appContext identifier differ from 
     * the previous use.
     * 
     * <p>Programmatic registrations performed via this method must update
     * (according to the replacement rules described above), the persistent 
     * declarative representation of provider registrations employed by the 
     * factory constructor.
     *
     * @param className the fully qualified name of an AuthConfigProvider
     *          implementation class (or null). Calling this method with a 
     *          null value for this parameter shall cause 
     *          <code>getConfigProvider</code> to return null when it is
     *          called with layer and appContext values for which the 
     *          resulting registration is the best match.
     *
     * @param properties a Map object containing the initialization 
     *          properties to be passed to the properties argument of the
     *          provider constructor.
     *          This argument may be null. When this argument is not null, 
     *          all the values and keys occuring in the Map must be of 
     *          type String.
     *
     * @param layer a String identifying the message layer
     *		for which the provider will be registered at the factory.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered at all layers.
     *
     * @param appContext a String value that may be used by a runtime
     *          to request a configuration object from this provider.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered for all 
     *          configuration ids (at the indicated layers).
     *
     * @param description a text String descripting the provider.
     *          this value may be null.
     *
     * @return a String identifier assigned by 
     *          the factory to the provider registration, and that may be 
     *          used to remove the registration from the factory.
     *
     * @exception SecurityException if the caller does not have
     *		permission to register a provider at the factory.
     *
     * @exception AuthException if the provider 
     *          construction (given a non-null <code>className</code>) 
     *          or registration fails.
     */
    public abstract String 
    registerConfigProvider(String className,
			   Map properties,
			   String layer,
			   String appContext,
			   String description);

    /**
     * Registers within the (in-memory) factory, a provider 
     * of ServerAuthConfig and/or ClientAuthConfig objects for a 
     * message layer and application context identifier.
     * This method does NOT effect the factory's persistent 
     * declarative representation of provider registrations, and is intended
     * to be used by providers to perform self-Registration.
     *
     * <P>At most one registration may exist within the factory for a 
     * given combination of message layer 
     * and appContext. Any pre-existing
     * registration with identical values for layer and appContext is replaced 
     * by a subsequent registration. When replacement occurs, the registration
     * identifier, layer, and appContext identifier remain unchanged,
     * and the AuthConfigProvider (with initialization properties) and 
     * description are replaced. 
     *
     *<p>Within the lifetime of its Java process, a factory must assign unique
     * registration identifiers to registrations, and must never 
     * assign a previously used registration identifier to a registration 
     * whose message layer and or appContext identifier differ from 
     * the previous use.
     * 
     * @param provider the AuthConfigProvider to be registered at the factory
     *          (or null). Calling this method with a null value for this 
     *          parameter shall cause <code>getConfigProvider</code> to 
     *          return null when it iscalled with layer and appContext values 
     *          for which the resulting registration is the best match.
     *
     * @param layer a String identifying the message layer
     *		for which the provider will be registered at the factory.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered at all layers.
     *
     * @param appContext a String value that may be used by a runtime
     *          to request a configuration object from this provider.
     *          A null value may be passed as an argument for this parameter,
     *          in which case, the provider is registered for all 
     *          configuration ids (at the indicated layers).
     *
     * @param description a text String descripting the provider.
     *          this value may be null.
     *
     * @return a String identifier assigned by 
     *          the factory to the provider registration, and that may be 
     *          used to remove the registration from the factory.
     *
     * @exception SecurityException if the caller does not have
     *		permission to register a provider at the factory.
     *
     * @exception AuthException if the provider registration fails.
     */
    public abstract String
    registerConfigProvider(AuthConfigProvider provider,
			   String layer,
			   String appContext,
			   String description);

    /**
     * Remove the identified provider registration from the factory (and from
     * the persistent declarative representation of provider registrations, 
     * if appropriate) and invoke any listeners associated with the removed 
     * registration.
     *
     * @param registrationID a String that identifies a provider registration
     *          at the factory
     *
     * @return true if there was a registration with the specified identifier 
     *          and it was removed. Return false if the registrationID was
     *          invalid.
     *
     * @exception SecurityException if the caller does not have
     *		permission to unregister the provider at the factory.
     *
     */
    public abstract boolean
    removeRegistration(String registrationID);

    /**
     * Disassociate the listener from all the provider
     * registrations whose layer and appContext values are matched
     * by the corresponding arguments to this method.
     * <p>
     * Factories should periodically notify Listeners to effectively
     * detach listeners that are no longer in use.
     *
     * @param listener the RegistrationListener to be detached.
     *
     * @param layer a String identifying the message layer or null.
     *
     * @param appContext a String value identifying the application contex
     *          or null.
     *
     * @return an array of String values where each value identifies a 
     *          provider registration from which the listener was removed.
     *          This method never returns null; it returns an empty array if 
     *          the listener was not removed from any registrations.
     *
     * @exception SecurityException if the caller does not have
     *		permission to detach the listener from the factory.
     *
     */
    public abstract String[]
    detachListener(RegistrationListener listener,
		   String layer, String appContext);

    /**
     * Get the registration identifiers for all registrations of the 
     * provider instance at the factory.
     *
     * @param provider the AuthConfigurationProvider whose registration
     *          identifiers are to be returned. This argument may be
     *          null, in which case, it indicates that the the id's of
     *          all active registrations within the factory are to be returned.
     *
     * @return an array of String values where each value identifies a 
     * provider registration at the factory. This method never returns null;
     * it returns an empty array when their are no registrations at the 
     * factory for the identified provider.
     */
    public abstract String[] getRegistrationIDs(AuthConfigProvider provider);

    /**
     * Get the the registration context for the identified registration.
     *
     * @param registrationID a String that identifies a provider registration
     *          at the factory
     *
     * @return a RegistrationContext or null. When a Non-null value is
     * returned, it is a copy of the registration context corresponding to the
     * registration. Null is returned when the registration identifier does
     * not correpond to an active registration
      */
    public abstract RegistrationContext 
    getRegistrationContext(String registrationID);

   /**
     * Cause the factory to reprocess its persisent declarative 
     * representation of provider registrations. 
     *
     * <p> A factory should only replace an existing registration when 
     * a change of provider implementation class or initialization 
     * properties has occured. 
     *
     * @exception AuthException if an error occured during the 
     *          reinitialization.
     *
     * @exception SecurityException if the caller does not have permission
     *		to refresh the factory.
     */
    public abstract void refresh();

    /**
     * Represents the layer identifier, application context identifier.,
     * and description components of an AuthConfigProvider registration at the
     * factory
     */

    public interface RegistrationContext {

       /**
	* Get the layer name from the registration context
	*
	* @return a String identifying the message layer
	*	  for which the AuthConfigProvider was registered.
	*         the returned value may be null.
	*/

        public String getMessageLayer();
	
       /**
	* Get the application context identifier from the registration context
	*
	* @return a String identifying the application context
	*	  for which the AuthConfigProvider was registered.
	*         the returned value may be null.
	*/
        public String getAppContext();

       /**
	* Get the description from the registration context
	*
	* @return the description String from the registration, or null,
	*         if no description string was included in the registration.
	*/
	public String getDescription();


       /**
	* Get the persisted status from the registration context.
	*
	* @return a boolean indicating whether the registration is the result 
	*         of a className based registration, or an instance based
	*         (e.g. self-) registration. Only registrations performed
	*         using the five argument <code>registerConfigProvider</code>
	*         method are persistent.
	*/
	public boolean isPersistent();

    }

}
