Authorization working: It currently only allows me, but that's an important starting point. webid
authorHenry Story <henry.story@bblfish.net>
Sat, 15 Oct 2011 16:58:48 +0200
branchwebid
changeset 68 86ffbd1bfc34
parent 64 215441a92c78
child 74 25c0b03d1091
Authorization working: It currently only allows me, but that's an important starting point.
src/main/scala/Filesystem.scala
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/Authz.scala
src/main/scala/auth/X509Cert.scala
src/main/scala/auth/X509view.scala
src/main/scala/plan.scala
--- a/src/main/scala/Filesystem.scala	Fri Oct 14 17:51:14 2011 +0200
+++ b/src/main/scala/Filesystem.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -40,7 +40,7 @@
           val reader = model.getReader(lang)
           reader.read(model, fis, url.toString)
         } catch {
-          case je:JenaException => error("@@@")
+          case je:JenaException => error(je.toString)
         }
         fis.close()
         model.success
--- a/src/main/scala/ReadWriteWebMain.scala	Fri Oct 14 17:51:14 2011 +0200
+++ b/src/main/scala/ReadWriteWebMain.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -1,6 +1,6 @@
 package org.w3.readwriteweb
 
-import auth.X509view
+import auth.{SimpleAuthZ, X509view}
 import org.w3.readwriteweb.util._
 
 import unfiltered.jetty._
@@ -64,7 +64,6 @@
 
    implicit val webCache = new WebCache()
 
-
    val baseURL = parser.parameter[String]("baseURL", "base URL", false)
 
   // regular Java main
@@ -82,7 +81,7 @@
           baseURL.value.get,
           lang=rdfLanguage.value getOrElse "N3")(mode.value getOrElse ResourcesDontExistByDefault)
 
-    val app = new ReadWriteWeb(filesystem)
+    val app = new ReadWriteWeb(filesystem, new SimpleAuthZ())
 
     //this is incomplete: we should be able to start both ports.... not sure how to do this yet.
     val service = httpsPort.value match {
@@ -95,8 +94,8 @@
       context("/public"){ ctx:ContextBuilder =>
       ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
     }.
-      filter(new X509view().plan).
-      filter(app.plan).run()
+      filter(app.plan).
+      filter(new X509view().plan).run()
 
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/auth/Authz.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -0,0 +1,112 @@
+/*
+ * 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.auth
+
+import unfiltered.filter.Plan
+import unfiltered.Cycle
+import unfiltered.request._
+import unfiltered.response.{Unauthorized, BadRequest}
+import collection.JavaConversions._
+import javax.security.auth.Subject
+import org.w3.readwriteweb.WebCache
+import org.w3.readwriteweb.auth.Authz._
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+
+/**
+ * @author hjs
+ * @created: 14/10/2011
+ */
+
+object HttpMethod {
+   def unapply(req: HttpRequest[_]): Option[Method] =
+     Some(
+       req.method match {
+         case "GET" => GET
+         case "PUT" => PUT
+         case "HEAD" => HEAD
+         case "POST" => POST
+         case "CONNECT" => CONNECT
+         case "OPTIONS" => OPTIONS
+         case "TRACE" => TRACE
+         case m => new Method(m)
+       })
+     
+   
+}
+
+object Authz {
+
+  def authMethod(s: String, httpMethod: Method) = new AuthzFunc
+
+
+  
+  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)
+    ) yield claim.principal
+    subject.getPrincipals.addAll(verified)
+    subject
+  }
+}
+
+class NoAuthZ extends Authz {
+  def apply(in: Plan.Intent) = in
+}
+
+class SimpleAuthZ(implicit val WebCache: WebCache) extends Authz {
+
+  def apply(in: Plan.Intent): Plan.Intent = {
+    req: HttpRequest[HttpServletRequest] => req match {
+        case Path(path) & HttpMethod(m) =>  { //first get things that cost nothing
+            val autf = authMethod(path,m)
+            if (autf.requiresAuth) {
+               X509Claim.unapply(req) match {
+                 case Some(claim) => {
+                   if (autf.isAuthorized(claim,m,path)) in(req)
+                   else Unauthorized
+                 }
+                 case None => Unauthorized
+               }
+            } else Unauthorized
+        }
+        case _ => BadRequest
+      }
+  }
+
+}
+
+trait Authz {
+   def apply(in: Plan.Intent): Plan.Intent // we may want to generalise more later to  Cycle.Intent[A,B]
+
+}
+
+class AuthzFunc {
+   def requiresAuth(): Boolean = true
+   def isAuthorized(webid: Subject,  m: Method, path: String ): Boolean =
+     webid.getPrincipals().exists(p=>p.getName=="http://bblfish.net/people/henry/card#me")
+   
+}
--- a/src/main/scala/auth/X509Cert.scala	Fri Oct 14 17:51:14 2011 +0200
+++ b/src/main/scala/auth/X509Cert.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -26,8 +26,6 @@
 import javax.servlet.http.HttpServletRequest
 import java.security.cert.X509Certificate
 import unfiltered.request.HttpRequest
-import java.util.concurrent.TimeUnit
-import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
 
 /**
  * @author Henry Story, with help from Doug Tangren on unfiltered mailing list
--- a/src/main/scala/auth/X509view.scala	Fri Oct 14 17:51:14 2011 +0200
+++ b/src/main/scala/auth/X509view.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -28,7 +28,7 @@
 import org.w3.readwriteweb.WebCache
 
 /**
- * This plan just described the authentication information.
+ * This plan just described the X509 WebID authentication information.
  * This is a simple version. A future version will show EARL output, and so be useful for debugging the endpoint.
  *
  * @author hjs
--- a/src/main/scala/plan.scala	Fri Oct 14 17:51:14 2011 +0200
+++ b/src/main/scala/plan.scala	Sat Oct 15 16:58:48 2011 +0200
@@ -1,5 +1,6 @@
 package org.w3.readwriteweb
 
+import auth.{NoAuthZ, Authz}
 import org.w3.readwriteweb.util._
 
 import unfiltered.request._
@@ -19,9 +20,8 @@
               QueryTypeDescribe => DESCRIBE}
 
 import scalaz._
-import Scalaz._
 
-class ReadWriteWeb(rm: ResourceManager) {
+class ReadWriteWeb(rm: ResourceManager, implicit val auth: Authz = new NoAuthZ()) {
   
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
 
@@ -53,6 +53,7 @@
    *  through another implicit conversion. It saves us the call to the Validation.lift() method
    */
   val plan = unfiltered.filter.Planify {
+    auth {
     case req @ Path(path) if path startsWith rm.basePath => {
       val baseURI = req.underlying.getRequestURL.toString
       val r: Resource = rm.resource(new URL(baseURI))
@@ -129,6 +130,7 @@
         case _ => MethodNotAllowed ~> Allow("GET", "PUT", "POST")
       }
     }
+  }
 
   }