Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / jigsaw / auth / GenericAuthFilter.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/auth/GenericAuthFilter.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/jigsaw/auth/GenericAuthFilter.java
new file mode 100644 (file)
index 0000000..103ff60
--- /dev/null
@@ -0,0 +1,477 @@
+// GenericAuthFilter.java\r
+// $Id: GenericAuthFilter.java,v 1.2 2010/06/15 17:53:03 smhuang Exp $\r
+// (c) COPYRIGHT MIT and INRIA, 1996.\r
+// Please first read the full copyright statement in file COPYRIGHT.html\r
+\r
+package org.w3c.jigsaw.auth;\r
+\r
+import java.io.PrintStream;\r
+\r
+import java.util.Enumeration;\r
+\r
+import java.net.InetAddress;\r
+\r
+import org.w3c.tools.resources.Attribute;\r
+import org.w3c.tools.resources.AttributeHolder;\r
+import org.w3c.tools.resources.AttributeRegistry;\r
+import org.w3c.tools.resources.FramedResource;\r
+import org.w3c.tools.resources.InvalidResourceException;\r
+import org.w3c.tools.resources.ProtocolException;\r
+import org.w3c.tools.resources.Resource;\r
+import org.w3c.tools.resources.ResourceFilter;\r
+import org.w3c.tools.resources.ResourceFrame;\r
+import org.w3c.tools.resources.ResourceReference;\r
+import org.w3c.tools.resources.StringArrayAttribute;\r
+\r
+import org.w3c.jigsaw.http.Client;\r
+import org.w3c.jigsaw.http.HTTPException;\r
+import org.w3c.jigsaw.http.Reply;\r
+import org.w3c.jigsaw.http.Request;\r
+import org.w3c.jigsaw.http.httpd;\r
+\r
+import org.w3c.jigsaw.html.HtmlGenerator;\r
+\r
+import org.w3c.tools.codec.Base64Decoder;\r
+import org.w3c.tools.codec.Base64FormatException;\r
+\r
+import org.w3c.www.http.HTTP;\r
+import org.w3c.www.http.HttpChallenge;\r
+import org.w3c.www.http.HttpCredential;\r
+import org.w3c.www.http.HttpFactory;\r
+import org.w3c.www.http.HttpReplyMessage;\r
+import org.w3c.www.http.HttpRequestMessage;\r
+\r
+import org.w3c.tools.resources.ProtocolException;\r
+\r
+/**\r
+ * A generic authentication filter.\r
+ * This filter will use both IP and basic authentication to try to authenticate\r
+ * incomming request. It should not be use for big user's database (typically\r
+ * the ones that have more than 1000 entries).\r
+ */\r
+\r
+class BasicAuthContextException extends Exception {\r
+\r
+    BasicAuthContextException (String msg) {\r
+       super (msg) ;\r
+    }\r
+}\r
+\r
+class BasicAuthContext {\r
+    String user     = null ;\r
+    String password = null ;\r
+    String cookie   = null ;\r
+\r
+    public String toString() {\r
+       return user+":"+password;\r
+    }\r
+\r
+    BasicAuthContext (Request request) \r
+       throws BasicAuthContextException, ProtocolException\r
+    {\r
+       HttpCredential credential = null;\r
+\r
+       credential = (request.isProxy()\r
+                     ? request.getProxyAuthorization()\r
+                     : request.getAuthorization());\r
+       if ( ! credential.getScheme().equalsIgnoreCase("Basic") ) {\r
+           String msg = ("Invalid authentication scheme \""\r
+                         + credential.getScheme()\r
+                         + " expecting \"Basic\"");\r
+           throw new BasicAuthContextException (msg) ;\r
+       }\r
+       // Decode the credentials:\r
+       String decoded = null ;\r
+       this.cookie    = credential.getAuthParameter("cookie");\r
+       try {\r
+           Base64Decoder b  = new Base64Decoder (cookie) ;\r
+           decoded          = b.processString() ;\r
+       } catch (Base64FormatException e) {\r
+           String msg = "Invalid BASE64 encoding of credentials." ;\r
+           throw new BasicAuthContextException (msg) ;\r
+       }\r
+       // Get user and password:\r
+       int icolon = decoded.indexOf (':') ;\r
+       if ( (icolon > 0) && (icolon+1 < decoded.length()) ) {\r
+           // ok, parse was find, check user:\r
+           this.user     = decoded.substring (0, icolon) ;\r
+           this.password = decoded.substring (icolon+1) ;\r
+       } else {\r
+           String msg = "Invalid credentials syntax in " + decoded ;\r
+           throw new BasicAuthContextException (msg) ;\r
+       }\r
+    }\r
+}\r
+\r
+/**\r
+ * GenericAuthFilter provides for both IP and basic authentication.\r
+ * This is really a first implementation. It looses on several points:\r
+ * <ul>\r
+ * <li>AuthUser instances, being a subclass of resource dump their classes\r
+ * along with their attributes, although here we know that they will all\r
+ * be instances of AuthUser.\r
+ * <li>The way the ipmatcher is maintained doesn't make much sense.\r
+ * <li>The way groups are handled is no good.\r
+ * <li>The SimpleResourceStore is not an adequat store for the user database,\r
+ * it should rather use the jdbmResourceStore (not written yet).\r
+ * </ul>\r
+ * However, this provides for the basic functionnalities.\r
+ */\r
+\r
+public class GenericAuthFilter extends AuthFilter {\r
+    /**\r
+     * Attribute index - The list of allowed users.\r
+     */\r
+    protected static int ATTR_ALLOWED_USERS = -1 ;\r
+    /**\r
+     * Attribute index - The list of allowed groups.\r
+     */\r
+    protected static int ATTR_ALLOWED_GROUPS = -1 ;\r
+\r
+    static {\r
+       Attribute   a = null ;\r
+       Class       c = null ;\r
+       try {\r
+           c = Class.forName("org.w3c.jigsaw.auth.GenericAuthFilter");\r
+           //Added by Jeff Huang\r
+           //TODO: FIXIT\r
+       } catch (Exception ex) {\r
+           ex.printStackTrace() ;\r
+           System.exit(1) ;\r
+       }\r
+       // The list of allowed users\r
+       a = new StringArrayAttribute("users"\r
+                                    , null\r
+                                    , Attribute.EDITABLE) ;\r
+       ATTR_ALLOWED_USERS = AttributeRegistry.registerAttribute(c, a) ;\r
+       // The list of allowed groups:\r
+       a = new StringArrayAttribute("groups"\r
+                                    , null\r
+                                    , Attribute.EDITABLE);\r
+       ATTR_ALLOWED_GROUPS = AttributeRegistry.registerAttribute(c, a) ;\r
+    }\r
+\r
+    /**\r
+     * The IPMatcher to match IP templates to user records.\r
+     */\r
+    protected IPMatcher ipmatcher = null ;\r
+    /**\r
+     * The catalog of realms that make our scope.\r
+     */\r
+    protected RealmsCatalog catalog = null ;\r
+    /**\r
+     * Our associated realm.\r
+     */\r
+    protected ResourceReference rr_realm = null ;\r
+    /**\r
+     * The nam of the realm we cache in <code>realm</code>.\r
+     */\r
+    protected String loaded_realm = null ;\r
+\r
+    /**\r
+     * The challenge to issue to any client for Basic Authentication.\r
+     */\r
+    protected HttpChallenge challenge = null;\r
+\r
+    /**\r
+     * Get a pointer to our realm, and initialize our ipmatcher.\r
+     */\r
+\r
+    protected synchronized void acquireRealm() {\r
+       // Get our catalog:\r
+       if ( catalog == null ) {\r
+           httpd server = (httpd) \r
+               ((FramedResource) getTargetResource()).getServer() ;\r
+           catalog = server.getRealmsCatalog() ;\r
+       }\r
+       // Check that our realm name is valid:\r
+       String name = getRealm() ;\r
+       if ( name == null )\r
+           return ;\r
+       if ((rr_realm != null) && name.equals(loaded_realm)) \r
+           return ;\r
+       // Load the realm and create the ipmtacher object\r
+       rr_realm = catalog.loadRealm(name) ;\r
+       if (rr_realm != null) {\r
+           try {\r
+               AuthRealm realm = (AuthRealm) rr_realm.lock();\r
+               Enumeration e = realm.enumerateUserNames() ;\r
+               if (e.hasMoreElements()) {\r
+                   ipmatcher       = new IPMatcher() ;\r
+               }\r
+               while (e.hasMoreElements()) {\r
+                   String   uname = (String) e.nextElement() ;\r
+                   ResourceReference rr_user = realm.loadUser(uname) ;\r
+                   try {\r
+                       AuthUser user  = (AuthUser) rr_user.lock();\r
+                       short    ips[][] = user.getIPTemplates() ;\r
+                       if ( ips != null ) {\r
+                           for (int i = 0 ; i < ips.length ; i++) \r
+                               ipmatcher.add(ips[i], rr_user) ;\r
+                       }\r
+                   } catch (InvalidResourceException ex) {\r
+                       System.out.println("Invalid user reference : "+uname);\r
+                   } finally {\r
+                       rr_user.unlock();\r
+                   }\r
+               }\r
+           } catch (InvalidResourceException ex) {\r
+\r
+           } finally {\r
+               rr_realm.unlock();\r
+           }\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Check that our realm does exist.\r
+     * Otherwise we are probably being initialized, and we don't authenticate\r
+     * yet.\r
+     * @return A boolean <strong>true</strong> if realm can be initialized.\r
+     */\r
+\r
+    protected synchronized boolean checkRealm() {\r
+       acquireRealm() ;\r
+       return (ipmatcher != null) ;\r
+    }\r
+\r
+    /**\r
+     * Get the list of allowed users.\r
+     */\r
+\r
+    public String[] getAllowedUsers() {\r
+       return (String[]) getValue(ATTR_ALLOWED_USERS, null) ;\r
+    }\r
+\r
+    /**\r
+     * Get the list of allowed groups.\r
+     */\r
+\r
+    public String[] getAllowedGroups() {\r
+       return (String[]) getValue(ATTR_ALLOWED_GROUPS, null) ;\r
+    }\r
+\r
+    /**\r
+     * Lookup a user by its IP address.\r
+     * @param ipaddr The IP address to look for.\r
+     * @return An AuthUser instance or <strong>null</strong>.\r
+     */\r
+\r
+    public synchronized ResourceReference lookupUser (InetAddress ipaddr) {\r
+       if ( ipmatcher == null )\r
+           acquireRealm() ;\r
+       return (ResourceReference) ipmatcher.lookup(ipaddr.getAddress()) ;\r
+    }\r
+\r
+    /**\r
+     * Lookup a user by its name.\r
+     * @param name The user's name.\r
+     * @return An AuthUser instance, or <strong>null</strong>.\r
+     */\r
+\r
+    public synchronized ResourceReference lookupUser (String name) {\r
+       if ( rr_realm == null )\r
+           acquireRealm() ;\r
+       try {\r
+           AuthRealm realm = (AuthRealm) rr_realm.lock();\r
+           return realm.loadUser(name) ;\r
+       } catch (InvalidResourceException ex) {\r
+           return null;\r
+       } finally {\r
+           rr_realm.unlock();\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Check the given Basic context against our database.\r
+     * @param ctxt The basic auth context to check.\r
+     * @return A AuthUser instance if check succeeded, <strong>null</strong>\r
+     *    otherwise.\r
+     */\r
+\r
+    protected ResourceReference checkBasicAuth(BasicAuthContext ctxt) {\r
+       ResourceReference rr_user = (ResourceReference)lookupUser(ctxt.user) ;\r
+       if (rr_user != null) {\r
+           try {\r
+               AuthUser user = (AuthUser) rr_user.lock();\r
+               // This user doesn't even exists !\r
+               if ( user == null )\r
+                   return null ;\r
+               // If it has a password check it\r
+               if ( ! user.definesAttribute("password") ) {\r
+                   return  rr_user;\r
+               } else {\r
+                   return user.getPassword().equals(ctxt.password) \r
+                       ? rr_user \r
+                       : null ;\r
+               }\r
+           } catch (InvalidResourceException ex) {\r
+               return null;\r
+           } finally {\r
+               rr_user.unlock();\r
+           }\r
+       }\r
+       return null;\r
+    }\r
+\r
+    /**\r
+     * Is this user allowed in the realm ?\r
+     * First check in the list of allowed users (if any), than in the list\r
+     * of allowed groups (if any). If no allowed users or allowed groups\r
+     * are defined, than simply check for the existence of this user.\r
+     * @return A boolean <strong>true</strong> if access allowed.\r
+     */\r
+\r
+    protected boolean checkUser(AuthUser user) {\r
+       String allowed_users[] = getAllowedUsers() ;\r
+       // Check in the list of allowed users:\r
+       if ( allowed_users != null ) {\r
+           for (int i = 0 ; i < allowed_users.length ; i++) {\r
+               if (allowed_users[i].equals(user.getName()))\r
+                   return true ;\r
+           }\r
+       }\r
+       // Check in the list of allowed groups:\r
+       String allowed_groups[] = getAllowedGroups() ;\r
+       if ( allowed_groups != null ) {\r
+           String ugroups[] = user.getGroups() ;\r
+           if ( ugroups != null ) {\r
+               for (int i = 0 ; i < ugroups.length ; i++) {\r
+                   for (int j = 0 ; j < allowed_groups.length ; j++) {\r
+                       if ( allowed_groups[j].equals(ugroups[i]) ) \r
+                           return true ;\r
+                   }\r
+               }\r
+           }\r
+       }\r
+       // If no users or groups specified, return true\r
+       if ((allowed_users == null) && (allowed_groups == null)) \r
+           return true ;\r
+       return false ;\r
+    }\r
+\r
+    /**\r
+     * Catch set value on the realm, to maintain cached values.\r
+     */\r
+\r
+    public void setValue(int idx, Object value) {\r
+       super.setValue(idx, value);\r
+       if ( idx == ATTR_REALM ) {\r
+           // Initialize the filter challenge:\r
+           challenge = HttpFactory.makeChallenge("Basic");\r
+           challenge.setAuthParameter("realm", getRealm());\r
+       }\r
+    }\r
+\r
+    /**\r
+     * Authenticate the given request.\r
+     * We first check for valid authentication information. If no \r
+     * authentication is provided, than we try to map the IP address to some\r
+     * of the ones we know about. If the IP address is not found, we challenge\r
+     * the client for a password.\r
+     * <p>If the IP address is found, than either our user entry requires an\r
+     * extra password step (in wich case we challenge it), or simple IP\r
+     * based authentication is enough, so we allow the request.\r
+     * @param request The request to be authentified.\r
+     * @exception org.w3c.tools.resources.ProtocolException if authentication\r
+     * failed\r
+     */\r
+\r
+    public void authenticate (Request request) \r
+       throws ProtocolException \r
+    {\r
+       // Are we being edited ?\r
+       if ( ! checkRealm() )\r
+           return ;\r
+       // Internal requests always allowed:\r
+       Client client = request.getClient() ;\r
+       if ( client == null )\r
+           return ;\r
+       // Check for User by IP address:\r
+       boolean ipchecked = false ;\r
+       ResourceReference rr_user = lookupUser(client.getInetAddress());\r
+       if (rr_user != null) {\r
+           try {\r
+               AuthUser user = (AuthUser) rr_user.lock();\r
+               if ( user != null ) {\r
+                   ipchecked = true ;\r
+                   // Good the user exists, does it need more authentication ?\r
+                   if ( ! user.definesAttribute("password") &&\r
+                        checkUser(user)) {\r
+                       request.setState(STATE_AUTHUSER, user.getName()) ;\r
+                       request.setState(STATE_AUTHTYPE, "ip");\r
+                       return ;\r
+                   }\r
+               }\r
+           } catch (InvalidResourceException ex) {\r
+               //FIXME\r
+           } finally {\r
+               rr_user.unlock();\r
+           }\r
+       }\r
+       // Check authentication according to auth method:\r
+       if ((request.hasAuthorization() && ! request.isProxy())\r
+           || (request.isProxy() && request.hasProxyAuthorization())) {\r
+           BasicAuthContext ctxt = null ;\r
+           try {\r
+               ctxt = new BasicAuthContext(request);\r
+           } catch (BasicAuthContextException ex) {\r
+               ctxt = null;\r
+           }\r
+           // Is that user allowed ?\r
+           if ( ctxt != null ) {\r
+               rr_user = checkBasicAuth(ctxt) ;\r
+               if (rr_user != null) {\r
+                   try {\r
+                       AuthUser user = (AuthUser) rr_user.lock();\r
+                       if ((user != null) && checkUser(user)) {\r
+                           // Check that if IP auth was required,\r
+                           // it succeeded:\r
+                           boolean iprequired = \r
+                               user.definesAttribute("ipaddress") ;\r
+                           if ( ( ! iprequired) || ipchecked ) {\r
+                               // Set the request fields, and continue:\r
+                               request.setState(STATE_AUTHUSER, ctxt.user);\r
+                               request.setState(STATE_AUTHTYPE, "Basic") ;\r
+                               return ;\r
+                           }\r
+                       }\r
+                   } catch (InvalidResourceException ex) {\r
+                       //FIXME\r
+                   } finally {\r
+                       rr_user.unlock();\r
+                   }\r
+               } \r
+           }\r
+       }\r
+                       \r
+       // Every possible scheme has failed for this request, emit an error\r
+       Reply e = null;\r
+       if ( request.isProxy() ) {\r
+           e = request.makeReply(HTTP.PROXY_AUTH_REQUIRED);\r
+           e.setProxyAuthenticate(challenge);\r
+       } else {\r
+           e = request.makeReply(HTTP.UNAUTHORIZED);\r
+           e.setWWWAuthenticate (challenge);\r
+       }\r
+       HtmlGenerator g = new HtmlGenerator("Unauthorized");\r
+       g.append ("<h1>Unauthorized access</h1>"\r
+                 + "<p>You are denied access to this resource.");\r
+       e.setStream(g);\r
+       throw new HTTPException (e);\r
+    }\r
+\r
+    /**\r
+     * Initialize the filter.\r
+     */\r
+\r
+    public void initialize(Object values[]) {\r
+       super.initialize(values) ;\r
+       if ( getRealm() != null ) {\r
+           // Initialize the filter challenge:\r
+           challenge = HttpFactory.makeChallenge("Basic");\r
+           challenge.setAuthParameter("realm", getRealm());\r
+       }\r
+    }\r
+\r
+}\r