added /test/authinfo resource, that displays very simply the authentication information. It is now easy to just add an Authz filter to enable or disable access to resourcs. webid
authorHenry Story <henry.story@bblfish.net>
Fri, 14 Oct 2011 00:44:05 +0200
branchwebid
changeset 60 fcfad2d7d118
parent 59 34ec240b4fd2
child 62 419ae72e4ecf
added /test/authinfo resource, that displays very simply the authentication information. It is now easy to just add an Authz filter to enable or disable access to resourcs.
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/AuthenticationFilter.scala
src/main/scala/auth/Authn.scala
src/main/scala/auth/X509view.scala
--- a/src/main/scala/ReadWriteWebMain.scala	Thu Oct 13 21:50:21 2011 +0200
+++ b/src/main/scala/ReadWriteWebMain.scala	Fri Oct 14 00:44:05 2011 +0200
@@ -1,9 +1,8 @@
 package org.w3.readwriteweb
 
+import auth.X509view
 import org.w3.readwriteweb.util._
 
-import javax.servlet._
-import javax.servlet.http._
 import unfiltered.jetty._
 import java.io.File
 import Console.err
@@ -11,7 +10,6 @@
 
 import org.clapper.argot._
 import ArgotConverters._
-
 object ReadWriteWebMain {
 
   val logger:Logger = LoggerFactory.getLogger(this.getClass)
@@ -94,10 +92,12 @@
 
     // configures and launches a Jetty server
     service.filter(new FilterLogger(logger)).
-      filter(new auth.Authn).
+      filter(new auth.AuthenticationFilter).
       context("/public"){ ctx:ContextBuilder =>
       ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
-    }.filter(app.plan).run()
+    }.
+      filter(X509view.plan).
+      filter(app.plan).run()
 
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/auth/AuthenticationFilter.scala	Fri Oct 14 00:44:05 2011 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2011 Henry Story (bblfish.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Henry Story.  The name of bblfish.net may not be used to endorse
+ * or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+package org.w3.readwriteweb.auth
+
+import java.security.cert.X509Certificate
+import javax.servlet._
+import org.w3.readwriteweb._
+
+import collection.JavaConversions._
+import javax.security.auth.Subject
+import java.security.PrivilegedExceptionAction
+import java.util.concurrent.TimeUnit
+import com.google.common.cache.{CacheBuilder, Cache, CacheLoader}
+
+/**
+ * This filter places the all the principals into a Subject,
+ * which can then be accessed later on in by the code.
+ *
+ * note: It would be better if this were only called at the point when authentication
+ * is needed. That is in fact possible with TLS renegotiation, but requires a server that allows
+ * access to the TLS layer. This is an intermediary solution.
+ */
+class AuthenticationFilter(implicit webCache: WebCache) extends Filter {
+  def init(filterConfig: FilterConfig) {}
+
+  val idCache: Cache[X509Certificate, X509Claim] =
+    CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).
+      build(new CacheLoader[X509Certificate, X509Claim]() {
+        def load(x509: X509Certificate) = new X509Claim(x509)
+    })
+
+
+  def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
+    val certChain = request.getAttribute("javax.servlet.request.X509Certificate") match {
+      case certs: Array[X509Certificate] => certs.toList
+      case _ => Nil
+    }
+
+    val subject = new Subject()
+    if (certChain.size == 0) {
+      System.err.println("No certificate found!")
+      subject.getPrincipals.add(Anonymous())
+    } else {
+      val x509c = idCache.get(certChain.get(0))
+      subject.getPublicCredentials.add(x509c)
+      val verified = for (
+        claim <- x509c.webidclaims;
+        if (claim.verified)
+      ) yield claim.principal
+      subject.getPrincipals.addAll(verified)
+      System.err.println("Found "+verified.size+" principals: "+verified)
+    }
+    try {
+      Subject.doAs(subject,new PrivilegedExceptionAction[Unit]() { def run(): Unit = chain.doFilter(request, response) } )
+    } catch {
+      case e: Exception => System.err.println("cought "+e)
+    }
+//    chain.doFilter(request, response)
+  }
+
+  def destroy() {}
+}
+
--- a/src/main/scala/auth/Authn.scala	Thu Oct 13 21:50:21 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright (c) 2011 Henry Story (bblfish.net)
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Henry Story.  The name of bblfish.net may not be used to endorse
- * or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-package org.w3.readwriteweb.auth
-
-import java.security.cert.X509Certificate
-import javax.servlet._
-import org.w3.readwriteweb._
-
-import collection.JavaConversions._
-import javax.security.auth.Subject
-import java.security.PrivilegedExceptionAction
-import java.util.concurrent.TimeUnit
-import com.google.common.cache.{CacheBuilder, Cache, CacheLoader}
-
-class Authn(implicit webCache: WebCache) extends Filter {
-  def init(filterConfig: FilterConfig) {}
-
-  val idCache: Cache[X509Certificate, X509Claim] =
-    CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).
-      build(new CacheLoader[X509Certificate, X509Claim]() {
-        def load(x509: X509Certificate) = new X509Claim(x509)
-    })
-
-
-  def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
-    val certChain = request.getAttribute("javax.servlet.request.X509Certificate") match {
-      case certs: Array[X509Certificate] => certs.toList
-      case _ => Nil
-    }
-
-    val subject = new Subject()
-    if (certChain.size == 0) {
-      System.err.println("No certificate found!")
-      subject.getPrincipals.add(Anonymous())
-    } else {
-      val x509c = idCache.get(certChain.get(0))
-      subject.getPublicCredentials.add(x509c)
-      val verified = for (
-        claim <- x509c.webidclaims;
-        if (claim.verified)
-      ) yield claim.principal
-      subject.getPrincipals.addAll(verified)
-      System.err.println("Found "+verified.size+" principals: "+verified)
-    }
-    try {
-      Subject.doAs(subject,new PrivilegedExceptionAction[Unit]() { def run(): Unit = chain.doFilter(request, response) } )
-    } catch {
-      case e: Exception => System.err.println("cought "+e)
-    }
-//    chain.doFilter(request, response)
-  }
-
-  def destroy() {}
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/auth/X509view.scala	Fri Oct 14 00:44:05 2011 +0200
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2011 Henry Story (bblfish.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Henry Story.  The name of bblfish.net may not be used to endorse 
+ * or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+package org.w3.readwriteweb.auth
+
+import javax.security.auth.Subject
+import java.security.{PrivilegedExceptionAction, PrivilegedActionException, AccessController}
+import unfiltered.response.{Html, ContentType, Ok}
+import unfiltered.request.Path
+import collection.JavaConversions._
+
+/**
+ * This plan just described the authentication information.
+ * This is a simple version. A future version will show EARL output, and so be useful for debugging the endpoint.
+ *
+ * @author hjs
+ * @created: 13/10/2011
+ */
+
+object X509view {
+    val plan = unfiltered.filter.Planify {
+      case req @ Path(path) if path startsWith "/test/authinfo"=> {
+        val context = AccessController.getContext
+        val subj = try {
+          AccessController.doPrivileged(new PrivilegedExceptionAction[Option[Subject]] {
+            def run = Option(Subject.getSubject(context))
+          })
+        } catch {
+          case ex: PrivilegedActionException => {
+            ex.getCause match {
+              case runE: RuntimeException => throw runE
+              case e => {
+                System.out.println("error " + e)
+                None
+              }
+            }
+          }
+          case _ => None
+        }
+        Ok ~> ContentType("text/html") ~> Html(<html><head><title>Authentication Page</title></head>
+          <body><h1>Authentication Info received</h1>
+            {subj match {
+          case Some(x) => <span><p>You were identified with the following WebIDs</p>
+             <ul>{x.getPrincipals.map(p=> <li>{p}</li>)}</ul>
+            {val certs = x.getPublicCredentials(classOf[X509Claim])
+            if (certs.size() >0) <span><p>You sent the following certificate</p>
+            <pre>{certs.head.cert.toString}</pre>
+            </span> 
+            }
+          </span>
+          case None => <p>We received no Authentication information</p>
+        }
+          }
+          </body></html>)
+      }
+
+    }
+
+}
\ No newline at end of file