hello there,

this patch contains an implementation of the JAAS Login Module 
Configuration provider.


cheers;
rsn
Index: ChangeLog
===================================================================
RCS file: /cvsroot/classpath/classpath/ChangeLog,v
retrieving revision 1.6064
diff -u -r1.6064 ChangeLog
--- ChangeLog	14 Jan 2006 11:14:43 -0000	1.6064
+++ ChangeLog	14 Jan 2006 20:03:38 -0000
@@ -1,3 +1,16 @@
+2006-01-15  Raif S. Naffah  <[EMAIL PROTECTED]>
+
+	* gnu/javax/security/auth/login/ConfigFileTokenizer.java: New class.
+	* gnu/javax/security/auth/login/ConfigFileParser.java: New class.
+	* gnu/javax/security/auth/login/GnuConfiguration.java: New class.
+	* javax/security/auth/login/AppConfigurationEntry.java: Updated
+	  copyright year.
+	  (toString): Added method implementation.
+	  (LoginModuleControlFlag.toString): Removed class name from result.
+	* javax/security/auth/login/Configuration.java: Updated copyright year.
+	  Added documentation.
+	  (getConfig(): replaced calls to NullConfiguration with GnuConfiguration.
+
 2006-01-14  Wolfgang Baer  <[EMAIL PROTECTED]>

 	Fixes bug #25387
Index: ConfigFileParser.java
===================================================================
RCS file: ConfigFileParser.java
diff -N ConfigFileParser.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ConfigFileParser.java	14 Jan 2006 20:01:19 -0000
@@ -0,0 +1,339 @@
+/* ConfigurationParser.java -- JAAS Login Configuration default syntax parser
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.security.auth.login;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.login.AppConfigurationEntry;
+
+/**
+ * A parser that knows how to interpret JAAS Login Module Configuration files
+ * written in the <i>default syntax</i> which is interpreted as adhering to
+ * the following grammar:
+ *
+ * <pre>
+ *   CONFIG              ::= APP_OR_OTHER_ENTRY+
+ *   APP_OR_OTHER_ENTRY  ::= APP_NAME_OR_OTHER JAAS_CONFIG_BLOCK
+ *   APP_NAME_OR_OTHER   ::= APP_NAME
+ *                         | 'other'
+ *   JAAS_CONFIG_BLOCK   ::= '{' (LOGIN_MODULE_ENTRY ';')+ '}' ';'
+ *   LOGIN_MODULE_ENTRY  ::= MODULE_CLASS FLAG MODULE_OPTION* ';'
+ *   FLAG                ::= 'required'
+ *                         | 'requisite'
+ *                         | 'sufficient'
+ *                         | 'optional'
+ *   MODULE_OPTION       ::= PARAM_NAME '=' PARAM_VALUE
+ *
+ *   APP_NAME     ::= JAVA_IDENTIFIER
+ *   MODULE_CLASS ::= JAVA_IDENTIFIER ('.' JAVA_IDENTIFIER)*
+ *   PARAM_NAME   ::= STRING
+ *   PARAM_VALUE  ::= '"' STRING '"' | ''' STRING '''
+ * </pre>
+ *
+ * <p>This parser handles UTF-8 entities when used as APP_NAME and PARAM_VALUE.
+ * It also checks for the use of Java identifiers used in MODULE_CLASS, thus
+ * minimizing the risks of having [EMAIL PROTECTED] java.lang.ClassCastException}s thrown
+ * at runtime due to syntactically invalid names.</p>
+ *
+ * <p>In the above context, a JAVA_IDENTIFIER is a sequence of tokens,
+ * separated by the character '.'. Each of these tokens obeys the following:</p>
+ *
+ * <ol>
+ *   <li>its first character yields <code>true</code> when used as an input to
+ *   the [EMAIL PROTECTED] java.lang.Character#isJavaIdentifierStart(char)}, and</li>
+ *   <li>all remaining characters, yield <code>true</code> when used as an
+ *   input to [EMAIL PROTECTED] java.lang.Character#isJavaIdentifierPart(char)}.</li>
+ * </ol>
+ */
+public final class ConfigFileParser
+{
+  // Constants and fields
+  // --------------------------------------------------------------------------
+
+  private static final boolean DEBUG = false;
+  private static final void debug(String s) {if (DEBUG) System.out.println(s);};
+
+  private ConfigFileTokenizer cft;
+  private Map map = new HashMap();
+
+  // Constructor(s)
+  // --------------------------------------------------------------------------
+
+  // default 0-arguments constructor
+
+  // Class methods
+  // --------------------------------------------------------------------------
+
+  // Instance methods
+  // --------------------------------------------------------------------------
+
+  /**
+   * Returns the parser result as a [EMAIL PROTECTED] Map} where the keys are application
+   * names, and the entries are [EMAIL PROTECTED] List}s of [EMAIL PROTECTED] AppConfigurationEntry}
+   * entries, one for each login module entry, in the order they were
+   * encountered, for that application name in the just parsed configuration
+   * file.
+   */
+  public Map getLoginModulesMap()
+  {
+    return map;
+  }
+
+  /**
+   * Parses the [EMAIL PROTECTED] Reader}'s contents assuming it is in the <i>default
+   * syntax</i>.
+   * @param r the [EMAIL PROTECTED] Reader} whose contents are assumed to be a JAAS Login
+   * Configuration Module file written in the <i>default syntax</i>.
+   * @throws IOException if an exception occurs while parsing the input.
+   */
+  public void parse(Reader r) throws IOException
+  {
+    initParser(r);
+
+    while (parseAppOrOtherEntry())
+      ; // do nothing
+  }
+
+  private void initParser(Reader r) throws IOException
+  {
+    map.clear();
+
+    cft = new ConfigFileTokenizer(r);
+  }
+
+  /**
+   * @return <code>true</code> if an APP_OR_OTHER_ENTRY was correctly parsed.
+   * Returns <code>false</code> otherwise.
+   * @throws IOException if an exception occurs while parsing the input.
+   */
+  private boolean parseAppOrOtherEntry() throws IOException
+  {
+    int c = cft.nextToken();
+    if (c == ConfigFileTokenizer.TT_EOF)
+      return false;
+
+    if (c != ConfigFileTokenizer.TT_WORD)
+      {
+        cft.pushBack();
+        return false;
+      }
+
+    String appName = cft.sval;
+    debug("DEBUG: APP_NAME_OR_OTHER: " + appName);
+    if (cft.nextToken() != '{')
+      abort("Missing '{' after APP_NAME_OR_OTHER");
+
+    List lmis = new ArrayList();
+    while (parseACE(lmis))
+      ; // do nothing
+
+    c = cft.nextToken();
+    if (c != '}')
+      abort("Was expecting '}' but found " + (char) c);
+
+    c = cft.nextToken();
+    if (c != ';')
+      abort("Was expecting ';' but found " + (char) c);
+
+    List listOfACEs = (List) map.get(appName);
+    if (listOfACEs == null)
+      {
+        listOfACEs = new ArrayList();
+        map.put(appName, listOfACEs);
+      }
+    listOfACEs.addAll(lmis);
+    return !appName.equalsIgnoreCase("other");
+  }
+
+  /**
+   * @return <code>true</code> if a LOGIN_MODULE_ENTRY was correctly parsed.
+   * Returns <code>false</code> otherwise.
+   * @throws IOException if an exception occurs while parsing the input.
+   */
+  private boolean parseACE(List listOfACEs) throws IOException
+  {
+    int c = cft.nextToken();
+    if (c != ConfigFileTokenizer.TT_WORD)
+      {
+        cft.pushBack();
+        return false;
+      }
+
+    String clazz = validateClassName(cft.sval);
+    debug("DEBUG: MODULE_CLASS: " + clazz);
+
+    if (cft.nextToken() != ConfigFileTokenizer.TT_WORD)
+      abort("Was expecting FLAG but found none");
+
+    String flag = cft.sval;
+    debug("DEBUG: FLAG: " + flag);
+    AppConfigurationEntry.LoginModuleControlFlag f = null;
+    if (flag.equalsIgnoreCase("required"))
+      f = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
+    else if (flag.equalsIgnoreCase("requisite"))
+      f = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
+    else if (flag.equalsIgnoreCase("sufficient"))
+      f = AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
+    else if (flag.equalsIgnoreCase("optional"))
+      f = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
+    else
+      abort("Unknown Flag: " + flag);
+
+    Map options = new HashMap();
+    String paramName, paramValue;
+    c = cft.nextToken();
+    while (c != ';')
+      {
+        if (c != ConfigFileTokenizer.TT_WORD)
+          abort("Was expecting PARAM_NAME but got '" + ((char) c) + "'");
+
+        paramName = cft.sval;
+        debug("DEBUG: PARAM_NAME: " + paramName);
+        if (cft.nextToken() != '=')
+          abort("Missing '=' after PARAM_NAME");
+
+        c = cft.nextToken();
+        if (c != '"' && c != '\'')
+          abort("Was expecting PARAM_VALUE, as a quoted string, but got none");
+
+        paramValue = expandParamValue(cft.sval);
+        debug("DEBUG: PARAM_VALUE: " + paramValue);
+        options.put(paramName, paramValue);
+
+        c = cft.nextToken();
+      }
+
+    AppConfigurationEntry ace = new AppConfigurationEntry(clazz, f, options);
+    debug("DEBUG: LOGIN_MODULE_ENTRY: " + ace);
+    listOfACEs.add(ace);
+    return true;
+  }
+
+  private void abort(String m) throws IOException
+  {
+    debug("ERROR: " + m);
+    debug("DEBUG: Map (so far): " + String.valueOf(map));
+    throw new IOException(m);
+  }
+
+  private String validateClassName(String cn) throws IOException
+  {
+    if (cn.startsWith(".") || cn.endsWith("."))
+      abort("MODULE_CLASS MUST NOT start or end with a '.'");
+
+    String[] tokens = cn.split(".");
+    for (int i = 0; i < tokens.length; i++)
+      {
+        String t = tokens[i];
+        if (Character.isJavaIdentifierStart(cn.toCharArray()[0]))
+          abort("");
+
+        // we dont check the rest of the characters for isJavaIdentifierPart()
+        // because that's what the tokenizer does.
+      }
+
+    return cn;
+  }
+
+  /**
+   * The documentation of the [EMAIL PROTECTED] javax.security.auth.login.Configuration}
+   * states that: <i>"...If a String in the form, ${system.property}, occurs in
+   * the value, it will be expanded to the value of the system property."</i>.
+   * This method ensures this is the case. If such a string can not be expanded
+   * then it is left AS IS, assuming the LoginModule knows what to do with it.
+   *
+   * <p><b>IMPORTANT</b>: This implementation DOES NOT handle embedded ${}
+   * constructs.
+   *
+   * @param s the raw parameter value, incl. eventually strings of the form
+   * <code>${system.property}</code>.
+   * @return the input string with every occurence of
+   * <code>${system.property}</code> replaced with the value of the
+   * corresponding System property at the time of this method invocation. If
+   * the string is not a known System property name, then the complete sequence
+   * (incl. the ${} characters are passed AS IS.
+   */
+  private String expandParamValue(String s)
+  {
+    String result = s;
+    try
+      {
+        int searchNdx = 0;
+        // FIXME: this logic barfs on the first non-expandable system property
+        // although others after it may be expanded correctly.
+        while (searchNdx < result.length())
+          {
+            int i = s.indexOf("${", searchNdx);
+            if (i == -1)
+              break;
+
+            int j = s.indexOf("}", i + 2);
+            if (j == -1)
+              {
+                debug(" WARN: Found a ${ prefix with no } suffix. Ignore");
+                break;
+              }
+
+            String sysPropName = s.substring(i + 2, j);
+            debug("DEBUG: Found a reference to System property: " + sysPropName);
+            String sysPropValue = System.getProperty(sysPropName);
+            debug("DEBUG: Resolved " + sysPropName + " to '" + sysPropValue + "'");
+
+            if (sysPropValue != null)
+              {
+                result = s.substring(0, i) + sysPropValue + s.substring(j + 1);
+                searchNdx = i + sysPropValue.length();
+              }
+            else
+              searchNdx = j + 1;
+          }
+      }
+    catch (Exception x)
+      {
+        debug("ERROR: Exception while expanding " + s + ". Ignore: " + x);
+      }
+
+    return result;
+  }
+}
Index: Configuration.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/security/auth/login/Configuration.java,v
retrieving revision 1.4
diff -u -r1.4 Configuration.java
--- Configuration.java	9 Sep 2005 12:17:30 -0000	1.4
+++ Configuration.java	14 Jan 2006 20:03:16 -0000
@@ -1,5 +1,5 @@
 /* Configuration.java
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.

 This file is part of GNU Classpath.

@@ -38,15 +38,140 @@

 package javax.security.auth.login;

+import gnu.javax.security.auth.login.GnuConfiguration;
+
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.Security;

 import javax.security.auth.AuthPermission;

+/**
+ * This is an abstract class for representing the configuration of
+ * [EMAIL PROTECTED] javax.security.auth.spi.LoginModule}s under an application. The
+ * <code>Configuration</code> specifies which LoginModules should be used for a
+ * particular application, and in what order the LoginModules should be invoked.
+ * This abstract class, in GNU Classpath, is subclassed by
+ * [EMAIL PROTECTED] gnu.javax.security.auth.login.GnuConfiguration} to provide an
+ * implementation which reads and loads the actual Configuration.
+ *
+ * <p>A login configuration contains the following information. Note that this
+ * example only represents the <i>default syntax</i> for the Configuration.
+ * Subclass implementations of this class may implement alternative syntaxes and
+ * may retrieve the Configuration from any source such as files, databases, or
+ * servers.</p>
+ *
+ * <pre>
+ *    Name {
+ *       ModuleClass  Flag    ModuleOptions;
+ *       ModuleClass  Flag    ModuleOptions;
+ *       ModuleClass  Flag    ModuleOptions;
+ *    };
+ *    Name {
+ *       ModuleClass  Flag    ModuleOptions;
+ *       ModuleClass  Flag    ModuleOptions;
+ *    };
+ *    other {
+ *       ModuleClass  Flag    ModuleOptions;
+ *       ModuleClass  Flag    ModuleOptions;
+ *    };
+ * </pre>
+ *
+ * <p>Each entry in the Configuration is indexed via an application name,
+ * <i>Name</i>, and contains a list of LoginModules configured for that
+ * application. Each LoginModule is specified via its fully qualified class
+ * name. Authentication proceeds down the module list in the exact order
+ * specified. If an application does not have specific entry, it defaults to the
+ * specific entry for <i>"other"</i>.</p>
+ *
+ * <p>The <i>Flag</i> value controls the overall behavior as authentication
+ * proceeds down the stack. The following represents a description of the valid
+ * values for <i>Flag</i> and their respective semantics:</p>
+ *
+ * <pre>
+ *    1) Required     - The LoginModule is required to succeed.
+ *                      If it succeeds or fails, authentication still continues
+ *                      to proceed down the LoginModule list.
+ *
+ *    2) Requisite    - The LoginModule is required to succeed.
+ *                      If it succeeds, authentication continues down the
+ *                      LoginModule list.  If it fails,
+ *                      control immediately returns to the application
+ *                      (authentication does not proceed down the
+ *                      LoginModule list).
+ *
+ *    3) Sufficient   - The LoginModule is not required to
+ *                      succeed.  If it does succeed, control immediately
+ *                      returns to the application (authentication does not
+ *                      proceed down the LoginModule list).
+ *                      If it fails, authentication continues down the
+ *                      LoginModule list.
+ *
+ *    4) Optional     - The LoginModule is not required to
+ *                      succeed.  If it succeeds or fails,
+ *                      authentication still continues to proceed down the
+ *                      LoginModule list.
+ * </pre>
+ *
+ * <p>The overall authentication succeeds only if all <i>Required</i> and
+ * <i>Requisite</i> LoginModules succeed. If a <i>Sufficient</i> LoginModule is
+ * configured and succeeds, then only the <i>Required</i> and <i>Requisite</i>
+ * LoginModules prior to that <i>Sufficient</i> LoginModule need to have
+ * succeeded for the overall authentication to succeed. If no <i>Required</i> or
+ * <i>Requisite</i> LoginModules are configured for an application, then at
+ * least one <i>Sufficient</i> or <i>Optional</i> LoginModule must succeed.</p>
+ *
+ * <p><i>ModuleOptions</i> is a space separated list of LoginModule-specific
+ * values which are passed directly to the underlying LoginModules. Options are
+ * defined by the LoginModule itself, and control the behavior within it. For
+ * example, a LoginModule may define options to support debugging/testing
+ * capabilities. The correct way to specify options in the Configuration is by
+ * using the following key-value pairing: debug="true". The key and value should
+ * be separated by an 'equals' symbol, and the value should be surrounded by
+ * double quotes. If a String in the form, ${system.property}, occurs in the
+ * value, it will be expanded to the value of the system property. Note that
+ * there is no limit to the number of options a LoginModule may define.</p>
+ *
+ * <p>The following represents an example Configuration entry based on the
+ * syntax above:</p>
+ *
+ * <pre>
+ * Login {
+ *    com.sun.security.auth.module.UnixLoginModule required;
+ *    com.sun.security.auth.module.Krb5LoginModule optional
+ *          useTicketCache="true"
+ *          ticketCache="${user.home}${/}tickets";
+ * };
+ * </pre>
+ *
+ * <p>This Configuration specifies that an application named, "Login", requires
+ * users to first authenticate to the
+ * <code>com.sun.security.auth.module.UnixLoginModule</code>, which is required
+ * to succeed. Even if the <code>UnixLoginModule</code> authentication fails,
+ * the <code>com.sun.security.auth.module.Krb5LoginModule</code> still gets
+ * invoked. This helps hide the source of failure. Since the
+ * <code>Krb5LoginModule</code> is <i>Optional</i>, the overall authentication
+ * succeeds only if the <code>UnixLoginModule</code> (<i>Required</i>) succeeds.
+ * </p>
+ *
+ * <p>Also note that the LoginModule-specific options, <i>useTicketCache="true"</i>
+ * and <i>ticketCache=${user.home}${/}tickets"</i>, are passed to the
+ * <code>Krb5LoginModule</code>. These options instruct the
+ * <code>Krb5LoginModule</code> to use the ticket cache at the specified
+ * location. The system properties, <code>user.home</code> and /
+ * (<code>file.separator</code>), are expanded to their respective values.</p>
+ *
+ * <p>The default Configuration implementation can be changed by setting the
+ * value of the "<i>login.configuration.provider</i>" security property (in the
+ * Java security properties file) to the fully qualified name of the desired
+ * Configuration implementation class. The Java security properties file is
+ * located in the file named &lt;JAVA_HOME&gt;/lib/security/java.security, where
+ * &lt;JAVA_HOME&gt; refers to the directory where the JDK was installed.</p>
+ *
+ * @see LoginContext
+ */
 public abstract class Configuration
 {
-
   // Fields.
   // -------------------------------------------------------------------------

@@ -81,8 +206,29 @@
   // Abstract methods.
   // -------------------------------------------------------------------------

+  /**
+   * Retrieves the [EMAIL PROTECTED] AppConfigurationEntry} instances (as an array) for
+   * the designated <i>Application</i> name.
+   *
+   * @param applicationName usually the name of an <i>Application</i> used by a
+   * <code>Configuration</code> to index the entries.
+   * @return an array of [EMAIL PROTECTED] AppConfigurationEntry} instances for the
+   * specified <i>Application</i> name. Returns <code>null</code> if there are
+   * no entries for the specified <code>applicationName</code>.
+   */
   public abstract AppConfigurationEntry[] getAppConfigurationEntry (String applicationName);

+  /**
+   * Refreshes and reloads the <code>Configuration</code>.
+   *
+   * <p>This method causes this <code>Configuration</code> object to
+   * refresh/reload its contents in an implementation-dependent manner. For
+   * example, if this <code>Configuration</code> object stores its entries in a
+   * file, calling [EMAIL PROTECTED] #refresh()} may cause the file to be re-read.</p>
+   *
+   * @throws SecurityException if the caller does not have permission to refresh
+   * its <code>Configuration</code>.
+   */
   public abstract void refresh();

   // Package-private methods.
@@ -108,11 +254,11 @@
             if (conf != null)
               config = (Configuration) Class.forName (conf).newInstance();
             else
-              config = new NullConfiguration();
+              config = new GnuConfiguration();
           }
         catch (Exception x)
           {
-            config = new NullConfiguration();
+            config = new GnuConfiguration();
           }
       }
     return config;
