From 05b440995cb1a2b3aeaf8831d4e563bc463ce8f4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 11 Apr 2007 22:39:23 +0000
Subject: [PATCH] Implement support for virtual static groups, which are entries which appear to be static groups but get their membership information from another group and present it through a virtual attribute. This can make it possible to use a dynamic group to actually define the set of membership, but still support applications which can only interact with static groups.
---
opendj-sdk/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java | 484 +++++++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java | 859 +++++++++++++++++++++++++++
opendj-sdk/opends/resource/config/config.ldif | 27
opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java | 13
opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java | 96 +++
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java | 17
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java | 2
opendj-sdk/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java | 349 +++++++++++
opendj-sdk/opends/resource/schema/02-config.ldif | 6
9 files changed, 1,849 insertions(+), 4 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index b219bd5..9752405 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -441,6 +441,13 @@
ds-cfg-group-implementation-class: org.opends.server.extensions.StaticGroup
ds-cfg-group-implementation-enabled: true
+dn: cn=Virtual Static,cn=Group Implementations,cn=config
+objectClass: top
+objectClass: ds-cfg-group-implementation
+cn: Virtual Static
+ds-cfg-group-implementation-class: org.opends.server.extensions.VirtualStaticGroup
+ds-cfg-group-implementation-enabled: true
+
dn: cn=Identity Mappers,cn=config
objectClass: top
objectClass: ds-cfg-branch
@@ -1731,6 +1738,26 @@
ds-cfg-virtual-attribute-type: subschemaSubentry
ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+dn: cn=Virtual Static member,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+cn: Virtual Static member
+ds-cfg-virtual-attribute-class: org.opends.server.extensions.MemberVirtualAttributeProvider
+ds-cfg-virtual-attribute-enabled: true
+ds-cfg-virtual-attribute-type: member
+ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+ds-cfg-virtual-attribute-filter: (&(objectClass=groupOfNames)(objectClass=ds-virtual-static-group))
+
+dn: cn=Virtual Static uniqueMember,cn=Virtual Attributes,cn=config
+objectClass: top
+objectClass: ds-cfg-virtual-attribute
+cn: Virtual Static uniqueMember
+ds-cfg-virtual-attribute-class: org.opends.server.extensions.MemberVirtualAttributeProvider
+ds-cfg-virtual-attribute-enabled: true
+ds-cfg-virtual-attribute-type: uniqueMember
+ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real
+ds-cfg-virtual-attribute-filter: (&(objectClass=groupOfUniqueNames)(objectClass=ds-virtual-static-group))
+
dn: cn=Work Queue,cn=config
objectClass: top
objectClass: ds-cfg-work-queue
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index bc9bf86..1bced74 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -1150,6 +1150,9 @@
NAME 'ds-task-rebuild-max-threads'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.343 NAME 'ds-target-group-dn'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1602,5 +1605,8 @@
MUST ( ds-task-rebuild-base-dn $ ds-task-rebuild-index )
MAY ( ds-task-rebuild-max-threads )
X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.99
+ NAME 'ds-virtual-static-group' SUP top AUXILIARY MUST ds-target-group-dn
+ X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java
new file mode 100644
index 0000000..42db6c0
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/MemberVirtualAttributeProvider.java
@@ -0,0 +1,349 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.opends.server.admin.std.server.VirtualAttributeCfg;
+import org.opends.server.api.Group;
+import org.opends.server.api.VirtualAttributeProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.MemberList;
+import org.opends.server.types.MembershipException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * This class implements a virtual attribute provider that works in conjunction
+ * with virtual static groups to generate the values for the member or
+ * uniqueMember attribute.
+ */
+public class MemberVirtualAttributeProvider
+ extends VirtualAttributeProvider<VirtualAttributeCfg>
+{
+ // The attribute type used to indicate which target group should be used to
+ // obtain the member list.
+ private AttributeType targetGroupType;
+
+
+
+ /**
+ * Creates a new instance of this member virtual attribute provider.
+ */
+ public MemberVirtualAttributeProvider()
+ {
+ super();
+
+ // All initialization should be performed in the
+ // initializeVirtualAttributeProvider method.
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeVirtualAttributeProvider(
+ VirtualAttributeCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ targetGroupType =
+ DirectoryServer.getAttributeType(ATTR_TARGET_GROUP_DN, true);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMultiValued()
+ {
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public LinkedHashSet<AttributeValue> getValues(Entry entry,
+ VirtualAttributeRule rule)
+ {
+ Group g = DirectoryServer.getGroupManager().getGroupInstance(entry.getDN());
+ if (g == null)
+ {
+ return new LinkedHashSet<AttributeValue>(0);
+ }
+
+ LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
+ try
+ {
+ MemberList memberList = g.getMembers();
+ while (memberList.hasMoreMembers())
+ {
+ try
+ {
+ DN memberDN = memberList.nextMemberDN();
+ if (memberDN != null)
+ {
+ values.add(new AttributeValue(rule.getAttributeType(),
+ memberDN.toString()));
+ }
+ }
+ catch (MembershipException me)
+ {
+ if (! me.continueIterating())
+ {
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ return values;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule)
+ {
+ Group g = DirectoryServer.getGroupManager().getGroupInstance(entry.getDN());
+ if (g == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ MemberList memberList = g.getMembers();
+ while (memberList.hasMoreMembers())
+ {
+ try
+ {
+ DN memberDN = memberList.nextMemberDN();
+ if (memberDN != null)
+ {
+ memberList.close();
+ return true;
+ }
+ }
+ catch (MembershipException me)
+ {
+ if (! me.continueIterating())
+ {
+ break;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasValue(Entry entry, VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ Group g = DirectoryServer.getGroupManager().getGroupInstance(entry.getDN());
+ if (g == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ return g.isMember(DN.decode(value.getValue()));
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean hasAnyValue(Entry entry, VirtualAttributeRule rule,
+ Collection<AttributeValue> values)
+ {
+ for (AttributeValue v : values)
+ {
+ if (hasValue(entry, rule, v))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult matchesSubstring(Entry entry,
+ VirtualAttributeRule rule,
+ ByteString subInitial,
+ List<ByteString> subAny,
+ ByteString subFinal)
+ {
+ // DNs cannot be used in substring matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult greaterThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult lessThanOrEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in ordering matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public ConditionResult approximatelyEqualTo(Entry entry,
+ VirtualAttributeRule rule,
+ AttributeValue value)
+ {
+ // DNs cannot be used in approximate matching.
+ return ConditionResult.UNDEFINED;
+ }
+
+
+
+ /**
+ * {@inheritDoc}. This virtual attribute will support search operations only
+ * if one of the following is true about the search filter:
+ * <UL>
+ * <LI>It is an equality filter targeting the associated attribute
+ * type.</LI>
+ * <LI>It is an AND filter in which at least one of the components is an
+ * equality filter targeting the associated attribute type.</LI>
+ * <LI>It is an OR filter in which all of the components are equality
+ * filters targeting the associated attribute type.</LI>
+ * </UL>
+ */
+ @Override()
+ public boolean isSearchable(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void processSearch(VirtualAttributeRule rule,
+ SearchOperation searchOperation)
+ {
+ searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
+ return;
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
index 64871ad..ee7fda9 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/StaticGroup.java
@@ -79,9 +79,6 @@
public class StaticGroup
extends Group
{
-
-
-
// The attribute type used to hold the membership list for this group.
private AttributeType memberAttributeType;
@@ -241,7 +238,8 @@
// FIXME -- This needs to exclude enhanced groups once we have support for
// them.
String filterString =
- "(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames))";
+ "(&(|(objectClass=groupOfNames)(objectClass=groupOfUniqueNames))" +
+ "(!(objectClass=ds-virtual-static-group))";
return SearchFilter.createFilterFromString(filterString);
}
@@ -257,6 +255,13 @@
// FIXME -- This needs to exclude enhanced groups once we have support for
//them.
+ ObjectClass virtualStaticGroupClass =
+ DirectoryConfig.getObjectClass(OC_VIRTUAL_STATIC_GROUP, true);
+ if (entry.hasObjectClass(virtualStaticGroupClass))
+ {
+ return false;
+ }
+
ObjectClass groupOfNamesClass =
DirectoryConfig.getObjectClass(OC_GROUP_OF_NAMES_LC, true);
ObjectClass groupOfUniqueNamesClass =
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java
new file mode 100644
index 0000000..2ff7c80
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/VirtualStaticGroup.java
@@ -0,0 +1,484 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collections;
+import java.util.List;
+
+import org.opends.server.api.Group;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.DebugLogLevel;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.MemberList;
+import org.opends.server.types.ObjectClass;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.SearchScope;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import static org.opends.server.messages.ExtensionsMessages.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.Validator.*;
+
+
+
+/**
+ * This class provides a virtual static group implementation, in which
+ * membership is based on membership of another group.
+ */
+public class VirtualStaticGroup
+ extends Group
+{
+ // The DN of the entry that holds the definition for this group.
+ private DN groupEntryDN;
+
+ // The DN of the target group that will provide membership information.
+ private DN targetGroupDN;
+
+
+
+ /**
+ * Creates a new, uninitialized virtual static group instance. This is
+ * intended for internal use only.
+ */
+ public VirtualStaticGroup()
+ {
+ super();
+
+ // No initialization is required here.
+ }
+
+
+
+ /**
+ * Creates a new virtual static group instance with the provided information.
+ *
+ * @param groupEntryDN The DN of the entry that holds the definition for
+ * this group. It must not be {@code null}.
+ * @param targetGroupDN The DN of the target group that will provide
+ * membership information. It must not be
+ * {@code null}.
+ */
+ public VirtualStaticGroup(DN groupEntryDN, DN targetGroupDN)
+ {
+ super();
+
+ ensureNotNull(groupEntryDN, targetGroupDN);
+
+ this.groupEntryDN = groupEntryDN;
+ this.targetGroupDN = targetGroupDN;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void initializeGroupImplementation(ConfigEntry configEntry)
+ throws ConfigException, InitializationException
+ {
+ // No additional initialization is required.
+ }
+
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public VirtualStaticGroup newInstance(Entry groupEntry)
+ throws DirectoryException
+ {
+ ensureNotNull(groupEntry);
+
+
+ // Get the target group DN attribute from the entry, if there is one.
+ DN targetDN = null;
+ AttributeType targetType =
+ DirectoryServer.getAttributeType(ATTR_TARGET_GROUP_DN, true);
+ List<Attribute> attrList = groupEntry.getAttribute(targetType);
+ if (attrList != null)
+ {
+ for (Attribute a : attrList)
+ {
+ for (AttributeValue v : a.getValues())
+ {
+ if (targetDN != null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS;
+ String message = getMessage(msgID,
+ String.valueOf(groupEntry.getDN()));
+ throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION,
+ message, msgID);
+ }
+
+ try
+ {
+ targetDN = DN.decode(v.getValue());
+ }
+ catch (DirectoryException de)
+ {
+ if (debugEnabled())
+ {
+ debugCaught(DebugLogLevel.ERROR, de);
+ }
+
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET;
+ String message = getMessage(msgID, v.getStringValue(),
+ String.valueOf(groupEntry.getDN()),
+ de.getErrorMessage());
+ throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
+ message, msgID, de);
+ }
+ }
+ }
+ }
+
+ if (targetDN == null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET;
+ String message = getMessage(msgID, String.valueOf(groupEntry.getDN()));
+ throw new DirectoryException(ResultCode.OBJECTCLASS_VIOLATION, message,
+ msgID);
+ }
+
+ return new VirtualStaticGroup(groupEntry.getDN(), targetDN);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public SearchFilter getGroupDefinitionFilter()
+ throws DirectoryException
+ {
+ // FIXME -- This needs to exclude enhanced groups once we have support for
+ // them.
+ return SearchFilter.createFilterFromString("(" + ATTR_OBJECTCLASS + "=" +
+ OC_VIRTUAL_STATIC_GROUP + ")");
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isGroupDefinition(Entry entry)
+ {
+ ensureNotNull(entry);
+
+ // FIXME -- This needs to exclude enhanced groups once we have support for
+ //them.
+ ObjectClass virtualStaticGroupClass =
+ DirectoryServer.getObjectClass(OC_VIRTUAL_STATIC_GROUP, true);
+ return entry.hasObjectClass(virtualStaticGroupClass);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public DN getGroupDN()
+ {
+ return groupEntryDN;
+ }
+
+
+
+ /**
+ * Retrieves the DN of the target group for this virtual static group.
+ *
+ * @return The DN of the target group for this virtual static group.
+ */
+ public DN getTargetGroupDN()
+ {
+ return targetGroupDN;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean supportsNestedGroups()
+ {
+ // Dynamic groups don't support nesting.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public List<DN> getNestedGroupDNs()
+ {
+ // Dynamic groups don't support nesting.
+ return Collections.<DN>emptyList();
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void addNestedGroup(DN nestedGroupDN)
+ throws UnsupportedOperationException, DirectoryException
+ {
+ // Dynamic groups don't support nesting.
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED;
+ String message = getMessage(msgID);
+ throw new UnsupportedOperationException(message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void removeNestedGroup(DN nestedGroupDN)
+ throws UnsupportedOperationException, DirectoryException
+ {
+ // Dynamic groups don't support nesting.
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED;
+ String message = getMessage(msgID);
+ throw new UnsupportedOperationException(message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMember(DN userDN)
+ throws DirectoryException
+ {
+ Group targetGroup =
+ DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
+ if (targetGroup == null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP;
+ String message = getMessage(msgID, String.valueOf(targetGroupDN),
+ String.valueOf(groupEntryDN));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID);
+ }
+ else if (targetGroup instanceof VirtualStaticGroup)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN),
+ String.valueOf(targetGroupDN));
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+ else
+ {
+ return targetGroup.isMember(userDN);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean isMember(Entry userEntry)
+ throws DirectoryException
+ {
+ Group targetGroup =
+ DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
+ if (targetGroup == null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP;
+ String message = getMessage(msgID, String.valueOf(targetGroupDN),
+ String.valueOf(groupEntryDN));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID);
+ }
+ else if (targetGroup instanceof VirtualStaticGroup)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN),
+ String.valueOf(targetGroupDN));
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+ else
+ {
+ return targetGroup.isMember(userEntry);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public MemberList getMembers()
+ throws DirectoryException
+ {
+ Group targetGroup =
+ DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
+ if (targetGroup == null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP;
+ String message = getMessage(msgID, String.valueOf(targetGroupDN),
+ String.valueOf(groupEntryDN));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID);
+ }
+ else if (targetGroup instanceof VirtualStaticGroup)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN),
+ String.valueOf(targetGroupDN));
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+ else
+ {
+ return targetGroup.getMembers();
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public MemberList getMembers(DN baseDN, SearchScope scope,
+ SearchFilter filter)
+ throws DirectoryException
+ {
+ Group targetGroup =
+ DirectoryServer.getGroupManager().getGroupInstance(targetGroupDN);
+ if (targetGroup == null)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP;
+ String message = getMessage(msgID, String.valueOf(targetGroupDN),
+ String.valueOf(groupEntryDN));
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message, msgID);
+ }
+ else if (targetGroup instanceof VirtualStaticGroup)
+ {
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN),
+ String.valueOf(targetGroupDN));
+ throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+ msgID);
+ }
+ else
+ {
+ return targetGroup.getMembers(baseDN, scope, filter);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public boolean mayAlterMemberList()
+ {
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void addMember(Entry userEntry)
+ throws UnsupportedOperationException, DirectoryException
+ {
+ // Dynamic groups don't support altering the member list.
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN));
+ throw new UnsupportedOperationException(message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void removeMember(DN userDN)
+ throws UnsupportedOperationException, DirectoryException
+ {
+ // Dynamic groups don't support altering the member list.
+ int msgID = MSGID_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED;
+ String message = getMessage(msgID, String.valueOf(groupEntryDN));
+ throw new UnsupportedOperationException(message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public void toString(StringBuilder buffer)
+ {
+ buffer.append("VirtualStaticGroup(dn=");
+ buffer.append(groupEntryDN);
+ buffer.append(",targetGroupDN=");
+ buffer.append(targetGroupDN);
+ buffer.append(")");
+ }
+}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
index 96dd595..bd082cc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4941,6 +4941,78 @@
/**
+ * The message ID for the message that will be used if the virtual static
+ * group has multiple targets. This takes a single argument, which is the DN
+ * of the group.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 470;
+
+
+
+ /**
+ * The message ID for the message that will be used if the virtual static
+ * group has a target that can't be decoded as a DN. This takes three
+ * arguments, which are the target group value, the group DN, and a message
+ * explaining the problem that occurred.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 471;
+
+
+
+ /**
+ * The message ID for the message that will be used if the virtual static
+ * group does not have a target group DN. This takes a single argument, which
+ * is the DN of the group.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 472;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * nest a virtual static group. This takes a single argument, which is the
+ * DN of the group.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 473;
+
+
+
+ /**
+ * The message ID for the message that will be used if the target group does
+ * not exist. This takes two arguments, which is the target group DN and the
+ * virtual static group DN.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 474;
+
+
+
+ /**
+ * The message ID for the message that will be used if an attempt is made to
+ * alter the membership for a virtual static group. This takes a single
+ * argument, which is the DN of the group.
+ */
+ public static final int
+ MSGID_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED =
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 475;
+
+
+
+ /**
+ * The message ID for the message that will be used if a virtual static group
+ * target is also a virtual static group. This takes two arguments, which are
+ * the object group DN and the target group DN.
+ */
+ public static final int MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL=
+ CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 476;
+
+
+
+ /**
* Associates a set of generic messages with the message IDs defined in this
* class.
*/
@@ -7109,6 +7181,30 @@
"The provided character set definition '%s' is invalid " +
"because it contains character '%s' which has already " +
"been used.");
+
+
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_MULTIPLE_TARGETS,
+ "The virtual static group defined in entry %s contains " +
+ "multiple target group DNs, but only one is allowed.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_CANNOT_DECODE_TARGET,
+ "Unable to decode \"%s\" as the target DN for group %s: " +
+ "%s.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET,
+ "The virtual static group defined in entry %s does not " +
+ "contain a target group definition.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_NESTING_NOT_SUPPORTED,
+ "Virtual static groups do not support nesting.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_NO_TARGET_GROUP,
+ "Target group %s referenced by virtual static group %s " +
+ "does not exist.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_ALTERING_MEMBERS_NOT_SUPPORTED,
+ "Altering membership for virtual static group %s is not " +
+ "allowed.");
+ registerMessage(MSGID_VIRTUAL_STATIC_GROUP_TARGET_CANNOT_BE_VIRTUAL,
+ "Virtual static group %s references target group %s " +
+ "which is itself a virtual static group. One " +
+ "virtual static group is not allowed to reference " +
+ "another as its target group.");
}
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index 57ebefc..8484a2f 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -490,6 +490,14 @@
/**
+ * The name of the attribute that is used to specify the DN of the target
+ * group for a virtual static group.
+ */
+ public static final String ATTR_TARGET_GROUP_DN = "ds-target-group-dn";
+
+
+
+ /**
* The name of the attribute that is used to specify the total number of
* connections established since startup, formatted in camel case.
*/
@@ -824,6 +832,15 @@
/**
+ * The name of the ds-virtual-static-group objectclass in all lowercase
+ * characters.
+ */
+ public static final String OC_VIRTUAL_STATIC_GROUP =
+ "ds-virtual-static-group";
+
+
+
+ /**
* The English name for the basic disabled log severity used for all
* log severities.
*/
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
index cc4a76d..dc81c2b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/GroupManagerTestCase.java
@@ -43,6 +43,7 @@
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.extensions.DynamicGroup;
import org.opends.server.extensions.StaticGroup;
+import org.opends.server.extensions.VirtualStaticGroup;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.types.Attribute;
@@ -100,6 +101,7 @@
LinkedHashSet<Class> groupClasses = new LinkedHashSet<Class>();
groupClasses.add(StaticGroup.class);
groupClasses.add(DynamicGroup.class);
+ groupClasses.add(VirtualStaticGroup.class);
for (Group g : groupManager.getGroupImplementations())
{
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java
new file mode 100644
index 0000000..fc43be7
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/VirtualStaticGroupTestCase.java
@@ -0,0 +1,859 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.extensions;
+
+
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.GroupManager;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AttributeType;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.MemberList;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchScope;
+import org.opends.server.types.SearchFilter;
+import org.opends.server.types.VirtualAttributeRule;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for the virtual static group implementation and the
+ * member virtual attribute provider.
+ */
+public class VirtualStaticGroupTestCase
+ extends ExtensionsTestCase
+{
+ /**
+ * The lines comprising the LDIF test data.
+ */
+ private static final String[] LDIF_LINES =
+ {
+ "dn: ou=People,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: People",
+ "",
+ "dn: uid=test.1,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.1",
+ "givenName: Test",
+ "sn: 1",
+ "cn: Test 1",
+ "userPassword: password",
+ "",
+ "dn: uid=test.2,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.2",
+ "givenName: Test",
+ "sn: 2",
+ "cn: Test 2",
+ "userPassword: password",
+ "",
+ "dn: uid=test.3,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.3",
+ "givenName: Test",
+ "sn: 3",
+ "cn: Test 3",
+ "userPassword: password",
+ "",
+ "dn: uid=test.4,ou=People,o=test",
+ "objectClass: top",
+ "objectClass: person",
+ "objectClass: organizationalPerson",
+ "objectClass: inetOrgPerson",
+ "uid: test.4",
+ "givenName: Test",
+ "sn: 4",
+ "cn: Test 4",
+ "userPassword: password",
+ "",
+ "dn: ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: organizationalUnit",
+ "ou: Groups",
+ "",
+ "dn: cn=Dynamic All Users,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Dynamic All Users",
+ "memberURL: ldap:///ou=People,o=test??sub?(objectClass=person)",
+ "",
+ "dn: cn=Dynamic One User,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfURLs",
+ "cn: Dynamic One User",
+ "memberURL: ldap:///ou=People,o=test??sub?(&(objectClass=person)(sn=4))",
+ "",
+ "dn: cn=Static member List,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Static member List",
+ "member: uid=test.1,ou=People,o=test",
+ "member: uid=test.3,ou=People,o=test",
+ "",
+ "dn: cn=Static uniqueMember List,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "cn: Static uniqueMember List",
+ "uniqueMember: uid=test.2,ou=People,o=test",
+ "uniqueMember: uid=test.3,ou=People,o=test",
+ "uniqueMember: uid=no-such-user,ou=People,o=test",
+ "",
+ "dn: cn=Virtual member All Users,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual member All Users",
+ "ds-target-group-dn: cn=Dynamic All Users,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual uniqueMember All Users,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual uniqueMember All Users",
+ "ds-target-group-dn: cn=Dynamic All Users,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual member One User,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual member One User",
+ "ds-target-group-dn: cn=Dynamic One User,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual uniqueMember One User,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual uniqueMember One User",
+ "ds-target-group-dn: cn=Dynamic One User,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual Static member List,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual Static member List",
+ "ds-target-group-dn: cn=Static member List,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual Static uniqueMember List,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual Static uniqueMember List",
+ "ds-target-group-dn: cn=Static uniqueMember List,ou=Groups,o=test",
+ "",
+ "dn: cn=Crossover member Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfUniqueNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Crossover member Static Group",
+ "ds-target-group-dn: cn=Static member List,ou=Groups,o=test",
+ "",
+ "dn: cn=Crossover uniqueMember Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Crossover uniqueMember Static Group",
+ "ds-target-group-dn: cn=Static uniqueMember List,ou=Groups,o=test",
+ "",
+ "dn: cn=Virtual Nonexistent,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Virtual Nonexistent",
+ "ds-target-group-dn: cn=Nonexistent,ou=Groups,o=test"
+ };
+
+
+
+ // The attribute type for the member attribute.
+ private AttributeType memberType;
+
+ // The attribute type for the uniqueMember attribute.
+ private AttributeType uniqueMemberType;
+
+ // The server group manager.
+ private GroupManager groupManager;
+
+ // The DNs of the various entries in the data set.
+ private DN u1;
+ private DN u2;
+ private DN u3;
+ private DN u4;
+ private DN da;
+ private DN d1;
+ private DN sm;
+ private DN su;
+ private DN vmda;
+ private DN vuda;
+ private DN vmd1;
+ private DN vud1;
+ private DN vsm;
+ private DN vsu;
+ private DN vcm;
+ private DN vcu;
+ private DN vn;
+ private DN ne;
+
+
+
+ /**
+ * Ensures that the Directory Server is running.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ memberType = DirectoryServer.getAttributeType("member", false);
+ assertNotNull(memberType);
+
+ uniqueMemberType = DirectoryServer.getAttributeType("uniquemember", false);
+ assertNotNull(uniqueMemberType);
+
+ groupManager = DirectoryServer.getGroupManager();
+
+ u1 = DN.decode("uid=test.1,ou=People,o=test");
+ u2 = DN.decode("uid=test.2,ou=People,o=test");
+ u3 = DN.decode("uid=test.3,ou=People,o=test");
+ u4 = DN.decode("uid=test.4,ou=People,o=test");
+ da = DN.decode("cn=Dynamic All Users,ou=Groups,o=test");
+ d1 = DN.decode("cn=Dynamic One User,ou=Groups,o=test");
+ sm = DN.decode("cn=Static member List,ou=Groups,o=test");
+ su = DN.decode("cn=Static uniqueMember List,ou=Groups,o=test");
+ vmda = DN.decode("cn=Virtual member All Users,ou=Groups,o=test");
+ vuda = DN.decode("cn=Virtual uniqueMember All Users,ou=Groups,o=test");
+ vmd1 = DN.decode("cn=Virtual member One User,ou=Groups,o=test");
+ vud1 = DN.decode("cn=Virtual uniqueMember One User,ou=Groups,o=test");
+ vsm = DN.decode("cn=Virtual Static member List,ou=Groups,o=test");
+ vsu = DN.decode("cn=Virtual Static uniqueMember List,ou=Groups,o=test");
+ vcm = DN.decode("cn=Crossover member Static Group,ou=Groups,o=test");
+ vcu = DN.decode("cn=Crossover uniqueMember Static Group,ou=Groups,o=test");
+ vn = DN.decode("cn=Virtual Nonexistent,ou=Groups,o=test");
+ ne = DN.decode("cn=Nonexistent,ou=Groups,o=test");
+ }
+
+
+
+ /**
+ * Tests creating a new instance of a virtual static group from a valid entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testCreateValidGroup()
+ throws Exception
+ {
+ Entry entry = TestCaseUtils.makeEntry(
+ "dn: cn=Valid Virtual Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Valid Virtual Static Group",
+ "ds-target-group-dn: cn=Static member List,ou=Groups,o=test");
+
+ VirtualStaticGroup groupImplementation = new VirtualStaticGroup();
+ VirtualStaticGroup groupInstance = groupImplementation.newInstance(entry);
+ assertNotNull(groupInstance);
+ groupImplementation.finalizeGroupImplementation();
+ }
+
+
+
+ /**
+ * Retrieves a set of invalid vittual static group definition entries.
+ *
+ * @return A set of invalid virtul static group definition entries.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @DataProvider(name = "invalidGroups")
+ public Object[][] getInvalidGroupDefinitions()
+ throws Exception
+ {
+ List<Entry> groupEntries = TestCaseUtils.makeEntries(
+ "dn: cn=Not a Virtual Static Group,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "cn: Not a Virtual Static Group",
+ "member: uid=test.1,ou=People,o=test",
+ "",
+ "dn: cn=No Target,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: No Target",
+ "",
+ "dn: cn=Invalid Target,ou=Groups,o=test",
+ "objectClass: top",
+ "objectClass: groupOfNames",
+ "objectClass: ds-virtual-static-group",
+ "cn: Invalid Target",
+ "ds-target-group-dn: invalid");
+
+ Object[][] entryArray = new Object[groupEntries.size()][1];
+ for (int i=0; i < entryArray.length; i++)
+ {
+ entryArray[i][0] = groupEntries.get(i);
+ }
+
+ return entryArray;
+ }
+
+
+
+ /**
+ * Tests creating a new instance of a virtual static group from an invalid
+ * entry.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test(dataProvider = "invalidGroups",
+ expectedExceptions = { DirectoryException.class })
+ public void testCreateInvalidGroup(Entry entry)
+ throws Exception
+ {
+ VirtualStaticGroup groupImplementation = new VirtualStaticGroup();
+ try
+ {
+ VirtualStaticGroup groupInstance = groupImplementation.newInstance(entry);
+ }
+ finally
+ {
+ groupImplementation.finalizeGroupImplementation();
+ }
+ }
+
+
+
+ /**
+ * Performs general tests of the group API for virtual static groups with a
+ * group that has a real target group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGroupAPI()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualStaticGroup g =
+ (VirtualStaticGroup) groupManager.getGroupInstance(vmda);
+ assertNotNull(g);
+ assertTrue(g.isMember(u1));
+
+ assertNotNull(g.getGroupDefinitionFilter());
+ assertEquals(g.getGroupDN(), vmda);
+ assertEquals(g.getTargetGroupDN(), da);
+ assertFalse(g.supportsNestedGroups());
+ assertTrue(g.getNestedGroupDNs().isEmpty());
+ assertFalse(g.mayAlterMemberList());
+
+ Entry entry = DirectoryServer.getEntry(u1);
+ assertTrue(g.isMember(entry));
+
+ MemberList memberList = g.getMembers();
+ assertTrue(memberList.hasMoreMembers());
+ assertNotNull(memberList.nextMemberDN());
+ assertNotNull(memberList.nextMemberEntry());
+ assertNotNull(memberList.nextMemberDN());
+ assertNotNull(memberList.nextMemberDN());
+ assertFalse(memberList.hasMoreMembers());
+
+ SearchFilter filter = SearchFilter.createFilterFromString("(sn=1)");
+ memberList = g.getMembers(DN.decode("o=test"), SearchScope.WHOLE_SUBTREE,
+ filter);
+ assertTrue(memberList.hasMoreMembers());
+ assertNotNull(memberList.nextMemberDN());
+ assertFalse(memberList.hasMoreMembers());
+
+ try
+ {
+ g.addNestedGroup(d1);
+ fail("Expected an exception from addNestedGroupDN");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.removeNestedGroup(d1);
+ fail("Expected an exception from removeNestedGroupDN");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.addMember(entry);
+ fail("Expected an exception from addMember");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.removeMember(u1);
+ fail("Expected an exception from removeMember");
+ } catch (Exception e) {}
+
+ assertNotNull(g.toString());
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Performs general tests of the group API for virtual static groups with a
+ * group that has a nonexistent target group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testGroupAPINonexistent()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualStaticGroup g =
+ (VirtualStaticGroup) groupManager.getGroupInstance(vn);
+ assertNotNull(g);
+
+ assertNotNull(g.getGroupDefinitionFilter());
+ assertEquals(g.getGroupDN(), vn);
+ assertEquals(g.getTargetGroupDN(), ne);
+ assertFalse(g.supportsNestedGroups());
+ assertTrue(g.getNestedGroupDNs().isEmpty());
+ assertFalse(g.mayAlterMemberList());
+
+ Entry entry = DirectoryServer.getEntry(u1);
+
+ try
+ {
+ g.isMember(u1);
+ fail("Expected an exception from isMember(DN)");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.isMember(entry);
+ fail("Expected an exception from isMember(Entry)");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.getMembers();
+ fail("Expected an exception from getMembers()");
+ } catch (Exception e) {}
+
+ try
+ {
+ SearchFilter filter = SearchFilter.createFilterFromString("(sn=1)");
+ g.getMembers(DN.decode("o=test"), SearchScope.WHOLE_SUBTREE, filter);
+ fail("Expected an exception from getMembers(base, scope, filter)");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.addNestedGroup(d1);
+ fail("Expected an exception from addNestedGroupDN");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.removeNestedGroup(d1);
+ fail("Expected an exception from removeNestedGroupDN");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.addMember(entry);
+ fail("Expected an exception from addMember");
+ } catch (Exception e) {}
+
+ try
+ {
+ g.removeMember(u1);
+ fail("Expected an exception from removeMember");
+ } catch (Exception e) {}
+
+ assertNotNull(g.toString());
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Tests the behavior of the virtual static group with a dynamic group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualGroupDynamicGroupWithMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualStaticGroup g =
+ (VirtualStaticGroup) groupManager.getGroupInstance(vmda);
+ assertNotNull(g);
+ assertTrue(g.isMember(u1));
+ assertTrue(g.isMember(u2));
+ assertTrue(g.isMember(u3));
+ assertTrue(g.isMember(u4));
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Tests the behavior of the virtual static group with a static group based on
+ * the member attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualGroupStaticGroupWithMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualStaticGroup g =
+ (VirtualStaticGroup) groupManager.getGroupInstance(vsm);
+ assertNotNull(g);
+ assertTrue(g.isMember(u1));
+ assertFalse(g.isMember(u2));
+ assertTrue(g.isMember(u3));
+ assertFalse(g.isMember(u4));
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Tests the behavior of the virtual static group with a static group based on
+ * the uniqueMember attribute.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualGroupStaticGroupWithUniqueMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualStaticGroup g =
+ (VirtualStaticGroup) groupManager.getGroupInstance(vsu);
+ assertNotNull(g);
+ assertFalse(g.isMember(u1));
+ assertTrue(g.isMember(u2));
+ assertTrue(g.isMember(u3));
+ assertFalse(g.isMember(u4));
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Performs general tests of the virtual attribute provider API for the member
+ * virtual attribute with a target group that exists.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualAttributeAPI()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualAttributeRule rule = null;
+ for (VirtualAttributeRule r : DirectoryServer.getVirtualAttributes())
+ {
+ if (r.getAttributeType().equals(memberType))
+ {
+ rule = r;
+ break;
+ }
+ }
+ assertNotNull(rule);
+
+ MemberVirtualAttributeProvider provider =
+ (MemberVirtualAttributeProvider) rule.getProvider();
+ assertNotNull(provider);
+
+ Entry entry = DirectoryServer.getEntry(vsm);
+ assertNotNull(entry);
+
+ assertTrue(provider.isMultiValued());
+
+ LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+ assertNotNull(values);
+ assertFalse(values.isEmpty());
+ assertTrue(provider.hasValue(entry, rule));
+ assertTrue(provider.hasValue(entry, rule,
+ new AttributeValue(memberType, u1.toString())));
+ assertFalse(provider.hasValue(entry, rule,
+ new AttributeValue(memberType, ne.toString())));
+ assertTrue(provider.hasAnyValue(entry, rule, values));
+ assertFalse(provider.hasAnyValue(entry, rule,
+ Collections.<AttributeValue>emptySet()));
+ assertEquals(provider.matchesSubstring(entry, rule, null, null, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.greaterThanOrEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.lessThanOrEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.approximatelyEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null, DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString(
+ "(member=" + u1.toString() + ")"),
+ null, null);
+ assertFalse(provider.isSearchable(rule, searchOperation));
+
+ provider.processSearch(rule, searchOperation);
+ assertFalse(searchOperation.getResultCode() == ResultCode.SUCCESS);
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Performs general tests of the virtual attribute provider API for the member
+ * virtual attribute with a target group that does not exist.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualAttributeAPINonexistent()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ VirtualAttributeRule rule = null;
+ for (VirtualAttributeRule r : DirectoryServer.getVirtualAttributes())
+ {
+ if (r.getAttributeType().equals(memberType))
+ {
+ rule = r;
+ break;
+ }
+ }
+ assertNotNull(rule);
+
+ MemberVirtualAttributeProvider provider =
+ (MemberVirtualAttributeProvider) rule.getProvider();
+ assertNotNull(provider);
+
+ Entry entry = DirectoryServer.getEntry(vn);
+ assertNotNull(entry);
+
+ assertTrue(provider.isMultiValued());
+
+ LinkedHashSet<AttributeValue> values = provider.getValues(entry, rule);
+ assertNotNull(values);
+ assertTrue(values.isEmpty());
+ assertFalse(provider.hasValue(entry, rule));
+ assertFalse(provider.hasValue(entry, rule,
+ new AttributeValue(memberType, u1.toString())));
+ assertFalse(provider.hasValue(entry, rule,
+ new AttributeValue(memberType, ne.toString())));
+ assertFalse(provider.hasAnyValue(entry, rule, values));
+ assertFalse(provider.hasAnyValue(entry, rule,
+ Collections.<AttributeValue>emptySet()));
+ assertEquals(provider.matchesSubstring(entry, rule, null, null, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.greaterThanOrEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.lessThanOrEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+ assertEquals(provider.approximatelyEqualTo(entry, rule, null),
+ ConditionResult.UNDEFINED);
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(conn, conn.nextOperationID(),
+ conn.nextMessageID(), null, DN.decode("o=test"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
+ SearchFilter.createFilterFromString(
+ "(member=" + u1.toString() + ")"),
+ null, null);
+ assertFalse(provider.isSearchable(rule, searchOperation));
+
+ provider.processSearch(rule, searchOperation);
+ assertFalse(searchOperation.getResultCode() == ResultCode.SUCCESS);
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Tests the behavior of the member virtual attribute with a dynamic group.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualAttrDynamicGroupWithMember()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ Entry e = DirectoryServer.getEntry(vmda);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(memberType));
+
+ Attribute a = e.getAttribute(memberType).get(0);
+ assertEquals(a.getValues().size(), 4);
+
+ AttributeValue v = new AttributeValue(memberType, u1.toString());
+ assertTrue(a.hasValue(v));
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Tests the behavior of the member virtual attribute with a dynamic group.
+ * The target dynamic group will initially have only one memberURL which
+ * matches only one user, but will then be updated on the fly to contain a
+ * second URL that matches all users.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @Test()
+ public void testVirtualAttrDynamicGroupWithUpdatedMemberURLs()
+ throws Exception
+ {
+ TestCaseUtils.initializeTestBackend(true);
+ TestCaseUtils.addEntries(LDIF_LINES);
+
+ Entry e = DirectoryServer.getEntry(vmd1);
+ assertNotNull(e);
+ assertTrue(e.hasAttribute(memberType));
+
+ Attribute a = e.getAttribute(memberType).get(0);
+ assertEquals(a.getValues().size(), 1);
+
+ AttributeValue v = new AttributeValue(memberType, u4.toString());
+ assertTrue(a.hasValue(v));
+
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ LinkedList<Modification> mods = new LinkedList<Modification>();
+ mods.add(new Modification(ModificationType.ADD,
+ new Attribute("memberurl",
+ "ldap:///o=test??sub?(objectClass=person)")));
+ ModifyOperation modifyOperation = conn.processModify(d1, mods);
+ assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+ a = e.getAttribute(memberType).get(0);
+ assertEquals(a.getValues().size(), 4);
+ assertTrue(a.hasValue(v));
+
+ cleanUp();
+ }
+
+
+
+ /**
+ * Removes all of the groups that have been added to the server.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ private void cleanUp()
+ throws Exception
+ {
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ InternalSearchOperation searchOperation =
+ conn.processSearch(DN.decode("ou=Groups,dc=example,dc=com"),
+ SearchScope.SINGLE_LEVEL,
+ SearchFilter.createFilterFromString("(objectClass=*)"));
+ for (Entry e : searchOperation.getSearchEntries())
+ {
+ conn.processDelete(e.getDN());
+ }
+ }
+}
+
--
Gitblit v1.10.0