/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/security/SecurityImpl.java,v 1.53.2.5 2004/11/24 11:40:00 ozeigermann Exp $
* $Revision: 1.53.2.5 $
* $Date: 2004/11/24 11:40:00 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* Licensed 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.slide.security;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.store.Store;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.LinkNode;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.Configuration;
import org.apache.slide.util.XMLValue;
import org.apache.slide.util.logger.Logger;
import org.apache.slide.event.VetoException;
import org.apache.slide.event.EventDispatcher;
import org.apache.slide.event.SecurityEvent;
import org.jdom.JDOMException;
/**
* Security helper.
*
* @version $Revision: 1.53.2.5 $
*/
public class SecurityImpl implements Security {
private static final String LOG_CHANNEL = SecurityImpl.class.getName();
private static final String PRIVILEGE_MEMBER_SET = "privilege-member-set";
private static final String PRIVILEGE_NAMESPACE = "privilege-namespace";
protected Logger logger;
/**
* Constructor.
*/
public SecurityImpl() {}
/**
* Constructor.
*
* @param namespace Namespace
* @param namespaceConfig Namespace configuration
*/
public SecurityImpl(Namespace namespace, NamespaceConfig namespaceConfig) {
init(namespace, namespaceConfig);
}
public void init(Namespace namespace, NamespaceConfig namespaceConfig) {
this.namespace = namespace;
this.namespaceConfig = namespaceConfig;
this.rolesCache = new Hashtable();
aclInheritanceType = namespaceConfig.getAclInheritanceType();
logger = namespace.getLogger();
}
// ----------------------------------------------------- Instance Variables
/**
* Namespace.
*/
protected Namespace namespace;
/**
* Namespace configuration.
*/
protected NamespaceConfig namespaceConfig;
/**
* Roles cache.
* Role name -> Role interface.
*/
protected Hashtable rolesCache;
protected int aclInheritanceType;
/**
* The actions that can modify objects in the repository. These are used
* to determine actions cache invalidation.
*/
private Set modificationActions;
// ------------------------------------------------------- Security Methods
/**
* Set a new set of permissions on an object.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void setPermissions(SlideToken token, String object,
Enumeration permissions)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException {
Uri objectUri = namespace.getUri(token, object);
ObjectNode objectNode = objectUri.getStore().retrieveObject(objectUri);
checkCredentials(token, objectNode,
namespaceConfig.getGrantPermissionAction());
checkCredentials(token, objectNode,
namespaceConfig.getRevokePermissionAction());
objectUri.getStore().revokePermissions(objectUri);
while (permissions.hasMoreElements()) {
NodePermission permission =
(NodePermission) permissions.nextElement();
objectUri.getStore().grantPermission(objectUri, permission);
}
}
/**
* Grants a new permission.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @param subject The subject to whom the permission is granted.
* @param action The action which the subject can perform
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void grantPermission(SlideToken token, ObjectNode object,
SubjectNode subject, ActionNode action)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
grantPermission(token, object, subject, action, true);
}
/**
* Grants a new permission.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @param subject Subject who can perform the action
* @param action Action which can be performed
* @param inheritable Create an inheritable permission
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void grantPermission(SlideToken token, ObjectNode object,
SubjectNode subject, ActionNode action,
boolean inheritable)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
NodePermission permission = new NodePermission(object, subject, action,
inheritable);
grantPermission(token, permission);
}
/**
* Grants a new permission.
*
* @param token Credentials token
* @param permission New permission
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void grantPermission(SlideToken token,
NodePermission permission)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
Uri objectUri = namespace.getUri(token, permission.getObjectUri());
ObjectNode object = objectUri.getStore()
.retrieveObject(objectUri);
// Checking if the permission is already present
Enumeration permissions = enumeratePermissions(token, object);
boolean alreadyPresent = false;
while (permissions.hasMoreElements() && !alreadyPresent) {
if (permission.equals(permissions.nextElement())) {
alreadyPresent = true;
}
}
if (!alreadyPresent) {
checkCredentials(token, object,
namespaceConfig.getGrantPermissionAction());
objectUri.getStore().grantPermission(objectUri, permission);
}
// Fire event
if ( permission.isNegative() ) {
if ( SecurityEvent.DENY_PERMISSION.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(SecurityEvent.DENY_PERMISSION, new SecurityEvent(this, token, namespace, objectUri, permission));
} else {
if ( SecurityEvent.GRANT_PERMISSION.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(SecurityEvent.GRANT_PERMISSION, new SecurityEvent(this, token, namespace, objectUri, permission));
}
}
/**
* Deny a new permission.
*
* @param token Credentials token
* @param object Object on which permission is denied
* @param subject The subject to whom a action is denied
* @param action The action which is denied
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void denyPermission(SlideToken token, ObjectNode object,
SubjectNode subject, ActionNode action)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
denyPermission(token, object, subject, action, true);
}
/**
* Deny a new permission.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @param subject Subject who can perform the action
* @param action Action which can be performed
* @param inheritable Create an inheritable permission
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void denyPermission(SlideToken token, ObjectNode object,
SubjectNode subject, ActionNode action,
boolean inheritable)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
NodePermission permission = new NodePermission(object, subject, action,
inheritable, true);
denyPermission(token, permission);
}
/**
* Deny a new permission.
*
* @param token Credentials token
* @param permission New permission
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void denyPermission(SlideToken token,
NodePermission permission)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
// Make sure the permission we're about to set is indeed a negative
// permission
if (!permission.isNegative())
permission.setNegative(true);
grantPermission(token, permission);
}
/**
* Revokes a permission.
*
* @param token Credentials token
* @param object Object on which permission is revoked
* @param subject Subject who can perform the action
* @param action Action which can be performed
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void revokePermission(SlideToken token, ObjectNode object,
SubjectNode subject, ActionNode action)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
//Domain.info("Revoke permission on " + object.getUri());
checkCredentials(token, object, namespaceConfig
.getRevokePermissionAction());
NodePermission permission = new NodePermission(object, subject,
action);
Uri objectUri = namespace.getUri(token, object.getUri());
objectUri.getStore()
.revokePermission(objectUri, permission);
// Fire event
if ( SecurityEvent.REVOKE_PERMISSION.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(SecurityEvent.REVOKE_PERMISSION, new SecurityEvent(this, token, namespace, objectUri, permission));
}
/**
* Revokes a permission.
*
* @param token Credentials token
* @param permission Permission to be removed
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void revokePermission(SlideToken token, NodePermission permission)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException, VetoException {
Uri objectUri = namespace.getUri(token, permission.getObjectUri());
ObjectNode object = objectUri.getStore().retrieveObject(objectUri);
checkCredentials(token, object,
namespaceConfig.getRevokePermissionAction());
objectUri.getStore().revokePermission(objectUri, permission);
// Fire event
if ( SecurityEvent.REVOKE_PERMISSION.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(SecurityEvent.REVOKE_PERMISSION, new SecurityEvent(this, token, namespace, objectUri, permission));
}
/**
* Check if the credentials given grants permission to perform
* the specified action on the specified subject.
*
* @param token Credentials token
* @param object Object on which the action is performed
* @param action Action performed
* @exception ServiceAccessException DataSource access error
* @exception AccessDeniedException The credentials does not grant
* the permission to perform the specified action
*/
public void checkCredentials(SlideToken token, ObjectNode object,
ActionNode action)
throws ServiceAccessException, AccessDeniedException {
if (!token.isForceSecurity()) {
return;
}
try {
if (Configuration.useIntegratedSecurity()) {
// check if permission has already been checked
Boolean permission = token.checkPermissionCache(object, action);
if (permission == null) {
// if not checked before, check now
try {
Uri objectUri = namespace.getUri(token, object.getUri());
ObjectNode realObject = objectUri.getStore()
.retrieveObject(objectUri);
checkPermission(token, realObject, action);
token.cachePermission(object, action, true);
} catch (AccessDeniedException ade) {
token.cachePermission(object, action, false);
ade.fillInStackTrace();
throw ade;
}
}
else {
if (!(permission.booleanValue())) {
throw new AccessDeniedException
(object.getUri(),
getPrincipal(token).getPath().toString(),
action.getUri());
}
}
}
} catch (ObjectNotFoundException e) {
String subjectUri = "*** Could not determine principal ***";
try {
subjectUri = getPrincipal(token).getPath().toString();
} catch (Exception x) {}
throw new AccessDeniedException
(object.getUri(),
subjectUri,
action.getUri());
}
}
/**
* Check whether or not an actor can perform the specified activity
* on a collection.
*
* @param object Object on which access is tested
* @param subject Subject who seeks to perform the action
* @param action Action which is to be performed
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public void checkPermission(ObjectNode object, SubjectNode subject,
ActionNode action)
throws ServiceAccessException, AccessDeniedException,
ObjectNotFoundException {
if (!hasPermission(object, subject, action)) {
throw new AccessDeniedException(object.getUri(), subject.getUri(),
action.getUri());
}
determineActionsCacheInvalidation(object, action);
}
/**
* Check whether or not an actor (principal) can perform the specified activity
* on the specified resource.
*
* @param token a SlideToken
* @param object Object on which access is tested
* @param action Action which is to be performed
*
* @throws ServiceAccessException
* @throws AccessDeniedException
* @throws ObjectNotFoundException
*/
public void checkPermission(SlideToken token, ObjectNode object, ActionNode action) throws ServiceAccessException, AccessDeniedException, ObjectNotFoundException {
if (!hasPermission(token, object, action)) {
throw new AccessDeniedException(object.getUri(), getPrincipal(token).getUri(),
action.getUri());
}
determineActionsCacheInvalidation(object, action);
}
/**
* Check whether or not an actor can perform the specified activity
* on a collection.
*
* @param object Object on which access is tested
* @param subject Subject who seeks to perform the action
* @param action Action which is to be performed
* @return true if the action can be performed
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
*/
public boolean hasPermission(ObjectNode object, SubjectNode subject,
ActionNode action)
throws ServiceAccessException, ObjectNotFoundException {
// no check for default action (server intitialization)
if (action.equals(ActionNode.DEFAULT)) {
return true;
}
boolean granted = false;
boolean denied = false;
boolean rootObjectReached = false;
ObjectNode courObject = object;
Uri subjectUri = namespace.getUri(subject.getUri());
Uri actionUri = namespace.getUri(action.getUri());
// check if allready granded
while (!granted && !denied && !rootObjectReached) {
Uri courUri = namespace.getUri(courObject.getUri());
Enumeration permissions = courUri.getStore()
.enumeratePermissions(courUri);
while (permissions.hasMoreElements()) {
boolean oldGranted = granted;
boolean oldDenied = denied;
NodePermission permission =
(NodePermission) permissions.nextElement();
String permissionSubject = permission.getSubjectUri();
if (permissionSubject.equals(SubjectNode.SELF_URI)) {
boolean check;
check = object.getUri().equals(subjectUri.toString());
if (permission.isInheritable()) {
String subjectUriString = subjectUri.toString();
if(!subjectUriString.endsWith("/"))
subjectUriString = subjectUriString + "/";
check |= object.getUri().startsWith(subjectUriString);
}
// Self permission
granted = (!permission.isNegative())
&& (check)
&& (actionUri.toString()
.startsWith(permission.getActionUri()));
denied = (permission.isNegative())
&& (check)
&& (actionUri.toString()
.startsWith(permission.getActionUri()));
} else if (permission.isInheritable()
|| permission.getObjectUri().equals(object.getUri())) {
if (permissionSubject.startsWith("/")) {
// Node permission
String permSubj = permission.getSubjectUri();
if(!permSubj.endsWith("/"))
permSubj = permSubj + "/";
boolean match = subjectUri.toString().
equals(permission.getSubjectUri()) ||
subjectUri.toString().startsWith(permSubj);
match &= actionUri.toString().
startsWith(permission.getActionUri());
granted = (!permission.isNegative()) && match;
denied = permission.isNegative() && match;
} else if (permissionSubject.startsWith("+")) {
// Permission group which needs to be expanded
Uri permissionSubjectUri =
namespace.getUri(permissionSubject.substring(1));
ObjectNode group =
permissionSubjectUri.getStore().retrieveObject
(permissionSubjectUri);
// if the node is a GroupNode, expand it out to
// normal permissions
if (group instanceof
org.apache.slide.structure.GroupNode ) {
if (group.hasChildren()) {
Enumeration groupMembers =
group.enumerateChildren();
// parse thru the children of the group and
// check permissions on each
while (groupMembers.hasMoreElements()) {
oldGranted = granted;
oldDenied = denied;
Uri childUri =
namespace.getUri
((String) groupMembers.nextElement());
ObjectNode childNode =
childUri.getStore().retrieveObject
(childUri);
String childSubjectUri = childNode
instanceof LinkNode ?
((LinkNode) childNode)
.getLinkedUri() :
childNode.getUri() ;
String testUri;
if(!childSubjectUri.endsWith("/"))
testUri = childSubjectUri+"/";
else
testUri = childSubjectUri;
boolean match = subjectUri.toString().
equals(childSubjectUri) ||
subjectUri.toString().
startsWith(testUri);
match &= actionUri.toString().
startsWith(permission.getActionUri());
granted = (!permission.isNegative()) &&
match;
denied = permission.isNegative() && match;
granted = granted | oldGranted;
denied = denied | oldDenied;
}
}
}
} else {
// Role permission
granted = (!permission.isNegative())
&& (hasRole(subject, permissionSubject))
&& (actionUri.toString()
.startsWith(permission.getActionUri()));
denied = (permission.isNegative())
&& (hasRole(subject, permissionSubject))
&& (actionUri.toString()
.startsWith(permission.getActionUri()));
}
}
granted = granted | oldGranted;
denied = denied | oldDenied;
}
Uri parentUri = courUri.getParentUri();
if (parentUri != null) {
courObject = parentUri.getStore()
.retrieveObject(parentUri);
} else {
rootObjectReached = true;
}
}
// Negative permissions have priority (if they're defined on the same
// node)
if (denied) {
return false;
}
if (!granted) {
return false;
}
return true;
}
/**
* Check whether or not an actor (principal) can perform the specified activity
* on the specified resource.
*
* @param token a SlideToken
* @param object Object on which access is tested
* @param action Action which is to be performed
*
* @return true if the action can be performed
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public boolean hasPermission(SlideToken token, ObjectNode object, ActionNode action) throws ServiceAccessException, ObjectNotFoundException {
Boolean cachedPermission = token.checkPermissionCache(object, action);
if (cachedPermission != null) {
return cachedPermission.booleanValue();
}
else {
return hasPermission(object, (SubjectNode)getPrincipal(token), action);
}
}
/**
* Enumerates permissions on an object.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public Enumeration enumeratePermissions(SlideToken token,
ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException {
return enumeratePermissions(token, object, false);
}
/**
* Enumerates permissions on an object.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public Enumeration enumeratePermissions(SlideToken token,
ObjectNode object,
boolean includeInherited)
throws ServiceAccessException, ObjectNotFoundException {
return enumeratePermissions(token, object.getUri(), includeInherited);
}
/**
* Enumerates permissions on an object.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public Enumeration enumeratePermissions(SlideToken token,
String object)
throws ServiceAccessException, ObjectNotFoundException,
AccessDeniedException {
return enumeratePermissions(token, object, false);
}
/**
* Enumerates permissions on an object.
*
* @param token Credentials token
* @param object Object on which permission is granted
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
* @exception AccessDeniedException Insufficent credentials
*/
public Enumeration enumeratePermissions(SlideToken token,
String object,
boolean includeInherited)
throws ServiceAccessException, ObjectNotFoundException {
Uri objectUri = namespace.getUri(token, object);
if (!includeInherited) {
return objectUri.getStore().enumeratePermissions(objectUri);
}
else {
ObjectNode objectNode = objectUri.getStore().retrieveObject(objectUri);
Iterator i = retrieveAclSourceNodes(token, objectNode).iterator();
Vector permissions = new Vector();
while (i.hasNext()) {
ObjectNode oNode = (ObjectNode)i.next();
Uri oUri = namespace.getUri(token, oNode.getUri());
Enumeration permEnum = oUri.getStore().enumeratePermissions(oUri);
while (permEnum.hasMoreElements()) {
NodePermission perm = (NodePermission)permEnum.nextElement();
if (object.equals(oNode.getUri())) {
// FIXME A node should not be able to inherit permissions from itself.
// Hide this from the client when it happens.
perm.setInheritedFrom(null);
permissions.add(perm);
}
else if (perm.isInheritable()) {
perm.setInheritedFrom(oNode.getUri());
permissions.add(perm);
}
}
}
return permissions.elements();
}
}
/**
* Retrieve the list of object nodes from which to get the ACLs.
* The specified object is returned as first element of the list.
*
* @param token a SlideToken
* @param object an ObjectNode
*
* @return a List of ObjectNode
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public List retrieveAclSourceNodes(SlideToken token, ObjectNode object)
throws ServiceAccessException, ObjectNotFoundException {
List result = new ArrayList();
Uri uri;
switch (aclInheritanceType) {
case NamespaceConfig.ACL_INHERIT_TYPE_NONE:
uri = namespace.getUri(token, object.getUri());
result.add( uri.getStore().retrieveObject(uri) );
break;
case NamespaceConfig.ACL_INHERIT_TYPE_ROOT:
uri = namespace.getUri(token, object.getUri());
Uri rootUri = namespace.getUri(token, uri.getScope().toString());
result.add( uri.getStore().retrieveObject(uri) );
result.add( rootUri.getStore().retrieveObject(rootUri) );
break;
case NamespaceConfig.ACL_INHERIT_TYPE_PATH:
uri = namespace.getUri(token, object.getUri());
Enumeration enum = uri.getScopes();
while (enum.hasMoreElements()) {
Uri element = namespace.getUri(token, (String)enum.nextElement());
ObjectNode objectNode = element.getStore().retrieveObject(element);
result.add(objectNode);
}
break;
case NamespaceConfig.ACL_INHERIT_TYPE_FULL:
//TODO
break;
default:
break;
}
return result;
}
/**
* Check whether or not the current user has the specified role.
*
* @param token Credentials token
* @param role Role
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
*/
public boolean hasRole(SlideToken token, String role)
throws ServiceAccessException, ObjectNotFoundException {
ObjectNode subject = getPrincipal(token);
return hasRole(subject, role);
}
/**
* Check whether or not the current user has the specified role.
*
* @param object Object node
* @param role Role
* @exception ServiceAccessException DataSource access error
* @exception ObjectNotFoundException Specified object was not found
* in the DataSource
*/
public boolean hasRole(ObjectNode object, String role)
throws ServiceAccessException, ObjectNotFoundException {
if (role.equals(NamespaceConfig.NOBODY))
return true;
String associatedRole = namespaceConfig.getRoleMapping(role);
if ((associatedRole != null)
&& (associatedRole.equals(NamespaceConfig.NOBODY)))
return true;
Class roleClass = (Class) rolesCache.get(role);
if ((roleClass == null) && (associatedRole != null)) {
roleClass = (Class) rolesCache.get(associatedRole);
if (roleClass == null) {
try {
roleClass = Class.forName(associatedRole);
rolesCache.put(role, roleClass);
rolesCache.put(associatedRole, roleClass);
} catch (ClassNotFoundException ex) {
}
}
}
if (roleClass == null) {
try {
roleClass = Class.forName(role);
rolesCache.put(role, roleClass);
} catch (ClassNotFoundException e) {
}
}
if (roleClass == null) {
return false;
}
if (roleClass.isInstance(object))
return true;
return false;
}
/**
* Return the list of roles the specified node has.
*
* @param object Object node
*/
public Enumeration getRoles(ObjectNode object) {
Vector result = new Vector();
result.addElement(NamespaceConfig.NOBODY);
Class currentObjectClass = object.getClass();
while (!currentObjectClass.equals(ObjectNode.class)) {
Class[] interfaces = currentObjectClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
String className = interfaces[i].getName();
String associatedName =
namespaceConfig.getRoleMapping(className);
if (associatedName != null)
result.addElement(associatedName);
else
result.addElement(className);
}
currentObjectClass = currentObjectClass.getSuperclass();
if (currentObjectClass == null) {
// Illegal state
throw new IllegalStateException("Invalid node");
}
}
return result.elements();
}
/**
* Return the list of roles the specified token has.
*
* @param token Credentials token
*/
public Enumeration getRoles(SlideToken token)
throws ServiceAccessException, ObjectNotFoundException {
return getRoles(getPrincipal(token));
}
public Enumeration getRoles(SlideToken token, SubjectNode subjectNode)
throws ServiceAccessException, ObjectNotFoundException {
return getRoles(subjectNode);
}
/**
* Get the SubjectNode associated with the credentials token.
*
* @param token a SlideToken
* @return an ObjectNode
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public ObjectNode getPrincipal(SlideToken token)
throws ServiceAccessException, ObjectNotFoundException {
String user =
token.getCredentialsToken().getPublicCredentials();
if ((user == null) || user.equals("") || user.equals("/")) {
return SubjectNode.UNAUTHENTICATED;
}
Uri subjectUri = namespace.getUri
(token, namespaceConfig.getUsersPath()+"/"+user);
try {
return subjectUri.getStore().retrieveObject(subjectUri);
} catch (ObjectNotFoundException e) {
if (!namespaceConfig.isAutoCreateUsers()) {
throw e;
}
else {
try {
Uri parentUri = subjectUri.getParentUri();
ObjectNode parent =
subjectUri.getStore().retrieveObject(parentUri);
Enumeration childrenEnum = parent.enumerateChildren();
Enumeration linksEnum = parent.enumerateLinks();
Vector children = new Vector();
while (childrenEnum.hasMoreElements()) {
children.addElement(childrenEnum.nextElement());
}
children.addElement(subjectUri.toString());
Vector links = new Vector();
while (linksEnum.hasMoreElements()) {
links.addElement(linksEnum.nextElement());
}
// First, load the object's class
Class objectClass = Class.forName
(namespaceConfig.getAutoCreateUsersRole());
Class[] types = { String.class };
Object[] args = { subjectUri.toString() };
Constructor constructor =
objectClass.getConstructor(types);
ObjectNode object =
(ObjectNode) constructor.newInstance(args);
subjectUri.getStore().createObject(subjectUri, object);
Class[] types2 =
{ String.class, Vector.class, Vector.class };
Object[] args2 = { parentUri.toString(), children, links };
constructor = parent.getClass().getConstructor(types2);
object = (ObjectNode) constructor.newInstance(args2);
parentUri.getStore().storeObject(parentUri, object);
NodeRevisionNumber initialRevision = new NodeRevisionNumber(1, 0); // new NodeRevisionNumber();
Hashtable workingRevisions = new Hashtable();
workingRevisions.put(NodeRevisionDescriptors.MAIN_BRANCH, initialRevision);
Hashtable latestRevisionNumbers = new Hashtable();
latestRevisionNumbers.put(NodeRevisionDescriptors.MAIN_BRANCH, initialRevision);
Hashtable branches = new Hashtable();
branches.put(initialRevision, new Vector());
boolean isVersioned = false;
NodeRevisionDescriptors revisionDescriptors = new NodeRevisionDescriptors(subjectUri.toString(),
initialRevision, workingRevisions, latestRevisionNumbers, branches, isVersioned);
NodeRevisionDescriptor revisionDescriptor = new NodeRevisionDescriptor(initialRevision,
NodeRevisionDescriptors.MAIN_BRANCH, new Vector(), new Hashtable());
subjectUri.getStore().createRevisionDescriptors(subjectUri, revisionDescriptors);
subjectUri.getStore().createRevisionDescriptor(subjectUri, revisionDescriptor);
} catch (ClassNotFoundException ex) {
// Can't find role implementing class
throw new ObjectNotFoundException(subjectUri);
} catch (NoSuchMethodException ex) {
// Can't find appropriate constructor
throw new ObjectNotFoundException(subjectUri);
} catch (InstantiationException ex) {
// Can't instatiate object
throw new ObjectNotFoundException(subjectUri);
} catch (InvocationTargetException ex) {
// Can't invoke constructor
throw new ObjectNotFoundException(subjectUri);
} catch (IllegalAccessException ex) {
// Constructor is not public
throw new ObjectNotFoundException(subjectUri);
} catch (ObjectAlreadyExistsException ex) {
// Should never happen
ex.printStackTrace();
throw new ObjectNotFoundException(subjectUri);
}
return subjectUri.getStore().retrieveObject(subjectUri);
}
}
}
/**
* Get the direct aggregates
*
* @param aNode an ActionNode
* @return set of direct aggregates (ActionNode objects)
* @throws SlideException
* @throws JDOMException
*/
private static synchronized Set getActionAggregates(SecurityImpl security, SlideToken token, ActionNode aNode) throws SlideException, JDOMException {
Set result = new HashSet();
Uri aNodeUri = security.namespace.getUri(token, aNode.getUri());
NodeRevisionDescriptor aNrd = aNodeUri.getStore().retrieveRevisionDescriptor(aNodeUri, new NodeRevisionNumber());
NodeProperty membersProp = aNrd.getProperty(PRIVILEGE_MEMBER_SET);
if (membersProp != null && membersProp.getValue() != null) {
XMLValue membersVal;
if (membersProp.getValue() instanceof XMLValue) {
membersVal = (XMLValue)membersProp.getValue();
}
else {
membersVal = new XMLValue((String)membersProp.getValue());
}
security.logger.log(membersVal.getHrefStrings(), LOG_CHANNEL, Logger.DEBUG);
Iterator mUris = membersVal.getHrefStrings().iterator();
while (mUris.hasNext()) {
String uriAsString = (String)mUris.next();
Uri uri = security.namespace.getUri(token, uriAsString);
NodeRevisionNumber latestRevisionNumber = aNodeUri.getStore().retrieveRevisionDescriptors(uri).getLatestRevision();
if (latestRevisionNumber != null) {
NodeProperty privilegeNamespace = aNodeUri.getStore().retrieveRevisionDescriptor(uri, latestRevisionNumber).getProperty(PRIVILEGE_NAMESPACE, "DAV:");
org.jdom.Namespace namespace = null;
if (privilegeNamespace != null && privilegeNamespace.getValue() instanceof String) {
namespace = org.jdom.Namespace.getNamespace((String) privilegeNamespace.getValue());
} else {
namespace = org.jdom.Namespace.getNamespace("DAV:");
}
result.add(ActionNode.getActionNode(uriAsString, namespace));
}
}
}
return result;
}
/**
* Return true, if-and-only-if checkAction matches permAction.
*
* @param checkAction the "current" action
* @param permAction the action to check against
* (from NodePermission or NodeLock)
*
* @return a boolean
*
*/
public boolean matchAction(SlideToken token, ActionNode checkAction, ActionNode permAction) throws ServiceAccessException {
if (permAction.equals(ActionNode.ALL)) {
return true;
}
Map actionAggregationClosure = getActionAggregationClosureImpl(this, token, checkAction.getUri());
Set permActionSet = (Set)actionAggregationClosure.get(permAction);
if (permActionSet == null) {
logger.log("Unknown action " + permAction.getUri() , LOG_CHANNEL, Logger.WARNING);
return false;
}
return permActionSet.contains(checkAction);
}
/**
* Return true, if-and-only-if checkSubject matches permSubject.
*
* @param token a SlideToken
* @param checkSubject the "current" principal
* @param matchSubject the principal to check against (e.g. user
* or group from NodePermission or NodeLock)
*
* @return a boolean
*
* @throws ServiceAccessException
*
*/
public boolean matchPrincipal(SlideToken token, SubjectNode checkSubject, SubjectNode matchSubject) throws ServiceAccessException {
Boolean b = token.checkMatchPrincipalCache(checkSubject, matchSubject);
if (b != null) {
return b.booleanValue();
}
else {
boolean match = matchPrincipal(token, checkSubject, matchSubject, namespaceConfig.getNestedRolesMaxDepth());
token.cacheMatchPrincipal(checkSubject, matchSubject, match);
return match;
}
}
/**
* Return true, if-and-only-if checkSubject matches permSubject.
*
* @param token a SlideToken
* @param checkSubject the "current" principal
* @param matchSubject the principal to check against (e.g. user
* or group from NodePermission or NodeLock)
*
* @return a boolean
*
* @throws ServiceAccessException
*
*/
public boolean matchPrincipal(SlideToken token, SubjectNode checkSubject, SubjectNode matchSubject, int level) throws ServiceAccessException {
if (matchSubject.equals(checkSubject)) {
return true;
}
else {
Uri groupUri = namespace.getUri(token, matchSubject.getUri());
try {
NodeRevisionDescriptor nrd =
groupUri.getStore().retrieveRevisionDescriptor(groupUri, new NodeRevisionNumber());
NodeProperty membersetProp = nrd.getProperty("group-member-set");
if (membersetProp != null && membersetProp.getValue() != null) {
XMLValue xmlVal = new XMLValue((String)membersetProp.getValue());
List memberNodes = xmlVal.getHrefNodes();
if (memberNodes.contains(checkSubject)) {
return true;
}
else if (level > 0) {
int nextLevel = level - 1;
boolean match = false;
Iterator i = memberNodes.iterator();
while (!match && i.hasNext()) {
SubjectNode nextMatchNode = (SubjectNode)i.next();
if (namespaceConfig.isRole(nextMatchNode.getUri())
|| namespaceConfig.isGroup(nextMatchNode.getUri())) {
match = matchPrincipal(token, checkSubject, nextMatchNode, nextLevel);
}
}
return match;
}
else {
return false;
}
}
else {
return false;
}
}
catch (RevisionDescriptorNotFoundException e) {
return false;
}
catch (ServiceAccessException e) {
throw e;
}
catch (JDOMException e) {
e.printStackTrace();
return false;
}
}
}
/**
* Method getActionAggregation
* @return a Map: actionNode -> Set-of-aggregated-nodes (direct aggregates)
*/
public Map getActionAggregation(SlideToken token) {
logger.log("Action aggregation being retrieved", LOG_CHANNEL, Logger.DEBUG);
return Collections.unmodifiableMap(getActionAggregationImpl(this, token));
}
/**
* Get the actions that modify objects in the repository. Which actions
* these are is defined in the configuration.
*
* @return The actions that modify objects in the repository.
*/
private synchronized Set getModificationActions() {
if (this.modificationActions == null) {
Set modificationActions = new HashSet();
modificationActions.add(namespaceConfig.getBindMemberAction());
modificationActions.add(namespaceConfig.getCreateObjectAction());
modificationActions.add(namespaceConfig.getCreateRevisionContentAction());
modificationActions.add(namespaceConfig.getCreateRevisionMetadataAction());
modificationActions.add(namespaceConfig.getModifyRevisionContentAction());
modificationActions.add(namespaceConfig.getModifyRevisionMetadataAction());
modificationActions.add(namespaceConfig.getRemoveObjectAction());
modificationActions.add(namespaceConfig.getRemoveRevisionContentAction());
modificationActions.add(namespaceConfig.getRemoveRevisionMetadataAction());
modificationActions.add(namespaceConfig.getUnbindMemberAction());
modificationActions.remove(namespaceConfig.getDefaultAction());
this.modificationActions = Collections.unmodifiableSet(modificationActions);
}
return this.modificationActions;
}
/**
* Get whether an acion can modify objects in the repository.
*
* @param action The action for which to check whether it can modify
* objects in the repository.
* @return True if the actions can modify objects in the repository,
* false otherwise.
*/
private boolean isModificationAction(ActionNode action) {
return getModificationActions().contains(action);
}
/**
* Determine whether an operation on an object in the repository
* invalidates the actions cache.
*
* @param object The subject of the operation.
* @param action The operation.
*/
private void determineActionsCacheInvalidation(ObjectNode object, ActionNode action) {
if (object.getUri().startsWith(namespaceConfig.getActionsPath())) {
if (isModificationAction(action)) {
if (logger.isEnabled(Logger.DEBUG)) {
logger.log("Actions cache invalidated for operation " + action.getUri()
+ " on action " + object.getUri(), LOG_CHANNEL, Logger.DEBUG);
}
invalidateActionsCache(this);
}
}
}
/**
* Invalidates the actions cache causing it to be reloaded the next time it
* is needed.
*/
private static synchronized void invalidateActionsCache(SecurityImpl security) {
ActionsCache cache = getActionsCache(security);
cache.invalidate();
}
private static synchronized ActionsCache getActionsCache(SecurityImpl security) {
String namespaceName = security.namespace.getName();
ActionsCache result = (ActionsCache)caches.get(namespaceName);
if (result == null)
{
result = new ActionsCache();
caches.put(namespaceName, result);
}
return result;
}
private static final Map caches = new HashMap();
/**
* Populate the actions cache.
*
* @param namespace
* @param namespaceConfig
*/
private static synchronized void loadActionsCache(SecurityImpl security, SlideToken token) {
ActionsCache cache = getActionsCache(security);
try {
cache.aggregation = new HashMap();
cache.aggregationClosure = new HashMap();
String actionsPath = security.namespaceConfig.getActionsPath();
Uri actionsPathUri = security.namespace.getUri(token, actionsPath);
ObjectNode actionsPathNode = actionsPathUri.getStore().retrieveObject(actionsPathUri);
Enumeration actions = actionsPathNode.enumerateChildren();
addActionLeafsToActionAggregation(security, token, cache, actions);
Iterator keys = cache.aggregationClosure.keySet().iterator();
while (keys.hasNext()) {
ActionNode aNode = (ActionNode)keys.next();
Set aClosure = (Set)cache.aggregationClosure.get(aNode);
cache.aggregationClosure.put(aNode, buildClosure(cache, aClosure));
}
// log success
if (security.logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
security.logger.log("Action aggregations loaded successfully", LOG_CHANNEL, Logger.DEBUG);
}
if (security.logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
security.logger.log("\n@@@ Actions aggregations", LOG_CHANNEL, Logger.DEBUG);
Iterator i = cache.aggregation.entrySet().iterator();
while (i.hasNext()) {
security.logger.log(" "+i.next(), LOG_CHANNEL, Logger.DEBUG);
}
security.logger.log("\n@@@ Action aggregations (transitive closure)", LOG_CHANNEL, Logger.DEBUG);
i = cache.aggregationClosure.entrySet().iterator();
while (i.hasNext()) {
security.logger.log(" "+i.next(), LOG_CHANNEL, Logger.DEBUG);
}
}
}
catch (Throwable e) {
security.logger.log(e, LOG_CHANNEL, Logger.ERROR);
cache.fail();
}
}
/**
* Add children of <code>actions</code> which are leafs, collections
* without children, to the action cache. Recursively invoke this method
* for each child which is not a leaf.
*
* @param actions The collection containing child nodes.
* @throws SlideException
* @throws JDOMException
*/
private static synchronized void addActionLeafsToActionAggregation(SecurityImpl security, SlideToken token, ActionsCache cache, Enumeration actions) throws SlideException, JDOMException {
while (actions.hasMoreElements()) {
Uri aNodeUri = security.namespace.getUri(token, (String)actions.nextElement());
ObjectNode oNode = security.namespace.getStore(aNodeUri.getScope()).retrieveObject(aNodeUri);
if (oNode.hasChildren()) {
if (security.logger.isEnabled(Logger.DEBUG)) {
security.logger.log("Adding children of action " + oNode.getUri() + " to action aggregation", LOG_CHANNEL, Logger.DEBUG);
}
addActionLeafsToActionAggregation(security, token, cache, oNode.enumerateChildren());
} else {
if (security.logger.isEnabled(Logger.DEBUG)) {
security.logger.log("Adding action " + oNode.getUri() + " to action aggregation", LOG_CHANNEL, Logger.DEBUG);
}
NodeRevisionNumber latestRevisionNumber = security.namespace.getStore(aNodeUri.getScope()).retrieveRevisionDescriptors(aNodeUri).getLatestRevision();
NodeProperty privilegeNamespace = security.namespace.getStore(aNodeUri.getScope()).retrieveRevisionDescriptor(aNodeUri, latestRevisionNumber).getProperty(PRIVILEGE_NAMESPACE, "DAV:");
ActionNode aNode;
org.jdom.Namespace actionNamespace;
if (privilegeNamespace != null && privilegeNamespace.getValue() instanceof String) {
actionNamespace = org.jdom.Namespace.getNamespace((String) privilegeNamespace.getValue());
} else {
actionNamespace = org.jdom.Namespace.getNamespace("DAV:");
}
aNode = ActionNode.getActionNode(oNode.getUri(), actionNamespace);
Set directAggregates = getActionAggregates(security, token, aNode);
cache.aggregation.put(aNode, directAggregates);
Set aClosure = new HashSet();
aClosure.add(aNode);
aClosure.addAll(directAggregates);
cache.aggregationClosure.put(aNode, aClosure);
}
}
}
/**
* Determines the complete set of children and grandchildren of an action.
* These are the children/grandchildren determined by the
* <code>D:privilege-member-set</code> hierarchy.
*
* @param aClosure A collection containing the action and its known
* children and grandchildren.
* @return A collection containing the action, its known and additionally
* found children and grandchildren.
*/
private static synchronized Set buildClosure(ActionsCache cache, Set aClosure) {
Set result = new HashSet(aClosure);
int size = 0;
while (result.size() > size) {
size = result.size();
Set newResult = new HashSet();
Iterator i = result.iterator();
while (i.hasNext()) {
Object member = i.next();
Set membersOfMember = (Set)cache.aggregationClosure.get(member);
if (membersOfMember != null) {
newResult.addAll(membersOfMember);
}
}
result = newResult;
}
return result;
}
/**
* Get a map which maps an action to its direct aggregated actions.
*
* If the actions cache is not loaded, an attempt will be made to load the
* actions cache.
*
* @return A map from actions to their aggregated actions, or and empty map
* if loading of the actions cache failed.
*/
private static synchronized Map getActionAggregationImpl(SecurityImpl security, SlideToken token) {
ActionsCache cache = getActionsCache(security);
if (!cache.hasBeenLoaded()) {
loadActionsCache(security, token);
}
if (cache.hasLoadingFailed) {
security.logger.log("actionAggregation retrieved but cache didn't load successfully" , LOG_CHANNEL, Logger.WARNING);
return new HashMap();
}
return cache.aggregation;
}
/**
* Get a map which maps an action to all its aggregated actions.
*
* If the actions cache is not loaded, an attempt will be made to load the
* actions cache.
*
* @return A map from actions to their aggregated actions.
* @throws ServiceAccessException Indicates the loading of the actions
* cache failed.
*/
private static synchronized Map getActionAggregationClosureImpl(SecurityImpl security, SlideToken token, String uri) throws ServiceAccessException {
ActionsCache cache = getActionsCache(security);
if (!cache.hasBeenLoaded()) {
loadActionsCache(security, token);
}
if (cache.hasLoadingFailed()) {
Uri u = security.namespace.getUri(token, uri);
Store s = u.getStore();
throw new ServiceAccessException(s, "Actions cache not loaded");
}
return cache.aggregationClosure;
}
}