| /* |
| * 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 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.Comparator; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.Node; |
| import javax.jcr.Property; |
| import javax.jcr.PropertyType; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.UnsupportedRepositoryOperationException; |
| import javax.jcr.Value; |
| |
| import org.apache.jackrabbit.api.security.principal.GroupPrincipal; |
| 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.LazyIteratorChain; |
| import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter; |
| import org.apache.jackrabbit.core.NodeImpl; |
| import org.apache.jackrabbit.core.PropertyImpl; |
| import org.apache.jackrabbit.core.session.SessionContext; |
| import org.apache.jackrabbit.core.session.SessionWriteOperation; |
| import org.apache.jackrabbit.spi.commons.iterator.Iterators; |
| import org.apache.jackrabbit.util.Text; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * GroupImpl... |
| */ |
| class GroupImpl extends AuthorizableImpl implements Group { |
| |
| private static final Logger log = LoggerFactory.getLogger(GroupImpl.class); |
| |
| private Principal principal; |
| |
| protected GroupImpl(NodeImpl node, UserManagerImpl userManager) { |
| super(node, userManager); |
| } |
| |
| //-------------------------------------------------------< Authorizable >--- |
| |
| /** |
| * @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#getDeclaredMembers() |
| */ |
| public Iterator<Authorizable> getDeclaredMembers() throws RepositoryException { |
| if (isEveryone()) { |
| return userManager.findAuthorizables(getSession().getJCRName(P_PRINCIPAL_NAME), null, UserManager.SEARCH_TYPE_AUTHORIZABLE); |
| } else { |
| return getMembers(false, UserManager.SEARCH_TYPE_AUTHORIZABLE); |
| } |
| } |
| |
| /** |
| * @see Group#getMembers() |
| */ |
| public Iterator<Authorizable> getMembers() throws RepositoryException { |
| if (isEveryone()) { |
| return getDeclaredMembers(); |
| } else { |
| return getMembers(true, UserManager.SEARCH_TYPE_AUTHORIZABLE); |
| } |
| } |
| |
| public boolean isDeclaredMember(Authorizable authorizable) throws RepositoryException { |
| if (authorizable == null || !(authorizable instanceof AuthorizableImpl) |
| || getNode().isSame(((AuthorizableImpl) authorizable).getNode())) { |
| return false; |
| } else if (isEveryone()) { |
| return true; |
| } else { |
| return getMembershipProvider(getNode()).hasMember((AuthorizableImpl) authorizable); |
| } |
| } |
| |
| /** |
| * @see Group#isMember(Authorizable) |
| */ |
| public boolean isMember(Authorizable authorizable) throws RepositoryException { |
| if (authorizable == null || !(authorizable instanceof AuthorizableImpl) |
| || getNode().isSame(((AuthorizableImpl) authorizable).getNode())) { |
| return false; |
| } else if (isEveryone()) { |
| return true; |
| } else { |
| String thisID = getID(); |
| AuthorizableImpl impl = (AuthorizableImpl) authorizable; |
| for (Iterator<Group> it = impl.memberOf(); it.hasNext(); ) { |
| if (thisID.equals(it.next().getID())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * @see Group#addMember(Authorizable) |
| */ |
| public boolean addMember(Authorizable authorizable) throws RepositoryException { |
| if (!(authorizable instanceof AuthorizableImpl)) { |
| log.warn("Invalid Authorizable: {}", authorizable); |
| return false; |
| } |
| if (isEveryone() || ((AuthorizableImpl) authorizable).isEveryone()) { |
| return false; |
| } |
| |
| AuthorizableImpl authImpl = ((AuthorizableImpl) authorizable); |
| Node memberNode = authImpl.getNode(); |
| if (memberNode.isSame(getNode())) { |
| String msg = "Attempt to add a group as member of itself (" + getID() + ")."; |
| log.warn(msg); |
| return false; |
| } |
| |
| if (isCyclicMembership(authImpl)) { |
| log.warn("Attempt to create circular group membership."); |
| return false; |
| } |
| |
| return getMembershipProvider(getNode()).addMember(authImpl); |
| } |
| |
| @Override |
| public Set<String> addMembers(String... memberIds) throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException("not implemented"); |
| } |
| |
| |
| /** |
| * @see Group#removeMember(Authorizable) |
| */ |
| public boolean removeMember(Authorizable authorizable) throws RepositoryException { |
| if (!(authorizable instanceof AuthorizableImpl)) { |
| log.warn("Invalid Authorizable: {}", authorizable); |
| return false; |
| } |
| if (isEveryone()) { |
| return false; |
| } |
| |
| return getMembershipProvider(getNode()).removeMember((AuthorizableImpl) authorizable); |
| } |
| |
| @Override |
| public Set<String> removeMembers(String... memberIds) throws RepositoryException { |
| throw new UnsupportedRepositoryOperationException("not implemented"); |
| } |
| |
| //-------------------------------------------------------------------------- |
| /** |
| * Retrieve the membership provider for this group. This method deals with |
| * members stored in the <code>P_MEMBERS</code> property and with those |
| * repositories the store group members in a separate tree underneath the |
| * <code>N_MEMBERS</code> node. |
| * |
| * @param node The node associated with this group. |
| * @return an instance of <code>MembershipProvider</code>. |
| * @throws RepositoryException If an error occurs. |
| */ |
| private MembershipProvider getMembershipProvider(NodeImpl node) throws RepositoryException { |
| MembershipProvider msp; |
| if (userManager.hasMemberSplitSize()) { |
| if (node.hasNode(N_MEMBERS) || !node.hasProperty(P_MEMBERS)) { |
| msp = new NodeBasedMembershipProvider(node); |
| } else { |
| msp = new PropertyBasedMembershipProvider(node); |
| } |
| } else { |
| msp = new PropertyBasedMembershipProvider(node); |
| } |
| |
| if (node.hasProperty(P_MEMBERS) && node.hasNode(N_MEMBERS)) { |
| log.warn("Found members node and members property on node {}. Ignoring {} members", node, |
| userManager.hasMemberSplitSize() ? "property" : "node"); |
| } |
| |
| return msp; |
| } |
| |
| /** |
| * @param includeIndirect If <code>true</code> all members of this group |
| * will be return; otherwise only the declared members. |
| * @param type Any of {@link UserManager#SEARCH_TYPE_AUTHORIZABLE}, |
| * {@link UserManager#SEARCH_TYPE_GROUP}, {@link UserManager#SEARCH_TYPE_USER}. |
| * @return A collection of members of this group. |
| * @throws RepositoryException If an error occurs while collecting the members. |
| */ |
| private Iterator<Authorizable> getMembers(boolean includeIndirect, int type) throws RepositoryException { |
| return getMembershipProvider(getNode()).getMembers(includeIndirect, type); |
| } |
| |
| /** |
| * Returns <code>true</code> if the given <code>newMember</code> is a Group |
| * and contains <code>this</code> Group as declared or inherited member. |
| * |
| * @param newMember The new member to be tested for cyclic membership. |
| * @return true if the 'newMember' is a group and 'this' is an declared or |
| * inherited member of it. |
| * @throws javax.jcr.RepositoryException If an error occurs. |
| */ |
| private boolean isCyclicMembership(AuthorizableImpl newMember) throws RepositoryException { |
| if (newMember.isGroup()) { |
| GroupImpl gr = (GroupImpl) newMember; |
| for (Iterator<Authorizable> it = gr.getMembers(true, UserManager.SEARCH_TYPE_GROUP); it.hasNext(); ) { |
| Authorizable member = it.next(); |
| GroupImpl grMemberImpl = (GroupImpl) member; |
| if (getNode().getUUID().equals(grMemberImpl.getNode().getUUID())) { |
| // found cyclic group membership |
| return true; |
| } |
| |
| } |
| } |
| return false; |
| } |
| |
| private String safeGetID() { |
| try { |
| return getID(); |
| } catch (RepositoryException e) { |
| return getNode().toString(); |
| } |
| } |
| |
| static PropertySequence getPropertySequence(Node nMembers, UserManagerImpl userManager) throws RepositoryException { |
| Comparator<String> order = Rank.comparableComparator(); |
| int maxChildren = userManager.getMemberSplitSize(); |
| int minChildren = maxChildren / 2; |
| |
| TreeManager treeManager = new BTreeManager(nMembers, minChildren, maxChildren, order, |
| userManager.isAutoSave()); |
| |
| return ItemSequence.createPropertySequence(treeManager); |
| } |
| |
| //------------------------------------------------------< inner classes >--- |
| /** |
| * Principal Implementation |
| */ |
| private class NodeBasedGroup extends NodeBasedPrincipal implements GroupPrincipal { |
| |
| private NodeBasedGroup(String name) { |
| super(name); |
| } |
| |
| //----------------------------------------------------------< Group >--- |
| |
| /** |
| * @return Always <code>false</code>. Group membership must be edited |
| * using the enclosing <code>GroupImpl</code> object. |
| * @see java.security.acl.Group#addMember(Principal) |
| */ |
| public boolean addMember(Principal user) { |
| return false; |
| } |
| |
| /** |
| * @return Always <code>false</code>. Group membership must be edited |
| * using the enclosing <code>GroupImpl</code> object. |
| * @see java.security.acl.Group#isMember(Principal) |
| */ |
| public boolean removeMember(Principal user) { |
| return false; |
| } |
| |
| //----------------------------------------------------------< GroupPrincipal >--- |
| |
| /** |
| * Returns true, if the given <code>Principal</code> is represented by |
| * a Authorizable, that is a member of the underlying UserGroup. |
| * |
| * @see org.apache.jackrabbit.api.security.principal.GroupPrincipal#isMember(Principal) |
| */ |
| public boolean isMember(Principal member) { |
| // shortcut for everyone group -> avoid collecting all members |
| // as all users and groups are member of everyone. |
| try { |
| if (isEveryone()) { |
| return !getPrincipal().equals(member); |
| } |
| } catch (RepositoryException e) { |
| // continue using regular membership evaluation |
| } |
| |
| Collection<Principal> members = getMembers(); |
| return members.contains(member); |
| } |
| |
| /** |
| * Return all principals that refer to every member of the underlying |
| * user group. |
| * |
| * @see org.apache.jackrabbit.api.security.principal.GroupPrincipal#members() |
| */ |
| public Enumeration<? extends Principal> members() { |
| return Collections.enumeration(getMembers()); |
| } |
| |
| //---------------------------------------------------< Serializable >--- |
| /** |
| * implement the writeObject method to assert initialization of all members |
| * before serialization. |
| * |
| * @param stream The object output stream. |
| * @throws IOException If an error occurs. |
| */ |
| private void writeObject(ObjectOutputStream stream) throws IOException { |
| getMembers(); |
| stream.defaultWriteObject(); |
| } |
| |
| //---------------------------------------------------------------------- |
| /** |
| * Collect the member of this group principal. |
| * |
| * @return the members of this group principal. |
| */ |
| private Collection<Principal> getMembers() { |
| Set<Principal> members = new HashSet<Principal>(); |
| try { |
| for (Iterator<Authorizable> it = GroupImpl.this.getMembers(); it.hasNext(); ) { |
| members.add(it.next().getPrincipal()); |
| } |
| } catch (RepositoryException e) { |
| // should not occur. |
| log.error("Unable to retrieve Group members."); |
| } |
| return members; |
| } |
| } |
| |
| /** |
| * Inner MembershipProvider interface |
| */ |
| private interface MembershipProvider { |
| boolean addMember(AuthorizableImpl authorizable) throws RepositoryException; |
| |
| boolean removeMember(AuthorizableImpl authorizable) throws RepositoryException; |
| |
| Iterator<Authorizable> getMembers(boolean includeIndirect, int type) throws RepositoryException; |
| |
| boolean hasMember(AuthorizableImpl authorizable) throws RepositoryException; |
| } |
| |
| /** |
| * PropertyBasedMembershipProvider |
| */ |
| private class PropertyBasedMembershipProvider implements MembershipProvider { |
| private final NodeImpl node; |
| |
| private PropertyBasedMembershipProvider(NodeImpl node) { |
| super(); |
| this.node = node; |
| } |
| |
| /** |
| * @see MembershipProvider#addMember(AuthorizableImpl) |
| */ |
| public boolean addMember(AuthorizableImpl authorizable) throws RepositoryException { |
| Node memberNode = authorizable.getNode(); |
| |
| Value[] values; |
| Value toAdd = getSession().getValueFactory().createValue(memberNode, true); |
| if (node.hasProperty(P_MEMBERS)) { |
| Value[] old = node.getProperty(P_MEMBERS).getValues(); |
| for (Value v : old) { |
| if (v.equals(toAdd)) { |
| log.debug("Authorizable {} is already member of {}", authorizable, this); |
| return false; |
| } |
| } |
| |
| 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.setProtectedProperty(node, P_MEMBERS, values, PropertyType.WEAKREFERENCE); |
| return true; |
| } |
| |
| /** |
| * @see MembershipProvider#removeMember(AuthorizableImpl) |
| */ |
| public boolean removeMember(AuthorizableImpl authorizable) throws RepositoryException { |
| if (!node.hasProperty(P_MEMBERS)) { |
| log.debug("Group has no members -> cannot remove member {}", authorizable.getID()); |
| return false; |
| } |
| |
| Value toRemove = getSession().getValueFactory().createValue((authorizable).getNode(), true); |
| |
| PropertyImpl property = node.getProperty(P_MEMBERS); |
| List<Value> valList = new ArrayList<Value>(Arrays.asList(property.getValues())); |
| |
| if (valList.remove(toRemove)) { |
| try { |
| if (valList.isEmpty()) { |
| userManager.removeProtectedItem(property, node); |
| } else { |
| Value[] values = 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 {} was not member of {}", authorizable.getID(), getID()); |
| return false; |
| } |
| } |
| |
| /** |
| * @see MembershipProvider#getMembers(boolean, int) |
| */ |
| public Iterator<Authorizable> getMembers(boolean includeIndirect, int type) throws RepositoryException { |
| if (node.hasProperty(P_MEMBERS)) { |
| Value[] members = node.getProperty(P_MEMBERS).getValues(); |
| |
| if (includeIndirect) { |
| return includeIndirect(toAuthorizables(members, type), type); |
| } else { |
| return new RangeIteratorAdapter(toAuthorizables(members, type), members.length); |
| } |
| } else { |
| return Iterators.empty(); |
| } |
| } |
| |
| /** |
| * @see MembershipProvider#hasMember(AuthorizableImpl) |
| */ |
| public boolean hasMember(AuthorizableImpl authorizable) throws RepositoryException { |
| if (node.hasProperty(P_MEMBERS)) { |
| Value[] members = node.getProperty(P_MEMBERS).getValues(); |
| for (Value v : members) { |
| if (authorizable.getNode().getIdentifier().equals(v.getString())) { |
| return true; |
| } |
| } |
| return false; |
| } else { |
| return false; |
| } |
| } |
| |
| } |
| |
| /** |
| * NodeBasedMembershipProvider |
| */ |
| private class NodeBasedMembershipProvider implements MembershipProvider { |
| private final NodeImpl node; |
| |
| private NodeBasedMembershipProvider(NodeImpl node) { |
| super(); |
| this.node = node; |
| } |
| |
| /** |
| * @see MembershipProvider#addMember(AuthorizableImpl) |
| */ |
| public boolean addMember(final AuthorizableImpl authorizable) throws RepositoryException { |
| return userManager.performProtectedOperation(getSession(), new SessionWriteOperation<Boolean>() { |
| public Boolean perform(SessionContext context) throws RepositoryException { |
| NodeImpl nMembers = (node.hasNode(N_MEMBERS) |
| ? node.getNode(N_MEMBERS) |
| : node.addNode(N_MEMBERS, NT_REP_MEMBERS, null)); |
| |
| try { |
| PropertySequence properties = getPropertySequence(nMembers, userManager); |
| String propName = Text.escapeIllegalJcrChars(authorizable.getID()); |
| if (properties.hasItem(propName)) { |
| log.debug("Authorizable {} is already member of {}", authorizable, this); |
| return false; |
| } else { |
| Value newMember = getSession().getValueFactory().createValue(authorizable.getNode(), true); |
| properties.addProperty(propName, newMember); |
| } |
| |
| if (userManager.isAutoSave()) { |
| node.save(); |
| } |
| return true; |
| } catch (RepositoryException e) { |
| log.debug("addMember failed. Reverting changes", e); |
| if (nMembers.isNew()) { |
| node.refresh(false); |
| } else { |
| nMembers.refresh(false); |
| } |
| throw e; |
| } |
| } |
| }); |
| } |
| |
| /** |
| * @see MembershipProvider#removeMember(AuthorizableImpl) |
| */ |
| public boolean removeMember(final AuthorizableImpl authorizable) throws RepositoryException { |
| if (!node.hasNode(N_MEMBERS)) { |
| log.debug("Group has no members -> cannot remove member {}", authorizable.getID()); |
| return false; |
| } |
| |
| return userManager.performProtectedOperation(getSession(), new SessionWriteOperation<Boolean>() { |
| public Boolean perform(SessionContext context) throws RepositoryException { |
| NodeImpl nMembers = node.getNode(N_MEMBERS); |
| try { |
| PropertySequence properties = getPropertySequence(nMembers, userManager); |
| String propName = Text.escapeIllegalJcrChars(authorizable.getID()); |
| if (properties.hasItem(propName)) { |
| properties.removeProperty(propName); |
| if (!properties.iterator().hasNext()) { |
| nMembers.remove(); |
| } |
| } else { |
| log.debug("Authorizable {} was not member of {}", authorizable.getID(), getID()); |
| return false; |
| } |
| |
| if (userManager.isAutoSave()) { |
| node.save(); |
| } |
| return true; |
| } catch (RepositoryException e) { |
| log.debug("removeMember failed. Reverting changes", e); |
| nMembers.refresh(false); |
| throw e; |
| } |
| } |
| }); |
| } |
| |
| /** |
| * @see MembershipProvider#getMembers(boolean, int) |
| */ |
| public Iterator<Authorizable> getMembers(boolean includeIndirect, int type) throws RepositoryException { |
| if (node.hasNode(N_MEMBERS)) { |
| PropertySequence members = getPropertySequence(node.getNode(N_MEMBERS), userManager); |
| if (includeIndirect) { |
| return includeIndirect(toAuthorizables(members.iterator(), type), type); |
| } else { |
| return toAuthorizables(members.iterator(), type); |
| } |
| } else { |
| return Iterators.empty(); |
| } |
| } |
| |
| /** |
| * @see MembershipProvider#hasMember(AuthorizableImpl) |
| */ |
| public boolean hasMember(AuthorizableImpl authorizable) throws RepositoryException { |
| if (node.hasNode(N_MEMBERS)) { |
| PropertySequence members = getPropertySequence(node.getNode(N_MEMBERS), userManager); |
| return members.hasItem(authorizable.getID()); |
| } else { |
| return false; |
| } |
| } |
| |
| } |
| |
| // -----------------------------------------------------------< utility >--- |
| /** |
| * Returns an iterator of authorizables which includes all indirect members of the given iterator |
| * of authorizables. |
| * |
| * @param authorizables |
| * @param type |
| * @return Iterator of Authorizable objects |
| */ |
| private Iterator<Authorizable> includeIndirect(final Iterator<Authorizable> authorizables, final int type) { |
| Iterator<Iterator<Authorizable>> indirectMembers = new Iterator<Iterator<Authorizable>>() { |
| public boolean hasNext() { |
| return authorizables.hasNext(); |
| } |
| |
| public Iterator<Authorizable> next() { |
| Authorizable next = authorizables.next(); |
| return Iterators.iteratorChain(Iterators.singleton(next), indirect(next)); |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns the transitive closure over the members of this authorizable. |
| * |
| * @param authorizable |
| * @return Iterator of Authorizable objects |
| */ |
| private Iterator<Authorizable> indirect(Authorizable authorizable) { |
| if (authorizable.isGroup()) { |
| try { |
| return ((GroupImpl) authorizable).getMembers(true, type); |
| } catch (RepositoryException e) { |
| log.warn("Could not determine members of " + authorizable, e); |
| } |
| } |
| return Iterators.empty(); |
| } |
| }; |
| |
| return unique(new LazyIteratorChain<Authorizable>(indirectMembers)); |
| } |
| |
| /** |
| * Filter the passed {@code authorizables} in order to ensure uniqueness. |
| * @param authorizables |
| * @return all members of {@code authorizable} with duplicates removed |
| * |
| * @see <a href="https://issues.apache.org/jira/browse/JCR-3156">JCR-3156</a> |
| */ |
| private Iterator<Authorizable> unique(Iterator<Authorizable> authorizables) { |
| final HashSet<String> seenAuthorizables = new HashSet<String>(); |
| return Iterators.filterIterator(authorizables, |
| new org.apache.jackrabbit.spi.commons.iterator.Predicate<Authorizable>() { |
| |
| public boolean evaluate(Authorizable authorizable) { |
| try { |
| return seenAuthorizables.add(authorizable.getID()); |
| } |
| catch (RepositoryException e) { |
| log.warn("Could not determine id of " + authorizable, e); |
| return true; |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Map an array of values to an iterator of authorizables. |
| * |
| * @param members |
| * @param type |
| * @return Iterator of Authorizable objects |
| */ |
| private Iterator<Authorizable> toAuthorizables(final Value[] members, int type) { |
| return new AuthorizableIterator(type) { |
| private int pos; |
| |
| @Override |
| protected String getNextMemberRef() throws RepositoryException { |
| return pos < members.length |
| ? members[pos++].getString() |
| : null; |
| } |
| }; |
| } |
| |
| /** |
| * Map an iterator of properties to an iterator of authorizables. |
| * |
| * @param members |
| * @param type |
| * @return Iterator of Authorizable objects |
| */ |
| private Iterator<Authorizable> toAuthorizables(final Iterator<Property> members, int type) { |
| return new AuthorizableIterator(type) { |
| @Override |
| protected String getNextMemberRef() throws RepositoryException { |
| return members.hasNext() |
| ? members.next().getString() |
| : null; |
| } |
| }; |
| } |
| |
| /** |
| * Iterator of authorizables of a specific type. |
| */ |
| private abstract class AuthorizableIterator implements Iterator<Authorizable> { |
| private Authorizable next; |
| private final int type; |
| |
| public AuthorizableIterator(int type) { |
| super(); |
| this.type = type; |
| } |
| |
| public boolean hasNext() { |
| prefetch(); |
| return next != null; |
| } |
| |
| public Authorizable next() { |
| prefetch(); |
| if (next == null) { |
| throw new NoSuchElementException(); |
| } |
| |
| Authorizable element = next; |
| next = null; |
| return element; |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Returns the reference value of the next node representing the next authorizable or |
| * <code>null</code> if there there are no more. |
| * |
| * @return reference value of the next node representing the next authorizable or |
| * <code>null</code> if there there are no more. |
| * @throws javax.jcr.RepositoryException If an error occurs. |
| */ |
| protected abstract String getNextMemberRef() throws RepositoryException; |
| |
| private void prefetch() { |
| while (next == null) { |
| try { |
| String memberRef = getNextMemberRef(); |
| if (memberRef == null) { |
| return; |
| } |
| |
| NodeImpl member = (NodeImpl) getSession().getNodeByIdentifier(memberRef); |
| if (type != UserManager.SEARCH_TYPE_USER && member.isNodeType(NT_REP_GROUP)) { |
| next = userManager.createGroup(member); |
| } else if (type != UserManager.SEARCH_TYPE_GROUP && member.isNodeType(NT_REP_USER)) { |
| next = userManager.createUser(member); |
| } else { |
| log.debug("Group member entry with invalid node type {} -> " + |
| "Not included in member set.", member.getPrimaryNodeType().getName()); |
| } |
| } catch (ItemNotFoundException e) { |
| log.debug("Authorizable node referenced by {} doesn't exist any more -> " + |
| "Ignored from member list.", safeGetID()); |
| } catch (RepositoryException e) { |
| log.debug("Error pre-fetching member for " + safeGetID(), e); |
| } |
| |
| } |
| } |
| } |
| } |