blob: cd1f200c591e8fddb5b0922d4ba219f7a23fb387 [file] [log] [blame]
/*
* 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.sling.resourceaccesssecurity.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.security.AccessSecurityException;
import org.apache.sling.api.security.ResourceAccessSecurity;
import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
import org.apache.sling.resourceaccesssecurity.ResourceAccessGate.GateResult;
import org.osgi.framework.ServiceReference;
public abstract class ResourceAccessSecurityImpl implements ResourceAccessSecurity {
private List<ResourceAccessGateHandler> allHandlers = Collections.emptyList();
private final boolean defaultAllowIfNoGateMatches;
public ResourceAccessSecurityImpl(final boolean defaultAllowIfNoGateMatches) {
this.defaultAllowIfNoGateMatches = defaultAllowIfNoGateMatches;
}
/**
* This method returns either an iterator delivering the matching handlers
* or <code>null</code>.
*/
private Iterator<ResourceAccessGateHandler> getMatchingResourceAccessGateHandlerIterator(
final String path, final ResourceAccessGate.Operation operation) {
//
// TODO: maybe caching some frequent paths with read operation would be
// a good idea
//
final List<ResourceAccessGateHandler> handlers = allHandlers;
if (handlers.size() > 0) {
final Iterator<ResourceAccessGateHandler> iter = handlers.iterator();
return new Iterator<ResourceAccessGateHandler>() {
private ResourceAccessGateHandler next;
{
peek();
}
private void peek() {
this.next = null;
while ( iter.hasNext() && next == null ) {
final ResourceAccessGateHandler handler = iter.next();
if (handler.matches(path, operation)) {
next = handler;
}
}
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public ResourceAccessGateHandler next() {
if ( next == null ) {
throw new NoSuchElementException();
}
final ResourceAccessGateHandler handler = this.next;
peek();
return handler;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
return null;
}
@Override
public Resource getReadableResource(final Resource resource) {
Resource returnValue = null;
final Iterator<ResourceAccessGateHandler> accessGateHandlers = getMatchingResourceAccessGateHandlerIterator(
resource.getPath(), ResourceAccessGate.Operation.READ);
GateResult finalGateResult = null;
List<ResourceAccessGate> accessGatesForReadValues = null;
boolean canReadAllValues = false;
if ( accessGateHandlers != null ) {
boolean noGateMatched = true;
while ( accessGateHandlers.hasNext() ) {
noGateMatched = false;
final ResourceAccessGateHandler resourceAccessGateHandler = accessGateHandlers.next();
final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canRead(resource);
if (!canReadAllValues && gateResult == GateResult.GRANTED) {
if (resourceAccessGateHandler.getResourceAccessGate().canReadAllValues(resource)) {
canReadAllValues = true;
accessGatesForReadValues = null;
} else {
if (accessGatesForReadValues == null) {
accessGatesForReadValues = new ArrayList<ResourceAccessGate>();
}
accessGatesForReadValues.add(resourceAccessGateHandler.getResourceAccessGate());
}
}
if (finalGateResult == null) {
finalGateResult = gateResult;
} else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.CANT_DECIDE) {
finalGateResult = gateResult;
}
// stop checking if the operation is final and the result not GateResult.DONTCARE
if (gateResult != GateResult.CANT_DECIDE && resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.READ)) {
break;
}
}
// return null if access is denied or no ResourceAccessGate is present
if (finalGateResult == GateResult.DENIED) {
returnValue = null;
} else if (finalGateResult == GateResult.GRANTED ) {
returnValue = resource;
} else if (noGateMatched && this.defaultAllowIfNoGateMatches)
{
returnValue = resource;
}
}
boolean canUpdateResource = canUpdate(resource);
// wrap Resource if read access is not or partly (values) not granted
if (returnValue != null) {
if( !canReadAllValues || !canUpdateResource ) {
returnValue = new AccessGateResourceWrapper(returnValue,
accessGatesForReadValues,
canUpdateResource);
}
}
return returnValue;
}
@Override
public boolean canCreate(final String path,
final ResourceResolver resolver) {
final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
path, ResourceAccessGate.Operation.CREATE);
boolean result = false;
if ( handlers != null ) {
GateResult finalGateResult = null;
boolean noGateMatched = true;
while ( handlers.hasNext() ) {
noGateMatched = false;
final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canCreate(path, resolver);
if (finalGateResult == null) {
finalGateResult = gateResult;
} else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.CANT_DECIDE) {
finalGateResult = gateResult;
}
if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.CANT_DECIDE &&
resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.CREATE)) {
break;
}
}
if ( finalGateResult == GateResult.GRANTED ) {
result = true;
} else if ( finalGateResult == GateResult.DENIED ) {
result = false;
} else if ( noGateMatched && this.defaultAllowIfNoGateMatches )
{
result = true;
}
}
return result;
}
@Override
public boolean canUpdate(final Resource resource) {
final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
resource.getPath(), ResourceAccessGate.Operation.UPDATE);
boolean result = this.defaultAllowIfNoGateMatches;
if ( handlers != null ) {
GateResult finalGateResult = null;
boolean noGateMatched = true;
while ( handlers.hasNext() ) {
noGateMatched = false;
final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canUpdate(resource);
if (finalGateResult == null) {
finalGateResult = gateResult;
} else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.CANT_DECIDE) {
finalGateResult = gateResult;
}
if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.CANT_DECIDE &&
resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.UPDATE)) {
break;
}
}
if ( finalGateResult == GateResult.GRANTED ) {
result = true;
} else if ( finalGateResult == GateResult.DENIED ) {
result = false;
} else if ( noGateMatched && this.defaultAllowIfNoGateMatches )
{
result = true;
}
}
return result;
}
@Override
public boolean canDelete(final Resource resource) {
final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
resource.getPath(), ResourceAccessGate.Operation.DELETE);
boolean result = this.defaultAllowIfNoGateMatches;
if ( handlers != null ) {
GateResult finalGateResult = null;
boolean noGateMatched = true;
while ( handlers.hasNext() ) {
noGateMatched = false;
final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canDelete(resource);
if (finalGateResult == null) {
finalGateResult = gateResult;
} else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.CANT_DECIDE) {
finalGateResult = gateResult;
}
if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.CANT_DECIDE &&
resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.DELETE)) {
break;
}
}
if ( finalGateResult == GateResult.GRANTED ) {
result = true;
} else if ( finalGateResult == GateResult.DENIED ) {
result = false;
} else if ( noGateMatched && this.defaultAllowIfNoGateMatches )
{
result = true;
}
}
return result;
}
@Override
public boolean canExecute(final Resource resource) {
final Iterator<ResourceAccessGateHandler> handlers = getMatchingResourceAccessGateHandlerIterator(
resource.getPath(), ResourceAccessGate.Operation.EXECUTE);
boolean result = this.defaultAllowIfNoGateMatches;
if ( handlers != null ) {
GateResult finalGateResult = null;
boolean noGateMatched = true;
while ( handlers.hasNext() ) {
noGateMatched = false;
final ResourceAccessGateHandler resourceAccessGateHandler = handlers.next();
final GateResult gateResult = resourceAccessGateHandler.getResourceAccessGate().canExecute(resource);
if (finalGateResult == null) {
finalGateResult = gateResult;
} else if (finalGateResult != GateResult.GRANTED && gateResult != GateResult.CANT_DECIDE) {
finalGateResult = gateResult;
}
if (finalGateResult == GateResult.GRANTED || gateResult != GateResult.CANT_DECIDE && resourceAccessGateHandler.isFinalOperation(ResourceAccessGate.Operation.EXECUTE)) {
break;
}
}
if ( finalGateResult == GateResult.GRANTED ) {
result = true;
} else if ( finalGateResult == GateResult.DENIED ) {
result = false;
} else if ( noGateMatched && this.defaultAllowIfNoGateMatches )
{
result = true;
}
}
return result;
}
@Override
public boolean canReadValue(final Resource resource, final String valueName) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean canSetValue(final Resource resource, final String valueName) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean canDeleteValue(final Resource resource, final String valueName) {
// TODO Auto-generated method stub
return false;
}
@Override
public String transformQuery(final String query,
final String language,
final ResourceResolver resourceResolver)
throws AccessSecurityException {
return query;
}
/**
* Add a new resource access gate
*/
protected void bindResourceAccessGate(final ServiceReference ref) {
synchronized ( this ) {
final List<ResourceAccessGateHandler> newList = new ArrayList<ResourceAccessGateHandler>(this.allHandlers);
final ResourceAccessGateHandler h = new ResourceAccessGateHandler(ref);
newList.add(h);
Collections.sort(newList);
this.allHandlers = newList;
}
}
/**
* Remove a resource access gate
*/
protected void unbindResourceAccessGate(final ServiceReference ref) {
synchronized ( this ) {
final List<ResourceAccessGateHandler> newList = new ArrayList<ResourceAccessGateHandler>(this.allHandlers);
final ResourceAccessGateHandler h = new ResourceAccessGateHandler(ref);
newList.remove(h);
this.allHandlers = newList;
}
}
}