Index: AppConfigurationEntry.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/security/auth/login/AppConfigurationEntry.java,v
retrieving revision 1.2
diff -u -r1.2 AppConfigurationEntry.java
--- AppConfigurationEntry.java	2 Jul 2005 20:32:46 -0000	1.2
+++ AppConfigurationEntry.java	14 Jan 2006 20:03:00 -0000
@@ -1,5 +1,5 @@
 /* AppConfigurationEntry.java
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.

 This file is part of GNU Classpath.

@@ -44,7 +44,6 @@

 public class AppConfigurationEntry
 {
-
   // Fields.
   // -------------------------------------------------------------------------

@@ -61,13 +60,16 @@
   {
     if (loginModuleName == null || loginModuleName.length() == 0)
       throw new IllegalArgumentException ("module name cannot be null nor empty");
+
     if (LoginModuleControlFlag.OPTIONAL != controlFlag &&
         LoginModuleControlFlag.REQUIRED != controlFlag &&
         LoginModuleControlFlag.REQUISITE != controlFlag &&
         LoginModuleControlFlag.SUFFICIENT != controlFlag)
       throw new IllegalArgumentException ("invalid controlFlag");
+
     if (options == null)
       throw new IllegalArgumentException ("options cannot be null");
+
     this.loginModuleName = loginModuleName;
     this.controlFlag = controlFlag;
     this.options = Collections.unmodifiableMap (new HashMap (options));
@@ -91,7 +93,17 @@
     return options;
   }

-// Inner class.
+  // Object methods ----------------------------------------------------------
+
+  public String toString()
+  {
+
+    return loginModuleName + "\t"
+        + String.valueOf(controlFlag) + "\t"
+        + String.valueOf(options);
+  }
+
+  // Inner class.
   // -------------------------------------------------------------------------

   public static class LoginModuleControlFlag
@@ -117,19 +129,15 @@

     public String toString()
     {
-      StringBuffer buf = new StringBuffer (LoginModuleControlFlag.class.getName());
-      buf.append ('.');
-      if (this == OPTIONAL)
-        buf.append ("OPTIONAL");
-      else if (this == REQUIRED)
-        buf.append ("REQUIRED");
-      else if (this == REQUISITE)
-        buf.append ("REQUISITE");
-      else if (this == SUFFICIENT)
-        buf.append ("SUFFICIENT");
-      else
-        buf.append ("HARVEY_THE_RABBIT");
-      return buf.toString();
+      if (this == LoginModuleControlFlag.REQUIRED)
+        return "REQUIRED";
+      if (this == LoginModuleControlFlag.REQUISITE)
+        return "REQUISITE";
+      if (this == LoginModuleControlFlag.SUFFICIENT)
+        return "SUFFICIENT";
+      if (this == LoginModuleControlFlag.OPTIONAL)
+        return "OPTIONAL";
+      return "???";
     }
   }
 }
Index: GnuConfiguration.java
===================================================================
RCS file: GnuConfiguration.java
diff -N GnuConfiguration.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ GnuConfiguration.java	14 Jan 2006 20:02:41 -0000
@@ -0,0 +1,452 @@
+/* GnuConfiguration.java -- GNU Classpath implementation of JAAS Configuration
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.security.auth.login;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.AuthPermission;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+/**
+ * An implementation of the [EMAIL PROTECTED] Configuration} class which interprets JAAS
+ * Login Configuration files written in the <i>default</i> syntax described in
+ * the publicly available documentation of that class. A more formal definition
+ * of this syntax is as follows:
+ *
+ * <pre>
+ *   CONFIG              ::= APP_OR_OTHER_ENTRY+
+ *   APP_OR_OTHER_ENTRY  ::= APP_NAME_OR_OTHER JAAS_CONFIG_BLOCK
+ *   APP_NAME_OR_OTHER   ::= APP_NAME
+ *                         | 'other'
+ *   JAAS_CONFIG_BLOCK   ::= '{' (LOGIN_MODULE_ENTRY ';')+ '}' ';'
+ *   LOGIN_MODULE_ENTRY  ::= MODULE_CLASS FLAG MODULE_OPTION* ';'
+ *   FLAG                ::= 'required'
+ *                         | 'requisite'
+ *                         | 'sufficient'
+ *                         | 'optional'
+ *   MODULE_OPTION       ::= PARAM_NAME '=' PARAM_VALUE
+ *
+ *   APP_NAME     ::= JAVA_IDENTIFIER
+ *   MODULE_CLASS ::= JAVA_IDENTIFIER ('.' JAVA_IDENTIFIER)*
+ *   PARAM_NAME   ::= STRING
+ *   PARAM_VALUE  ::= '"' STRING '"' | ''' STRING '''
+ * </pre>
+ *
+ * <p>This implementation will specifically attempt to process one or more
+ * Login Configuration files in the following locations, and when found parse
+ * them and merge their contents. The locations, and the order in which they are
+ * investigated, follows:</p>
+ *
+ * <ol>
+ *   <li>If the following Security properties:
+ *   <i>java.security.auth.login.config.url.<b>N</b></i>, where <i><b>N</b></i>
+ *   is a digit, from <code>1</code> to an arbitrary number, are defined, then
+ *   the value of each of those properties will be considered as a JAAS Login
+ *   Configuration file written in the default syntax. This implementation will
+ *   attempt parsing all such files.
+ *
+ *   <p>It is worth noting the following:
+ *     <ul>
+ *       <li>The GNU Classpath security file, named <i>classpath.security</i>,
+ *       where all Security properties are encoded, is usually located in
+ *       <i>/usr/local/classpath/lib/security</i> folder.</li>
+ *
+ *       <li>The numbers used in the properties
+ *       <i>java.security.auth.login.config.url.<b>N</b></i> MUST be sequential,
+ *       with no breaks in-between.</li>
+ *     </ul>
+ *   </p>
+ *
+ *   <p>If at least one of the designated Configuration files was found, and
+ *   was parsed correctly, then no other location will be inspected.</p></li>
+ *
+ *   <li>If the System property named <i>java.security.auth.login.config</i>
+ *   is not null or empty, its contents are then interpreted as a URL to a
+ *   JAAS Login Configuration file written in the default syntax.
+ *
+ *   <p>If this System property is defined, and the file it refers to was
+ *   parsed correctly, then no other location will be inspected.</p></li>
+ *
+ *   <li>If a file named <i>.java.login.config</i> or <i>java.login.config</i>
+ *   (in that order) is found in the location referenced by the value of the
+ *   System property <i>user.home</i>, then that file is parsed as a JAAS Login
+ *   Configuration written in the default syntax.</li>
+ *
+ *   <li>If none of the above resulted in a correctly parsed JAAS Login
+ *   Configuration file, then this implementation will install a <i>Null
+ *   Configuration</i> which basically does not recognize any Application.</li>
+ * </ol>
+ */
+public final class GnuConfiguration extends Configuration
+{
+  // Constants and fields
+  // --------------------------------------------------------------------------
+
+  private static final boolean DEBUG = true;
+  private static final void debug(String s) {if (DEBUG) System.out.println(s);};
+
+  /**
+   * The internal map of login modules keyed by application name. Each entry in
+   * this map is a [EMAIL PROTECTED] List} of [EMAIL PROTECTED] AppConfigurationEntry}s for that
+   * application name.
+   */
+  private Map loginModulesMap;
+  /** Our reference to our default syntax parser. */
+  private ConfigFileParser cp;
+
+  // Constructor(s)
+  // --------------------------------------------------------------------------
+
+  /** Trivial 0-arguments Constructor. */
+  public GnuConfiguration()
+  {
+    super();
+
+    loginModulesMap = new HashMap();
+    cp = new ConfigFileParser();
+    init();
+  }
+
+  // Class methods
+  // --------------------------------------------------------------------------
+
+  // Instance methods
+  // --------------------------------------------------------------------------
+
+  // Configuration abstract methods implementation ----------------------------
+
+  /* (non-Javadoc)
+   * @see javax.security.auth.login.Configuration#getAppConfigurationEntry(java.lang.String)
+   */
+  public AppConfigurationEntry[] getAppConfigurationEntry(String appName)
+  {
+    if (appName == null)
+      return null;
+
+    appName = appName.trim();
+    if (appName.length() == 0)
+      return null;
+
+    List loginModules = (List) loginModulesMap.get(appName);
+    if (loginModules == null || loginModules.size() == 0)
+      return null;
+
+    debug("DEBUG: " + appName + " -> " + loginModules.size() + " entry(ies)");
+    return (AppConfigurationEntry[]) loginModules.toArray(new AppConfigurationEntry[0]);
+  }
+
+  /**
+   * Refreshes and reloads this <code>Configuration</code>.
+   *
+   * <p>This method causes this <code>Configuration</code> object to refresh /
+   * reload its contents following the locations and logic described above in
+   * the class documentation section.</p>
+   *
+   * @throws SecurityException if the caller does not have an
+   * [EMAIL PROTECTED] AuthPermission} for the action named
+   * <code>refreshLoginConfiguration</code>.
+   * @see [EMAIL PROTECTED] AuthPermission}
+   */
+  public void refresh()
+  {
+    SecurityManager sm = System.getSecurityManager();
+    if (sm != null)
+      sm.checkPermission(new AuthPermission("refreshLoginConfiguration"));
+
+    loginModulesMap.clear();
+    init();
+  }
+
+  // helper methods -----------------------------------------------------------
+
+  /**
+   * Attempts to find and parse JAAS Login Configuration file(s) written in
+   * the default syntax. The locations searched are as descibed in the class
+   * documentation.
+   */
+  private void init()
+  {
+    if (processSecurityProperties())
+      debug(" INFO: Using login configuration defined by Security property(ies)");
+    else if (processSystemProperty())
+      debug(" INFO: Using login configuration defined by System property");
+    else if (processUserHome())
+      debug(" INFO: Using login configuration defined in ${user.home}");
+    else
+      debug(" WARN: No login configuration file found");
+  }
+
+  /**
+   * Attempts to locate and parse one or more JAAS Login Configuration files
+   * defined as the values of the Security properties
+   * <i>java.security.auth.login.config.url.N</i>.
+   *
+   * @return <code>true</code> if it succeeds, and <code>false</code>
+   *         otherwsie.
+   */
+  private boolean processSecurityProperties()
+  {
+    boolean result = false;
+    int counter = 0;
+    String s;
+    while (true)
+      {
+        counter++;
+        s = Security.getProperty("java.security.auth.login.config.url."
+                                 + counter);
+        if (s == null)
+          break;
+
+        s = s.trim();
+        if (s.length() != 0)
+          {
+            debug(" INFO: java.security.auth.login.config.url." + counter
+                  + " = " + s);
+            try
+              {
+                parseConfig(getInputStreamFromURL(s));
+                result = true;
+              }
+            catch (Throwable t)
+              {
+                debug(" WARN: Exception while handling Security property at #"
+                      + counter + ". Continue: " + t);
+              }
+          }
+      }
+    return result;
+  }
+
+  /**
+   * Attempts to open a designated string as a well-formed [EMAIL PROTECTED] URL}. If a
+   * [EMAIL PROTECTED] MalformedURLException} occurs, this method then tries to open that
+   * string as a [EMAIL PROTECTED] File} (with the same name). If it succeeds, an
+   * [EMAIL PROTECTED] InputStream} is constructed and returned.
+   *
+   * @param s
+   *          the designated name of either a [EMAIL PROTECTED] URL} or a [EMAIL PROTECTED] File}
+   *          assumed to contain a JAAS Login Configuration in the default
+   *          syntax.
+   * @return an [EMAIL PROTECTED] InputStream} around the data source.
+   * @throws IOException
+   *           if an exception occurs during the operation.
+   */
+  private InputStream getInputStreamFromURL(String s) throws IOException
+  {
+    InputStream result = null;
+    try
+      {
+        URL url = new URL(s);
+        result = url.openStream();
+      }
+    catch (MalformedURLException x)
+      {
+        debug(" WARN: Failed opening as URL: " + s + ". Will try as File");
+        result = new FileInputStream(s);
+      }
+    return result;
+  }
+
+  /**
+   * Attempts to locate and parse a JAAS Login Configuration file defined as the
+   * value of the System property <i>java.security.auth.login.config</i>.
+   *
+   * @return <code>true</code> if it succeeds, and <code>false</code>
+   *         otherwsie.
+   */
+  private boolean processSystemProperty()
+  {
+    boolean result = false;
+    try
+      {
+        String s = System.getProperty("java.security.auth.login.config");
+        if (s != null)
+          {
+            s = s.trim();
+            if (s.length() != 0)
+              {
+                debug(" INFO: java.security.auth.login.config = " + s);
+                parseConfig(getInputStreamFromURL(s));
+                result = true;
+              }
+          }
+      }
+    catch (Throwable t)
+      {
+        debug(" WARN: Exception while handling System property. Continue: " + t);
+      }
+    return result;
+  }
+
+  /**
+   * Attempts to locate and parse a JAAS Login Configuration file named either
+   * as <i>.java.login.config</i> or <i>java.login.config</i> (without the
+   * leading dot) in the folder referenced by the System property
+   * <code>user.home</code>.
+   *
+   * @return <code>true</code> if it succeeds, and <code>false</code>
+   *         otherwsie.
+   */
+  private boolean processUserHome()
+  {
+    boolean result = false;
+    try
+      {
+        File userHome = getUserHome();
+        if (userHome == null)
+          return result;
+
+        File jaasFile;
+        jaasFile = getConfigFromUserHome(userHome, ".java.login.config");
+        if (jaasFile == null)
+          jaasFile = getConfigFromUserHome(userHome, "java.login.config");
+
+        if (jaasFile == null)
+          {
+            debug(" INFO: Login Configuration file, in " + userHome
+                  + ", does not exist or is inaccessible");
+            return result;
+          }
+
+        FileInputStream fis = new FileInputStream(jaasFile);
+        parseConfig(fis);
+        result = true;
+      }
+    catch (Throwable t)
+      {
+        debug(" WARN: Exception while handling ${user.home}: " + t);
+      }
+    return result;
+  }
+
+  private void parseConfig(InputStream configStream) throws IOException
+  {
+    cp.parse(new InputStreamReader(configStream, "UTF-8"));
+    Map loginModulesMap = cp.getLoginModulesMap();
+    mergeLoginModules(loginModulesMap);
+  }
+
+  private void mergeLoginModules(Map otherLoginModules)
+  {
+    if (otherLoginModules == null || otherLoginModules.size() < 1)
+      return;
+
+    for (Iterator it = otherLoginModules.keySet().iterator(); it.hasNext();)
+      {
+        String appName = (String) it.next();
+        List thatListOfACEs = (List) otherLoginModules.get(appName);
+        if (thatListOfACEs == null || thatListOfACEs.size() < 1)
+          continue;
+
+        List thisListsOfACEs = (List) loginModulesMap.get(appName);
+        if (thisListsOfACEs == null)
+          loginModulesMap.put(appName, thatListOfACEs);
+        else
+          thisListsOfACEs.addAll(thatListOfACEs);
+      }
+  }
+
+  private File getUserHome()
+  {
+    String uh = System.getProperty("user.home");
+    if (uh == null || uh.trim().length() == 0)
+      {
+        debug(" INFO: User home path is not set or is empty");
+        return null;
+      }
+
+    uh = uh.trim();
+    File result = new File(uh);
+    if (!result.exists())
+      {
+        debug("ERROR: User home '" + uh + "' does not exist");
+        return null;
+      }
+
+    if (!result.isDirectory())
+      {
+        debug("ERROR: User home '" + uh + "' is not a directory");
+        return null;
+      }
+
+    if (!result.canRead())
+      {
+        debug("ERROR: User home '" + uh + "' is not readable");
+        return null;
+      }
+
+    return result;
+  }
+
+  private File getConfigFromUserHome(File userHome, String fileName)
+  {
+    File result = new File(userHome, fileName);
+    if (!result.exists())
+      {
+        debug(" WARN: File '" + fileName + "' does not exist in user's home");
+        return null;
+      }
+
+    if (!result.isFile())
+      {
+        debug("ERROR: File '" + fileName + "' in user's home is not a file");
+        return null;
+      }
+
+    if (!result.canRead())
+      {
+        debug("ERROR: File '" + fileName + "' in user's home is not readable");
+        return null;
+      }
+
+    return result;
+  }
+}
Index: ConfigFileTokenizer.java
===================================================================
RCS file: ConfigFileTokenizer.java
diff -N ConfigFileTokenizer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ConfigFileTokenizer.java	14 Jan 2006 20:02:25 -0000
@@ -0,0 +1,243 @@
+/* ConfigFileTokenizer.java -- JAAS Login Configuration default syntax tokenizer
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.javax.security.auth.login;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A UTF-8 friendly, JAAS Login Module Configuration file tokenizer written in
+ * the deault syntax. This class emulates, to a certain extent, the behavior of
+ * a [EMAIL PROTECTED] java.io.SrteamTokenizer} instance <code>st</code>, when set as
+ * follows:
+ *
+ *  <pre>
+ *  st.resetSyntax();
+ *  st.lowerCaseMode(false);
+ *  st.slashSlashComments(true);
+ *  st.slashStarComments(true);
+ *  st.eolIsSignificant(false);
+ *  st.wordChars('_', '_');
+ *  st.wordChars('$', '$');
+ *  st.wordChars('A', 'Z');
+ *  st.wordChars('a', 'z');
+ *  st.wordChars('0', '9');
+ *  st.wordChars('.', '.');
+ *  st.whitespaceChars(' ', ' ');
+ *  st.whitespaceChars('\t', '\t');
+ *  st.whitespaceChars('\f', '\f');
+ *  st.whitespaceChars('\r', '\r');
+ *  st.whitespaceChars('\n', '\n');
+ *  st.quoteChar('"');
+ *  st.quoteChar('\'');
+ *  </pre>
+ *
+ * <p>The most important (negative) difference with a
+ * [EMAIL PROTECTED] java.io.StreamTokenizer} is that this tokenizer does not properly
+ * handle C++ and Java // style comments in the middle of the line. It only
+ * ignores them if/when found at the start of the line.</p>
+ */
+public class ConfigFileTokenizer
+{
+  // Constants and fields
+  // --------------------------------------------------------------------------
+
+  private static final boolean DEBUG = false;
+  private static final void debug(String s) {if (DEBUG) System.out.println(s);};
+
+  /** A constant indicating that the end of the stream has been read. */
+  public static final int TT_EOF = -1;
+  /** A constant indicating that a word token has been read. */
+  public static final int TT_WORD = -3;
+  /** A constant indicating that no tokens have been read yet. */
+  private static final int TT_NONE = -4;
+
+  public String sval;
+  public int ttype;
+
+  private BufferedReader br;
+  boolean initialised;
+  private StringBuffer sb;
+  private int sbNdx;
+
+  // Constructor(s)
+  // --------------------------------------------------------------------------
+
+  /** Trivial constructor. */
+  ConfigFileTokenizer(Reader r)
+  {
+    super();
+
+    br = r instanceof BufferedReader ? (BufferedReader) r : new BufferedReader(r);
+    initialised = false;
+  }
+
+  // Class methods
+  // --------------------------------------------------------------------------
+
+  // Instance methods
+  // --------------------------------------------------------------------------
+
+  public int nextToken() throws IOException
+  {
+    if (!initialised)
+      init();
+
+    if (sbNdx >= sb.length())
+      return TT_EOF;
+
+    skipWhitespace();
+
+    if (sbNdx >= sb.length())
+      return TT_EOF;
+
+    int endNdx;
+    if (Character.isJavaIdentifierPart(sb.charAt(sbNdx)))
+      {
+        endNdx = sbNdx + 1;
+        while (Character.isJavaIdentifierPart(sb.charAt(endNdx))
+            || sb.charAt(endNdx) == '.')
+          endNdx++;
+
+        ttype = TT_WORD;
+        sval = sb.substring(sbNdx, endNdx);
+        sbNdx = endNdx;
+        return ttype;
+      }
+
+    int c = sb.charAt(sbNdx);
+    if (c == '{' || c == '}' || c == ';' || c == '=')
+      {
+        ttype = c;
+        sbNdx++;
+        return ttype;
+      }
+
+    if (c == '"' || c == '\'')
+      {
+        ttype = c;
+        String quote = sb.substring(sbNdx, sbNdx + 1);
+        int i = sbNdx + 1;
+        while (true)
+          {
+            // find a candidate
+            endNdx = sb.indexOf(quote, i);
+            if (endNdx == -1)
+              abort("Missing closing quote: " + quote);
+
+            // found one; is it escaped?
+            if (sb.charAt(endNdx - 1) != '\\')
+              break;
+
+            i++;
+            continue;
+          }
+
+        endNdx++;
+        sval = sb.substring(sbNdx, endNdx);
+        sbNdx = endNdx;
+        return ttype;
+      }
+
+    abort("Unknown character: " + sb.charAt(sbNdx));
+    return Integer.MIN_VALUE;
+  }
+
+  public void pushBack()
+  {
+    sbNdx -= ttype != TT_WORD ? 1 : sval.length();
+  }
+
+  private void init() throws IOException
+  {
+    sb = new StringBuffer();
+    String line;
+    while ((line = br.readLine()) != null)
+      {
+        line = line.trim();
+        if (line.length() == 0)
+          continue;
+
+        if (line.startsWith("#") || line.startsWith("//"))
+          continue;
+
+        sb.append(line).append(" ");
+      }
+
+    sbNdx = 0;
+    sval = null;
+    ttype = TT_NONE;
+
+    initialised = true;
+  }
+
+  private void skipWhitespace() throws IOException
+  {
+    int endNdx;
+    while (sbNdx < sb.length())
+      if (Character.isWhitespace(sb.charAt(sbNdx)))
+        {
+          sbNdx++;
+          while (sbNdx < sb.length() && Character.isWhitespace(sb.charAt(sbNdx)))
+            sbNdx++;
+
+          continue;
+        }
+      else if (sb.charAt(sbNdx) == '/' && sb.charAt(sbNdx + 1) == '*')
+        {
+          endNdx = sb.indexOf("*/", sbNdx + 2);
+          if (endNdx == -1)
+            abort("Missing closing */ sequence");
+
+          sbNdx = endNdx + 2;
+          continue;
+        }
+      else
+        break;
+  }
+
+  private void abort(String m) throws IOException
+  {
+    debug("ERROR: " + m);
+    debug("DEBUG: sb: " + sb);
+    debug("DEBUG: sbNdx: " + sbNdx);
+    throw new IOException(m);
+  }
+}

Attachment: pgpPVYlSklIiv.pgp
Description: PGP signature

_______________________________________________
Classpath-patches mailing list
Classpath-patches@gnu.org
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to