~ pause in the middle of code review + refactoring
authorAlexandre Bertails <bertails@gmail.com>
Sun, 13 Nov 2011 17:26:02 -0500
changeset 114 7781eb6764bd
parent 113 94123d365eae
child 115 cffe1a122b23
~ pause in the middle of code review + refactoring
src/main/scala/HttpsTrustAll.scala
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/Authz.scala
src/main/scala/auth/X509Cert.scala
src/main/scala/auth/X509Claim.scala
src/main/scala/plan.scala
src/main/scala/util/package.scala
src/test/scala/auth/CreateWebIDSpec.scala
--- a/src/main/scala/HttpsTrustAll.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/HttpsTrustAll.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -29,7 +29,9 @@
 import org.jsslutils.sslcontext.trustmanagers.TrustAllClientsWrappingTrustManager
 import org.jsslutils.sslcontext.{X509TrustManagerWrapper, X509SSLContextFactory}
 import sys.SystemProperties
+import scala.util.Properties.{propOrNone => getProperty, javaHome}
 import unfiltered.jetty.{Ssl, Https}
+import unfiltered.jetty.Server
 
 
 /**
@@ -37,7 +39,10 @@
  * @created: 12/10/2011
  */
 
-case class HttpsTrustAll(override val port: Int, override val host: String) extends Https(port, host) with TrustAll
+class HttpsTrustAll(
+    override val port: Int,
+    override val host: String)
+extends Https(port, host) with TrustAll
 
 
 /**
@@ -55,36 +60,45 @@
  *  the user experience would be very bad, since TLS does not give many options for explaining what the problem
  *  is.
  */
