org.apache.jena.security.impl.SecuredItemImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.jena.security.impl.SecuredItemImpl.java

Source

/*
 * 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.security.impl;

import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.RDF;

import java.lang.reflect.Proxy;

import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.jena.security.AccessDeniedException;
import org.apache.jena.security.SecurityEvaluator;
import org.apache.jena.security.SecurityEvaluator.Action;
import org.apache.jena.security.SecurityEvaluator.SecNode;
import org.apache.jena.security.SecurityEvaluator.SecTriple;
import org.apache.jena.security.SecurityEvaluator.SecNode.Type;

/**
 * An abstract implementation of SecuredItem that caches security checks.
 * <p>
 * Security checks are performed at multiple locations.  This implementation ensures that 
 * during a single operation the specific check is only evaluated once by caching the result.
 * </p>
 * 
 */
public abstract class SecuredItemImpl implements SecuredItem {
    // a key for the secured item.
    private class CacheKey implements Comparable<CacheKey> {
        private final Action action;
        private final SecNode modelNode;
        private final SecTriple from;
        private final SecTriple to;
        private Integer hashCode;

        public CacheKey(final Action action, final SecNode modelNode) {
            this(action, modelNode, null, null);
        }

        public CacheKey(final Action action, final SecNode modelNode, final SecTriple to) {
            this(action, modelNode, to, null);
        }

        public CacheKey(final Action action, final SecNode modelNode, final SecTriple to, final SecTriple from) {
            this.action = action;
            this.modelNode = modelNode;
            this.to = to;
            this.from = from;
        }

        @Override
        public int compareTo(final CacheKey other) {
            int retval = this.action.compareTo(other.action);
            if (retval == 0) {
                retval = this.modelNode.compareTo(other.modelNode);
            }
            if (retval == 0) {
                if (this.to == null) {
                    if (other.to == null) {
                        return 0;
                    }
                    return -1;
                }
                retval = this.to.compareTo(other.to);
            }
            if (retval == 0) {
                if (this.from == null) {
                    if (other.from == null) {
                        return 0;
                    }
                    return -1;
                }
                retval = this.from.compareTo(other.from);
            }
            return retval;
        }

        @Override
        public boolean equals(final Object o) {
            if (o instanceof CacheKey) {
                return this.compareTo((CacheKey) o) == 0;
            }
            return false;
        }

        @Override
        public int hashCode() {
            if (hashCode == null) {
                hashCode = new HashCodeBuilder().append(action).append(modelNode).append(from).append(to)
                        .toHashCode();
            }
            return hashCode;
        }
    }

    // the maximum size of the cache
    public static int MAX_CACHE = 100;
    // the cache for this thread.
    public static final ThreadLocal<LRUMap> CACHE = new ThreadLocal<LRUMap>();
    // the number of times this thread has recursively called the constructor.
    public static final ThreadLocal<Integer> COUNT = new ThreadLocal<Integer>();

    /**
     * Convert a Jena Node object into a SecNode object.
     * @param jenaNode The Jena node to convert.
     * @return The SecNode that represents the jenaNode.
     */
    public static SecNode convert(final com.hp.hpl.jena.graph.Node jenaNode) {
        if (com.hp.hpl.jena.graph.Node.ANY.equals(jenaNode)) {
            return SecNode.ANY;
        }
        if (jenaNode.isLiteral()) {
            return new SecNode(Type.Literal, jenaNode.getLiteral().toString());
        }
        if (jenaNode.isBlank()) {
            return new SecNode(Type.Anonymous, jenaNode.getBlankNodeLabel());
        }
        if (jenaNode.isVariable()) {
            return SecNode.VARIABLE;
        }
        return new SecNode(Type.URI, jenaNode.getURI());
    }

    /**
     * Convert a Jena Triple into a SecTriple.
     * @param jenaTriple The Jena Triple to convert.
     * @return The SecTriple that represents the jenaTriple.
     */
    public static SecTriple convert(final com.hp.hpl.jena.graph.Triple jenaTriple) {
        return new SecTriple(SecuredItemImpl.convert(jenaTriple.getSubject()),
                SecuredItemImpl.convert(jenaTriple.getPredicate()),
                SecuredItemImpl.convert(jenaTriple.getObject()));
    }

