blob: 38d27c4c8809ef76a8f5dc8a475f220b61bb1c18 [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 javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
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 {@link ResourceProvider}.
*/
public class AuthenticatedResourceProvider {
private static final Logger logger = LoggerFactory.getLogger(ResourceResolverImpl.class);
public static final AuthenticatedResourceProvider UNAUTHENTICATED_PROVIDER = new AuthenticatedResourceProvider(null, false, null, null);
private final ResourceProviderHandler providerHandler;
private final ResolveContext<Object> resolveContext;
private final ResourceAccessSecurityTracker tracker;
private final boolean useRAS;
public AuthenticatedResourceProvider(@Nonnull final ResourceProviderHandler providerHandler,
final boolean useRAS,
@Nonnull final ResolveContext<Object> resolveContext,
@Nonnull final ResourceAccessSecurityTracker tracker) {
this.providerHandler = providerHandler;
this.resolveContext = resolveContext;
this.tracker = tracker;
this.useRAS = useRAS;
}
/**
* Get the resolve context.
* @return The resolve context
*/
public @Nonnull ResolveContext<Object> getResolveContext() {
return this.resolveContext;
}
/**
* #see {@link ResourceProvider#refresh(ResolveContext)}
*/
public void refresh() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.refresh(this.resolveContext);
}
}
/**
* #see {@link ResourceProvider#isLive(ResolveContext)}
*/
public boolean isLive() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.isLive(this.resolveContext);
}
return false;
}
/**
* #see {@link 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 {@link 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 {@link 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 {@link 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 {@link 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 {@link 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 {@link 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 {@link ResourceProvider#revert(ResolveContext)}
*/
public void revert() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.revert(this.resolveContext);
}
}
/**
* #see {@link ResourceProvider#commit(ResolveContext)}
*/
public void commit() throws PersistenceException {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
rp.commit(this.resolveContext);
}
}
/**
* #see {@link ResourceProvider#hasChanges(ResolveContext)}
*/
public boolean hasChanges() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.hasChanges(this.resolveContext);
}
return false;
}
/**
* #see {@link ResourceProvider#getQueryLanguageProvider()}
*/
private QueryLanguageProvider<Object> getQueryLanguageProvider() {
final ResourceProvider<Object> rp = this.providerHandler.getResourceProvider();
if ( rp != null ) {
return rp.getQueryLanguageProvider();
}
return null;
}
/**
* #see {@link QueryLanguageProvider#getSupportedLanguages(ResolveContext)}
*/
public String[] getSupportedLanguages() {
final QueryLanguageProvider<Object> jcrQueryProvider = getQueryLanguageProvider();
if (jcrQueryProvider == null) {
return null;
}
return jcrQueryProvider.getSupportedLanguages(this.resolveContext);
}
/**
* #see {@link QueryLanguageProvider}{@link #findResources(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 {@link 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 {@link 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 {@link 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 {@link 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 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 @CheckForNull Resource wrapResource(@CheckForNull 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() + "]";
}
}