/*
* $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/security/ACLSecurityImpl.java,v 1.10.2.3 2004/12/03 23:14:43 masonjm Exp $
* $Revision: 1.10.2.3 $
* $Date: 2004/12/03 23:14:43 $
*
* ====================================================================
*
* 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.util.Enumeration;
import java.util.List;
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.SlideToken;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.structure.ActionNode;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.XMLValue;
import org.apache.slide.util.logger.Logger;
import org.jdom.JDOMException;
/**
* WebDAV/ACL compliant security helper implementation (draft-12)
*
* @version $Revision: 1.10.2.3 $
*/
public class ACLSecurityImpl extends SecurityImpl {
private static final String LOG_CHANNEL = ACLSecurityImpl.class.getName();
private static final Vector EMPTY_VECTOR = new Vector();
/**
* Be sure to call init() before using an instance created with this
* constructor.
*/
public ACLSecurityImpl() {
super();
}
/**
* Constructor.
*
* @param namespace Namespace
* @param namespaceConfig Namespace configuration
*/
public ACLSecurityImpl(Namespace namespace, NamespaceConfig namespaceConfig) {
super(namespace, namespaceConfig);
}
/**
* Method init
*
* @param namespace a Namespace
* @param namespaceConfig a NamespaceConfig
*/
public void init(Namespace namespace, NamespaceConfig namespaceConfig) {
super.init(namespace, namespaceConfig);
}
/**
* 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 {
throw new UnsupportedOperationException(
"Please use alternate signature: "
+"hasPermission(SlideToken token, ObjectNode object, ActionNode action)");
}
/**
* Check whether or not an actor (principal) can perform the specified activity
* on the specified resource.
*
* @param token a SlideToken
* @param objectNode Object on which access is tested
* @param actionNode Action which is to be performed
*
* @return true if the action can be performed
*
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public boolean hasPermission(SlideToken token, ObjectNode objectNode, ActionNode actionNode)
throws ServiceAccessException, ObjectNotFoundException {
// logging
if (logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
try {
logger.log("@@@ check object="+objectNode+", subject="+getPrincipal(token)+", action="+actionNode, LOG_CHANNEL, Logger.DEBUG);
} catch (ObjectNotFoundException onfe) {
// ignore as this is logging only
}
}
// no check for default action (server intitialization)
if (actionNode == namespaceConfig.getDefaultAction()) {
return true;
}
Enumeration permissions = enumeratePermissions(token, objectNode, true);
return evaluateAcl(token, objectNode, actionNode, permissions);
}
public boolean hasRole(SlideToken token, String role) throws ServiceAccessException, ObjectNotFoundException {
return hasRole(token, (SubjectNode)getPrincipal(token), role);
}
// overwrites super
public boolean hasRole(SlideToken token, SubjectNode subjectNode, String role) throws ServiceAccessException, ObjectNotFoundException {
SubjectNode roleNode = null;
if (namespaceConfig.getRolesPath() != null && namespaceConfig.getRolesPath().length() != 0) {
roleNode = SubjectNode.getSubjectNode(namespaceConfig.getRolesPath()+"/"+role);
}
if (roleNode != null && matchPrincipal(token, subjectNode, roleNode)) {
return true;
}
else {
// check groups
SubjectNode groupNode = null;
if (namespaceConfig.getGroupsPath() != null && namespaceConfig.getGroupsPath().length() != 0) {
groupNode = SubjectNode.getSubjectNode(namespaceConfig.getGroupsPath()+"/"+role);
}
return (groupNode != null && matchPrincipal(token, subjectNode, groupNode));
}
}
// overwrites super
public boolean hasRole(ObjectNode object, String role) throws ServiceAccessException, ObjectNotFoundException {
throw new UnsupportedOperationException("Please use alternate signature: hasRole(SlideToken token, String role)");
}
// overwrites super
public Enumeration getRoles(SlideToken token) throws ServiceAccessException, ObjectNotFoundException {
return getRoles(token, (SubjectNode)getPrincipal(token));
}
/**
* Get the role names the specified subject has (deeply over nested roles, if there)
*
* @param token a SlideToken
* @param subjectNode a SubjectNode
* @return an Enumeration of role names
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public Enumeration getRoles(SlideToken token, SubjectNode subjectNode) throws ServiceAccessException, ObjectNotFoundException {
// check the principal exists
Uri subjectUri = namespace.getUri(token, subjectNode.getUri());
subjectUri.getStore().retrieveObject(subjectUri);
Vector result = new Vector();
Uri rolesUri = null;
ObjectNode rolesNode = null;
if (namespaceConfig.getRolesPath() != null && namespaceConfig.getRolesPath().length() != 0) {
rolesUri = namespace.getUri(token, namespaceConfig.getRolesPath());
try {
rolesNode = rolesUri.getStore().retrieveObject(rolesUri);
} catch (ObjectNotFoundException e) {}
if (rolesNode != null) {
Enumeration rolesEnum = rolesNode.enumerateBindings();
while (rolesEnum.hasMoreElements()) {
ObjectNode.Binding b = (ObjectNode.Binding)rolesEnum.nextElement();
String role = b.getName();
if (hasRole(token, subjectNode, role)) {
result.add(role);
}
}
}
}
Uri groupsUri = null;
ObjectNode groupsNode = null;
if (namespaceConfig.getGroupsPath() != null && namespaceConfig.getGroupsPath().length() != 0) {
groupsUri = namespace.getUri(token, namespaceConfig.getGroupsPath());
groupsNode = groupsUri.getStore().retrieveObject(groupsUri);
if (groupsNode != null) {
Enumeration groupsEnum = groupsNode.enumerateBindings();
while (groupsEnum.hasMoreElements()) {
ObjectNode.Binding b = (ObjectNode.Binding)groupsEnum.nextElement();
String group = b.getName();
if (hasRole(token, subjectNode, group)) {
result.add(group);
}
}
}
}
return result.elements();
}
/**
* Get enumeration of paths according to property DAV:group-membership
*
* @param token a SlideToken
* @param subjectNode a SubjectNode
* @return an Enumeration of paths (String)
* @throws ServiceAccessException
* @throws ObjectNotFoundException
*/
public Enumeration getGroupMembership(SlideToken token, SubjectNode subjectNode) throws ServiceAccessException, ObjectNotFoundException {
// check the principal exists
Uri subjectUri = namespace.getUri(token, subjectNode.getUri());
subjectUri.getStore().retrieveObject(subjectUri);
Vector result = new Vector();
Uri rolesUri = null;
ObjectNode rolesNode = null;
if (namespaceConfig.getRolesPath() != null && namespaceConfig.getRolesPath().length() != 0) {
rolesUri = namespace.getUri(token, namespaceConfig.getRolesPath());
try {
rolesNode = rolesUri.getStore().retrieveObject(rolesUri);
} catch (ObjectNotFoundException e) {}
if (rolesNode != null) {
Enumeration rolesEnum = rolesNode.enumerateBindings();
while (rolesEnum.hasMoreElements()) {
ObjectNode.Binding b = (ObjectNode.Binding)rolesEnum.nextElement();
String role = b.getName();
Uri roleUri = namespace.getUri(token, namespaceConfig.getRolesPath()+"/"+role);
try {
NodeRevisionDescriptor nrd =
roleUri.getStore().retrieveRevisionDescriptor(roleUri, 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(subjectNode)) {
result.add(roleUri.toString());
}
}
} catch (RevisionDescriptorNotFoundException e) {
} catch (JDOMException e) {}
}
}
}
Uri groupsUri = null;
ObjectNode groupsNode = null;
if (namespaceConfig.getGroupsPath() != null && namespaceConfig.getGroupsPath().length() != 0) {
groupsUri = namespace.getUri(token, namespaceConfig.getGroupsPath());
try {
groupsNode = groupsUri.getStore().retrieveObject(groupsUri);
} catch (ObjectNotFoundException e) {}
if (groupsNode != null) {
Enumeration rolesEnum = groupsNode.enumerateBindings();
while (rolesEnum.hasMoreElements()) {
ObjectNode.Binding b = (ObjectNode.Binding)rolesEnum.nextElement();
String group = b.getName();
Uri roleUri = namespace.getUri(token, namespaceConfig.getGroupsPath()+"/"+group);
try {
NodeRevisionDescriptor nrd =
roleUri.getStore().retrieveRevisionDescriptor(roleUri, 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(subjectNode)) {
result.add(roleUri.toString());
}
}
} catch (RevisionDescriptorNotFoundException e) {
} catch (JDOMException e) {}
}
}
}
return result.elements();
}
// overwrites super
public Enumeration getRoles(ObjectNode object) {
return EMPTY_VECTOR.elements();
}
/**
* Evaluates speified ACL by first-match principle
*/
private boolean evaluateAcl(SlideToken token, ObjectNode objectNode, ActionNode actionNode, Enumeration permissions) throws ServiceAccessException, ObjectNotFoundException {
boolean result = false;
SubjectNode subjectNode = (SubjectNode)getPrincipal(token);
while (permissions.hasMoreElements()) {
NodePermission permission = (NodePermission)permissions.nextElement();
if (match(token, objectNode, subjectNode, actionNode, permission)) {
result = !permission.isNegative();
break;
}
}
return result;
}
private boolean match(SlideToken token, ObjectNode objectNode, SubjectNode subjectNode, ActionNode actionNode, NodePermission permission) throws ServiceAccessException {
boolean result = true;
result = matchAction(token, actionNode, permission.getActionNode()) && matchSubject(token, objectNode, subjectNode, permission.getSubjectNode());
// logging
if (logger.isEnabled(LOG_CHANNEL, Logger.DEBUG)) {
logger.log(" permission="+permission+", match="+result, LOG_CHANNEL, Logger.DEBUG);
}
return result;
}
private boolean matchSubject(SlideToken token, ObjectNode objectNode, SubjectNode checkSubject, SubjectNode permSubject) throws ServiceAccessException {
if (permSubject.equals(SubjectNode.ALL)) {
return true;
}
else if (permSubject.equals(SubjectNode.AUTHENTICATED)) {
return !checkSubject.equals(SubjectNode.UNAUTHENTICATED);
}
else if (permSubject.equals(SubjectNode.UNAUTHENTICATED)) {
return checkSubject.equals(SubjectNode.UNAUTHENTICATED);
}
else if (permSubject.equals(SubjectNode.OWNER)) {
return matchOwner(token, objectNode, checkSubject);
}
else if (permSubject.equals(SubjectNode.SELF)) {
return matchPrincipal(token, checkSubject, (SubjectNode)objectNode);
}
else {
return matchPrincipal(token, checkSubject, permSubject);
}
}
private boolean matchOwner(SlideToken token, ObjectNode objectNode, SubjectNode checkSubject) throws ServiceAccessException {
Uri objectUri = namespace.getUri(token, objectNode.getUri());
try {
NodeRevisionDescriptor nrd =
objectUri.getStore().retrieveRevisionDescriptor(objectUri, new NodeRevisionNumber());
NodeProperty ownerProp = nrd.getProperty("owner");
if (ownerProp != null && ownerProp.getValue() != null) {
String usersPath = namespace.getConfig().getUsersPath();
SubjectNode ownerSubject = SubjectNode.getSubjectNode(usersPath+"/"+ownerProp.getValue());
return ownerSubject.equals(checkSubject);
}
else {
return false;
}
}
catch (RevisionDescriptorNotFoundException e) {
return false;
}
catch (ServiceAccessException e) {
throw e;
}
}
}