    /**
     * Decrement the number of instances of SecuredItem.
     */
    public static void decrementUse() {
        final Integer i = SecuredItemImpl.COUNT.get();
        if (i == null) {
            throw new IllegalStateException("No count on exit");
        }
        if (i < 1) {
            throw new IllegalStateException("No count less than 1");
        }
        if (i == 1) {
            SecuredItemImpl.CACHE.remove();
            SecuredItemImpl.COUNT.remove();
        } else {
            SecuredItemImpl.COUNT.set(i - 1);
        }
    }

    /**
     * Increment the number of instances of SecuredItem.
     */
    public static void incrementUse() {
        final Integer i = SecuredItemImpl.COUNT.get();
        if (i == null) {
            SecuredItemImpl.CACHE.set(new LRUMap(Math.max(SecuredItemImpl.MAX_CACHE, 100)));
            SecuredItemImpl.COUNT.set(1);
        } else {
            SecuredItemImpl.COUNT.set(i + 1);
        }
    }

    // the evaluator we are using 
    private final SecurityEvaluator securityEvaluator;

    // the secured node for that names the graph.
    private final SecNode modelNode;

    // the item holder that we are evaluating.
    private final ItemHolder<?, ?> itemHolder;

    /**
     * Create the SecuredItemImpl.
     * @param securedItem The securedItem.
     * @param holder The Item holder for the securedItem.
     * @throws IllegalArgumentException if securedItem is null or securedItem.getSecurityEvaluator() 
     * returns null, or the holder is null.
     */
    protected SecuredItemImpl(final SecuredItem securedItem, final ItemHolder<?, ?> holder) {
        if (securedItem == null) {
            throw new IllegalArgumentException("Secured item may not be null");
        }
        if (securedItem.getSecurityEvaluator() == null) {
            throw new IllegalArgumentException("Security evaluator in secured item may not be null");
        }
        if (holder == null) {
            throw new IllegalArgumentException("ItemHolder may not be null");
        }
        this.securityEvaluator = securedItem.getSecurityEvaluator();
        this.modelNode = new SecurityEvaluator.SecNode(SecurityEvaluator.SecNode.Type.URI,
                securedItem.getModelIRI());
        this.itemHolder = holder;
    }

    /**
     * Create the SecuredItemImpl.
     * @param securityEvaluator the secured evaluator to use.
     * @param modelURI the URI for the model.
     * @param holder The holder to use.
     * @throws IllegalArgumentException if security evaluator is null, modelURI is null or empty,
     * or holder is null.
     */
    protected SecuredItemImpl(final SecurityEvaluator securityEvaluator, final String modelURI,
            final ItemHolder<?, ?> holder) {
        if (securityEvaluator == null) {
            throw new IllegalArgumentException("Security evaluator may not be null");
        }
        if (StringUtils.isEmpty(modelURI)) {
            throw new IllegalArgumentException("ModelURI may not be empty or null");
        }
        if (holder == null) {
            throw new IllegalArgumentException("ItemHolder may not be null");
        }
        this.securityEvaluator = securityEvaluator;
        this.modelNode = new SecurityEvaluator.SecNode(SecurityEvaluator.SecNode.Type.URI, modelURI);
        this.itemHolder = holder;
    }

    @Override
    public String toString() {
        if (canRead()) {
            return itemHolder.getBaseItem().toString();
        }
        return super.toString();
    }

    /**
     * get the cached value.
     * @param key The key to look for.
     * @return the value of the security check or <code>null</code> if the value has not been cached.
     */
    private Boolean cacheGet(final CacheKey key) {
        final LRUMap cache = SecuredItemImpl.CACHE.get();
        return (cache == null) ? null : (Boolean) cache.get(key);
    }

