From 35fe04660e37c70d0703a19ab1190dd524953afe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=B4me=20Chilliet?= <come@opensides.be>
Date: Wed, 4 Apr 2018 16:42:04 +0200
Subject: [PATCH] :sparkles: feat(foreignkeys) Handle foreignKeys on subobjects
 DNs

Might slow down renaming a bit as all ACL assignment have to be opened,
 because the dn are base64 encoded in the LDAP field.

issue #5799
---
 include/class_pluglist.inc                    |  8 +++
 .../attributes/class_SetAttribute.inc         | 10 +++-
 include/simpleplugin/class_Attribute.inc      |  8 ++-
 .../simpleplugin/class_dialogAttributes.inc   | 14 ++++-
 include/simpleplugin/class_simplePlugin.inc   | 51 ++++++++++++++++++-
 plugins/admin/acl/class_aclAssignment.inc     | 26 +++++++---
 6 files changed, 106 insertions(+), 11 deletions(-)

diff --git a/include/class_pluglist.inc b/include/class_pluglist.inc
index dc019fba5..8504665ca 100644
--- a/include/class_pluglist.inc
+++ b/include/class_pluglist.inc
@@ -42,6 +42,11 @@ class pluglist {
    */
   var $info             = array();
 
+  /*!
+   * \brief Foreign references on DNs
+   */
+  var $dnForeignRefs = array();
+
   /*!
    * \brief Using the plugin index as a key, the class of the plugin.
    */
@@ -123,6 +128,9 @@ class pluglist {
                 $foreign_refs[$class][$field] = array();
               }
               $foreign_refs[$class][$field][] = array($cname, $ofield, $filter);
+              if ($field == 'dn') {
+                $this->dnForeignRefs[] = array($cname, $ofield, $filter, (isset($pfk[3]) ? $pfk[3] : "$ofield=*%oldvalue%"));
+              }
             }
             unset($pfk);
           }
