| | |
| | | */ |
| | | package org.opends.server.extensions; |
| | | |
| | | import static com.forgerock.opendj.util.StaticUtils.getBytes; |
| | | |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.LinkedList; |
| | | import java.util.List; |
| | |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.DN.CompactDn; |
| | | import org.forgerock.opendj.ldap.ModificationType; |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | |
| | | import org.forgerock.opendj.server.config.server.GroupImplementationCfg; |
| | | import org.forgerock.opendj.server.config.server.StaticGroupImplementationCfg; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.util.annotations.VisibleForTesting; |
| | | import org.opends.server.api.Group; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.ModifyOperation; |
| | |
| | | private DN groupEntryDN; |
| | | |
| | | /** The set of the DNs of the members for this group. */ |
| | | private LinkedHashSet<CompactDn> memberDNs; |
| | | private HashSet<CompactDn> memberDNs; |
| | | |
| | | /** The list of nested group DNs for this group. */ |
| | | private LinkedList<DN> nestedGroups = new LinkedList<>(); |
| | |
| | | { |
| | | try |
| | | { |
| | | someMemberDNs.add(DN.valueOf(v.toString()).compact()); |
| | | someMemberDNs.add(new CompactDn(DN.valueOf(v.toString()))); |
| | | } |
| | | catch (LocalizedIllegalArgumentException e) |
| | | { |
| | |
| | | newNestedGroups.add(nestedGroupDN); |
| | | nestedGroups = newNestedGroups; |
| | | //Add it to the member DN list. |
| | | LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(memberDNs); |
| | | newMemberDNs.add(toCompactDn(nestedGroupDN)); |
| | | HashSet<CompactDn> newMemberDNs = new HashSet<>(memberDNs); |
| | | newMemberDNs.add(new CompactDn(nestedGroupDN)); |
| | | memberDNs = newMemberDNs; |
| | | } |
| | | finally |
| | |
| | | nestedGroups = newNestedGroups; |
| | | //Remove it from the member DN list. |
| | | LinkedHashSet<CompactDn> newMemberDNs = new LinkedHashSet<>(memberDNs); |
| | | newMemberDNs.remove(toCompactDn(nestedGroupDN)); |
| | | newMemberDNs.remove(new CompactDn(nestedGroupDN)); |
| | | memberDNs = newMemberDNs; |
| | | } |
| | | finally |
| | |
| | | public boolean isMember(DN userDN, Set<DN> examinedGroups) throws DirectoryException |
| | | { |
| | | reloadIfNeeded(); |
| | | CompactDn compactUserDN = toCompactDn(userDN); |
| | | CompactDn compactUserDN = new CompactDn(userDN); |
| | | lock.readLock().lock(); |
| | | try |
| | | { |
| | |
| | | // Check if the group itself has been removed |
| | | if (thisGroup == null) |
| | | { |
| | | throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, |
| | | ERR_STATICGROUP_GROUP_INSTANCE_INVALID.get(groupEntryDN)); |
| | | throw new DirectoryException(ResultCode.NO_SUCH_ATTRIBUTE, ERR_STATICGROUP_GROUP_INSTANCE_INVALID |
| | | .get(groupEntryDN)); |
| | | } |
| | | else if (thisGroup != this) |
| | | { |
| | |
| | | { |
| | | try |
| | | { |
| | | newMemberDNs.add(toCompactDn(memberList.nextMemberDN())); |
| | | newMemberDNs.add(new CompactDn(memberList.nextMemberDN())); |
| | | } |
| | | catch (MembershipException ex) |
| | | { |
| | |
| | | nestedGroups.clear(); |
| | | for (CompactDn compactDn : memberDNs) |
| | | { |
| | | DN dn = fromCompactDn(compactDn); |
| | | DN dn = compactDn.toDn(serverContext); |
| | | Group<?> group = DirectoryServer.getGroupManager().getGroupInstance(dn); |
| | | if (group != null) |
| | | { |
| | |
| | | lock.readLock().lock(); |
| | | try |
| | | { |
| | | return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs); |
| | | return new SimpleStaticGroupMemberList(serverContext, groupEntryDN, memberDNs); |
| | | } |
| | | finally |
| | | { |
| | |
| | | { |
| | | if (baseDN == null && filter == null) |
| | | { |
| | | return new SimpleStaticGroupMemberList(groupEntryDN, memberDNs); |
| | | return new SimpleStaticGroupMemberList(serverContext, groupEntryDN, memberDNs); |
| | | } |
| | | return new FilteredStaticGroupMemberList(groupEntryDN, memberDNs, baseDN, scope, filter); |
| | | return new FilteredStaticGroupMemberList(serverContext, groupEntryDN, memberDNs, baseDN, scope, filter); |
| | | } |
| | | finally |
| | | { |
| | |
| | | for (ByteString v : attribute) |
| | | { |
| | | DN member = DN.valueOf(v); |
| | | memberDNs.add(toCompactDn(member)); |
| | | memberDNs.add(new CompactDn(member)); |
| | | if (DirectoryServer.getGroupManager().getGroupInstance(member) != null) |
| | | { |
| | | nestedGroups.add(member); |
| | |
| | | for (ByteString v : attribute) |
| | | { |
| | | DN member = DN.valueOf(v); |
| | | memberDNs.remove(toCompactDn(member)); |
| | | memberDNs.remove(new CompactDn(member)); |
| | | nestedGroups.remove(member); |
| | | } |
| | | } |
| | |
| | | for (ByteString v : attribute) |
| | | { |
| | | DN member = DN.valueOf(v); |
| | | memberDNs.add(toCompactDn(member)); |
| | | memberDNs.add(new CompactDn(member)); |
| | | if (DirectoryServer.getGroupManager().getGroupInstance(member) != null) |
| | | { |
| | | nestedGroups.add(member); |
| | |
| | | try |
| | | { |
| | | DN userDN = userEntry.getName(); |
| | | CompactDn compactUserDN = toCompactDn(userDN); |
| | | CompactDn compactUserDN = new CompactDn(userDN); |
| | | |
| | | if (memberDNs.contains(compactUserDN)) |
| | | { |
| | |
| | | { |
| | | Reject.ifNull(userDN); |
| | | |
| | | CompactDn compactUserDN = toCompactDn(userDN); |
| | | CompactDn compactUserDN = new CompactDn(userDN); |
| | | lock.writeLock().lock(); |
| | | try |
| | | { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Convert the provided DN to a compact DN. |
| | | * |
| | | * @param dn |
| | | * The DN |
| | | * @return the compact representation of the DN |
| | | * A compact representation of a DN, suitable for equality and comparisons, and providing a natural hierarchical |
| | | * ordering. |
| | | * <p> |
| | | * The memory consumption compared to a regular DN object is minimal. |
| | | */ |
| | | private CompactDn toCompactDn(DN dn) |
| | | static final class CompactDn implements Comparable<CompactDn> |
| | | { |
| | | return dn.compact(); |
| | | } |
| | | /** Original string corresponding to the DN. */ |
| | | private final byte[] originalValue; |
| | | |
| | | /** |
| | | * Convert the provided compact DN to a DN. |
| | | * |
| | | * @param compactDn |
| | | * Compact representation of a DN |
| | | * @return the regular DN |
| | | */ |
| | | static DN fromCompactDn(CompactDn compactDn) |
| | | { |
| | | return compactDn.toDn(); |
| | | /** |
| | | * Normalized byte string, suitable for equality and comparisons, and providing a natural |
| | | * hierarchical ordering, but not usable as a valid DN. |
| | | */ |
| | | private final byte[] normalizedValue; |
| | | |
| | | @VisibleForTesting |
| | | CompactDn(DN dn) |
| | | { |
| | | this.originalValue = getBytes(dn.toString()); |
| | | this.normalizedValue = dn.toNormalizedByteString().toByteArray(); |
| | | } |
| | | |
| | | @Override |
| | | public int compareTo(final CompactDn other) |
| | | { |
| | | final int length1 = normalizedValue.length; |
| | | final int length2 = other.normalizedValue.length; |
| | | int count = Math.min(length1, length2); |
| | | int i = 0; |
| | | int j = 0; |
| | | while (count-- != 0) |
| | | { |
| | | final int firstByte = 0xFF & normalizedValue[i++]; |
| | | final int secondByte = 0xFF & other.normalizedValue[j++]; |
| | | if (firstByte != secondByte) |
| | | { |
| | | return firstByte - secondByte; |
| | | } |
| | | } |
| | | return length1 - length2; |
| | | } |
| | | |
| | | /** |
| | | * Returns the DN corresponding to this compact representation. |
| | | * |
| | | * @param serverContext |
| | | * The server context. |
| | | * |
| | | * @return the DN |
| | | */ |
| | | public DN toDn(ServerContext serverContext) |
| | | { |
| | | return DN.valueOf(toString(), serverContext.getSchemaNG()); |
| | | } |
| | | |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | return Arrays.hashCode(normalizedValue); |
| | | } |
| | | |
| | | @Override |
| | | public boolean equals(Object obj) |
| | | { |
| | | if (this == obj) |
| | | { |
| | | return true; |
| | | } |
| | | else if (obj instanceof CompactDn) |
| | | { |
| | | final CompactDn other = (CompactDn) obj; |
| | | return Arrays.equals(normalizedValue, other.normalizedValue); |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String toString() |
| | | { |
| | | final int length = originalValue.length; |
| | | if (length == 0) { |
| | | return ""; |
| | | } |
| | | try { |
| | | return new String(originalValue, 0, length, "UTF-8"); |
| | | } catch (final UnsupportedEncodingException e) { |
| | | // TODO: I18N |
| | | throw new RuntimeException("Unable to decode bytes as UTF-8 string", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |