Commit Diff


commit - 5c3be72c5f44a87bd91169e87ddf128994522bde
commit + 38d6902abcd0b0df66b7f3fa21bc0dbaf3c9a184
blob - ba49b47a3801f32259b868d3c0ba6ea0c217e3a8
blob + c5667afd129b86653c2e164d411632fba01fe241
--- src/org/snipsnap/container/DefaultSessionService.java
+++ src/org/snipsnap/container/DefaultSessionService.java
@@ -26,14 +26,18 @@
 package org.snipsnap.container;
 
 import org.radeox.util.logging.Logger;
+import snipsnap.api.storage.UserStorage;
+import org.snipsnap.snip.HomePage;
+import org.snipsnap.user.AuthenticationService;
+import org.snipsnap.user.Digest;
+import org.snipsnap.user.UserManager;
+import org.snipsnap.util.Base64;
+import org.snipsnap.util.X509NameTokenizer;
 import snipsnap.api.app.Application;
 import snipsnap.api.config.Configuration;
-import snipsnap.api.snip.Snip;
 import snipsnap.api.snip.SnipSpace;
-import org.snipsnap.snip.storage.UserStorage;
-import org.snipsnap.user.AuthenticationService;
-import org.snipsnap.user.Digest;
 import snipsnap.api.user.User;
+import snipsnap.api.container.Components;
 
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
@@ -43,6 +47,7 @@ import java.io.BufferedReader;
 import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -51,7 +56,9 @@ public class DefaultSessionService implements SessionS
   private final static String COOKIE_NAME = "SnipSnapUser";
   private final static String ATT_USER = "user";
   private final static int SECONDS_PER_YEAR = 60 * 60 * 24 * 365;
+  private final static int HTTP_UNAUTHORIZED = 401;
 
+
   private Map authHash = new HashMap();
   private Map robots = new HashMap();
   private Map robotIds = new HashMap();
