nearly complete but broken code for moving partly to netty webid
authorHenry Story <henry.story@bblfish.net>
Sat, 22 Oct 2011 15:56:21 +0200
branchwebid
changeset 93 a87106f1ace0
parent 92 e1ff5771610f
child 94 f43eb2e7d07c
nearly complete but broken code for moving partly to netty
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/X509Cert.scala
src/main/scala/auth/X509Claim.scala
src/main/scala/auth/X509view.scala
src/main/scala/netty/ReadWriteWebNetty.scala
--- a/src/main/scala/ReadWriteWebMain.scala	Fri Oct 21 23:01:04 2011 +0200
+++ b/src/main/scala/ReadWriteWebMain.scala	Sat Oct 22 15:56:21 2011 +0200
@@ -10,11 +10,12 @@
 
 import org.clapper.argot._
 import ArgotConverters._
-import javax.servlet.http.HttpServletRequest
 import unfiltered.request.HttpRequest
+import unfiltered.Cycle
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import unfiltered.filter.Plan
 
-object ReadWriteWebMain {
-
+trait ReadWriteWebArgs {
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
 
   val postUsageMsg= Some("""
@@ -69,6 +70,14 @@
 
   val baseURL = parser.parameter[String]("baseURL", "base URL", false)
 
+}
+
+
+object ReadWriteWebMain extends ReadWriteWebArgs {
+
+  implicit def planify(intent: Plan.Intent): unfiltered.filter.Plan  =
+    unfiltered.filter.Planify(intent)
+
   // regular Java main
   def main(args: Array[String]) {
 
@@ -98,7 +107,7 @@
         ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
     }.
       filter(app.plan).
-      filter(new X509view().plan).
+      filter(new X509view().intent[HttpServletRequest,HttpServletResponse]).
       filter(new EchoPlan().plan).run()
     
   }
--- a/src/main/scala/auth/X509Cert.scala	Fri Oct 21 23:01:04 2011 +0200
+++ b/src/main/scala/auth/X509Cert.scala	Sat Oct 22 15:56:21 2011 +0200
@@ -24,19 +24,62 @@
 package org.w3.readwriteweb.auth
 
 import javax.servlet.http.HttpServletRequest
-import java.security.cert.X509Certificate
 import unfiltered.request.HttpRequest
+import unfiltered.netty.ReceivedMessage
+import java.security.cert.{X509Certificate}
+import java.security.cert.Certificate
 
-/**
- * @author Henry Story, with help from Doug Tangren on unfiltered mailing list
- * @created: 14/10/2011
- */
 
-object X509Cert {
-  def unapply[T <: HttpServletRequest](r: HttpRequest[T]): Option[Array[X509Certificate]] =
-    r.underlying.getAttribute("javax.servlet.request.X509Certificate") match {
-      case certs: Array[X509Certificate] => Some(certs)
-      case _ => None
+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 perhaps throw an exception here.
     }
 
-}
\ No newline at end of file
+
+    //todo: both of these return X509Certificates optionally.
+    //todo: but they don't pass any error messages along, which they could in the case of netty
+
+    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]] = {
+
+      import org.jboss.netty.handler.ssl.SslHandler
+      r.underlying.context.getPipeline.get(classOf[SslHandler]) match {
+        case sslh: SslHandler => {
+          sslh.setEnableRenegotiation(true)
+          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
+          }
+        }
+        case _ => None
+      }
+
+    }
+
+}
+
--- a/src/main/scala/auth/X509Claim.scala	Fri Oct 21 23:01:04 2011 +0200
+++ b/src/main/scala/auth/X509Claim.scala	Sat Oct 22 15:56:21 2011 +0200
@@ -50,9 +50,9 @@
          def load(x509: X509Certificate) = new X509Claim(x509)
      })
 
-  def unapply[T <: HttpServletRequest](r: HttpRequest[T])(implicit webCache: WebCache): Option[X509Claim] =
+  def unapply[T](r: HttpRequest[T])(implicit webCache: WebCache,m: Manifest[T]): Option[X509Claim] =
     r match {
-      case X509Cert(certs) => Some(idCache.get(certs(0)))
+      case Certs(c1: X509Certificate, _*) => Some(idCache.get(c1))
       case _ => None
     }
 
--- a/src/main/scala/auth/X509view.scala	Fri Oct 21 23:01:04 2011 +0200
+++ b/src/main/scala/auth/X509view.scala	Sat Oct 22 15:56:21 2011 +0200
@@ -26,6 +26,7 @@
 import unfiltered.request.Path
 import unfiltered.response.{Html, ContentType, Ok}
 import org.w3.readwriteweb.WebCache
+import unfiltered.Cycle
 
 /**
  * This plan just described the X509 WebID authentication information.
@@ -35,14 +36,14 @@
  * @created: 13/10/2011
  */
 
-class X509view(implicit val webCache: WebCache) {
+class X509view()(implicit val webCache: WebCache) {
 
-    val plan = unfiltered.filter.Planify {
+    def intent[A: Manifest,B]: Cycle.Intent[A, B] =  {
       case req @ Path(path) if path startsWith "/test/auth/x509" =>
         Ok ~> ContentType("text/html") ~> Html(
           <html><head><title>Authentication Page</title></head>
         { req match {
-          case X509Claim(xclaim: X509Claim) => <body>
+          case X509Claim(xclaim) => <body>
             <h1>Authentication Info received</h1>
             <p>You were identified with the following WebIDs</p>
              <ul>{xclaim.webidclaims.filter(cl=>cl.verified).map(p=> <li>{p.webId}</li>)}</ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala	Sat Oct 22 15:56:21 2011 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011 Henry Story (bblfish.net)
+ * under the MIT licence defined
+ *    http://www.opensource.org/licenses/mit-license.html
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in the
+ * Software without restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package org.w3.readwriteweb.netty
+
+import org.clapper.argot.ArgotUsageException
+import scala.Console._
+import org.w3.readwriteweb.auth.{X509view, RDFAuthZ}
+import org.w3.readwriteweb._
+import unfiltered.netty.{ServerErrorResponse, cycle}
+
+/**
+ * ReadWrite Web for Netty server, allowing content renegotiation
+ *
+ * @author hjs
+ * @created: 21/10/2011
+ */
+
+class ReadWriteWebNetty extends ReadWriteWebArgs {
+
+  // regular Java main
+   def main(args: Array[String]) {
+
+     try {
+       parser.parse(args)
+     } catch {
+       case e: ArgotUsageException => err.println(e.message); sys.exit(1)
+     }
+ 
+     val filesystem =
+       new Filesystem(
+         rootDirectory.value.get,
+         baseURL.value.get,
+         lang=rdfLanguage.value getOrElse RDFXML)(mode.value getOrElse ResourcesDontExistByDefault)
+     
+     val app = new ReadWriteWeb(filesystem, new RDFAuthZ(webCache,filesystem))
+ 
+     //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) => new KeyAuth_Https(port)
+       case None => new KeyAuth_Https(httpPort.value.get)
+     }
+
+     // configures and launches a Netty server
+     service.plan( new tmp ).run()
+     
+   }
+  
+   class tmp extends cycle.Plan with ServerErrorResponse
+}
\ No newline at end of file