Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 16875CDF8 for ; Tue, 8 May 2012 07:56:24 +0000 (UTC) Received: (qmail 18568 invoked by uid 500); 8 May 2012 07:56:24 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 18542 invoked by uid 500); 8 May 2012 07:56:23 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-commits@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 18510 invoked by uid 99); 8 May 2012 07:56:22 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 08 May 2012 07:56:22 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 08 May 2012 07:56:19 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id BCB112388865; Tue, 8 May 2012 07:55:59 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1335373 - /jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ Date: Tue, 08 May 2012 07:55:59 -0000 To: oak-commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120508075559.BCB112388865@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: angela Date: Tue May 8 07:55:58 2012 New Revision: 1335373 URL: http://svn.apache.org/viewvc?rev=1335373&view=rev Log: OAK-50 : User management (WIP) Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java Tue May 8 07:55:58 2012 @@ -50,6 +50,7 @@ abstract class AuthorizableImpl implemen static final String NT_REP_AUTHORIZABLE = "rep:Authorizable"; static final String NT_REP_USER = "rep:User"; static final String NT_REP_GROUP = "rep:Group"; + static final String NT_REP_MEMBERS = "rep:Members"; static final String REP_PRINCIPAL_NAME = "rep:principalName"; static final String REP_PASSWORD = "rep:password"; static final String REP_DISABLED = "rep:disabled"; @@ -414,10 +415,6 @@ abstract class AuthorizableImpl implemen } MembershipManager membershipManager = userManager.getMembershipManager(); - if (includeInherited) { - return membershipManager.getMembership(this); - } else { - return membershipManager.getDeclaredMembership(this); - } + return membershipManager.getMembership(this, includeInherited); } } \ No newline at end of file Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java?rev=1335373&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java (added) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableIterator.java Tue May 8 07:55:58 2012 @@ -0,0 +1,122 @@ +/* + * 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.oak.jcr.security.user; + +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.commons.flat.PropertySequence; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * AuthorizableIterator... + */ +class AuthorizableIterator implements Iterator { + + private static final Logger log = LoggerFactory.getLogger(AuthorizableIterator.class); + + private final Iterator nodeIds; + private final AuthorizableTypePredicate predicate; + private final UserManagerImpl userManager; + private final long size; + + private Authorizable next; + + AuthorizableIterator(Value[] authorizableNodeIds, int authorizableType, UserManagerImpl userManager) { + this(Arrays.asList(authorizableNodeIds).iterator(), authorizableType, userManager, authorizableNodeIds.length); + } + + AuthorizableIterator(PropertySequence authorizableNodeIds, int authorizableType, UserManagerImpl userManager) { + this(authorizableNodeIds.iterator(), authorizableType, userManager, -1); // TODO calculate size here + } + + AuthorizableIterator(PropertyIterator authorizableNodeIds, int authorizableType, UserManagerImpl userManager) { + this(authorizableNodeIds, authorizableType, userManager, authorizableNodeIds.getSize()); + } + + private AuthorizableIterator(Iterator nodeIds, int authorizableType, + UserManagerImpl userManager, long size) { + this.nodeIds = nodeIds; + this.predicate = new AuthorizableTypePredicate(authorizableType); + this.userManager = userManager; + this.size = size; + + next = fetchNext(); + } + + //-----------------------------------------------------------< Iterator >--- + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public Authorizable next() { + if (next == null) { + throw new NoSuchElementException(); + } + + Authorizable a = next; + next = fetchNext(); + return a; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + //-------------------------------------------------------------------------- + long getSize() { + return size; + } + + private Authorizable fetchNext() { + while (nodeIds.hasNext()) { + try { + String nid = getNodeId(nodeIds.next()); + Node n = userManager.getSession().getNodeByIdentifier(nid); + Authorizable a = userManager.getAuthorizable(n); + if (a != null && predicate.evaluate(a)) { + return a; + } + } catch (RepositoryException e) { + log.debug(e.getMessage()); + } + } + + return null; + } + + private static String getNodeId(Object o) throws RepositoryException { + if (o instanceof Value) { + return ((Value) o).getString(); + } else if (o instanceof Property) { + return ((Property) o).getString(); + } else { + return o.toString(); + } + } +} \ No newline at end of file Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java?rev=1335373&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java (added) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableTypePredicate.java Tue May 8 07:55:58 2012 @@ -0,0 +1,59 @@ +/* + * 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.oak.jcr.security.user; + +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.commons.predicate.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AuthorizableTypePredicate... + */ +class AuthorizableTypePredicate implements Predicate { + + /** + * logger instance + */ + private static final Logger log = LoggerFactory.getLogger(AuthorizableTypePredicate.class); + + private final int authorizableType; + + AuthorizableTypePredicate(int authorizableType) { + this.authorizableType = authorizableType; + } + + @Override + public boolean evaluate(Object object) { + if (object instanceof Authorizable) { + Authorizable a = (Authorizable) object; + switch (authorizableType) { + case UserManager.SEARCH_TYPE_AUTHORIZABLE: + return true; + case UserManager.SEARCH_TYPE_GROUP: + return a.isGroup(); + case UserManager.SEARCH_TYPE_USER: + return !a.isGroup(); + default: + log.warn("Illegal authorizable type " + authorizableType); + return false; + } + } + return false; + } +} \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java Tue May 8 07:55:58 2012 @@ -118,15 +118,20 @@ class GroupImpl extends AuthorizableImpl if (authorizableImpl.isGroup()) { if (getID().equals(authorizableImpl.getID())) { String msg = "Attempt to add a group as member of itself (" + getID() + ")."; - log.warn(msg); + log.debug(msg); return false; } if (((Group) authorizableImpl).isMember(this)) { - log.warn("Attempt to create circular group membership."); + log.debug("Attempt to create circular group membership."); return false; } } + if (isDeclaredMember(authorizable)) { + log.debug("Authorizable {} is already declared member of {}", authorizable.getID(), getID()); + return false; + } + return getUserManager().getMembershipManager().addMember(this, authorizableImpl); } @@ -141,9 +146,9 @@ class GroupImpl extends AuthorizableImpl } if (isEveryone()) { return false; + } else { + return getUserManager().getMembershipManager().removeMember(this, (AuthorizableImpl) authorizable); } - - return getUserManager().getMembershipManager().removeMember(this, (AuthorizableImpl) authorizable); } //-------------------------------------------------------------------------- @@ -158,10 +163,9 @@ class GroupImpl extends AuthorizableImpl private Iterator getMembers(boolean includeInherited) throws RepositoryException { if (isEveryone()) { return getUserManager().findAuthorizables(AuthorizableImpl.REP_PRINCIPAL_NAME, null, UserManager.SEARCH_TYPE_AUTHORIZABLE); - } else if (includeInherited) { - return getUserManager().getMembershipManager().getMembers(this); } else { - return getUserManager().getMembershipManager().getDeclaredMembers(this); + MembershipManager mMgr = getUserManager().getMembershipManager(); + return mMgr.getMembers(this, UserManager.SEARCH_TYPE_AUTHORIZABLE, includeInherited); } } @@ -178,14 +182,15 @@ class GroupImpl extends AuthorizableImpl private boolean isMember(Authorizable authorizable, boolean includeInherited) throws RepositoryException { if (!isValidAuthorizableImpl(authorizable)) { return false; - } else if (getNode().isSame(((AuthorizableImpl) authorizable).getNode())) { - return false; - } else if (isEveryone()) { + } + + if (isEveryone()) { return true; - } else if (includeInherited) { - return getUserManager().getMembershipManager().hasMember(this, (AuthorizableImpl) authorizable); + } else if (getID().equals(authorizable.getID())) { + return false; } else { - return getUserManager().getMembershipManager().hasDeclaredMember(this, (AuthorizableImpl) authorizable); + AuthorizableImpl authorizableImpl = (AuthorizableImpl) authorizable; + return getUserManager().getMembershipManager().isMember(this, authorizableImpl, includeInherited); } } Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java?rev=1335373&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java (added) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/InheritingAuthorizableIterator.java Tue May 8 07:55:58 2012 @@ -0,0 +1,64 @@ +/* + * 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.oak.jcr.security.user; + +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.commons.iterator.FilterIterator; +import org.apache.jackrabbit.commons.iterator.LazyIteratorChain; +import org.apache.jackrabbit.commons.predicate.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.RepositoryException; +import java.util.HashSet; +import java.util.Iterator; + +/** + * InheritingAuthorizableIterator... + */ +public class InheritingAuthorizableIterator extends FilterIterator { + + /** + * logger instance + */ + private static final Logger log = LoggerFactory.getLogger(InheritingAuthorizableIterator.class); + + InheritingAuthorizableIterator(Iterator> inheritingIterator) { + super(new LazyIteratorChain(inheritingIterator), new ProcessedIdPredicate()); + } + + /** + * + */ + private static final class ProcessedIdPredicate implements Predicate { + + private final HashSet processedIds = new HashSet(); + + @Override + public boolean evaluate(Object object) { + if (object instanceof Group) { + try { + return processedIds.add(((Group) object).getID()); + } catch (RepositoryException e) { + // TODO + } + } + return false; + } + } +} \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/MembershipManager.java Tue May 8 07:55:58 2012 @@ -16,14 +16,32 @@ */ package org.apache.jackrabbit.oak.jcr.security.user; +import org.apache.commons.collections.iterators.EmptyIterator; +import org.apache.commons.collections.iterators.IteratorChain; +import org.apache.commons.collections.iterators.SingletonIterator; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.commons.flat.BTreeManager; +import org.apache.jackrabbit.commons.flat.ItemSequence; +import org.apache.jackrabbit.commons.flat.PropertySequence; +import org.apache.jackrabbit.commons.flat.Rank; +import org.apache.jackrabbit.commons.flat.TreeManager; +import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter; import org.apache.jackrabbit.oak.jcr.SessionDelegate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; +import javax.jcr.Value; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.Iterator; +import java.util.List; /** * MembershipManager... @@ -34,49 +52,245 @@ class MembershipManager { private final UserManagerImpl userManager; private final SessionDelegate sessionDelegate; + private final int memberSplitSize; - MembershipManager(UserManagerImpl userManager, SessionDelegate sessionDelegate) { + private final String repMembers; + + MembershipManager(UserManagerImpl userManager, int memberSplitSize, SessionDelegate sessionDelegate) { this.userManager = userManager; this.sessionDelegate = sessionDelegate; - } + this.memberSplitSize = memberSplitSize; - Iterator getDeclaredMembership(AuthorizableImpl authorizable) throws RepositoryException { - // TODO - return null; + repMembers = sessionDelegate.getNamePathMapper().getJcrName(AuthorizableImpl.REP_MEMBERS); } - Iterator getMembership(AuthorizableImpl authorizable) throws RepositoryException { - // TODO - return null; + Iterator getMembership(AuthorizableImpl authorizable, boolean includeInherited) throws RepositoryException { + PropertyIterator refs = null; + try { + String nodeID = authorizable.getNode().getIdentifier(); + refs = authorizable.getNode().getWeakReferences(null); + } catch (RepositoryException e) { + log.error("Failed to retrieve membership references of " + authorizable.getID(), e); + // TODO retrieve by traversing + } + + if (refs != null) { + AuthorizableIterator iterator = new AuthorizableIterator(refs, UserManager.SEARCH_TYPE_GROUP, userManager); + if (includeInherited) { + return getAllMembership(iterator); + } else { + return new RangeIteratorAdapter(iterator, iterator.getSize()); + } + } else { + return RangeIteratorAdapter.EMPTY; + } + } - boolean hasDeclaredMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { - // TODO + boolean isMember(GroupImpl group, AuthorizableImpl authorizable, boolean includeInherited) throws RepositoryException { + Node node = group.getNode(); + + if (includeInherited) { + Iterator groups = getMembership(authorizable, true); + String id = group.getID(); + while (groups.hasNext()) { + if (id.equals(groups.next().getID())) { + return true; + } + } + } else { + if (useMemberNode(node)) { + if (node.hasNode(repMembers)) { + // TODO: fix.. testing for property name isn't correct. + PropertySequence propertySequence = getPropertySequence(node.getNode(repMembers)); + return propertySequence.hasItem(authorizable.getID()); + } + } else { + if (node.hasProperty(repMembers)) { + Value[] members = node.getProperty(repMembers).getValues(); + for (Value v : members) { + if (authorizable.getNode().getIdentifier().equals(v.getString())) { + return true; + } + } + } + } + } + // no a member of the specified group return false; } - boolean hasMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { - // TODO + Iterator getMembers(GroupImpl group, int authorizableType, + boolean includeInherited) throws RepositoryException { + Node node = group.getNode(); + AuthorizableIterator iterator = null; + if (useMemberNode(node)) { + if (node.hasNode(repMembers)) { + PropertySequence propertySequence = getPropertySequence(node.getNode(repMembers)); + iterator = new AuthorizableIterator(propertySequence, authorizableType, userManager); + } + } else { + if (node.hasProperty(repMembers)) { + Value[] members = node.getProperty(repMembers).getValues(); + iterator = new AuthorizableIterator(members, authorizableType, userManager); + } + } + + if (iterator != null) { + if (includeInherited) { + return getAllMembers(iterator, authorizableType); + } else { + return new RangeIteratorAdapter(iterator, iterator.getSize()); + } + } else { + return RangeIteratorAdapter.EMPTY; + } + } + + boolean addMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { + Node node = group.getNode(); + if (useMemberNode(node)) { + // TODO: modify items on oak-api directly + } else { + Node memberNode = authorizable.getNode(); + Value[] values; + Value toAdd = sessionDelegate.getValueFactory().createValue(memberNode, true); + if (node.hasProperty(repMembers)) { + Value[] old = node.getProperty(repMembers).getValues(); + values = new Value[old.length + 1]; + System.arraycopy(old, 0, values, 0, old.length); + } else { + values = new Value[1]; + } + values[values.length - 1] = toAdd; + + userManager.setInternalProperty(node, repMembers, values); + } + return true; + } + + boolean removeMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { + Node node = group.getNode(); + + if (useMemberNode(node)) { + if (node.hasNode(repMembers)) { + Node nMembers = node.getNode(repMembers); + PropertySequence properties = getPropertySequence(nMembers); + String propName = authorizable.getNode().getName(); + // TODO: fix.. testing for property name isn't correct. + if (properties.hasItem(propName)) { + Property p = properties.getItem(propName); + userManager.removeInternalProperty(p.getParent(), propName); + } + return true; + } + } else { + if (node.hasProperty(repMembers)) { + Value toRemove = sessionDelegate.getValueFactory().createValue((authorizable).getNode(), true); + Property property = node.getProperty(repMembers); + List valList = new ArrayList(Arrays.asList(property.getValues())); + + if (valList.remove(toRemove)) { + if (valList.isEmpty()) { + userManager.removeInternalProperty(node, repMembers); + } else { + Value[] values = valList.toArray(new Value[valList.size()]); + userManager.setInternalProperty(node, repMembers, values); + } + return true; + } + } + } + + // nothing changed + log.debug("Authorizable {} was not member of {}", authorizable.getID(), group.getID()); return false; } - Iterator getDeclaredMembers(GroupImpl group) throws RepositoryException { - // TODO - return null; + //-------------------------------------------------------------------------- + private boolean useMemberNode(Node n) throws RepositoryException { + return memberSplitSize >= 4 && !n.hasProperty(repMembers); } - Iterator getMembers(GroupImpl group) throws RepositoryException { - // TODO - return null; + private PropertySequence getPropertySequence(Node nMembers) throws RepositoryException { + Comparator order = Rank.comparableComparator(); + int minChildren = memberSplitSize / 2; + TreeManager treeManager = new BTreeManager(nMembers, minChildren, memberSplitSize, order, false); + return ItemSequence.createPropertySequence(treeManager); } - boolean addMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { - // todo - return false; + /** + * Returns an iterator of authorizables which includes all indirect members + * of the given iterator of authorizables. + * + * @param authorizables + * @param authorizableType + * @return Iterator of Authorizable objects + */ + private Iterator getAllMembers(final Iterator authorizables, + final int authorizableType) { + Iterator> inheritedMembers = new Iterator>() { + @Override + public boolean hasNext() { + return authorizables.hasNext(); + } + + @Override + public Iterator next() { + Authorizable next = authorizables.next(); + return new IteratorChain(new SingletonIterator(next), inherited(next)); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private Iterator inherited(Authorizable authorizable) { + if (authorizable.isGroup()) { + try { + return getMembers(((GroupImpl) authorizable), authorizableType, true); + } catch (RepositoryException e) { + log.warn("Could not determine members of " + authorizable, e); + } + } + return EmptyIterator.INSTANCE; + } + }; + + return new InheritingAuthorizableIterator(inheritedMembers); } - boolean removeMember(GroupImpl group, AuthorizableImpl authorizable) throws RepositoryException { - // todo - return false; + private Iterator getAllMembership(final AuthorizableIterator membership) { + Iterator> inheritedMembership = new Iterator>() { + @Override + public boolean hasNext() { + return membership.hasNext(); + } + + @Override + public Iterator next() { + Group next = (Group) membership.next(); + return new IteratorChain(new SingletonIterator(next), inherited((AuthorizableImpl) next)); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + private Iterator inherited(AuthorizableImpl authorizable) { + if (authorizable.isGroup()) { + try { + return getMembership(authorizable, true); + } catch (RepositoryException e) { + log.warn("Could not determine members of " + authorizable, e); + } + } + return EmptyIterator.INSTANCE; + } + }; + + return new InheritingAuthorizableIterator(inheritedMembership); } } \ No newline at end of file Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java Tue May 8 07:55:58 2012 @@ -30,6 +30,17 @@ public class UserManagerConfig { private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class); /** + * If this parameter is present group members are collected in a node + * structure below a {@link AuthorizableImpl#REP_MEMBERS} node instead of the + * default multi valued property {@link AuthorizableImpl#REP_MEMBERS}. + * Its value determines the maximum number of member properties until + * additional intermediate nodes are inserted. Valid values are integers + * > 4. The default value is 0 and indicates that the + * {@link AuthorizableImpl#REP_MEMBERS} property is used to record group members. + */ + public static final String PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE = "groupMembershipSplitSize"; + + /** * Configuration parameter to change the default algorithm used to generate * password hashes. The default value is {@link PasswordUtility#DEFAULT_ALGORITHM}. */ Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java Tue May 8 07:55:58 2012 @@ -36,6 +36,7 @@ import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Value; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.Principal; @@ -342,6 +343,11 @@ public class UserManagerImpl implements sessionDelegate.getNode(getInternalPath(userNode)).setProperty(name, cvs); } + void setInternalProperty(Node userNode, String name, Value[] values) throws RepositoryException { + List cvs = ValueConverter.toCoreValues(values, sessionDelegate); + sessionDelegate.getNode(getInternalPath(userNode)).setProperty(name, cvs); + } + void removeInternalProperty(Node userNode, String name) throws RepositoryException { sessionDelegate.getNode(getInternalPath(userNode)).getProperty(name).remove(); } @@ -352,12 +358,17 @@ public class UserManagerImpl implements MembershipManager getMembershipManager() { if (membershipManager == null) { - membershipManager = new MembershipManager(this, sessionDelegate); + int splitSize = config.getConfigValue(UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE, 0); + if (splitSize < 4) { + log.warn("Invalid value {} for {}. Expected integer >= 4", splitSize, UserManagerConfig.PARAM_GROUP_MEMBERSHIP_SPLIT_SIZE); + splitSize = 0; + } + membershipManager = new MembershipManager(this, splitSize, sessionDelegate); } return membershipManager; } - private Authorizable getAuthorizable(Node node) throws RepositoryException { + Authorizable getAuthorizable(Node node) throws RepositoryException { if (node.isNodeType(AuthorizableImpl.NT_REP_USER)) { return new UserImpl(node, this); } else if (node.isNodeType(AuthorizableImpl.NT_REP_GROUP)) { Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt?rev=1335373&r1=1335372&r2=1335373&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt (original) +++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt Tue May 8 07:55:58 2012 @@ -18,7 +18,6 @@ UserManager: Authorizable: - setProperty, removeProperty executes no extra shortcut check for protected properties - not relying on rep:principalName to exist -> using ID as fallback -- User: @@ -32,6 +31,7 @@ TODO: - Configuration based on oak-core implementation +- check if node type definition with rep:members child node and/or rep:members child prop still works.