/*
* 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.permissions.graph;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.GraphEventManager;
import org.apache.jena.graph.GraphListener;
import org.apache.jena.graph.Triple;
import org.apache.jena.graph.impl.CollectionGraph;
import org.apache.jena.permissions.SecurityEvaluator;
import org.apache.jena.permissions.SecurityEvaluator.Action;
import org.apache.jena.permissions.impl.CachedSecurityEvaluator;
import org.apache.jena.permissions.utils.PermTripleFilter;
import org.apache.jena.shared.AuthenticationRequiredException;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.util.iterator.NiceIterator;
import org.apache.jena.util.iterator.WrappedIterator;
/**
* Since we sit between the graph and other items we have to determine when the
* message is first seen and send it to the underlying graph if necessary.
*/
public class SecuredGraphEventManager implements GraphEventManager {
private class SecuredGraphListener implements GraphListener {
private final GraphListener wrapped;
private final Object runAs;
SecuredGraphListener(final GraphListener wrapped) {
if (wrapped == null) {
throw new IllegalArgumentException(
"Wrapped listener may not be null");
}
this.wrapped = wrapped;
this.runAs = securedGraph.getSecurityEvaluator().getPrincipal();
}
private Triple[] getArray(final Graph g, final Triple[] triples,
final Set<Action> perms) throws AuthenticationRequiredException {
Triple[] retval = triples;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs, perms, sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs, perms, sg.getModelNode(),
Triple.ANY)) {
final List<Triple> list = wrapPermIterator(sg,
Arrays.asList(triples).iterator(), perms)
.toList();
retval = list.toArray(new Triple[list.size()]);
} else {
retval = triples;
}
} else {
retval = new Triple[0];
}
}
return retval;
}
@Override
public void notifyAddArray(final Graph g, final Triple[] triples)
throws AuthenticationRequiredException {
final Triple[] added = getArray(g, triples,
SecuredGraphEventManager.ADD);
if (added.length > 0) {
wrapped.notifyAddArray(g, added);
}
}
@Override
public void notifyAddGraph(final Graph g, final Graph added)
throws AuthenticationRequiredException {
Graph addGraph = added;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs, SecuredGraphEventManager.ADD,
sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.ADD, sg.getModelNode(),
Triple.ANY)) {
final List<Triple> lst = added.find(Triple.ANY)
.toList();
addGraph = new CollectionGraph(Arrays.asList(getArray(
g, lst.toArray(new Triple[lst.size()]),
SecuredGraphEventManager.ADD)));
} else {
addGraph = added;
}
} else {
addGraph = new CollectionGraph(
Collections.<Triple> emptyList());
}
}
if (addGraph.size() > 0) {
wrapped.notifyAddGraph(g, addGraph);
}
}
@Override
public void notifyAddIterator(final Graph g, final Iterator<Triple> it)
throws AuthenticationRequiredException {
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
// only report if we can write to the graph
if (evaluator.evaluateAny(runAs, SecuredGraphEventManager.ADD,
sg.getModelNode())) {
final ExtendedIterator<Triple> iter = wrapPermIterator(sg,
it, SecuredGraphEventManager.ADD);
try {
wrapped.notifyAddIterator(g, iter);
} finally {
iter.close();
}
}
} else {
wrapped.notifyAddIterator(g, it);
}
}
@Override
public void notifyAddList(final Graph g, final List<Triple> triples)
throws AuthenticationRequiredException {
List<Triple> list = triples;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs, SecuredGraphEventManager.ADD,
sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.ADD, sg.getModelNode(),
Triple.ANY)) {
list = wrapPermIterator(sg, triples.iterator(),
SecuredGraphEventManager.ADD).toList();
} else {
list = triples;
}
} else {
list = Collections.emptyList();
}
}
if (list.size() > 0) {
wrapped.notifyAddList(g, list);
}
}
@Override
public void notifyAddTriple(final Graph g, final Triple t)
throws AuthenticationRequiredException {
boolean notify = false;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
notify = evaluator.evaluateAny(runAs,
SecuredGraphEventManager.ADD, sg.getModelNode());
if (notify) {
notify = evaluator.evaluateAny(runAs,
SecuredGraphEventManager.ADD, sg.getModelNode(), t);
}
} else {
notify = true;
}
if (notify) {
wrapped.notifyAddTriple(g, t);
}
}
@Override
public void notifyDeleteArray(final Graph g, final Triple[] triples)
throws AuthenticationRequiredException {
Triple[] deleted = triples;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode(),
Triple.ANY)) {
final List<Triple> list = wrapPermIterator(sg,
Arrays.asList(triples).iterator(),
SecuredGraphEventManager.DELETE).toList();
deleted = list.toArray(new Triple[list.size()]);
} else {
deleted = triples;
}
} else {
deleted = new Triple[0];
}
}
if (deleted.length > 0) {
wrapped.notifyDeleteArray(g, deleted);
}
}
@Override
public void notifyDeleteGraph(final Graph g, final Graph removed)
throws AuthenticationRequiredException {
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode())) {
Graph g2 = removed;
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode(),
Triple.ANY)) {
g2 = new CollectionGraph(
removed.find(Triple.ANY)
.filterKeep(
new PermTripleFilter(
SecuredGraphEventManager.DELETE,
sg, evaluator))
.toList());
}
wrapped.notifyDeleteGraph(g, g2);
} else {
// do nothing.
}
} else {
wrapped.notifyDeleteGraph(g, removed);
}
}
@Override
public void notifyDeleteIterator(final Graph g,
final Iterator<Triple> it)
throws AuthenticationRequiredException {
Iterator<Triple> iter = it;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode(),
Triple.ANY)) {
iter = WrappedIterator.create(it).filterKeep(
new PermTripleFilter(
SecuredGraphEventManager.DELETE, sg,
evaluator));
}
// else use the default list as all can bee seen
wrapped.notifyDeleteIterator(g, iter);
} else {
// do nothing.
}
} else {
wrapped.notifyDeleteIterator(g, iter);
}
}
@Override
public void notifyDeleteList(final Graph g, final List<Triple> triples)
throws AuthenticationRequiredException {
List<Triple> list = triples;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode())) {
if (!evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode(),
Triple.ANY)) {
list = WrappedIterator
.create(triples.iterator())
.filterKeep(
new PermTripleFilter(
SecuredGraphEventManager.DELETE,
sg, evaluator)).toList();
}
// else use the default list as all can bee seen
} else {
list = Collections.emptyList();
}
}
if (list.size() > 0) {
wrapped.notifyDeleteList(g, list);
}
}
@Override
public void notifyDeleteTriple(final Graph g, final Triple t)
throws AuthenticationRequiredException {
boolean notify = false;
if (g instanceof SecuredGraph) {
final SecuredGraph sg = (SecuredGraph) g;
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
notify = evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode());
if (notify) {
notify = evaluator.evaluateAny(runAs,
SecuredGraphEventManager.DELETE, sg.getModelNode(),
t);
}
} else {
notify = true;
}
if (notify) {
wrapped.notifyDeleteTriple(g, t);
}
}
@Override
public void notifyEvent(final Graph source, final Object value) {
wrapped.notifyEvent(source, value);
}
private ExtendedIterator<Triple> wrapPermIterator(
final SecuredGraph sg, final Iterator<Triple> it,
final Set<Action> perms) throws AuthenticationRequiredException {
final SecurityEvaluator evaluator = new CachedSecurityEvaluator(
sg.getSecurityEvaluator(), runAs);
if (!evaluator.evaluateAny(runAs, perms, sg.getModelNode(),
Triple.ANY)) {
// nope so wrap the iterator with security iterator
return WrappedIterator.create(it).filterKeep(
new PermTripleFilter(perms, sg, evaluator));
}
return WrappedIterator.create(it);
}
}
// the security evaluator in use
private final SecuredGraph securedGraph;
private final Graph baseGraph;
private final Map<GraphListener, Stack<SecuredGraphListener>> listenerMap = new HashMap<GraphListener, Stack<SecuredGraphListener>>();
private static Set<Action> DELETE;
private static Set<Action> ADD;
static {
SecuredGraphEventManager.ADD = new HashSet<Action>(
Arrays.asList(new Action[] { Action.Create, Action.Read }));
SecuredGraphEventManager.DELETE = new HashSet<Action>(
Arrays.asList(new Action[] { Action.Delete, Action.Read }));
}
public SecuredGraphEventManager(final SecuredGraph securedGraph,
final Graph baseGraph, final GraphEventManager manager) {
this.securedGraph = securedGraph;
this.baseGraph = baseGraph;
manager.register(this);
}
private synchronized Collection<SecuredGraphListener> getListenerCollection() {
ExtendedIterator<SecuredGraphListener> retval = NiceIterator
.emptyIterator();
for (final Collection<SecuredGraphListener> coll : listenerMap.values()) {
retval = retval.andThen(coll.iterator());
}
return retval.toList();
}
@Override
public boolean listening() {
return !listenerMap.isEmpty();
}
@Override
public void notifyAddArray(final Graph g, final Triple[] triples)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyAddArray(securedGraph, triples);
} else {
sgl.notifyAddArray(g, triples);
}
}
}
@Override
public void notifyAddGraph(final Graph g, final Graph added)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyAddGraph(securedGraph, added);
} else {
sgl.notifyAddGraph(g, added);
}
}
}
@Override
public void notifyAddIterator(final Graph g, final Iterator<Triple> it)
throws AuthenticationRequiredException {
notifyAddIterator(g, WrappedIterator.create(it).toList());
baseGraph.equals(g);
}
@Override
public void notifyAddIterator(final Graph g, final List<Triple> triples)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyAddIterator(securedGraph, triples.iterator());
} else {
sgl.notifyAddIterator(g, triples.iterator());
}
}
}
@Override
public void notifyAddList(final Graph g, final List<Triple> triples)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyAddList(securedGraph, triples);
} else {
sgl.notifyAddList(g, triples);
}
}
}
@Override
public void notifyAddTriple(final Graph g, final Triple t)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyAddTriple(securedGraph, t);
} else {
sgl.notifyAddTriple(g, t);
}
}
}
@Override
public void notifyDeleteArray(final Graph g, final Triple[] triples)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyDeleteArray(securedGraph, triples);
} else {
sgl.notifyDeleteArray(g, triples);
}
}
}
@Override
public void notifyDeleteGraph(final Graph g, final Graph removed)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyDeleteGraph(securedGraph, removed);
} else {
sgl.notifyDeleteGraph(g, removed);
}
}
}
@Override
public void notifyDeleteIterator(final Graph g, final Iterator<Triple> it)
throws AuthenticationRequiredException {
notifyDeleteIterator(g, WrappedIterator.create(it).toList());
}
@Override
public void notifyDeleteIterator(final Graph g, final List<Triple> triples)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyDeleteIterator(securedGraph, triples.iterator());
} else {
sgl.notifyDeleteIterator(g, triples.iterator());
}
}
}
@Override
public void notifyDeleteList(final Graph g, final List<Triple> L)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyDeleteList(securedGraph, L);
} else {
sgl.notifyDeleteList(g, L);
}
}
}
@Override
public void notifyDeleteTriple(final Graph g, final Triple t)
throws AuthenticationRequiredException {
final boolean wrap = baseGraph.equals(g);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyDeleteTriple(securedGraph, t);
} else {
sgl.notifyDeleteTriple(g, t);
}
}
}
@Override
public void notifyEvent(final Graph source, final Object value)
throws AuthenticationRequiredException {
if ((source instanceof SecuredGraph) && securedGraph.equals(source)) {
baseGraph.getEventManager().notifyEvent(baseGraph, value);
} else {
final boolean wrap = baseGraph.equals(source);
for (final SecuredGraphListener sgl : getListenerCollection()) {
if (wrap) {
sgl.notifyEvent(securedGraph, value);
} else {
sgl.notifyEvent(source, value);
}
}
}
}
@Override
public synchronized GraphEventManager register(final GraphListener listener) {
Stack<SecuredGraphListener> sgl = listenerMap.get(listener);
if (sgl == null) {
sgl = new Stack<SecuredGraphListener>();
}
sgl.push(new SecuredGraphListener(listener));
listenerMap.put(listener, sgl);
return this;
}
@Override
public synchronized GraphEventManager unregister(
final GraphListener listener) {
final Stack<SecuredGraphListener> sgl = listenerMap.get(listener);
if (sgl != null) {
if (sgl.size() == 1) {
listenerMap.remove(listener);
} else {
sgl.pop();
listenerMap.put(listener, sgl);
}
}
return this;
}
}