20% of the way to getting the WebID test resource operational. Aiming to do better than: https://foafssl.org/test/WebId webid
authorHenry Story <henry.story@bblfish.net>
Sun, 20 Nov 2011 01:15:14 +0100
branchwebid
changeset 129 0e0d7212e63c
parent 128 6ca652858803
child 139 cc0fb90aa327
20% of the way to getting the WebID test resource operational. Aiming to do better than: https://foafssl.org/test/WebId
src/main/resources/ontologies/RelyingParty.n3
src/main/resources/template/WebId.xhtml
src/main/resources/template/hello.html
src/main/resources/template/hello.ssp
src/main/resources/tmp/hello.html
src/main/resources/tmp/hello.ssp
src/main/scala/ReadWriteWebMain.scala
src/main/scala/auth/WebIdClaim.scala
src/main/scala/auth/X509Claim.scala
src/main/scala/auth/X509view.scala
src/main/scala/auth/earl.scala
src/main/scala/netty/ReadWriteWebNetty.scala
src/main/scala/ui/WebIDAuthnReport.scala
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/ontologies/RelyingParty.n3	Sun Nov 20 01:15:14 2011 +0100
@@ -0,0 +1,176 @@
+# vocabulary to allow a RelyingParty to make a report on an attempt at a WebID authentication.
+
+@prefix cert: <http://www.w3.org/ns/auth/cert#> .
+@prefix rsa: <http://www.w3.org/ns/auth/rsa#> .
+@prefix earl: <http://www.w3.org/ns/earl#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix dct: <http://purl.org/dc/terms/> .
+@prefix dc: <http://purl.org/dc/elements/1.1/> .
+@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
+@prefix owl: <http://www.w3.org/2002/07/owl#> .
+@prefix wit: <http://www.w3.org/2005/Incubator/webid/earl/RelyingParty#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix log: <http://www.w3.org/2000/10/swap/log#> .
+
+
+<#> a owl:Ontology .
+
+<> a foaf:Document;
+	dc:author <http://bblfish.net/people/henry/card#me>; 
+	dc:contributor <http://www.bergnet.org/people/bergi/card#me>;
+	rdfs:comment "Document describing a vocabulary to allow a RelyingParty to make a report on an attempt at a WebID authentication."@en;
+	rdfs:seeAlso <http://www.w3.org/2005/Incubator/webid/earl/RelyingPartyExample#>.
+
+#
+# Classes
+#
+
+wit:WebIDClaim a rdfs:Class;
+	rdfs:comment "A WebID Claim is a graph that consists of a claim that a public key identifies some WebID. Every WebID published in a certificate constitutes one WebID claim: the claim that the referent of the WebID is the only one to know the private key of the public key that came in the certificate"@en.
+
+#
+# Properties
+#
+wit:claimedKey a rdfs:Property;
+	rdfs:comment "Public key of a WebID Claim."@en;
+	rdfs:domain wit:WebIDClaim;
+	rdfs:range rsa:RSAPublicKey.
+
+wit:claimedIdentity a rdfs:Property;
+	rdfs:comment "Identity URI of a WebID Claim."@en;
+	rdfs:domain wit:WebIDClaim.
+
+#
+# pure certificate tests
+#
+wit:certificateProvided a earl:TestCase;
+	dct:title "Did the client provide a X509 certificate?"@en;
+    dct:description "Without a client certificate this type of WebID Authentication can not take place."@en;
+	skos:note "If the client provided an certificate, the earl:pointer property must point to it. The certificate is described with the class cert:Certificate using the property cert:base64der. The property cert:principal_key must point to the contained public key. The public key is described with a rsa:publicKey which contains the properties rsa:modulus and rsa:public_exponent. The log:semantics property must point to a blank node that contains a log:includes property for every WebIDClaim."@en.
+
+wit:certificateProvidedSAN a earl:TestCase;
+	dct:title "Does the client certificate contain a subject alternative name?"@en;
+    dct:description "The client certificate must contain at least one Subject Alternative Name in the SAN field of the certificate"@en;
+	skos:note "The earl:subject property must point to the certificate. The earl:pointer must contain the complete subject alternative name string. The certificate is described with the class cert:Certificate using the property cert:base64der. The property cert:principal_key should point to the contained public key."@en.
+
+wit:certificateDateOk a earl:TestCase;
+	dct:title "Is the certificate alive?"@en;
+    dct:description "The time of this session should be between the begin and end date of the certificate validity times"@en;
+	skos:note "The earl:subject property must point to the certificate. The certificate is described with the class cert:Certificate using the property cert:base64der. The property cert:principal_key should point to the contained public key."@en.
+
+wit:certificatePubkeyRecognised a earl:TestCase;
+	dct:title "Could the public key be recognised?"@en;
+	dct:description "The public key in the certificate is recognised by the WebId code. If it is not then this server will not know how to match it with the remote WebID Profile. "@en;
+	skos:note "The earl:subject property must point to the certificate. The earl:pointer must point to the public key. The certificate is described with the class cert:Certificate using the property cert:base64der. The property cert:principal_key should point to the contained public key. The public key is described with the class rsa:RSAPublicKey with the properties rsa:modulus and rsa:public_exponent like described in the WebID specification."@en.
+
+wit:certificateCriticalExtensionsOk a earl:TestCase;
+	dct:title "Does the certificate contain no unnecessary critical extensions?"@en;
+	dct:description "Critical Extensions are not a direct problem for WebID, but may cause many servers to reject the certificate before the WebID code gets to see it. These tests should not generate errors but only warnings"@en;
+	skos:note "The earl:subject property must point to the certificate. The certificate is described with the class cert:Certificate using the property cert:base64der. The property cert:principal_key should point to the contained public key."@en.
+
+wit:certificateOk a earl:TestRequirement;
+	dct:title "Does the certificate fulfill all requirements for a WebID certificate?"@en;
+	dct:description "The certificate must be alive, have one or more WebIDs, should have a public key recognised by the semantic web layer, and should avoid having critical extensions. "@en;
+	dct:hasPart
+		wit:certificateProvided,
+		wit:certificateProvidedSAN,
+		wit:certificateDateOk,
+		wit:certificatePubkeyRecognised,
+		wit:certificateCriticalExtensionsOk;
+	skos:note "If any of the child test cases fails this test requirement must return earl:failed."@en.
+
+#
+# profile tests
+#
+wit:profileGet a earl:TestCase;
+	dct:title "Is the WebID Profile accessible and downloadable?"@en;
+    dct:description "The WebID profile must be retrievable if the claims are going to be verified."@en;
+    skos:note "The earl:subject property should point to the WebID profile, or to a link where a copy of that profile can be fetched. The content could also be included in the document." .
+
+wit:profileWellFormed a earl:TestCase;
+	dct:title "Is the profile well formed?"@en;
+	dct:description "The WebId Profile must be parseable Content and transformable to an RDF graph"@en;
+    skos:note "The earl:subject property should point to the WebID profile, or to a link where a copy of that profile can be fetched. The content could also be included in the document." .
+
+
+wit:profileAllKeysWellFormed a earl:TestCase;
+	dct:title "Does the profile contain only well formed keys for that WebID?"@en;
+	dct:description "All the keys in the profile should be well formed and semantically consistent. It is not necessarily fatal to a particular WebID authentication if they are not, but it is worth alerting the user, as this may lead to inconsistent user experience."@en;
+	skos:note "One does not need to test all keys in a profile, only those that are tied to the WebIDs found in the X509 cert. But to help users one could give them a deeper test of the profile."@en;
+	dct:hasPart
+		wit:profileWellFormedPubkey.
+
+wit:profileWellFormedPubkey a earl:TestRequirement;
+	dct:title "Is the public key well formed?"@en;
+	dct:description "A particular Public key should be well formed"@en;
+	skos:note "The earl:subject property must point to a graph that contains the public key and its webid relation. To help this being processed by tools that are not able to deal with n3 graphs, this should point using the log:n3string relation to a turtle serialisation of the graph (turtle is a subset of n3)"@en;
+	skos:note "The current cert ontology doesn't include properties for DSA, and so there are currently no tests for DSA keys either"@en;
+	dct:hasPart
+		wit:pubkeyRSAModulus,
+		wit:pubkeyRSAExponent.
+
+wit:pubkeyRSAModulus a earl:TestCase;
+	dct:title "Is the RSA modulus well formed?"@en;
+    dct:description "There may be a number of ways of writing the modulus. Is this server able to parse this particular modulus?"@en;
+	dct:hasPart
+		wit:pubkeyRSAModulusFunctional,
+		wit:pubkeyRSAModulusLiteral.
+
+wit:pubkeyRSAModulusFunctional a earl:TestCase;
+	dct:title "Does the public key contain only one modulus?"@en;
+	dct:description "More than one modulus if they don't convert to the same number will lead to erratic behavior (one server will choose one the other server will chose the other)"@en.
+
+wit:pubkeyRSAModulusLiteral a earl:TestCase;
+	dct:title "Is the RSA modulus a literal number?"@en;
+	dct:description "In the current ontology we have moved to literals as the standard way of describing modulus and exponents"@en.
+
+wit:pubkeyRSAExponent a earl:TestCase;
+	dct:title "Is the RSA public exponent well formed?"@en;
+    dct:description "There may be a number of ways of writing the exponent. Is this server able to parse this particular exponent?"@en;
+	dct:hasPart
+		wit:pubkeyRSAExponentFunctional,
+		wit:pubkeyRSAExponentLiteral.
+
+wit:pubkeyRSAExponentFunctional a earl:TestCase;
+	dct:title "Does the public key contain only one public exponent?"@en;
+	dct:description "More than one exponent if they don't convert to the same number is very likely to create erratic behavior (one server will choose one the other server will chose the other)"@en.
+
+wit:pubkeyRSAExponentLiteral a earl:TestCase;
+	dct:title "Is the RSA public exponent a literal number?"@en;
+	dct:description "In the current ontology we have moved to literals as the standard way of describing modulus and exponents"@en.
+
+wit:profileOk a earl:TestRequirement;
+	dct:title "Does the profile fulfill all requirements for WebID authentication?"@en;
+	dct:hasPart
+		wit:profileGet,
+		wit:profileWellFormed,
+		wit:profileAllKeysWellFormed.
+
+wit:pubkeyRSAModulusOldFunctional a earl:TestCase;
+	dct:title "If modulus is using non literal notation, is there only one cert:hex relation to plain literal?"@en;
+	skos:note "this should be a deprecated test sooner rather than later. Warn people to move to newer notation."@en.
+
+wit:pubkeyRSAExponentOldFunctional a earl:TestCase;
+	dct:title "If public exponent is using non literal notation, is there only one cert:decimal relation to plain literal?"@en.
+
+wit:pubkeyOldOk a earl:TestRequirement;
+	dct:title "Is the public key present in valid old non literal notation?"@en;
+	dct:hasPart
+		wit:pubkeyRSAModulusOldFunctional,
+		wit:pubkeyRSAExponentOldFunctional.
+
+#
+# webid protocol tests: ie: tying pubkey and  Webid in certificate to remote WebID identifying description
+#
+wit:webidClaim a earl:TestRequirement;
+	dct:title "Could the particular WebID claim be verified?"@en;
+	dct:description "Verification of a particular WebID claim"@en;
+	dct:hasPart
+		wit:certificateOk,
+		wit:profileOk.
+
+wit:webidAuthentication a earl:TestRequirement;
+	dct:title "Could at least one WebID claim be verified?"@en;
+	dct:description "At least one WebID claimed in the certificate has public key that verifies."@en;
+	dct:hasPart wit:webidClaim.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/template/WebId.xhtml	Sun Nov 20 01:15:14 2011 +0100
@@ -0,0 +1,40 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+	<head id="head">
+	<title>WebId Tests</title>	
+    </head>
+<body>
+  <h1>WebID Login Test Page</h1>
+      
+  <p>This page describes in detail the state of your <a href="http://webid.info/">WebID authentication</a> session on Thu Nov 17 08:31:53 PST 2011. </p>
+
+  <h1>Certificate</h1>
+
+  <table class="cert_test" width="100%" rules="groups">
+      <caption class="tst_question">Could at least one WebID claim be verified?</caption>
+      <thead><tr><td>description</td><td class="tst_txt">At least one WebID claimed in the certificate has public key that verifies.</td></tr></thead>
+      <tbody>
+      <tr><td>subject</td><td><a href="#cert" class="tst_sub">the certificate sent by your browser</a></td></tr>
+      <tr><td>test result</td><td><font color="green" class="tst_res">passed</font></td></tr>
+      <tr><td>result description</td><td class="tst_res_txt">found 1 valid principals</td></tr>
+      <tr><td></td><td><span>http://bblfish.net/people/henry/card#me</span></td></tr>
+      </tbody>
+  </table>
+
+
+  <h2>WebIDs</h2>
+
+  <p class="no_certificate">We have not received a cert</p>
+
+     <table class="test" width="100%" rules="groups">
+        <caption class="tst_question">Could at least one WebID claim be verified?</caption>
+        <thead class="tst_txt"><tr><td>description</td><td>At least one WebID claimed in the certificate has public key that verifies.</td></tr></thead>
+        <tbody>
+        <tr class="tst_sub"><td>subject</td><td><a href="#cert">the certificate sent by your browser</a></td></tr>
+        <tr class="tst_res"><td>test result</td><td><font color="green">passed</font></td></tr>
+        <tr class="tst_res_txt"><td>result description</td><td>found 1 valid principals</td></tr>
+        <tr><td></td><td><span>http://bblfish.net/people/henry/card#me</span></td></tr>
+        </tbody>
+    </table>
+
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/template/hello.html	Sun Nov 20 01:15:14 2011 +0100
@@ -0,0 +1,9 @@
+<html>
+<head>
+  <title>Template Title</title>
+</head>
+<body>
+  <h1 class="title">Template Title</h1>
+  <p> hi</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/resources/template/hello.ssp	Sun Nov 20 01:15:14 2011 +0100
@@ -0,0 +1,9 @@
+<html>
+<head>
+  <title>${title}</title>
+</head>
+<body>
+  <h1>again ${title}</h1>
+  <p> hi</p>
+</body>
+</html>
--- a/src/main/resources/tmp/hello.html	Sun Nov 20 01:10:35 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<html>
-<head>
-  <title>Template Title</title>
-</head>
-<body>
-  <h1 class="title">Template Title</h1>
-  <p> hi</p>
-</body>
-</html>
--- a/src/main/resources/tmp/hello.ssp	Sun Nov 20 01:10:35 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<html>
-<head>
-  <title>${title}</title>
-</head>
-<body>
-  <h1>again ${title}</h1>
-  <p> hi</p>
-</body>
-</html>
--- a/src/main/scala/ReadWriteWebMain.scala	Sun Nov 20 01:10:35 2011 +0100
+++ b/src/main/scala/ReadWriteWebMain.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -3,7 +3,6 @@
 import auth.{RDFAuthZ, X509view}
 import org.w3.readwriteweb.util._
 