@@ -67,7 +74,7 @@ public class DefaultSessionService implements SessionS
       snipsnap.api.snip.Snip robots = space.load(snipsnap.api.config.Configuration.SNIPSNAP_CONFIG_ROBOTS);
       if (robots != null) {
         BufferedReader crawler = new BufferedReader(new StringReader(robots.getContent()));
-        String line = null;
+        String line;
         int ln = 0;
         while ((line = crawler.readLine()) != null) {
           ln++;
@@ -121,28 +128,82 @@ public class DefaultSessionService implements SessionS
   public User getUser(HttpServletRequest request, HttpServletResponse response) {
     HttpSession session = request.getSession();
     User user = (User) session.getAttribute(ATT_USER);
-    String appOid = (String)Application.get().getObject(Application.OID);
+    String appOid = (String) Application.get().getObject(Application.OID);
     if (null != user && !appOid.equals(user.getApplication())) {
-      user = null; 
+      user = null;
     }
 
+    // TODO: refactor to several session service modules
+    // TODO: Idea: Basic, Digest or Certificate access and possible guest access (Cookie)
     if (null == user) {
-      Cookie cookie = getCookie(request, COOKIE_NAME);
-      if (cookie != null) {
-        String auth = cookie.getValue();
-        if (!authHash.containsKey(auth)) {
-          updateAuthHash();
+      if ("Cookie".equals(Application.get().getConfiguration().getAuth())) {
+        Cookie cookie = getCookie(request, COOKIE_NAME);
+        if (cookie != null) {
+          String auth = cookie.getValue();
+          if (!authHash.containsKey(auth)) {
+            updateAuthHash();
+          }
+
+          user = (User) authHash.get(auth);
+          if (user != null && appOid.equals(user.getApplication())) {
+            user = authService.authenticate(user.getLogin(), user.getPasswd(), AuthenticationService.ENCRYPTED);
+            if (null != user) {
+              setCookie(request, response, user);
+            }
+          } else {
+            Logger.warn("SessionService: invalid hash: " + auth);
+            user = null;
+          }
         }
+      } else if ("Basic".equals(Application.get().getConfiguration().getAuth())) {
+        // make sure the user is authorized
+        String auth = request.getHeader("Authorization");
+        String login = "", password = "";
 
-        user = (User) authHash.get(auth);
-        if (user != null && appOid.equals(user.getApplication())) {
-          user = authService.authenticate(user.getLogin(), user.getPasswd(), AuthenticationService.ENCRYPTED);
-          if(null != user) {
-            setCookie(request, response, user);
+        if (auth != null) {
+          auth = new String(Base64.decode(auth.substring(auth.indexOf(' ') + 1)));
+          login = auth.substring(0, auth.indexOf(':'));
+          password = auth.substring(auth.indexOf(':') + 1);
+        }
+
+        user = authService.authenticate(login, password);
+        if (user == null) {
+          response.setHeader("WWW-Authenticate", "Basic realm=\"SnipSnap\"");
+          response.setStatus(HTTP_UNAUTHORIZED);
+          return null;
+        }
+      } else if ("Certificate".equals(Application.get().getConfiguration().getAuth())) {
+        // Part for authenticating users with X509Certificates. If the user have a trusted client certificate
+        // he can get access to the server. Since the certificate is trusted already, by java/jsse, we don't
+        // have to verify it here.
+        // If the CA puts the users uid in the DN we can use that as login.
+
+        // Check if we have a user in the certificate authentication
+        X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
+        if (certs != null) {
+          X509Certificate clientCert = certs[0];
+          if (clientCert != null) {
+            // Get the Distinguised Name for the user.
+            java.security.Principal userDN = clientCert.getSubjectDN();
+            String dn = userDN.toString();
+            // Get uid, which is the username we will use
+            String uid = getPartFromDN(dn, "UID");
+            String email = getPartFromDN(dn, "emailAddress");
+            // Create users home page if it does not exist
+            UserManager um = (UserManager) Components.getComponent(UserManager.class);
+            user = authService.authenticate(uid);
+            // create a user and home page for new logins
+            if(null == user) {
+              // set password to "*", if we switch back to Cookie auth service
+              // this is no problem as the users password is expected to be encrypted
+              // switching to Basic auth poses a security risk as it compares unencrypted
+              // passwords.
+              user = um.create(uid, "*", email);
+              Application.get().setUser(user, session);
+              HomePage.create(uid);
+              user = authService.authenticate(uid);
+            }
           }
-        } else {
-          Logger.warn("SessionService: invalid hash: " + auth);
-          user = null;
         }
       }
 
@@ -221,9 +282,10 @@ public class DefaultSessionService implements SessionS
 
   /**
    * Helper method for getUser to extract user from request/cookie/session
+   *
    * @param request
    * @param name
-   * @return
+   * @return the cookie
    */
   public Cookie getCookie(HttpServletRequest request, String name) {
     Cookie cookies[] = request.getCookies();
@@ -234,4 +296,32 @@ public class DefaultSessionService implements SessionS
     }
     return null;
   }
+
+  /**
+   * Gets a specified part of a DN. Specifically the first occurrence it the DN contains several
+   * instances of a part (i.e. cn=x, cn=y returns x).
+   *
+   * @param dn     String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
+   * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
+   * @return String containing dnpart or null if dnpart is not present
+   */
+  private String getPartFromDN(String dn, String dnpart) {
+    String part = null;
+    if ((dn != null) && (dnpart != null)) {
+      String o;
+      dnpart += "="; // we search for 'CN=' etc.
+      X509NameTokenizer xt = new X509NameTokenizer(dn);
+      while (xt.hasMoreTokens()) {
+        o = xt.nextToken();
+        if ((o.length() > dnpart.length()) &&
+                o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
+          part = o.substring(dnpart.length());
+          break;
+        }
+      }
+    }
+    return part;
+  } //getPartFromDN
+
+
 }
blob - /dev/null
blob + ac73430ca7c1c228ba4172b321b1ad831dce8e6a (mode 644)
--- /dev/null
+++ src/org/snipsnap/util/X509NameTokenizer.java
@@ -0,0 +1,92 @@
+package org.snipsnap.util;
+
+/**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ * Class originally from the free JCE-provider bouncycastle.org, under BSD-like license.
+ */
+public class X509NameTokenizer
+{
+    private String          value;
+    private int             index;
+    private char            seperator;
+    private StringBuffer    buf = new StringBuffer();
+
+    public X509NameTokenizer(
+        String  oid)
+    {
+        this(oid, ',');
+    }
+    
+    public X509NameTokenizer(
+        String  oid,
+        char    seperator)
+    {
+        this.value = oid;
+        this.index = -1;
+        this.seperator = seperator;
+    }
+
+    public boolean hasMoreTokens()
+    {
+        return (index != value.length());
+    }
+
+    public String nextToken()
+    {
+        if (index == value.length())
+        {
+            return null;
+        }
+
+        int     end = index + 1;
+        boolean quoted = false;
+        boolean escaped = false;
+
+        buf.setLength(0);
+
+        while (end != value.length())
+        {
+            char    c = value.charAt(end);
+
+            if (c == '"')
+            {
+                if (!escaped)
+                {
+                    quoted = !quoted;
+                }
+                else
+                {
+                    buf.append(c);
+                }
+                escaped = false;
+            }
+            else
+            {
+                if (escaped || quoted)
+                {
+                    buf.append(c);
+                    escaped = false;
+                }
+                else if (c == '\\')
+                {
+                    escaped = true;
+                }
+                else if (c == seperator)
+                {
+                    break;
+                }
+                else
+                {
+                    buf.append(c);
+                }
+            }
+            end++;
+        }
+
+        index = end;
+        return buf.toString().trim();
+    }
+}