You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2013/06/26 01:56:42 UTC
svn commit: r1496676 - in /jena/trunk/jena-fuseki: ./
src/main/java/org/apache/jena/fuseki/
src/main/java/org/apache/jena/fuseki/server/
src/test/java/org/apache/jena/fuseki/
Author: rvesse
Date: Tue Jun 25 23:56:41 2013
New Revision: 1496676
URL: http://svn.apache.org/r1496676
Log:
Add support for a --basic-auth argument in Fuseki that can be used to have basic auth configured using a Jetty realm file in lieu of a full blown Jetty config. It configures a single role fuseki to which users must belong in order to access any services on the server. If --jetty-config is used the --basic-auth argument is ignored.
With this functionality in place added unit tests that verify JENA-475
Added:
jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TestAuth.java
Modified:
jena/trunk/jena-fuseki/pom.xml
jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java
jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/ServerTest.java
jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
Modified: jena/trunk/jena-fuseki/pom.xml
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/pom.xml?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/pom.xml (original)
+++ jena/trunk/jena-fuseki/pom.xml Tue Jun 25 23:56:41 2013
@@ -191,6 +191,18 @@
<artifactId>jetty-xml</artifactId>
<version>${ver.jetty}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-security</artifactId>
+ <version>${ver.jetty}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${ver.jetty}</version>
+ </dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
Modified: jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java (original)
+++ jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/FusekiCmd.java Tue Jun 25 23:56:41 2013
@@ -121,6 +121,7 @@ public class FusekiCmd extends CmdARQ
private static ArgDecl argJettyConfig = new ArgDecl(ArgDecl.HasValue, "jetty-config") ;
private static ArgDecl argGZip = new ArgDecl(ArgDecl.HasValue, "gzip") ;
private static ArgDecl argUber = new ArgDecl(ArgDecl.NoValue, "uber", "über") ; // Use the überservlet (experimental)
+ private static ArgDecl argBasicAuth = new ArgDecl(ArgDecl.HasValue, "basic-auth") ;
private static ArgDecl argGSP = new ArgDecl(ArgDecl.NoValue, "gsp") ; // GSP compliance mode
@@ -152,6 +153,7 @@ public class FusekiCmd extends CmdARQ
private String fusekiConfigFile = null ;
private boolean enableCompression = true ;
private String jettyConfigFile = null ;
+ private String authConfigFile = null ;
private String homeDir = null ;
private String pagesDir = null ;
@@ -177,6 +179,7 @@ public class FusekiCmd extends CmdARQ
add(argAllowUpdate, "--update", "Allow updates (via SPARQL Update and SPARQL HTTP Update)") ;
add(argFusekiConfig, "--config=", "Use a configuration file to determine the services") ;
add(argJettyConfig, "--jetty-config=", "Set up the server (not services) with a Jetty XML file") ;
+ add(argBasicAuth, "--basic-auth", "Configure basic auth using provided Jetty realm file, ignored if --jetty-config is used") ;
add(argMgtPort, "--mgtPort=port", "Enable the management commands on the given port") ;
add(argHome, "--home=DIR", "Root of Fuseki installation (overrides environment variable FUSEKI_HOME)") ;
add(argGZip, "--gzip=on|off", "Enable GZip compression (HTTP Accept-Encoding) if request header set") ;
@@ -344,7 +347,14 @@ public class FusekiCmd extends CmdARQ
{
jettyConfigFile = getValue(argJettyConfig) ;
if ( !FileOps.exists(jettyConfigFile) )
- throw new CmdException("No such file: : "+jettyConfigFile) ;
+ throw new CmdException("No such file: "+jettyConfigFile) ;
+ }
+
+ if ( contains(argBasicAuth) )
+ {
+ authConfigFile = getValue(argBasicAuth) ;
+ if ( !FileOps.exists(authConfigFile) )
+ throw new CmdException("No such file: " + authConfigFile) ;
}
if ( contains(argHome) )
@@ -432,6 +442,7 @@ public class FusekiCmd extends CmdARQ
serverConfig.pagesPort = port ;
serverConfig.enableCompression = enableCompression ;
serverConfig.jettyConfigFile = jettyConfigFile ;
+ serverConfig.authConfigFile = authConfigFile ;
serverConfig.verboseLogging = ( super.isVerbose() || super.isDebug() ) ;
SPARQLServer server = new SPARQLServer(serverConfig) ;
Modified: jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java (original)
+++ jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java Tue Jun 25 23:56:41 2013
@@ -39,6 +39,12 @@ import org.apache.jena.fuseki.validation
import org.apache.jena.fuseki.validation.UpdateValidator ;
import org.apache.jena.riot.WebContent ;
import org.eclipse.jetty.http.MimeTypes ;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.DefaultIdentityService;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.security.IdentityService;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.Connector ;
import org.eclipse.jetty.server.Server ;
import org.eclipse.jetty.server.nio.BlockingChannelConnector ;
@@ -46,6 +52,7 @@ import org.eclipse.jetty.servlet.Default
import org.eclipse.jetty.servlet.ServletContextHandler ;
import org.eclipse.jetty.servlet.ServletHolder ;
import org.eclipse.jetty.servlets.GzipFilter ;
+import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.xml.XmlConfiguration ;
import com.hp.hpl.jena.sparql.mgt.ARQMgt ;
@@ -135,6 +142,36 @@ public class SPARQLServer
context.setErrorHandler(new FusekiErrorHandler()) ;
// Increase form size.
context.getServletContext().getContextHandler().setMaxFormContentSize(10*1000*1000) ;
+
+ // Wire up authentication if appropriate
+ if (jettyConfig == null && serverConfig.authConfigFile != null) {
+ Constraint constraint = new Constraint();
+ constraint.setName(Constraint.__BASIC_AUTH);
+ constraint.setRoles(new String[] { "fuseki" });
+ constraint.setAuthenticate(true);
+
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setConstraint(constraint);
+ mapping.setPathSpec("/*");
+
+ IdentityService identService = new DefaultIdentityService();
+
+ ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
+ securityHandler.addConstraintMapping(mapping);
+ securityHandler.setIdentityService(identService);
+
+ HashLoginService loginService = new HashLoginService("Fuseki Authentication", serverConfig.authConfigFile);
+ loginService.setIdentityService(identService);
+
+ securityHandler.setLoginService(loginService);
+ securityHandler.setAuthenticator(new BasicAuthenticator());
+
+ context.setSecurityHandler(securityHandler);
+
+ serverLog.debug("Basic Auth Configuration = " + serverConfig.authConfigFile);
+ }
+
+ // Wire up context handler to server
server.setHandler(context);
// Constants. Add RDF types.
Modified: jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java (original)
+++ jena/trunk/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java Tue Jun 25 23:56:41 2013
@@ -44,6 +44,10 @@ public class ServerConfig
/** Enable additional logging */
public boolean verboseLogging = false ;
+ /**
+ * Authentication config file used to setup Jetty Basic auth, if a Jetty config file was set this is ignored since Jetty config allows much more complex auth methods to be implemented
+ */
+ public String authConfigFile ;
}
Modified: jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/ServerTest.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/ServerTest.java?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/ServerTest.java (original)
+++ jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/ServerTest.java Tue Jun 25 23:56:41 2013
@@ -86,7 +86,7 @@ public class ServerTest extends BaseServ
server = new SPARQLServer(conf) ;
server.start() ;
}
-
+
protected static void serverStart() {
Log.logLevel(Fuseki.serverLog.getName(), org.apache.log4j.Level.WARN, java.util.logging.Level.WARNING) ;
Log.logLevel(Fuseki.requestLog.getName(), org.apache.log4j.Level.WARN, java.util.logging.Level.WARNING) ;
Modified: jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java?rev=1496676&r1=1496675&r2=1496676&view=diff
==============================================================================
--- jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java (original)
+++ jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java Tue Jun 25 23:56:41 2013
@@ -36,6 +36,7 @@ import org.junit.runners.Suite ;
, TestDatasetAccessorHTTP.class
, TestUpdate.class
, TestQuery.class
+ , TestAuth.class
})
public class TS_Fuseki extends BaseServerTest
{
Added: jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TestAuth.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TestAuth.java?rev=1496676&view=auto
==============================================================================
--- jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TestAuth.java (added)
+++ jena/trunk/jena-fuseki/src/test/java/org/apache/jena/fuseki/TestAuth.java Tue Jun 25 23:56:41 2013
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import org.apache.jena.atlas.logging.Log;
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.fuseki.server.FusekiConfig;
+import org.apache.jena.fuseki.server.SPARQLServer;
+import org.apache.jena.fuseki.server.ServerConfig;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.hp.hpl.jena.query.QueryExecutionFactory;
+import com.hp.hpl.jena.sparql.core.DatasetGraph;
+import com.hp.hpl.jena.sparql.core.DatasetGraphFactory;
+import com.hp.hpl.jena.sparql.engine.http.QueryEngineHTTP;
+import com.hp.hpl.jena.sparql.engine.http.QueryExceptionHTTP;
+import com.hp.hpl.jena.sparql.modify.UpdateProcessRemoteBase;
+import com.hp.hpl.jena.update.UpdateExecutionFactory;
+import com.hp.hpl.jena.update.UpdateFactory;
+import com.hp.hpl.jena.update.UpdateRequest;
+
+/**
+ * Tests Fuseki operation with authentication enabled
+ * @author rvesse
+ *
+ */
+public class TestAuth extends BaseServerTest {
+
+ private static File realmFile;
+ private static SPARQLServer server;
+
+ @BeforeClass
+ public static void setup() throws IOException {
+ realmFile = File.createTempFile("realm", ".properties");
+
+ FileWriter writer = new FileWriter(realmFile);
+ writer.write("allowed: password, fuseki\n");
+ writer.write("forbidden: password, other");
+ writer.close();
+
+ Log.logLevel(Fuseki.serverLog.getName(), org.apache.log4j.Level.WARN, java.util.logging.Level.WARNING) ;
+ Log.logLevel(Fuseki.requestLog.getName(), org.apache.log4j.Level.WARN, java.util.logging.Level.WARNING) ;
+ Log.logLevel("org.eclipse.jetty", org.apache.log4j.Level.WARN, java.util.logging.Level.WARNING) ;
+
+ DatasetGraph dsg = DatasetGraphFactory.createMem() ;
+ // This must agree with BaseServerTest
+ ServerConfig conf = FusekiConfig.defaultConfiguration(datasetPath, dsg, true) ;
+ conf.port = BaseServerTest.port ;
+ conf.pagesPort = BaseServerTest.port ;
+ conf.authConfigFile = realmFile.getAbsolutePath() ;
+
+ server = new SPARQLServer(conf) ;
+ server.start() ;
+ }
+
+ @AfterClass
+ public static void teardown() {
+ server.stop();
+
+ realmFile.delete();
+ }
+
+ @Test(expected = QueryExceptionHTTP.class)
+ public void query_with_auth_01() {
+ QueryEngineHTTP qe = (QueryEngineHTTP)QueryExecutionFactory.sparqlService(serviceQuery, "ASK { }");
+ // No auth credentials should result in an error
+ qe.execAsk();
+ }
+
+ @Test(expected = QueryExceptionHTTP.class)
+ public void query_with_auth_02() {
+ QueryEngineHTTP qe = (QueryEngineHTTP)QueryExecutionFactory.sparqlService(serviceQuery, "ASK { }");
+ // Auth credentials for valid user with bad password
+ qe.setBasicAuthentication("allowed", "incorrect".toCharArray());
+ qe.execAsk();
+ }
+
+ @Test
+ public void query_with_auth_03() {
+ QueryEngineHTTP qe = (QueryEngineHTTP)QueryExecutionFactory.sparqlService(serviceQuery, "ASK { }");
+ // Auth credentials for valid user with correct password
+ qe.setBasicAuthentication("allowed", "password".toCharArray());
+ Assert.assertTrue(qe.execAsk());
+ }
+
+ @Test(expected = QueryExceptionHTTP.class)
+ public void query_with_auth_04() {
+ QueryEngineHTTP qe = (QueryEngineHTTP)QueryExecutionFactory.sparqlService(serviceQuery, "ASK { }");
+ // Auth credentials for valid user with correct password BUT not in correct role
+ qe.setBasicAuthentication("forbidden", "password".toCharArray());
+ qe.execAsk();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_01() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemote(updates, serviceUpdate);
+ // No auth credentials should result in an error
+ ue.execute();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_02() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemote(updates, serviceUpdate);
+ // Auth credentials for valid user with bad password
+ ue.setAuthentication("allowed", "incorrect".toCharArray());
+ ue.execute();
+ }
+
+ @Test
+ public void update_with_auth_03() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemote(updates, serviceUpdate);
+ // Auth credentials for valid user with correct password
+ ue.setAuthentication("allowed", "password".toCharArray());
+ ue.execute();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_04() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemote(updates, serviceUpdate);
+ // Auth credentials for valid user with correct password BUT not in correct role
+ ue.setAuthentication("forbidden", "password".toCharArray());
+ ue.execute();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_05() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemoteForm(updates, serviceUpdate);
+ // No auth credentials should result in an error
+ ue.execute();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_06() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemoteForm(updates, serviceUpdate);
+ // Auth credentials for valid user with bad password
+ ue.setAuthentication("allowed", "incorrect".toCharArray());
+ ue.execute();
+ }
+
+ @Test
+ public void update_with_auth_07() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemoteForm(updates, serviceUpdate);
+ // Auth credentials for valid user with correct password
+ ue.setAuthentication("allowed", "password".toCharArray());
+ ue.execute();
+ }
+
+ @Test(expected = HttpException.class)
+ public void update_with_auth_08() {
+ UpdateRequest updates = UpdateFactory.create("CREATE SILENT GRAPH <http://graph>");
+ UpdateProcessRemoteBase ue = (UpdateProcessRemoteBase)UpdateExecutionFactory.createRemoteForm(updates, serviceUpdate);
+ // Auth credentials for valid user with correct password BUT not in correct role
+ ue.setAuthentication("forbidden", "password".toCharArray());
+ ue.execute();
+ }
+}