-import ui.WebIDAuthnReport
 import unfiltered.jetty._
 import java.io.File
 import Console.err
@@ -102,7 +101,6 @@
       override implicit val authz = new RDFAuthZ[HttpServletRequest,HttpServletResponse](webCache,filesystem)
     }
 
-    def test = new WebIDAuthnReport[HttpServletRequest,HttpServletResponse]() {}
 
     //this is incomplete: we should be able to start both ports.... not sure how to do this yet.
     val service = httpsPort.value match {
@@ -114,8 +112,7 @@
     service.filter(new FilterLogger(logger)).
       context("/public"){ ctx:ContextBuilder =>
         ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
-    }.filter(Planify(test.intent)).
-      filter(Planify(rww.intent)).
+    }.filter(Planify(rww.intent)).
       filter(Planify(x509v.intent)).
       filter(new EchoPlan().plan).run()
     
--- a/src/main/scala/auth/WebIdClaim.scala	Sun Nov 20 01:10:35 2011 +0100
+++ b/src/main/scala/auth/WebIdClaim.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -23,14 +23,13 @@
 
 package org.w3.readwriteweb.auth
 
-import com.hp.hpl.jena.rdf.model.RDFNode
 import java.math.BigInteger
 import java.security.PublicKey
 import org.w3.readwriteweb.WebCache
