use of org.apache.jackrabbit.core.security.AccessManager in project jackrabbit by apache.
the class ItemSaveOperation method validateTransientItems.
/**
* the following validations/checks are performed on transient items:
*
* for every transient item:
* - if it is 'modified' or 'new' check the corresponding write permission.
* - if it is 'removed' check the REMOVE permission
*
* for every transient node:
* - if it is 'new' check that its node type satisfies the
* 'required node type' constraint specified in its definition
* - check if 'mandatory' child items exist
*
* for every transient property:
* - check if the property value satisfies the value constraints
* specified in the property's definition
*
* note that the protected flag is checked in Node.addNode/Node.remove
* (for adding/removing child entries of a node), in
* Node.addMixin/removeMixin/setPrimaryType (for type changes on nodes)
* and in Property.setValue (for properties to be modified).
*/
private void validateTransientItems(SessionContext context, Iterable<ItemState> dirty, Iterable<ItemState> removed) throws RepositoryException {
SessionImpl session = context.getSessionImpl();
ItemManager itemMgr = context.getItemManager();
SessionItemStateManager stateMgr = context.getItemStateManager();
AccessManager accessMgr = context.getAccessManager();
NodeTypeManagerImpl ntMgr = context.getNodeTypeManager();
// walk through list of dirty transient items and validate each
for (ItemState itemState : dirty) {
ItemDefinition def;
if (itemState.isNode()) {
def = itemMgr.getDefinition((NodeState) itemState);
} else {
def = itemMgr.getDefinition((PropertyState) itemState);
}
/* check permissions for non-protected items. protected items are
only added through API methods which need to assert that
permissions are not violated.
*/
if (!def.isProtected()) {
/* detect the effective set of modification:
- new added node -> add_node perm on the child
- new property added -> set_property permission
- property modified -> set_property permission
- modified nodes can be ignored for changes only included
child-item addition or removal or changes of protected
properties such as mixin-types which are covered separately
note: removed items are checked later on.
note: reordering of child nodes has been covered upfront as
this information isn't available here.
*/
Path path = stateMgr.getHierarchyMgr().getPath(itemState.getId());
boolean isGranted = true;
if (itemState.isNode()) {
if (itemState.getStatus() == ItemState.STATUS_NEW) {
isGranted = accessMgr.isGranted(path, Permission.ADD_NODE);
}
// else: modified node (see comment above)
} else {
// modified or new property: set_property permission
isGranted = accessMgr.isGranted(path, Permission.SET_PROPERTY);
}
if (!isGranted) {
String msg = itemMgr.safeGetJCRPath(path) + ": not allowed to add or modify item";
log.debug(msg);
throw new AccessDeniedException(msg);
}
}
if (itemState.isNode()) {
// the transient item is a node
NodeState nodeState = (NodeState) itemState;
ItemId id = nodeState.getNodeId();
NodeDefinition nodeDef = (NodeDefinition) def;
// primary type
NodeTypeImpl pnt = ntMgr.getNodeType(nodeState.getNodeTypeName());
// effective node type (primary type incl. mixins)
EffectiveNodeType ent = getEffectiveNodeType(context.getRepositoryContext().getNodeTypeRegistry(), nodeState);
/**
* if the transient node was added (i.e. if it is 'new') or if
* its primary type has changed, check its node type against the
* required node type in its definition
*/
boolean primaryTypeChanged = nodeState.getStatus() == ItemState.STATUS_NEW;
if (!primaryTypeChanged) {
NodeState overlaid = (NodeState) nodeState.getOverlayedState();
if (overlaid != null) {
Name newName = nodeState.getNodeTypeName();
Name oldName = overlaid.getNodeTypeName();
primaryTypeChanged = !newName.equals(oldName);
}
}
if (primaryTypeChanged) {
for (NodeType ntReq : nodeDef.getRequiredPrimaryTypes()) {
Name ntName = ((NodeTypeImpl) ntReq).getQName();
if (!(pnt.getQName().equals(ntName) || pnt.isDerivedFrom(ntName))) {
/**
* the transient node's primary node type does not
* satisfy the 'required primary types' constraint
*/
String msg = itemMgr.safeGetJCRPath(id) + " must be of node type " + ntReq.getName();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
// mandatory child properties
for (QPropertyDefinition pd : ent.getMandatoryPropDefs()) {
if (pd.getDeclaringNodeType().equals(NameConstants.MIX_VERSIONABLE) || pd.getDeclaringNodeType().equals(NameConstants.MIX_SIMPLE_VERSIONABLE)) {
/**
* todo FIXME workaround for mix:versionable:
* the mandatory properties are initialized at a
* later stage and might not exist yet
*/
continue;
}
String msg = itemMgr.safeGetJCRPath(id) + ": mandatory property " + pd.getName() + " does not exist";
if (!nodeState.hasPropertyName(pd.getName())) {
log.debug(msg);
throw new ConstraintViolationException(msg);
} else {
/*
there exists a property with the mandatory-name.
make sure the property really has the expected mandatory
property definition (and not another non-mandatory def,
such as e.g. multivalued residual instead of single-value
mandatory, named def).
*/
PropertyId pi = new PropertyId(nodeState.getNodeId(), pd.getName());
ItemData childData = itemMgr.getItemData(pi, null, false);
if (!childData.getDefinition().isMandatory()) {
throw new ConstraintViolationException(msg);
}
}
}
// mandatory child nodes
for (QItemDefinition cnd : ent.getMandatoryNodeDefs()) {
String msg = itemMgr.safeGetJCRPath(id) + ": mandatory child node " + cnd.getName() + " does not exist";
if (!nodeState.hasChildNodeEntry(cnd.getName())) {
log.debug(msg);
throw new ConstraintViolationException(msg);
} else {
/*
there exists a child node with the mandatory-name.
make sure the node really has the expected mandatory
node definition.
*/
boolean hasMandatoryChild = false;
for (ChildNodeEntry cne : nodeState.getChildNodeEntries(cnd.getName())) {
ItemData childData = itemMgr.getItemData(cne.getId(), null, false);
if (childData.getDefinition().isMandatory()) {
hasMandatoryChild = true;
break;
}
}
if (!hasMandatoryChild) {
throw new ConstraintViolationException(msg);
}
}
}
} else {
// the transient item is a property
PropertyState propState = (PropertyState) itemState;
ItemId propId = propState.getPropertyId();
org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl propDef = (org.apache.jackrabbit.spi.commons.nodetype.PropertyDefinitionImpl) def;
/**
* check value constraints
* (no need to check value constraints of protected properties
* as those are set by the implementation only, i.e. they
* cannot be set by the user through the api)
*/
if (!def.isProtected()) {
String[] constraints = propDef.getValueConstraints();
if (constraints != null) {
InternalValue[] values = propState.getValues();
try {
EffectiveNodeType.checkSetPropertyValueConstraints(propDef.unwrap(), values);
} catch (RepositoryException e) {
// repack exception for providing more verbose error message
String msg = itemMgr.safeGetJCRPath(propId) + ": " + e.getMessage();
log.debug(msg);
throw new ConstraintViolationException(msg);
}
/**
* need to manually check REFERENCE value constraints
* as this requires a session (target node needs to
* be checked)
*/
if (constraints.length > 0 && (propDef.getRequiredType() == PropertyType.REFERENCE || propDef.getRequiredType() == PropertyType.WEAKREFERENCE)) {
for (InternalValue internalV : values) {
boolean satisfied = false;
String constraintViolationMsg = null;
try {
NodeId targetId = internalV.getNodeId();
if (propDef.getRequiredType() == PropertyType.WEAKREFERENCE && !itemMgr.itemExists(targetId)) {
// target of weakref doesn;t exist, skip
continue;
}
Node targetNode = session.getNodeById(targetId);
/**
* constraints are OR-ed, i.e. at least one
* has to be satisfied
*/
for (String constrNtName : constraints) {
/**
* a [WEAK]REFERENCE value constraint specifies
* the name of the required node type of
* the target node
*/
if (targetNode.isNodeType(constrNtName)) {
satisfied = true;
break;
}
}
if (!satisfied) {
NodeType[] mixinNodeTypes = targetNode.getMixinNodeTypes();
String[] targetMixins = new String[mixinNodeTypes.length];
for (int j = 0; j < mixinNodeTypes.length; j++) {
targetMixins[j] = mixinNodeTypes[j].getName();
}
String targetMixinsString = Text.implode(targetMixins, ", ");
String constraintsString = Text.implode(constraints, ", ");
constraintViolationMsg = itemMgr.safeGetJCRPath(propId) + ": is constraint to [" + constraintsString + "] but references [primaryType=" + targetNode.getPrimaryNodeType().getName() + ", mixins=" + targetMixinsString + "]";
}
} catch (RepositoryException re) {
String msg = itemMgr.safeGetJCRPath(propId) + ": failed to check " + ((propDef.getRequiredType() == PropertyType.REFERENCE) ? "REFERENCE" : "WEAKREFERENCE") + " value constraint";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
if (!satisfied) {
log.debug(constraintViolationMsg);
throw new ConstraintViolationException(constraintViolationMsg);
}
}
}
}
}
/**
* no need to check the protected flag as this is checked
* in PropertyImpl.setValue(Value)
*/
}
}
// walk through list of removed transient items and check REMOVE permission
for (ItemState itemState : removed) {
QItemDefinition def;
try {
if (itemState.isNode()) {
def = itemMgr.getDefinition((NodeState) itemState).unwrap();
} else {
def = itemMgr.getDefinition((PropertyState) itemState).unwrap();
}
} catch (ConstraintViolationException e) {
// of a mixin (see also JCR-2130 & JCR-2408)
continue;
}
if (!def.isProtected()) {
Path path = stateMgr.getAtticAwareHierarchyMgr().getPath(itemState.getId());
// check REMOVE permission
int permission = (itemState.isNode()) ? Permission.REMOVE_NODE : Permission.REMOVE_PROPERTY;
if (!accessMgr.isGranted(path, permission)) {
String msg = itemMgr.safeGetJCRPath(path) + ": not allowed to remove item";
log.debug(msg);
throw new AccessDeniedException(msg);
}
}
}
}
use of org.apache.jackrabbit.core.security.AccessManager in project jackrabbit by apache.
the class NodeImpl method rename.
//-------------------------------------------------------< JackrabbitNode >
/**
* {@inheritDoc}
*/
public void rename(String newName) throws RepositoryException {
// check if this is the root node
if (getDepth() == 0) {
throw new RepositoryException("Cannot rename the root node");
}
Name qName;
try {
qName = sessionContext.getQName(newName);
} catch (NameException e) {
throw new RepositoryException("invalid node name: " + newName, e);
}
NodeImpl parent = (NodeImpl) getParent();
// check for name collisions
NodeImpl existing = null;
try {
existing = parent.getNode(qName);
// check same-name sibling setting of existing node
if (!existing.getDefinition().allowsSameNameSiblings()) {
throw new ItemExistsException("Same name siblings are not allowed: " + existing);
}
} catch (AccessDeniedException ade) {
// FIXME by throwing ItemExistsException we're disclosing too much information
throw new ItemExistsException();
} catch (ItemNotFoundException infe) {
// no name collision, fall through
}
// verify that parent node
// - is checked-out
// - is not protected neither by node type constraints nor by retention/hold
int options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION;
sessionContext.getItemValidator().checkRemove(parent, options, Permission.NONE);
sessionContext.getItemValidator().checkModify(parent, options, Permission.NONE);
// check constraints
// get applicable definition of renamed target node
NodeTypeImpl nt = (NodeTypeImpl) getPrimaryNodeType();
org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl newTargetDef;
try {
newTargetDef = parent.getApplicableChildNodeDefinition(qName, nt.getQName());
} catch (RepositoryException re) {
String msg = safeGetJCRPath() + ": no definition found in parent node's node type for renamed node";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
// necessarily have identical definitions
if (existing != null && !newTargetDef.allowsSameNameSiblings()) {
throw new ItemExistsException("Same name siblings not allowed: " + existing);
}
// check permissions:
// 1. on the parent node the session must have permission to manipulate the child-entries
AccessManager acMgr = sessionContext.getAccessManager();
if (!acMgr.isGranted(parent.getPrimaryPath(), qName, Permission.MODIFY_CHILD_NODE_COLLECTION)) {
String msg = "Not allowed to rename node " + safeGetJCRPath() + " to " + newName;
log.debug(msg);
throw new AccessDeniedException(msg);
}
// the primary node type on this node itself.
if (!nt.getName().equals(newTargetDef.getName()) && !(acMgr.isGranted(getPrimaryPath(), Permission.NODE_TYPE_MNGMT))) {
String msg = "Not allowed to rename node " + safeGetJCRPath() + " to " + newName;
log.debug(msg);
throw new AccessDeniedException(msg);
}
// change definition
onRedefine(newTargetDef.unwrap());
// delegate to parent
parent.renameChildNode(getNodeId(), qName, true);
}
use of org.apache.jackrabbit.core.security.AccessManager in project jackrabbit by apache.
the class ProtectedItemModifier method checkPermission.
private void checkPermission(ItemImpl item, int perm) throws RepositoryException {
if (perm > Permission.NONE) {
SessionImpl sImpl = (SessionImpl) item.getSession();
AccessManager acMgr = sImpl.getAccessManager();
Path path = item.getPrimaryPath();
acMgr.checkPermission(path, perm);
}
}
use of org.apache.jackrabbit.core.security.AccessManager in project jackrabbit by apache.
the class NodeImpl method orderBefore.
/**
* Same as <code>{@link Node#orderBefore(String, String)}</code> except that
* this method takes a <code>Path.Element</code> arguments instead of
* <code>String</code>s.
*
* @param srcName
* @param dstName
* @throws UnsupportedRepositoryOperationException
* @throws VersionException
* @throws ConstraintViolationException
* @throws ItemNotFoundException
* @throws LockException
* @throws RepositoryException
*/
public synchronized void orderBefore(Path.Element srcName, Path.Element dstName) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException {
// check state of this instance
sanityCheck();
if (!getPrimaryNodeType().hasOrderableChildNodes()) {
throw new UnsupportedRepositoryOperationException("child node ordering not supported on " + this);
}
// check arguments
if (srcName.equals(dstName)) {
// there's nothing to do
return;
}
// check existence
if (!hasNode(srcName.getName(), srcName.getIndex())) {
String name;
try {
Path.Element[] path = new Path.Element[] { srcName };
name = sessionContext.getJCRPath(new PathBuilder(path).getPath());
} catch (NameException e) {
name = srcName.toString();
} catch (NamespaceException e) {
name = srcName.toString();
}
throw new ItemNotFoundException(this + " has no child node with name " + name);
}
if (dstName != null && !hasNode(dstName.getName(), dstName.getIndex())) {
String name;
try {
Path.Element[] path = new Path.Element[] { dstName };
name = sessionContext.getJCRPath(new PathBuilder(path).getPath());
} catch (NameException e) {
name = dstName.toString();
} catch (NamespaceException e) {
name = dstName.toString();
}
throw new ItemNotFoundException(this + " has no child node with name " + name);
}
// make sure this node is checked-out and neither protected nor locked
int options = ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_CONSTRAINTS;
sessionContext.getItemValidator().checkModify(this, options, Permission.NONE);
/*
make sure the session is allowed to reorder child nodes.
since there is no specific privilege for reordering child nodes,
test if the the node to be reordered can be removed and added,
i.e. treating reorder similar to a move.
TODO: properly deal with sns in which case the index would change upon reorder.
*/
AccessManager acMgr = sessionContext.getAccessManager();
PathBuilder pb = new PathBuilder(getPrimaryPath());
pb.addLast(srcName.getName(), srcName.getIndex());
Path childPath = pb.getPath();
if (!acMgr.isGranted(childPath, Permission.MODIFY_CHILD_NODE_COLLECTION)) {
String msg = "Not allowed to reorder child node " + sessionContext.getJCRPath(childPath) + ".";
log.debug(msg);
throw new AccessDeniedException(msg);
}
ArrayList<ChildNodeEntry> list = new ArrayList<ChildNodeEntry>(data.getNodeState().getChildNodeEntries());
int srcInd = -1, destInd = -1;
for (int i = 0; i < list.size(); i++) {
ChildNodeEntry entry = list.get(i);
if (srcInd == -1) {
if (entry.getName().equals(srcName.getName()) && (entry.getIndex() == srcName.getIndex() || srcName.getIndex() == 0 && entry.getIndex() == 1)) {
srcInd = i;
}
}
if (destInd == -1 && dstName != null) {
if (entry.getName().equals(dstName.getName()) && (entry.getIndex() == dstName.getIndex() || dstName.getIndex() == 0 && entry.getIndex() == 1)) {
destInd = i;
if (srcInd != -1) {
break;
}
}
} else {
if (srcInd != -1) {
break;
}
}
}
// check if resulting order would be different to current order
if (destInd == -1) {
if (srcInd == list.size() - 1) {
// no change, we're done
return;
}
} else {
if ((destInd - srcInd) == 1) {
// no change, we're done
return;
}
}
// reorder list
if (destInd == -1) {
list.add(list.remove(srcInd));
} else {
if (srcInd < destInd) {
list.add(destInd, list.get(srcInd));
list.remove(srcInd);
} else {
list.add(destInd, list.remove(srcInd));
}
}
// modify the state of 'this', i.e. the parent node
NodeState thisState = (NodeState) getOrCreateTransientItemState();
thisState.setChildNodeEntries(list);
}
use of org.apache.jackrabbit.core.security.AccessManager in project jackrabbit by apache.
the class SessionMoveOperation method perform.
public Object perform(SessionContext context) throws RepositoryException {
// Get node instances
NodeImpl targetNode = getNode(context, srcPath, srcAbsPath);
NodeImpl srcParentNode = getNode(context, srcPath.getAncestor(1), srcAbsPath);
NodeImpl destParentNode = getNode(context, destPath.getAncestor(1), destAbsPath);
if (context.getHierarchyManager().isShareAncestor(targetNode.getNodeId(), destParentNode.getNodeId())) {
throw new RepositoryException("Move not possible because of a share cycle between " + srcAbsPath + " and " + destAbsPath);
}
// check for name collisions
NodeImpl existing = null;
try {
existing = context.getItemManager().getNode(destPath);
// check same-name sibling setting of existing node
if (!existing.getDefinition().allowsSameNameSiblings()) {
throw new ItemExistsException("Same name siblings are not allowed: " + existing);
}
} catch (AccessDeniedException ade) {
// FIXME by throwing ItemExistsException we're disclosing too much information
throw new ItemExistsException(destAbsPath);
} catch (PathNotFoundException pnfe) {
// no name collision, fall through
}
// verify that the targetNode can be removed
int options = ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION;
context.getItemValidator().checkRemove(targetNode, options, Permission.NONE);
// verify for both source and destination parent nodes that
// - they are checked-out
// - are not protected neither by node type constraints nor by retention/hold
options = ItemValidator.CHECK_CHECKED_OUT | ItemValidator.CHECK_LOCK | ItemValidator.CHECK_CONSTRAINTS | ItemValidator.CHECK_HOLD | ItemValidator.CHECK_RETENTION;
context.getItemValidator().checkModify(srcParentNode, options, Permission.NONE);
context.getItemValidator().checkModify(destParentNode, options, Permission.NONE);
// check constraints
// get applicable definition of target node at new location
NodeTypeImpl nt = (NodeTypeImpl) targetNode.getPrimaryNodeType();
org.apache.jackrabbit.spi.commons.nodetype.NodeDefinitionImpl newTargetDef;
try {
newTargetDef = destParentNode.getApplicableChildNodeDefinition(destPath.getName(), nt.getQName());
} catch (RepositoryException re) {
String msg = destAbsPath + ": no definition found in parent node's node type for new node";
log.debug(msg);
throw new ConstraintViolationException(msg, re);
}
// necessarily have identical definitions
if (existing != null && !newTargetDef.allowsSameNameSiblings()) {
throw new ItemExistsException("Same name siblings not allowed: " + existing);
}
NodeId targetId = targetNode.getNodeId();
// check permissions
AccessManager acMgr = context.getAccessManager();
if (!(acMgr.isGranted(srcPath, Permission.REMOVE_NODE) && acMgr.isGranted(destPath, Permission.ADD_NODE | Permission.NODE_TYPE_MNGMT))) {
String msg = "Not allowed to move node " + srcAbsPath + " to " + destAbsPath;
log.debug(msg);
throw new AccessDeniedException(msg);
}
if (srcParentNode.isSame(destParentNode)) {
// change definition of target
targetNode.onRedefine(newTargetDef.unwrap());
// do rename
destParentNode.renameChildNode(targetId, destPath.getName(), false);
} else {
// check shareable case
if (targetNode.getNodeState().isShareable()) {
String msg = "Moving a shareable node is not supported.";
log.debug(msg);
throw new UnsupportedRepositoryOperationException(msg);
}
// change definition of target
targetNode.onRedefine(newTargetDef.unwrap());
// Get the transient states
NodeState srcParentState = (NodeState) srcParentNode.getOrCreateTransientItemState();
NodeState targetState = (NodeState) targetNode.getOrCreateTransientItemState();
NodeState destParentState = (NodeState) destParentNode.getOrCreateTransientItemState();
// 1. remove child node entry from old parent
if (srcParentState.removeChildNodeEntry(targetId)) {
// 2. re-parent target node
targetState.setParentId(destParentNode.getNodeId());
// 3. add child node entry to new parent
destParentState.addChildNodeEntry(destPath.getName(), targetId);
}
}
return this;
}
Aggregations