updated to latest spec ( http://webid.info/spec ) which simplifies dramatically the code - it is not accidental. Also upgraded to spec2 which seems to work.
--- a/project/build.scala Wed Nov 23 15:22:01 2011 +0100
+++ b/project/build.scala Thu Nov 24 13:53:25 2011 +0100
@@ -4,7 +4,9 @@
// some usefull libraries
// they are pulled only if used
object Dependencies {
- val specs = "org.scala-tools.testing" %% "specs" % "1.6.9" % "test"
+// val specs = "org.scala-tools.testing" %% "specs" % "1.6.9" % "test"
+ val specs2 = "org.specs2" %% "specs2" % "1.6.1"
+ val specs2_scalaz = "org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test"
val dispatch_http = "net.databinder" %% "dispatch-http" % "0.8.5"
val unfiltered_version = "0.5.1"
val unfiltered_filter = "net.databinder" %% "unfiltered-filter" % unfiltered_version
@@ -82,7 +84,8 @@
resolvers += mavenLocal,
resolvers += ScalaToolsReleases,
resolvers += ScalaToolsSnapshots,
- libraryDependencies += specs,
+ libraryDependencies += specs2,
+ libraryDependencies += specs2_scalaz,
// libraryDependencies += unfiltered_spec,
ivyXML := ivyUnfilteredSpec,
libraryDependencies += dispatch_http,
--- a/src/main/scala/auth/WebIdClaim.scala Wed Nov 23 15:22:01 2011 +0100
+++ b/src/main/scala/auth/WebIdClaim.scala Thu Nov 24 13:53:25 2011 +0100
@@ -23,13 +23,12 @@
package org.w3.readwriteweb.auth
-import java.math.BigInteger
import java.security.PublicKey
import org.w3.readwriteweb.WebCache
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.datatypes.xsd.XSDDatatype
/**
* @author hjs
@@ -37,101 +36,16 @@
*/
object WebIDClaim {
- final val cert: String = "http://www.w3.org/ns/auth/cert#"
- final val xsd: String = "http://www.w3.org/2001/XMLSchema#"
-
- val selectQuery = QueryFactory.create("""
- PREFIX cert: <http://www.w3.org/ns/auth/cert#>
- PREFIX rsa: <http://www.w3.org/ns/auth/rsa#>
- SELECT ?m ?e ?mod ?exp
- WHERE {
- {
- ?key cert:identity ?webid .
- } UNION {
- ?webid cert:key ?key .
- }
- ?key rsa:modulus ?m ;
- rsa:public_exponent ?e .
-
- OPTIONAL { ?m cert:hex ?mod . }
- OPTIONAL { ?e cert:decimal ?exp . }
- }""") //Including OPTIONAL notation, for backward compatibility - should remove that after a while
-
- /**
- * Transform an RDF representation of a number into a BigInteger
- * <p/>
- * Passes a statement as two bindings and the relation between them. The
- * subject is the number. If num is already a literal number, that is
- * returned, otherwise if enough information from the relation to optstr
- * exists, that is used.
- *
- * @param num the number node
- * @param optRel name of the relation to the literal
- * @param optstr the literal representation if it exists
- * @return the big integer that num represents, or null if undetermined
- */
- def toInteger(num: RDFNode, optRel: String, optstr: RDFNode): Option[BigInteger] =
- if (null == num) None
- else if (num.isLiteral) {
- val lit = num.asLiteral()
- toInteger_helper(lit.getLexicalForm,lit.getDatatypeURI)
- } else if (null != optstr && optstr.isLiteral) {
- toInteger_helper(optstr.asLiteral().getLexicalForm,optRel)
- } else None
-
+ final val cert: String = "http://www.w3.org/ns/auth/cert#"
- private def intValueOfHexString(s: String): BigInteger = {
- val strval = cleanHex(s);
- new BigInteger(strval, 16);
- }
-
-
- /**
- * This takes any string and returns in order only those characters that are
- * part of a hex string
- *
- * @param strval
- * any string
- * @return a pure hex string
- */
-
- private def cleanHex(strval: String) = {
- def legal(c: Char) = {
- //in order of appearance probability
- ((c >= '0') && (c <= '9')) ||
- ((c >= 'A') && (c <= 'F')) ||
- ((c >= 'a') && (c <= 'f'))
- }
- (for (c <- strval; if legal(c)) yield c)
- }
-
+ val askQuery = QueryFactory.create("""
+ PREFIX : <http://www.w3.org/ns/auth/cert#>
+ ASK {
+ ?webid :key [ :modulus ?m ;
+ :exponent ?e ].
+ }""")
- /**
- * This transforms a literal into a number if possible ie, it returns the
- * BigInteger of "ddd"^^type
- *
- * @param num the string representation of the number
- * @param tpe the type of the string representation
- * @return the number
- */
- protected def toInteger_helper(num: String, tpe: String): Option[BigInteger] =
- try {
- if (tpe.equals(cert + "decimal") || tpe.equals(cert + "int")
- || tpe.equals(xsd + "integer") || tpe.equals(xsd + "int")
- || tpe.equals(xsd + "nonNegativeInteger")) {
- // cert:decimal is deprecated
- Some(new BigInteger(num.trim(), 10));
- } else if (tpe.equals(cert + "hex")) {
- Some(intValueOfHexString(num));
- } else {
- // it could be some other encoding - one should really write a
- // special literal transformation class
- None;
- }
- } catch {
- case e: NumberFormatException => None
- }
-
+ def hex(bytes: Array[Byte]): String = bytes.map("%02X" format _).mkString.stripPrefix("00")
}
@@ -147,6 +61,8 @@
var tests: List[Verification] = List()
+ /** the tests have been done and are still valid - the idea is perhaps after a time tests would
+ * have to be done again? Eg: the claim is cached and re-used after a while */
private var valid = false
def verified(implicit cache: WebCache): Boolean = {
@@ -157,7 +73,6 @@
private def verify(implicit cache: WebCache): List[Verification] = {
import org.w3.readwriteweb.util.wrapValidation
- import collection.JavaConversions._
import WebIDClaim._
try {
return if (!webId.startsWith("http:") && !webId.startsWith("https:")) {
@@ -171,32 +86,21 @@
t => new ProfileError("error fetching profile", t)
}
} yield {
+ val rsakey = key.asInstanceOf[RSAPublicKey]
val initialBinding = new QuerySolutionMap();
initialBinding.add("webid",model.createResource(webId))
- val qe: QueryExecution = QueryExecutionFactory.create(WebIDClaim.selectQuery, model,initialBinding)
+ initialBinding.add("m",model.createTypedLiteral( hex(rsakey.getModulus.toByteArray), XSDDatatype.XSDhexBinary))
+ initialBinding.add("e",model.createTypedLiteral( rsakey.getPublicExponent.toString, XSDDatatype.XSDinteger ))
+ val qe: QueryExecution = QueryExecutionFactory.create(WebIDClaim.askQuery, model,initialBinding)
try {
- qe.execSelect().map( qs => {
- val modulus = toInteger(qs.get("m"), cert + "hex", qs.get("mod"))
- val exponent = toInteger(qs.get("e"), cert + "decimal", qs.get("exp"))
-
- (modulus, exponent) match {
- case (Some(mod), Some(exp)) => {
- val rsakey = key.asInstanceOf[RSAPublicKey]
- if (rsakey.getPublicExponent == exp && rsakey.getModulus == mod) verifiedWebID
- else keyDoesNotMatch
- }
- case _ => new KeyProblem("profile contains key that cannot be analysed:" +
- qs.varNames().map(nm => nm + "=" + qs.get(nm).toString) + "; ")
- }
- }).toList
- //it would be nice if we could keep a lot more state of what was verified and how
- //will do that when implementing tests, so that these tests can then be used directly as much as possible
+ if (qe.execAsk()) verifiedWebID
+ else noMatchingKey
} finally {
qe.close()
}
}
res.either match {
- case Right(tests) => tests
+ case Right(tests) => tests::Nil
case Left(profileErr) => profileErr::Nil
}
}
@@ -206,8 +110,6 @@
}
-
-
def canEqual(other: Any) = other.isInstanceOf[WebIDClaim]
--- a/src/test/scala/auth/CreateWebIDSpec.scala Wed Nov 23 15:22:01 2011 +0100
+++ b/src/test/scala/auth/CreateWebIDSpec.scala Thu Nov 24 13:53:25 2011 +0100
@@ -34,6 +34,7 @@
import javax.net.ssl._
import java.io.File
import org.w3.readwriteweb.{Post, RDFXML, TURTLE}
+import org.apache.commons.codec.binary.Hex
/**
@@ -101,11 +102,11 @@
val updatePk = """
PREFIX cert: <http://www.w3.org/ns/auth/cert#>
- PREFIX rsa: <http://www.w3.org/ns/auth/rsa#>
+ PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX : <#>
INSERT DATA {
- :jL cert:key [ rsa:modulus "%s"^^cert:hex;
- rsa:public_exponent "%s"^^cert:int ] .
+ :jL cert:key [ cert:modulus "%s"^^xsd:hexBinary;
+ cert:exponent "%s"^^xsd:integer ] .
}
"""
@@ -181,8 +182,9 @@
val joeKey = joeCert(0).getPublicKey.asInstanceOf[RSAPublicKey]
+ val hex = new String(Hex.encodeHex(joeKey.getModulus.toByteArray))
val updateQStr = updatePk.format(
- joeKey.getModulus.toString(16),
+ hex.stripPrefix("00"),
joeKey.getPublicExponent()
)
--- a/src/test/scala/auth/secure_specs.scala Wed Nov 23 15:22:01 2011 +0100
+++ b/src/test/scala/auth/secure_specs.scala Thu Nov 24 13:53:25 2011 +0100
@@ -35,6 +35,8 @@
import dispatch.Http
import org.apache.http.client.HttpClient
import javax.net.ssl.{SSLContext, X509TrustManager, KeyManager}
+import util.trySome
+import java.nio.file.Files
/**
* @author hjs
@@ -122,7 +124,18 @@
lazy val baseURL = "/wiki"
- lazy val root = new File(new File(System.getProperty("java.io.tmpdir")), "readwriteweb")
+ /**
+ * finding where the specs2 output directory is, so that we can create temporary directories there,
+ * which can then be viewed if tests are unsuccessful, but that will also be removed on "sbt clean"
+ */
+ lazy val outDirBase = new File(trySome { System.getProperty("spec2.outDir") } getOrElse "target/specs2-reports/")
+
+ lazy val root = {
+ outDirBase.mkdirs()
+ val dir = Files.createTempDirectory(outDirBase.toPath, "test_rww_")
+ System.out.println("Temp directory: "+dir.toString)
+ dir.toFile
+ }
lazy val resourceManager = new Filesystem(root, baseURL, lang)(mode)