Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 65544 invoked from network); 19 Mar 2008 13:59:37 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 19 Mar 2008 13:59:37 -0000 Received: (qmail 92179 invoked by uid 500); 19 Mar 2008 13:59:30 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 92153 invoked by uid 500); 19 Mar 2008 13:59:30 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 92139 invoked by uid 99); 19 Mar 2008 13:59:30 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 19 Mar 2008 06:59:30 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 19 Mar 2008 13:58:51 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 0B0F21A985B; Wed, 19 Mar 2008 06:58:38 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Wed, 19 Mar 2008 13:57:11 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080319135838.0B0F21A985B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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; + +/** + * SimpleSecurityManager: 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}.
+ * 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 null 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 Node 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: + *
    + *
  • rep:principalName
  • + *
  • rep:userId
  • + *
  • rep:referees
  • + *
  • rep:members
  • + *
  • rep:impersonators
  • + *
+ * 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 Group, that + * has been used taking the principal name as hint. + * + * @return name of the node that defines this Group. + * @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 false. Group membership must be edited + * using the enclosing GroupImpl object. + * @see java.security.acl.Group#addMember(Principal) + */ + public boolean addMember(Principal user) { + return false; + } + + /** + * Returns true, if the given Principal 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 false. Group membership must be edited + * using the enclosing GroupImpl 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 true 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

+ * 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 NodeResolver. + * + * @param session; + * @throws RepositoryException if instanciation fails + */ + NodeResolver(SessionImpl session) + throws RepositoryException { + + this.session = session; + } + + /** + * Get the first node that matches ntName 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 null. + * + * @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 null. + * @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.
+ * 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 true 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 true 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