blob: cedcf4d291098a93c3bbe544b2f62281de51456c [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.
*
*/
/* $Id$ */
package org.apache.lenya.ac.file;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.parameters.ParameterException;
import org.apache.avalon.framework.parameters.Parameterizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.util.NetUtils;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceUtil;
import org.apache.lenya.ac.AccessControlException;
import org.apache.lenya.ac.Accreditable;
import org.apache.lenya.ac.AccreditableManager;
import org.apache.lenya.ac.Credential;
import org.apache.lenya.ac.Identity;
import org.apache.lenya.ac.InheritingPolicyManager;
import org.apache.lenya.ac.ModifiablePolicy;
import org.apache.lenya.ac.Policy;
import org.apache.lenya.ac.Role;
import org.apache.lenya.ac.cache.CachingException;
import org.apache.lenya.ac.cache.SourceCache;
import org.apache.lenya.ac.impl.CredentialImpl;
import org.apache.lenya.ac.impl.DefaultPolicy;
import org.apache.lenya.ac.impl.PolicyBuilder;
import org.apache.lenya.ac.impl.RemovedAccreditablePolicyBuilder;
import org.apache.lenya.ac.impl.URLPolicy;
import org.apache.lenya.xml.DocumentHelper;
import org.w3c.dom.Document;
/**
* A PolicyBuilder is used to build policies.
*/
public class FilePolicyManager extends AbstractLogEnabled implements InheritingPolicyManager,
Parameterizable, Disposable, Serviceable {
private static final class SubtreeFileFilter implements FileFilter {
private final String subtree;
private SubtreeFileFilter(String _subtree) {
super();
this.subtree = _subtree;
}
/**
* @see java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
return file.getName().equals(this.subtree);
}
}
private static final class IsDirectoryFileFilter implements FileFilter {
/**
* @see java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
return file.isDirectory();
}
}
/**
* Creates a new FilePolicyManager.
*/
public FilePolicyManager() {
// do nothing
}
/**
* Returns the source cache.
*
* @return A source cache.
*/
protected SourceCache getCache() {
return this.cache;
}
private SourceCache cache;
protected static final String SUBTREE_FILENAME = "subtree-policy.acml";
/**
* Builds a subtree policy from a file. When the file is not present, an
* empty policy is returned.
*
* @param controller The access controller to use.
* @param url The URL inside the web application.
* @return A policy.
* @throws AccessControlException when something went wrong.
*/
public Policy buildSubtreePolicy(AccreditableManager controller, String url)
throws AccessControlException {
return buildPolicy(controller, url, SUBTREE_FILENAME);
}
/**
* Builds a policy from a file. When the file is not present, an empty
* policy is returned.
*
* @param controller The access controller to use.
* @param url The url.
* @param policyFilename The policy filename.
* @return A policy.
* @throws AccessControlException when something went wrong.
*/
protected DefaultPolicy buildPolicy(AccreditableManager controller, String url,
String policyFilename) throws AccessControlException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Building policy for URL [" + url + "]");
}
DefaultPolicy policy = null;
String policyUri = getPolicySourceURI(url, policyFilename);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Policy source URI resolved to: " + policyUri);
}
try {
PolicyBuilder builder = new PolicyBuilder(controller);
policy = (DefaultPolicy) getCache().get(policyUri, builder);
} catch (CachingException e) {
throw new AccessControlException(e);
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Policy exists: [" + (policy != null) + "]");
}
if (policy == null) {
policy = new DefaultPolicy();
}
return policy;
}
/**
* Returns the policy file URI for a URL and a policy filename.
*
* @param url The url to get the file for.
* @param policyFilename The name of the policy file.
* @return A String.
* @throws AccessControlException if an error occurs
*/
protected String getPolicySourceURI(String url, String policyFilename)
throws AccessControlException {
if (url.startsWith("/")) {
url = url.substring(1);
}
// remove publication ID
if (url.indexOf("/") > -1) {
url = url.substring(url.indexOf("/") + 1);
}
else {
url = "";
}
final String policyUri = this.policiesDirectoryUri + "/" + url + "/" + policyFilename;
if (getLogger().isDebugEnabled()) {
getLogger().debug("Computing policy URI [" + policyUri + "]");
}
return policyUri;
}
/**
* Returns the policy file for a certain URL.
*
* @param url The URL to get the policy for.
* @param policyFilename The policy filename.
* @return A file.
* @throws AccessControlException when an error occurs.
*/
protected File getPolicyFile(String url, String policyFilename) throws AccessControlException {
String fileUri = getPolicySourceURI(url, policyFilename);
SourceResolver resolver = null;
Source source = null;
try {
resolver = (SourceResolver) this.serviceManager.lookup(SourceResolver.ROLE);
source = resolver.resolveURI(fileUri);
return SourceUtil.getFile(source);
} catch (final Exception e) {
throw new AccessControlException(e);
}
finally {
if (resolver != null) {
if (source != null) {
resolver.release(source);
}
this.serviceManager.release(resolver);
}
}
}
/**
* Saves a Subtree policy.
*
* @param url The url to save the policy for.
* @param policy The policy to save.
* @throws AccessControlException when something went wrong.
*/
public void saveSubtreePolicy(String url, Policy policy) throws AccessControlException {
getLogger().debug("Saving subtree policy for URL [" + url + "]");
savePolicy(url, policy, SUBTREE_FILENAME);
}
/**
* Saves a policy to a file.
*
* @param url The URL to save the policy for.
* @param policy The policy.
* @param filename The file.
* @throws AccessControlException if something goes wrong.
*/
protected void savePolicy(String url, Policy policy, String filename)
throws AccessControlException {
File file = getPolicyFile(url, filename);
savePolicy(policy, file);
}
/**
* Saves a policy to a file.
*
* @param policy The policy to save.
* @param file The file.
* @throws AccessControlException when an error occurs.
*/
protected void savePolicy(Policy policy, File file) throws AccessControlException {
Document document = PolicyBuilder.savePolicy(policy);
try {
if (!file.exists()) {
file.getParentFile().mkdirs();
if (!file.createNewFile()) {
throw new AccessControlException("File [" + file + "] could not be created.");
}
}
DocumentHelper.writeDocument(document, file);
} catch (AccessControlException e) {
throw e;
} catch (Exception e) {
throw new AccessControlException("Path: [" + file.getAbsolutePath() + "]", e);
}
}
/**
* @see org.apache.lenya.ac.PolicyManager#getPolicy(org.apache.lenya.ac.AccreditableManager,
* java.lang.String)
*/
public Policy getPolicy(AccreditableManager controller, String url)
throws AccessControlException {
return new URLPolicy(controller, url, this);
}
protected static final String DIRECTORY_PARAMETER = "directory";
private String policiesDirectoryUri;
private File policiesDirectory;
/**
* @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters)
*/
public void parameterize(Parameters parameters) throws ParameterException {
if (parameters.isParameter(DIRECTORY_PARAMETER)) {
this.policiesDirectoryUri = parameters.getParameter(DIRECTORY_PARAMETER);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Policies directory URI: " + this.policiesDirectoryUri);
}
}
}
/**
* Get the path to the policies directory.
*
* @return the path to the policies directory
* @throws AccessControlException if an error occurs
*/
public File getPoliciesDirectory() throws AccessControlException {
if (this.policiesDirectory == null) {
SourceResolver resolver = null;
Source source = null;
File directory;
try {
resolver = (SourceResolver) getServiceManager().lookup(SourceResolver.ROLE);
source = resolver.resolveURI(this.policiesDirectoryUri);
getLogger().debug("Policies directory source: [" + source.getURI() + "]");
directory = new File(new URI(NetUtils.encodePath(source.getURI())));
} catch (final Exception e) {
throw new AccessControlException("Resolving policies directory failed: ", e);
} finally {
if (resolver != null) {
if (source != null) {
resolver.release(source);
}
getServiceManager().release(resolver);
}
}
getLogger().debug(
"Policies directory resolved to [" + directory.getAbsolutePath() + "]");
setPoliciesDirectory(directory);
}
return this.policiesDirectory;
}
/**
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager manager) throws ServiceException {
this.serviceManager = manager;
this.cache = (SourceCache) manager.lookup(SourceCache.ROLE);
}
/**
* Sets the policies directory.
*
* @param directory The directory.
* @throws AccessControlException if the directory is not a directory
*/
public void setPoliciesDirectory(File directory) throws AccessControlException {
getLogger().debug("Setting policies directory [" + directory.getAbsolutePath() + "]");
if (!directory.isDirectory()) {
throw new AccessControlException("Policies directory invalid: ["
+ directory.getAbsolutePath() + "]");
}
this.policiesDirectory = directory;
}
/**
* @see org.apache.lenya.ac.InheritingPolicyManager#getPolicies(org.apache.lenya.ac.AccreditableManager,
* java.lang.String)
*/
public Policy[] getPolicies(AccreditableManager controller, String url)
throws AccessControlException {
if (!url.startsWith("/")) {
throw new IllegalArgumentException("The URL [" + url + "] doesn't start with a slash!");
}
url = url.substring(1);
List policies = new LinkedList();
HashMap orderedPolicies = new LinkedHashMap();
int position = 1;
Policy policy;
String[] directories = url.split("/");
url = directories[0] + "/";
for (int i = 1; i < directories.length; i++) {
url += directories[i] + "/";
policy = buildSubtreePolicy(controller, url);
orderedPolicies.put(String.valueOf(position), policy);
position++;
}
for (int i = orderedPolicies.size(); i > 0; i--) {
policies.add(orderedPolicies.get(String.valueOf(i)));
}
return (DefaultPolicy[]) policies.toArray(new DefaultPolicy[policies.size()]);
}
/**
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
if (getCache() != null) {
getServiceManager().release(getCache());
}
if (getLogger().isDebugEnabled()) {
getLogger().debug("Disposing [" + this + "]");
}
}
/**
* Removes an accreditable from all policies within a certain directory
* tree.
*
* @param manager The accreditable manager which owns the accreditable.
* @param accreditable The accreditable to remove.
* @param policyDirectory The directory where the policies are located.
* @throws AccessControlException when an error occurs.
*/
protected void removeAccreditable(AccreditableManager manager, Accreditable accreditable,
File policyDirectory) throws AccessControlException {
File[] policyFiles = policyDirectory.listFiles(new SubtreeFileFilter(SUBTREE_FILENAME));
try {
RemovedAccreditablePolicyBuilder builder = new RemovedAccreditablePolicyBuilder(manager);
builder.setRemovedAccreditable(accreditable);
for (int i = 0; i < policyFiles.length; i++) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Removing roles");
getLogger().debug(" Accreditable: [" + accreditable + "]");
getLogger().debug(
" File: [" + policyFiles[i].getAbsolutePath() + "]");
}
InputStream stream = new FileInputStream(policyFiles[i]);
ModifiablePolicy policy = builder.buildPolicy(stream);
policy.removeRoles(accreditable);
savePolicy(policy, policyFiles[i]);
}
} catch (final FileNotFoundException e1) {
throw new AccessControlException(e1);
}
File[] directories = policyDirectory.listFiles(new IsDirectoryFileFilter());
for (int i = 0; i < directories.length; i++) {
removeAccreditable(manager, accreditable, directories[i]);
}
}
/**
* @see org.apache.lenya.ac.PolicyManager#accreditableRemoved(org.apache.lenya.ac.AccreditableManager,
* org.apache.lenya.ac.Accreditable)
*/
public void accreditableRemoved(AccreditableManager manager, Accreditable accreditable)
throws AccessControlException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("An accreditable was removed: [" + accreditable + "]");
}
removeAccreditable(manager, accreditable, getPoliciesDirectory());
}
private ServiceManager serviceManager;
/**
* Returns the service manager.
*
* @return A service manager.
*/
protected ServiceManager getServiceManager() {
return this.serviceManager;
}
/**
* @see org.apache.lenya.ac.PolicyManager#accreditableAdded(org.apache.lenya.ac.AccreditableManager,
* org.apache.lenya.ac.Accreditable)
*/
public void accreditableAdded(AccreditableManager manager, Accreditable accreditable)
throws AccessControlException {
}
public Credential[] getCredentials(AccreditableManager controller, String url)
throws AccessControlException {
if (!url.startsWith("/")) {
throw new IllegalArgumentException("The URL [" + url + "] doesn't start with a slash!");
}
url = url.substring(1);
HashMap orderedCredential = new LinkedHashMap();
int position = 1;
String[] directories = url.split("/");
url = directories[0] + "/";
for (int i = 1; i < directories.length; i++) {
url += directories[i] + "/";
Policy policy = buildSubtreePolicy(controller, url);
Credential[] tmp = policy.getCredentials();
// we need to revert the order of the credentials
// to keep the most important policy on top
for (int j = tmp.length - 1; j >= 0; j--) {
Credential credential = tmp[j];
orderedCredential.put(String.valueOf(position), credential);
position++;
}
}
Credential[] returnCredential = new CredentialImpl[orderedCredential.size()];
int y = 0;
for (int i = orderedCredential.size(); i > 0; i--) {
returnCredential[y] = (Credential) orderedCredential.get(String.valueOf(i));
y++;
}
return returnCredential;
}
public Role[] getGrantedRoles(AccreditableManager accreditableManager, Identity identity,
String url) throws AccessControlException {
Role[] roles = accreditableManager.getRoleManager().getRoles();
Set grantedRoles = new HashSet();
Policy policy = getPolicy(accreditableManager, url);
for (int i = 0; i < roles.length; i++) {
if (policy.check(identity, roles[i]) == Policy.RESULT_GRANTED) {
grantedRoles.add(roles[i]);
}
}
return (Role[]) grantedRoles.toArray(new Role[grantedRoles.size()]);
}
}