You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/11/17 17:20:37 UTC
[11/34] jena git commit: JENA-1594: Add authentication support
(digest or basic).
JENA-1594: Add authentication support (digest or basic).
Add support for FROM handling with graph-access-controlled datasets.
Code cleaning.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/c6e9d367
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/c6e9d367
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/c6e9d367
Branch: refs/heads/master
Commit: c6e9d3678a48a9bc4e6243c53f987e67df33e442
Parents: 2303bfa
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Nov 1 19:55:42 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Tue Nov 13 15:39:14 2018 +0000
----------------------------------------------------------------------
.../fuseki/access/AssemblerAccessDataset.java | 8 +-
.../apache/jena/fuseki/access/AuthSetup.java | 36 ++++
.../jena/fuseki/access/DataAccessCtl.java | 51 ++++--
.../access/DatasetGraphAccessControl.java | 6 +-
.../access/Filtered_SPARQL_QueryDataset.java | 69 +++++++-
.../fuseki/access/Filtered_SPARQL_Update.java | 71 ++++++++
.../org/apache/jena/fuseki/access/LibSec.java | 177 +++++++++++++++++++
.../jena/fuseki/access/SecurityContext.java | 23 ++-
.../fuseki/access/TS_SecurityFiltering.java | 1 +
.../jena/fuseki/access/TestPasswordAccess.java | 145 +++++++++++++++
.../fuseki/access/TestSecurityFilterFuseki.java | 5 +-
.../org/apache/jena/fuseki/jetty/AuthMode.java | 21 +++
.../org/apache/jena/fuseki/jetty/JettyLib.java | 86 ++++++---
.../jena/fuseki/servlets/SPARQL_Query.java | 20 +--
.../fuseki/servlets/SPARQL_QueryDataset.java | 7 +-
.../fuseki/servlets/SPARQL_QueryGeneral.java | 15 +-
.../jena/fuseki/servlets/SPARQL_Update.java | 2 +-
.../apache/jena/fuseki/main/FusekiServer.java | 2 +-
18 files changed, 652 insertions(+), 93 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
index 71a89db..b379e2a 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
@@ -53,10 +53,10 @@ public class AssemblerAccessDataset extends AssemblerBase {
DatasetGraph dsg = new DatasetGraphAccessControl(ds.asDatasetGraph(), sr);
ds = DatasetFactory.wrap(dsg);
-// // Add marker
-// ds.getContext().set(DataAccessCtl.symControlledAccess, true);
-// // Add security registry
-// ds.getContext().set(DataAccessCtl.symSecurityRegistry, sr);
+ // Add marker
+ // ds.getContext().set(DataAccessCtl.symControlledAccess, true);
+ // Add security registry : if this dadaset is wrapped then this means the AuthorizationService is still accessible.
+ ds.getContext().set(DataAccessCtl.symAuthorizationService, sr);
return ds;
}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthSetup.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthSetup.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthSetup.java
new file mode 100644
index 0000000..e94ebff
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthSetup.java
@@ -0,0 +1,36 @@
+/*
+ * 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.access;
+
+/** Struct for the authentication information */
+public class AuthSetup {
+ public final String host;
+ public final int port;
+ public final String user;
+ public final String password;
+ public final String realm;
+
+ public AuthSetup(String host, int port, String user, String password, String realm) {
+ this.host = host;
+ this.port = port;
+ this.user = user;
+ this.password = password;
+ this.realm = realm;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
index 625f282..2da63fe 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -42,16 +42,17 @@ import org.eclipse.jetty.security.SecurityHandler;
public class DataAccessCtl {
static { JenaSystem.init(); }
- /**
- * Flag for whether this is data access controlled or not - boolean false or undef for "not
- * controlled". This is an alternative to {@link DatasetGraphAccessControl}.
- */
- public static final Symbol symControlledAccess = Symbol.create(VocabSecurity.getURI() + "controlled");
+// /**
+// * Flag for whether this is data access controlled or not - boolean false or undef for "not
+// * controlled". This is an alternative to {@link DatasetGraphAccessControl}.
+// * @see #isAccessControlled(DatasetGraph)
+// */
+// public static final Symbol symControlledAccess = Symbol.create(VocabSecurity.getURI() + "controlled");
/**
- * Symbol for the {@link AuthorizationService}. Must be present if
- * {@link #symControlledAccess} indicates data access control.
+ * Symbol for the {@link AuthorizationService}.
* This is an alternative to {@link DatasetGraphAccessControl}.
+ * @see #isAccessControlled(DatasetGraph)
*/
public static final Symbol symAuthorizationService = Symbol.create(VocabSecurity.getURI() + "authService");
@@ -69,7 +70,7 @@ public class DataAccessCtl {
* {@link DatasetGraph}'s {@link Context}.
*/
private static void addAuthorizatonService(DatasetGraph dsg, AuthorizationService authService) {
- dsg.getContext().set(symControlledAccess, true);
+ //dsg.getContext().set(symControlledAccess, true);
dsg.getContext().set(symAuthorizationService, authService);
}
@@ -118,6 +119,11 @@ public class DataAccessCtl {
builder.registerOperation(Operation.Query, WebContent.contentTypeSPARQLQuery, new Filtered_SPARQL_QueryDataset(determineUser));
builder.registerOperation(Operation.GSP_R, new Filtered_SPARQL_GSP_R(determineUser));
builder.registerOperation(Operation.Quads_R, new Filtered_REST_Quads_R(determineUser));
+
+ // Block updates
+ builder.registerOperation(Operation.Update, WebContent.contentTypeSPARQLUpdate, new Filtered_SPARQL_Update(determineUser));
+ builder.registerOperation(Operation.GSP_RW, null);
+ builder.registerOperation(Operation.Quads_RW, null);
return builder;
}
@@ -127,17 +133,28 @@ public class DataAccessCtl {
* (It is better to create the server via {@link #DataAccessCtl.builder} first rather than modify afterwards.)
*/
public static void modifyForAccessCtl(FusekiServer server, Function<HttpAction, String> determineUser) {
- /*
- * Reconfigure standard Jena Fuseki, replacing the default implementation of "query"
- * with a filtering one. This for this server only.
+ /*
+ * Reconfigure standard Jena Fuseki, replacing the default implementations with
+ * filtering ones. This for this server only, not system-wide.
*/
// The mapping operation to handler is in the ServiceDispatchRegistry and is per
- // server (per servlet context). "registerOrReplace" would be a better name,
+ // server (per servlet context). "registerOrReplace" might be a better name,
ActionService queryServletAccessFilter = new Filtered_SPARQL_QueryDataset(determineUser);
ServletContext cxt = server.getServletContext();
- ServiceDispatchRegistry.get(cxt).register(Operation.Query, WebContent.contentTypeSPARQLQuery, queryServletAccessFilter);
- ServiceDispatchRegistry.get(cxt).register(Operation.GSP_R, null, new Filtered_SPARQL_GSP_R(determineUser));
- ServiceDispatchRegistry.get(cxt).register(Operation.Quads_R, null, new Filtered_REST_Quads_R(determineUser));
+ ServiceDispatchRegistry reg = ServiceDispatchRegistry.get(cxt);
+
+ if ( reg.isRegistered(Operation.Query) )
+ reg.register(Operation.Query, WebContent.contentTypeSPARQLQuery, queryServletAccessFilter);
+ if ( reg.isRegistered(Operation.GSP_R) )
+ reg.register(Operation.GSP_R, null, new Filtered_SPARQL_GSP_R(determineUser));
+ if ( reg.isRegistered(Operation.Quads_R) )
+ reg.register(Operation.Quads_R, null, new Filtered_REST_Quads_R(determineUser));
+
+ // Block updates
+ //reg.register(Operation.Update, WebContent.contentTypeSPARQLUpdate, new Filtered_SPARQL_Update(determineUser));
+ reg.unregister(Operation.Update);
+ reg.unregister(Operation.GSP_RW);
+ reg.unregister(Operation.Quads_RW);
}
/**
@@ -147,8 +164,8 @@ public class DataAccessCtl {
public static boolean isAccessControlled(DatasetGraph dsg) {
if ( dsg instanceof DatasetGraphAccessControl )
return true;
- if ( dsg.getContext().isDefined(DataAccessCtl.symControlledAccess) )
- return true;
+// if ( dsg.getContext().isDefined(DataAccessCtl.symControlledAccess) )
+// return true;
if ( dsg.getContext().isDefined(DataAccessCtl.symAuthorizationService) )
return true;
return false;
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
index a518f75..2ccd538 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
@@ -24,14 +24,14 @@ import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphWrapper;
/** DatasetGraph layer that carries an {@link AuthorizationService}. */
-class DatasetGraphAccessControl extends DatasetGraphWrapper {
+public class DatasetGraphAccessControl extends DatasetGraphWrapper {
private AuthorizationService registry = null;
- public DatasetGraphAccessControl(DatasetGraph dsg, AuthorizationService authService) {
+ /*package*/ DatasetGraphAccessControl(DatasetGraph dsg, AuthorizationService authService) {
super(Objects.requireNonNull(dsg));
this.registry = Objects.requireNonNull(authService);
- }
+ }
public AuthorizationService getAuthService() {
return registry;
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
index 06a5829..950c503 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
@@ -20,15 +20,21 @@ package org.apache.jena.fuseki.access;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.function.Function;
+import static java.util.stream.Collectors.toList;
import org.apache.jena.fuseki.servlets.ActionService;
import org.apache.jena.fuseki.servlets.HttpAction;
import org.apache.jena.fuseki.servlets.SPARQL_QueryDataset;
-import org.apache.jena.query.Dataset;
+import org.apache.jena.fuseki.servlets.ServletOps;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
+import org.apache.jena.sparql.core.DatasetDescription;
import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphZero;
+import org.apache.jena.sparql.core.DynamicDatasets;
+import org.apache.jena.sparql.core.DynamicDatasets.DynamicDatasetGraph;
/** A Query {@link ActionService} that inserts a security filter on each query. */
final
@@ -39,24 +45,73 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
this.requestUser = requestUser;
}
+ private static boolean ALLOW_FROM = true;
+
@Override
protected Collection<String> customParams() {
// The additional ?user.
return Collections.singletonList("user");
}
+ /** Decide the dataset - this modifies the query
+ * If the query has a dataset description.
+ */
+ @Override
+ protected DatasetGraph decideDataset(HttpAction action, Query query, String queryStringLog) {
+ DatasetGraph dsg = action.getActiveDSG() ;
+ DatasetDescription dsDesc = getDatasetDescription(action, query) ;
+ if ( dsDesc == null )
+ return dsg;
+ if ( ! ALLOW_FROM )
+ ServletOps.errorBadRequest("Use GRAPH. (FROM/FROM NAMED is not compatible with data access control.)");
+
+ DatasetDescription dsDesc0 = getDatasetDescription(action, query);
+ if ( dsDesc0 == null )
+ return dsg;
+ // Filter the DatasetDescription by the SecurityContext
+ SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
+ DatasetDescription dsDesc1 = DatasetDescription.create(
+ mask(dsDesc0.getDefaultGraphURIs(), sCxt),
+ mask(dsDesc0.getNamedGraphURIs(), sCxt));
+ // dsDesc1 != null.
+ if ( dsDesc1.isEmpty() )
+ return DatasetGraphZero.create();
+
+ dsg = DynamicDatasets.dynamicDataset(dsDesc1, dsg, false) ;
+ if ( query.hasDatasetDescription() ) {
+ query.getGraphURIs().clear() ;
+ query.getNamedGraphURIs().clear() ;
+ }
+ return dsg ;
+ }
+
+ // Pass only those graphURIs in the security context.
+ private List<String> mask(List<String> graphURIs, SecurityContext sCxt) {
+ Collection<String> names = sCxt.visibleGraphNames();
+ return graphURIs.stream()
+ .filter(gn->names.contains(gn))
+ .collect(toList()) ;
+ }
+
@Override
- protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
- // Server database, not the possibly dynamically built "dataset"
+ protected QueryExecution createQueryExecution(HttpAction action, Query query, DatasetGraph target) {
+ if ( target instanceof DynamicDatasetGraph ) {
+ // Protocol query/FROM should have been caught by decideDataset
+ // but specialised setups might have DynamicDatasetGraph as the base dataset.
+ if ( ! ALLOW_FROM )
+ ServletOps.errorBadRequest("FROM/FROM NAMED is not compatible with data access control.");
+ }
+
+ // Database defined for this service, not the possibly dynamically built "dataset"
DatasetGraph dsg = action.getDataset();
if ( dsg == null )
- return super.createQueryExecution(action, query, dataset);
+ return super.createQueryExecution(action, query, target);
if ( ! DataAccessCtl.isAccessControlled(dsg) )
- return super.createQueryExecution(action, query, dataset);
+ return super.createQueryExecution(action, query, target);
- SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dataset.asDatasetGraph(), requestUser);
+ SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
// A QueryExecution for controlled access
- QueryExecution qExec = sCxt.createQueryExecution(query, dsg);
+ QueryExecution qExec = sCxt.createQueryExecution(query, target);
return qExec;
}
}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_Update.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_Update.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_Update.java
new file mode 100644
index 0000000..9ca0294
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_Update.java
@@ -0,0 +1,71 @@
+/*
+ * 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.access;
+
+import java.io.InputStream;
+import java.util.function.Function;
+
+import org.apache.jena.fuseki.servlets.ActionService;
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.SPARQL_Update;
+import org.apache.jena.fuseki.servlets.ServletOps;
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/** An Update {@link ActionService} that denies SPAQR Update in access controlled datasets. */
+final
+public class Filtered_SPARQL_Update extends SPARQL_Update {
+ private final Function<HttpAction, String> requestUser;
+
+ public Filtered_SPARQL_Update(Function<HttpAction, String> requestUser) {
+ this.requestUser = requestUser;
+ }
+
+ @Override
+ protected void validate(HttpAction action) {
+ super.validate(action);
+
+ DatasetGraph dsg = action.getActiveDSG() ;
+ if ( ! DataAccessCtl.isAccessControlled(dsg) )
+ return;
+
+ ServletOps.errorBadRequest("SPARQL Update no supported");
+ }
+
+ @Override
+ protected void perform(HttpAction action) {
+ DatasetGraph dsg = action.getActiveDSG() ;
+ if ( ! DataAccessCtl.isAccessControlled(dsg) ) {
+ super.perform(action);
+ return;
+ }
+
+ ServletOps.errorBadRequest("SPARQL Update no supported");
+ }
+
+ @Override
+ protected void execute(HttpAction action, InputStream input) {
+ DatasetGraph dsg = action.getActiveDSG() ;
+ if ( ! DataAccessCtl.isAccessControlled(dsg) ) {
+ super.execute(action, input);
+ return;
+ }
+ ServletOps.errorBadRequest("SPARQL Update no supported");
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/LibSec.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/LibSec.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/LibSec.java
new file mode 100644
index 0000000..2d34b11
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/LibSec.java
@@ -0,0 +1,177 @@
+/*
+ * 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.access;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.DigestScheme;
+import org.apache.http.impl.auth.RFC2617Scheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.protocol.HttpContext;
+import org.apache.jena.atlas.lib.InternalErrorException;
+import org.apache.jena.fuseki.jetty.AuthMode;
+import org.apache.jena.fuseki.jetty.JettyLib;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.rdfconnection.RDFConnection;
+import org.apache.jena.rdfconnection.RDFConnectionRemote;
+import org.apache.jena.riot.web.HttpOp;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.UserStore;
+
+/** Library for data access security */
+public class LibSec {
+ // See also DataAccessLib (package lib)
+
+ /** Create a {@link FusekiServer} - setup with one dataset. The returned server has not been started. */
+ public static FusekiServer fuseki(int port, String dsName, Dataset ds, AuthorizationService reg, UserStore userStore) {
+ Objects.requireNonNull(dsName);
+ Objects.requireNonNull(ds);
+ Objects.requireNonNull(reg);
+ Objects.requireNonNull(userStore);
+
+ Dataset dsx = DataAccessCtl.controlledDataset(ds, reg);
+ FusekiServer.Builder builder = DataAccessCtl.fusekiBuilder(DataAccessCtl.requestUserServlet)
+ .port(port)
+ .add(dsName, dsx, false);
+
+ if ( dsName.startsWith("/") )
+ dsName = dsName.substring(1);
+ ConstraintSecurityHandler sh = JettyLib.makeSecurityHandler("Dataset:"+dsName, userStore);
+ JettyLib.addPathConstraint(sh, "/"+dsName);
+ builder.securityHandler(sh);
+ return builder.build();
+ }
+
+ // HttpClientLibSec.
+
+ // [AuthScheme] default
+ public static AuthMode authMode = AuthMode.DIGEST;
+
+ public static void withAuth(String urlStr, AuthSetup auth, Consumer<RDFConnection> action) {
+ CredentialsProvider credsProvider = credsProvider(auth);
+ HttpHost target = new HttpHost(auth.host, auth.port, "http");
+ // --- AuthCache : not necessary
+ // Create AuthCache instance - necessary for non-repeatable request entity. (i.e. streaming)
+
+ // [AuthScheme]
+ AuthCache authCache = new BasicAuthCache();
+ if ( LibSec.authMode == AuthMode.BASIC ) {
+ RFC2617Scheme authScheme = authScheme(auth.realm);
+ // Can force the client to use basic first time by setting authCache.
+ // This does not work for digest because the nonce's will be wrong.
+ authCache.put(target, authScheme);
+ }
+
+ HttpContext httpContext = httpContext(authCache, credsProvider);
+ HttpClient httpClient = httpClient(auth);
+
+ // Needs retryable mods to RDFConnectionRemote??
+ try ( RDFConnection conn = RDFConnectionRemote.create()
+ .destination(urlStr)
+ .httpClient(httpClient)
+ .httpContext(httpContext)
+ .build() ) {
+ action.accept(conn);
+ }
+ }
+
+ /** Create digest auth {@link DigestScheme} */
+ private static RFC2617Scheme authScheme(String realm) {
+ switch (authMode) {
+ case BASIC: return authBasicScheme(realm);
+ case DIGEST : return authDigestScheme(realm);
+ default:
+ throw new InternalErrorException("RFC2617 auth scheme not reocgnized: "+authMode);
+ }
+ }
+
+ /** Create digest auth {@link DigestScheme} */
+ public static DigestScheme authDigestScheme(String realm) {
+ //Objects.requireNonNull(realm);
+ DigestScheme authScheme = new DigestScheme();
+ authScheme.overrideParamter("realm", realm);
+ authScheme.overrideParamter("nonce", "whatever");
+ return authScheme;
+ }
+
+ /** Create basic auth {@link BasicScheme} */
+ public static BasicScheme authBasicScheme(String realm) {
+ BasicScheme authScheme = new BasicScheme();
+ return authScheme;
+ }
+
+ public static HttpClient httpClient(AuthSetup auth) {
+ // HttpClient with password.
+ CredentialsProvider credsProvider = credsProvider(auth);
+ Credentials credentials = new UsernamePasswordCredentials(auth.user, auth.password);
+
+ String schemeAuthScope = authMode == AuthMode.BASIC ? "basic" : "digest";
+ AuthScope authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, schemeAuthScope);
+ //AuthScope authScope = AuthScope.ANY;
+
+ credsProvider.setCredentials(AuthScope.ANY, credentials);
+ HttpClient client = HttpOp.createPoolingHttpClientBuilder()
+ .setDefaultCredentialsProvider(credsProvider)
+ .build();
+ return client;
+ }
+
+ public static HttpClient httpClient(String host, int port, String user, String password, String realm) {
+ AuthSetup auth = new AuthSetup(host, port, user, password, realm);
+ return httpClient(auth);
+ }
+
+ public static HttpClientContext httpContext(AuthCache authCache, CredentialsProvider provider) {
+ // Add AuthCache to the execution context
+ HttpClientContext localContext = HttpClientContext.create();
+ return httpContext(localContext, authCache, provider);
+ }
+
+ public static HttpClientContext httpContext(HttpClientContext localContext, AuthCache authCache, CredentialsProvider provider) {
+ // Add AuthCache to the execution context
+ if ( authCache != null )
+ localContext.setAuthCache(authCache);
+ localContext.setCredentialsProvider(provider);
+ return localContext;
+ }
+
+ public static CredentialsProvider credsProvider(AuthSetup auth) {
+ return credsProvider(auth.host, auth.port, auth.user, auth.password);
+ }
+
+ private static CredentialsProvider credsProvider(String host, int port, String user, String password) {
+ CredentialsProvider credsProvider = new BasicCredentialsProvider();
+ credsProvider.setCredentials(
+ new AuthScope(host, port),
+ new UsernamePasswordCredentials(user, password));
+ return credsProvider;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
index 2ffaea0..4d55417 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
@@ -18,11 +18,12 @@
package org.apache.jena.fuseki.access;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
@@ -45,14 +46,14 @@ public class SecurityContext {
public static SecurityContext NONE = new SecurityContext();
public static SecurityContext DFT_GRAPH = new SecurityContext(true);
- private final Collection<Node> graphNames = ConcurrentHashMap.newKeySet();
+ private final Collection<Node> graphNames = new ArrayList<>();
private final boolean matchDefaultGraph;
- public SecurityContext() {
+ private SecurityContext() {
this(false);
}
- public SecurityContext(boolean matchDefaultGraph) {
+ private SecurityContext(boolean matchDefaultGraph) {
this.matchDefaultGraph = matchDefaultGraph;
}
@@ -76,6 +77,12 @@ public class SecurityContext {
public Collection<Node> visibleGraphs() {
return Collections.unmodifiableCollection(graphNames);
}
+ public Collection<String> visibleGraphNames() {
+ return graphNames.stream()
+ .filter(Node::isURI)
+ .map(Node::getURI)
+ .collect(Collectors.toList()) ;
+ }
/**
* Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
@@ -91,7 +98,10 @@ public class SecurityContext {
}
public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
- if ( ! ( dsg instanceof DatasetGraphAccessControl ) ) {
+ if ( ! DataAccessCtl.isAccessControlled(dsg) ) {
+// throw new InternalErrorException("SecurityContext.createQueryExecution called on an unsecured DatasetGraph");
+// // Internal error?
+ // Already setup or no security context.
return QueryExecutionFactory.create(query, dsg);
}
if ( isAccessControlledTDB(dsg) ) {
@@ -100,8 +110,9 @@ public class SecurityContext {
return qExec;
}
+ // XXX Does not work on GRAPH ?g {}
DatasetGraph dsgA = DataAccessCtl.filteredDataset(dsg, this);
- return QueryExecutionFactory.create(query, dsgA);
+ return QueryExecutionFactory.create(query, dsgA);
}
@Override
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
index 715054d..3face49 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
@@ -31,6 +31,7 @@ import org.junit.runners.Suite;
, TestSecurityAssemblerBuild.class
, TestAssemblerSeparate.class
, TestAssemblerShared.class
+ , TestPasswordAccess.class
})
public class TS_SecurityFiltering {
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestPasswordAccess.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestPasswordAccess.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestPasswordAccess.java
new file mode 100644
index 0000000..d5506f8
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestPasswordAccess.java
@@ -0,0 +1,145 @@
+/*
+ * 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.access;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.apache.http.client.HttpClient;
+import org.apache.jena.atlas.logging.LogCtl;
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.atlas.web.TypedInputStream;
+import org.apache.jena.atlas.web.WebLib;
+import org.apache.jena.fuseki.jetty.JettyLib;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.riot.web.HttpCaptureResponse;
+import org.apache.jena.riot.web.HttpOp;
+import org.apache.jena.riot.web.HttpOp.CaptureInput;
+import org.apache.jena.web.HttpSC;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.UserStore;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/** Tests for password access to a server. */
+public class TestPasswordAccess {
+
+ private static FusekiServer fusekiServer = null;
+ private static int port = WebLib.choosePort();
+ private static String serverURL = "http://localhost:"+port+"/";
+ private static AuthSetup authSetup = new AuthSetup("localhost", port, "user1", "pw1", "TripleStore");
+
+ @BeforeClass
+ public static void beforeClass() {
+ if ( false )
+ // To watch the HTTP headers
+ LogCtl.enable("org.apache.http.headers");
+
+ UserStore userStore = JettyLib.makeUserStore(authSetup.user, authSetup.password);
+ ConstraintSecurityHandler sh = JettyLib.makeSecurityHandler(authSetup.realm, userStore);
+
+ // Secure these areas.
+ JettyLib.addPathConstraint(sh, "/ds");
+ JettyLib.addPathConstraint(sh, "/nowhere");
+
+ fusekiServer =
+ FusekiServer.create()
+ .port(port)
+ .add("/ds", DatasetFactory.createTxnMem())
+ .add("/open", DatasetFactory.createTxnMem())
+ .securityHandler(sh)
+ //.staticFileBase(".")
+ .build();
+ fusekiServer.start();
+ }
+
+ @Before
+ public void before() {
+ // Reset before every test and after the suite.
+ HttpClient hc = HttpOp.createDefaultHttpClient();
+ HttpOp.setDefaultHttpClient(hc);
+ }
+
+
+ @AfterClass
+ public static void afterClass() {
+ fusekiServer.stop();
+ HttpClient hc = HttpOp.createDefaultHttpClient();
+ HttpOp.setDefaultHttpClient(hc);
+ }
+
+ @Test public void access_server() {
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL) ) {
+ assertNotNull(in);
+ } catch (HttpException ex) {
+ // 404 is OK - no static file area.
+ if ( ex.getResponseCode() != HttpSC.NOT_FOUND_404 )
+ throw ex;
+ }
+ }
+
+ @Test public void access_open() {
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL+"open") ) {
+ assertNotNull(in);
+ }
+ }
+
+ // Should fail.
+ @Test public void access_deny_ds() {
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL+"ds") ) {
+ assertNotNull(in);
+ } catch (HttpException ex) {
+ if ( ex.getResponseCode() != HttpSC.UNAUTHORIZED_401 )
+ throw ex;
+ }
+ }
+
+ // Should be 401, not be 404.
+ @Test public void access_deny_nowhere() {
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL+"nowhere") ) {
+ assertNotNull(in);
+ } catch (HttpException ex) {
+ if ( ex.getResponseCode() != HttpSC.UNAUTHORIZED_401 )
+ throw ex;
+ }
+ }
+
+ @Test public void access_allow_nowhere() {
+ HttpClient hc = LibSec.httpClient(authSetup);
+ HttpCaptureResponse<TypedInputStream> handler = new CaptureInput();
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL+"nowhere", null, hc, null) ) {
+ // null for 404.
+ assertNull(in);
+ } catch (HttpException ex) {
+ if ( ex.getResponseCode() != HttpSC.NOT_FOUND_404)
+ throw ex;
+ }
+ }
+
+ @Test public void access_allow_ds() {
+ HttpClient hc = LibSec.httpClient(authSetup);
+ HttpCaptureResponse<TypedInputStream> handler = new CaptureInput();
+ try( TypedInputStream in = HttpOp.execHttpGet(serverURL+"ds", null, hc, null) ) {
+ assertNotNull(in);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
index fb236ab..4ec6bc4 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -49,8 +49,8 @@ import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
import org.apache.jena.tdb.TDBFactory;
import org.apache.jena.tdb2.DatabaseMgr;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.PropertyUserStore;
-import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.security.UserStore;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
@@ -98,7 +98,8 @@ public class TestSecurityFilterFuseki {
testdsg3 = DataAccessCtl.controlledDataset(testdsg3, reg);
UserStore userStore = userStore();
- SecurityHandler sh = JettyLib.makeSecurityHandler("/*", "DatasetRealm", userStore);
+ ConstraintSecurityHandler sh = JettyLib.makeSecurityHandler("*", userStore);
+ JettyLib.addPathConstraint(sh, "/*");
fusekiServer = DataAccessCtl.fusekiBuilder(sh, DataAccessCtl.requestUserServlet)
.port(port)
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/AuthMode.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/AuthMode.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/AuthMode.java
new file mode 100644
index 0000000..2c6e30e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/AuthMode.java
@@ -0,0 +1,21 @@
+/*
+ * 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.jetty;
+
+public enum AuthMode { BASIC, DIGEST }
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
index efdbd2f..43d84d0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
@@ -24,6 +24,9 @@ import org.apache.jena.riot.WebContent;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.security.*;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.security.authentication.DigestAuthenticator;
+//import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+//import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
@@ -42,47 +45,82 @@ import org.eclipse.jetty.util.security.Password;
*/
public class JettyLib {
- /** Create a Jetty {@link SecurityHandler} for basic authentication. */
- public static SecurityHandler makeSecurityHandler(String pathSpec, String realm, UserStore userStore) {
- return makeSecurityHandler(pathSpec, realm, userStore, "**");
+ /** Create a Jetty {@link SecurityHandler} for a specific pathSpace, e.g {@code /database}. */
+ public static SecurityHandler makeSecurityHandlerForPathspec(String pathSpec, String realm, UserStore userStore) {
+ ConstraintSecurityHandler sh = makeSecurityHandler(realm, userStore);
+ addPathConstraint(sh, pathSpec);
+ return sh;
}
- /** Create a Jetty {@link SecurityHandler} for basic authentication. */
- public static SecurityHandler makeSecurityHandler(String pathSpec, String realm, UserStore userStore, String role) {
+// /** Create a Jetty {@link SecurityHandler} for basic authentication. */
+// @Deprecated
+// public static SecurityHandler makeSecurityHandlerForPathspec(String pathSpec, String realm, UserStore userStore, String role) {
+// ConstraintSecurityHandler securityHandler = makeSecurityHandler(realm, userStore, role);
+// // Pathspec based.
+// addDatasetConstraint(securityHandler, pathSpec);
+// return securityHandler;
+// }
+
+ /** Create a Jetty {@link SecurityHandler} for basic authentication.
+ * See {@linkplain #addPathConstraint(ConstraintSecurityHandler, String)}
+ * for adding the {@code pathspec} to apply it to.
+ */
+ public static ConstraintSecurityHandler makeSecurityHandler(String realm, UserStore userStore) {
+ return makeSecurityHandler(realm, userStore, "**");
+ }
+
+ /**
+ * Digest requires an extra round trip so it is unfriendly to API
+ * or scripts that stream.
+ */
+ // [AuthScheme] Default
+ public static AuthMode authMode = AuthMode.DIGEST;
+
+ /** Create a Jetty {@link SecurityHandler} for basic authentication.
+ * See {@linkplain #addPathConstraint(ConstraintSecurityHandler, String)}
+ * for adding the {@code pathspec} to apply it to.
+ */
+ public static ConstraintSecurityHandler makeSecurityHandler(String realm, UserStore userStore, String role) {
// role can be "**" for any authenticated user.
- Objects.requireNonNull(pathSpec);
Objects.requireNonNull(userStore);
Objects.requireNonNull(role);
- Constraint constraint = new Constraint();
- constraint.setName(Constraint.__BASIC_AUTH);
- String[] roles = new String[]{role};
- constraint.setRoles(roles);
- constraint.setAuthenticate(true);
-
- ConstraintMapping mapping = new ConstraintMapping();
- mapping.setConstraint(constraint);
- mapping.setPathSpec(pathSpec);
+ ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
IdentityService identService = new DefaultIdentityService();
- ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
- securityHandler.addConstraintMapping(mapping);
securityHandler.setIdentityService(identService);
-
+
// ---- HashLoginService
-
- HashLoginService loginService = new HashLoginService("Authentication");
+ HashLoginService loginService = new HashLoginService(realm);
loginService.setUserStore(userStore);
loginService.setIdentityService(identService);
- // ----
+ // [AuthScheme]
securityHandler.setLoginService(loginService);
- securityHandler.setAuthenticator(new BasicAuthenticator());
+ securityHandler.setAuthenticator( authMode == AuthMode.BASIC ? new BasicAuthenticator() : new DigestAuthenticator() );
if ( realm != null )
securityHandler.setRealmName(realm);
-
return securityHandler;
}
+
+ public static void addPathConstraint(ConstraintSecurityHandler securityHandler, String pathSpec) {
+ addPathConstraint(securityHandler, pathSpec, "**");
+ }
+
+ public static void addPathConstraint(ConstraintSecurityHandler securityHandler, String pathSpec, String role) {
+ Objects.requireNonNull(securityHandler);
+ Objects.requireNonNull(pathSpec);
+
+ ConstraintMapping mapping = new ConstraintMapping();
+ Constraint constraint = new Constraint();
+ String[] roles = new String[]{role};
+ constraint.setRoles(roles);
+ constraint.setName(securityHandler.getAuthenticator().getAuthMethod());
+ constraint.setAuthenticate(true);
+ mapping.setConstraint(constraint);
+ mapping.setPathSpec(pathSpec);
+ securityHandler.addConstraintMapping(mapping);
+ }
/**
* Make a {@link UserStore} from a password file.
@@ -91,7 +129,7 @@ public class JettyLib {
public static UserStore makeUserStore(String passwordFile) {
PropertyUserStore propertyUserStore = new PropertyUserStore();
propertyUserStore.setConfig(passwordFile);
- propertyUserStore.setHotReload(false);
+ propertyUserStore.setHotReload(true);
try { propertyUserStore.start(); }
catch (Exception ex) { throw new RuntimeException("UserStore", ex); }
return propertyUserStore;
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
index cb6424e..8692296 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
@@ -44,6 +44,7 @@ import org.apache.jena.query.* ;
import org.apache.jena.rdf.model.Model ;
import org.apache.jena.riot.web.HttpNames ;
import org.apache.jena.riot.web.HttpOp ;
+import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Prologue ;
import org.apache.jena.sparql.engine.EngineLib;
import org.apache.jena.sparql.resultset.SPARQLResult ;
@@ -274,7 +275,7 @@ public abstract class SPARQL_Query extends SPARQL_Protocol
// Assumes finished whole thing by end of sendResult.
try {
action.beginRead() ;
- Dataset dataset = decideDataset(action, query, queryStringLog) ;
+ DatasetGraph dataset = decideDataset(action, query, queryStringLog) ;
try ( QueryExecution qExec = createQueryExecution(action, query, dataset) ; ) {
SPARQLResult result = executeQuery(action, qExec, query, queryStringLog) ;
// Deals with exceptions itself.
@@ -301,24 +302,13 @@ public abstract class SPARQL_Query extends SPARQL_Protocol
protected abstract void validateQuery(HttpAction action, Query query) ;
/** Create the {@link QueryExecution} for this operation.
- * @param query
- * @param dataset
- * @return QueryExecution
- * @deprecated Use {@link #createQueryExecution(HttpAction, Query, Dataset)}
- */
- @Deprecated
- protected QueryExecution createQueryExecution(Query query, Dataset dataset) {
- return QueryExecutionFactory.create(query, dataset) ;
- }
-
- /** Create the {@link QueryExecution} for this operation.
* @param action
* @param query
* @param dataset
* @return QueryExecution
*/
- protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
- return createQueryExecution(query, dataset);
+ protected QueryExecution createQueryExecution(HttpAction action, Query query, DatasetGraph dataset) {
+ return QueryExecutionFactory.create(query, dataset) ;
}
/** Perform the {@link QueryExecution} once.
@@ -398,7 +388,7 @@ public abstract class SPARQL_Query extends SPARQL_Protocol
* @param queryStringLog
* @return {@link Dataset}
*/
- protected abstract Dataset decideDataset(HttpAction action, Query query, String queryStringLog) ;
+ protected abstract DatasetGraph decideDataset(HttpAction action, Query query, String queryStringLog) ;
/** Ship the results to the remote caller.
* @param action
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
index 325315a..668e563 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
@@ -18,8 +18,6 @@
package org.apache.jena.fuseki.servlets;
-import org.apache.jena.query.Dataset ;
-import org.apache.jena.query.DatasetFactory ;
import org.apache.jena.query.Query ;
import org.apache.jena.sparql.core.DatasetDescription ;
import org.apache.jena.sparql.core.DatasetGraph ;
@@ -44,7 +42,7 @@ public class SPARQL_QueryDataset extends SPARQL_Query
* If the query has a dataset description.
*/
@Override
- protected Dataset decideDataset(HttpAction action, Query query, String queryStringLog) {
+ protected DatasetGraph decideDataset(HttpAction action, Query query, String queryStringLog) {
DatasetGraph dsg = action.getActiveDSG() ;
DatasetDescription dsDesc = getDatasetDescription(action, query) ;
if ( dsDesc != null ) {
@@ -54,7 +52,6 @@ public class SPARQL_QueryDataset extends SPARQL_Query
query.getNamedGraphURIs().clear() ;
}
}
-
- return DatasetFactory.wrap(dsg) ;
+ return dsg ;
}
}
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
index d19b85c..c31174e 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
@@ -33,6 +33,7 @@ import org.apache.jena.rdf.model.Model ;
import org.apache.jena.rdf.model.ModelFactory ;
import org.apache.jena.riot.RiotException ;
import org.apache.jena.sparql.core.DatasetDescription ;
+import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphZero;
public class SPARQL_QueryGeneral extends SPARQL_Query {
@@ -64,22 +65,20 @@ public class SPARQL_QueryGeneral extends SPARQL_Query {
return Operation.Query;
}
- private static Dataset ds = DatasetFactory.wrap(new DatasetGraphZero());
@Override
- protected Dataset decideDataset(HttpAction action, Query query, String queryStringLog) {
+ protected DatasetGraph decideDataset(HttpAction action, Query query, String queryStringLog) {
DatasetDescription datasetDesc = getDatasetDescription(action, query) ;
if ( datasetDesc == null )
//ServletOps.errorBadRequest("No dataset description in protocol request or in the query string") ;
- return ds;
+ return new DatasetGraphZero();
return datasetFromDescriptionWeb(action, datasetDesc) ;
}
/**
- * Construct a Dataset based on a dataset description. Loads graph from the
- * web.
+ * Construct a Dataset based on a dataset description.
+ * Loads graph from the web.
*/
-
- protected Dataset datasetFromDescriptionWeb(HttpAction action, DatasetDescription datasetDesc) {
+ protected DatasetGraph datasetFromDescriptionWeb(HttpAction action, DatasetDescription datasetDesc) {
try {
if ( datasetDesc == null )
return null ;
@@ -140,7 +139,7 @@ public class SPARQL_QueryGeneral extends SPARQL_Query {
}
}
- return dataset ;
+ return dataset.asDatasetGraph() ;
}
catch (ActionErrorException ex) {
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
index cb327c8..8099249 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
@@ -205,7 +205,7 @@ public class SPARQL_Update extends SPARQL_Protocol
ServletOps.successPage(action,"Update succeeded") ;
}
- private void execute(HttpAction action, InputStream input) {
+ protected void execute(HttpAction action, InputStream input) {
// OPTIONS
if ( action.request.getMethod().equals(HttpNames.METHOD_OPTIONS) ) {
// Share with update via SPARQL_Protocol.
http://git-wip-us.apache.org/repos/asf/jena/blob/c6e9d367/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
index cd9b9e0..6bacd45 100644
--- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
@@ -272,7 +272,7 @@ public class FusekiServer {
return staticFileBase(directory);
}
- /** Set the location (filing system directory) to serve static file from. */
+ /** Set the location (filing system directory) to serve static files from. */
public Builder staticFileBase(String directory) {
requireNonNull(directory, "directory");
this.staticContentDir = directory;