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 2008/03/19 14:57:11 UTC

svn commit: r638834 [9/14] - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/principal/ jackr...

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.simple;
+
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.config.AccessManagerConfig;
+import org.apache.jackrabbit.core.config.LoginModuleConfig;
+import org.apache.jackrabbit.core.config.SecurityConfig;
+import org.apache.jackrabbit.core.security.AMContext;
+import org.apache.jackrabbit.core.security.AccessManager;
+import org.apache.jackrabbit.core.security.AnonymousPrincipal;
+import org.apache.jackrabbit.core.security.JackrabbitSecurityManager;
+import org.apache.jackrabbit.core.security.UserPrincipal;
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.core.security.principal.PrincipalIteratorAdapter;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.core.security.authentication.AuthContext;
+import org.apache.jackrabbit.core.security.authentication.AuthContextProvider;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.core.security.principal.ProviderRegistryImpl;
+import org.apache.jackrabbit.core.security.principal.EveryonePrincipal;
+import org.apache.jackrabbit.core.security.principal.PrincipalManagerImpl;
+import org.apache.jackrabbit.core.security.principal.PrincipalProvider;
+import org.apache.jackrabbit.core.security.principal.PrincipalProviderRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * <code>SimpleSecurityManager</code>: simple implementation ignoring both
+ * configuration entries for 'principalProvider' and for 'workspaceAccessManager'.
+ * The AccessManager is initialized using
+ * {@link AccessManager#init(org.apache.jackrabbit.core.security.AMContext)}.
+ */
+public class SimpleSecurityManager implements JackrabbitSecurityManager {
+
+    private static Logger log = LoggerFactory.getLogger(SimpleSecurityManager.class);
+
+    private boolean initialized;
+
+    private SecurityConfig config;
+
+    /**
+     * session on the system workspace.
+     */
+    private Session systemSession;
+
+    /**
+     * the principal provider registry
+     */
+    private PrincipalProviderRegistry principalProviderRegistry;
+
+    /**
+     * factory for login-context {@see Repository#login())
+     */
+    private AuthContextProvider authCtxProvider;
+
+    private String adminID;
+    private String anonymID;
+
+    //------------------------------------------< JackrabbitSecurityManager >---
+    public void init(Repository repository, Session systemSession) throws RepositoryException {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+        if (!(repository instanceof RepositoryImpl)) {
+            throw new RepositoryException("RepositoryImpl expected");
+        }
+
+        this.systemSession = systemSession;
+        config = ((RepositoryImpl) repository).getConfig().getSecurityConfig();
+
+        // read the LoginModule configuration
+        LoginModuleConfig loginModConf = config.getLoginModuleConfig();
+        authCtxProvider = new AuthContextProvider(config.getAppName(), loginModConf);
+        if (authCtxProvider.isJAAS()) {
+            log.info("init: using JAAS LoginModule configuration for " + config.getAppName());
+        } else if (authCtxProvider.isLocal()) {
+            log.info("init: using Repository LoginModule configuration for " + config.getAppName());
+        } else {
+            String msg = "No valid LoginModule configuriation for " + config.getAppName();
+            log.error(msg);
+            throw new RepositoryException(msg);
+        }
+
+        Properties[] moduleConfig = authCtxProvider.getModuleConfig();
+
+        // retrieve default-ids (admin and anomymous) from login-module-configuration.
+        for (int i = 0; i < moduleConfig.length; i++) {
+            if (moduleConfig[i].containsKey(LoginModuleConfig.PARAM_ADMIN_ID)) {
+                adminID = moduleConfig[i].getProperty(LoginModuleConfig.PARAM_ADMIN_ID);
+            }
+            if (moduleConfig[i].containsKey(LoginModuleConfig.PARAM_ANONYMOUS_ID)) {
+                anonymID = moduleConfig[i].getProperty(LoginModuleConfig.PARAM_ANONYMOUS_ID, null);
+            }
+        }
+        // most simple principal provider registry, that does not read anything
+        // from configuration
+        PrincipalProvider principalProvider = new SimplePrincipalProvider();
+        // skip init of provider (nop)
+        principalProviderRegistry = new ProviderRegistryImpl(principalProvider);
+        // register all configured principal providers.
+        for (int i = 0; i < moduleConfig.length; i++) {
+            principalProviderRegistry.registerProvider(moduleConfig[i]);
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#dispose(String)
+     */
+    public void dispose(String workspaceName) {
+        checkInitialized();
+        // nop
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#close()
+     */
+    public void close() {
+        checkInitialized();
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#getSecurityConfig()
+     */
+    public SecurityConfig getSecurityConfig() throws RepositoryException {
+        return config;
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#getAccessManager(Session,AMContext)
+     */
+    public AccessManager getAccessManager(Session session, AMContext amContext) throws RepositoryException {
+        checkInitialized();
+        try {
+            AccessManagerConfig amc = config.getAccessManagerConfig();
+            AccessManager accessMgr;
+            if (amc == null) {
+                accessMgr = new SimpleAccessManager();
+            } else {
+                accessMgr = (AccessManager) amc.newInstance();
+            }
+            accessMgr.init(amContext);
+            return accessMgr;
+        } catch (AccessDeniedException ade) {
+            // re-throw
+            throw ade;
+        } catch (Exception e) {
+            // wrap in RepositoryException
+            String msg = "failed to instantiate AccessManager implementation: " + SimpleAccessManager.class.getName();
+            log.error(msg, e);
+            throw new RepositoryException(msg, e);
+        }
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#getPrincipalManager(Session)
+     */
+    public synchronized PrincipalManager getPrincipalManager(Session session)
+            throws RepositoryException {
+        checkInitialized();
+        if (session instanceof SessionImpl) {
+            SessionImpl sImpl = ((SessionImpl)session);
+            return new PrincipalManagerImpl(sImpl, principalProviderRegistry.getProviders());
+        } else {
+            throw new RepositoryException("Internal error: SessionImpl expected.");
+        }
+    }
+
+    /**
+     * @see JackrabbitSecurityManager#getUserManager(Session)
+     */
+    public UserManager getUserManager(Session session) throws RepositoryException {
+        checkInitialized();
+        throw new UnsupportedRepositoryOperationException("UserManager not supported.");
+    }
+
+    /**
+     * Creates an AuthContext for the given {@link Credentials} and
+     * {@link Subject}.<br>
+     * This includes selection of applicatoin specific LoginModules and
+     * initalization with credentials and Session to System-Workspace
+     *
+     * @return an {@link AuthContext} for the given Credentials, Subject
+     * @throws RepositoryException in other exceptional repository states
+     */
+    public AuthContext getAuthContext(Credentials creds, Subject subject)
+            throws RepositoryException {
+        checkInitialized();
+        return authCtxProvider.getAuthContext(creds, subject, systemSession, principalProviderRegistry);
+    }
+
+    //--------------------------------------------------------------------------
+    private void checkInitialized() {
+        if (!initialized) {
+            throw new IllegalStateException("Not initialized");
+        }
+    }
+
+    /**
+     * Simple Principal provider
+     */
+    private class SimplePrincipalProvider implements PrincipalProvider {
+
+        private final Map principals = new HashMap();
+
+        private SimplePrincipalProvider() {
+            if (adminID != null) {
+                principals.put(adminID, new AdminPrincipal(adminID));
+            }
+            if (anonymID != null) {
+                principals.put(anonymID, new AnonymousPrincipal());
+            }
+            EveryonePrincipal everyone = EveryonePrincipal.getInstance();
+            principals.put(everyone.getName(), everyone);
+
+        }
+
+        public boolean hasPrincipal(String principalName) {
+            return true;
+        }
+
+        public Principal getPrincipal(String principalName) {
+            if (principals.containsKey(principalName)) {
+                return (Principal) principals.get(principalName);
+            } else {
+                return new UserPrincipal(principalName);
+            }
+        }
+
+        public PrincipalIterator findPrincipals(String simpleFilter) {
+            return findPrincipals(simpleFilter, PrincipalManager.SEARCH_TYPE_ALL);
+        }
+
+        public PrincipalIterator findPrincipals(String simpleFilter, int searchType) {
+            Principal p = getPrincipal(simpleFilter);
+            if (p == null) {
+                return PrincipalIteratorAdapter.EMPTY;
+            } else if (p instanceof Group && searchType == PrincipalManager.SEARCH_TYPE_NOT_GROUP ||
+                       !(p instanceof Group) && searchType == PrincipalManager.SEARCH_TYPE_GROUP) {
+                return PrincipalIteratorAdapter.EMPTY;
+            } else {
+                return new PrincipalIteratorAdapter(Collections.singletonList(p));
+            }
+        }
+
+        public PrincipalIterator getPrincipals(int searchType) {
+            PrincipalIterator it;
+            switch (searchType) {
+                case PrincipalManager.SEARCH_TYPE_GROUP:
+                    it = new PrincipalIteratorAdapter(Collections.singletonList(EveryonePrincipal.getInstance()));
+                    break;
+                case PrincipalManager.SEARCH_TYPE_NOT_GROUP:
+                    Set set = new HashSet(principals.values());
+                    set.remove(EveryonePrincipal.getInstance());
+                    it = new PrincipalIteratorAdapter(set);
+                    break;
+                case PrincipalManager.SEARCH_TYPE_ALL:
+                    it = new PrincipalIteratorAdapter(principals.values());
+                    break;
+                // no default
+                default:
+                    throw new IllegalArgumentException("Unknown search type " + searchType);
+            }
+            return it;
+        }
+
+        public PrincipalIterator getGroupMembership(Principal principal) {
+            if (principal instanceof EveryonePrincipal) {
+                return PrincipalIteratorAdapter.EMPTY;
+            } else {
+                return new PrincipalIteratorAdapter(Collections.singletonList(EveryonePrincipal.getInstance()));
+            }
+        }
+
+        public void init(Properties options) {
+            // nothing to do
+        }
+
+        public void close() {
+            // nothing to do
+        }
+
+        public boolean canReadPrincipal(Session session, Principal principal) {
+            return true;
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/simple/SimpleSecurityManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.PropertyImpl;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.security.principal.PrincipalIteratorAdapter;
+import org.apache.jackrabbit.spi.Name;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.nodetype.ConstraintViolationException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * AuthorizableImpl
+ */
+abstract class AuthorizableImpl implements Authorizable, UserConstants {
+
+    static final Logger log = LoggerFactory.getLogger(AuthorizableImpl.class);
+
+    final UserManagerImpl userManager;
+    private final NodeImpl node;
+
+    /**
+     * @param node    the Authorizable is persisted to.
+     * @param userManager UserManager that created this Authorizable.
+     * @throws RepositoryException
+     */
+    protected AuthorizableImpl(NodeImpl node, UserManagerImpl userManager)
+            throws RepositoryException {
+        if (!node.isNodeType(NT_REP_AUTHORIZABLE)) {
+            throw new IllegalArgumentException("Node argument of NodeType " + NT_REP_AUTHORIZABLE + " required");
+        }
+        this.node = node;
+        this.userManager = userManager;
+    }
+
+    //-------------------------------------------------------< Authorizable >---
+    /**
+     * @see Authorizable#getPrincipals()
+     */
+    public PrincipalIterator getPrincipals() throws RepositoryException {
+        Collection coll = new ArrayList();
+        // the first element is the main principal of this user.
+        coll.add(getPrincipal());
+        // in addition add all referees.
+        PrincipalManager prMgr = getSession().getPrincipalManager();
+        for (Iterator it = getRefereeValues().iterator(); it.hasNext();) {
+            String refName = ((Value) it.next()).getString();
+            if (prMgr.hasPrincipal(refName)) {
+                coll.add(prMgr.getPrincipal(refName));
+            } else {
+                log.warn("Principal "+ refName +" unknown to PrincipalManager.");
+                coll.add(new PrincipalImpl(refName));
+            }
+        }
+        return new PrincipalIteratorAdapter(coll);
+    }
+
+    /**
+     * @see Authorizable#addReferee(Principal)
+     */
+    public synchronized boolean addReferee(Principal principal) throws RepositoryException {
+        String principalName = principal.getName();
+        Value princValue = getSession().getValueFactory().createValue(principalName);
+
+        List refereeValues = getRefereeValues();
+        if (refereeValues.contains(princValue) || getPrincipal().getName().equals(principalName)) {
+            return false;
+        }
+        if (userManager.hasAuthorizableOrReferee(principal)) {
+            throw new AuthorizableExistsException("Another authorizable already represented by or refeering to " +  principalName);
+        }
+        refereeValues.add(princValue);
+
+        userManager.setProtectedProperty(node, P_REFEREES, (Value[]) refereeValues.toArray(new Value[refereeValues.size()]));
+        return true;
+    }
+
+    /**
+     * @see Authorizable#removeReferee(Principal)
+     */
+    public synchronized boolean removeReferee(Principal principal) throws RepositoryException {
+        Value princValue = getSession().getValueFactory().createValue(principal.getName());
+        List existingValues = getRefereeValues();
+
+        if (existingValues.remove(princValue))  {
+            PropertyImpl prop = node.getProperty(P_REFEREES);
+            if (existingValues.isEmpty()) {
+                userManager.removeProtectedItem(prop, node);
+            } else {
+                userManager.setProtectedProperty(node, P_REFEREES, (Value[]) existingValues.toArray(new Value[existingValues.size()]));
+            }
+            return true;
+        }
+
+        // specified principal was not referee of this authorizable.
+        return false;
+    }
+
+    /**
+     * @see Authorizable#memberOf()
+     */
+    public Iterator memberOf() throws RepositoryException {
+        // TODO: replace by weak-refs
+        PropertyIterator itr = node.getReferences();
+        Collection tmp = new HashSet((int) itr.getSize());
+        while (itr.hasNext()) {
+            NodeImpl groupNode = (NodeImpl) itr.nextProperty().getParent();
+            if (groupNode.isNodeType(NT_REP_GROUP)) {
+                Group group = GroupImpl.create(groupNode, userManager);
+                tmp.add(group);
+            }
+        }
+        return tmp.iterator();
+    }
+
+    /**
+     * Tests if a Value exists for a property at the given name.
+     *
+     * @param name
+     * @return
+     * @throws javax.jcr.RepositoryException
+     * @see #getProperty(String)
+     */
+    public boolean hasProperty(String name) throws RepositoryException {
+        return node.hasProperty(name);
+    }
+
+    /**
+     * @param name
+     * @return the value or <code>null</code> if no value exists for the given name
+     * @throws javax.jcr.RepositoryException
+     * @see #hasProperty(String)
+     * @see Authorizable#getProperty(String)
+     */
+    public Value[] getProperty(String name) throws RepositoryException {
+        if (hasProperty(name)) {
+            Property prop = node.getProperty(name);
+            if (prop.getDefinition().isMultiple()) {
+                return prop.getValues();
+            } else {
+                return new Value[] {prop.getValue()};
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets the Value for the given name. If a value existed, it is replaced,
+     * if not it is created.
+     *
+     * @param name
+     * @param value
+     * @see Authorizable#setProperty(String, Value)
+     */
+    public synchronized void setProperty(String name, Value value) throws RepositoryException {
+		checkProtectedProperty(getSession().getQName(name));
+        try {
+			node.setProperty(name, value);
+			node.save();
+		} catch (RepositoryException e) {
+            log.warn("Failed to set Property " + name + " for Authorizable " + getID());
+            node.refresh(false);
+            throw e;
+        }
+    }
+
+    /**
+     * Sets the Value[] for the given name. If a value existed, it is replaced,
+     * if not it is created.
+     *
+     * @param name
+     * @param values
+     * @see Authorizable#setProperty(String, Value[])
+     */
+    public synchronized void setProperty(String name, Value[] values) throws RepositoryException {
+		checkProtectedProperty(getSession().getQName(name));
+        try {
+			node.setProperty(name, values);
+			node.save();
+		} catch (RepositoryException e) {
+            log.warn("Failed to set Property " + name + " for Authorizable " + getID());
+            node.refresh(false);
+            throw e;
+        }
+    }
+    /**
+     * @see Authorizable#removeProperty(String)
+     */
+    public synchronized boolean removeProperty(String name) throws RepositoryException {
+		checkProtectedProperty(getSession().getQName(name));
+        try {
+            if (node.hasProperty(name)) {
+                // 'node' is protected -> use setValue instead of Property.remove()
+                Property p = node.getProperty(name);
+                if (p.getDefinition().isMultiple()) {
+                    p.setValue((Value[]) null);
+                } else {
+                    p.setValue((Value) null);
+                }
+                node.save();
+                return true;
+            } else {
+                return false;
+            }
+        } catch (RepositoryException e) {
+            log.warn("Failed to remove Property " + name + " from Authorizable " + getID());
+            node.refresh(false);
+            throw e;
+        }
+    }
+
+    /**
+     * @see Authorizable#remove()
+     */
+    public synchronized void remove() throws RepositoryException {
+        // TODO: ev. remove group-memberships first?
+        userManager.removeProtectedItem(node, node.getParent());
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * @return node The underlying <code>Node</code> object.
+     */
+    NodeImpl getNode() throws RepositoryException {
+        return node;
+    }
+
+    SessionImpl getSession() throws RepositoryException {
+        return (SessionImpl) node.getSession();
+    }
+
+    String getPrincipalName() throws RepositoryException {
+        // principal name is mandatory property -> no check required.
+        return node.getProperty(P_PRINCIPAL_NAME).getString();
+    }
+
+    /**
+     * Check if the property to be modified/removed is one of the following that
+     * has a special meaning and must be altered using this user API:
+     * <ul>
+     * <li>rep:principalName</li>
+     * <li>rep:userId</li>
+     * <li>rep:referees</li>
+     * <li>rep:members</li>
+     * <li>rep:impersonators</li>
+     * </ul>
+     * Basically these properties are marked 'protected' in their property
+     * definition. This method is a simple utility in order to save the
+     * extra effort to modify the props just to find out later that they
+     * are in fact protected.
+     *
+     * @param pName
+     * @throws RepositoryException
+     */
+    private void checkProtectedProperty(Name pName) throws RepositoryException {
+        if (P_PRINCIPAL_NAME.equals(pName) || P_USERID.equals(pName)
+                || P_REFEREES.equals(pName) || P_MEMBERS.equals(pName)
+                || P_IMPERSONATORS.equals(pName)) {
+            throw new ConstraintViolationException("Attempt to modify protected property " + getSession().getJCRName(pName) + " of an Authorizable.");
+        }
+    }
+
+    private List getRefereeValues() throws RepositoryException {
+        List principalNames = new ArrayList();
+        if (node.hasProperty(P_REFEREES)) {
+            try {
+                Value[] refProp = node.getProperty(P_REFEREES).getValues();
+                for (int i = 0; i < refProp.length; i++) {
+                    principalNames.add(refProp[i]);
+                }
+            } catch (PathNotFoundException e) {
+                // ignore. should never occur.
+            }
+        }
+        return principalNames;
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     *
+     */
+    class NodeBasedPrincipal extends PrincipalImpl implements ItemBasedPrincipal {
+
+        /**
+         * @param name for the principal
+         */
+        NodeBasedPrincipal(String name) {
+            super(name);
+        }
+
+        //---------------------------------------------< ItemBasedPrincipal >---
+        /**
+         * Method revealing the path to the Node that represents the
+         * Authorizable this principal is created for.
+         *
+         * @return
+         * @see ItemBasedPrincipal#getPath()
+         */
+        public String getPath() throws RepositoryException {
+            return node.getPath();
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/AuthorizableImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.PropertyImpl;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * GroupImpl...
+ */
+class GroupImpl extends AuthorizableImpl implements Group {
+
+    private static final Logger log = LoggerFactory.getLogger(GroupImpl.class);
+
+    private Principal principal = null;
+
+    private GroupImpl(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+        super(node, userManager);
+    }
+
+    static Group create(NodeImpl node, UserManagerImpl userManager) throws RepositoryException {
+        if (node == null || !node.isNodeType(NT_REP_GROUP)) {
+            throw new IllegalArgumentException();
+        }
+        if(!Text.isDescendant(GROUPS_PATH, node.getPath())) {
+            throw new IllegalArgumentException("Group has to be within the Group Path");
+        }
+        return new GroupImpl(node, userManager);
+    }
+
+
+    //-------------------------------------------------------< Authorizable >---
+    /**
+     * Returns the name of the node that defines this <code>Group</code>, that
+     * has been used taking the principal name as hint.
+     *
+     * @return name of the node that defines this <code>Group</code>.
+     * @see Authorizable#getID()
+     */
+    public String getID() throws RepositoryException {
+        return getNode().getName();
+    }
+
+    /**
+     * @see Authorizable#isGroup()
+     */
+    public boolean isGroup() {
+        return true;
+    }
+
+    /**
+     * @see Authorizable#getPrincipal()
+     */
+    public Principal getPrincipal() throws RepositoryException {
+        if (principal == null) {
+            principal = new NodeBasedGroup(getPrincipalName());
+        }
+        return principal;
+    }
+
+    //--------------------------------------------------------------< Group >---
+    /**
+     * @see Group#getMembers()
+     */
+    public Iterator getMembers() throws RepositoryException {
+        return new MemberIterator(memberUUIDs().iterator());
+    }
+
+    /**
+     * @see Group#isMember(Authorizable)
+     */
+    public boolean isMember(Authorizable authorizable) throws RepositoryException {
+        if (authorizable == null || !(authorizable instanceof AuthorizableImpl)) {
+            return false;
+        } else {
+            AuthorizableImpl impl = (AuthorizableImpl) authorizable;
+            String uuid = impl.getNode().getUUID();
+            return memberUUIDs().contains(uuid);
+        }
+    }
+
+    /**
+     * @see Group#addMember(Authorizable)
+     */
+    public boolean addMember(Authorizable authorizable) throws RepositoryException {
+        if (authorizable == null || !(authorizable instanceof AuthorizableImpl)
+                || isMember(authorizable)) {
+            return false;
+        }
+        if (isCyclicMembership(authorizable)) {
+            log.warn("Attempt to create circular group membership.");
+            return false;
+        }
+
+        Node memberNode = ((AuthorizableImpl) authorizable).getNode();
+        if (memberNode.isSame(getNode())) {
+            String msg = "Attempt to add a Group as member of itself (" + getID() + ").";
+            log.warn(msg);
+            return false;
+        }
+
+        Value[] values;
+        // TODO: replace by weak-refs
+        Value added = getSession().getValueFactory().createValue(memberNode);
+        NodeImpl node = getNode();
+        if (node.hasProperty(P_MEMBERS)) {
+            Value[] old = node.getProperty(P_MEMBERS).getValues();
+            values = new Value[old.length + 1];
+            System.arraycopy(old, 0, values, 0, old.length);
+        } else {
+            values = new Value[1];
+        }
+        values[values.length - 1] = added;
+        userManager.setProtectedProperty(node, P_MEMBERS, values);
+        return true;
+    }
+
+    /**
+     * @see Group#removeMember(Authorizable)
+     */
+    public boolean removeMember(Authorizable authorizable) throws RepositoryException {
+        if (!isMember(authorizable) || !(authorizable instanceof AuthorizableImpl)) {
+            return false;
+        }
+        NodeImpl node = getNode();
+        if (!node.hasProperty(P_MEMBERS)) {
+            log.debug("Group has no members -> cannot remove member " + authorizable.getID());
+            return false;
+        }
+
+        Value toRemove = getSession().getValueFactory().createValue(((AuthorizableImpl)authorizable).getNode());
+
+        PropertyImpl property = node.getProperty(P_MEMBERS);
+        List valList = new ArrayList(Arrays.asList(property.getValues()));
+
+        if (valList.remove(toRemove)) {
+            try {
+                if (valList.isEmpty()) {
+                    userManager.removeProtectedItem(property, node);
+                } else {
+                    Value[] values = (Value[]) valList.toArray(new Value[valList.size()]);
+                    userManager.setProtectedProperty(node, P_MEMBERS, values);
+                }
+                return true;
+            } catch (RepositoryException e) {
+                // modification failed -> revert all pending changes.
+                node.refresh(false);
+                throw e;
+            }
+        } else {
+            // nothing changed
+            log.debug("Authorizable " + authorizable.getID() + " was not member of " + getID());
+            return false;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+
+    /**
+     *
+     * @return
+     * @throws RepositoryException
+     */
+    private Collection memberUUIDs() throws RepositoryException {
+        Collection tmp = new HashSet();
+        if (getNode().hasProperty(P_MEMBERS)) {
+            Property prop = getNode().getProperty(P_MEMBERS);
+            Value[] val = prop.getValues();
+            for (int i = 0; i < val.length; i++) {
+                tmp.add(val[i].getString());
+            }
+        }
+        return tmp;
+    }
+
+    /**
+     * Since {@link #isMember(Authorizable)} only detects the declared
+     * members of this group, this methods is used to avoid cyclic membership
+     * declarations.
+     *
+     * @param newMember
+     * @return true if the 'newMember' is a group and 'this' is an declared or
+     * inherited member of it.
+     */
+    private boolean isCyclicMembership(Authorizable newMember) throws RepositoryException {
+        boolean cyclic = false;
+        if (newMember.isGroup()) {
+            Group gr = (Group) newMember;
+            cyclic = gr.isMember(this);
+            if (!cyclic) {
+                PrincipalManager pmgr = getSession().getPrincipalManager();
+                for (Iterator it = pmgr.getGroupMembership(getPrincipal());
+                     it.hasNext() && !cyclic;) {
+                    cyclic = newMember.getPrincipal().equals(it.next());
+                }
+            }
+        }
+        return cyclic;
+    }
+
+    //------------------------------------------------------< inner classes >---
+    private class MemberIterator implements Iterator {
+
+        private final Iterator ids;
+        private Authorizable next;
+
+        private MemberIterator(Iterator ids) {
+            this.ids = ids;
+            next = seekNext();
+        }
+
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        public Object next() {
+            if (next == null) {
+                throw new NoSuchElementException();
+            }
+
+            Authorizable n = next;
+            next = seekNext();
+            return n;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Authorizable seekNext() {
+            Authorizable auth = null;
+            while (auth == null && ids.hasNext()) {
+                String uuid = (String) ids.next();
+                try {
+                    NodeImpl mem = (NodeImpl) getSession().getNodeByUUID(uuid);
+                    if (mem.isNodeType(NT_REP_GROUP)) {
+                        auth = create(mem, userManager);
+                    } else {
+                        auth = UserImpl.create(mem, userManager);
+                    }
+                } catch (RepositoryException e) {
+                    log.warn("Internal error while building next member.", e.getMessage());
+                    // ignore and try next
+                }
+            }
+            return auth;
+        }
+    }
+
+    /**
+     *
+     */
+    private class NodeBasedGroup extends NodeBasedPrincipal implements java.security.acl.Group {
+
+        private Set members;
+
+        private NodeBasedGroup(String name) {
+            super(name);
+        }
+
+        //----------------------------------------------------------< Group >---
+        /**
+         * @return Always <code>false</code>. Group membership must be edited
+         * using the enclosing <code>GroupImpl</code> object.
+         * @see java.security.acl.Group#addMember(Principal)
+         */
+        public boolean addMember(Principal user) {
+            return false;
+        }
+
+        /**
+         * Returns true, if the given <code>Principal</code> is represented by
+         * a Authorizable, that is a member of the underlying UserGroup.
+         *
+         * @see java.security.acl.Group#isMember(Principal)
+         */
+        public boolean isMember(Principal member) {
+            Collection members = getMembers();
+            if (members.contains(member)) {
+                // shortcut.
+                return true;
+            }
+
+            // test if member of a member-group
+            for (Iterator it = members.iterator(); it.hasNext();) {
+                Principal p = (Principal) it.next();
+                if (p instanceof java.security.acl.Group &&
+                        ((java.security.acl.Group) p).isMember(member)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return Always <code>false</code>. Group membership must be edited
+         * using the enclosing <code>GroupImpl</code> object.
+         *
+         * @see java.security.acl.Group#isMember(Principal)
+         */
+        public boolean removeMember(Principal user) {
+            return false;
+        }
+
+        /**
+         * Return all principals that refer to every member of the underlying
+         * user group.
+         *
+         * @see java.security.acl.Group#members()
+         */
+        public Enumeration members() {
+            return Collections.enumeration(getMembers());
+        }
+
+        //---------------------------------------------------< Serializable >---
+        /**
+         * implement the writeObject method to assert initalization of all members
+         * before serialization.
+         *
+         * @param stream
+         * @throws IOException
+         */
+        private void writeObject(ObjectOutputStream stream) throws IOException {
+            getMembers();
+            stream.defaultWriteObject();
+        }
+
+        //----------------------------------------------------------------------
+        private Collection getMembers() {
+            if (members == null) {
+                members = new HashSet();
+                try {
+                    for (Iterator it = GroupImpl.this.getMembers(); it.hasNext();) {
+                        Authorizable authrz = (Authorizable) it.next();
+                        // TODO: check again. only add main principal, since
+                        // 'referees' belong to a different provider and should
+                        // not be exposed here
+                        members.add(authrz.getPrincipal());
+                    }
+                } catch (RepositoryException e) {
+                    // should not occur.
+                    log.error("Unable to retrieve Group members.");
+                }
+            }
+            return members;
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/GroupImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.core.security.SystemPrincipal;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.principal.NoSuchPrincipalException;
+import org.apache.jackrabbit.core.security.principal.PrincipalIteratorAdapter;
+import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
+import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.PropertyImpl;
+import org.apache.jackrabbit.value.StringValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * ImpersonationImpl
+ */
+class ImpersonationImpl implements Impersonation, UserConstants {
+
+    private static final Logger log = LoggerFactory.getLogger(ImpersonationImpl.class);
+
+    private final UserImpl user;
+    private final UserManagerImpl userManager;
+
+    ImpersonationImpl(UserImpl user, UserManagerImpl userManager) throws RepositoryException {
+        this.user = user;
+        this.userManager = userManager;
+    }
+
+    //------------------------------------------------------< Impersonation >---
+    /**
+     * @see Impersonation#getImpersonators()
+     */
+    public PrincipalIterator getImpersonators() throws RepositoryException {
+        Set impersonators = getImpersonatorNames();
+        if (impersonators.isEmpty()) {
+            return PrincipalIteratorAdapter.EMPTY;
+        } else {
+            final PrincipalManager pMgr = user.getSession().getPrincipalManager();
+
+            Set s = new HashSet();
+            for (Iterator it = impersonators.iterator(); it.hasNext();) {
+                String pName = it.next().toString();
+                Principal p = null;
+                if (pMgr.hasPrincipal(pName)) {
+                    try {
+                        p = pMgr.getPrincipal(pName);
+                    } catch (NoSuchPrincipalException e) {
+                        // should never get here.
+                    }
+                }
+                if (p == null) {
+                    log.debug("Impersonator " + pName + " does not correspond to a known Principal.");
+                    p = new PrincipalImpl(pName);
+                }
+                s.add(p);
+
+            }
+            return new PrincipalIteratorAdapter(s);
+        }
+    }
+
+    /**
+     * @see Impersonation#grantImpersonation(Principal)
+     */
+    public synchronized boolean grantImpersonation(Principal principal) throws RepositoryException {
+        if (principal instanceof AdminPrincipal || principal instanceof SystemPrincipal) {
+            log.debug("Admin and System principal are already granted impersonation.");
+            return false;
+        }
+
+        // make sure the given principals belong to an existing authorizable
+        Authorizable auth = user.userManager.getAuthorizable(principal);
+        if (auth == null || auth.isGroup()) {
+            // TODO: check again if this assumption is correct...
+            log.debug("Cannot grant impersonation to a principal that is a Group " +
+                      "or an unknown Authorizable.");
+            return false;
+        }
+
+        String pName = principal.getName();
+        // make sure user does not impersonate himself
+        for (PrincipalIterator it = user.getPrincipals(); it.hasNext();) {
+            Principal p = it.nextPrincipal();
+            if (p.getName().equals(pName)) {
+                log.debug("Cannot grant impersonation to oneself.");
+                return false;
+            }
+        }
+
+        boolean granted = false;
+        Set impersonators = getImpersonatorNames();
+        if (impersonators.add(pName)) {
+            updateImpersonatorNames(impersonators);
+            granted = true;
+        }
+        return granted;
+    }
+
+    /**
+     * @see Impersonation#revokeImpersonation(Principal)
+     */
+    public synchronized boolean revokeImpersonation(Principal principal) throws RepositoryException {
+        if (principal instanceof AdminPrincipal || principal instanceof SystemPrincipal) {
+            log.debug("Admin and System principal are always granted impersonation.");
+            return false;
+        }
+
+        boolean revoked = false;
+        String pName = principal.getName();
+
+        Set impersonators = getImpersonatorNames();
+        if (impersonators.remove(pName)) {
+            updateImpersonatorNames(impersonators);
+            revoked = true;
+        }
+        return revoked;
+    }
+
+    /**
+     * @see Impersonation#allows(Subject) 
+     */
+    public boolean allows(Subject subject) throws RepositoryException {
+        if (subject == null) {
+            return false;
+        }
+        //shortcut admin/system -> always allowed
+        if (!subject.getPrincipals(AdminPrincipal.class).isEmpty()
+                || !subject.getPrincipals(SystemPrincipal.class).isEmpty()) {
+            return true;
+        }
+
+        Set principalNames = new HashSet();
+        for (Iterator it = subject.getPrincipals().iterator(); it.hasNext();) {
+            principalNames.add(((Principal) it.next()).getName());
+        }
+
+        boolean allows = false;
+        try {
+            Set impersonators = getImpersonatorNames();
+            allows = impersonators.removeAll(principalNames);
+        } catch (RepositoryException e) {
+            // should never get here
+            log.debug(e.getMessage());
+        }
+        return allows;
+    }
+
+    //------------------------------------------------------------< private >---
+
+    private Set getImpersonatorNames() throws RepositoryException {
+        Set princNames = new HashSet();
+        if (user.getNode().hasProperty(P_IMPERSONATORS)) {
+            Value[] vs = user.getNode().getProperty(P_IMPERSONATORS).getValues();
+            for (int i = 0; i < vs.length; i++) {
+                princNames.add(vs[i].getString());
+            }
+        }
+        return princNames;
+    }
+
+    private void updateImpersonatorNames(Set principalNames) throws RepositoryException {
+        NodeImpl userNode = user.getNode();
+        try {
+            String[] pNames = (String[]) principalNames.toArray(new String[principalNames.size()]);
+            if (pNames.length == 0) {
+                PropertyImpl prop = userNode.getProperty(P_IMPERSONATORS);
+                userManager.removeProtectedItem(prop, userNode);
+            } else {
+                Value[] values = new Value[pNames.length];
+                for (int i = 0; i < pNames.length; i++) {
+                    values[i] = new StringValue(pNames[i]);
+                }
+                userManager.setProtectedProperty(userNode, P_IMPERSONATORS, values);
+            }
+        } catch (RepositoryException e) {
+            // revert pending changes
+            userNode.refresh(false);
+            throw e;
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/ImpersonationImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.core.SessionImpl;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Collections;
+
+/**
+ * 
+ */
+class IndexNodeResolver extends NodeResolver {
+
+    private final QueryManager queryManager;
+
+    IndexNodeResolver(SessionImpl session)
+            throws RepositoryException {
+        super(session);
+        queryManager = session.getWorkspace().getQueryManager();
+    }
+
+    //-------------------------------------------------------< NodeResolver >---
+    /**
+     * @inheritDoc
+     */
+    public Node findNode(Name propertyName, String value, Name ntName) throws RepositoryException {
+        Query query = buildQuery(value, Collections.singleton(propertyName), ntName, true, 1);
+        NodeIterator res = query.execute().getNodes();
+        if (res.hasNext()) {
+            return res.nextNode();
+        }
+        return null;
+    }
+
+    /**
+     * Search nodes. Take the arguments as search criteria.
+     * The queried value has to be a string fragment of one of the Properties
+     * contained in the given set. And the node have to be of a requested nodetype
+     *
+     * @param propertyNames
+     * @param value
+     * @param ntName NodeType the hits have to have
+     * @param exact  if <code>true</code> match must be exact
+     * @return
+     * @throws javax.jcr.RepositoryException
+     */
+    public NodeIterator findNodes(Set propertyNames, String value, Name ntName,
+                                  boolean exact, long maxSize) throws RepositoryException {
+        Query query = buildQuery(value, propertyNames, ntName, exact, maxSize);
+        return query.execute().getNodes();
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     *
+     * @param value
+     * @param props
+     * @param ntName
+     * @param exact
+     * @param maxSize Currently ignored!
+     * @return
+     * @throws RepositoryException
+     */
+    private Query buildQuery(String value, Set props, Name ntName,
+                             boolean exact, long maxSize)
+            throws RepositoryException {
+        // TODO: include maxSize in query statement.
+        StringBuffer stmt = new StringBuffer("/jcr:root");
+        stmt.append(getSearchRoot(ntName));
+        stmt.append("//element(*,");
+        stmt.append(getSession().getJCRName(ntName));
+        stmt.append(")[");
+
+        int i = 0;
+        Iterator itr = props.iterator();
+        while (itr.hasNext()) {
+            stmt.append((exact) ? "@" : "jcr:like(@");
+            String pName = getSession().getJCRName((Name) itr.next());
+            stmt.append(ISO9075.encode(pName));
+            stmt.append((exact) ? "='" : ",'%");
+            stmt.append(value);
+            stmt.append((exact) ? "'" : "%')");
+            if (++i < props.size()) {
+                stmt.append(" or ");
+            }
+        }
+        stmt.append("]");
+        return queryManager.createQuery(stmt.toString(), Query.XPATH);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/IndexNodeResolver.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.spi.Name;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Resolver: searches for Principals stored in Nodes of a {@link javax.jcr.Workspace}
+ * which match a certain criteria<p>
+ * The principalNames are assumed to be stored in properties.
+ */
+abstract class NodeResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(NodeResolver.class);
+
+    private final SessionImpl session;
+
+    /**
+     * Create a new <code>NodeResolver</code>.
+     *
+     * @param session;
+     * @throws RepositoryException if instanciation fails
+     */
+    NodeResolver(SessionImpl session)
+            throws RepositoryException {
+
+        this.session = session;
+    }
+
+    /**
+     * Get the first node that matches <code>ntName</code> and has a
+     * property whose value exactly matches the given value. Same as
+     * {@link #findNodes(Set,String,Name,boolean,long)} but returning a single node or <code>null</code>.
+     *
+     * @param propertyName
+     * @param value
+     * @param ntName
+     * @return The first node that has a property with the given propertyName that
+     * exactly matches the given value or <code>null</code>.
+     * @throws RepositoryException
+     */
+    public abstract Node findNode(Name propertyName, String value, Name ntName) throws RepositoryException;
+
+    /**
+     * Search for Nodes which contain an exact match for the given value in
+     * their property as indicated by the propertyName argument.<br>
+     * Same as {@link #findNodes(Set,String,Name,boolean,long)}; where
+     * the maxSize parameters is set to {@link Long#MAX_VALUE)}.
+     *
+     * @param propertyName property to be searched
+     * @param value        value to be matched
+     * @param ntName
+     * @param exact        if <code>true</code> value has to match exactly
+     * @return matching nodes (or an empty iterator if no match was found).
+     */
+    public NodeIterator findNodes(Name propertyName, String value, Name ntName, boolean exact)
+            throws RepositoryException {
+        return findNodes(Collections.singleton(propertyName), value, ntName, exact, Long.MAX_VALUE);
+    }
+
+    /**
+     * Search nodes. Take the arguments as search cirteria.
+     * The queried value has to be a string fragment of one of the Properties
+     * contained in the given set. And the node have to be of a requested nodetype
+     *
+     * @param propertyNames
+     * @param value
+     * @param ntName NodeType the hits have to have
+     * @param exact  if <code>true</code> match must be exact
+     * @param maxSize maximal number of results to search for.
+     * @return
+     * @throws RepositoryException
+     */
+    public abstract NodeIterator findNodes(Set propertyNames, String value, Name ntName,
+                                           boolean exact, long maxSize)
+            throws RepositoryException;
+
+    /**
+     * @return Session this instance has been constructed with
+     */
+    SessionImpl getSession() {
+        return session;
+    }
+
+    String getSearchRoot(Name ntName) {
+        String searchRoot;
+        if (UserConstants.NT_REP_USER.equals(ntName)) {
+            searchRoot = UserConstants.USERS_PATH;
+        } else if (UserConstants.NT_REP_GROUP.equals(ntName)) {
+            searchRoot = UserConstants.GROUPS_PATH;
+        } else {
+            searchRoot = UserConstants.AUTHORIZABLES_PATH;
+        }
+        return searchRoot;
+    }
+}
+
+

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/NodeResolver.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java?rev=638834&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java Wed Mar 19 06:56:13 2008
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.core.security.user;
+
+import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.spi.Name;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Collections;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ *
+ */
+class TraversingNodeResolver extends NodeResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(TraversingNodeResolver.class);
+
+    /**
+     * Additonally to the NodeType-Argument the resolvers searched is narrowed
+     * by indicating a Path to an {@link javax.jcr.Item} as start for the search
+     *
+     * @param session      to use for repository access
+     */
+    TraversingNodeResolver(SessionImpl session) throws RepositoryException {
+        super(session);
+    }
+
+    //-------------------------------------------------------< NodeResolver >---
+    /**
+     * @inheritDoc
+     */
+    public Node findNode(Name propertyName, String value, Name ntName) throws RepositoryException {
+        try {
+            Node root = (Node) getSession().getItem(getSearchRoot(ntName));
+            NodeIterator nodes = collectNodes(value, Collections.singleton(propertyName), ntName,
+                    root.getNodes(), true, 1);
+            if (nodes.hasNext()) {
+                return nodes.nextNode();
+            }
+        } catch (PathNotFoundException e) {
+            log.warn("Error while searching for node having a property " + propertyName + " with value " + value);
+        }
+
+        return null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public NodeIterator findNodes(Set propertyNames, String value, Name ntName,
+                                  boolean exact, long maxSize) throws RepositoryException {
+
+        NodeImpl root = (NodeImpl) getSession().getItem(getSearchRoot(ntName));
+        return collectNodes(value, propertyNames, ntName, root.getNodes(), exact, maxSize);
+    }
+
+    //--------------------------------------------------------------------------
+    /**
+     * searches the given value in the range of the given NodeIterator.
+     * recurses unitll all matching values in all configured props are found.
+     *
+     * @param value   the value to be found in the nodes
+     * @param props   property to be searched, or null if {@link javax.jcr.Item#getName()}
+     * @param ntName  to filter search
+     * @param nodes   range of nodes and descendants to be searched
+     * @param exact   if set to true the value has to match exactly else a
+     * substring is searched
+     * @param maxSize
+     */
+    private NodeIterator collectNodes(String value, Set props, Name ntName,
+                                      NodeIterator nodes, boolean exact,
+                                      long maxSize) {
+        Set matches = new HashSet();
+        collectNodes(value, props, ntName, nodes, matches, exact, maxSize);
+        return new NodeIteratorAdapter(matches);
+    }
+
+    /**
+     * searches the given value in the range of the given NodeIterator.
+     * recurses unitll all matching values in all configured properties are found.
+     *
+     * @param value         the value to be found in the nodes
+     * @param propertyNames property to be searched, or null if {@link javax.jcr.Item#getName()}
+     * @param nodeTypeName  name of nodetypes to search
+     * @param itr           range of nodes and descendants to be searched
+     * @param matches       Set of found matches to append results
+     * @param exact         if set to true the value has to match exact
+     * @param maxSize
+     */
+    private void collectNodes(String value, Set propertyNames,
+                              Name nodeTypeName, NodeIterator itr,
+                              Set matches, boolean exact, long maxSize) {
+        while (itr.hasNext()) {
+            NodeImpl node = (NodeImpl) itr.nextNode();
+            try {
+                if (matches(node, nodeTypeName, propertyNames, value, exact)) {
+                    matches.add(node);
+                    maxSize--;
+                }
+                if (node.hasNodes() && maxSize > 0) {
+                    collectNodes(value, propertyNames, nodeTypeName,
+                            node.getNodes(), matches, exact, maxSize);
+                }
+            } catch (RepositoryException e) {
+                log.warn("failed to access Node at " + e);
+            }
+        }
+    }
+
+    /**
+     * 
+     * @param node
+     * @param nodeTypeName
+     * @param propertyNames
+     * @param value
+     * @param exact
+     * @return
+     * @throws RepositoryException
+     */
+    private boolean matches(NodeImpl node, Name nodeTypeName,
+                            Collection propertyNames, String value,
+                            boolean exact) throws RepositoryException {
+
+        boolean match = false;
+        if (node.isNodeType(nodeTypeName)) {
+            try {
+                if (propertyNames.isEmpty()) {
+                    match = (exact) ? node.getName().equals(value) :
+                            node.getName().matches(".*"+value+".*");
+                } else {
+                    Iterator pItr = propertyNames.iterator();
+                    while (!match && pItr.hasNext()) {
+                        Name propertyName = (Name) pItr.next();
+                        if (node.hasProperty(propertyName)) {
+                            String toMatch = node.getProperty(propertyName).getString();
+                            match = (exact) ?
+                                    toMatch.equals(value) :
+                                    toMatch.matches(".*"+value+".*");
+                        }
+                    }
+                }
+            } catch (PatternSyntaxException pe) {
+                log.debug("couldn't search for {}, pattern invalid: {}",
+                          value, pe.getMessage());
+            }
+        }
+        return match;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/TraversingNodeResolver.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url