blob: 7207b57ce8bc7365f39e26f053e576e996ece798 [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.resourceresolver.impl.providers.stateful;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import org.apache.sling.api.resource.PersistenceException;
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.resourceresolver.impl.ResourceAccessSecurityTracker;
import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
import org.apache.sling.resourceresolver.impl.helper.AbstractIterator;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
import org.apache.sling.spi.resource.provider.QueryLanguageProvider;
import org.apache.sling.spi.resource.provider.ResolveContext;
import org.apache.sling.spi.resource.provider.ResourceContext;
import org.apache.sling.spi.resource.provider.ResourceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This {@link AuthenticatedResourceProvider} implementation keeps a resource
* provider and the authentication information (through the {@link ResolveContext}).
*
* The methods are similar to those of {@link ResourceProvider}.
*/
public class AuthenticatedResourceProvider {
private static final Logger logger = LoggerFactory.getLogger(ResourceResolverImpl.class);
public static final AuthenticatedResourceProvider UNAUTHENTICATED_PROVIDER = new AuthenticatedResourceProvider();
private final ResourceProviderHandler providerHandler;
private final ResolveContext<Object> resolveContext;
private final ResourceAccessSecurityTracker tracker;
private final boolean useRAS;
/**
* Constructor
* @param providerHandler
* @param useRAS
* @param resolveContext
* @param tracker
*/
public AuthenticatedResourceProvider(@NotNull final ResourceProviderHandler providerHandler,
final boolean useRAS,
@NotNull final ResolveContext<Object> resolveContext,
@NotNull final ResourceAccessSecurityTracker tracker) {
this.providerHandler = providerHandler;
this.resolveContext = resolveContext;
this.tracker = tracker;
this.useRAS = useRAS;
}
private AuthenticatedResourceProvider() {
this.providerHandler = null;
this.resolveContext = null;
this.tracker = null;
this.useRAS = false;
}
/**
* Get the resolve context.
* @return The resolve context
*/
public @NotNull ResolveContext<Object> getResolveContext() {
return this.resolveContext;
}
/**
* @see ResourceProvider#refresh(ResolveContext)
*/
public void refresh() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.refresh(this.resolveContext);
}
}
/**
* Check if the provider is live
* @return {@code true} If live
* @see ResourceProvider#isLive(ResolveContext)
*/
public boolean isLive() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.isLive(this.resolveContext);
}
return false;
}
/**
* Get the parent resource
* @param child The child
* @return The parent
* @see ResourceProvider#getParent(ResolveContext, Resource)
*/
public Resource getParent(final Resource child) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return wrapResource(rp.getParent(this.resolveContext, child));
}
return null;
}
/**
* @see ResourceProvider#getResource(ResolveContext, String, ResourceContext, Resource)
*/
public Resource getResource(final String path, final Resource parent, final Map<String, String> parameters) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp == null ) {
return null;
}
final ResourceContext resourceContext;
if ( parameters != null ) {
resourceContext = new ResourceContext() {
@Override
public Map<String, String> getResolveParameters() {
return parameters;
}
};
} else {
resourceContext = ResourceContext.EMPTY_CONTEXT;
}
return wrapResource(rp.getResource(this.resolveContext, path, resourceContext, parent));
}
/**
* @see ResourceProvider#listChildren(ResolveContext, Resource)
*/
public Iterator<Resource> listChildren(final Resource parent) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return wrapIterator(rp.listChildren(this.resolveContext, parent));
}
return null;
}
/**
* @see ResourceProvider#getAttributeNames(ResolveContext)
*/
public void getAttributeNames(final Set<String> attributeNames) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
Collection<String> rpAttributeNames = rp.getAttributeNames(this.resolveContext);
if (rpAttributeNames != null) {
attributeNames.addAll(rpAttributeNames);
}
}
}
/**
* @see ResourceProvider#getAttribute(ResolveContext, String)
*/
public Object getAttribute(final String name) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.getAttribute(this.resolveContext, name);
}
return null;
}
/**
* @see ResourceProvider#create(ResolveContext, String, Map)
*/
public Resource create(final ResourceResolver resolver,
final String path,
final Map<String, Object> properties)
throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null && this.canCreate(resolver, path) ) {
return rp.create(this.resolveContext, path, properties);
}
return null;
}
/**
* @see ResourceProvider#orderBefore(ResolveContext, Resource, String, String)
*/
public boolean orderBefore(final @NotNull Resource parent, final @NotNull String name, final @Nullable String followingSiblingName)
throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if (rp != null && this.canOrderChildren(parent)) {
return rp.orderBefore(this.resolveContext, parent, name, followingSiblingName);
} else {
throw new PersistenceException("Unable to order child resources of " + parent.getPath());
}
}
/**
* @see ResourceProvider#delete(ResolveContext, Resource)
*/
public void delete(final Resource resource) throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null && this.canDelete(resource) ) {
rp.delete(this.resolveContext, resource);
} else {
throw new PersistenceException("Unable to delete resource " + resource.getPath());
}
}
/**
* @see ResourceProvider#revert(ResolveContext)
*/
public void revert() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.revert(this.resolveContext);
}
}
/**
* @see ResourceProvider#commit(ResolveContext)
*/
public void commit() throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.commit(this.resolveContext);
}
}
/**
* @see ResourceProvider#hasChanges(ResolveContext)
*/
public boolean hasChanges() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.hasChanges(this.resolveContext);
}
return false;
}
/**
* @see ResourceProvider#getQueryLanguageProvider()
*/
private QueryLanguageProvider<Object> getQueryLanguageProvider() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.getQueryLanguageProvider();
}
return null;
}
/**
* @see QueryLanguageProvider#getSupportedLanguages(ResolveContext)
*/
public String[] getSupportedLanguages() {
final QueryLanguageProvider<Object> jcrQueryProvider = getQueryLanguageProvider();
if (jcrQueryProvider == null) {
return null;
}
return jcrQueryProvider.getSupportedLanguages(this.resolveContext);
}
/**
* @see QueryLanguageProvider#findResources(ResolveContext, String, String)
*/
public Iterator<Resource> findResources(final String query, final String language) {
final QueryLanguageProvider<Object> jcrQueryProvider = getQueryLanguageProvider();
if (jcrQueryProvider == null) {
return null;
}
return wrapIterator(jcrQueryProvider.findResources(this.resolveContext, transformQuery(query, language), language));
}
/**
* @see QueryLanguageProvider#queryResources(ResolveContext, String, String)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public Iterator<Map<String, Object>> queryResources(final String query, final String language) {
final QueryLanguageProvider<Object> jcrQueryProvider = getQueryLanguageProvider();
if (jcrQueryProvider == null) {
return null;
}
return (Iterator) jcrQueryProvider.queryResources(this.resolveContext, transformQuery(query, language), language);
}
/**
* @see ResourceProvider#adaptTo(ResolveContext, Class)
*/
public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.adaptTo(this.resolveContext, type);
}
return null;
}
/**
* @see ResourceProvider#copy(ResolveContext, String, String)
*/
public boolean copy(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.copy(this.resolveContext, srcAbsPath, destAbsPath);
}
return false;
}
/**
* @see ResourceProvider#move(ResolveContext, String, String)
*/
public boolean move(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.move(this.resolveContext, srcAbsPath, destAbsPath);
}
return false;
}
private boolean canCreate(final ResourceResolver resolver, final String path) {
boolean allowed = true;
if ( this.useRAS ) {
final ResourceAccessSecurity security = tracker.getProviderResourceAccessSecurity();
if ( security != null ) {
allowed = security.canCreate(path, resolver);
} else {
allowed = false;
}
}
if ( allowed ) {
final ResourceAccessSecurity security = tracker.getApplicationResourceAccessSecurity();
if (security != null) {
allowed = security.canCreate(path, resolver);
}
}
return allowed;
}
private boolean canOrderChildren(final Resource resource) {
boolean allowed = true;
if ( this.useRAS ) {
final ResourceAccessSecurity security = tracker.getProviderResourceAccessSecurity();
if ( security != null ) {
allowed = security.canOrderChildren(resource);
} else {
allowed = false;
}
}
if ( allowed ) {
final ResourceAccessSecurity security = tracker.getApplicationResourceAccessSecurity();
if (security != null) {
allowed = security.canOrderChildren(resource);
}
}
return allowed;
}
private boolean canDelete(final Resource resource) {
boolean allowed = true;
if ( this.useRAS ) {
final ResourceAccessSecurity security = tracker.getProviderResourceAccessSecurity();
if ( security != null ) {
allowed = security.canDelete(resource);
} else {
allowed = false;
}
}
if ( allowed ) {
final ResourceAccessSecurity security = tracker.getApplicationResourceAccessSecurity();
if (security != null) {
allowed = security.canDelete(resource);
}
}
return allowed;
}
/**
* applies resource access security if configured
*/
private String transformQuery ( final String query, final String language ) {
String returnValue = query;
if (this.useRAS) {
final ResourceAccessSecurity resourceAccessSecurity = tracker
.getProviderResourceAccessSecurity();
if (resourceAccessSecurity != null) {
try {
returnValue = resourceAccessSecurity.transformQuery(
returnValue, language, this.resolveContext.getResourceResolver());
} catch (AccessSecurityException e) {
logger.error(
"AccessSecurityException occurred while trying to transform the query {} (language {}).",
new Object[] { query, language }, e);
}
}
}
final ResourceAccessSecurity resourceAccessSecurity = tracker
.getApplicationResourceAccessSecurity();
if (resourceAccessSecurity != null) {
try {
returnValue = resourceAccessSecurity.transformQuery(
returnValue, language, this.resolveContext.getResourceResolver());
} catch (AccessSecurityException e) {
logger.error(
"AccessSecurityException occurred while trying to transform the query {} (language {}).",
new Object[] { query, language }, e);
}
}
return returnValue;
}
/**
* Wrap a resource with additional resource access security
* @param rsrc The resource or {@code null}.
* @return The wrapped resource or {@code null}
*/
private @Nullable Resource wrapResource(@Nullable Resource rsrc) {
Resource returnValue = null;
if (useRAS && rsrc != null) {
final ResourceAccessSecurity resourceAccessSecurity = tracker.getProviderResourceAccessSecurity();
if (resourceAccessSecurity != null) {
returnValue = resourceAccessSecurity.getReadableResource(rsrc);
}
} else {
returnValue = rsrc;
}
if ( returnValue != null ) {
final ResourceAccessSecurity resourceAccessSecurity = tracker.getApplicationResourceAccessSecurity();
if (resourceAccessSecurity != null) {
returnValue = resourceAccessSecurity.getReadableResource(returnValue);
}
}
return returnValue;
}
private Iterator<Resource> wrapIterator(Iterator<Resource> iterator) {
if (iterator == null) {
return iterator;
} else {
return new SecureIterator(iterator);
}
}
private class SecureIterator extends AbstractIterator<Resource> {
private final Iterator<Resource> iterator;
public SecureIterator(Iterator<Resource> iterator) {
this.iterator = iterator;
}
@Override
protected Resource seek() {
while (iterator.hasNext()) {
final Resource resource = wrapResource(iterator.next());
if (resource != null) {
return resource;
}
}
return null;
}
}
@Override
public String toString() {
return "[" + getClass().getSimpleName() + "# rp: " + this.providerHandler.getResourceProvider() + "]";
}
}