diff --git a/include/simpleplugin/attributes/class_SetAttribute.inc b/include/simpleplugin/attributes/class_SetAttribute.inc
index 7871fcf64..f27f3c5df 100644
--- a/include/simpleplugin/attributes/class_SetAttribute.inc
+++ b/include/simpleplugin/attributes/class_SetAttribute.inc
@@ -317,7 +317,15 @@ class SetAttribute extends Attribute
   function foreignKeyUpdate($oldvalue, $newvalue, $source)
   {
     foreach ($this->value as $key => &$value) {
-      if ($value == $oldvalue) {
+      if (($source['FIELD'] == 'dn') && ($source['MODE'] == 'move')) {
+        if ($newvalue === NULL) {
+          if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $value)) {
+            unset($this->value[$key]);
+          }
+        } else {
+          $value = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $value);
+        }
+      } elseif ($value == $oldvalue) {
         if ($newvalue === NULL) {
           unset($this->value[$key]);
         } elseif ($source['MODE'] == 'copy') {
diff --git a/include/simpleplugin/class_Attribute.inc b/include/simpleplugin/class_Attribute.inc
index 65b247891..730b2a82a 100644
--- a/include/simpleplugin/class_Attribute.inc
+++ b/include/simpleplugin/class_Attribute.inc
@@ -710,7 +710,13 @@ class Attribute
   function foreignKeyUpdate($oldvalue, $newvalue, $source)
   {
     if ($source['MODE'] == 'move') {
-      if ($this->getValue() == $oldvalue) {
+      if ($source['FIELD'] == 'dn') {
+        $value = $this->getValue();
+        $value = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $value, -1, $count);
+        if ($count > 0) {
+          $this->setValue($value);
+        }
+      } elseif ($this->getValue() == $oldvalue) {
         $this->setValue($newvalue);
       }
     }
diff --git a/include/simpleplugin/class_dialogAttributes.inc b/include/simpleplugin/class_dialogAttributes.inc
index d09bf46f7..6e7b47a53 100644
--- a/include/simpleplugin/class_dialogAttributes.inc
+++ b/include/simpleplugin/class_dialogAttributes.inc
@@ -446,7 +446,19 @@ class GenericDialogAttribute extends DialogAttribute
   function foreignKeyUpdate($oldvalue, $newvalue, $source)
   {
     foreach ($this->value as $key => &$value) {
-      if ($value == $oldvalue) {
+      if (($source['FIELD'] == 'dn') && ($source['MODE'] == 'move')) {
+        if ($newvalue === NULL) {
+          if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $value)) {
+            $this->removeValue($key);
+          }
+        } else {
+          $value = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $value, -1, $count);
+          if ($count > 0) {
+            /* Update display */
+            $this->fillDisplayValue($key);
+          }
+        }
+      } elseif ($value == $oldvalue) {
         if ($newvalue === NULL) {
           $this->removeValue($key);
         } elseif ($source['MODE'] == 'copy') {
diff --git a/include/simpleplugin/class_simplePlugin.inc b/include/simpleplugin/class_simplePlugin.inc
index 097f6db82..450e220af 100644
--- a/include/simpleplugin/class_simplePlugin.inc
+++ b/include/simpleplugin/class_simplePlugin.inc
@@ -1536,15 +1536,64 @@ class simplePlugin
 
   function browseForeignKeys($mode, $param1 = NULL, $param2 = NULL)
   {
+    global $plist;
     if (preg_match('/^handle_/', $mode)) {
       $olddn    = $param1;
       $newdn    = $param2;
       $classes  = array(get_class($this));
+      $subobjects = ($olddn != $newdn); //FIXME
     } elseif ($mode == 'references') {
       $classes = array_keys($this->parent->by_object);
     }
-    // We group by objetType concerned
+    // We group by objectType concerned
     $foreignRefs = array();
+    if ($subobjects) {
+      $field = 'dn';
+      /* Special treatment for foreign keys on DN when moving an object
+       * All references on DN are treated on subobjects */
+      foreach ($plist->dnForeignRefs as $ref) {
+        $class      = $ref[0];
+        $ofield     = $ref[1];
+        $filter     = $ref[2];
+        $filtersub  = $ref[3];
+        if ($class == 'aclAssignment') {
+          /* Special case: aclAssignment foreignKey is ignored on department types as it’s handled by the aclAssignment objectType */
+          $objectTypes = array('ACLASSIGNMENT');
+        } elseif (is_subclass_of($class, 'simpleService')) {
+          $objectTypes = array('SERVER');
+        } else {
+          $objectTypes = array();
+          $cinfos = pluglist::pluginInfos($class);
+          foreach ($cinfos['plObjectType'] as $key => $objectType) {
+            if (!is_numeric($key)) {
+              $objectType = $key;
+            }
+            if (preg_match('/^ogroup-/i', $objectType)) {
+              $objectType = 'OGROUP';
+            }
+            $objectTypes[] = $objectType;
+          }
+          $objectTypes = array_unique($objectTypes);
+        }
+        foreach ($objectTypes as $objectType) {
+          $oldvalue = $olddn;
+          $newvalue = $newdn;
+
+          $foreignRefs[$objectType]['refs'][$class][$ofield][$field] =
+            array(
+              'tab'       => $classes[0],
+              'field'     => $field,
+              'oldvalue'  => $oldvalue,
+              'newvalue'  => $newvalue,
+            );
+          $filter = templateHandling::parseString($filtersub, array('oldvalue' => $oldvalue, 'newvalue' => $newvalue), 'ldap_escape_f');
+          if (!preg_match('/^\(.*\)$/', $filter)) {
+            $filter = '('.$filter.')';
+          }
+          $foreignRefs[$objectType]['filters'][$filter] = $filter;
+        }
+      }
+    }
     foreach ($classes as $tabclass) {
       $infos = pluglist::pluginInfos($tabclass);
       foreach ($infos['plForeignRefs'] as $field => $refs) {
diff --git a/plugins/admin/acl/class_aclAssignment.inc b/plugins/admin/acl/class_aclAssignment.inc
index 19c59043b..fd4667fca 100644
--- a/plugins/admin/acl/class_aclAssignment.inc
+++ b/plugins/admin/acl/class_aclAssignment.inc
@@ -211,11 +211,23 @@ class ACLsAssignmentAttribute extends DialogOrderedArrayAttribute
   function foreignKeyUpdate($oldvalue, $newvalue, $source)
   {
     foreach ($this->value as $key => &$value) {
-      if (($source['CLASS'] == 'aclRole') && ($value['role'] == $oldvalue) && ($source['MODE'] != 'copy')) {
+      if (($source['FIELD'] == 'dn') && ($source['MODE'] == 'move')) {
         if ($newvalue === NULL) {
-          unset($this->value[$key]);
+          if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $value['role'])) {
+            unset($this->value[$key]);
+          }
+          foreach ($value['members'] as $member_key => $member) {
+            if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $member)) {
+              unset($value['members'][$member_key]);
+            }
+          }
+          unset($member);
         } else {
-          $value['role'] = $newvalue;
+          $value['role'] = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $value['role']);
+          foreach ($value['members'] as &$member) {
+            $member = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $member);
+          }
+          unset($member);
         }
       } elseif (in_array($source['CLASS'], array('user','posixGroup','roleGeneric')) && (($member_key = array_search($oldvalue, $value['members'])) !== FALSE)) {
         if ($newvalue === NULL) {
@@ -275,10 +287,10 @@ class aclAssignment extends simplePlugin
       'plObjectType'  => $oc,
       'plForeignKeys'  => array(
         'gosaAclEntry' => array(
-          array('aclRole',      'dn', 'gosaAclEntry=*:*:%b|oldvalue%:*'),
-          array('user',         'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*'),
-          array('posixGroup',   'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*'),
-          array('roleGeneric',  'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*'),
+          array('aclRole',      'dn', 'gosaAclEntry=*:*:%b|oldvalue%:*',    'gosaAclEntry=*'),
+          array('user',         'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*',  'gosaAclEntry=*'),
+          array('posixGroup',   'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*',  'gosaAclEntry=*'),
+          array('roleGeneric',  'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*',  'gosaAclEntry=*'),
         )
       ),
 
-- 
GitLab