better doc, cmd line info, & netty does http, ... (clean up)
--- a/README.markdown Fri May 11 17:27:57 2012 +0200
+++ b/README.markdown Tue May 22 23:04:54 2012 +0200
@@ -40,7 +40,7 @@
### to auto-compile the source
- > ~ compile
+ > compile
### to launch tests under sbt (will cache all the dependencies the first time, can take a while)
@@ -48,16 +48,94 @@
### to run the Web App
+Get the full command line options
+
+ > run --help
+
+You are then given a choise of using either the Jetty or the Netty Server. ( The Netty Server has better WebID support.)
+You will then get a listing of all the options.
+
+The following is known to work with https
+
+ > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
+
+Because of the .meta.n3 in the test_www directory you will not be able to GET the contents there
+
+
+The ReadWriteWeb app
+--------------------
+
+To get this:
+
+ hg clone https://dvcs.w3.org/hg/read-write-web
+ cd read-write-web
+ less README.markdown
+ ./sbt
+
+* see [http://mercurial.selenic.com/](http://mercurial.selenic.com/) for hg
+* see [https://github.com/harrah/xsbt/wiki](https://github.com/harrah/xsbt/wiki) for sbt
+
+This project depends on:
+
+* Java 6
+* that's all :-)
+
+It comes with
+
+* sbt project
+* generic sbt launcher
+* jar packager (assembly)
+* eclipse plugin for sbt
+* Web framework (Unfiltered)
+* embedded Web server (Jetty)
+* tests for web api (specs)
+* logger (slf4j)
+* the Jena/ARQ libraries
+
+How to start geeking
+--------------------
+
+BE PATIENT: the first time, some operations take some time because it downloads
+ all the dependencies...
+
+### to launch sbt
+
+ $ ./sbt
+
+### to auto-compile the source
+
+ > compile
+
+### to launch tests under sbt (will cache all the dependencies the first time, can take a while)
+
+ > test
+
+### to run the Web App and get the full command line options
+
> run
-or
+ Choose either the Netty or the Jetty servers to continue.
+ You will get a full list of options
- > run 8080
+ To start an insecure server start
+
+ > run --lang turtle --http 8080 test_www /2012/
+
+ You can now request resources using curl on the command line
+
+ $ curl -i -H "Accept: application/rdf+xml" http://localhost:8080/2012/foaf.n3
+
+ It is possible to PUT, RDF resources too and a couple of image formats, as
+ well as to create directories etc...
### to generate the eclipse configuration
> eclipse same-targets
+### to generate the IntelliJ configuration
+
+ > gen-idea
+
### to package the application as a stand-alone jar (creates target/read-write-web.jar)
> assembly
@@ -65,39 +143,42 @@
Using the stand-alone jar
-------------------------
- java -jar target/read-write-web.jar 8080 ~/WWW/2011/09 /2011/09 [options]
-
-Options:
+ $ java -jar target/read-write-web.jar
- * --relax All documents exist as empty RDF files (like a wiki).
- * --strict Documents must be created using PUT else they return 404
-
-To run with WebID see next section.
-
HTTPS with WebID
----------------
-### to run on https with WebID
+### to run on https with WebID ( http://webid.info/ )
- Wether you run the binary from the command line as described below or run it directly inside sbt you need to set the following parameters to java
- * -Djetty.ssl.keyStoreType=JKS - the keystore type (usually JKS)
- * -Djetty.ssl.keyStore=src/test/resources/KEYSTORE.jks - the path to the keystore
- * -Djetty.ssl.keyStorePassword=secret - the secret password for the keystore
- * -Dsun.security.ssl.allowUnsafeRenegotiation=true - to allow unsafe TLS renegotiation
- * -Dsun.security.ssl.allowLegacyHelloMessages=true - to allow legacy TLS hello Messages
+ > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
-The sun.security options are described in more detail http://download.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#workarounds
-So to run the compiled jar you can use
-
- > java -Djetty.ssl.keyStoreType=JKS -Djetty.ssl.keyStore=/Users/hjs/tmp/cert/KEYSTORE.jks -Djetty.ssl.keyStorePassword=secret -jar target/read-write-web.jar --https 8443 www_test /2011/09
-
-or to run sbt use the shorthand options in the bin/rwsbt.sh shell script eg
+ You can now request resources using curl over https using the command line
- > bin/rwsbt.sh -n -sslUnsafe -sslLegacy
+ $ curl -k -i -H "Accept: application/rdf+xml" https://localhost:8443/2012/foaf.n3
-(exercise: improve the script so that all options can be set with it)
-### to enable debug add the following parameters after 'java'
+ In the test_www directory there is a meta.n3 file. If you move it to .meta.n3
- -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+ $ cd test_www
+ $ mv meta.n3 .meta.n3
+ Then the directory will be access controlled. You will need a functioning WebID to authenticate.
+
+### to enable debug mode
+
+ start sbt with the
+
+ $ bin/rwsbt.sh -d
+
+### to generate the eclipse configuration
+
+ > eclipse same-targets
+
+### to generate the IntelliJ configuration
+
+ > gen-idea
+
+### to package the application as a stand-alone jar (creates target/read-write-web.jar)
+
+ > assembly
+
--- a/project/build.scala Fri May 11 17:27:57 2012 +0200
+++ b/project/build.scala Tue May 22 23:04:54 2012 +0200
@@ -29,7 +29,7 @@
val htmlparser = "nu.validator.htmlparser" % "htmlparser" % "1.2.1"
val grizzled = "org.clapper" %% "grizzled-scala" % "1.0.8" % "test"
val scalaz = "org.scalaz" %% "scalaz-core" % "6.0.4"
- val argot = "org.clapper" %% "argot" % "0.3.5"
+ val argot = "org.clapper" %% "argot" % "0.4"
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"
@@ -56,7 +56,7 @@
object BuildSettings {
val buildOrganization = "org.w3"
- val buildVersion = "0.1-SNAPSHOT"
+ val buildVersion = "0.2-SNAPSHOT"
val buildScalaVersion = "2.9.1"
val buildSettings = Defaults.defaultSettings ++ Seq (
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/ReadWriteWebArgs.scala Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2011 World Wide Web Consortium
+ * under the W3C licence defined at http://opensource.org/licenses/W3C
+ */
+
+package org.w3.readwriteweb
+
+import java.io.File
+import java.net.{MalformedURLException, URL}
+import org.clapper.argot.{ArgotConversionException, ArgotParser, ArgotConverters}
+import org.slf4j.{LoggerFactory, Logger}
+import org.w3.readwriteweb.auth.X509CertSigner
+
+
+/**
+ * @author bblfish
+ * @created 22/05/2012
+ */
+
+trait ReadWriteWebArgs {
+ val logger: Logger = LoggerFactory.getLogger(this.getClass)
+
+ // in Order to be receptive to DNS changes the DNS cache properties below must be set
+ // please tune them to see what works best
+ java.security.Security.setProperty("networkaddress.cache.ttl" , ""+60*10);
+ java.security.Security.setProperty("networkaddress.cache.negative.ttl",""+60*3) //3 minutes
+ import ArgotConverters._
+
+ val postUsageMsg= Some(
+ """
+ |NOTES
+ |
+ | - One of --http or --https must be selected
+ | - Trust stores are not needed because we use the WebID protocol, and client certs are nearly never signed by CAs
+ |
+ |SYSTEM PROPERTIES
+ |
+ | You can also set the following system properties with -Dproperty=value
+ |
+ | SECURITY:
+ | * ssl.keyStore: location of keystore (also --ks )
+ | * ssl.keyStorePassword: keystore password (so you don't need to type it on command line)
+ | * ssl.keyStoreType: keystore type (eg. JKS) where the server sertificate is located
+ |
+ |EXAMPLES
+ |
+ | from sbt shell in the source directory
+ | > run --lang turtle --keyStore src/test/resources/KEYSTORE.jks --ksPass secret --https 8443 test_www /2012/
+ |
+ """.stripMargin);
+
+ val parser = new ArgotParser("read-write-web",
+ outputWidth = 120,
+ preUsage = Some("Version 0.2"), postUsage = postUsageMsg,
+ sortUsage = false
+ )
+
+ val mode = parser.flag[RWWMode](List("wiki"), List("strict"),
+ "all resources already exist in --wiki mode (you can GET them) but not in --strict mode") {
+ (sValue, opt) =>
+ sValue match {
+ case true => AllResourcesAlreadyExist
+ case false => ResourcesDontExistByDefault
+ case _ => throw new ArgotConversionException("Option %s: must be either wiki or strict" format (opt.name, sValue))
+ }
+ }
+
+ val host = parser.option[String](List("host"),"domain","the domain of your machine (localhost otherwise) - not sure where this ends up revealing itself")
+
+ val rdfLanguage = parser.option[Lang](List("language","lang"), "l", "RDF language files are stored as: turtle or rdfxml") {
+ (sValue, opt) =>
+ sValue match {
+ case "n3" => N3
+ case "turtle" => TURTLE
+ case "rdfxml" => RDFXML
+ case _ => throw new ArgotConversionException("Option %s: must be either n3, turtle or rdfxml" format (opt.name, sValue))
+ }
+ }
+
+
+ /**
+ * set environmental variables, but adapt them for the underlying server
+ * (a silly hack because jetty and netty in unfiltered use different TLS security
+ * properties, and I don't want to dig through each library to set that right just now)
+ * @param name
+ * @param value
+ */
+ def setPropHack(name: String, value: String)
+
+ private val keyStoreOpt = parser.option[String](List("keyStore","ks"),"url",
+ "path to keystore (same as system property ssl.keyStore )")
+
+ //this should only be called after parsing
+ //todo: The ArgotParsers options cannot deal with default values, which is why we have to do this ugly hack
+ //it would require some mothod like orElse
+ lazy val keyStore = keyStoreOpt.value.orElse{
+ Option(System.getProperty("ssl.keyStore"))
+ }.map(p => new URL(new File(".").toURI.toURL,p))
+
+
+ private val keyStoreTypeOpt = parser.option[String](List("ksType"),"t","Type of KeyStore (JKS by default)") {
+ (sValue, opt) =>
+ sValue match {
+ case tp: String => tp
+ case _ => "JKS"
+ }
+ }
+
+ //this should only be called after parsing
+ lazy val keyStoreType = keyStoreTypeOpt.value.orElse{
+ Option(System.getProperty("ssl.keyStoreType"))
+ }
+
+ val keyStoreAlias = parser.option[String](List("ksAlias"),"name","alias for the key in the keystore")
+
+ private val keyStorePasswordOpt = parser.option[String](List("ksPass"),"pass",
+ "password for keystore (better set ssl.keyStorePassword System property)")
+
+ lazy val keyStorePassword = keyStorePasswordOpt.value.orElse{
+ Option(System.getProperty("ssl.keyStorePassword"))
+ }
+
+ val clientTLSsecurity = parser.option[Boolean](List("clientTLS"),"mode",
+ """client TLS connection security mode:
+ | * secure: if server certificate is not signed by well known CA don't accept
+ | * noCA: if the server certificate is not signed by well known CA ignore and continue
+ | * noDomain: for test situations where the server cert does not name its server correctly
+ | * [todo: add more flexible server certificate verification mechanisms]
+ """.stripMargin) {
+ (sValue, opt) =>
+ sValue match {
+ case "noCA" => {
+ //todo: work with system property as a hack for the moment, as passing around conexts is going to require
+ // a lot of rewriting
+ System.setProperty("rww.clientTLSsecurity","noCA")
+ false
+ }
+ case "noDomain" => {
+ System.setProperty("rww.clientTLSsecurity","noDomain")
+ false
+ }
+ case _ => {
+ System.setProperty("rww.clientTLSsecurity","secure")
+ true
+ }
+ }
+ }
+
+ parser.option[Unit](List("sslReneg"),"type",
+ """enable workaround for SSL regegotiation issue RFC5746 (see http://tinyurl.com/d9ydrnw )
+ | * sslUnsafe: allow unsafe renegotiation (will work with older browsers)
+ | * sslLegacy: allow legacy handshake without RFC 5746 messages
+ | (otherwise will use the default of the JVM used)
+ """.stripMargin) {
+ (sValue, opt) =>
+ sValue match {
+ case "sslUnsafe" => System.setProperty("sun.security.ssl.allowUnsafeRenegotiation","true")
+ case "sslLegacy" => System.setProperty("sun.security.ssl.allowLegacyHelloMessages","true")
+ case _ => ()
+ }
+ }
+
+ parser.flag[Unit](List("sslDebug"),"trace all SSL messages to output") {
+ (select, opt) => if (select) System.setProperty("javax.net.debug","all")
+ }
+
+ val proxy = parser.option[URL](List("proxy"),"p",
+ "Proxy to use when making client http(s) connections (sets http.proxy system property)") {
+ (sValue, opt) =>
+ try {
+ val proxy = new URL(sValue);
+ System.getProperties().put("http.proxy", proxy);
+ proxy
+ } catch {
+ case e: MalformedURLException => throw new ArgotConversionException("Option %s: url does not parse correctly "
+ format (opt.name, sValue))
+ }
+ }
+
+ val httpPort = parser.option[Int]("http", "port","start the http server on port")
+ val httpsPort = parser.option[Int]("https","port","start the https server on port")
+
+ val rootDirectory = parser.parameter[File]("rootDirectory", "path to root directory where files are served", false) {
+ (sValue, opt) => {
+ val file = new File(sValue)
+ if (! file.exists)
+ throw new ArgotConversionException("Option %s: %s must be a valid path" format (opt.name, sValue))
+ else
+ file
+ }
+ }
+
+ lazy val signer =
+ X509CertSigner( keyStore, keyStoreType,
+ keyStorePassword, keyStoreAlias.value.orElse(Some("selfsigned")) )
+
+ val baseURL = parser.parameter[String]("baseURL", "the base path in the URL from which resources will be served", false)
+
+ /** execute after parseing command line */
+ protected def postParse {
+ keyStore.map(u=>setPropHack("ssl.keyStore",u.getPath))
+ keyStorePassword.map(setPropHack("ssl.keyStorePassword",_))
+ keyStoreType.map(setPropHack("ssl.keyStoreType",_))
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/ReadWriteWebJetty.scala Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 World Wide Web Consortium
+ * under the W3C licence defined at http://opensource.org/licenses/W3C
+ */
+
+
+package org.w3.readwriteweb
+
+import auth.{X509CertSigner, RDFAuthZ, X509view}
+import org.w3.readwriteweb.util._
+
+import unfiltered.jetty._
+import Console.err
+import org.slf4j.{Logger, LoggerFactory}
+
+import org.clapper.argot._
+import ArgotConverters._
+import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
+import java.lang.String
+import java.io.File
+import java.net.{MalformedURLException, URL}
+
+
+object ReadWriteWebJetty extends ReadWriteWebArgs {
+
+ import unfiltered.filter.Planify
+
+ // regular Java main
+ def main(args: Array[String]) {
+
+ try {
+ parser.parse(args)
+ postParse
+ } 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 rww = new ReadWriteWeb[HttpServletRequest,HttpServletResponse] {
+ val rm = filesystem
+ def manif = manifest[HttpServletRequest]
+ override implicit val authz = new RDFAuthZ[HttpServletRequest,HttpServletResponse](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 HttpsTrustAll(port,host.value.getOrElse("0.0.0.0"))
+ case None => Http(httpPort.value.get,host.value.getOrElse("0.0.0.0"))
+ }
+
+ // configures and launches a Jetty server
+ service.filter(new FilterLogger(logger)).
+ context("/public"){ ctx:ContextBuilder =>
+ ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
+ }.filter(Planify(JettyEchoPlan.intent)).
+ filter(Planify(rww.intent)).
+ filter(Planify(x509v.intent)).
+ run()
+
+ }
+
+
+
+ object x509v extends X509view[HttpServletRequest,HttpServletResponse] {
+ def manif = manifest[HttpServletRequest]
+ }
+
+ /**
+ * set environmental variables, but adapt them for the underlying server
+ * (a silly hack because jetty and netty in unfiltered use different TLS security
+ * properties, and I don't want to dig through each library to set that right just now)
+ * @param name
+ * @param value
+ */
+ def setPropHack(name: String, value: String) {
+ System.setProperty("jetty."+name,value)
+ }
+}
+
+
--- a/src/main/scala/ReadWriteWebMain.scala Fri May 11 17:27:57 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-package org.w3.readwriteweb
-
-import auth.{X509CertSigner, RDFAuthZ, X509view}
-import org.w3.readwriteweb.util._
-
-import unfiltered.jetty._
-import Console.err
-import org.slf4j.{Logger, LoggerFactory}
-
-import org.clapper.argot._
-import ArgotConverters._
-import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
-import java.lang.String
-import java.io.File
-import java.net.{MalformedURLException, URL}
-
-trait ReadWriteWebArgs {
- val logger: Logger = LoggerFactory.getLogger(this.getClass)
-
- // in Order to be receptive to DNS changes the DNS cache properties below must be set
- // please tune them to see what works best
- java.security.Security.setProperty("networkaddress.cache.ttl" , ""+60*10);
- java.security.Security.setProperty("networkaddress.cache.negative.ttl",""+60*3) //3 minutes
-
-
- val postUsageMsg= Some("""
- |PROPERTIES
- |
- | * Keystore properties that need to be set if https is started
- | -Dnetty.ssl.keyStoreType=type : the type of the keystore, JKS by default usually
- | -Dnetty.ssl.keyStore=path : specify path to key store (for https server certificate)
- | -Dnetty.ssl.keyStorePassword=password : specify password for keystore store (optional)
- | (for jetty, replace "netty" with "jetty")
- |
- | * application arguments:
- | --http start server as plain http server
- | --https start server as in secured mode using https (TLS)
- | --language [turtle, rdfxml] save RDF in one of the given formats on disk
- | --clientTLS [secure, noCA, noDomain] client connections abide by CA verification
- | * secure : if server certificate is not signed by well known CA don't accept
- | * noCA: if the server certificate is not signed by well known CA ignore and continue
- | * noDomain: for test situations where the server certificate does not even name the machine it is on correctly
- | * [todo: add more flexible server certificate verification mechanisms]
- | --proxy url: url in the form http://host.example:port
- |
- |NOTES
- |
- | - Trust stores are not needed because we use the WebID protocol, and client certs are nearly never signed by CAs
- | - one of --http or --https must be selected
- |
- """.stripMargin);
-
- val parser = new ArgotParser("read-write-web",postUsage=postUsageMsg)
-
- val mode = parser.option[RWWMode](List("mode"), "m", "wiki mode: wiki or strict") {
- (sValue, opt) =>
- sValue match {
- case "wiki" => AllResourcesAlreadyExist
- case "strict" => ResourcesDontExistByDefault
- case _ => throw new ArgotConversionException("Option %s: must be either wiki or strict" format (opt.name, sValue))
- }
- }
-
- val rdfLanguage = parser.option[Lang](List("language"), "l", "RDF language") {
- (sValue, opt) =>
- sValue match {
- case "n3" => N3
- case "turtle" => TURTLE
- case "rdfxml" => RDFXML
- case _ => throw new ArgotConversionException("Option %s: must be either n3, turtle or rdfxml" format (opt.name, sValue))
- }
- }
-
- val clientTLSsecurity = parser.option[Boolean](List("clientTLS"),"c","client TLS connection security level") {
- (sValue, opt) =>
- sValue match {
- case "noCA" => {
- //todo: work with system property as a hack for the moment, as passing around conexts is going to require
- // a lot of rewriting
- System.setProperty("rww.clientTLSsecurity","noCA")
- false
- }
- case "noDomain" => {
- System.setProperty("rww.clientTLSsecurity","noDomain")
- false
- }
- case _ => {
- System.setProperty("rww.clientTLSsecurity","secure")
- true
- }
- }
- }
-
- val proxy = parser.option[URL](List("proxy"),"p","Proxy to use when making client http(s) connections") {
- (sValue, opt) =>
- try {
- val proxy = new URL(sValue);
- System.getProperties().put("http.proxy", proxy);
- proxy
- } catch {
- case e: MalformedURLException => throw new ArgotConversionException("Option %s: url does not parse correctly "
- format (opt.name, sValue))
- }
- }
-
- val httpPort = parser.option[Int]("http", "Port","start the http server on port")
- val httpsPort = parser.option[Int]("https","port","start the https server on port")
-
- val rootDirectory = parser.parameter[File]("rootDirectory", "root directory", false) {
- (sValue, opt) => {
- val file = new File(sValue)
- if (! file.exists)
- throw new ArgotConversionException("Option %s: %s must be a valid path" format (opt.name, sValue))
- else
- file
- }
- }
-
- val signer = {
- val keystore = new File(System.getProperty( "netty.ssl.keyStore")).toURI.toURL
- val ksTpe = System.getProperty("netty.ssl.keyStoreType","JKS")
- val ksPass = System.getProperty("netty.ssl.keyStorePassword")
- val alias = System.getProperty("netty.ssl.keyAlias","selfsigned")
- X509CertSigner( keystore, ksTpe, ksPass, alias )
- }
-
- val baseURL = parser.parameter[String]("baseURL", "base URL", false)
-
-}
-
-
-
-object ReadWriteWebMain extends ReadWriteWebArgs {
-
- import unfiltered.filter.Planify
-
- // 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 rww = new ReadWriteWeb[HttpServletRequest,HttpServletResponse] {
- val rm = filesystem
- def manif = manifest[HttpServletRequest]
- override implicit val authz = new RDFAuthZ[HttpServletRequest,HttpServletResponse](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 HttpsTrustAll(port,"0.0.0.0")
- case None => Http(httpPort.value.get)
- }
-
- // configures and launches a Jetty server
- service.filter(new FilterLogger(logger)).
- context("/public"){ ctx:ContextBuilder =>
- ctx.resources(ClasspathUtils.fromClasspath("public/").toURI.toURL)
- }.filter(Planify(JettyEchoPlan.intent)).
- filter(Planify(rww.intent)).
- filter(Planify(x509v.intent)).
- run()
-
- }
-
-
-
- object x509v extends X509view[HttpServletRequest,HttpServletResponse] {
- def manif = manifest[HttpServletRequest]
- }
-
-}
-
-
--- a/src/main/scala/auth/WebIDSrvc.scala Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/auth/WebIDSrvc.scala Tue May 22 23:04:54 2012 +0200
@@ -55,7 +55,7 @@
*/
trait WebIDSrvc[Req,Res] {
implicit def manif: Manifest[Req]
- val signer: X509CertSigner
+ val signer: Option[X509CertSigner]
import WebIDSrvc._
@@ -63,8 +63,10 @@
def sign(urlStr: String): URL = {
val timeStampedUrlStr = urlStr + "ts=" + URLEncoder.encode(dateFormat.format(Calendar.getInstance.getTime), "UTF-8")
- val signedUri = timeStampedUrlStr +
- "&sig=" + URLEncoder.encode(new String(Base64.encodeBase64URLSafeString(signer.sign(timeStampedUrlStr))), "UTF-8")
+ val signedUri = signer.map(sgnr =>
+ timeStampedUrlStr +
+ "&sig=" + URLEncoder.encode(new String(Base64.encodeBase64URLSafeString(sgnr.sign(timeStampedUrlStr))), "UTF-8")
+ ).getOrElse(timeStampedUrlStr)
return new URL(signedUri)
}
@@ -148,9 +150,9 @@
}
object aboutTransform extends Transformer {
- val key = signer.signingCert.getPublicKey.asInstanceOf[RSAPublicKey]
- $(".modulus").contents = key.getModulus.toString(16)
- $(".exponent").contents = key.getPublicExponent.toString
+ val key = signer.map(_.signingCert.getPublicKey.asInstanceOf[RSAPublicKey])
+ $(".modulus").contents = key.map(_.getModulus.toString(16)).getOrElse("no key, no modulus - no signature - contact admin!")
+ $(".exponent").contents = key.map(_.getPublicExponent.toString).getOrElse("no key, no exponent")
}
class ServiceTrans(relyingParty: URL, claim: XClaim) extends Transformer {
--- a/src/main/scala/auth/X509Cert.scala Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/auth/X509Cert.scala Tue May 22 23:04:54 2012 +0200
@@ -37,25 +37,47 @@
import org.w3.readwriteweb.util.trySome
import actors.threadpool.TimeUnit
import com.google.common.cache.{CacheLoader, CacheBuilder, Cache}
-
-object X509CertSigner {
+import scalaz.Validation
+import scalaz.Scalaz._
- def apply(
- keyStoreLoc: URL,
- keyStoreType: String,
- password: String,
- alias: String): X509CertSigner = {
- val keystore = KeyStore.getInstance(keyStoreType)
+import com.weiglewilczek.slf4s.Logging
- IO.use(keyStoreLoc.openStream()) { in =>
- keystore.load(in, password.toCharArray)
+object X509CertSigner extends Logging {
+
+ def apply( keyStoreLoc: Option[URL],
+ keyStoreType: Option[String],
+ password: Option[String],
+ alias: Option[String]): Option[X509CertSigner] = {
+ try {
+ for {
+ loc <- keyStoreLoc
+ tp <- keyStoreType
+ } yield {
+ val pass = password.map(_.toCharArray).getOrElse(null)
+ val alias2 = alias.getOrElse("") //todo there are better ways of finding an alias than this
+ val ks = KeyStore.getInstance(tp)
+ IO.use(loc.openStream()) {
+ in => ks.load(in, pass)
+ }
+ val privateKey = ks.getKey(alias2, pass).asInstanceOf[PrivateKey]
+ val certificate = ks.getCertificate(alias2).asInstanceOf[X509Certificate]
+ //one could verify that indeed this is the private key corresponding to the public key in the cert.
+ new X509CertSigner(certificate, privateKey)
+ }
+ } catch {
+ case e: Exception => {
+ logger.warn("could not load TLS certificate for certificate signing service", e)
+ None
+ }
}
- val privateKey = keystore.getKey(alias, password.toCharArray).asInstanceOf[PrivateKey]
- val certificate = keystore.getCertificate(alias).asInstanceOf[X509Certificate]
- //one could verify that indeed this is the private key corresponding to the public key in the cert.
+ }
- new X509CertSigner(certificate, privateKey)
- }
+ def apply( keyStoreLoc: URL,
+ keyStoreType: String,
+ password: String,
+ alias: String): X509CertSigner =
+ apply(Option(keyStoreLoc),Option(keyStoreType),Option(password),Option(alias)).get
+
}
class X509CertSigner(
--- a/src/main/scala/netty/ReadWriteWebNetty.scala Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/netty/ReadWriteWebNetty.scala Tue May 22 23:04:54 2012 +0200
@@ -35,6 +35,7 @@
import unfiltered.response._
import unfiltered.netty._
import collection.immutable.List
+import scala.None
/**
* ReadWrite Web for Netty server, allowing TLS renegotiation
@@ -50,6 +51,7 @@
try {
parser.parse(args)
+ postParse
} catch {
case e: ArgotUsageException => err.println(e.message); sys.exit(1)
}
@@ -68,19 +70,24 @@
val echo = new cycle.Plan with cycle.ThreadPool with ServerErrorResponse with NettyEchoPlan
- //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)
+ val domain = host.value.getOrElse("0.0.0.0")
+ //only one of the following two servers can be started at one time
+ //having two would require a lot more care with issues of overwriting
+ httpsPort.value.map {
+ port => new KeyAuth_Https(port).
+ plan(publicStatic).
+ plan( echo ).
+ plan( x509v ).
+ plan( webidp ).
+ plan( rww ).run()
}
-
- // configures and launches a Netty server
- service.plan(publicStatic).
- plan( echo ).
- plan( x509v ).
- plan( webidp ).
- plan( rww ).run()
+ httpPort.value.map {
+ port => Http(httpPort.value.get,domain).
+ plan(publicStatic).
+ plan( echo ).
+ plan( rww ).run()
+ }
}
@@ -137,6 +144,16 @@
val signer = ReadWriteWebNetty.signer
}
+ /**
+ * set environmental variables, but adapt them for the underlying server
+ * (a silly hack because jetty and netty in unfiltered use different TLS security
+ * properties, and I don't want to dig through each library to set that right just now)
+ * @param name
+ * @param value
+ */
+ def setPropHack(name: String, value: String) {
+ System.setProperty("netty."+name,value)
+ }
}
--- a/src/main/scala/netty/SslLoginTest.scala Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/netty/SslLoginTest.scala Tue May 22 23:04:54 2012 +0200
@@ -93,7 +93,7 @@
}
- def main(args: Array[String]) {
- new KeyAuth_Https(8443).plan(SslLoginTest).run()
- }
+// def main(args: Array[String]) {
+// new KeyAuth_Https(8443).plan(SslLoginTest).run()
+// }
}
\ No newline at end of file
--- a/src/main/scala/sommer/ResourceReader.scala Fri May 11 17:27:57 2012 +0200
+++ b/src/main/scala/sommer/ResourceReader.scala Tue May 22 23:04:54 2012 +0200
@@ -179,6 +179,7 @@
)
} )
+/*
def main(args: Array[String]) {
val url: String = "http://bblfish.net/people/henry/card"
@@ -219,4 +220,5 @@
// }
}
+*/
}
\ No newline at end of file
--- a/test_www/.meta.n3 Fri May 11 17:27:57 2012 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-@prefix acl: <http://www.w3.org/ns/auth/acl#> .
-@prefix foaf: <http://xmlns.com/foaf/0.1/> .
-@prefix : <#> .
-
-:a1 a acl:Authorization;
- acl:accessTo <foaf.n3>;
- acl:mode acl:Write;
- acl:agent <http://bblfish.net/people/henry/card#me> .
-
-
-:readAll a acl:Authorization;
- acl:accessTo <foaf.n3>;
- acl:mode acl:Read;
- acl:agentClass foaf:Agent .
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test_www/meta.n3 Tue May 22 23:04:54 2012 +0200
@@ -0,0 +1,14 @@
+@prefix acl: <http://www.w3.org/ns/auth/acl#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix : <#> .
+
+:a1 a acl:Authorization;
+ acl:accessTo <foaf.n3>;
+ acl:mode acl:Write;
+ acl:agent <http://bblfish.net/people/henry/card#me> .
+
+
+:readAll a acl:Authorization;
+ acl:accessTo <foaf.n3>;
+ acl:mode acl:Read;
+ acl:agentClass foaf:Agent .