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.
--- 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