-trait TrustAll { self: Ssl =>
-   import scala.sys.SystemProperties._
+trait TrustAll extends Ssl with Server with DelayedInit {
 
-   lazy val sslContextFactory = new X509SSLContextFactory(
-               serverCertKeyStore,
-               tryProperty("jetty.ssl.keyStorePassword"),
-               serverCertKeyStore); //this one is not needed since our wrapper ignores all trust managers
+  import scala.sys.SystemProperties._
 
-   lazy val trustWrapper = new X509TrustManagerWrapper {
-     def wrapTrustManager(trustManager: X509TrustManager) = new TrustAllClientsWrappingTrustManager(trustManager)
-   }
-
-   lazy val serverCertKeyStore = {
+  val sslContextFactory = {
+    val trustWrapper =
+      new X509TrustManagerWrapper {
+        def wrapTrustManager(trustManager: X509TrustManager) =
+          new TrustAllClientsWrappingTrustManager(trustManager)
+      }
+    
+    val serverCertKeyStore = {
       val keyStoreLoader = new KeyStoreLoader
-   		keyStoreLoader.setKeyStoreType(System.getProperty("jetty.ssl.keyStoreType","JKS"))
-   		keyStoreLoader.setKeyStorePath(trustStorePath)
-   		keyStoreLoader.setKeyStorePassword(System.getProperty("jetty.ssl.keyStorePassword","password"))
-      keyStoreLoader.loadKeyStore();
-   }
-
-   sslContextFactory.setTrustManagerWrapper(trustWrapper);
+      keyStoreLoader.setKeyStoreType(getProperty("jetty.ssl.keyStoreType") getOrElse "JKS")
+      keyStoreLoader.setKeyStorePath(trustStorePath)
+      keyStoreLoader.setKeyStorePassword(getProperty("jetty.ssl.keyStorePassword") getOrElse "password")
+      keyStoreLoader.loadKeyStore()
+    }
+    
+    val factory = new X509SSLContextFactory(
+      serverCertKeyStore,
+      getProperty("jetty.ssl.keyStorePassword") getOrElse sys.error("jetty.ssl.keyStorePassword not set"),
+      serverCertKeyStore) //this one is not needed since our wrapper ignores all trust managers
+    
+    factory.setTrustManagerWrapper(trustWrapper)
+    
+    factory
+  }
 
-
- 	 lazy val trustStorePath =  new SystemProperties().get("jetty.ssl.keyStore") match {
-       case Some(path) => path
-       case None => new File(new File(tryProperty("user.home")), ".keystore").getAbsolutePath
-   }
-
-   sslConn.setSslContext(sslContextFactory.buildSSLContext())
-   sslConn.setWantClientAuth(true)
+  val trustStorePath =
+    getProperty("jetty.ssl.keyStore") getOrElse {
+      new File(new File(javaHome), ".keystore").getAbsolutePath
+    }
+  
+  // not tested if ok, there was a problem anyway
+  def delayedInit(x: ⇒ Unit): Unit = {
+    sslConn.setSslContext(sslContextFactory.buildSSLContext())
+    sslConn.setWantClientAuth(true)
+  }
 
 }
 
--- a/src/main/scala/ReadWriteWebMain.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/ReadWriteWebMain.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -97,7 +97,7 @@
 
     //this is incomplete: we should be able to start both ports.... not sure how to do this yet.
     val service = httpsPort.value match {
-      case Some(port) => HttpsTrustAll(port,"0.0.0.0")
+      case Some(port) => new HttpsTrustAll(port,"0.0.0.0")
       case None => Http(httpPort.value.get)
     }
 
--- a/src/main/scala/auth/Authz.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/auth/Authz.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -25,7 +25,7 @@
 
 import unfiltered.filter.Plan
 import unfiltered.request._
-import collection.JavaConversions._
+import collection.JavaConverters._
 import javax.security.auth.Subject
 import java.net.URL
 import com.hp.hpl.jena.query.{QueryExecutionFactory, QueryExecution, QuerySolutionMap, QueryFactory}
@@ -40,6 +40,7 @@
  * @created: 14/10/2011
  */
 
+// TODO pull request to the unfiltered project!
 object HttpMethod {
   def unapply(req: HttpRequest[_]): Option[Method] =
     Some(
@@ -59,39 +60,35 @@
 
 object AuthZ {
 
-
   implicit def x509toSubject(x509c: X509Claim)(implicit cache: WebCache): Subject = {
     val subject = new Subject()
     subject.getPublicCredentials.add(x509c)
     val verified = for (
-      claim <- x509c.webidclaims;
-      if (claim.verified)
+      claim <- x509c.webidclaims if (claim.verified)
     ) yield claim.principal
-    subject.getPrincipals.addAll(verified)
+    subject.getPrincipals.addAll(verified.asJava)
     subject
   }
 }
 
-class NullAuthZ[Request,Response] extends AuthZ[Request,Response] {
-  override def subject(req: Req): Option[Subject] = None
+class NullAuthZ[Request, Response] extends AuthZ[Request, Response] {
+  def subject(req: Req): Option[Subject] = None
 
-  override def guard(m: Method, path: URL): Guard = null
+  def guard(m: Method, path: URL): Guard = null
 
-  override def protect(in: Req=>Res)(implicit  m: Manifest[Request]) = in
+  override def protect(in: Req => Res)(implicit  m: Manifest[Request]) = in
 }
 
 
-abstract class AuthZ[Request,Response] {
+trait AuthZ[Request, Response] {
   type Req = HttpRequest[Request]
   type Res = ResponseFunction[Response]
 
-
-  def protect(in: Req=>Res)(implicit  m: Manifest[Request]): Req=>Res =  {
-      case req @ HttpMethod(method) & Authoritative(url,_) if guard(method, url).allow(() => subject(req)) => in(req)
-      case _ => Unauthorized
-    }
+  def protect(in: Req => Res)(implicit m: Manifest[Request]): Req => Res = {
+    case req @ HttpMethod(method) & Authoritative(url,_) if guard(method, url).allow(subject(req)) => in(req)
+    case _ => Unauthorized
+  }
   
-
   protected def subject(req: Req): Option[Subject]
 
   /** create the guard defined in subclass */
@@ -103,16 +100,18 @@
      * verify if the given request is authorized
      * @param subj function returning the subject to be authorized if the resource needs authorization
      */
-    def allow(subj: () => Option[Subject]): Boolean
+    def allow(subj: => Option[Subject]): Boolean
   }
 
 }
 
 
-class RDFAuthZ[Request,Response](val webCache: WebCache, rm: ResourceManager)
+class RDFAuthZ[Request, Response](val webCache: WebCache, rm: ResourceManager)
   (implicit val m: Manifest[Request]) extends AuthZ[Request,Response] {
+  
   import AuthZ.x509toSubject
-  implicit val cache : WebCache = webCache
+  
+  implicit val cache: WebCache = webCache
 
   def subject(req: Req) = req match {
     case X509Claim(claim) => Option(claim)
@@ -127,24 +126,21 @@
     val Control = acl+"Control"
 
     val selectQuery = QueryFactory.create("""
-    		  PREFIX acl: <http://www.w3.org/ns/auth/acl#>
-    		  SELECT ?mode ?group ?agent
-    		  WHERE {
+          PREFIX acl: <http://www.w3.org/ns/auth/acl#>
+          SELECT ?mode ?group ?agent
+          WHERE {
               ?auth acl:accessTo ?res ;
                     acl:mode ?mode .
           OPTIONAL { ?auth acl:agentClass ?group . }
-	        OPTIONAL { ?auth acl:agent ?agent . }
-    		  }""")
+          OPTIONAL { ?auth acl:agent ?agent . }
+          }""")
   }
 
   def guard(method: Method, url: URL) = new Guard(method, url) {
     import RDFGuard._
-    import org.w3.readwriteweb.util.wrapValidation
-    import org.w3.readwriteweb.util.ValidationW
+    import org.w3.readwriteweb.util.{ValidationW, wrapValidation}
 
-
-
-    def allow(subj: () => Option[Subject]) = {
+    def allow(subj: => Option[Subject]) = {
       val r: Resource = rm.resource(new URL(url,".meta.n3"))
       val res: ValidationW[Boolean,Boolean] = for {
         model <- r.get() failMap { x => true }
@@ -154,7 +150,7 @@
         val qe = QueryExecutionFactory.create(selectQuery, model, initialBinding)
         val agentsAllowed = try {
           val exec = qe.execSelect()
-          val res = for (qs <- exec) yield {
+          val res = for (qs <- exec.asScala) yield {
             val methods = qs.get("mode").toString match {
               case Read => List(GET)
               case Write => List(PUT, POST)
@@ -170,10 +166,10 @@
         }
         if (agentsAllowed.size > 0) {
           if (agentsAllowed.exists( pair =>  pair._2 == foafAgent )) true
-          else subj() match {
+          else subj match {
             case Some(s) => {
               agentsAllowed.exists{
-                p =>  s.getPrincipals(classOf[WebIdPrincipal]).
+                p =>  s.getPrincipals(classOf[WebIdPrincipal]).asScala.
                   exists(id=> {
                   val ps = if (p._1 != null) p._1.toString else null;
                   ps == id.webid
@@ -197,7 +193,6 @@
 
 class ResourceGuard(path: String, reqMethod: Method) {
 
-
   def allow(subjFunc: () => Option[Subject]) = {
     subjFunc().isEmpty
   }
--- a/src/main/scala/auth/X509Cert.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/auth/X509Cert.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -34,12 +34,16 @@
 import interfaces.RSAPublicKey
 import unfiltered.util.IO
 import sun.security.x509._
-
+import org.w3.readwriteweb.util.trySome
 
 object X509CertSigner {
 
-  def apply(keyStoreLoc: URL, keyStoreType: String, password: String, alias: String) = {
-    val keystore =  KeyStore.getInstance(keyStoreType)
+  def apply(
+      keyStoreLoc: URL,
+      keyStoreType: String,
+      password: String,
+      alias: String): X509CertSigner = {
+    val keystore = KeyStore.getInstance(keyStoreType)
 
     IO.use(keyStoreLoc.openStream()) { in =>
       keystore.load(in, password.toCharArray)
@@ -48,11 +52,13 @@
     val certificate = keystore.getCertificate(alias).asInstanceOf[X509Certificate]
     //one could verify that indeed this is the private key corresponding to the public key in the cert.
 
-    new X509CertSigner(certificate,privateKey)
+    new X509CertSigner(certificate, privateKey)
   }
 }
 
-class X509CertSigner(signingCert: X509Certificate, signingKey: PrivateKey ) {
+class X509CertSigner(
+    signingCert: X509Certificate,
+    signingKey: PrivateKey ) {
   val WebID_DN="""O=FOAF+SSL, OU=The Community of Self Signers, CN=Not a Certification Authority"""
 
   /**
@@ -69,136 +75,131 @@
    * @param days how many days from now the Certificate is valid for
    * @param algorithm the signing algorithm, eg "SHA1withRSA"
    */
-    def generate(subjectDN: String,
-                 subjectKey: RSAPublicKey,
-                 days: Int,
-                 webId: URL): X509Certificate = {   //todo: the algorithm should be deduced from private key in part
-
-
-      var info = new X509CertInfo
-      val from = new Date(System.currentTimeMillis()-10*1000*60) //start 10 minutes ago, to avoid network trouble
-      val to = new Date(from.getTime + days*24*60*60*1000) 
-      val interval = new CertificateValidity(from, to)
-      val serialNumber = new BigInteger(64, new SecureRandom)
-      val subjectXN = new X500Name(subjectDN)
-      val issuerXN = new X500Name(signingCert.getSubjectDN.toString)
-
-      info.set(X509CertInfo.VALIDITY, interval)
-      info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber))
-      info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(subjectXN))
-      info.set(X509CertInfo.ISSUER, new CertificateIssuerName(issuerXN))
-      info.set(X509CertInfo.KEY, new CertificateX509Key(subjectKey))
-      info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3))
-
-      //
-      //extensions
-      //
-      val extensions = new CertificateExtensions();
-
-      val san = new SubjectAlternativeNameExtension(true, new GeneralNames().add(new GeneralName(new URIName(webId.toExternalForm))))
-      extensions.set(san.getName,san)
-
-      val basicCstrExt = new BasicConstraintsExtension(false,1)
-      extensions.set(basicCstrExt.getName,basicCstrExt)
+  def generate(
+      subjectDN: String,
+      subjectKey: RSAPublicKey,
+      days: Int,
+      webId: URL): X509Certificate = {   //todo: the algorithm should be deduced from private key in part
 
-      { import KeyUsageExtension._
-        val keyUsage = new KeyUsageExtension()
-        List(DIGITAL_SIGNATURE, NON_REPUDIATION, KEY_ENCIPHERMENT, KEY_AGREEMENT, KEY_CERTSIGN).foreach {
-           usage => keyUsage.set(usage,true)
-        }
-        extensions.set(keyUsage.getName,keyUsage)
-      }
-
-      { import NetscapeCertTypeExtension._
-      val netscapeExt = new NetscapeCertTypeExtension()
-       List(SSL_CLIENT, S_MIME) foreach { ext => netscapeExt.set(ext,true) }
-        extensions.set(netscapeExt.getName, new NetscapeCertTypeExtension(false,netscapeExt.getValue))
-      }
-      
-      val subjectKeyExt = new SubjectKeyIdentifierExtension(new KeyIdentifier(subjectKey).getIdentifier)
-      extensions.set(subjectKeyExt.getName,subjectKeyExt)
-      
-      info.set(X509CertInfo.EXTENSIONS,extensions)
+    var info = new X509CertInfo
+    val from = new Date(System.currentTimeMillis()-10*1000*60) //start 10 minutes ago, to avoid network trouble
+    val to = new Date(from.getTime + days*24*60*60*1000) 
+    val interval = new CertificateValidity(from, to)
+    val serialNumber = new BigInteger(64, new SecureRandom)
+    val subjectXN = new X500Name(subjectDN)
+    val issuerXN = new X500Name(signingCert.getSubjectDN.toString)
 
-      val algo = signingCert.getPublicKey.getAlgorithm match {
-        case "DSA" =>  new AlgorithmId(AlgorithmId.sha1WithDSA_oid )
-        case "RSA" =>  new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)
-        case _ => throw new RuntimeException("Don't know how to sign with this type of key")  
-      }
-
-      info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo))
+    info.set(X509CertInfo.VALIDITY, interval)
+    info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber))
+    info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(subjectXN))
+    info.set(X509CertInfo.ISSUER, new CertificateIssuerName(issuerXN))
+    info.set(X509CertInfo.KEY, new CertificateX509Key(subjectKey))
+    info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3))
 
-      // Sign the cert to identify the algorithm that's used.
-      val tmpCert = new X509CertImpl(info)
-      tmpCert.sign(signingKey,algo.getName)
+    //
+    //extensions
+    //
+    val extensions = new CertificateExtensions
 
-      //update the algorithm and re-sign
-      val sigAlgo = tmpCert.get(X509CertImpl.SIG_ALG).asInstanceOf[AlgorithmId]
-      info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgo)
-      val cert = new X509CertImpl(info)
-      cert.sign(signingKey,algo.getName)
-      
-      cert.verify(signingCert.getPublicKey)
-      return cert
+    val san =
+      new SubjectAlternativeNameExtension(
+          true,
+          new GeneralNames().add(
+              new GeneralName(new URIName(webId.toExternalForm))))
+    
+    extensions.set(san.getName, san)
+
+    val basicCstrExt = new BasicConstraintsExtension(false,1)
+    extensions.set(basicCstrExt.getName,basicCstrExt)
+
+    {
+      import KeyUsageExtension._
+      val keyUsage = new KeyUsageExtension
+      val usages =
+        List(DIGITAL_SIGNATURE, NON_REPUDIATION, KEY_ENCIPHERMENT, KEY_AGREEMENT,  KEY_CERTSIGN)
+      usages foreach { usage => keyUsage.set(usage, true) }
+      extensions.set(keyUsage.getName,keyUsage)
     }
 
+    {
+      import NetscapeCertTypeExtension._
+      val netscapeExt = new NetscapeCertTypeExtension
+      List(SSL_CLIENT, S_MIME) foreach { ext => netscapeExt.set(ext, true) }
+      extensions.set(
+        netscapeExt.getName,
+        new NetscapeCertTypeExtension(false, netscapeExt.getValue))
+    }
+      
+    val subjectKeyExt =
+      new SubjectKeyIdentifierExtension(new KeyIdentifier(subjectKey).getIdentifier)
+
+    extensions.set(subjectKeyExt.getName, subjectKeyExt)
+    
+    info.set(X509CertInfo.EXTENSIONS, extensions)
+
+    val algo = signingCert.getPublicKey.getAlgorithm match {
+      case "DSA" => new AlgorithmId(AlgorithmId.sha1WithDSA_oid )
+      case "RSA" => new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid)
+      case _ => sys.error("Don't know how to sign with this type of key")  
+    }
+
+    info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo))
+
+    // Sign the cert to identify the algorithm that's used.
+    val tmpCert = new X509CertImpl(info)
+    tmpCert.sign(signingKey, algo.getName)
+
+    //update the algorithm and re-sign
+    val sigAlgo = tmpCert.get(X509CertImpl.SIG_ALG).asInstanceOf[AlgorithmId]
+    info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgo)
+    val cert = new X509CertImpl(info)
+    cert.sign(signingKey,algo.getName)
+      
+    cert.verify(signingCert.getPublicKey)
+    return cert
+  }
 
 }
 
 
 object Certs {
 
-
   def unapplySeq[T](r: HttpRequest[T])(implicit m: Manifest[T]): Option[IndexedSeq[Certificate]] = {
-    if (m <:< manifest[HttpServletRequest]) unapplyServletRequest(r.asInstanceOf[HttpRequest[HttpServletRequest]])
-    else if (m <:< manifest[ReceivedMessage]) unapplyReceivedMessage(r.asInstanceOf[HttpRequest[ReceivedMessage]])
-    else None //todo: should  throw an exception here?
+    if (m <:< manifest[HttpServletRequest])
+      unapplyServletRequest(r.asInstanceOf[HttpRequest[HttpServletRequest]])
+    else if (m <:< manifest[ReceivedMessage])
+      unapplyReceivedMessage(r.asInstanceOf[HttpRequest[ReceivedMessage]])
+    else
+      None //todo: should  throw an exception here?
   }
 
 
   //todo: should perhaps pass back error messages, which they could in the case of netty
 
-  private def unapplyServletRequest[T <: HttpServletRequest](r: HttpRequest[T]):
-  Option[IndexedSeq[Certificate]] = {
+  private def unapplyServletRequest[T <: HttpServletRequest](r: HttpRequest[T]): Option[IndexedSeq[Certificate]] =
     r.underlying.getAttribute("javax.servlet.request.X509Certificate") match {
       case certs: Array[Certificate] => Some(certs)
       case _ => None
     }
-  }
-
-  private def unapplyReceivedMessage[T <: ReceivedMessage](r: HttpRequest[T]):
-  Option[IndexedSeq[Certificate]] = {
+  
+  private def unapplyReceivedMessage[T <: ReceivedMessage](r: HttpRequest[T]): Option[IndexedSeq[Certificate]] = {
 
     import org.jboss.netty.handler.ssl.SslHandler
-    r.underlying.context.getPipeline.get(classOf[SslHandler]) match {
-      case sslh: SslHandler => try {
-        //return the client certificate in the existing session if one exists
-        Some(sslh.getEngine.getSession.getPeerCertificates)
-      } catch {
-        case e => {
-          // request a certificate from the user
-          sslh.setEnableRenegotiation(true)
-          r match {
-            case UserAgent(agent) if needAuth(agent) => sslh.getEngine.setNeedClientAuth(true)
-            case _ => sslh.getEngine.setWantClientAuth(true)  
-          }
-          
-          val future = sslh.handshake()
-          future.await(30000) //that's certainly way too long.
-          if (future.isDone) {
-            if (future.isSuccess) try {
-              Some(sslh.getEngine.getSession.getPeerCertificates)
-            } catch {
-              case e => None
-            } else {
-              None
-            }
-          } else {
-            None
-          }
-        }
+    
+    val sslh = r.underlying.context.getPipeline.get(classOf[SslHandler])
+    
+    trySome(sslh.getEngine.getSession.getPeerCertificates.toIndexedSeq) orElse {
+      sslh.setEnableRenegotiation(true)
+      r match {
+        case UserAgent(agent) if needAuth(agent) => sslh.getEngine.setNeedClientAuth(true)
+        case _ => sslh.getEngine.setWantClientAuth(true)  
       }
-      case _ => None
+      val future = sslh.handshake()
+      future.await(30000) //that's certainly way too long.
+      if (future.isDone && future.isSuccess)
+        trySome(sslh.getEngine.getSession.getPeerCertificates.toIndexedSeq)
+      else
+        None
     }
 
   }
@@ -211,9 +212,8 @@
   *  so that changes to browsers could update server behavior
   *
   */
-  def needAuth(agent: String): Boolean = {
+  def needAuth(agent: String): Boolean =
     agent.contains("Java")
-  }
   
 }
 
--- a/src/main/scala/auth/X509Claim.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/auth/X509Claim.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -45,16 +45,16 @@
   final val logger = LoggerFactory.getLogger(classOf[X509Claim])
 
   val idCache: Cache[X509Certificate, X509Claim] =
-     CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).
-       build(new CacheLoader[X509Certificate, X509Claim]() {
-         def load(x509: X509Certificate) = new X509Claim(x509)
+     CacheBuilder.newBuilder()
+     .expireAfterWrite(30, TimeUnit.MINUTES)
+     .build(new CacheLoader[X509Certificate, X509Claim] {
+       def load(x509: X509Certificate) = new X509Claim(x509)
      })
 
-  def unapply[T](r: HttpRequest[T])(implicit webCache: WebCache,m: Manifest[T]): Option[X509Claim] =
-    r match {
-      case Certs(c1: X509Certificate, _*) => Some(idCache.get(c1))
-      case _ => None
-    }
+  def unapply[T](r: HttpRequest[T])(implicit webCache: WebCache,m: Manifest[T]): Option[X509Claim] = r match {
+    case Certs(c1: X509Certificate, _*) => Some(idCache.get(c1))
+    case _ => None
+  }
 
 
 
@@ -65,13 +65,14 @@
    * @param cert X.509 certificate from which to extract the URIs.
    * @return Iterator of URIs as strings found in the subjectAltName extension.
    */
-	def getClaimedWebIds(cert: X509Certificate): Iterator[String] =
-    if (cert == null)  Iterator.empty;
+  def getClaimedWebIds(cert: X509Certificate): Iterator[String] =
+    if (cert == null)
+      Iterator.empty
     else cert.getSubjectAlternativeNames() match {
       case coll if (coll != null) => {
-        for (sanPair <- coll
-             if (sanPair.get(0) == 6)
-        ) yield sanPair(1).asInstanceOf[String]
+        for {
+          sanPair <- coll if (sanPair.get(0) == 6)
+        } yield sanPair(1).asInstanceOf[String]
       }.iterator
       case _ => Iterator.empty
     }
@@ -89,18 +90,19 @@
  * @author bblfish
  * @created: 30/03/2011
  */
-class X509Claim(val cert: X509Certificate) extends Refreshable  {
+// should just be a case class
+class X509Claim(val cert: X509Certificate) extends Refreshable {
 
   import X509Claim._
-  val claimReceivedDate = new Date();
+  val claimReceivedDate = new Date()
   lazy val tooLate = claimReceivedDate.after(cert.getNotAfter())
   lazy val tooEarly = claimReceivedDate.before(cert.getNotBefore())
 
   /* a list of unverified principals */
-  lazy val webidclaims = getClaimedWebIds(cert).map {
-    webid =>new WebIDClaim(webid, cert.getPublicKey)
-  }.toSet
-
+  lazy val webidclaims = {
+    val claims = getClaimedWebIds(cert) map { webid => new WebIDClaim(webid, cert.getPublicKey) }
+    claims.toSet
+  }
 
 
   //note could also implement Destroyable
@@ -112,28 +114,22 @@
   //note: one could also take the validity period to be dependent on the validity of the profile representation
   //in which case updating the validity period would make more sense.
 
-  override
-  def refresh() {
-  }
+  override def refresh() = ()
 
   /* The certificate is currently within the valid time zone */
-  override
-  def isCurrent(): Boolean = !(tooLate||tooEarly)
+  override def isCurrent(): Boolean = ! (tooLate || tooEarly)
 
-  lazy val error = {}
+  lazy val error = ()
 
   def canEqual(other: Any) = other.isInstanceOf[X509Claim]
 
-  override
-  def equals(other: Any): Boolean =
-    other match {
-      case that: X509Claim => (that eq this) || (that.canEqual(this) && cert == that.cert)
-      case _ => false
-    }
+  override def equals(other: Any): Boolean = other match {
+    case that: X509Claim => (that eq this) || (that.canEqual(this) && cert == that.cert)
+    case _ => false
+  }
 
-  override
-  lazy val hashCode: Int = 41 * (41 +
-    (if (cert != null) cert.hashCode else 0))
+  override lazy val hashCode: Int =
+    41 * (41 + (if (cert != null) cert.hashCode else 0))
 
 }
 
--- a/src/main/scala/plan.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/plan.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -16,7 +16,7 @@
               QueryTypeConstruct => CONSTRUCT,
               QueryTypeDescribe => DESCRIBE}
 
-import scalaz.{Resource=>SzResource}
+import scalaz.{Resource => _}
 import unfiltered.request._
 import unfiltered.Cycle
 import unfiltered.response._
@@ -33,12 +33,12 @@
  * The ReadWriteWeb intent.
  * It is independent of jetty or netty
  */
-trait ReadWriteWeb[Req,Res] {
+trait ReadWriteWeb[Req, Res] {
   val rm: ResourceManager
   implicit def manif: Manifest[Req]
-  implicit val authz: AuthZ[Req,Res] = new NullAuthZ[Req,Res]
+  implicit val authz: AuthZ[Req, Res] = new NullAuthZ[Req, Res]
   // a few type short cuts to make it easier to reason with the code here
-  // one may want to generalise this code so that it does not depend so strongly on servlets.
+  // one may want to generalize this code so that it does not depend so strongly on servlets.
 //  type Request = HttpRequest[Req]
 //  type Response = ResponseFunction[Res]
 
@@ -50,7 +50,7 @@
    * ( Note that we don't want to protect this intent, since that would be to apply the security to all other applications,
    * many of which may want different authorization implementations )
    */
-  def intent : Cycle.Intent[Req,Res] = {
+  def intent : Cycle.Intent[Req, Res] = {
       case req @ Path(path) if path startsWith rm.basePath => authz.protect(rwwIntent)(manif)(req)
   }
 
--- a/src/main/scala/util/package.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/main/scala/util/package.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -8,8 +8,6 @@
 
 package object util {
   
-
-
   def modelFromInputStream(
       is: InputStream,
       base: URL,
@@ -40,4 +38,12 @@
   
   implicit def unwrap[E, F <: E, S <: E](v: Validation[F,S]): E = v.fold(e => e, s => s)
   
+  // I wonder if this is already defined somewhere...
+  def trySome[T](body: => T): Option[T] =
+    try {
+      Option(body)
+    } catch {
+      case _ => None
+    }
+  
 }
--- a/src/test/scala/auth/CreateWebIDSpec.scala	Sat Nov 12 08:33:10 2011 -0500
+++ b/src/test/scala/auth/CreateWebIDSpec.scala	Sun Nov 13 17:26:02 2011 -0500
@@ -42,10 +42,10 @@
  * these test cases it does.
  */
 class FlexiKeyManager extends X509ExtendedKeyManager {
-  val keys = mutable.Map[String, Pair[Array[X509Certificate],PrivateKey]]()
+  val keys = mutable.Map[String, (Array[X509Certificate], PrivateKey)]()
   
   def addClientCert(alias: String,certs: Array[X509Certificate], privateKey: PrivateKey) {
-    keys.put(alias,Pair(certs,privateKey))
+    keys += (alias -> (certs -> privateKey))
   }
   
   var currentId: String = null