Browse Source

Bug 619623 - attr-unique-plugin ignores requiredObjectClass on modrdn operations

The attribute uniqueness plug-in is not checking if renamed entries
meet the requiredObjectClass requirements.  In addition, a rename
that uses a new superior is not properly handled.

This patch adds a check for requiredObjectClass for MODRDN ops.  I
also added a helper function called slapi_entry_rename() which allows
the caller to apply a MODRDN operation to a Slapi_Entry.  My patch
makes use of this to create a renamed dummy entry that is used to
check for any attribute uniqueness conflicts.
Nathan Kinder 15 years ago
parent
commit
20833dee41
3 changed files with 142 additions and 28 deletions
  1. 38 28
      ldap/servers/plugins/uiduniq/uid.c
  2. 86 0
      ldap/servers/slapd/entry.c
  3. 18 0
      ldap/servers/slapd/slapi-plugin.h

+ 38 - 28
ldap/servers/plugins/uiduniq/uid.c

@@ -771,6 +771,9 @@ preop_modify(Slapi_PBlock *pb)
     err = slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
     if (err) { result = uid_op_error(11); break; }
 
+        /*
+         * Check if it has the required object class
+         */
         if (requiredObjectClass &&
                 !(spb = dnHasObjectClass(dn, requiredObjectClass))) { break; }
 
@@ -823,18 +826,16 @@ preop_modify(Slapi_PBlock *pb)
 static int
 preop_modrdn(Slapi_PBlock *pb)
 {
-  int result;
-  Slapi_Entry *e;
+  int result = LDAP_SUCCESS;
+  Slapi_Entry *e = NULL;
+  Slapi_DN *sdn = NULL;
+  Slapi_Value *sv_requiredObjectClass = NULL;
 
 #ifdef DEBUG
     slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
       "MODRDN begin\n");
 #endif
 
-  /* Init */
-  result = LDAP_SUCCESS;
-  e = 0;
-
   BEGIN
     int err;
     char *attrName = NULL;
@@ -843,8 +844,9 @@ preop_modrdn(Slapi_PBlock *pb)
     char *dn;
     char *superior;
     char *rdn;
-        int isupdatedn;
-        Slapi_Attr *attr;
+    int deloldrdn = 0;
+    int isupdatedn;
+    Slapi_Attr *attr;
     int argc;
     char **argv = NULL;
 
@@ -879,10 +881,19 @@ preop_modrdn(Slapi_PBlock *pb)
           break;
         }
 
+    /* Create a Slapi_Value for the requiredObjectClass to use
+     * for checking the entry. */
+    if (requiredObjectClass) {
+        sv_requiredObjectClass = slapi_value_new_string(requiredObjectClass);
+    }
+
     /* Get the DN of the entry being renamed */
     err = slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &dn);
     if (err) { result = uid_op_error(31); break; }
 
+    /* Create a Slapi_DN to use for searching. */
+    sdn = slapi_sdn_new_dn_byref(dn);
+
     /* Get superior value - unimplemented in 3.0/4.0/5.0 DS */
     err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &superior);
     if (err) { result = uid_op_error(32); break; }
@@ -902,27 +913,18 @@ preop_modrdn(Slapi_PBlock *pb)
       "MODRDN newrdn=%s\n", rdn);
 #endif
 
-    /*
-     * Parse the RDN into attributes by creating a "dummy" entry
-     * and setting the attributes from the RDN.
-     *
-     * The new entry must be freed.
-     */
-    e = slapi_entry_alloc();
-    if (!e) { result = uid_op_error(34); break; }
-
-    /* NOTE: strdup on the rdn, since it will be freed when
-     * the entry is freed */
+    /* See if the old RDN value is being deleted. */
+    err = slapi_pblock_get(pb, SLAPI_MODRDN_DELOLDRDN, &deloldrdn);
+    if (err) { result = uid_op_error(34); break; }
 
-    slapi_entry_set_dn(e, slapi_ch_strdup(rdn));
+    /* Get the entry that is being renamed so we can make a dummy copy
+     * of what it will look like after the rename. */
+    err = slapi_search_internal_get_entry(sdn, NULL, &e, plugin_identity);
+    if (err != LDAP_SUCCESS) { result = uid_op_error(35); break; }
 
-    err = slapi_entry_add_rdn_values(e);
-    if (err)
-    {
-      slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
-        "MODRDN bad rdn value=%s\n", rdn);
-      break; /* Bad DN */
-    }
+    /* Apply the rename operation to the dummy entry. */
+    err = slapi_entry_rename(e, rdn, deloldrdn, superior);
+    if (err != LDAP_SUCCESS) { result = uid_op_error(36); break; }
 
         /*
          * Find any unique attribute data in the new RDN
@@ -930,6 +932,12 @@ preop_modrdn(Slapi_PBlock *pb)
         err = slapi_entry_attr_find(e, attrName, &attr);
         if (err) break;  /* no UID attribute */
 
+        /*
+         * Check if it has the required object class
+         */
+        if (requiredObjectClass &&
+            !slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sv_requiredObjectClass)) { break; }
+
         /*
          * Passed all the requirements - this is an operation we
          * need to enforce uniqueness on. Now find all parent entries
@@ -938,7 +946,7 @@ preop_modrdn(Slapi_PBlock *pb)
         if (NULL != markerObjectClass)
         {
           /* Subtree defined by location of marker object class */
