From 7dbb2ffb7f4587baf363193f7e25aa11bfc775c4 Mon Sep 17 00:00:00 2001
From: dugan <dugan@localhost>
Date: Fri, 06 Apr 2007 00:01:33 +0000
Subject: [PATCH] Add ACI support for LDAP modify DN operation (export and import rights). Also add support for self-write (selfwrite) right.
---
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java | 177 +++++++++++++++++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java | 13 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java | 47 +++++
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java | 7
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java | 6
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java | 219 ++++++++++++++++++++---
opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java | 10 +
opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java | 18 +
8 files changed, 448 insertions(+), 49 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
index 8a27eb5..5308889 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/Aci.java
@@ -183,7 +183,7 @@
public static final int ACI_SEARCH = 0x0020;
/**
- * ACI_SELF is used for the SELFWRITE right. Currently not implemented.
+ * ACI_SELF is used for the SELFWRITE right.
*/
public static final int ACI_SELF = 0x0040;
@@ -200,13 +200,13 @@
/**
* ACI_IMPORT is used to set the container rights for a LDAP
- * modify dn operation. Currently not used.
+ * modify dn operation.
*/
public static final int ACI_IMPORT = 0x0100;
/**
* ACI_EXPORT is used to set the container rights for a LDAP
- * modify dn operation. Currently not used.
+ * modify dn operation.
*/
public static final int ACI_EXPORT = 0x0200;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
index f46529d..8544649 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -44,8 +44,10 @@
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.InternalClientConnection;
+import static org.opends.server.schema.SchemaConstants.*;
import java.util.*;
+import java.util.concurrent.locks.Lock;
/**
* The AciHandler class performs the main processing for the
@@ -216,9 +218,9 @@
List<Modification> modifications=container.getModifications();
for(Modification m : modifications) {
Attribute modAttr=m.getAttribute();
- AttributeType modType=modAttr.getAttributeType();
+ AttributeType modAttrType=modAttr.getAttributeType();
- if(modType.equals(aciType)) {
+ if(modAttrType.equals(aciType)) {
/*
* Check that the operation has modify privileges if
* it contains an "aci" attribute type.
@@ -236,20 +238,22 @@
return false;
}
}
-
- switch(m.getModificationType()) {
- case DELETE:
- case REPLACE:
- case INCREMENT:
- {
- /*
+ //This access check handles the case where all attributes of this
+ //type are being replaced or deleted. If only a subset is being
+ //deleted than this access check is skipped.
+ ModificationType modType=m.getModificationType();
+ if((modType == ModificationType.DELETE &&
+ modAttr.getValues().isEmpty()) ||
+ (modType == ModificationType.REPLACE ||
+ modType == ModificationType.INCREMENT)) {
+ /*
* Check if we have rights to delete all values of
* an attribute type in the resource entry.
*/
- if(resourceEntry.hasAttribute(modType)) {
- container.setCurrentAttributeType(modType);
+ if(resourceEntry.hasAttribute(modAttrType)) {
+ container.setCurrentAttributeType(modAttrType);
List<Attribute> attrList =
- resourceEntry.getAttribute(modType,modAttr.getOptions());
+ resourceEntry.getAttribute(modAttrType,modAttr.getOptions());
for (Attribute a : attrList) {
for (AttributeValue v : a.getValues()) {
container.setCurrentAttributeValue(v);
@@ -260,11 +264,11 @@
}
}
}
- }
- }
+ }
+
if(modAttr.hasValue()) {
for(AttributeValue v : modAttr.getValues()) {
- container.setCurrentAttributeType(modType);
+ container.setCurrentAttributeType(modAttrType);
switch (m.getModificationType())
{
case ADD:
@@ -283,7 +287,7 @@
case INCREMENT:
Entry modifiedEntry = operation.getModifiedEntry();
List<Attribute> modifiedAttrs =
- modifiedEntry.getAttribute(modType,
+ modifiedEntry.getAttribute(modAttrType,
modAttr.getOptions());
if (modifiedAttrs != null)
{
@@ -305,12 +309,12 @@
If so, check the syntax of that attribute value. Fail the
the operation if the syntax check fails.
*/
- if(modType.equals(aciType) ||
- modType.equals(globalAciType)) {
+ if(modAttrType.equals(aciType) ||
+ modAttrType.equals(globalAciType)) {
try {
//A global ACI needs a NULL DN, not the DN of the
//modification.
- if(modType.equals(globalAciType))
+ if(modAttrType.equals(globalAciType))
dn=DN.nullDN();
Aci.decode(v.getValue(),dn);
} catch (AciException ex) {
@@ -420,6 +424,25 @@
if(container.hasRights(ACI_WRITE_ADD) ||
container.hasRights(ACI_WRITE_DELETE))
container.setRights(container.getRights() | ACI_WRITE);
+ //Check if the ACI_SELF right needs to be set (selfwrite right).
+ //Only done if the right is ACI_WRITE, an attribute value is set and
+ //that attribute value is a DN.
+ if((container.getCurrentAttributeValue() != null) &&
+ (container.hasRights(ACI_WRITE)) &&
+ (isAttributeDN(container.getCurrentAttributeType()))) {
+ try {
+ String DNString =
+ container.getCurrentAttributeValue().getStringValue();
+ DN tmpDN = DN.decode(DNString);
+ //Have a valid DN, compare to clientDN to see if the ACI_SELF
+ //right should be set.
+ if(tmpDN.equals(container.getClientDN())) {
+ container.setRights(container.getRights() | ACI_SELF);
+ }
+ } catch (DirectoryException ex) {
+ return false;
+ }
+ }
/*
* First get all allowed candidate ACIs.
*/
@@ -435,6 +458,17 @@
return(testApplicableLists(container));
}
+ /**
+ * Check if the specified attribute type is a DN by checking if its syntax
+ * OID is equal to the DN syntax OID.
+ * @param attribute The attribute type to check.
+ * @return True if the attribute type syntax OID is equal to a DN syntax OID.
+ */
+ private boolean isAttributeDN(AttributeType attribute) {
+ return (attribute.getSyntaxOID().equals(SYNTAX_DN_OID));
+ }
+
+
/**
* Performs an access check against all of the attributes of an entry.
* The attributes that fail access are removed from the entry. This method
@@ -781,15 +815,148 @@
return returnEntry;
}
- //Planned to be implemented methods
+ /**
+ * Perform all needed RDN checks for the modifyDN operation. These checks
+ * are:
+ *
+ * - Verify WRITE access to the entry.
+ * - Verfiy WRITE_ADD access on each RDN component of the new RDN. The
+ * WRITE_ADD access is used because this access could be restricted by
+ * the targattrfilters keyword.
+ * - If the deleteOLDRDN flag is set, verify WRITE_DELETE access on the
+ * old RDN. The WRITE_DELETE access is used because this access could be
+ * restricted by the targattrfilters keyword.
+ *
+ * @param operation The ModifyDN operation class containing information to
+ * check access on.
+ * @return True if access is allowed.
+ */
+ private boolean aciCheckRDNs(ModifyDNOperation operation) {
+ boolean ret;
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_WRITE),
+ operation.getOriginalEntry());
+ ret=accessAllowed(operationContainer);
+ if(ret)
+ ret=checkRDN(ACI_WRITE_ADD,operation.getNewRDN(),operationContainer);
+ if(ret && operation.deleteOldRDN()) {
+ RDN oldRDN=operation.getOriginalEntry().getDN().getRDN();
+ ret =
+ checkRDN(ACI_WRITE_DELETE, oldRDN, operationContainer);
+ }
+ return ret;
+ }
+
/**
+ * Check access on each attribute-value pair component of the specified RDN.
+ * There may be more than one attribute-value pair if the RDN is multi-valued.
+ *
+ * @param right The access right to check for.
+ * @param rdn The RDN to examine the attribute-value pairs of.
+ * @param container The container containing the information needed to
+ * evaluate the specified RDN.
+ * @return True if access is allowed for all attribute-value pairs.
+ */
+ private boolean checkRDN(int right, RDN rdn, AciContainer container) {
+ boolean ret=false;
+ int numAVAs = rdn.getNumValues();
+ container.setRights(right);
+ for (int i = 0; i < numAVAs; i++){
+ AttributeType type=rdn.getAttributeType(i);
+ AttributeValue value=rdn.getAttributeValue(i);
+ container.setCurrentAttributeType(type);
+ container.setCurrentAttributeValue(value);
+ if(!(ret=accessAllowed(container)))
+ break;
+ }
+ return ret;
+ }
+
+ /**
+ * Check access on the new superior entry if it exists. If the entry does not
+ * exist or the DN cannot be locked then false is returned.
+ *
+ * @param superiorDN The DN of the new superior entry.
+ * @param op The modifyDN operation to check access on.
+ * @return True if access is granted to the new superior entry.
+ * @throws DirectoryException If a problem occurs while trying to
+ * retrieve the new superior entry.
+ */
+ private boolean aciCheckSuperiorEntry(DN superiorDN, ModifyDNOperation op)
+ throws DirectoryException {
+ boolean ret=false;
+ Lock entryLock = null;
+ for (int i=0; i < 3; i++) {
+ entryLock = LockManager.lockRead(superiorDN);
+ if (entryLock != null)
+ break;
+ }
+ if (entryLock == null) {
+ int msgID = MSGID_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER;
+ String message = getMessage(msgID, String.valueOf(superiorDN));
+ logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.INFORMATIONAL,
+ message, msgID);
+ return false;
+ }
+ try {
+ Entry superiorEntry=DirectoryServer.getEntry(superiorDN);
+ if(superiorEntry!= null) {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(op, (ACI_IMPORT),
+ superiorEntry);
+ ret=accessAllowed(operationContainer);
+ }
+ } finally {
+ LockManager.unlock(superiorDN, entryLock);
+ }
+ return ret;
+ }
+
+ /**
+ * Checks access on a modifyDN operation.
+ *
+ * @param operation The modifyDN operation to check access on.
+ * @return True if access is allowed.
+ *
+ */
+ public boolean isAllowed(ModifyDNOperation operation) {
+ boolean ret=true;
+ DN newSuperiorDN;
+ if(!skipAccessCheck(operation)) {
+ //If this is a modifyDN move to a new superior, then check if the
+ //superior DN has import accesss.
+ if((newSuperiorDN=operation.getNewSuperior()) != null) {
+ try {
+ ret=aciCheckSuperiorEntry(newSuperiorDN, operation);
+ } catch (DirectoryException ex) {
+ ret=false;
+ }
+ }
+ //Perform the RDN access checks.
+ if(ret)
+ ret=aciCheckRDNs(operation);
+ //If this is a modifyDN move to a new superior, then check if the
+ //original entry DN has export access.
+ if(ret && (newSuperiorDN != null)) {
+ AciLDAPOperationContainer operationContainer =
+ new AciLDAPOperationContainer(operation, (ACI_EXPORT),
+ operation.getOriginalEntry());
+ ret=accessAllowed(operationContainer);
+ }
+ }
+ return ret;
+ }
+
+ //Not planned to be implemented methods.
+
+ /**
* {@inheritDoc}
*/
@Override
public boolean maySend(SearchOperation operation,
SearchResultReference reference) {
- //TODO: Planned to be implemented.
+ //TODO: Deferred.
return true;
}
@@ -797,16 +964,6 @@
* {@inheritDoc}
*/
@Override
- public boolean isAllowed(ModifyDNOperation modifyDNOperation) {
- // TODO: Planned to be implemented.
- return true;
- }
-
- //Not planned to be implemented methods.
- /**
- * {@inheritDoc}
- */
- @Override
public boolean isAllowed(BindOperation bindOperation) {
//Not planned to be implemented.
return true;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
index 30b9560..cf1898d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciLDAPOperationContainer.java
@@ -29,13 +29,10 @@
import java.util.List;
-import org.opends.server.core.AddOperation;
-import org.opends.server.core.CompareOperation;
-import org.opends.server.core.DeleteOperation;
-import org.opends.server.core.ModifyOperation;
-import org.opends.server.core.SearchOperation;
+import org.opends.server.core.*;
import org.opends.server.types.Modification;
import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.Entry;
/**
* The AciLDAPOperationContainer is an AciContainer
@@ -93,6 +90,17 @@
}
/**
+ * Constructor interface for the modify DN operation.
+ * @param operation The modify DN operation.
+ * @param rights The rights of the modify DN operation.
+ * @param entry The entry to evalauted for this modify DN.
+ */
+ public AciLDAPOperationContainer(ModifyDNOperation operation, int rights,
+ Entry entry) {
+ super(operation, rights, entry);
+ }
+
+ /**
* Constructor interface for the LDAP search operation.
* @param operation The search operation.
* @param rights The rights of a search operation.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
index 399ca85e..7c6d913 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciList.java
@@ -343,4 +343,51 @@
// Replace the ACI list with the copy.
aciList = aciCopy;
}
+
+ /**
+ * Rename all ACIs under the specified old DN to the new DN. A simple
+ * interation over the entire list is performed.
+ * @param oldDN The DN of the original entry that was moved.
+ * @param newDN The DN of the new entry.
+ */
+ public synchronized void renameAci(DN oldDN, DN newDN ) {
+ LinkedHashMap<DN, List<Aci>> newCopyList =
+ new LinkedHashMap<DN, List<Aci>>();
+ int oldRDNCount=oldDN.getNumComponents();
+ int newRDNCount=newDN.getNumComponents();
+ for (Map.Entry<DN,List<Aci>> hashEntry : aciList.entrySet()) {
+ if(hashEntry.getKey().isDescendantOf(oldDN)) {
+ int keyRDNCount=hashEntry.getKey().getNumComponents();
+ int keepRDNCount=keyRDNCount - oldRDNCount;
+ RDN[] newRDNs = new RDN[keepRDNCount + newRDNCount];
+ for (int i=0; i < keepRDNCount; i++)
+ newRDNs[i] = hashEntry.getKey().getRDN(i);
+ for (int i=keepRDNCount, j=0; j < newRDNCount; i++,j++)
+ newRDNs[i] = newDN.getRDN(j);
+ DN relocateDN=new DN(newRDNs);
+ List<Aci> acis = new LinkedList<Aci>();
+ for(Aci aci : hashEntry.getValue()) {
+ try {
+ Aci newAci =
+ Aci.decode(ByteStringFactory.create(aci.toString()), relocateDN);
+ acis.add(newAci);
+ } catch (AciException ex) {
+ //This should never happen since only a copy of the
+ //ACI with a new DN is being made. Log a message if it does and
+ //keep going.
+ int msgID = MSGID_ACI_ADD_LIST_FAILED_DECODE;
+ String message = getMessage(msgID,
+ ex.getMessage());
+ logError(ErrorLogCategory.ACCESS_CONTROL,
+ ErrorLogSeverity.INFORMATIONAL,
+ message, msgID);
+ }
+ }
+ newCopyList.put(relocateDN, acis);
+ } else
+ newCopyList.put(hashEntry.getKey(), hashEntry.getValue());
+ }
+ // Replace the ACI list with the copy.
+ aciList = newCopyList;
+ }
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
index 5c3e268..9841ec6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/authorization/dseecompat/AciListenerManager.java
@@ -150,7 +150,8 @@
}
/**
- * Not implemented.
+ * A modify DN operation has succeeded. Adjust the ACIs by moving ACIs
+ * under the old entry DN to the new entry DN.
* @param modifyDNOperation The LDAP modify DN operation.
* @param oldEntry The old entry.
* @param newEntry The new entry.
@@ -159,9 +160,7 @@
PostResponseModifyDNOperation modifyDNOperation,
Entry oldEntry, Entry newEntry)
{
- /*
- * TODO Not yet implemented.
- */
+ aciList.renameAci(oldEntry.getDN(), newEntry.getDN());
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
index 23c43e9..0103519 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/AciMessages.java
@@ -722,6 +722,13 @@
public static final int MSGID_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN =
CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 71;
+ /**
+ * The message ID for the message that will be used if the server is unable to
+ * obtain a lock on a ModifyDN new superior entry. This takes a
+ * single argument, which is the DN of the new superior entry.
+ */
+ public static final int MSGID_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER =
+ CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 72;
/**
* Associates a set of generic messages with the message IDs defined in
@@ -1137,5 +1144,8 @@
registerMessage(MSGID_PATTERN_DN_TYPE_WILDCARD_IN_MULTIVALUED_RDN,
"The pattern DN %s is not valid because it contains a wildcard in " +
"an attribute type in a multi-valued RDN");
+
+ registerMessage(MSGID_ACI_HANDLER_CANNOT_LOCK_NEW_SUPERIOR_USER,
+ "Unable to obtain a lock on the ModifyDN new superior entry %s.");
}
}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
index a849e1b..7948766 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/authorization/dseecompat/AciTests.java
@@ -155,10 +155,18 @@
private static final String OU_GROUP_1_DN = "cn=group1," + OU_GROUPS_DN;
private static final String OU_GROUP_2_DN = "cn=group2," + OU_GROUPS_DN;
//End group entries.
- private static final String MANAGER_DN = "cn=the managers,dc=example,dc=com";
+ //Used by modrdn new superior
+ private static final String MANAGER_NEW_DN =
+ "cn=new managers," + OU_BASE_DN;
+ private static final String MGR_NEW_DN_URL = "ldap:///" + MANAGER_NEW_DN;
+ private static final String MANAGER_DN = "cn=the managers," + OU_BASE_DN;
+ private static final String MGR_DN_URL = "ldap:///" + MANAGER_DN;
//These entries are going to be used to test userattr parent stuff.
private static final String SALES_DN = "cn=sales dept," + MANAGER_DN;
+ private static final String SALES_NEW_DN = "cn=sales dept," + MANAGER_NEW_DN;
private static final String SALES_USER_1 = "cn=sales1 person," + SALES_DN;
+ private static final String SALES_USER_NEW_1 =
+ "cn=sales1 person," + SALES_NEW_DN;
private static final String SALES_USER_2 = "cn=sales2 person," + SALES_DN;
private static final String SALES_USER_3 = "cn=sales3 person," + SALES_DN;
private static final String LEVEL_1_USER_URL =
@@ -190,6 +198,7 @@
OU_LEAF_DN,
OU_INNER_DN,
MANAGER_DN,
+ MANAGER_NEW_DN,
OU_GROUPS_DN,
OU_BASE_DN,
ADMIN_DN,
@@ -297,6 +306,27 @@
private static final String ALLOW_ALL_TO_COMPARE =
buildAciValue("name", "allow compare", "targetattr", "*", "target", "ldap:///cn=*," + OU_LEAF_DN, "allow(compare)", BIND_RULE_USERDN_ALL);
+ private static final String ALLOW_ALL_TO_IMPORT_MGR_NEW =
+ buildAciValue("name", "allow import mgr new tree", "target", MGR_NEW_DN_URL, "allow(import)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_IMPORT_MGR=
+ buildAciValue("name", "allow import mgr tree", "target", MGR_DN_URL, "allow(import)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_EXPORT_MGR_NEW =
+ buildAciValue("name", "allow export mgr new tree", "target", MGR_NEW_DN_URL, "allow(export)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_EXPORT_MGR=
+ buildAciValue("name", "allow export mgr tree", "target", MGR_DN_URL, "allow(export)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_WRITE_RDN_ATTRS=
+ buildAciValue("name", "allow write to RDN attrs", "targetattr", "uid || cn || sn", "allow(write)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_MOVED_ENTRY =
+ buildAciValue("name", "allow all to moved", "targetattr", "*", "allow(search,read)", BIND_RULE_USERDN_ALL);
+
+ private static final String ALLOW_ALL_TO_SELFWRITE =
+ buildAciValue("name", "allow selfwrite", "targetattr", "member", "allow(selfwrite)", BIND_RULE_USERDN_ALL);
+
private static final String ALLOW_ALL_TO_ADMIN =
buildAciValue("name", "allow all to admin", "targetattr", "*", "allow(all)", BIND_RULE_USERDN_ADMIN);
@@ -944,6 +974,11 @@
makeUserLdif(MANAGER_DN, "the", "managers", "pa$$word",
ADMIN_DN, OU_GROUP_2_DN );
+ private static final String MANAGER_NEW__SEARCH_TESTS =
+ makeUserLdif(MANAGER_NEW_DN, "new", "managers", "pa$$word",
+ ADMIN_DN, OU_GROUP_2_DN );
+
+
private static final String SALES__SEARCH_TESTS =
makeUserLdif(SALES_DN, "sales", "dept", "pa$$word",
LEVEL_2_USER_DN, LEVEL_1_USER_URL);
@@ -951,6 +986,7 @@
//LDIF entries used to test group stuff.
private static final String GROUP_LDIF__SEARCH_TESTS =
makeOuLdif(OU_GROUPS_DN, "groups");
+
private static final
String GROUP_1_LDIF__SEARCH_TESTS = makeGroupLdif(OU_GROUP_1_DN,
LEVEL_1_USER_DN,
@@ -1009,6 +1045,31 @@
String COMPARE_ACI = makeAddAciLdif(OU_LEAF_DN,
ALLOW_ALL_TO_COMPARE);
+ //ACI used to test selfwrite
+ private static final
+ String SELFWRITE_ACI = makeAddAciLdif(OU_GROUP_1_DN,
+ ALLOW_ALL_TO_SELFWRITE);
+
+ //ACIs used for modDN tests (export, import)
+
+ private static final String ACI_IMPORT_MGR_NEW =
+ makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_IMPORT_MGR_NEW);
+
+ private static final String ACI_IMPORT_MGR =
+ makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_IMPORT_MGR);
+
+ private static final String ACI_EXPORT_MGR_NEW =
+ makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_EXPORT_MGR_NEW);
+
+ private static final String ACI_EXPORT_MGR =
+ makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_EXPORT_MGR);
+
+ private static final String ACI_WRITE_RDN_ATTRS =
+ makeAddAciLdif(OU_BASE_DN, ALLOW_ALL_TO_WRITE_RDN_ATTRS);
+
+ private static final String ACI_MOVED_ENTRY =
+ makeAddAciLdif(SALES_USER_1, ALLOW_ALL_TO_MOVED_ENTRY);
+
//ACI used in testing the groupdn/roledn bind rule keywords.
private static final
@@ -1046,6 +1107,7 @@
USER_LDIF__SEARCH_TESTS +
BASE_OU_LDIF__SEARCH_TESTS +
MANAGER__SEARCH_TESTS +
+ MANAGER_NEW__SEARCH_TESTS +
SALES__SEARCH_TESTS +
SALES_USER_1__SEARCH_TESTS +
SALES_USER_2__SEARCH_TESTS +
@@ -1578,7 +1640,7 @@
/**
* Test LDAP compare.
- * @throws Throwable If the search returned is not valid for the ACI.
+ * @throws Throwable If the compare is not valid for the ACI.
*/
@Test()
public void testCompare() throws Throwable {
@@ -1597,7 +1659,82 @@
}
}
- /**
+ /**
+ * Test modify DN. Add a set of ACIs to allow exports, imports and write
+ * rights. Also add an aci low in the DIT to test the ACI list after a move
+ * has been made. Move the subtree, search with base at new DN, move the
+ * tree back and re-search with base at orig DN.
+ * @throws Throwable
+ */
+ @Test()
+ public void testModDN() throws Throwable {
+ SingleSearchParams userParamOrig = new SingleSearchParams(LEVEL_1_USER_DN,
+ "pa$$word", SALES_USER_1,
+ OBJECTCLASS_STAR, SCOPE_BASE,
+ null, null, null);
+ SingleSearchParams userParamNew = new SingleSearchParams(LEVEL_1_USER_DN,
+ "pa$$word", SALES_USER_NEW_1,
+ OBJECTCLASS_STAR, SCOPE_BASE,
+ null, null, null);
+
+
+ try {
+ addEntries(BASIC_LDIF__GROUP_SEARCH_TESTS, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_IMPORT_MGR, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_IMPORT_MGR_NEW, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_EXPORT_MGR, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_EXPORT_MGR_NEW, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_WRITE_RDN_ATTRS, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(ACI_MOVED_ENTRY, DIR_MGR_DN, DIR_MGR_PW);
+ String modrdnLdif =
+ makeModDN(SALES_DN, "cn=sales dept", "0", MANAGER_NEW_DN);
+ modEntries(modrdnLdif, LEVEL_1_USER_DN, "pa$$word");
+ String userNewResults = ldapSearch(userParamNew.getLdapSearchArgs());
+ Assert.assertFalse(userNewResults.equals(""));
+ String modrdnLdif1 =
+ makeModDN(SALES_NEW_DN, "cn=sales dept", "0", MANAGER_DN);
+ modEntries(modrdnLdif1, LEVEL_1_USER_DN, "pa$$word");
+ String userOrigResults = ldapSearch(userParamOrig.getLdapSearchArgs());
+ Assert.assertFalse(userOrigResults.equals(""));
+ } catch (Throwable e) {
+ throw e;
+ }
+ }
+ /**
+ * Test selfwrite right. Attempt to bind as level3 user and remove level1
+ * user from a group, should fail.
+ * @throws Throwable If the delete succeeds.
+ */
+ @Test()
+ public void testNonSelfWrite() throws Throwable {
+ try {
+ addEntries(BASIC_LDIF__GROUP_SEARCH_TESTS, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(SELFWRITE_ACI, DIR_MGR_DN, DIR_MGR_PW);
+ deleteAttrFromEntry(OU_GROUP_1_DN, "member",LEVEL_1_USER_DN,
+ LEVEL_3_USER_DN, "pa$$word", false);
+ } catch(Throwable e) {
+ throw e;
+ }
+ }
+
+ /**
+ * Test selfwrite right. Attempt to bind as level1 user and remove itself
+ * from a group, should succeed.
+ * @throws Throwable If the delete fails.
+ */
+ @Test()
+ public void testSelfWrite() throws Throwable {
+ try {
+ addEntries(BASIC_LDIF__GROUP_SEARCH_TESTS, DIR_MGR_DN, DIR_MGR_PW);
+ modEntries(SELFWRITE_ACI, DIR_MGR_DN, DIR_MGR_PW);
+ deleteAttrFromEntry(OU_GROUP_1_DN, "member",LEVEL_1_USER_DN,
+ LEVEL_1_USER_DN, "pa$$word", true);
+ } catch(Throwable e) {
+ throw e;
+ }
+ }
+
+ /**
* Test group and role bind rule ACI keywords. Both groupdn and roledn keywords
* funnel through the same code so the results should be the same.
* @throws Throwable
@@ -1919,15 +2056,43 @@
deleteEntries(ALL_TEST_ENTRY_DNS_BOTTOM_UP);
}
- private void deleteAttrFromEntry(String dn, String attr, boolean errorOk) throws Exception {
+ private void deleteAttrFromEntry(String dn, String attr, String val,
+ String bindDN, String pwd,
+ boolean errorOk) throws Exception {
StringBuilder ldif = new StringBuilder();
ldif.append(TestCaseUtils.makeLdif(
"dn: " + dn,
"changetype: modify",
- "delete: " + attr));
- modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW, errorOk, false);
+ "delete: " + attr,
+ attr + ":" + val));
+ modEntries(ldif.toString(), bindDN, pwd, errorOk, false);
}
+
+ private static String makeModDN(String dn, String newRDN, String deleteOldRDN,
+ String newSuperior ) throws Exception {
+ StringBuilder ldif = new StringBuilder();
+ ldif.append("dn: " + dn).append(EOL);
+ ldif.append("changetype: modrdn").append(EOL);
+ ldif.append("newrdn: " + newRDN).append(EOL);
+ ldif.append("deleteoldrdn: " + deleteOldRDN).append(EOL);
+ if(newSuperior != null)
+ ldif.append("newsuperior: " + newSuperior).append(EOL);
+ ldif.append(EOL);
+ return ldif.toString();
+ }
+
+
+
+ private void deleteAttrFromEntry(String dn, String attr, boolean errorOk) throws Exception {
+ StringBuilder ldif = new StringBuilder();
+ ldif.append(TestCaseUtils.makeLdif(
+ "dn: " + dn,
+ "changetype: modify",
+ "delete: " + attr));
+ modEntries(ldif.toString(), DIR_MGR_DN, DIR_MGR_PW, errorOk, false);
+ }
+
private void deleteEntries(String[] entries) throws Exception {
// TODO: make this actually do a search first!
StringBuilder ldif = new StringBuilder();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java
index 8873dc4..e205b42 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java
@@ -147,6 +147,7 @@
"sn: User",
"cn: Proxy User",
"userPassword: password",
+ "ds-privilege-name: bypass-acl",
"ds-privilege-name: proxied-auth");
Entry proxyUserEntry =
@@ -1116,7 +1117,19 @@
ASN1Reader r = new ASN1Reader(s);
ASN1Writer w = new ASN1Writer(s);
r.setIOTimeout(6000);
+ BindRequestProtocolOp bindRequest =
+ new BindRequestProtocolOp(
+ new ASN1OctetString("cn=Directory Manager"),
+ 3, new ASN1OctetString("password"));
+ LDAPMessage bindMessage = new LDAPMessage(1, bindRequest);
+ w.writeElement(bindMessage.encode());
+ bindMessage = LDAPMessage.decode(r.readElement().decodeAsSequence());
+ BindResponseProtocolOp bindResponse = bindMessage.getBindResponseProtocolOp();
+ assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+ assertTrue(DirectoryServer.getWorkQueue().waitUntilIdle(10000));
+ InvocationCounterPlugin.resetAllCounters();
ModifyDNRequestProtocolOp modifyRequest =
new ModifyDNRequestProtocolOp(
new ASN1OctetString(entry.getDN().toString()),
--
Gitblit v1.10.0