You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2009/10/22 19:26:39 UTC
svn commit: r828791 [3/8] - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/config/ jackrabbi...
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java?rev=828791&r1=828790&r2=828791&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserAccessControlProvider.java Thu Oct 22 17:26:37 2009
@@ -33,6 +33,7 @@
import org.apache.jackrabbit.core.security.authorization.Permission;
import org.apache.jackrabbit.core.security.authorization.PrivilegeRegistry;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.security.SecurityConstants;
import org.apache.jackrabbit.spi.Path;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
@@ -88,11 +89,15 @@
private final AccessControlPolicy policy;
- private Path groupsPath;
- private Path usersPath;
+ private String groupsPath;
+ private String usersPath;
- private String userAdminGroup;
- private String groupAdminGroup;
+ private Principal userAdminGroup;
+ private Principal groupAdminGroup;
+
+ private String userAdminGroupPath;
+ private String groupAdminGroupPath;
+ private String administratorsGroupPath;
/**
*
@@ -130,22 +135,26 @@
super.init(systemSession, configuration);
if (systemSession instanceof SessionImpl) {
SessionImpl sImpl = (SessionImpl) systemSession;
- userAdminGroup = (configuration.containsKey(USER_ADMIN_GROUP_NAME)) ? configuration.get(USER_ADMIN_GROUP_NAME).toString() : USER_ADMIN_GROUP_NAME;
- groupAdminGroup = (configuration.containsKey(GROUP_ADMIN_GROUP_NAME)) ? configuration.get(GROUP_ADMIN_GROUP_NAME).toString() : GROUP_ADMIN_GROUP_NAME;
+ String userAdminName = (configuration.containsKey(USER_ADMIN_GROUP_NAME)) ? configuration.get(USER_ADMIN_GROUP_NAME).toString() : USER_ADMIN_GROUP_NAME;
+ String groupAdminName = (configuration.containsKey(GROUP_ADMIN_GROUP_NAME)) ? configuration.get(GROUP_ADMIN_GROUP_NAME).toString() : GROUP_ADMIN_GROUP_NAME;
// make sure the groups exist (and possibly create them).
UserManager uMgr = sImpl.getUserManager();
- if (!initGroup(uMgr, userAdminGroup)) {
- log.warn("Unable to initialize User admininistrator group -> no user admins.");
- userAdminGroup = null;
+ userAdminGroup = initGroup(uMgr, userAdminName);
+ if (userAdminGroup != null && userAdminGroup instanceof ItemBasedPrincipal) {
+ userAdminGroupPath = ((ItemBasedPrincipal) userAdminGroup).getPath();
}
- if (!initGroup(uMgr, groupAdminGroup)) {
- log.warn("Unable to initialize Group admininistrator group -> no group admins.");
- groupAdminGroup = null;
+ groupAdminGroup = initGroup(uMgr, groupAdminName);
+ if (groupAdminGroup != null && groupAdminGroup instanceof ItemBasedPrincipal) {
+ groupAdminGroupPath = ((ItemBasedPrincipal) groupAdminGroup).getPath();
}
- usersPath = sImpl.getQPath(USERS_PATH);
- groupsPath = sImpl.getQPath(GROUPS_PATH);
+ Principal administrators = initGroup(uMgr, SecurityConstants.ADMINISTRATORS_NAME);
+ if (administrators != null && administrators instanceof ItemBasedPrincipal) {
+ administratorsGroupPath = ((ItemBasedPrincipal) administrators).getPath();
+ }
+ usersPath = (uMgr instanceof UserManagerImpl) ? ((UserManagerImpl) uMgr).getUsersPath() : UserConstants.USERS_PATH;
+ groupsPath = (uMgr instanceof UserManagerImpl) ? ((UserManagerImpl) uMgr).getGroupsPath() : UserConstants.GROUPS_PATH;
} else {
throw new RepositoryException("SessionImpl (system session) expected.");
}
@@ -194,7 +203,7 @@
/**
* @see org.apache.jackrabbit.core.security.authorization.AccessControlProvider#canAccessRoot(Set)
*/
- public boolean canAccessRoot(Set principals) throws RepositoryException {
+ public boolean canAccessRoot(Set<Principal> principals) throws RepositoryException {
checkInitialized();
return true;
}
@@ -255,35 +264,36 @@
return PrivilegeRegistry.getBits(privs);
}
- private static boolean containsGroup(Set<Principal> principals, String groupName) {
- for (Iterator it = principals.iterator(); it.hasNext() && groupName != null;) {
- Principal p = (Principal) it.next();
- if (p.getName().equals(groupName)) {
+ private static boolean containsGroup(Set<Principal> principals, Principal group) {
+ for (Iterator<Principal> it = principals.iterator(); it.hasNext() && group != null;) {
+ Principal p = it.next();
+ if (p.getName().equals(group.getName())) {
return true;
}
}
return false;
}
- private static boolean initGroup(UserManager uMgr, String principalName) {
- boolean success;
+ private static Principal initGroup(UserManager uMgr, String principalName) {
Principal prnc = new PrincipalImpl(principalName);
try {
Authorizable auth = uMgr.getAuthorizable(prnc);
if (auth == null) {
- success = (uMgr.createGroup(prnc) != null);
+ auth = uMgr.createGroup(prnc);
} else {
- success = auth.isGroup();
- if (!success) {
+ if (!auth.isGroup()) {
log.warn("Cannot create group '" + principalName + "'; User with that principal already exists.");
+ auth = null;
}
}
+ if (auth != null) {
+ return auth.getPrincipal();
+ }
} catch (RepositoryException e) {
// should never get here
log.error("Error while initializing user/group administrators", e.getMessage());
- success = false;
}
- return success;
+ return null;
}
//--------------------------------------------------------< inner class >---
@@ -304,7 +314,7 @@
isGroupAdmin = containsGroup(principals, groupAdminGroup);
int events = Event.PROPERTY_CHANGED | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED;
- observationMgr.addEventListener(this, events, USERS_PATH, true, null, null, false);
+ observationMgr.addEventListener(this, events, groupsPath, true, null, null, false);
}
//------------------------------------< AbstractCompiledPermissions >---
@@ -335,102 +345,78 @@
int privs;
// Determine if for path, the set of privileges must be calculated:
// Generally, privileges can only be determined for existing nodes.
- boolean calcPrivs = session.nodeExists(resolver.getJCRPath(path.getNormalizedPath()));
+ String jcrPath = resolver.getJCRPath(path.getNormalizedPath());
+ boolean calcPrivs = session.nodeExists(jcrPath);
if (calcPrivs) {
privs = getPrivilegeBits(Privilege.JCR_READ);
} else {
privs = PrivilegeRegistry.NO_PRIVILEGE;
}
- Path abs2Path = (4 > path.getLength()) ? null : path.subPath(0, 4);
- if (usersPath.equals(abs2Path)) {
+ if (Text.isDescendant(usersPath, jcrPath)) {
/*
below the user-tree
- - determine position of target relative
+ - determine position of target relative to the editing user
- target may not be below an existing user but only below an
authorizable folder.
- - determine if the editing user is user/group-admin
- - special treatment for rep:groups property
+ - determine if the editing user is user-admin
*/
NodeImpl node = (NodeImpl) getExistingNode(path);
-
- if (node.isNodeType(NT_REP_AUTHORIZABLE) || node.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
- boolean editingHimSelf = node.isSame(userNode);
- boolean isGroupProp = P_GROUPS.equals(path.getNameElement().getName());
- // only user-admin is allowed to modify users.
- // for group membership (rep:groups) group-admin is required
- // in addition.
- boolean memberOfRequiredGroups = isUserAdmin;
- if (memberOfRequiredGroups && isGroupProp) {
- memberOfRequiredGroups = isGroupAdmin;
+ if (node.isNodeType(NT_REP_AUTHORIZABLE_FOLDER)) {
+ // an authorizable folder -> must be user admin in order
+ // to have permission to write.
+ if (isUserAdmin) {
+ allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
+ if (calcPrivs) {
+ // grant WRITE privilege
+ // note: ac-read/modification is not included
+ privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
+ }
}
- if (editingHimSelf) {
- /*
- node to be modified is same node as userNode. 3 cases to distinguish
- 1) user is User-Admin -> R, W
- 2) user is NOT U-admin but nodeID is its own node.
- 3) special treatment for rep:group property which can
- only be modified by group-administrators
- */
- Path aPath = session.getQPath(node.getPath());
- if (memberOfRequiredGroups) {
- // principals contain 'user-admin'
- // -> user can modify items below the user-node except rep:group.
- // principals contains 'user-admin' + 'group-admin'
- // -> user can modify rep:group property as well.
- if (path.equals(aPath)) {
- allows |= (Permission.ADD_NODE | Permission.REMOVE_PROPERTY | Permission.SET_PROPERTY);
- } else {
- allows |= Permission.ALL;
- }
+ } else {
+ // rep:User node or some other custom node below an existing user.
+ // as the auth-folder doesn't allow other residual child nodes.
+ boolean editingOwnUser = node.isSame(userNode);
+ if (editingOwnUser) {
+ // user can only read && write his own props
+ allows |= (Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY);
+ if (calcPrivs) {
+ privs |= getPrivilegeBits(Privilege.JCR_MODIFY_PROPERTIES);
+ }
+ } else if (isUserAdmin) {
+ allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
+ if (calcPrivs) {
+ // grant WRITE privilege
+ // note: ac-read/modification is not included
+ privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
+ }
+ } // else: normal user that isn't allowed to modify another user.
+ }
+ } else if (Text.isDescendant(groupsPath, jcrPath)) {
+ /*
+ below group-tree:
+ - test if the user is group-administrator.
+ - make sure group-admin cannot modify user-admin or administrators
+ - ... and cannot remove itself.
+ */
+ if (isGroupAdmin) {
+ if (!jcrPath.startsWith(administratorsGroupPath) &&
+ !jcrPath.startsWith(userAdminGroupPath)) {
+ if (jcrPath.equals(groupAdminGroupPath)) {
+ // no remove perm on group-admin node
+ allows |= (Permission.ADD_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
if (calcPrivs) {
- // grant WRITE privilege
- // note: ac-read/modification is not included
- // remove_node is not included
privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
- if (!path.equals(aPath)) {
- privs |= getPrivilegeBits(Privilege.JCR_REMOVE_NODE);
- }
+ privs ^= getPrivilegeBits(Privilege.JCR_REMOVE_NODE);
}
- } else if (userNode.isSame(node) && (!isGroupProp || isGroupAdmin)) {
- // user can only read && write his own props
- // except for the rep:group property.
- allows |= (Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY);
+ } else {
+ // complete write
+ allows |= (Permission.ADD_NODE | Permission.REMOVE_NODE | Permission.SET_PROPERTY | Permission.REMOVE_PROPERTY | Permission.NODE_TYPE_MNGMT);
if (calcPrivs) {
- privs |= getPrivilegeBits(Privilege.JCR_MODIFY_PROPERTIES);
- }
- } // else some other node below but not U-admin -> read-only.
- } else {
- /*
- authN points to some other user-node, i.e.
- 1) nodeId points to an authorizable that isn't the editing user
- 2) nodeId points to an auth-folder within the user-tree
-
- In either case user-admin group-membership is
- required in order to get write permission.
- group-admin group-membership is required in addition
- if rep:groups is the target item.
- */
- if (memberOfRequiredGroups) {
- allows = Permission.ALL;
- if (calcPrivs) {
- // grant WRITE privilege
- // note: ac-read/modification is not included
privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
}
}
}
- } // outside of the user tree
- } else if (groupsPath.equals(abs2Path)) {
- /*
- below group-tree:
- - test if the user is group-administrator.
- */
- if (isGroupAdmin) {
- allows = Permission.ALL;
- if (calcPrivs) {
- privs |= getPrivilegeBits(PrivilegeRegistry.REP_WRITE);
- }
}
} // else outside of user/group tree -> read only.
return new Result(allows, denies, privs, PrivilegeRegistry.NO_PRIVILEGE);
@@ -480,33 +466,27 @@
Event ev = events.nextEvent();
try {
String evPath = ev.getPath();
- String repGroups = session.getJCRName(UserConstants.P_GROUPS);
- // TODO: add better evaluation.
- if (repGroups.equals(Text.getName(evPath)) &&
- userNodePath.equals(Text.getRelativeParent(evPath, 1))) {
- // recalculate the is...Admin flags
- switch (ev.getType()) {
- case Event.PROPERTY_REMOVED:
- isUserAdmin = false;
- isGroupAdmin = false;
- break;
- case Event.PROPERTY_ADDED:
- case Event.PROPERTY_CHANGED:
- if (session.propertyExists(evPath)) {
- Value[] vs = session.getProperty(evPath).getValues();
- String princName = session.getJCRName(P_PRINCIPAL_NAME);
- for (Value v : vs) {
- Node groupNode = session.getNodeByUUID(v.getString());
- String pName = groupNode.getProperty(princName).getString();
- if (userAdminGroup.equals(pName)) {
- isUserAdmin = true;
- } else if (groupAdminGroup.equals(pName)) {
- isGroupAdmin = true;
- }
- }
+ String repMembers = session.getJCRName(UserConstants.P_MEMBERS);
+ if (repMembers.equals(Text.getName(evPath))) {
+ // recalculate the is...Admin flages
+ Node userNode = session.getNode(userNodePath);
+ String nodePath = Text.getRelativeParent(evPath, 1);
+ if (userAdminGroupPath.equals(nodePath)) {
+ isUserAdmin = false;
+ if (ev.getType() != Event.PROPERTY_REMOVED) {
+ Value[] vs = session.getProperty(evPath).getValues();
+ for (int i = 0; i < vs.length && !isUserAdmin; i++) {
+ isUserAdmin = userNode.getIdentifier().equals(vs[i].getString());
}
- break;
- // default: other events are not relevant.
+ }
+ } else if (groupAdminGroupPath.equals(nodePath)) {
+ isGroupAdmin = false;
+ if (ev.getType() != Event.PROPERTY_REMOVED) {
+ Value[] vs = session.getProperty(evPath).getValues();
+ for (int i = 0; i < vs.length && !isGroupAdmin; i++) {
+ isGroupAdmin = userNode.getIdentifier().equals(vs[i].getString());
+ }
+ }
}
// invalidate the cached results
clearCache();
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java?rev=828791&r1=828790&r2=828791&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserConstants.java Thu Oct 22 17:26:37 2009
@@ -55,7 +55,12 @@
Name P_USERID = NF.create(Name.NS_REP_URI, "userId");
Name P_PASSWORD = NF.create(Name.NS_REP_URI, "password");
+ /**
+ * @deprecated As of 2.0 group membership is stored with the group node.
+ * @see #P_MEMBERS
+ */
Name P_GROUPS = NF.create(Name.NS_REP_URI, "groups");
+ Name P_MEMBERS = NF.create(Name.NS_REP_URI, "members");
/**
* Name of the user property containing the principal names of those allowed
@@ -67,5 +72,6 @@
Name NT_REP_AUTHORIZABLE_FOLDER = NF.create(Name.NS_REP_URI, "AuthorizableFolder");
Name NT_REP_USER = NF.create(Name.NS_REP_URI, "User");
Name NT_REP_GROUP = NF.create(Name.NS_REP_URI, "Group");
+ Name MIX_REP_IMPERSONATABLE = NF.create(Name.NS_REP_URI, "Impersonatable");
}
\ No newline at end of file
Copied: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java (from r818472, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java?p2=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java&p1=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java&r1=818472&r2=828791&rev=828791&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/xml/AccessControlImporter.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java Thu Oct 22 17:26:37 2009
@@ -14,375 +14,557 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.jackrabbit.core.xml;
-
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-
-import javax.jcr.AccessDeniedException;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.Value;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.security.AccessControlEntry;
-import javax.jcr.security.AccessControlManager;
-import javax.jcr.security.AccessControlPolicy;
-import javax.jcr.security.Privilege;
+package org.apache.jackrabbit.core.security.user;
import org.apache.jackrabbit.api.JackrabbitSession;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
-import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.jackrabbit.core.security.authorization.AccessControlConstants;
-import org.apache.jackrabbit.core.security.principal.UnknownPrincipal;
-import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.xml.DefaultProtectedPropertyImporter;
+import org.apache.jackrabbit.core.xml.PropInfo;
+import org.apache.jackrabbit.core.util.ReferenceChangeTracker;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jcr.RepositoryException;
+import javax.jcr.PropertyType;
+import javax.jcr.Value;
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.nodetype.ConstraintViolationException;
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.security.Principal;
+
/**
- * <code>AccessControlImporter</code> implements a
- * <code>ProtectedNodeImporter</code> that is able to deal with access control
- * content as defined by the default ac related node types present with
- * jackrabbit-core.
+ * <code>UserImporter</code> implements a
+ * <code>DefaultProtectedPropertyImporter</code> that is able to deal with
+ * user/group content as defined by the default user related node types present
+ * with jackrabbit-core.<p/>
+ *
+ * The importer is intended to be used by applications that import user content
+ * extracted from another repository instance and immediately persist the
+ * imported content using {@link javax.jcr.Session#save()}. Omitting the
+ * save call will lead to transient, semi-validated user content and eventually
+ * to inconsistencies.
+ * <p/>
+ * Note the following restrictions:
+ * <ul>
+ * <li>The importer will only be initialized if the user manager is an instance
+ * of <code>TransientChangeUserManager</code>.
+ * </li>
+ * <li>The importer will only be initialized if the editing session starting
+ * this import is the same as the UserManager's Session instance.
+ * </li>
+ * <li>The jcr:uuid property of user and groups is defined to represent the
+ * hashed authorizable id as calculated by the UserManager. This importer
+ * is therefore not able to handle imports with
+ * {@link ImportUUIDBehavior#IMPORT_UUID_CREATE_NEW}.</li>
+ * <li>The rep:password property is expected to contain the crypted password
+ * value as stored in the content upon calling {@link UserManager#createUser}
+ * and exposed upon {@link javax.jcr.Property#getString()}
+ * or {@link javax.jcr.Session#exportSystemView}</li>
+ * <li>Importing user/group nodes outside of the hierarchy defined by
+ * {@link org.apache.jackrabbit.core.security.user.UserManagerImpl#getUsersPath()}
+ * and {@link org.apache.jackrabbit.core.security.user.UserManagerImpl#getGroupsPath()}
+ * will fail upon save as the mandatory properties will not be imported. The same may
+ * be true in case of {@link ImportUUIDBehavior#IMPORT_UUID_COLLISION_REPLACE_EXISTING}
+ * inserting the user/group node at some other place in the node hierarchy.</li>
+ * <li>While creating user/groups through the API the <code>UserManagerImpl</code> makes
+ * sure that authorizables are never nested and are created below a hierarchy
+ * of nt:AuthorizableFolder nodes. This isn't efforced by means of node type
+ * constraints but only by the API. This importer currently doesn't perform such
+ * a validation check.</li>
+ * <li>Any attempt to import conflicting data will cause the import to fail
+ * either immediately or upon calling {@link javax.jcr.Session#save()} with the
+ * following exceptions:
+ * <ul>
+ * <li><code>rep:members</code> : Group membership</li>
+ * <li><code>rep:impersonators</code> : Impersonators of a User.</li>
+ * </ul>
+ * The import behavior of these two properties is defined by the {@link #PARAM_IMPORT_BEHAVIOR}
+ * configuration parameter, which can be set to
+ * <ul>
+ * <li>{@link ImportBehavior#NAME_IGNORE ignore}: A warning is logged.</li>
+ * <li>{@link ImportBehavior#NAME_BESTEFFORT besteffort}: A warning is logged
+ * and the importer tries to fix the problem.</li>
+ * <li>{@link ImportBehavior#NAME_ABORT abort}: The import is immediately
+ * aborted with a ConstraintViolationException. (<strong>default</strong>)</li>
+ * </ul>
+ * </li>
+ * </ul>
+ * Known Issue:<br>
+ * Importing <code>rep:impersonators</code> property refering to principals
+ * that are created during this import AND have principalName different from the
+ * ID will no succeed, as the validation in <code>ImpersonationImpl</code> isn't able
+ * to find the authorizable with the given principal (reason: query will only
+ * find persisted content).
*/
-public class AccessControlImporter extends DefaultProtectedNodeImporter {
+public class UserImporter extends DefaultProtectedPropertyImporter {
/**
* logger instance
*/
- private static final Logger log = LoggerFactory.getLogger(AccessControlImporter.class);
+ private static final Logger log = LoggerFactory.getLogger(UserImporter.class);
- private static final int STATUS_UNDEFINED = 0;
- private static final int STATUS_AC_FOLDER = 1;
- private static final int STATUS_PRINCIPAL_AC = 2;
- private static final int STATUS_ACL = 3;
- private static final int STATUS_ACE = 4;
-
- private static final Set<Name> ACE_NODETYPES = new HashSet<Name>(2);
- static {
- ACE_NODETYPES.add(AccessControlConstants.NT_REP_DENY_ACE);
- ACE_NODETYPES.add(AccessControlConstants.NT_REP_GRANT_ACE);
- }
+ public static final String PARAM_IMPORT_BEHAVIOR = "importBehavior";
- private final AccessControlManager acMgr;
- private final Stack<Integer> prevStatus = new Stack<Integer>();
+ private UserPerWorkspaceUserManager userManager;
- private int status = STATUS_UNDEFINED;
- private NodeImpl parent = null;
+ private boolean initialized = false;
- private boolean principalbased = false;
+ private boolean resetAutoSave = false;
- /**
- * the ACL for non-principal based
- */
- private JackrabbitAccessControlList acl = null;
+ private int importBehavior = ImportBehavior.IGNORE;
- public AccessControlImporter(JackrabbitSession session, NamePathResolver resolver,
- boolean isWorkspaceImport, int uuidBehavior) throws RepositoryException {
- super(session, resolver, isWorkspaceImport, uuidBehavior);
-
- acMgr = session.getAccessControlManager();
- }
-
- public boolean start(NodeImpl protectedParent) throws RepositoryException {
- if (isStarted()) {
- // only ok if same parent
- if (!protectedParent.isSame(parent)) {
- throw new IllegalStateException();
+ @Override
+ public boolean init(JackrabbitSession session, NamePathResolver resolver,
+ boolean isWorkspaceImport,
+ int uuidBehavior, ReferenceChangeTracker referenceTracker) {
+ if (super.init(session, resolver, isWorkspaceImport, uuidBehavior, referenceTracker)) {
+ if (initialized) {
+ throw new IllegalStateException("Already initialized");
}
- return true;
- }
- if (isWorkspaceImport) {
- log.debug("AccessControlImporter may not be used with the WorkspaceImporter");
- return false;
- }
- if (!protectedParent.getDefinition().isProtected()) {
- log.debug("AccessControlImporter may not be started with a non-protected parent.");
- return false;
- }
-
- if (AccessControlConstants.N_POLICY.equals(protectedParent.getQName())
- && protectedParent.isNodeType(AccessControlConstants.NT_REP_ACL)) {
- acl = getACL(protectedParent.getParent().getPath());
- if (acl == null) {
- log.warn("AccessControlImporter cannot be started: no ACL for {}.", parent.getParent().getPath());
+ if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW) {
+ log.debug("ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW isn't supported when importing users or groups.");
+ return false;
+ }
+ if (isWorkspaceImport) {
+ log.debug("Only Session-Import is supported when importing users or groups.");
return false;
}
- status = STATUS_ACL;
- } else if (protectedParent.isNodeType(AccessControlConstants.NT_REP_ACCESS_CONTROL)) {
- status = STATUS_AC_FOLDER;
- principalbased = true;
- acl = null;
- } // else: nothing this importer can deal with.
-
- if (isStarted()) {
- parent = protectedParent;
- return true;
- } else {
+ try {
+ UserManager uMgr = session.getUserManager();
+ if (uMgr instanceof UserPerWorkspaceUserManager) {
+ // make sure the user managers autosave flag can be changed to false.
+ if (uMgr.isAutoSave()) {
+ uMgr.autoSave(false);
+ resetAutoSave = true;
+ log.debug("Changed autosave behavior of UserManager to 'false'.");
+ }
+ userManager = (UserPerWorkspaceUserManager) uMgr;
+ initialized = true;
+ } else {
+ // either wrong implementation or one that implicitly calls save.
+ log.debug("Failed to initialize UserImporter: UserManager isn't instance of UserPerWorkspaceUserManager or does implicit save call.");
+ }
+ } catch (RepositoryException e) {
+ // failed to access user manager or to set the autosave behavior
+ // -> return false (not initialized) as importer can't operate.
+ log.error("Failed to initialize UserImporter: ", e);
+ }
+ }
+ return initialized;
+ }
+
+ @Override
+ public boolean handlePropInfo(NodeImpl parent, PropInfo protectedPropInfo, QPropertyDefinition def) throws RepositoryException {
+ if (!initialized) {
+ throw new IllegalStateException("Not initialized");
+ }
+
+ /* importer can only handle protected properties below user/group
+ nodes that are properly stored underneith the configured users/groups
+ hierarchies (see {@link UserManagerImpl#getAuthorizable(NodeImpl)}.
+ this prevents from importing user/group nodes somewhere in the
+ content hierarchy which isn't possible when creating user/groups
+ using the corresponding API calls {@link UserManager#createUser} or
+ {@link UserManager#createGroup} respectively. */
+ Authorizable a = userManager.getAuthorizable(parent);
+ if (a == null) {
+ log.warn("Cannot handle protected PropInfo " + protectedPropInfo + ". Node " + parent + " doesn't represent a valid Authorizable.");
return false;
}
- }
- private JackrabbitAccessControlList getACL(String path) throws RepositoryException, AccessDeniedException {
- JackrabbitAccessControlList acl = null;
- for (AccessControlPolicy p: acMgr.getPolicies(path)) {
- if (p instanceof JackrabbitAccessControlList) {
- acl = (JackrabbitAccessControlList) p;
- // don't know if this check is needed
- if (path.equals(acl.getPath())) {
- break;
+ // TODO: check if import should be aborted in case of nested authorizable.
+
+ // assert that user manager is isn't in auto-save mode
+ if (userManager.isAutoSave()) {
+ userManager.autoSave(false);
+ }
+ try {
+ Name propName = protectedPropInfo.getName();
+ if (UserConstants.P_PRINCIPAL_NAME.equals(propName)) {
+ // minimal validation that passed definition really matches the
+ // protected rep:principalName property defined by rep:Authorizable.
+ if (def.isMultiple() || !UserConstants.NT_REP_AUTHORIZABLE.equals(def.getDeclaringNodeType())) {
+ // some other unexpected property definition -> cannot handle
+ log.warn("Unexpected definition for property rep:principalName");
+ return false;
}
- acl = null;
- }
- }
- if (acl != null) {
- // clear all existing entries
- for (AccessControlEntry ace: acl.getAccessControlEntries()) {
- acl.removeAccessControlEntry(ace);
- }
- }
- return acl;
- }
- public boolean start(NodeState protectedParent) throws IllegalStateException, RepositoryException {
- if (isStarted()) {
- throw new IllegalStateException();
- }
- if (isWorkspaceImport) {
- log.debug("AccessControlImporter may not be used with the WorkspaceImporter");
+ Value v = protectedPropInfo.getValues(PropertyType.STRING, resolver)[0];
+ String princName = v.getString();
+ userManager.setPrincipal(parent, new PrincipalImpl(princName));
+ return true;
+ } else if (UserConstants.P_PASSWORD.equals(propName)) {
+ if (a.isGroup()) {
+ log.warn("Expected parent node of type rep:User.");
+ return false;
+ }
+ // minimal validation of the passed definition
+ if (def.isMultiple() || !UserConstants.NT_REP_USER.equals(def.getDeclaringNodeType())) {
+ // some other unexpected property definition -> cannot handle
+ log.warn("Unexpected definition for property rep:password");
+ return false;
+ }
+
+ // expectation: pw must already be crypted.
+ Value v = protectedPropInfo.getValues(PropertyType.STRING, resolver)[0];
+ userManager.setProtectedProperty(parent, UserConstants.P_PASSWORD, v);
+
+ return true;
+
+ } else if (UserConstants.P_IMPERSONATORS.equals(propName)) {
+ if (a.isGroup()) {
+ // unexpected parent type -> cannot handle
+ log.warn("Expected parent node of type rep:User.");
+ return false;
+ }
+
+ // minimal validation of the passed definition
+ if (!def.isMultiple() || !UserConstants.MIX_REP_IMPERSONATABLE.equals(def.getDeclaringNodeType())) {
+ // some other unexpected property definition -> cannot handle
+ log.warn("Unexpected definition for property rep:impersonators");
+ return false;
+ }
+
+ // since impersonators may be imported later on, postpone processing
+ // to the end.
+ // see -> processRefeferences
+ Value[] vs = protectedPropInfo.getValues(PropertyType.STRING, resolver);
+ referenceTracker.processedReference(new Impersonators(a.getID(), vs));
+ return true;
+
+ } else if (UserConstants.P_MEMBERS.equals(propName)) {
+ if (!a.isGroup()) {
+ // unexpected parent type -> cannot handle
+ log.warn("Expected parent node of type rep:Group.");
+ return false;
+ }
+
+ // minimal validation of the passed definition
+ if (!def.isMultiple() || !UserConstants.NT_REP_GROUP.equals(def.getDeclaringNodeType())) {
+ // some other unexpected property definition -> cannot handle
+ log.warn("Unexpected definition for property rep:members");
+ return false;
+ }
+
+ // since group-members are references to user/groups that potentially
+ // are to be imported later on -> postpone processing to the end.
+ // see -> processRefeferences
+ Value[] vs = protectedPropInfo.getValues(PropertyType.WEAKREFERENCE, resolver);
+ NodeId[] ids = new NodeId[vs.length];
+ for (int i = 0; i < vs.length; i++) {
+ ids[i] = new NodeId(vs[i].getString());
+ }
+ referenceTracker.processedReference(new Membership(a.getID(), ids));
+ return true;
+
+ } // else: cannot handle -> return false
+
return false;
+ } finally {
+ // reset the autosave mode of the user manager in order to restore
+ // the original state.
+ if (resetAutoSave) {
+ userManager.autoSave(true);
+ }
}
- return false;
}
- public void end(NodeImpl protectedParent) throws RepositoryException {
- if (!isStarted()) {
- return;
+ @Override
+ public void processReferences() throws RepositoryException {
+ if (!initialized) {
+ throw new IllegalStateException("Not initialized");
}
- if (!principalbased) {
- checkStatus(STATUS_ACL, "");
- acMgr.setPolicy(acl.getPath(), acl);
- } else {
- checkStatus(STATUS_AC_FOLDER, "");
- if (!prevStatus.isEmpty()) {
- throw new ConstraintViolationException("Incomplete protected item tree: "+ prevStatus.size()+ " calls to 'endChildInfo' missing.");
- }
+ // assert that user manager is isn't in auto-save mode
+ if (userManager.isAutoSave()) {
+ userManager.autoSave(false);
}
- reset();
- }
+ try {
+ List<Object> processed = new ArrayList();
+ for (Iterator<Object> it = referenceTracker.getProcessedReferences(); it.hasNext();) {
+ Object reference = it.next();
+ if (reference instanceof Membership) {
+ Authorizable a = userManager.getAuthorizable(((Membership) reference).groupId);
+ if (a == null || !a.isGroup()) {
+ throw new RepositoryException(((Membership) reference).groupId + " does not represent a valid group.");
+ }
- public void end(NodeState protectedParent) throws IllegalStateException, ConstraintViolationException, RepositoryException {
- // nothing to do. will never get here.
- }
+ Group gr = (Group) a;
+ // 1. collect members to add and to remove.
+ Map<String, Authorizable> toRemove = new HashMap();
+ for (Iterator<Authorizable> aIt = gr.getDeclaredMembers(); it.hasNext();) {
+ Authorizable dm = aIt.next();
+ toRemove.put(dm.getID(), dm);
+ }
- public void startChildInfo(NodeInfo childInfo, List<PropInfo> propInfos) throws RepositoryException {
- if (!isStarted()) {
- return;
- }
-
- Name ntName = childInfo.getNodeTypeName();
- int previousStatus = status;
-
- if (!principalbased) {
- checkStatus(STATUS_ACL, "Cannot handle childInfo " + childInfo + "; rep:ACL may only contain a single level of child nodes representing the ACEs");
- addACE(childInfo, propInfos);
- status = STATUS_ACE;
- } else {
- switch (status) {
- case STATUS_AC_FOLDER:
- if (AccessControlConstants.NT_REP_ACCESS_CONTROL.equals(ntName)) {
- // yet another intermediate node -> keep status
- } else if (AccessControlConstants.NT_REP_PRINCIPAL_ACCESS_CONTROL.equals(ntName)) {
- // the start of a principal-based acl
- status = STATUS_PRINCIPAL_AC;
- } else {
- // illegal node type.
- throw new ConstraintViolationException("Unexpected node type " + ntName + ". Should be rep:AccessControl or rep:PrincipalAccessControl.");
- }
- checkIdMixins(childInfo);
- break;
- case STATUS_PRINCIPAL_AC:
- if (AccessControlConstants.NT_REP_ACCESS_CONTROL.equals(ntName)) {
- // some intermediate path between principal paths.
- status = STATUS_AC_FOLDER;
- } else if (AccessControlConstants.NT_REP_PRINCIPAL_ACCESS_CONTROL.equals(ntName)) {
- // principal-based ac node underneath another one
- // keep status
- } else {
- // the start the acl definition itself
- checkDefinition(childInfo, AccessControlConstants.N_POLICY, AccessControlConstants.NT_REP_ACL);
- status = STATUS_ACL;
- }
- checkIdMixins(childInfo);
- break;
- case STATUS_ACL:
- // nodeinfo must define an ACE
- addACE(childInfo, propInfos);
- status = STATUS_ACE;
- break;
- default:
- throw new ConstraintViolationException("Cannot handle childInfo " + childInfo + "; inexpected status " + status + " .");
+ List<Authorizable> toAdd = new ArrayList();
+ List<Value> nonExisting = new ArrayList();
+
+ for (NodeId originalId : ((Membership) reference).ids) {
+
+ NodeId remapped = referenceTracker.getMappedId(originalId);
+ NodeId id = (remapped == null) ? originalId : remapped;
+ Authorizable authorz = null;
+ try {
+ NodeImpl n = ((SessionImpl) session).getNodeById(id);
+ authorz = userManager.getAuthorizable(n);
+ } catch (RepositoryException e) {
+ // no such node or failed to retrieve authorizable
+ // warning is logged below.
+ }
+ if (authorz != null) {
+ if (toRemove.remove(authorz.getID()) == null) {
+ toAdd.add(authorz);
+ } // else: no need to remove from rep:members
+ } else {
+ handleFailure("Ignoring new member of " + gr + ". No such authorizable (NodeID = " + id + ")");
+ if (importBehavior == ImportBehavior.BESTEFFORT) {
+ nonExisting.add(session.getValueFactory().createValue(id.toString(), PropertyType.WEAKREFERENCE));
+ }
+ }
+ }
+
+ // 2. adjust members of the group
+ for (Authorizable m : toRemove.values()) {
+ if (!gr.removeMember(m)) {
+ handleFailure("Failed remove existing member (" + m + ") from " + gr);
+ }
+ }
+ for (Authorizable m : toAdd) {
+ if (!gr.addMember(m)) {
+ handleFailure("Failed add member (" + m + ") to " + gr);
+ }
+ }
+
+ // handling non-existing members in case of best-effort
+ if (!nonExisting.isEmpty()) {
+ log.warn("Found " + nonExisting.size() + " entries of rep:members pointing to non-existing authorizables. Best-effort approach configured -> add to rep:members.");
+
+ NodeImpl groupNode = ((AuthorizableImpl) gr).getNode();
+ // build list of valid members set before ....
+ List<Value> memberValues = new ArrayList();
+ if (groupNode.hasProperty(UserConstants.P_MEMBERS)) {
+ Value[] vls = groupNode.getProperty(UserConstants.P_MEMBERS).getValues();
+ memberValues.addAll(Arrays.asList(vls));
+ }
+ // ... and the non-Existing onces.
+ memberValues.addAll(nonExisting);
+ // and use implementation specific method to set the
+ // value of rep:members properties which was not possible
+ // through the API
+ userManager.setProtectedProperty(groupNode, UserConstants.P_MEMBERS, memberValues.toArray(new Value[memberValues.size()]));
+ }
+
+ processed.add(reference);
+
+ } else if (reference instanceof Impersonators) {
+ Authorizable a = userManager.getAuthorizable(((Impersonators) reference).userId);
+ if (a == null || a.isGroup()) {
+ throw new RepositoryException(((Impersonators) reference).userId + " does not represent a valid user.");
+ }
+
+ Impersonation imp = ((User) a).getImpersonation();
+
+ // 1. collect principals to add and to remove.
+ Map<String, Principal> toRemove = new HashMap();
+ for (PrincipalIterator pit = imp.getImpersonators(); pit.hasNext();) {
+ Principal princ = pit.nextPrincipal();
+ toRemove.put(princ.getName(), princ);
+ }
+
+ List<Principal> toAdd = new ArrayList();
+ Value[] vs = ((Impersonators) reference).values;
+ for (Value v : vs) {
+ String princName = v.getString();
+ if (toRemove.remove(princName) == null) {
+ // add it to the list of new impersonators to be added.
+ toAdd.add(new PrincipalImpl(princName));
+ } // else: no need to revoke impersonation for the given principal.
+ }
+
+ // 2. adjust set of impersonators
+ for (Principal princ : toRemove.values()) {
+ if (!imp.revokeImpersonation(princ)) {
+ handleFailure("Failed to revoke impersonation for " + princ.getName() + " on " + a);
+ }
+ }
+ for (Principal princ : toAdd) {
+ if (!imp.grantImpersonation(princ)) {
+ handleFailure("Failed to grant impersonation for " + princ.getName() + " on " + a);
+ }
+ }
+ // NOTE: no best effort handling so far. (TODO)
+
+ processed.add(reference);
+ }
+ }
+ // successfully processed this entry of the reference tracker
+ // -> remove from the reference tracker.
+ referenceTracker.removeReferences(processed);
+ } finally {
+ // reset the autosave mode of the user manager in order to restore
+ // the original state.
+ if (resetAutoSave) {
+ userManager.autoSave(true);
}
}
- prevStatus.push(previousStatus);
}
- /**
- * @throws javax.jcr.RepositoryException
- */
- public void endChildInfo() throws RepositoryException {
- if (!isStarted()) {
- return;
- }
-
- // if the protected imported is started at an existing protected node
- // SessionImporter does not remember it on the stack of parents node.
- if (!principalbased) {
- // childInfo + props have already been handled
- // -> assert valid status
- // -> no further actions required.
- checkStatus(STATUS_ACE, "Upon completion of a NodeInfo the status must be STATUS_ACE.");
- }
+ //------------------------------------------------------------< private >---
+ private void handleFailure(String msg) throws RepositoryException {
+ switch (importBehavior) {
+ case ImportBehavior.IGNORE:
+ case ImportBehavior.BESTEFFORT:
+ log.warn(msg);
+ break;
+ case ImportBehavior.ABORT:
+ throw new ConstraintViolationException(msg);
+ default:
+ // no other behavior. nothing to do.
- // reset the status
- status = prevStatus.pop();
+ }
}
- private boolean isStarted() {
- return status > STATUS_UNDEFINED;
+ //---------------------------------------------------------< BeanConfig >---
+ /**
+ * @return human readable representation of the <code>importBehavior</code> value.
+ */
+ public String getImportBehavior() {
+ return ImportBehavior.nameFromValue(importBehavior);
}
-
- private void reset() {
- status = STATUS_UNDEFINED;
- parent = null;
- acl = null;
+
+ /**
+ *
+ * @param importBehaviorStr
+ */
+ public void setImportBehavior(String importBehaviorStr) {
+ this.importBehavior = ImportBehavior.valueFromName(importBehaviorStr);
}
- private void checkStatus(int expectedStatus, String message) throws ConstraintViolationException {
- if (status != expectedStatus) {
- throw new ConstraintViolationException(message);
+ /**
+ *
+ * @param importBehavior
+ */
+ public void setImportBehavior(int importBehavior) {
+ switch (importBehavior) {
+ case ImportBehavior.IGNORE:
+ case ImportBehavior.ABORT:
+ case ImportBehavior.BESTEFFORT:
+ this.importBehavior = importBehavior;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid import behavior: " + importBehavior);
}
}
- private static void checkDefinition(NodeInfo nInfo, Name expName, Name expNodeTypeName) throws ConstraintViolationException {
- if (expName != null && !expName.equals(nInfo.getName())) {
- // illegal name
- throw new ConstraintViolationException("Unexpected Node name "+ nInfo.getName() +". Node name should be " + expName + ".");
- }
- if (expNodeTypeName != null && !expNodeTypeName.equals(nInfo.getNodeTypeName())) {
- // illegal name
- throw new ConstraintViolationException("Unexpected node type " + nInfo.getNodeTypeName() + ". Node type should be " + expNodeTypeName + ".");
+ //--------------------------------------------------------------------------
+ /**
+ * Inner class used to postpone import of group membership to the very end
+ * of the import. This allows to import membership of user/groups that
+ * are only being created during this import.
+ *
+ * @see ImportBehavior For additional configuration options.
+ */
+ private final class Membership {
+
+ private final String groupId;
+ private final NodeId[] ids;
+
+ private Membership(String groupId, NodeId[] ids) {
+ this.groupId = groupId;
+ this.ids = ids;
}
}
- private static void checkIdMixins(NodeInfo nInfo) throws ConstraintViolationException {
- // neither explicit id NOR mixin types may be present.
- Name[] mixins = nInfo.getMixinNames();
- NodeId id = nInfo.getId();
- if (id != null || mixins != null) {
- throw new ConstraintViolationException("The node represented by NodeInfo " + nInfo + " may neither be referenceable nor have mixin types.");
+ /**
+ * Inner class used to postpone import of impersonators to the very end
+ * of the import. This allows to import impersonation values pointing
+ * to user that are only being created during this import.
+ *
+ * @see ImportBehavior For additional configuration options.
+ */
+ private final class Impersonators {
+
+ private final String userId;
+ private final Value[] values;
+
+ private Impersonators(String userId, Value[] values) {
+ this.userId = userId;
+ this.values = values;
}
}
- private void addACE(NodeInfo childInfo, List<PropInfo> propInfos) throws RepositoryException, UnsupportedRepositoryOperationException {
+ /**
+ * Inner class defining the treatment of membership or impersonator
+ * values pointing to non-existing authorizables.
+ */
+ public static final class ImportBehavior {
- // node type may only be rep:GrantACE or rep:DenyACE
- Name ntName = childInfo.getNodeTypeName();
- if (!ACE_NODETYPES.contains(ntName)) {
- throw new ConstraintViolationException("Cannot handle childInfo " + childInfo + "; expected a valid, applicable rep:ACE node definition.");
- }
-
- checkIdMixins(childInfo);
-
- boolean isAllow = AccessControlConstants.NT_REP_GRANT_ACE.equals(ntName);
- Principal principal = null;
- Privilege[] privileges = null;
- Map<String, TextValue> restrictions = new HashMap<String, TextValue>();
-
- for (PropInfo pInfo : propInfos) {
- Name name = pInfo.getName();
- if (AccessControlConstants.P_PRINCIPAL_NAME.equals(name)) {
- Value[] values = pInfo.getValues(PropertyType.STRING, resolver);
- if (values == null || values.length != 1) {
- throw new ConstraintViolationException("");
- }
- String pName = values[0].getString();
- principal = session.getPrincipalManager().getPrincipal(pName);
- if (principal == null) {
- // create "fake" principal
- principal = new UnknownPrincipal(pName);
- }
- } else if (AccessControlConstants.P_PRIVILEGES.equals(name)) {
- Value[] values = pInfo.getValues(PropertyType.NAME, resolver);
- privileges = new Privilege[values.length];
- for (int i = 0; i < values.length; i++) {
- privileges[i] = acMgr.privilegeFromName(values[i].getString());
- }
+ /**
+ * If a member or impersonator value cannot be set due to constraints
+ * enforced by the API implementation, the failure is logged as
+ * warning but otherwise ignored.
+ */
+ public static final int IGNORE = 1;
+ /**
+ * Same as {@link #IGNORE} but in addition tries to circumvent the
+ * problem. This option should only be used with validated and trusted
+ * XML passed to the SessionImporter.
+ */
+ public static final int BESTEFFORT = 2;
+ /**
+ * Aborts the import as soon as invalid values are detected throwing
+ * a <code>ConstraintViolationException</code>.
+ */
+ public static final int ABORT = 3;
+
+ public static final String NAME_IGNORE = "ignore";
+ public static final String NAME_BESTEFFORT = "besteffort";
+ public static final String NAME_ABORT = "abort";
+
+ public static int valueFromName(String behaviorString) {
+ if (NAME_IGNORE.equalsIgnoreCase(behaviorString)) {
+ return IGNORE;
+ } else if (NAME_BESTEFFORT.equalsIgnoreCase(behaviorString)) {
+ return BESTEFFORT;
+ } else if (NAME_ABORT.equalsIgnoreCase(behaviorString)) {
+ return ABORT;
} else {
- TextValue[] txtVls = pInfo.getTextValues();
- for (TextValue txtV : txtVls) {
- restrictions.put(resolver.getJCRName(name), txtV);
- }
+ log.error("Invalid behavior " + behaviorString + " -> Using default: ABORT.");
+ return ABORT;
}
}
- if (principalbased) {
- // try to access policies
- List<AccessControlPolicy> policies = new ArrayList<AccessControlPolicy>();
- if (acMgr instanceof JackrabbitAccessControlManager) {
- JackrabbitAccessControlManager jacMgr = (JackrabbitAccessControlManager) acMgr;
- policies.addAll(Arrays.asList(jacMgr.getPolicies(principal)));
- policies.addAll(Arrays.asList(jacMgr.getApplicablePolicies(principal)));
- }
- for (AccessControlPolicy policy : policies) {
- if (policy instanceof JackrabbitAccessControlList) {
- JackrabbitAccessControlList acl = (JackrabbitAccessControlList) policy;
- Map<String, Value> restr = new HashMap<String, Value>();
- for (String restName : acl.getRestrictionNames()) {
- TextValue txtVal = restrictions.remove(restName);
- if (txtVal != null) {
- restr.put(restName, txtVal.getValue(acl.getRestrictionType(restName), resolver));
- }
- }
- if (!restrictions.isEmpty()) {
- throw new ConstraintViolationException("ACE childInfo contained restrictions that could not be applied.");
- }
- acl.addEntry(principal, privileges, isAllow, restr);
- acMgr.setPolicy(acl.getPath(), acl);
- return;
- }
- }
- } else {
- Map<String, Value> restr = new HashMap<String, Value>();
- for (String restName : acl.getRestrictionNames()) {
- TextValue txtVal = restrictions.remove(restName);
- if (txtVal != null) {
- restr.put(restName, txtVal.getValue(acl.getRestrictionType(restName), resolver));
- }
- }
- if (!restrictions.isEmpty()) {
- throw new ConstraintViolationException("ACE childInfo contained restrictions that could not be applied.");
+ public static String nameFromValue(int importBehavior) {
+ switch (importBehavior) {
+ case ImportBehavior.IGNORE:
+ return NAME_IGNORE;
+ case ImportBehavior.ABORT:
+ return NAME_ABORT;
+ case ImportBehavior.BESTEFFORT:
+ return NAME_BESTEFFORT;
+ default:
+ throw new IllegalArgumentException("Invalid import behavior: " + importBehavior);
}
- acl.addEntry(principal, privileges, isAllow, restr);
- return;
}
-
-
- // could not apply the ACE. No suitable ACL found.
- throw new ConstraintViolationException("Cannot handle childInfo " + childInfo + "; No policy found to apply the ACE.");
}
-}
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/UserImporter.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev Url