package er.rest;
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eocontrol.EOKeyComparisonQualifier;
import com.webobjects.eocontrol.EOKeyValueQualifier;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.eocontrol.EOQualifierEvaluation;
import er.extensions.eof.ERXKey;
import er.extensions.eof.ERXKeyFilter;
import er.extensions.qualifiers.ERXQualifierTraversal;
/**
* ERXFilteredQualifierTraversal performs a security check on a qualifier, throwing a SecurityException if a qualifier
* is found that attempts to qualify a key that isn't permitted by a given ERXKeyFilter. This prevents people from doing
* things like "employee.salary > 100000" when employee.salary is an excluded key in your filter.
*
* @author mschrag
*/
public class ERXFilteredQualifierTraversal extends ERXQualifierTraversal {
private EOEntity _entity;
private ERXKeyFilter _filter;
/**
* Constructs a new ERXFilteredQualifierTraversal.
*
* @param entity
* the entity to resolve keypaths on
* @param filter
* the filter to check against
*/
protected ERXFilteredQualifierTraversal(EOEntity entity, ERXKeyFilter filter) {
_entity = entity;
_filter = filter;
}
/**
* Checks the given key and throws an exception if the filter does not match it.
*
* @param key
* the key to check
* @throws SecurityException
* if the key does not match
*/
protected void checkKey(String key) throws SecurityException {
if (!_filter.matches(new ERXKey<Object>(key), ERXFilteredQualifierTraversal.typeForKeyInEntity(key, _entity))) {
throw new SecurityException("You do not have access to the key path '" + key + "'.");
}
}
@Override
protected boolean traverseKeyComparisonQualifier(EOKeyComparisonQualifier q) {
checkKey(q.leftKey());
checkKey(q.rightKey());
return super.traverseKeyComparisonQualifier(q);
}
@Override
protected boolean traverseKeyValueQualifier(EOKeyValueQualifier q) {
checkKey(q.key());
return super.traverseKeyValueQualifier(q);
}
@Override
protected boolean traverseUnknownQualifier(EOQualifierEvaluation q) {
throw new SecurityException("Unknown qualifier: " + q);
}
/**
* Returns the ERXKey.Type for a keypath on a particular entity. This should probably be in a more generic utility
* class. I can't put it on ERXKey because it would break JavaClient people when they use eogenerated classes with
* ERXKey properties.
*
* @param key
* the key to lookup
* @param entity
* the entity to resolve the keypath on
* @return the ERXKey.Type for the given keypath, or null if there is no matching key
*/
public static ERXKey.Type typeForKeyInEntity(String key, EOEntity entity) {
ERXKey.Type type;
EOAttribute attribute = entity._attributeForPath(key);
if (attribute != null) {
type = ERXKey.Type.Attribute;
}
else {
EORelationship relationship = entity._relationshipForPath(key);
if (relationship != null) {
if (relationship.isToMany()) {
type = ERXKey.Type.ToManyRelationship;
}
else {
type = ERXKey.Type.ToOneRelationship;
}
}
else {
type = null;
}
}
return type;
}
/**
* Traverses the given qualifier, checking each keypath against the given filter, evaluated against the given
* entity.
*
* @param qualifier
* the qualifier to check
* @param entity
* the entity to resolve keypaths against
* @param filter
* the filter to evaluate with
* @throws SecurityException
* if a keypath is not matched by the filter
*/
public static void checkQualifierForEntityWithFilter(EOQualifier qualifier, EOEntity entity, ERXKeyFilter filter) throws SecurityException {
new ERXFilteredQualifierTraversal(entity, filter).traverse(qualifier);
}
}