merged changes into main webid branch. (getting used to hg)
--- a/project/build.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/project/build.scala Wed Jan 11 13:30:56 2012 +0100
@@ -22,21 +22,19 @@
<exclude org="net.databinder" module="dispatch-mime_2.9.0-1"/>
</dependency>
</dependencies>
- val slf4jSimple = "org.slf4j" % "slf4j-simple" % "1.6.4"
- val slf4japi = "org.slf4j" % "slf4j-api" % "1.6.4"
- val slf4jlog = "org.slf4j" % "slf4j-log4j12" % "1.6.4"
val antiXML = "com.codecommit" %% "anti-xml" % "0.4-SNAPSHOT" % "test"
- val jena = "com.hp.hpl.jena" % "jena" % "2.6.4"
+ val jena = "org.apache.jena" % "jena-core" % "2.7.0-incubating"
+ val arq = "org.apache.jena" % "jena-arq" % "2.9.0-incubating"
val rdfa = "net.rootdev" % "java-rdfa" % "0.4.2-RC2"
val htmlparser = "nu.validator.htmlparser" % "htmlparser" % "1.2.1"
- val arq = "com.hp.hpl.jena" % "arq" % "2.8.8"
val grizzled = "org.clapper" %% "grizzled-scala" % "1.0.8" % "test"
val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.3"
val argot = "org.clapper" %% "argot" % "0.3.5"
- val guava = "com.google.guava" % "guava" % "10.0.1"
+ val guava = "com.google.guava" % "guava" % "11.0"
// val restlet = "org.restlet.dev" % "org.restlet" % "2.1-SNAPSHOT"
// val restlet_ssl = "org.restlet.dev" % "org.restlet.ext.ssl" % "2.1-SNAPSHOT"
val jsslutils = "org.jsslutils" % "jsslutils" % "1.0.5"
+ val slf4s = "com.weiglewilczek.slf4s" %% "slf4s" % "1.0.7"
}
// some usefull repositories
@@ -111,9 +109,6 @@
libraryDependencies += unfiltered_filter,
libraryDependencies += unfiltered_jetty,
libraryDependencies += unfiltered_netty,
- libraryDependencies += slf4jSimple,
- libraryDependencies += slf4japi,
- libraryDependencies += slf4jlog,
libraryDependencies += jena,
libraryDependencies += arq,
@@ -126,6 +121,7 @@
libraryDependencies += scalate,
libraryDependencies += rdfa,
libraryDependencies += htmlparser,
+ libraryDependencies += slf4s,
jarName in assembly := "read-write-web.jar",
mainClass in assembly := Some("org.w3.readwriteweb.netty.ReadWriteWebNetty")
--- a/src/main/resources/log4j.properties Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/resources/log4j.properties Wed Jan 11 13:30:56 2012 +0100
@@ -1,9 +1,9 @@
-// Appenders
-log4j.appender.Console=org.apache.log4j.ConsoleAppender
-log4j.appender.Console.layout=org.apache.log4j.PatternLayout
-// Pattern not for use in production ( %C
-log4j.appender.Console.layout.ConversionPattern=%-5p (%F:%L) : %m%n
-
-// Loggers
-log4j.rootLogger=info, Console
-log4j.logger.org.w3.readwriteweb=debug
+// Appenders
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+// Pattern not for use in production ( %C
+log4j.appender.Console.layout.ConversionPattern=%-5p (%F:%L) : %m%n
+
+// Loggers
+log4j.rootLogger=info, Console
+log4j.logger.org.w3.readwriteweb=debug
--- a/src/main/scala/Filesystem.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Filesystem.scala Wed Jan 11 13:30:56 2012 +0100
@@ -45,7 +45,7 @@
logger.debug("%s successfully created: %s" format (fileOnDisk.getAbsolutePath, r.toString))
}
- def get(): Validation[Throwable, Model] = {
+ def get(unused: CacheControl.Value = CacheControl.CacheFirst): Validation[Throwable, Model] = {
val model = ModelFactory.createDefaultModel()
val guessLang = fileOnDisk.getName match {
case Authoritative.r(_,suffix) => Representation.fromSuffix(suffix) match {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/GraphCache.scala Wed Jan 11 13:30:56 2012 +0100
@@ -0,0 +1,140 @@
+ /*
+ * 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
+
+import com.hp.hpl.jena.rdf.model.Model
+import org.apache.http.MethodNotSupportedException
+import org.w3.readwriteweb.util._
+import java.net.{ConnectException, URL}
+import scalaz.{Scalaz, Validation}
+import java.util.concurrent.TimeUnit
+import com.google.common.cache.{LoadingCache, CacheLoader, CacheBuilder, Cache}
+import java.io.{File, FileOutputStream}
+import com.weiglewilczek.slf4s.Logging
+
+
+/**
+ * Fetch resources on the Web and cache them
+ * ( at a later point this would include saving them to an indexed quad store )
+ *
+ * @author Henry Story
+ * @created: 12/10/2011
+ *
+ */
+object GraphCache extends ResourceManager with Logging {
+ import dispatch._
+ import Scalaz._
+
+ //use shellac's rdfa parser
+ new net.rootdev.javardfa.jena.RDFaReader //import rdfa parser
+
+
+ //this is a simple but quite stupid web cache so that graphs can stay in memory and be used a little
+ // bit across sessions
+ val cache: LoadingCache[URL,Validation[Throwable,Model]] =
+ CacheBuilder.newBuilder()
+ .expireAfterAccess(5, TimeUnit.MINUTES)
+ .softValues()
+// .expireAfterWrite(30, TimeUnit.MINUTES)
+ .build(new CacheLoader[URL, Validation[Throwable,Model]] {
+ def load(url: URL) = getUrl(url)
+ })
+
+ val http = new Http with thread.Safety {
+ import org.apache.http.params.CoreConnectionPNames
+ client.getParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 3000)
+ client.getParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 15000)
+ }
+
+ def basePath = null //should be cache dir?
+
+ def sanityCheck() = true //cache dire exists? But is this needed for functioning?
+
+ def resource(u : URL) = new org.w3.readwriteweb.Resource {
+ import CacheControl._
+ def name() = u
+ def get(cacheControl: CacheControl.Value) = cacheControl match {
+ case CacheOnly => {
+ val res = cache.getIfPresent(u)
+ if (null==res) NoCachEntry.fail
+ else res
+ }
+ case CacheFirst => cache.get(u)
+ case NoCache => {
+ val res = getUrl(u)
+ cache.put(u,res) //todo: should this only be done if say the returned value is not an error?
+ res
+ }
+ }
+ // when fetching information from the web creating directories does not make sense
+ //perhaps the resource manager should be split into read/write sections?
+ def save(model: Model) = throw new MethodNotSupportedException("not implemented")
+
+ def createDirectory(model: Model) = throw new MethodNotSupportedException("not implemented")
+ }
+
+ private def getUrl(u: URL) = {
+
+ // note we prefer rdf/xml and turtle over html, as html does not always contain rdfa, and we prefer those over n3,
+ // as we don't have a full n3 parser. Better would be to have a list of available parsers for whatever rdf framework is
+ // installed (some claim to do n3 when they only really do turtle)
+ // we can't currently accept */* as we don't have GRDDL implemented
+ val request = url(u.toString) <:< Map("Accept"->
+ "application/rdf+xml,text/turtle,application/xhtml+xml;q=0.8,text/html;q=0.7,text/n3;q=0.6")
+ logger.info("fetching "+u.toExternalForm)
+
+ //we need to tell the model about the content type
+ val handler: Handler[Validation[Throwable, Model]] = request.>+>[Validation[Throwable, Model]](res => {
+ res >:> { headers =>
+ val encoding = headers("Content-Type").headOption match {
+ case Some(mime) => {
+ Lang(mime.split(";")(0)) getOrElse Lang.default
+ }
+ case None => RDFXML //todo: it would be better to try to do a bit of guessing in this case by looking at content
+ }
+ val loc = headers("Content-Location").headOption match {
+ case Some(loc) => new URL(u,loc)
+ case None => new URL(u.getProtocol,u.getAuthority,u.getPort,u.getPath)
+ }
+ res>>{ in=> modelFromInputStream(in,loc,encoding) }
+
+ }
+ })
+ try {
+ val future = http(handler)
+ future
+ } catch {
+ case e: ConnectException => {
+ logger.info("failed to connect to "+u.getHost,e)
+ e.fail
+ }
+ }
+
+ }
+
+
+ override def finalize() { http.shutdown() }
+}
+
+ object NoCachEntry extends Exception
\ No newline at end of file
--- a/src/main/scala/Lang.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Lang.scala Wed Jan 11 13:30:56 2012 +0100
@@ -59,7 +59,7 @@
case "text/turtle" => Some(TURTLE)
case "application/rdf+xml" => Some(RDFXML)
case "text/html" => Some(HTML)
- case "text/xhtml" => Some(XHTML)
+ case "application/xhtml+xml" => Some(XHTML)
case _ => None
}
--- a/src/main/scala/ReadWriteWebMain.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/ReadWriteWebMain.scala Wed Jan 11 13:30:56 2012 +0100
@@ -5,18 +5,20 @@
import org.w3.readwriteweb.util._
import unfiltered.jetty._
-import java.io.File
import Console.err
import org.slf4j.{Logger, LoggerFactory}
import org.clapper.argot._
import ArgotConverters._
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
-import java.security.KeyStore
+import com.weiglewilczek.slf4s.Logging
+import java.lang.{Class, String}
+import java.net.InetAddress
+import java.io.{FileDescriptor, File}
+import java.security.{Permission, KeyStore}
trait ReadWriteWebArgs {
val logger: Logger = LoggerFactory.getLogger(this.getClass)
- new net.rootdev.javardfa.jena.RDFaReader //import rdfa parser
val postUsageMsg= Some("""
|PROPERTIES
@@ -79,6 +81,7 @@
}
+
object ReadWriteWebMain extends ReadWriteWebArgs {
import unfiltered.filter.Planify
@@ -92,6 +95,7 @@
case e: ArgotUsageException => err.println(e.message); sys.exit(1)
}
+
val filesystem =
new Filesystem(
rootDirectory.value.get,
--- a/src/main/scala/Resource.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/Resource.scala Wed Jan 11 13:30:56 2012 +0100
@@ -13,9 +13,13 @@
def resource(url:URL):Resource
}
+object CacheControl extends Enumeration {
+ val CacheOnly, CacheFirst, NoCache = Value
+}
+
trait Resource {
def name: URL
- def get():Validation[Throwable, Model]
+ def get(policy: CacheControl.Value = CacheControl.CacheFirst): Validation[Throwable, Model]
def save(model:Model):Validation[Throwable, Unit]
def createDirectory(model: Model): Validation[Throwable, Unit]
}
--- a/src/main/scala/WebCache.scala Wed Jan 11 13:30:13 2012 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
- /*
- * 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
-
-import com.hp.hpl.jena.rdf.model.Model
-import org.apache.http.MethodNotSupportedException
-import org.w3.readwriteweb.util._
-import java.net.{ConnectException, URL}
-import scalaz.{Scalaz, Validation}
-import java.util.concurrent.TimeUnit
-import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
-
-
-/**
- * @author Henry Story
- * @created: 12/10/2011
- *
- * The WebCache currently does not cache
- */
-object WebCache extends ResourceManager {
- import dispatch._
- import Scalaz._
-
- //this is a simple but quite stupid web cache so that graphs can stay in memory and be used a little
- // bit across sessions
- val cache: Cache[URL,Validation[Throwable,Model]] =
- CacheBuilder.newBuilder()
- .expireAfterAccess(5, TimeUnit.MINUTES)
-// .softValues()
-// .expireAfterWrite(30, TimeUnit.MINUTES)
- .build(new CacheLoader[URL, Validation[Throwable,Model]] {
- def load(url: URL) = getUrl(url)
- })
-
- val http = new Http with thread.Safety
-
- def basePath = null //should be cache dir?
-
- def sanityCheck() = true //cache dire exists? But is this needed for functioning?
-
- def resource(u : URL) = new org.w3.readwriteweb.Resource {
- def name() = u
- def get() = cache.get(u)
-
- // when fetching information from the web creating directories does not make sense
- //perhaps the resource manager should be split into read/write sections?
- def save(model: Model) = throw new MethodNotSupportedException("not implemented")
-
- def createDirectory(model: Model) = throw new MethodNotSupportedException("not implemented")
- }
-
- private def getUrl(u: URL) = {
-
- // note we prefer rdf/xml and turtle over html, as html does not always contain rdfa, and we prefer those over n3,
- // as we don't have a full n3 parser. Better would be to have a list of available parsers for whatever rdf framework is
- // installed (some claim to do n3 when they only really do turtle)
- // we can't currently accept */* as we don't have GRDDL implemented
- val request = url(u.toString) <:< Map("Accept"->
- "application/rdf+xml,text/turtle,application/xhtml+xml;q=0.8,text/html;q=0.7,text/n3;q=0.6")
-
- //we need to tell the model about the content type
- val handler: Handler[Validation[Throwable, Model]] = request.>+>[Validation[Throwable, Model]](res => {
- res >:> { headers =>
- val encoding = headers("Content-Type").headOption match {
- case Some(mime) => {
- Lang(mime.split(";")(0)) getOrElse Lang.default
- }
- case None => RDFXML //todo: it would be better to try to do a bit of guessing in this case by looking at content
- }
- val loc = headers("Content-Location").headOption match {
- case Some(loc) => new URL(u,loc)
- case None => new URL(u.getProtocol,u.getAuthority,u.getPort,u.getPath)
- }
- res>>{ in=> modelFromInputStream(in,loc,encoding) }
-
- }
- })
- try {
- val future = http(handler)
- future
- } catch {
- case e: ConnectException => e.fail
- }
-
- }
-
-
- override def finalize() { http.shutdown() }
-}
--- a/src/main/scala/auth/Authz.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/Authz.scala Wed Jan 11 13:30:56 2012 +0100
@@ -30,7 +30,7 @@
import com.hp.hpl.jena.query.{QueryExecutionFactory, QuerySolutionMap, QueryFactory}
import unfiltered.response.{ResponseFunction, Unauthorized}
import com.hp.hpl.jena.rdf.model.ResourceFactory
-import org.w3.readwriteweb.{Authoritative, Resource, ResourceManager, WebCache}
+import org.w3.readwriteweb.{Authoritative, Resource, ResourceManager, GraphCache}
import org.w3.readwriteweb.util.HttpMethod
/**
--- a/src/main/scala/auth/Principals.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/Principals.scala Wed Jan 11 13:30:56 2012 +0100
@@ -24,12 +24,12 @@
package org.w3.readwriteweb.auth
import java.security.Principal
-import org.w3.readwriteweb.WebCache
import com.hp.hpl.jena.rdf.model.Model
import com.hp.hpl.jena.shared.WrappedIOException
import scalaz.{Scalaz, Validation}
import Scalaz._
import java.net.{ConnectException, URL}
+import org.w3.readwriteweb.{CacheControl, GraphCache}
/**
* @author Henry Story from http://bblfish.net/
@@ -75,10 +75,10 @@
case _ => false
}
- //TODO: now that we are no longer passing the WebCache around it's questionable whether we still need this method
+ //TODO: now that we are no longer passing the GraphCache around it's questionable whether we still need this method
//in this class
- def getDefiningModel: Validation[ProfileError, Model] =
- WebCache.resource(url).get() failMap {
+ def getDefiningModel(cacheControl: CacheControl.Value = CacheControl.CacheFirst): Validation[ProfileError, Model] =
+ GraphCache.resource(url).get(cacheControl) failMap {
case ioe: WrappedIOException => new ProfileGetError("error fetching profile", Some(ioe),url)
case connE : ConnectException => new ProfileGetError("error fetching profile", Some(connE),url)
case other => new ProfileParseError("error parsing profile", Some(other),url)
--- a/src/main/scala/auth/WebIDSrvc.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/WebIDSrvc.scala Wed Jan 11 13:30:56 2012 +0100
@@ -33,7 +33,6 @@
import com.hp.hpl.jena.rdf.model.ModelFactory
import sommer.{CertAgent, Extractors}
import org.w3.readwriteweb.util._
-import org.w3.readwriteweb.auth._
import unfiltered.request._
import org.fusesource.scalate.scuery.{Transform, Transformer}
import org.w3.readwriteweb.netty.ReadWriteWebNetty.StaticFiles
@@ -41,6 +40,7 @@
import xml._
import unfiltered.response._
import java.security.interfaces.RSAPublicKey
+import org.w3.readwriteweb.auth._
object WebIDSrvc {
val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
@@ -86,16 +86,23 @@
if (next!=Nil && next.size==1) srvStaticFiles(next.head)
else req match {
case Params(RelyingParty(rp)) => req match {
- case Params(DoIt(_)) & XClaim(claim: XClaim) => {
- val answer = if (claim == NoClaim) "error=nocert&"
- else if (claim.claims.size == 0) "error=noWebID&"
- else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
- claim.claims.map(claim => claim.verify.failMap(e => "cause="+e.getMessage)).mkString("&")+"&"
- else claim.verified.slice(0, 3).foldRight("") {
- (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+ //&doit => we redirect to the relying party with the identity answer
+ //TODO: here I would have liked to go with what the user decided without forcing him to login,
+ //ie by using XClaim(claim) in the match,
+ //but http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
+ case Params(DoIt(_)) => {
+ val answer = X509Claim.unapply(req) match {
+ case None => "error=nocert&"
+ case Some(claim) =>
+ if (claim.claims.size == 0) "error=noWebID&"
+ else if (claim.verified.size == 0) "error=noVerifiedWebID&" +
+ claim.claims.map(claim => claim.verify.failMap(e => "cause=" + e.getMessage)).mkString("&") + "&"
+ else claim.verified.slice(0, 3).foldRight("") {
+ (wid, str) => "webid=" + URLEncoder.encode(wid.url.toExternalForm, "UTF-8") + "&"
+ }
}
val signedAnswer = sign(rp.toExternalForm + "?" + answer).toExternalForm
- Redirect(signedAnswer)
+ Redirect(signedAnswer) ~> Expires("0") ~> CacheControl("no-cache")
}
//GET=>The user just arrived on the page. We recuperated the X509 claim in case he has authenticated already
case GET(_) & XClaim(claim: XClaim) => {
@@ -103,7 +110,7 @@
case NoClaim => profilePg
case claim: X509Claim => if (claim.verified.size > 0) authenticatedPg else errorPg
}
- Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg))
+ Ok ~> Html5(new ServiceTrans(rp, claim).apply(pg)) ~> Expires("0") ~> CacheControl("no-cache")
}
//POST=> we authenticate the user because he has agreed to be authenticated on the page, which we know if the
// request is a POST
@@ -159,7 +166,7 @@
claim match {
case NoClaim => <span/>
case _ => new Transform(node) {
- val union = claim.verified.flatMap(_.getDefiningModel.toOption).fold(ModelFactory.createDefaultModel()) {
+ val union = claim.verified.flatMap(_.getDefiningModel().toOption).fold(ModelFactory.createDefaultModel()) {
(m1, m2) => m1.add(m2)
}
//this works because we have verified before
--- a/src/main/scala/auth/WebIdClaim.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/WebIdClaim.scala Wed Jan 11 13:30:56 2012 +0100
@@ -32,6 +32,8 @@
import com.hp.hpl.jena.query._
import java.math.BigInteger
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype
+import org.w3.readwriteweb.CacheControl
+import scalaz.{Failure, Scalaz, Validation}
/**
@@ -95,9 +97,15 @@
}
}
- lazy val verify: Validation[WebIDClaimFailure, WebID] = key match {
+ def verify: Validation[WebIDClaimFailure, WebID] = key match {
case rsakey: RSAPublicKey =>
- WebID(san).flatMap(webid=> webid.getDefiningModel.flatMap(rsaTest(webid, rsakey)) )
+ WebID(san).flatMap(webid=> {
+ webid.getDefiningModel(CacheControl.CacheOnly).flatMap(rsaTest(webid, rsakey)) match {
+ case Failure(_) => webid.getDefiningModel(CacheControl.NoCache).flatMap(rsaTest(webid, rsakey))
+ case o => o
+ }
+ }
+ )
case _ => new UnsupportedKeyType("We only support RSA keys at present", key).fail
}
}
--- a/src/main/scala/auth/X509Cert.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509Cert.scala Wed Jan 11 13:30:56 2012 +0100
@@ -217,9 +217,11 @@
val sslh = r.underlying.context.getPipeline.get(classOf[SslHandler])
trySome(sslh.getEngine.getSession.getPeerCertificates.toIndexedSeq) orElse {
+ //it seems that the jvm does not keep a very good cache of remote certificates in a session. But
+ //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
if (!fetch) None
else {
- sslh.setEnableRenegotiation(true)
+ sslh.setEnableRenegotiation(true) // todo: does this have to be done on every request?
r match {
case UserAgent(agent) if needAuth(agent) => sslh.getEngine.setNeedClientAuth(true)
case _ => sslh.getEngine.setWantClientAuth(true)
--- a/src/main/scala/auth/X509Claim.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509Claim.scala Wed Jan 11 13:30:56 2012 +0100
@@ -32,10 +32,11 @@
import unfiltered.request.HttpRequest
import java.security.interfaces.RSAPublicKey
import collection.immutable.List
-import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
import java.util.concurrent.TimeUnit
import org.w3.readwriteweb.util.trySome
import java.util.Date
+import com.weiglewilczek.slf4s.Logging
+import com.google.common.cache.{LoadingCache, CacheLoader, CacheBuilder, Cache}
/**
* @author hjs
@@ -47,8 +48,8 @@
implicit val fetch = true //fetch the certificate if we don't have it
// this is cool because it is not in danger of running out of memory but it makes it impossible to create the claim
-// with an implicit WebCache...
- val idCache: Cache[X509Certificate, X509Claim] =
+// with an implicit GraphCache...
+ val idCache: LoadingCache[X509Certificate, X509Claim] =
CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.MINUTES)
.build(new CacheLoader[X509Certificate, X509Claim] {
@@ -92,6 +93,8 @@
}
object XClaim {
+ //warning: it turns out that one nearly never recuperates the client certificate
+ //see http://stackoverflow.com/questions/8731157/netty-https-tls-session-duration-why-is-renegotiation-needed
implicit val fetch = false
def unapply[T](r: HttpRequest[T])(implicit m: Manifest[T]): Option[XClaim] = r match {
case Certs(c1: X509Certificate, _*) => trySome(X509Claim.idCache.get(c1))
@@ -122,19 +125,19 @@
* @created: 30/03/2011
*/
// can't be a case class as it then creates object which clashes with defined one
-class X509Claim(val cert: X509Certificate) extends Refreshable with XClaim {
+class X509Claim(val cert: X509Certificate) extends Refreshable with XClaim with Logging {
import X509Claim._
val claimReceivedDate = new Date()
lazy val tooLate = claimReceivedDate.after(cert.getNotAfter())
lazy val tooEarly = claimReceivedDate.before(cert.getNotBefore())
-
+ logger.debug("X509 Claim for certificate with DN="+cert.getSubjectDN)
lazy val claims: List[WebIDClaim] = getClaimedWebIds(cert) map { webid =>
new WebIDClaim(webid, cert.getPublicKey.asInstanceOf[RSAPublicKey])
}
- lazy val verified: List[WebID] = claims.flatMap(_.verify.toOption)
+ def verified: List[WebID] = claims.flatMap(_.verify.toOption)
//note could also implement Destroyable
//
--- a/src/main/scala/auth/X509view.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/auth/X509view.scala Wed Jan 11 13:30:56 2012 +0100
@@ -23,17 +23,14 @@
package org.w3.readwriteweb.auth
-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.scalate.Scalate
import java.text.DateFormat
import java.util.Date
+import unfiltered.response.{CacheControl, Expires, Ok, Html}
/**
* This plan just described the X509 WebID authentication information.
@@ -56,8 +53,8 @@
def intent : Cycle.Intent[Req,Res] = {
case req @ Path("/test/WebId") => req match {
- case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) )
- case _ => Ok ~> Html (new NoX509().apply(noX509))
+ case X509Claim(claim) => Ok ~> Html( new X509Filler(claim).apply(webidTst) ) ~> Expires("0") ~> CacheControl("no-cache")
+ case _ => Ok ~> Html (new NoX509().apply(noX509)) ~> Expires("0") ~> CacheControl("no-cache")
}
case req @ Path("/test/WebIdAuth2") => Ok ~> Scalate(req, "hello.ssp")
}
--- a/src/main/scala/netty/ReadWriteWebNetty.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala Wed Jan 11 13:30:56 2012 +0100
@@ -23,6 +23,7 @@
package org.w3.readwriteweb.netty
+
import _root_.auth.WebIDSrvc
import org.clapper.argot.ArgotUsageException
import scala.Console._
@@ -34,7 +35,7 @@
import unfiltered.response._
import unfiltered.netty._
import collection.immutable.List
-import java.lang.String
+import util.{NetworkLoggingSM, AllowAllSecurityManager}
/**
* ReadWrite Web for Netty server, allowing TLS renegotiation
@@ -72,6 +73,7 @@
case None => new KeyAuth_Https(httpPort.value.get)
}
+
// configures and launches a Netty server
service.plan(publicStatic).
plan( x509v ).
@@ -81,6 +83,7 @@
}
+
trait StaticFiles extends PartialFunction[String, ResponseFunction[Any]] {
/* override this if the local path is somehow different from the url path */
def toLocal(webpath: String): String = webpath
--- a/src/main/scala/sommer/ResourceReader.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/sommer/ResourceReader.scala Wed Jan 11 13:30:56 2012 +0100
@@ -26,7 +26,7 @@
import com.hp.hpl.jena.vocabulary.RDF
import com.hp.hpl.jena.sparql.vocabulary.FOAF
import java.lang.String
-import org.w3.readwriteweb.{Resource, WebCache}
+import org.w3.readwriteweb.{Resource, GraphCache}
import scalaz.Validation
import java.net.URL
import collection._
@@ -107,7 +107,7 @@
type Val[A] = Validation[scala.Throwable,A]
def findPeople(m: Resource): Validation[scala.Throwable,Set[Person]] = {
- for (gr<-m.get) yield {
+ for (gr<-m.get()) yield {
for (st <- gr.listStatements(null,RDF.`type`,FOAF.Person).asScala;
val subj = st.getSubject;
st2 <- gr.listStatements(subj, FOAF.name,null).asScala
@@ -137,13 +137,13 @@
}
def findDefinedPeople(m: Resource): Validation[scala.Throwable,Set[IdPerson]] = {
- for (gr<-m.get) yield {
+ for (gr<-m.get()) yield {
definedPeople(gr, m.name)
}.toSet
}
def findIdPeople(m: Resource): Val[Set[IdPerson]] = {
- for (gr<-m.get) yield {
+ for (gr<-m.get()) yield {
for (st <- gr.listStatements(null,RDF.`type`,FOAF.Person).asScala;
val subj = st.getSubject;
if (subj.isURIResource)
@@ -162,7 +162,7 @@
}
object Test {
- implicit def urlToResource(u: URL) = WebCache.resource(u)
+ implicit def urlToResource(u: URL) = GraphCache.resource(u)
import System._
val peopleRd = new ResourceReader[Set[Person]](Extractors.findPeople)
@@ -170,7 +170,7 @@
val idPeopleRd = new ResourceReader[Set[IdPerson]](Extractors.findIdPeople)
val definedPeopleFriends = definedPeopleRd.flatMap(people =>ResourceReader[Set[IdPerson]]{
resource: Resource =>
- resource.get.map(gr=>
+ resource.get().map(gr=>
for ( p <- people;
st <- gr.listStatements(p.id, FOAF.knows, null).asScala ;
val friend = st.getObject;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/util/LogSecurityManager.scala Wed Jan 11 13:30:56 2012 +0100
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2012 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.util
+
+import com.weiglewilczek.slf4s.Logging
+import java.lang.{Class, String}
+import java.net.InetAddress
+import java.io.FileDescriptor
+import java.security.Permission
+
+/**
+ * This class can be set as the SecurityManager on the command line with
+ * -Djava.security.manager=org.w3.readwriteweb.util.NetworkLoggingSM
+ * IT will show all attempts to make outbound network connections, which can be useful to track connections made in
+ * various libraries
+ */
+class NetworkLoggingSM extends AllowAllSecurityManager with Logging {
+
+ override def checkConnect(host: String, port: Int) {
+ logger.info("connecting to "+host+":"+port)
+ super.checkConnect(host,port)
+ }
+
+ override def checkConnect(host: String, port: Int, context: AnyRef) {
+ logger.info("connecting to "+host+":"+port)
+ super.checkConnect(host,port,context)
+ }
+
+}
+
+class AllowAllSecurityManager extends SecurityManager {
+ override def checkAccept(host: String, port: Int) {}
+
+
+ override def checkAwtEventQueueAccess() {}
+
+ override def checkConnect(host: String, port: Int) {}
+
+ override def checkConnect(host: String, port: Int, context: AnyRef) {}
+
+ override def checkCreateClassLoader() {}
+
+ override def checkDelete(file: String) {}
+
+ override def checkExec(cmd: String) {}
+
+ override def checkExit(status: Int) {}
+
+ override def checkLink(lib: String) {}
+
+ override def checkListen(port: Int) {}
+
+ override def checkMemberAccess(clazz: Class[_], which: Int) {}
+
+ override def checkMulticast(maddr: InetAddress) {}
+
+ override def checkMulticast(maddr: InetAddress, ttl: Byte) {}
+
+ override def checkPackageAccess(pkg: String) {}
+
+ override def checkPackageDefinition(pkg: String) {}
+
+ override def checkPermission(perm: Permission) {}
+
+ override def checkPermission(perm: Permission, context: AnyRef) {}
+
+ override def checkPrintJobAccess() {}
+
+ override def checkPropertiesAccess() {}
+
+ override def checkPropertyAccess(key: String) {}
+
+ override def checkRead(fd: FileDescriptor) {}
+
+ override def checkRead(file: String) {}
+
+ override def checkRead(file: String, context: AnyRef) {}
+
+ override def checkSecurityAccess(target: String) {}
+
+ override def checkSetFactory() {}
+
+ override def checkSystemClipboardAccess() {}
+
+ override def checkTopLevelWindow(window: AnyRef) = false
+
+ override def checkWrite(fd: FileDescriptor) {}
+
+ override def checkWrite(file: String) {}
+}
+
+trait WrappedSecurityManager extends SecurityManager {
+ val wrap: SecurityManager
+ override def getSecurityContext = wrap.getSecurityContext
+
+ override def checkPermission(perm: Permission) { wrap.checkPermission(perm)}
+
+ override def checkPermission(perm: Permission, context: AnyRef) {wrap.checkPermission(perm,context)}
+
+ override def checkCreateClassLoader() { wrap.checkCreateClassLoader() }
+
+ override def checkAccess(t: Thread) { wrap.checkAccess(t)}
+
+ override def checkAccess(g: ThreadGroup) { wrap.checkAccess(g)}
+
+ override def checkExit(status: Int) { wrap.checkExit(status) }
+
+ override def checkExec(cmd: String) { wrap.checkExec(cmd) }
+
+ override def checkLink(lib: String) { wrap.checkLink(lib)}
+
+ override def checkRead(fd: FileDescriptor) {wrap.checkRead(fd)}
+
+ override def checkRead(file: String) { wrap.checkRead(file)}
+
+ override def checkRead(file: String, context: AnyRef) { wrap.checkRead(file)}
+
+ override def checkWrite(fd: FileDescriptor) {wrap.checkWrite(fd)}
+
+ override def checkWrite(file: String) {wrap.checkWrite(file)}
+
+ override def checkDelete(file: String) {wrap.checkDelete(file)}
+
+ override def checkListen(port: Int) {wrap.checkListen(port)}
+
+ override def checkAccept(host: String, port: Int) {wrap.checkAccept(host,port)}
+
+ override def checkMulticast(maddr: InetAddress) { wrap.checkMulticast(maddr)}
+
+ override def checkPropertiesAccess() { wrap.checkPropertiesAccess()}
+
+ override def checkPropertyAccess(key: String) {wrap.checkPropertyAccess(key)}
+
+ override def checkTopLevelWindow(window: AnyRef) = wrap.checkTopLevelWindow(window)
+
+ override def checkPrintJobAccess() { wrap.checkPrintJobAccess()}
+
+ override def checkSystemClipboardAccess() { wrap.checkSystemClipboardAccess()}
+
+ override def checkAwtEventQueueAccess() { wrap.checkAwtEventQueueAccess()}
+
+ override def checkPackageAccess(pkg: String) { wrap.checkPackageAccess(pkg)}
+
+ override def checkPackageDefinition(pkg: String) {wrap.checkPackageDefinition(pkg)}
+
+ override def checkSetFactory() {wrap.checkSetFactory()}
+
+ override def checkMemberAccess(clazz: Class[_], which: Int) {wrap.checkMemberAccess(clazz,which)}
+
+ override def checkSecurityAccess(target: String) {wrap.checkSecurityAccess(target)}
+
+ override def getThreadGroup = wrap.getThreadGroup
+}
+
+
+
+
+
+
--- a/src/main/scala/util/SpyInputStream.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/util/SpyInputStream.scala Wed Jan 11 13:30:56 2012 +0100
@@ -34,7 +34,7 @@
class SpyInputStream(val in: InputStream, val out: OutputStream) extends InputStream {
var stopOut = false
-
+
def read() ={
val i = try {
--- a/src/main/scala/util/package.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/main/scala/util/package.scala Wed Jan 11 13:30:56 2012 +0100
@@ -5,9 +5,9 @@
import scalaz._
import Scalaz._
import java.net.URL
-import com.hp.hpl.jena.shared.WrappedIOException
+import com.weiglewilczek.slf4s.Logging
-package object util {
+package object util extends Logging {
def modelFromInputStream(
is: InputStream,
@@ -18,7 +18,10 @@
m.getReader(lang.jenaLang).read(m, is, base.toString)
m.success
} catch {
- case t => t.fail
+ case t => {
+ logger.info("cought exception turning stream into model ",t)
+ t.fail
+ }
}
def modelFromString(
--- a/src/test/scala/auth/CreateWebIDSpec.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/test/scala/auth/CreateWebIDSpec.scala Wed Jan 11 13:30:56 2012 +0100
@@ -39,7 +39,7 @@
/**
* A key manager that can contain multiple keys, but where the client can take one of a number of identities
- * One at a time - so this is not sychronised. It also assumes that the server will accept all CAs, which in
+ * One at a time - so this is not synchronised. It also assumes that the server will accept all CAs, which in
* these test cases it does.
*/
class FlexiKeyManager extends X509ExtendedKeyManager {
--- a/src/test/scala/auth/secure_specs.scala Wed Jan 11 13:30:13 2012 +0100
+++ b/src/test/scala/auth/secure_specs.scala Wed Jan 11 13:30:56 2012 +0100
@@ -93,7 +93,7 @@
- val webCache = WebCache
+ val webCache = GraphCache
val serverSslContext = javax.net.ssl.SSLContext.getInstance("TLS");