-import java.util.LinkedList
 import java.security.interfaces.RSAPublicKey
+import com.hp.hpl.jena.query.{QueryExecutionFactory, QueryExecution, QuerySolutionMap, QueryFactory}
+import com.hp.hpl.jena.rdf.model.RDFNode
 import java.net.URL
-import com.hp.hpl.jena.query.{QueryExecutionFactory, QueryExecution, QuerySolutionMap, QueryFactory}
 
 /**
  * @author hjs
@@ -137,19 +136,13 @@
 }
 
 /**
- * An X509 Claim maintains information about the proofs associated with claims
- * found in an X509 Certificate. It is the type of object that can be passed
- * into the public credentials part of a Subject node
- *
- * todo: think of what this would look like for a chain of certificates
+ * A claim that the user identified by the WebId controls the public key
  *
  * @author bblfish
  * @created 30/03/2011
  */
 class WebIDClaim(val webId: String, val key: PublicKey) {
 
-	var errors = new LinkedList[java.lang.Throwable]()
-
 	lazy val principal = new WebIdPrincipal(webId)
 
 	var tests: List[Verification] = List()
@@ -158,7 +151,7 @@
 
   def verified(implicit cache: WebCache): Boolean = {
     if (!valid) tests = verify(cache)
-    tests.exists(v => v.isInstanceOf[Verified])
+    tests.contains(verifiedWebID)
   }
   
   private def verify(implicit cache: WebCache): List[Verification] = {
@@ -234,20 +227,15 @@
 
 }
 
-
-class Verification(msg: String)
-class Verified(msg: String) extends Verification(msg)
-class Unverified(msg: String) extends Verification(msg)
+class ProfileError(msg: String,  t: Throwable) extends Verification(profileOkTst,failed,msg, Some(t))
+class KeyProblem(msg: String) extends Verification(profileWellFormedKeyTst,failed,msg)
 
-class TestFailure(msg: String) extends Verification(msg)
-class ProfileError(msg: String,  t: Throwable ) extends TestFailure(msg)
-class KeyProblem(msg: String) extends TestFailure(msg)
+object keyDoesNotMatch extends Verification(null,null,null) //this test will be forgotten
 
-object unsupportedProtocol extends TestFailure("WebID protocol not supported")
-object noMatchingKey extends TestFailure("No keys in profile matches key in cert")
-object keyDoesNotMatch extends TestFailure("Key does not match")
+object verifiedWebID extends Verification(webidClaimTst, passed, "WebId verified")
+object noMatchingKey extends Verification(webidClaimTst, failed, "No keys in profile matches key in cert")
+object unsupportedProtocol extends Verification(webidClaimTst,untested,"WebID protocol not supported" )
 
-object verifiedWebID extends Verified("WebId verified")
-object notstarted extends Unverified("No verification attempt started")
-object failed extends Unverified("Tests failed")
-object certificateKeyTypeNotSupported extends TestFailure("The certificate key type is not supported. We only support RSA")
\ No newline at end of file
+object certificateKeyTypeNotSupported extends Verification(pubkeyTypeTst,failed,"The certificate key type is not supported. We only support RSA")
+
+
--- a/src/main/scala/auth/X509Claim.scala	Sun Nov 20 01:10:35 2011 +0100
+++ b/src/main/scala/auth/X509Claim.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -90,7 +90,7 @@
  * @author bblfish
  * @created: 30/03/2011
  */
-// should just be a case class
+// can't be a case class as it then creates object which clashes with defined one
 class X509Claim(val cert: X509Certificate) extends Refreshable {
 
   import X509Claim._
@@ -104,6 +104,10 @@
     claims.toSet
   }
 
+  def verifiedClaims(implicit cache: WebCache) = for (
+    claim <- webidclaims if (claim.verified)
+  ) yield claim
+
 
   //note could also implement Destroyable
   //
@@ -119,8 +123,6 @@
   /* The certificate is currently within the valid time zone */
   override def isCurrent(): Boolean = ! (tooLate || tooEarly)
 
-  lazy val error = ()
-
   def canEqual(other: Any) = other.isInstanceOf[X509Claim]
 
   override def equals(other: Any): Boolean = other match {
--- a/src/main/scala/auth/X509view.scala	Sun Nov 20 01:10:35 2011 +0100
+++ b/src/main/scala/auth/X509view.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -23,10 +23,15 @@
 
 package org.w3.readwriteweb.auth
 
-import unfiltered.request.Path
-import unfiltered.response.{Html, ContentType, Ok}
+import java.io.File
+import unfiltered.response.{Ok, Html}
+import unfiltered.Cycle
+import org.fusesource.scalate.{Binding, TemplateEngine}
+import xml.{Elem, XML}
+import unfiltered.request.{Path, &}
+import org.fusesource.scalate.scuery.{Transform, Transformer}
 import org.w3.readwriteweb.WebCache
-import unfiltered.Cycle
+import unfiltered.scalate.Scalate
 
 /**
  * This plan just described the X509 WebID authentication information.
@@ -39,26 +44,37 @@
  * @created: 13/10/2011
  */
 
-trait X509view[Request,Response]  {
+trait X509view[Req,Res]  {
    implicit def wc: WebCache
-   implicit def manif: Manifest[Request]
+   implicit def manif: Manifest[Req]
 
-    def intent: Cycle.Intent[Request, Response] =  {
-      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) => <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>
-            <p>You sent the following certificate</p>
-            <pre>{xclaim.cert.toString}</pre>
-          </body>
-          case _ => <body><p>We received no Authentication information</p></body>
-        }
-          }</html>)
+  val fileDir: File = new File(this.getClass.getResource("/template/").toURI)
+  val templateDirs = List(fileDir)
+  implicit val engine = new TemplateEngine(templateDirs)
+  implicit val bindings: List[Binding] = List(Binding(name = "title", className = "String"))
+  implicit val additionalAttributes = List(("title", "My First Title"))
 
+  val template: Elem = XML.loadFile(new File(fileDir, "WebId.xhtml"))
+
+  def intent : Cycle.Intent[Req,Res] = {
+    case Path("/test/auth/webid") & X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(template) )
+    case req @ Path("/test/WebIdAuth2") => Ok ~> Scalate(req, "hello.ssp")
+  }
+
+}
+
+
+class X509Filler(x509: X509Claim)(implicit cache: WebCache) extends Transformer {
+  $(".cert_test") { node =>
+      val x509Tests = certOk.test(x509);
+      val ff = for (tst <- x509Tests) yield {
+        new Transform(node) {
+          $(".tst_question").contents = tst.of.title
+          $(".tst_txt").contents = tst.of.description
+          $(".tst_res").contents = tst.result.name
+          $(".tst_res_txt").contents = tst.msg
+        }.toNodes()
       }
-
-}
\ No newline at end of file
+      ff.flatten
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/auth/earl.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2011 Henry Story (bblfish.net)
+ * under the MIT licence defined at
+ *    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 com.hp.hpl.jena.vocabulary.DCTerms
+import java.security.cert.X509Certificate
+import java.security.interfaces.RSAPublicKey
+import org.w3.readwriteweb.util.trySome
+import com.hp.hpl.jena.rdf.model.{Property, ModelFactory}
+
+/**
+ * Classes for the tests in WebID Authentication.
+ *
+ * The idea is to try to use the earl test cases we are defining at the WebID XG as
+ * a way of collecting tests done to prove the X509Claim and the WebIDClaim.
+ *
+ * ( bblfish: theses classes feel very ad-hoc, and it is clearly a first pass:
+ *  - we are at the point currently of verifying which tests we need
+ *  - one gets the feeling these classes could even contain behavior
+ *  - we have instances of one class failing but not necessarily succeeding )
+ *
+ */
+
+
+object Tests {
+  // This is where the earl tests are documented and named
+  val ns = "http://www.w3.org/2005/Incubator/webid/earl/RelyingParty"
+  val skos = "http://www.w3.org/2004/02/skos/core#"
+
+  //todo: this model should be a weak pointer
+  val model= ModelFactory.createDefaultModel().read(
+    this.getClass.getResourceAsStream("/ontologies/RelyingParty.n3"),
+    ns,  "TURTLE" )
+
+
+}
+
+
+trait Test {
+  val title: String
+  val description: String
+  val note: String
+}
+
+/**
+ * Public tests
+ */
+class PubTest(val name: String) extends Test {
+  import Tests._
+  implicit def boolToResult(bool: Boolean): Result = if (bool) passed else failed
+
+  private def value(p: Property) = trySome(resource.getProperty(p).getLiteral.getLexicalForm) getOrElse "-missing-"
+  
+  val title: String = value(DCTerms.title)
+  val description: String = value(DCTerms.description)
+  val note: String = value(model.createProperty(skos,"note"))
+
+  private def resource = model.getResource(ns+"#"+name)
+}
+
+//todo: (bblfish:) I get the feeling that one could put the logic into the tests directly.
+//      would that make things easier or better?
+
+//
+//The types of tests that we do here
+//
+
+//for X509Claim
+object certProvided extends PubTest("certificateProvided")   {
+  def test(cert: Option[X509Certificate]): Verification = cert match {
+    case Some(x509) => new Verification(this,passed,"got certificate") //the subject is the session
+    case None => new Verification(this,failed,"missing certificate")
+  }
+}
+
+object certOk extends PubTest("certificateOk") {
+   def test(x509: X509Claim): List[Verification] = {
+     val res = certDateOk.test(x509)::certProvidedSan.test(x509)::certPubKeyRecognized.test(x509)::Nil
+     val problems = res.filter(v => v.result != passed)
+     new Verification(this,problems.length==0,
+       if (problems.length==0) "There were no issues with the certificate"
+       else "There were some issues with your certificate")::res
+   }
+}
+object certProvidedSan extends PubTest("certificateProvidedSAN") {
+  def test(x509: X509Claim) = new Verification(this,x509.webidclaims.size >0,
+    " There are "+x509.webidclaims.size+" SANs in the certificate")
+}
+object certDateOk extends PubTest("certificateDateOk") {
+  def test(x509: X509Claim) = 
+    new Verification(this, x509.isCurrent,
+      "the x509certificate " + (
+        if (x509.tooEarly) "is not yet valid "
+        else if (x509.tooLate) " passed its validity date "
+        else " is valid")
+    )
+  
+}
+
+object certPubKeyRecognized extends PubTest("certificatePubkeyRecognised") {
+  def test(claim: X509Claim) = {
+    val pk = claim.cert.getPublicKey;
+    new Verification(this, pk.isInstanceOf[RSAPublicKey], "We only support RSA Keys at present. " )
+  }
+}
+
+//for WebIDClaims
+object webidClaimTst extends PubTest("webidClaim")
+object pubkeyTypeTst extends PubTest("certificatePubkeyRecognised")
+object profileOkTst extends PubTest("profileWellFormed")
+object profileWellFormedKeyTst extends PubTest("profileWellFormedPubkey")
+
+
+
+/** short verification, that sits inside X509Claim or WebIdClaim and from which full verifications can be constituted */
+class Verification( val of: Test,
+             val result: Result = untested,
+             val msg: String,
+             val err: Option[Throwable] = None )
+
+
+sealed class Result(val name: String)  {
+  val earl = "http://www.w3.org/ns/earl#"
+  def id = earl+name
+}
+
+object untested extends Result("untested")
+object passed extends Result("passed")
+object failed extends Result("failed")
+
+
--- a/src/main/scala/netty/ReadWriteWebNetty.scala	Sun Nov 20 01:10:35 2011 +0100
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala	Sun Nov 20 01:15:14 2011 +0100
@@ -28,10 +28,7 @@
 import org.w3.readwriteweb.auth.{X509view, RDFAuthZ}
 import org.w3.readwriteweb._
 import org.jboss.netty.handler.codec.http.HttpResponse
-import ui.WebIDAuthnReport
 import unfiltered.netty.{ServerErrorResponse, ReceivedMessage, cycle}
-import unfiltered.filter.Planify._
-import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
 
 /**
  * ReadWrite Web for Netty server, allowing TLS renegotiation
@@ -63,8 +60,6 @@
           override val authz = new RDFAuthZ[ReceivedMessage,HttpResponse](webCache,filesystem)
      }
 
-     val test = new cycle.Plan with cycle.ThreadPool with ServerErrorResponse with WebIDAuthnReport[ReceivedMessage,HttpResponse]
-
      //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)
@@ -73,8 +68,7 @@
 
      // configures and launches a Netty server
      service.plan( x509v ).
-            plan( test ).
-            plan( rww ).run()
+             plan( rww ).run()
      
    }
 
--- a/src/main/scala/ui/WebIDAuthnReport.scala	Sun Nov 20 01:10:35 2011 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2011 Henry Story (bblfish.net)
- * under the MIT licence defined at
- *    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.ui
-
-import java.io.File
-import xml.XML
-import unfiltered.response.{Ok, Html}
-import unfiltered.request.Path
-import unfiltered.Cycle
-import org.fusesource.scalate.scuery.Transformer
-import org.fusesource.scalate.{Binding, TemplateEngine}
-import unfiltered.scalate.Scalate
-
-/**
- *
- * @author Henry Story
- */
-
-trait WebIDAuthnReport[A,B] {
-
-  val fileDir: File = new File(this.getClass.getResource("/tmp/").toURI)
-  val templateDirs = List(fileDir)
-  implicit val engine = new TemplateEngine(templateDirs)
-  implicit val bindings: List[Binding] = List(Binding(name = "title", className = "String"))
-  implicit val additionalAttributes = List(("title", "My First Title"))
-
-
-  def intent : Cycle.Intent[A,B] = {
-    case req @ Path("/test/WebIdAuth") =>  Ok ~> Html(transformer.apply(XML.loadFile(new File(fileDir,"hello.html"))))
-    case req @ Path("/test/WebIdAuth2") => Ok ~> Scalate(req, "hello.ssp")
-  }
-
-}
-
-object transformer extends Transformer {
-  $(".title").contents = "The Real Title"
-
-}