是否可以将SecurityEvaluator分配给所有新创建的图? 我可以创建一个可以正常工作的安全模型,但是我想将安全性应用于所有新创建的图形。用户可以只通过INSERT查询创建一个新图,然后将SecurityEvaluator不适用于该图。
更新 对于TDB数据集,我编写了一个包装器,该包装器实现了DatasetGraph,并在允许的情况下代理了对包装好的DatasetGraph的请求。这是否足以确保整个数据集都受到保护(当然,假设SecurityEvaluator实现正确)?我的意思是是否还有其他方法可以绕过此SecuredDatasetGraph访问/修改数据集?
SecuredDatasetGraph的代码
package com.vdanyliuk.jena.security;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.permissions.SecurityEvaluator;
import org.apache.jena.query.ReadWrite;
import org.apache.jena.query.TxnType;
import org.apache.jena.shared.AccessDeniedException;
import org.apache.jena.shared.Lock;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.graph.GraphZero;
import org.apache.jena.sparql.util.Context;
public class SecuredDatasetGraph implements DatasetGraph {
public static final String DEFAULT_GRAPH_SECURITY_NAME = "TEST";
private DatasetGraph base;
private SecurityEvaluator securityEvaluator;
protected SecuredDatasetGraph(DatasetGraph base, SecurityEvaluator securityEvaluator) {
this.base = base;
this.securityEvaluator = securityEvaluator;
}
private boolean hasReadAccess(Node test) {
return securityEvaluator.evaluate(securityEvaluator.getPrincipal(), SecurityEvaluator.Action.Read, test);
}
private boolean hasCreateAccess(Node test) {
return securityEvaluator.evaluate(securityEvaluator.getPrincipal(), SecurityEvaluator.Action.Create, test);
}
private boolean hasDeleteAccess(Node test) {
return securityEvaluator.evaluate(securityEvaluator.getPrincipal(), SecurityEvaluator.Action.Delete, test);
}
@Override
public Graph getDefaultGraph() {
boolean isReadAllowed = hasReadAccess(NodeFactory.createURI(DEFAULT_GRAPH_SECURITY_NAME));
if (isReadAllowed) {
return base.getDefaultGraph();
} else {
return GraphZero.instance();
}
}
@Override
public Graph getGraph(Node graphNode) {
boolean isReadAllowed = hasReadAccess(graphNode);
if (isReadAllowed) {
return base.getGraph(graphNode);
} else {
return GraphZero.instance();
}
}
@Override
public Graph getUnionGraph() {
throw new UnsupportedOperationException("Union graph is not supported by secured dataset.");
}
@Override
public boolean containsGraph(Node graphNode) {
return hasReadAccess(graphNode);
}
@Override
public void setDefaultGraph(Graph g) {
base.setDefaultGraph(g);
}
@Override
public void addGraph(Node graphName, Graph graph) {
if (hasCreateAccess(graphName)) {
base.addGraph(graphName, graph);
} else {
throw new AccessDeniedException("User is not allowed to create graph " + graphName);
}
}
@Override
public void removeGraph(Node graphName) {
if (hasDeleteAccess(graphName)) {
base.removeGraph(graphName);
} else {
throw new AccessDeniedException("User is not allowed to delete graph " + graphName);
}
}
@Override
public Iterator<Node> listGraphNodes() {
List<Node> result = getBaseGraphNodes();
return getReadAllowedGraphNodes(result)
.iterator();
}
private List<Node> getReadAllowedGraphNodes(List<Node> result) {
return result.stream()
.filter(this::hasReadAccess)
.collect(toList());
}
private List<Node> getBaseGraphNodes() {
List<Node> result = new ArrayList<>();
base.listGraphNodes().forEachRemaining(result::add);
return result;
}
@Override
public void add(Quad quad) {
Node graph = quad.getGraph();
if (hasCreateAccess(graph)) {
base.add(quad);
} else {
throw new AccessDeniedException("User is not allowed to add triples to graph " + graph);
}
}
@Override
public void delete(Quad quad) {
Node graph = quad.getGraph();
if (hasDeleteAccess(graph)) {
base.delete(quad);
} else {
throw new AccessDeniedException("User is not allowed to delete triples from graph " + graph);
}
}
@Override
public void add(Node g, Node s, Node p, Node o) {
if (hasCreateAccess(g)) {
base.add(g, s, p, o);
} else {
throw new AccessDeniedException("User is not allowed to add triples to graph " + g);
}
}
@Override
public void delete(Node g, Node s, Node p, Node o) {
if (hasDeleteAccess(g)) {
base.delete(g, s, p, o);
} else {
throw new AccessDeniedException("User is not allowed to delete triples from graph " + g);
}
}
@Override
public void deleteAny(Node g, Node s, Node p, Node o) {
if (g == null || Quad.isDefaultGraph(g)) { //default graph case
if (hasDeleteAccess(NodeFactory.createURI(DEFAULT_GRAPH_SECURITY_NAME))) {
base.deleteAny(g, s, p, o);
return;
}
} else if (g.equals(Node.ANY)) {
if (getBaseGraphNodes().stream().allMatch(this::hasDeleteAccess)) {
base.deleteAny(g, s, p, o);
return;
}
} else {
if (hasDeleteAccess(g)) {
base.deleteAny(g, s, p, o);
return;
}
}
throw new AccessDeniedException("User is not allowed to delete triples from graph " + g);
}
@Override
public Iterator<Quad> find() {
return Iter.filter(base.find(), quad -> hasReadAccess(quad.getGraph()));
}
@Override
public Iterator<Quad> find(Quad quad) {
return find(quad.getGraph(), quad.getSubject(), quad.getPredicate(), quad.getObject());
}
@Override
public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
if (g == null || Quad.isDefaultGraph(g)) { //default graph case
if (hasReadAccess(NodeFactory.createURI(DEFAULT_GRAPH_SECURITY_NAME))) {
return base.find(g, s, p, o);
}
} else if (g.equals(Node.ANY)) {
return Iter.filter(base.find(g, s, p, o), q -> hasReadAccess(q.getGraph()));
} else {
if (hasReadAccess(g)) {
return base.find(g, s, p, o);
}
}
throw new AccessDeniedException("User is not allowed to delete triples from graph " + g);
}
@Override
public Iterator<Quad> findNG(Node g, Node s, Node p, Node o) {
if (g == null || Quad.isDefaultGraph(g)) { //default graph case
if (hasReadAccess(NodeFactory.createURI(DEFAULT_GRAPH_SECURITY_NAME))) {
return base.findNG(g, s, p, o);
}
} else if (g.equals(Node.ANY)) {
return Iter.filter(base.findNG(g, s, p, o), q -> hasReadAccess(q.getGraph()));
} else {
if (hasReadAccess(g)) {
return base.findNG(g, s, p, o);
}
}
throw new AccessDeniedException("User is not allowed to delete triples from graph " + g);
}
@Override
public boolean contains(Node g, Node s, Node p, Node o) {
Iterator<Quad> iter = find(g, s, p, o);
boolean b = iter.hasNext();
Iter.close(iter);
return b;
}
@Override
public boolean contains(Quad quad) {
return contains(quad.getGraph(), quad.getSubject(), quad.getPredicate(), quad.getObject());
}
@Override
public void clear() {
if (getBaseGraphNodes().stream().allMatch(this::hasDeleteAccess)) {
base.clear();
}
throw new AccessDeniedException("User is not allowed to clear dataset.");
}
@Override
public boolean isEmpty() {
return !contains(Node.ANY, Node.ANY, Node.ANY, Node.ANY);
}
@Override
public Lock getLock() {
return base.getLock();
}
@Override
public Context getContext() {
return base.getContext();
}
@Override
public long size() {
return -1;
}
@Override
public void close() {
base.close();
}
@Override
public boolean supportsTransactions() {
return base.supportsTransactions();
}
@Override
public void begin(TxnType type) {
base.begin();
}
@Override
public void begin(ReadWrite readWrite) {
base.begin(readWrite);
}
@Override
public boolean promote(Promote mode) {
return base.promote(mode);
}
@Override
public void commit() {
base.commit();
}
@Override
public void abort() {
base.abort();
}
@Override
public void end() {
base.end();
}
@Override
public ReadWrite transactionMode() {
return base.transactionMode();
}
@Override
public TxnType transactionType() {
return base.transactionType();
}
@Override
public boolean isInTransaction() {
return base.isInTransaction();
}
}
SecuredDatasetAssembler的代码
package com.vdanyliuk.jena.security;
import static org.apache.jena.permissions.AssemblerConstants.EVALUATOR_IMPL;
import static org.apache.jena.permissions.AssemblerConstants.URI;
import static org.apache.jena.sparql.util.graph.GraphUtils.exactlyOneProperty;
import static org.apache.jena.sparql.util.graph.GraphUtils.getStringValue;
import static org.apache.jena.tdb.assembler.VocabTDB.pLocation;
import static org.apache.jena.tdb.assembler.VocabTDB.pUnionDefaultGraph;
import org.apache.jena.assembler.Assembler;
import org.apache.jena.assembler.Mode;
import org.apache.jena.assembler.assemblers.AssemblerGroup;
import org.apache.jena.assembler.exceptions.AssemblerException;
import org.apache.jena.atlas.logging.Log;
import org.apache.jena.graph.Node;
import org.apache.jena.permissions.AssemblerConstants;
import org.apache.jena.permissions.SecurityEvaluator;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.assembler.AssemblerUtils;
import org.apache.jena.sparql.core.assembler.DatasetAssembler;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.util.MappingRegistry;
import org.apache.jena.sys.JenaSystem;
import org.apache.jena.tdb.TDB;
import org.apache.jena.tdb.TDBFactory;
import org.apache.jena.tdb.assembler.DatasetAssemblerTDB;
import org.apache.jena.tdb.base.file.Location;
public class SecuredDatasetAssembler extends DatasetAssembler {
private static boolean initialized;
static {
JenaSystem.init();
init();
}
private static synchronized void init() {
if (initialized)
return;
MappingRegistry.addPrefixMapping("sec", AssemblerConstants.URI);
registerWith(Assembler.general);
initialized = true;
}
/**
* Register this assembler in the assembler group.
*
* @param group The assembler group to register with.
*/
private static void registerWith(AssemblerGroup group) {
if (group == null)
group = Assembler.general;
group.implementWith(ResourceFactory.createProperty(URI + "SecuredDataset"), new SecuredDatasetAssembler());
}
@Override
public Dataset createDataset(Assembler a, Resource root, Mode mode) {
return make(a, root);
}
private static Dataset make(Assembler a, Resource root) {
if (!exactlyOneProperty(root, pLocation))
throw new AssemblerException(root, "No location given");
String dir = getStringValue(root, pLocation);
Location loc = Location.create(dir);
DatasetGraph dsg = TDBFactory.createDatasetGraph(loc);
Resource evaluatorImpl = getUniqueResource(root, EVALUATOR_IMPL);
if (evaluatorImpl == null) {
throw new AssemblerException(root,
String.format("Property %s must be provided for %s", EVALUATOR_IMPL, root));
}
SecurityEvaluator securityEvaluator = getEvaluatorImpl(a, evaluatorImpl);
dsg = new SecuredDatasetGraph(dsg, securityEvaluator);
if (root.hasProperty(pUnionDefaultGraph)) {
Node b = root.getProperty(pUnionDefaultGraph).getObject().asNode();
NodeValue nv = NodeValue.makeNode(b);
if (nv.isBoolean())
dsg.getContext().set(TDB.symUnionDefaultGraph, nv.getBoolean());
else
Log.warn(DatasetAssemblerTDB.class, "Failed to recognize value for union graph setting (ignored): " + b);
}
AssemblerUtils.setContext(root, dsg.getContext());
return DatasetFactory.wrap(dsg);
}
private static SecurityEvaluator getEvaluatorImpl(Assembler a, Resource evaluatorImpl) {
Object obj = a.open(a, evaluatorImpl, Mode.ANY);
if (obj instanceof SecurityEvaluator) {
return (SecurityEvaluator) obj;
}
throw new AssemblerException(evaluatorImpl, String.format(
"%s does not specify a SecurityEvaluator instance", evaluatorImpl));
}
}
最后一个数据大部分是从DatasetAssemblerTDB复制的,并且仅使用上述实现包装创建的数据集。