Skip to content

Commit

Permalink
jena-fuseki-access - PoC - Dynamic Graph ACL via query pragma
Browse files Browse the repository at this point in the history
- Requires context propagation patch (see apache#1291)
- Comment specified before query can be used to override security
  context with a given set of graphs.
- Overridden security context is preserved in the execution context
- Simplified regex pattern only - has to be first non-whitespace line
  to work.
  • Loading branch information
vtermanis committed Jul 4, 2022
1 parent 91c3991 commit 22ef4a1
Showing 1 changed file with 54 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,39 @@

import static java.util.stream.Collectors.toList;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.fuseki.servlets.*;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.sparql.core.*;
import org.apache.jena.sparql.core.DynamicDatasets.DynamicDatasetGraph;
import org.apache.jena.sparql.util.Symbol;

/** A Query {@link ActionService} that inserts a security filter on each query. */
final
public class AccessCtl_SPARQL_QueryDataset extends SPARQL_QueryDataset {
// For storing override security context in execution context
private static final Symbol symSecContextOverride = Symbol.create(VocabSecurity.getURI() + "secCxtOverride");
private static final Pattern GRAPH_PRAGMA_DELIMITER_PATTERN = Pattern.compile("\\|");
private static final String GRAPH_PRAGMA_PATTERN_GROUP = "graphs";
private static final Pattern GRAPH_PRAGMA_PATTERN = Pattern.compile(
"^" +
// Leading whitespace
"\\s*" +
// For simplicity expect pragma on first non-whitespace line
"#pragma acl\\.graphs\\h+(?<" + GRAPH_PRAGMA_PATTERN_GROUP + ">\\S*)\\h*\\v" +
// Anything else
".*",
Pattern.DOTALL
);
private final Function<HttpAction, String> requestUser;

public AccessCtl_SPARQL_QueryDataset(Function<HttpAction, String> requestUser) {
Expand All @@ -58,8 +76,9 @@ protected Pair<DatasetGraph, Query> decideDataset(HttpAction action, Query query
if ( ! DataAccessCtl.isAccessControlled(dsg) )
return super.decideDataset(action, query, queryStringLog);


DatasetDescription dsDesc0 = SPARQLProtocol.getDatasetDescription(action, query);
SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
SecurityContext sCxt = getSecurityContext(action, dsg, requestUser);
DatasetGraph dsg2 = dynamicDataset(action, query, dsg, dsDesc0, sCxt);
return Pair.create(dsg2, query);
}
Expand Down Expand Up @@ -104,6 +123,39 @@ private List<String> mask(List<String> graphURIs, SecurityContext sCxt) {
.collect(toList());
}

/*
* Note:
* - This is called before both decideDataset() and createQueryExecution()
* - To ensure that no graphs are matched, could set SecurityContextAllowNone instead of relying on default when
* the pragma is not found.
*/
@Override
protected void execute(String queryString, HttpAction action) {
Matcher m = GRAPH_PRAGMA_PATTERN.matcher(queryString);
if (m.matches()) {
String[] graphs = GRAPH_PRAGMA_DELIMITER_PATTERN
.splitAsStream(m.group(GRAPH_PRAGMA_PATTERN_GROUP))
.map(String::trim)
.toArray(String[]::new);
// Attach custom security context to query context for later retrieval
action.getContext().set(symSecContextOverride, new SecurityContextView(graphs));
}
super.execute(queryString, action);
}

private SecurityContext getSecurityContext(HttpAction action, DatasetGraph dsg, Function<HttpAction, String> requestUser) {
action.log.warn("Checking override ctx");
Object obj = action.getContext().get(symSecContextOverride);
if (obj != null ) {
try {
return (SecurityContext) obj;
} catch (ClassCastException ex) {
action.log.warn("Unexpected context override cast failure", ex);
}
}
return DataAccessLib.getSecurityContext(action, dsg, requestUser);
}

@Override
protected QueryExecution createQueryExecution(HttpAction action, Query query, DatasetGraph target) {
if ( ! ALLOW_FROM ) {
Expand All @@ -120,10 +172,9 @@ protected QueryExecution createQueryExecution(HttpAction action, Query query, Da
if ( ! DataAccessCtl.isAccessControlled(dsg) )
return super.createQueryExecution(action, query, target);

SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
SecurityContext sCxt = getSecurityContext(action, dsg, requestUser);
// A QueryExecution for controlled access
QueryExecution qExec = sCxt.createQueryExecution(query, target);
return qExec;
}
}

0 comments on commit 22ef4a1

Please sign in to comment.