    /**
     * set teh cache value.
     * @param key The key to set the value for.
     * @param value The value to set.
     */
    void cachePut(final CacheKey key, final boolean value) {
        final LRUMap cache = SecuredItemImpl.CACHE.get();
        if (cache != null) {
            cache.put(key, value);
            SecuredItemImpl.CACHE.set(cache);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#canCreate()
     */
    @Override
    public boolean canCreate() {
        final CacheKey key = new CacheKey(Action.Create, modelNode);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Create, modelNode);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canCreate(final com.hp.hpl.jena.graph.Triple t) {
        return canCreate(SecuredItemImpl.convert(t));
    }

    @Override
    public boolean canCreate(final SecTriple t) {
        final CacheKey key = new CacheKey(Action.Create, modelNode, t);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Create, modelNode, t);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canCreate(final Statement s) {
        return canCreate(s.asTriple());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#canDelete()
     */
    @Override
    public boolean canDelete() {
        final CacheKey key = new CacheKey(Action.Delete, modelNode);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Delete, modelNode);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canDelete(final com.hp.hpl.jena.graph.Triple t) {
        return canDelete(SecuredItemImpl.convert(t));
    }

    @Override
    public boolean canDelete(final SecTriple t) {
        final CacheKey key = new CacheKey(Action.Delete, modelNode, t);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Delete, modelNode, t);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canDelete(final Statement s) {
        return canDelete(s.asTriple());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#canRead()
     */
    @Override
    public boolean canRead() {
        final CacheKey key = new CacheKey(Action.Read, modelNode);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Read, modelNode);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canRead(final com.hp.hpl.jena.graph.Triple t) {
        return canRead(SecuredItemImpl.convert(t));
    }

    @Override
    public boolean canRead(final SecTriple t) {
        final CacheKey key = new CacheKey(Action.Read, modelNode, t);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Read, modelNode, t);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canRead(final Statement s) {
        return canRead(s.asTriple());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#canUpdate()
     */
    @Override
    public boolean canUpdate() {
        final CacheKey key = new CacheKey(Action.Update, modelNode);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluate(Action.Update, modelNode);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canUpdate(final com.hp.hpl.jena.graph.Triple from, final com.hp.hpl.jena.graph.Triple to) {
        return canUpdate(SecuredItemImpl.convert(from), SecuredItemImpl.convert(to));
    }

    @Override
    public boolean canUpdate(final SecTriple from, final SecTriple to) {
        final CacheKey key = new CacheKey(Action.Update, modelNode, from, to);
        Boolean retval = cacheGet(key);
        if (retval == null) {
            retval = securityEvaluator.evaluateUpdate(modelNode, from, to);
            cachePut(key, retval);
        }
        return retval;
    }

    public boolean canUpdate(final Statement from, final Statement to) {
        return canUpdate(from.asTriple(), to.asTriple());
    }

    /**
     * check that create on the securedModel is allowed,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkCreate() {
        if (!canCreate()) {
            throw new AccessDeniedException(modelNode, Action.Create);
        }
    }

    protected void checkCreate(final com.hp.hpl.jena.graph.Triple t) {
        checkCreate(SecuredItemImpl.convert(t));
    }

    /**
     * check that the triple can be created in the securedModel.,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkCreate(final SecTriple t) {
        if (!canCreate(t)) {
            throw new AccessDeniedException(modelNode, t.toString(), Action.Create);
        }
    }

    protected void checkCreate(final Statement s) {
        checkCreate(s.asTriple());
    }

    protected void checkCreateReified(final String uri, final SecTriple t) {
        checkUpdate();
        final SecNode n = uri == null ? SecNode.FUTURE : new SecNode(Type.URI, uri);
        checkCreate(new SecTriple(n, SecuredItemImpl.convert(RDF.subject.asNode()), t.getSubject()));
        checkCreate(new SecTriple(n, SecuredItemImpl.convert(RDF.predicate.asNode()), t.getPredicate()));
        checkCreate(new SecTriple(n, SecuredItemImpl.convert(RDF.object.asNode()), t.getObject()));
    }

    protected void checkCreateStatement(final ExtendedIterator<Statement> stmts) {
        if (!canCreate(SecTriple.ANY)) {
            try {
                while (stmts.hasNext()) {
                    checkCreate(stmts.next());
                }
            } finally {
                stmts.close();
            }
        }
    }

    protected void checkCreateTriples(final ExtendedIterator<com.hp.hpl.jena.graph.Triple> triples) {
        if (!canCreate(SecTriple.ANY)) {
            try {
                while (triples.hasNext()) {
                    checkCreate(triples.next());
                }
            } finally {
                triples.close();
            }
        }
    }

    /**
     * check that delete on the securedModel is allowed,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkDelete() {
        if (!canDelete()) {
            throw new AccessDeniedException(modelNode, Action.Delete);
        }
    }

    protected void checkDelete(final com.hp.hpl.jena.graph.Triple t) {
        checkDelete(SecuredItemImpl.convert(t));
    }

    /**
     * check that the triple can be deleted in the securedModel.,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkDelete(final SecTriple t) {
        if (!canDelete(t)) {
            throw new AccessDeniedException(modelNode, t.toString(), Action.Delete);
        }
    }

    protected void checkDelete(final Statement s) {
        checkDelete(s.asTriple());
    }

    protected void checkDeleteStatements(final ExtendedIterator<Statement> stmts) {
        if (!canDelete(SecTriple.ANY)) {
            try {
                while (stmts.hasNext()) {
                    checkDelete(stmts.next());
                }
            } finally {
                stmts.close();
            }
        }
    }

    protected void checkDeleteTriples(final ExtendedIterator<com.hp.hpl.jena.graph.Triple> triples) {
        if (!canDelete(SecTriple.ANY)) {
            try {
                while (triples.hasNext()) {
                    checkDelete(triples.next());
                }
            } finally {
                triples.close();
            }
        }
    }

    /**
     * check that read on the securedModel is allowed,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkRead() {
        if (!canRead()) {
            throw new AccessDeniedException(modelNode, Action.Read);
        }
    }

    protected void checkRead(final com.hp.hpl.jena.graph.Triple t) {
        checkRead(SecuredItemImpl.convert(t));
    }

    /**
     * check that the triple can be read in the securedModel.,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkRead(final SecTriple t) {
        if (!canRead(t)) {
            throw new AccessDeniedException(modelNode, t.toString(), Action.Read);
        }
    }

    protected void checkRead(final Statement s) {
        checkRead(s.asTriple());
    }

    protected void checkReadStatement(final ExtendedIterator<Statement> stmts) {
        try {
            while (stmts.hasNext()) {
                checkRead(stmts.next());
            }
        } finally {
            stmts.close();
        }
    }

    protected void checkReadTriples(final ExtendedIterator<com.hp.hpl.jena.graph.Triple> triples) {
        try {
            while (triples.hasNext()) {
                checkRead(triples.next());
            }
        } finally {
            triples.close();
        }
    }

    /**
     * check that update on the securedModel is allowed,
     * 
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkUpdate() {
        if (!canUpdate()) {
            throw new AccessDeniedException(modelNode, Action.Update);
        }
    }

    protected void checkUpdate(final com.hp.hpl.jena.graph.Triple from, final com.hp.hpl.jena.graph.Triple to) {
        checkUpdate(SecuredItemImpl.convert(from), SecuredItemImpl.convert(to));
    }

    /**
     * check that the triple can be updated in the securedModel.,
     * 
     * @param from the starting triple
     * @param to the final triple.
     * @throws AccessDeniedException
     *             on failure
     */
    protected void checkUpdate(final SecTriple from, final SecTriple to) {
        if (!canUpdate(from, to)) {
            throw new AccessDeniedException(modelNode, String.format("%s to %s", from, to), Action.Update);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object o) {
        if (Proxy.isProxyClass(o.getClass())) {
            return o.equals(itemHolder.getSecuredItem());
        } else {
            if (o instanceof SecuredItemImpl) {
                return itemHolder.getBaseItem().equals(((SecuredItemImpl) o).getBaseItem());
            }
            return false;
        }
    }

    @Override
    public int hashCode() {
        return itemHolder.getBaseItem().hashCode();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#getBaseItem()
     */
    @Override
    public Object getBaseItem() {
        return itemHolder.getBaseItem();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#getModelIRI()
     */
    @Override
    public String getModelIRI() {
        return modelNode.getValue();
    }

    /**
     * get the name of the model.
     */
    @Override
    public SecNode getModelNode() {
        return modelNode;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.SecuredItem#getSecurityEvaluator()
     */
    @Override
    public SecurityEvaluator getSecurityEvaluator() {
        return securityEvaluator;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.apache.jena.security.isEquivalent()
     */
    @Override
    public boolean isEquivalent(final SecuredItem securedItem) {
        return SecuredItem.Util.isEquivalent(this, securedItem);
    }
}