/*
* 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.jackrabbit.core.security.authorization;
import org.apache.commons.collections.map.LRUMap;
import org.apache.jackrabbit.spi.Path;
import javax.jcr.RepositoryException;
import javax.jcr.security.Privilege;
import java.util.Map;
import java.util.Set;
/**
* <code>AbstractCompiledPermissions</code>...
*/
public abstract class AbstractCompiledPermissions implements CompiledPermissions {
// cache mapping a Path to a 'Result' containing permissions and privileges.
private final Map<Path, Result> cache;
private final Object monitor = new Object();
@SuppressWarnings("unchecked")
protected AbstractCompiledPermissions() {
cache = new LRUMap(1000);
}
/**
*
* @param absPath Absolute path to return the result for.
* @return the <code>Result</code> for the give <code>absPath</code>.
* @throws RepositoryException if an error occurs.
*/
public Result getResult(Path absPath) throws RepositoryException {
Result result;
synchronized (monitor) {
result = cache.get(absPath);
if (result == null) {
if (absPath == null) {
result = buildRepositoryResult();
} else {
result = buildResult(absPath);
}
cache.put(absPath, result);
}
}
return result;
}
/**
* Retrieve the result for the specified path.
*
* @param absPath Absolute path to build the result for.
* @return Result for the specified <code>absPath</code>.
* @throws RepositoryException If an error occurs.
*/
protected abstract Result buildResult(Path absPath) throws RepositoryException;
/**
* Retrieve the result for repository level operations.
*
* @return The result instance for those permissions and privileges granted
* for repository level operations.
* @throws RepositoryException
*/
protected abstract Result buildRepositoryResult() throws RepositoryException;
/**
* Retrieve the privilege manager.
*
* @return An instance of privilege manager.
* @throws javax.jcr.RepositoryException If an error occurs.
*/
protected abstract PrivilegeManagerImpl getPrivilegeManagerImpl() throws RepositoryException;
/**
* Removes all entries from the cache.
*/
protected void clearCache() {
synchronized (monitor) {
cache.clear();
}
}
//------------------------------------------------< CompiledPermissions >---
/**
* @see CompiledPermissions#close()
*/
public void close() {
clearCache();
}
/**
* @see CompiledPermissions#grants(Path, int)
*/
public boolean grants(Path absPath, int permissions) throws RepositoryException {
return getResult(absPath).grants(permissions);
}
/**
* @see CompiledPermissions#getPrivileges(Path)
*/
public int getPrivileges(Path absPath) throws RepositoryException {
Set<Privilege> pvs = getPrivilegeSet(absPath);
return PrivilegeRegistry.getBits(pvs.toArray(new Privilege[pvs.size()]));
}
/**
* @see CompiledPermissions#hasPrivileges(org.apache.jackrabbit.spi.Path, javax.jcr.security.Privilege[])
*/
public boolean hasPrivileges(Path absPath, Privilege... privileges) throws RepositoryException {
Result result = getResult(absPath);
PrivilegeBits bits = getPrivilegeManagerImpl().getBits(privileges);
return result.allowPrivileges.includes(bits);
}
/**
* @see CompiledPermissions#getPrivilegeSet(Path)
*/
public Set<Privilege> getPrivilegeSet(Path absPath) throws RepositoryException {
Result result = getResult(absPath);
return getPrivilegeManagerImpl().getPrivileges(result.allowPrivileges);
}
/**
* @see CompiledPermissions#canReadAll()
*/
public boolean canReadAll() throws RepositoryException {
return false;
}
//--------------------------------------------------------< inner class >---
/**
* Result of permission (and optionally privilege) evaluation for a given path.
*/
public static class Result {
public static final Result EMPTY = new Result(Permission.NONE, Permission.NONE, PrivilegeBits.EMPTY, PrivilegeBits.EMPTY);
private final int allows;
private final int denies;
private final PrivilegeBits allowPrivileges;
private final PrivilegeBits denyPrivileges;
private int hashCode = -1;
/**
* @deprecated
*/
public Result(int allows, int denies, int allowPrivileges, int denyPrivileges) {
this(allows, denies, PrivilegeBits.getInstance(allowPrivileges), PrivilegeBits.getInstance(denyPrivileges));
}
public Result(int allows, int denies, PrivilegeBits allowPrivileges, PrivilegeBits denyPrivileges) {
this.allows = allows;
this.denies = denies;
// make sure privilegebits are unmodifiable -> proper hashcode generation
this.allowPrivileges = allowPrivileges.unmodifiable();
this.denyPrivileges = denyPrivileges.unmodifiable();
}
public boolean grants(int permissions) {
return (this.allows | ~permissions) == -1;
}
/**
* @deprecated jackrabbit 2.3 (throws UnsupportedOperationException, use getPrivilegeBits instead)
*/
public int getPrivileges() {
throw new UnsupportedOperationException("use #getPrivilegeBits instead.");
}
public PrivilegeBits getPrivilegeBits() {
return allowPrivileges;
}
public Result combine(Result other) {
int cAllows = allows | Permission.diff(other.allows, denies);
int cDenies = denies | Permission.diff(other.denies, allows);
PrivilegeBits cAPrivs = PrivilegeBits.getInstance(allowPrivileges);
cAPrivs.addDifference(other.allowPrivileges, denyPrivileges);
PrivilegeBits cdPrivs = PrivilegeBits.getInstance(denyPrivileges);
cdPrivs.addDifference(other.denyPrivileges, allowPrivileges);
return new Result(cAllows, cDenies, allowPrivileges, denyPrivileges);
}
/**
* @see Object#hashCode()
*/
@Override
public int hashCode() {
if (hashCode == -1) {
int h = 17;
h = 37 * h + allows;
h = 37 * h + denies;
h = 37 * h + allowPrivileges.hashCode();
h = 37 * h + denyPrivileges.hashCode();
hashCode = h;
}
return hashCode;
}
/**
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (object instanceof Result) {
Result other = (Result) object;
return allows == other.allows &&
denies == other.denies &&
allowPrivileges.equals(other.allowPrivileges) &&
denyPrivileges.equals(other.denyPrivileges);
}
return false;
}
}
}