-                result = findSubtreeAndSearch(dn, attrName, attr, NULL,
+                result = findSubtreeAndSearch(slapi_entry_get_dn(e), attrName, attr, NULL,
                                               requiredObjectClass, dn,
                                               markerObjectClass);
         } else
@@ -949,6 +957,8 @@ preop_modrdn(Slapi_PBlock *pb)
         }
   END
   /* Clean-up */
+  slapi_sdn_free(&sdn);
+  slapi_value_free(&sv_requiredObjectClass);
   if (e) slapi_entry_free(e);
 
   if (result)

+ 86 - 0
ldap/servers/slapd/entry.c

@@ -3055,6 +3055,92 @@ slapi_entry_has_children(const Slapi_Entry *entry)
 	return(0);
 }
 
+/*
+ * Renames an entry to simulate a MODRDN operation
+ */
+int
+slapi_entry_rename(Slapi_Entry *e, const char *newrdn, int deleteoldrdn, const char *newsuperior)
+{
+    int err = LDAP_SUCCESS;
+    char *newdn = NULL;
+    char *olddn = NULL;
+    Slapi_RDN *oldrdn = NULL;
+    Slapi_Mods *smods = NULL;
+
+    LDAPDebug( LDAP_DEBUG_TRACE, "=> slapi_entry_rename\n", 0, 0, 0 );
+
+    /* Check if entry or newrdn are NULL. */
+    if (!e || !newrdn) {
+        err = LDAP_PARAM_ERROR;
+        goto done;
+    }
+
+    /* Get the old DN. */
+    olddn = slapi_entry_get_dn(e);
+
+    /* If deleteoldrdn, find old RDN values and remove them from the entry. */
+    if (deleteoldrdn) {
+        char *type = NULL;
+        char * val = NULL;
+        int num_rdns = 0;
+        int i = 0;
+
+        oldrdn = slapi_rdn_new_dn(olddn);
+
+        /* Create mods based on the number of rdn elements. */
+        num_rdns = slapi_rdn_get_num_components(oldrdn);
+        smods = slapi_mods_new();
+        slapi_mods_init(smods, num_rdns + 2);
+
+        /* Loop through old rdns and construct a mod to remove the value. */
+        for (i = 0; i < num_rdns; i++) {
+            if (slapi_rdn_get_next(oldrdn, i, &type, &val) != -1) {
+                slapi_mods_add(smods, LDAP_MOD_DELETE, type, strlen(val), val);
+            }
+        }
+
+        /* Apply the mods to the entry. */
+        if ((err = slapi_entry_apply_mods(e, slapi_mods_get_ldapmods_byref(smods))) != LDAP_SUCCESS) {
+            /* A problem was encountered applying the mods.  Bail. */
+            goto done;
+        }
+    }
+
+    /* We remove the parentid and entrydn since the backend will change these.
+     * We don't want to give the caller an inconsistent entry. */
+    slapi_entry_attr_delete(e, "parentid");
+    slapi_entry_attr_delete(e, "entrydn");
+
+    /* Build new DN.  If newsuperior is set, just use "newrdn,newsuperior".  If
+     * newsuperior is not set, need to add newrdn to old superior. */
+    if (newsuperior) {
+        newdn = slapi_ch_smprintf("%s,%s", newrdn, newsuperior);
+    } else {
+        char *oldsuperior = NULL;
+
+        oldsuperior = slapi_dn_parent(olddn);
+        newdn = slapi_ch_smprintf("%s,%s", newrdn, oldsuperior);
+
+        slapi_ch_free_string(&oldsuperior);
+    }
+
+    /* Set the new DN in the entry.  This hands off the memory used by newdn to the entry. */
+    slapi_entry_set_dn(e, newdn);
+
+    /* Set the RDN in the entry. */
+    slapi_entry_set_rdn(e, newdn);
+
+    /* Add RDN values to entry. */
+    err = slapi_entry_add_rdn_values(e);
+
+done:
+    slapi_rdn_free(&oldrdn);
+    slapi_mods_free(&smods);
+
+    LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_entry_rename\n", 0, 0, 0 );
+    return err;
+}
+
 /*
  * Apply a set of modifications to an entry 
  */

+ 18 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -1979,6 +1979,24 @@ void slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1, Slapi_Entry *e2, int d
  */
 int slapi_entry_apply_mods(Slapi_Entry *e, LDAPMod **mods);
 
+/**
+ * Renames a Slapi_Entry.
+ *
+ * This function will rename an existing \c Slapi_Entry, similar to what
+ * would happen with a \c MODRDN operation.  New RDN values will be added
+ * as attributes to the entry and old RDN values will be deleted if requested.
+ *
+ * \param e Entry that you want to rename.
+ * \param newrdn The new RDN value to be used for renaming the entry.  This must
+ *               not be \c NULL.
+ * \param deleteoldrdn Will delete the old RDN values from the entry if set to \c 1.
+ * \param newsuperior The new superior DN to use when renaming the entry.  Set this
+ *                    to \c NULL if you do not want to move the entry.
+ * \return \c LDAP_SUCCESS if the rename was successful, otherwise an LDAP error
+ *         is returned.
+ */
+int slapi_entry_rename(Slapi_Entry *e, const char *newrdn, int deleteoldrdn, const char *newsuperior);
+
 
 /*------------------------
  * Entry flags.