From 2295cf1c1b834f84ed3da936fce22f07d39bba2d Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 10:54:58 +0100
Subject: [PATCH 01/36] redesign

---
 plugins/tasks/Notifications.php | 67 ++++++++++++++++-----------------
 1 file changed, 32 insertions(+), 35 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index f78ef5e..bd8222d 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -4,6 +4,7 @@ class Notifications implements EndpointInterface
 {
 
   private TaskGateway $gateway;
+  private string $errorMessage = 'No matching audited attributes with monitored attributes, safely removed!';
 
   public function __construct (TaskGateway $gateway)
   {
@@ -61,7 +62,6 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
-
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -69,26 +69,7 @@ class Notifications implements EndpointInterface
         // Generate the mail form with all mail controller requirements
         $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask);
 
-        // Simply retrieve the list of audited attributes
-        $auditAttributes = $this->decodeAuditAttributes($task);
-
-        // Recovering monitored attributes list from the defined notification task.
-        $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-        // Reformat supann
-        $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
-
-        // Simply remove keys with 'count' reported by ldap.
-        $this->gateway->unsetCountKeys($monitoredAttrs);
-        $this->gateway->unsetCountKeys($monitoredSupannResource);
-
-        // Find matching attributes between audited and monitored attributes
-        $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
-
-        // Verify Supann resource state if applicable
-        if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-        }
+        $matchingAttrs = $this->getMatchingAttrs($notificationsMainTask, $task);
 
         if (!empty($matchingAttrs)) {
           // Fill an array with UID of audited user and related matching attributes
@@ -103,7 +84,7 @@ class Notifications implements EndpointInterface
 
         } else { // Simply remove the subTask has no notifications are required
           $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']);
-          $result[$task['dn']]['Status']  = 'No matching audited attributes with monitored attributes, safely removed!';
+          $result[$task['dn']]['Status']  = $this->errorMessage;
         }
       }
     }
@@ -115,6 +96,32 @@ class Notifications implements EndpointInterface
     return $result;
   }
 
+  private function getMatchingAttrs (array $notificationsMainTask, array $task): array
+  {
+      // Simply retrieve the list of audited attributes
+      $auditAttributes = $this->decodeAuditAttributes($task);
+
+      // Recovering monitored attributes list from the defined notification task.
+      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+      // Reformat supann
+      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+
+      // Simply remove keys with 'count' reported by ldap.
+      $this->gateway->unsetCountKeys($monitoredAttrs);
+      $this->gateway->unsetCountKeys($monitoredSupannResource);
+
+      // Find matching attributes between audited and monitored attributes
+      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+
+      // Verify Supann resource state if applicable
+      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+          // Adds it to the mating attrs for further notification process.
+          $matchingAttrs[] = 'supannRessourceEtat';
+      }
+
+      return $matchingAttrs;
+  }
+
   /**
    * Determine if Supann resource verification is needed.
    *
@@ -197,25 +204,15 @@ class Notifications implements EndpointInterface
    */
   private function verifySupannState (array $supannResource, array $auditedAttrs): bool
   {
-    $result = FALSE;
-
     //Construct Supann Resource State as string
+    $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
     if (!empty($supannResource['subState'][0])) {
-      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0] . ':' . $supannResource['subState'][0];
-    } else {
-      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
+      $monitoredSupannState = $monitoredSupannState . ':' . $supannResource['subState'][0];
     }
 
     // Get all the values only of a multidimensional array.
     $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
-
-    if (in_array($monitoredSupannState, $auditedValues)) {
-      $result = TRUE;
-    } else {
-      $result = FALSE;
-    }
-
-    return $result;
+    return in_array($monitoredSupannState, $auditedValues);
   }
 
   /**
-- 
GitLab


From 26a576d788e6c1eb0ff1534d255b13f52c010d70 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 10:56:01 +0100
Subject: [PATCH 02/36] fix stan

---
 plugins/tasks/Notifications.php | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index bd8222d..85d28d2 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -98,26 +98,26 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-      // Simply retrieve the list of audited attributes
-      $auditAttributes = $this->decodeAuditAttributes($task);
+    // Simply retrieve the list of audited attributes
+    $auditAttributes = $this->decodeAuditAttributes($task);
 
-      // Recovering monitored attributes list from the defined notification task.
-      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-      // Reformat supann
-      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+    // Recovering monitored attributes list from the defined notification task.
+    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+    // Reformat supann
+    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-      // Simply remove keys with 'count' reported by ldap.
-      $this->gateway->unsetCountKeys($monitoredAttrs);
-      $this->gateway->unsetCountKeys($monitoredSupannResource);
+    // Simply remove keys with 'count' reported by ldap.
+    $this->gateway->unsetCountKeys($monitoredAttrs);
+    $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-      // Find matching attributes between audited and monitored attributes
-      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    // Find matching attributes between audited and monitored attributes
+    $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
 
-      // Verify Supann resource state if applicable
-      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-      }
+    // Verify Supann resource state if applicable
+    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+      // Adds it to the mating attrs for further notification process.
+      $matchingAttrs[] = 'supannRessourceEtat';
+    }
 
       return $matchingAttrs;
   }
-- 
GitLab


From db7c0f4f928211cba71ff8d78e152cc0f0a5148c Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:08:50 +0100
Subject: [PATCH 03/36] Refactor libraries

---
 library/TokenUtils.php          | 197 +++++++++++++++++++++++++++
 library/Utils.php               |  83 ++++++++++++
 plugins/tasks/Audit.php         |  25 +---
 plugins/tasks/Notifications.php |  49 +------
 plugins/tasks/Reminder.php      | 233 +-------------------------------
 5 files changed, 290 insertions(+), 297 deletions(-)
 create mode 100644 library/TokenUtils.php
 create mode 100644 library/Utils.php

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
new file mode 100644
index 0000000..d19392c
--- /dev/null
+++ b/library/TokenUtils.php
@@ -0,0 +1,197 @@
+<?php
+
+class TokenUtils
+{
+    private function __construct() {
+    }
+
+    /**
+     * @param string $userDN
+     * @param int $timeStamp
+     * @return string
+     * @throws Exception
+     */
+    public static function generateToken (string $userDN, int $timeStamp): string
+    {
+        $token = NULL;
+        // Salt has been generated with APG.
+        $salt    = '8onOlEsItKond';
+        $payload = json_encode($userDN . $salt);
+        // This allows the token to be different every time.
+        $time = time();
+
+        // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
+        $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
+
+        // We need to have a token allowed to be used within an URL.
+        $token = Utils::base64urlEncode($token_hmac);
+
+        // Save token within LDAP
+        self::saveTokenInLdap($userDN, $token, $timeStamp);
+
+        return $token;
+    }
+
+    /**
+     * @param string $userDN
+     * @param string $token
+     * NOTE : UID is the full DN of the user. (uid=...).
+     * @param int $days
+     * @return bool
+     * @throws Exception
+     */
+    public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
+    {
+        $result = FALSE;
+
+        $currentTimestamp = time();
+        // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
+        $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
+
+        preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
+        $uid = $matches[1];
+        $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+
+        $ldap_entry["objectClass"]      = ['top', 'fdTokenEntry'];
+        $ldap_entry["fdTokenUserDN"]    = $userDN;
+        $ldap_entry["fdTokenType"]      = 'reminder';
+        $ldap_entry["fdToken"]          = $token;
+        $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
+        $ldap_entry["cn"]               = $uid;
+
+        // set the dn for the token, only take what's between "uid=" and ",ou="
+
+
+        // Verify if token ou branch exists
+        if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
+            // Create the branch
+            self::createBranchToken();
+        }
+
+        // The user token DN creation
+        $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+        // Verify if a token already exists for specified user and remove it to create new one correctly.
+        if (self::tokenBranchExist($userTokenDN)) {
+            // Remove the user token
+            self::removeUserToken($userTokenDN);
+        }
+
+        // Add token to LDAP for specific UID
+        try {
+            $result = ldap_add($gateway->ds, $dn, $ldap_entry); // bool returned
+        } catch (Exception $e) {
+            echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
+            exit;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param int $subTaskCall
+     * @param int $firstCall
+     * @param int $secondCall
+     * @return int
+     * Note : Simply return the difference between first and second call. (First call can be null).
+     */
+    public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
+    {
+        // if firstCall is empty, secondCall is the timestamp expiry for the token.
+        $result = $secondCall;
+
+        if (!empty($firstCall)) {
+            // Verification if the subTask is the second reminder or the first reminder.
+            if ($subTaskCall === $firstCall) {
+                $result = $firstCall - $secondCall;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param $userTokenDN
+     * @return void
+     * Note : Simply remove the token for specific user DN
+     */
+    public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
+    {
+        // Add token to LDAP for specific UID
+        try {
+            $result = ldap_delete($gateway->ds, $userTokenDN); // bool returned
+        } catch (Exception $e) {
+            echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
+            exit;
+        }
+    }
+
+    /**
+     * Create ou=pluginManager LDAP branch
+     * @throws Exception
+     */
+    public static function createBranchToken (TaskGateway $gateway): void
+    {
+        try {
+            ldap_add(
+                $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
+                [
+                    'ou'          => 'tokens',
+                    'objectClass' => 'organizationalUnit',
+                ]
+            );
+        } catch (Exception $e) {
+
+            echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
+            exit;
+        }
+    }
+
+    /**
+     * @param string $token
+     * @param array $mailTemplateForm
+     * @param string $taskDN
+     * @return array
+     */
+    public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
+    {
+        //Only take the cn of the main task name :
+        preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
+        $taskName = $matches[1];
+
+        // Remove the API URI
+        $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
+        $url        = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
+
+        $mailTemplateForm['body'] .= $url;
+
+        return $mailTemplateForm;
+    }
+
+    /**
+     * @param string $dn
+     * @return bool
+     * Note : Simply inspect if the branch for token is existing.
+     */
+    public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
+    {
+        $result = FALSE;
+
+        try {
+            $search = ldap_search($gateway->ds, $dn, "(objectClass=*)");
+            // Check if the search was successful
+            if ($search) {
+                // Get the number of entries found
+                $entries = ldap_get_entries($gateway->ds, $search);
+
+                // If entries are found, set result to true
+                if ($entries["count"] > 0) {
+                    $result = TRUE;
+                }
+            }
+        } catch (Exception $e) {
+            $result = FALSE;
+        }
+
+        return $result;
+    }
+}
\ No newline at end of file
diff --git a/library/Utils.php b/library/Utils.php
new file mode 100644
index 0000000..c241a0a
--- /dev/null
+++ b/library/Utils.php
@@ -0,0 +1,83 @@
+<?php
+
+class Utils
+{
+    private function __construct() {}
+
+    /**
+     * @param array $array
+     * @return array
+     * Note : Recursively filters out empty values and arrays at any depth.
+     */
+    public static function recursiveArrayFilter (array $array): array
+    {
+        // First filter the array for non-empty elements
+        $filtered = array_filter($array, function ($item) {
+            if (is_array($item)) {
+                // Recursively filter the sub-array
+                $item = $this->recursiveArrayFilter($item);
+                // Only retain non-empty arrays
+                return !empty($item);
+            } else {
+                // Retain non-empty scalar values
+                return !empty($item);
+            }
+        });
+
+        return $filtered;
+    }
+
+    /**
+     * Find matching keys between 2 lists.
+     *
+     * @param array|null $elements
+     * @param array $keys
+     * @return array
+     */
+    public static function findMatchingKeys (?array $elements, array $keys): array
+    {
+        $matching = [];
+
+        if (!empty($elements)) {
+            foreach ($elements as $element) {
+                foreach ($keys as $key) {
+                    if (!empty($element) && array_key_exists($key, $element)) {
+                        $matching[] = $key;
+                    }
+                }
+            }
+        }
+
+        return $matching;
+    }
+
+    /**
+     * @param $array
+     * @return array
+     * Note : simply return all values of a multi-dimensional array.
+     */
+    public static function getArrayValuesRecursive ($array)
+    {
+        $values = [];
+        foreach ($array as $value) {
+            if (is_array($value)) {
+                // If value is an array, merge its values recursively
+                $values = array_merge($values, self::getArrayValuesRecursive($value));
+            } else {
+                // If value is not an array, add it to the result
+                $values[] = $value;
+            }
+        }
+        return $values;
+    }
+
+    /**
+     * @param string $text
+     * @return string
+     * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
+     */
+    public static function base64urlEncode (string $text): string
+    {
+        return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+    }
+}
\ No newline at end of file
diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php
index 6e51ae7..c4bbe40 100644
--- a/plugins/tasks/Audit.php
+++ b/plugins/tasks/Audit.php
@@ -47,7 +47,7 @@ class Audit implements EndpointInterface
     $result = $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit'));
 
     // Recursive function to filter out empty arrays at any depth
-    $nonEmptyResults = $this->recursiveArrayFilter($result);
+    $nonEmptyResults = Utils::recursiveArrayFilter($result);
 
     if (!empty($nonEmptyResults)) {
       return $nonEmptyResults;
@@ -119,27 +119,4 @@ class Audit implements EndpointInterface
 
     return $audit;
   }
-
-  /**
-   * @param array $array
-   * @return array
-   * Note : Recursively filters out empty values and arrays at any depth.
-   */
-  private function recursiveArrayFilter (array $array): array
-  {
-    // First filter the array for non-empty elements
-    $filtered = array_filter($array, function ($item) {
-      if (is_array($item)) {
-        // Recursively filter the sub-array
-        $item = $this->recursiveArrayFilter($item);
-        // Only retain non-empty arrays
-        return !empty($item);
-      } else {
-        // Retain non-empty scalar values
-        return !empty($item);
-      }
-    });
-
-    return $filtered;
-  }
 }
\ No newline at end of file
diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 85d28d2..3b1d774 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -62,6 +62,7 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
+
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -111,7 +112,7 @@ class Notifications implements EndpointInterface
     $this->gateway->unsetCountKeys($monitoredSupannResource);
 
     // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
 
     // Verify Supann resource state if applicable
     if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
@@ -172,30 +173,6 @@ class Notifications implements EndpointInterface
     return $auditAttributes;
   }
 
-  /**
-   * Find matching attributes between audit and monitored attributes.
-   *
-   * @param array|null $auditAttributes
-   * @param array $monitoredAttrs
-   * @return array
-   */
-  private function findMatchingAttributes (?array $auditAttributes, array $monitoredAttrs): array
-  {
-    $matchingAttrs = [];
-
-    if (!empty($auditAttributes)) {
-      foreach ($auditAttributes as $attributeName) {
-        foreach ($monitoredAttrs as $monitoredAttr) {
-          if (!empty($attributeName) && array_key_exists($monitoredAttr, $attributeName)) {
-            $matchingAttrs[] = $monitoredAttr;
-          }
-        }
-      }
-    }
-
-    return $matchingAttrs;
-  }
-
   /**
    * @param array $supannResource
    * @param array $auditedAttrs
@@ -211,30 +188,10 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-  /**
-   * @param $array
-   * @return array
-   * Note : simply return all values of a multi-dimensional array.
-   */
-  public function getArrayValuesRecursive ($array)
-  {
-    $values = [];
-    foreach ($array as $value) {
-      if (is_array($value)) {
-        // If value is an array, merge its values recursively
-        $values = array_merge($values, $this->getArrayValuesRecursive($value));
-      } else {
-        // If value is not an array, add it to the result
-        $values[] = $value;
-      }
-    }
-    return $values;
-  }
-
   /**
    * @param string $mainTaskDn
    * @return array
diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php
index ca46b55..8cda3fc 100644
--- a/plugins/tasks/Reminder.php
+++ b/plugins/tasks/Reminder.php
@@ -109,13 +109,13 @@ class Reminder implements EndpointInterface
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0];
 
             // Create timeStamp expiration for token
-            $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0],
+            $tokenExpire = TokenUtils::getTokenExpiration($task['fdtasksgranularhelper'][0],
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
+            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
             // Edit the mailForm with the url link containing the token
-            $tokenMailTemplateForm = $this->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
+            $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm;
 
@@ -136,13 +136,13 @@ class Reminder implements EndpointInterface
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0];
 
             // Create timeStamp expiration for token
-            $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0],
+            $tokenExpire = TokenUtils::getTokenExpiration($task['fdtasksgranularhelper'][0],
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
+            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
             // Edit the mailForm with the url link containing the token
-            $tokenMailTemplateForm = $this->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
+            $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm;
 
@@ -217,207 +217,6 @@ class Reminder implements EndpointInterface
     return $result;
   }
 
-  /**
-   * @param string $token
-   * @param array $mailTemplateForm
-   * @param string $taskDN
-   * @return array
-   */
-  private function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
-  {
-    //Only take the cn of the main task name :
-    preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
-    $taskName = $matches[1];
-
-    // Remove the API URI
-    $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
-    $url        = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
-
-    $mailTemplateForm['body'] .= $url;
-
-    return $mailTemplateForm;
-  }
-
-  /**
-   * @param int $subTaskCall
-   * @param int $firstCall
-   * @param int $secondCall
-   * @return int
-   * Note : Simply return the difference between first and second call. (First call can be null).
-   */
-  private function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
-  {
-    // if firstCall is empty, secondCall is the timestamp expiry for the token.
-    $result = $secondCall;
-
-    if (!empty($firstCall)) {
-      // Verification if the subTask is the second reminder or the first reminder.
-      if ($subTaskCall === $firstCall) {
-        $result = $firstCall - $secondCall;
-      }
-    }
-
-    return $result;
-  }
-
-  /**
-   * @param string $userDN
-   * @param int $timeStamp
-   * @return string
-   * @throws Exception
-   */
-  private function generateToken (string $userDN, int $timeStamp): string
-  {
-    $token = NULL;
-    // Salt has been generated with APG.
-    $salt    = '8onOlEsItKond';
-    $payload = json_encode($userDN . $salt);
-    // This allows the token to be different every time.
-    $time = time();
-
-    // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
-    $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
-
-    // We need to have a token allowed to be used within an URL.
-    $token = $this->base64urlEncode($token_hmac);
-
-    // Save token within LDAP
-    $this->saveTokenInLdap($userDN, $token, $timeStamp);
-
-    return $token;
-  }
-
-  /**
-   * @param string $userDN
-   * @param string $token
-   * NOTE : UID is the full DN of the user. (uid=...).
-   * @param int $days
-   * @return bool
-   * @throws Exception
-   */
-  private function saveTokenInLdap (string $userDN, string $token, int $days): bool
-  {
-    $result = FALSE;
-
-    $currentTimestamp = time();
-    // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
-    $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
-
-    preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
-    $uid = $matches[1];
-    $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-
-    $ldap_entry["objectClass"]      = ['top', 'fdTokenEntry'];
-    $ldap_entry["fdTokenUserDN"]    = $userDN;
-    $ldap_entry["fdTokenType"]      = 'reminder';
-    $ldap_entry["fdToken"]          = $token;
-    $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
-    $ldap_entry["cn"]               = $uid;
-
-    // set the dn for the token, only take what's between "uid=" and ",ou="
-
-
-    // Verify if token ou branch exists
-    if (!$this->tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
-      // Create the branch
-      $this->createBranchToken();
-    }
-
-    // The user token DN creation
-    $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-    // Verify if a token already exists for specified user and remove it to create new one correctly.
-    if ($this->tokenBranchExist($userTokenDN)) {
-      // Remove the user token
-      $this->removeUserToken($userTokenDN);
-    }
-
-    // Add token to LDAP for specific UID
-    try {
-      $result = ldap_add($this->gateway->ds, $dn, $ldap_entry); // bool returned
-    } catch (Exception $e) {
-      echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
-      exit;
-    }
-
-    return $result;
-  }
-
-  /**
-   * @param $userTokenDN
-   * @return void
-   * Note : Simply remove the token for specific user DN
-   */
-  private function removeUserToken ($userTokenDN): void
-  {
-    // Add token to LDAP for specific UID
-    try {
-      $result = ldap_delete($this->gateway->ds, $userTokenDN); // bool returned
-    } catch (Exception $e) {
-      echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
-      exit;
-    }
-  }
-
-  /**
-   * Create ou=pluginManager LDAP branch
-   * @throws Exception
-   */
-  protected function createBranchToken (): void
-  {
-    try {
-      ldap_add(
-        $this->gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
-        [
-          'ou'          => 'tokens',
-          'objectClass' => 'organizationalUnit',
-        ]
-      );
-    } catch (Exception $e) {
-
-      echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
-      exit;
-    }
-  }
-
-
-  /**
-   * @param string $dn
-   * @return bool
-   * Note : Simply inspect if the branch for token is existing.
-   */
-  private function tokenBranchExist (string $dn): bool
-  {
-    $result = FALSE;
-
-    try {
-      $search = ldap_search($this->gateway->ds, $dn, "(objectClass=*)");
-      // Check if the search was successful
-      if ($search) {
-        // Get the number of entries found
-        $entries = ldap_get_entries($this->gateway->ds, $search);
-
-        // If entries are found, set result to true
-        if ($entries["count"] > 0) {
-          $result = TRUE;
-        }
-      }
-    } catch (Exception $e) {
-      $result = FALSE;
-    }
-
-    return $result;
-  }
-
-  /**
-   * @param string $text
-   * @return string
-   * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
-   */
-  private function base64urlEncode (string $text): string
-  {
-    return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
-  }
-
   /**
    * @param string $dn
    * @return string
@@ -592,26 +391,6 @@ class Reminder implements EndpointInterface
     return DateTime::createFromFormat('Ymd', $dateString);
   }
 
-  /**
-   * @param $array
-   * @return array
-   * Note : simply return all values of a multi-dimensional array.
-   */
-  public function getArrayValuesRecursive ($array)
-  {
-    $values = [];
-    foreach ($array as $value) {
-      if (is_array($value)) {
-        // If value is an array, merge its values recursively
-        $values = array_merge($values, $this->getArrayValuesRecursive($value));
-      } else {
-        // If value is not an array, add it to the result
-        $values[] = $value;
-      }
-    }
-    return $values;
-  }
-
   /**
    * @param string $mainTaskDn
    * @return array
-- 
GitLab


From 1e21ea1f58ad7e429aba4ffaf7538ec18f128194 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:10:22 +0100
Subject: [PATCH 04/36] fix indent

---
 library/Utils.php | 138 +++++++++++++++++++++++-----------------------
 1 file changed, 69 insertions(+), 69 deletions(-)

diff --git a/library/Utils.php b/library/Utils.php
index c241a0a..1d24e06 100644
--- a/library/Utils.php
+++ b/library/Utils.php
@@ -2,82 +2,82 @@
 
 class Utils
 {
-    private function __construct() {}
+  private function __construct () {}
 
-    /**
-     * @param array $array
-     * @return array
-     * Note : Recursively filters out empty values and arrays at any depth.
-     */
-    public static function recursiveArrayFilter (array $array): array
-    {
-        // First filter the array for non-empty elements
-        $filtered = array_filter($array, function ($item) {
-            if (is_array($item)) {
-                // Recursively filter the sub-array
-                $item = $this->recursiveArrayFilter($item);
-                // Only retain non-empty arrays
-                return !empty($item);
-            } else {
-                // Retain non-empty scalar values
-                return !empty($item);
-            }
-        });
+  /**
+   * @param array $array
+   * @return array
+   * Note : Recursively filters out empty values and arrays at any depth.
+   */
+  public static function recursiveArrayFilter (array $array): array
+  {
+    // First filter the array for non-empty elements
+    $filtered = array_filter($array, function ($item) {
+      if (is_array($item)) {
+        // Recursively filter the sub-array
+        $item = $this->recursiveArrayFilter($item);
+        // Only retain non-empty arrays
+        return !empty($item);
+      } else {
+        // Retain non-empty scalar values
+        return !empty($item);
+      }
+    });
 
-        return $filtered;
-    }
+    return $filtered;
+  }
 
-    /**
-     * Find matching keys between 2 lists.
-     *
-     * @param array|null $elements
-     * @param array $keys
-     * @return array
-     */
-    public static function findMatchingKeys (?array $elements, array $keys): array
-    {
-        $matching = [];
+  /**
+   * Find matching keys between 2 lists.
+   *
+   * @param array|null $elements
+   * @param array $keys
+   * @return array
+   */
+  public static function findMatchingKeys (?array $elements, array $keys): array
+  {
+    $matching = [];
 
-        if (!empty($elements)) {
-            foreach ($elements as $element) {
-                foreach ($keys as $key) {
-                    if (!empty($element) && array_key_exists($key, $element)) {
-                        $matching[] = $key;
-                    }
-                }
-            }
+    if (!empty($elements)) {
+      foreach ($elements as $element) {
+        foreach ($keys as $key) {
+          if (!empty($element) && array_key_exists($key, $element)) {
+            $matching[] = $key;
+          }
         }
-
-        return $matching;
+      }
     }
 
-    /**
-     * @param $array
-     * @return array
-     * Note : simply return all values of a multi-dimensional array.
-     */
-    public static function getArrayValuesRecursive ($array)
-    {
-        $values = [];
-        foreach ($array as $value) {
-            if (is_array($value)) {
-                // If value is an array, merge its values recursively
-                $values = array_merge($values, self::getArrayValuesRecursive($value));
-            } else {
-                // If value is not an array, add it to the result
-                $values[] = $value;
-            }
-        }
-        return $values;
-    }
+    return $matching;
+  }
 
-    /**
-     * @param string $text
-     * @return string
-     * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
-     */
-    public static function base64urlEncode (string $text): string
-    {
-        return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+  /**
+   * @param $array
+   * @return array
+   * Note : simply return all values of a multi-dimensional array.
+   */
+  public static function getArrayValuesRecursive ($array)
+  {
+    $values = [];
+    foreach ($array as $value) {
+      if (is_array($value)) {
+        // If value is an array, merge its values recursively
+        $values = array_merge($values, self::getArrayValuesRecursive($value));
+      } else {
+        // If value is not an array, add it to the result
+        $values[] = $value;
+      }
     }
+    return $values;
+  }
+
+  /**
+   * @param string $text
+   * @return string
+   * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
+   */
+  public static function base64urlEncode (string $text): string
+  {
+    return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+  }
 }
\ No newline at end of file
-- 
GitLab


From 3ba0756f638c528c25af08fa028181307ffb5242 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:11:12 +0100
Subject: [PATCH 05/36] fix braket

---
 library/Utils.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/library/Utils.php b/library/Utils.php
index 1d24e06..d1afd6d 100644
--- a/library/Utils.php
+++ b/library/Utils.php
@@ -2,7 +2,9 @@
 
 class Utils
 {
-  private function __construct () {}
+  private function __construct ()
+  {
+  }
 
   /**
    * @param array $array
-- 
GitLab


From 07fbbbd2a94f6961aca340b7a39b561dd005620f Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:12:12 +0100
Subject: [PATCH 06/36] fix indent

---
 library/TokenUtils.php | 361 +++++++++++++++++++++--------------------
 1 file changed, 181 insertions(+), 180 deletions(-)

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
index d19392c..dbf72bb 100644
--- a/library/TokenUtils.php
+++ b/library/TokenUtils.php
@@ -2,196 +2,197 @@
 
 class TokenUtils
 {
-    private function __construct() {
+  private function __construct ()
+  {
+  }
+
+  /**
+   * @param string $userDN
+   * @param int $timeStamp
+   * @return string
+   * @throws Exception
+   */
+  public static function generateToken (string $userDN, int $timeStamp): string
+  {
+    $token = NULL;
+    // Salt has been generated with APG.
+    $salt  = '8onOlEsItKond';
+    $payload = json_encode($userDN . $salt);
+    // This allows the token to be different every time.
+    $time = time();
+
+    // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
+    $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
+
+    // We need to have a token allowed to be used within an URL.
+    $token = Utils::base64urlEncode($token_hmac);
+
+    // Save token within LDAP
+    self::saveTokenInLdap($userDN, $token, $timeStamp);
+
+    return $token;
+  }
+
+  /**
+   * @param string $userDN
+   * @param string $token
+   * NOTE : UID is the full DN of the user. (uid=...).
+   * @param int $days
+   * @return bool
+   * @throws Exception
+   */
+  public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
+  {
+    $result = FALSE;
+
+    $currentTimestamp = time();
+    // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
+    $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
+
+    preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
+    $uid = $matches[1];
+    $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+
+    $ldap_entry["objectClass"]    = ['top', 'fdTokenEntry'];
+    $ldap_entry["fdTokenUserDN"]  = $userDN;
+    $ldap_entry["fdTokenType"]    = 'reminder';
+    $ldap_entry["fdToken"]      = $token;
+    $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
+    $ldap_entry["cn"]         = $uid;
+
+    // set the dn for the token, only take what's between "uid=" and ",ou="
+
+
+    // Verify if token ou branch exists
+    if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
+      // Create the branch
+      self::createBranchToken();
     }
 
-    /**
-     * @param string $userDN
-     * @param int $timeStamp
-     * @return string
-     * @throws Exception
-     */
-    public static function generateToken (string $userDN, int $timeStamp): string
-    {
-        $token = NULL;
-        // Salt has been generated with APG.
-        $salt    = '8onOlEsItKond';
-        $payload = json_encode($userDN . $salt);
-        // This allows the token to be different every time.
-        $time = time();
-
-        // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
-        $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
-
-        // We need to have a token allowed to be used within an URL.
-        $token = Utils::base64urlEncode($token_hmac);
-
-        // Save token within LDAP
-        self::saveTokenInLdap($userDN, $token, $timeStamp);
-
-        return $token;
+    // The user token DN creation
+    $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+    // Verify if a token already exists for specified user and remove it to create new one correctly.
+    if (self::tokenBranchExist($userTokenDN)) {
+      // Remove the user token
+      self::removeUserToken($userTokenDN);
     }
 
-    /**
-     * @param string $userDN
-     * @param string $token
-     * NOTE : UID is the full DN of the user. (uid=...).
-     * @param int $days
-     * @return bool
-     * @throws Exception
-     */
-    public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
-    {
-        $result = FALSE;
-
-        $currentTimestamp = time();
-        // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
-        $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
-
-        preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
-        $uid = $matches[1];
-        $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-
-        $ldap_entry["objectClass"]      = ['top', 'fdTokenEntry'];
-        $ldap_entry["fdTokenUserDN"]    = $userDN;
-        $ldap_entry["fdTokenType"]      = 'reminder';
-        $ldap_entry["fdToken"]          = $token;
-        $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
-        $ldap_entry["cn"]               = $uid;
-
-        // set the dn for the token, only take what's between "uid=" and ",ou="
-
-
-        // Verify if token ou branch exists
-        if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
-            // Create the branch
-            self::createBranchToken();
-        }
-
-        // The user token DN creation
-        $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-        // Verify if a token already exists for specified user and remove it to create new one correctly.
-        if (self::tokenBranchExist($userTokenDN)) {
-            // Remove the user token
-            self::removeUserToken($userTokenDN);
-        }
-
-        // Add token to LDAP for specific UID
-        try {
-            $result = ldap_add($gateway->ds, $dn, $ldap_entry); // bool returned
-        } catch (Exception $e) {
-            echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
-            exit;
-        }
-
-        return $result;
-    }
-
-    /**
-     * @param int $subTaskCall
-     * @param int $firstCall
-     * @param int $secondCall
-     * @return int
-     * Note : Simply return the difference between first and second call. (First call can be null).
-     */
-    public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
-    {
-        // if firstCall is empty, secondCall is the timestamp expiry for the token.
-        $result = $secondCall;
-
-        if (!empty($firstCall)) {
-            // Verification if the subTask is the second reminder or the first reminder.
-            if ($subTaskCall === $firstCall) {
-                $result = $firstCall - $secondCall;
-            }
-        }
-
-        return $result;
+    // Add token to LDAP for specific UID
+    try {
+      $result = ldap_add($gateway->ds, $dn, $ldap_entry); // bool returned
+    } catch (Exception $e) {
+      echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
+      exit;
     }
 
-    /**
-     * @param $userTokenDN
-     * @return void
-     * Note : Simply remove the token for specific user DN
-     */
-    public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
-    {
-        // Add token to LDAP for specific UID
-        try {
-            $result = ldap_delete($gateway->ds, $userTokenDN); // bool returned
-        } catch (Exception $e) {
-            echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
-            exit;
-        }
+    return $result;
+  }
+
+  /**
+   * @param int $subTaskCall
+   * @param int $firstCall
+   * @param int $secondCall
+   * @return int
+   * Note : Simply return the difference between first and second call. (First call can be null).
+   */
+  public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
+  {
+    // if firstCall is empty, secondCall is the timestamp expiry for the token.
+    $result = $secondCall;
+
+    if (!empty($firstCall)) {
+      // Verification if the subTask is the second reminder or the first reminder.
+      if ($subTaskCall === $firstCall) {
+        $result = $firstCall - $secondCall;
+      }
     }
 
-    /**
-     * Create ou=pluginManager LDAP branch
-     * @throws Exception
-     */
-    public static function createBranchToken (TaskGateway $gateway): void
-    {
-        try {
-            ldap_add(
-                $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
-                [
-                    'ou'          => 'tokens',
-                    'objectClass' => 'organizationalUnit',
-                ]
-            );
-        } catch (Exception $e) {
-
-            echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
-            exit;
-        }
+    return $result;
+  }
+
+  /**
+   * @param $userTokenDN
+   * @return void
+   * Note : Simply remove the token for specific user DN
+   */
+  public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
+  {
+    // Add token to LDAP for specific UID
+    try {
+      $result = ldap_delete($gateway->ds, $userTokenDN); // bool returned
+    } catch (Exception $e) {
+      echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
+      exit;
     }
-
-    /**
-     * @param string $token
-     * @param array $mailTemplateForm
-     * @param string $taskDN
-     * @return array
-     */
-    public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
-    {
-        //Only take the cn of the main task name :
-        preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
-        $taskName = $matches[1];
-
-        // Remove the API URI
-        $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
-        $url        = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
-
-        $mailTemplateForm['body'] .= $url;
-
-        return $mailTemplateForm;
+  }
+
+  /**
+   * Create ou=pluginManager LDAP branch
+   * @throws Exception
+   */
+  public static function createBranchToken (TaskGateway $gateway): void
+  {
+    try {
+      ldap_add(
+        $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
+        [
+          'ou'      => 'tokens',
+          'objectClass' => 'organizationalUnit',
+        ]
+      );
+    } catch (Exception $e) {
+
+      echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
+      exit;
     }
-
-    /**
-     * @param string $dn
-     * @return bool
-     * Note : Simply inspect if the branch for token is existing.
-     */
-    public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
-    {
-        $result = FALSE;
-
-        try {
-            $search = ldap_search($gateway->ds, $dn, "(objectClass=*)");
-            // Check if the search was successful
-            if ($search) {
-                // Get the number of entries found
-                $entries = ldap_get_entries($gateway->ds, $search);
-
-                // If entries are found, set result to true
-                if ($entries["count"] > 0) {
-                    $result = TRUE;
-                }
-            }
-        } catch (Exception $e) {
-            $result = FALSE;
+  }
+
+  /**
+   * @param string $token
+   * @param array $mailTemplateForm
+   * @param string $taskDN
+   * @return array
+   */
+  public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
+  {
+    //Only take the cn of the main task name :
+    preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
+    $taskName = $matches[1];
+
+    // Remove the API URI
+    $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
+    $url    = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
+
+    $mailTemplateForm['body'] .= $url;
+
+    return $mailTemplateForm;
+  }
+
+  /**
+   * @param string $dn
+   * @return bool
+   * Note : Simply inspect if the branch for token is existing.
+   */
+  public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
+  {
+    $result = FALSE;
+
+    try {
+      $search = ldap_search($gateway->ds, $dn, "(objectClass=*)");
+      // Check if the search was successful
+      if ($search) {
+        // Get the number of entries found
+        $entries = ldap_get_entries($gateway->ds, $search);
+
+        // If entries are found, set result to true
+        if ($entries["count"] > 0) {
+          $result = TRUE;
         }
-
-        return $result;
+      }
+    } catch (Exception $e) {
+      $result = FALSE;
     }
+
+    return $result;
+  }
 }
\ No newline at end of file
-- 
GitLab


From 79a2090bef346258300873912dae1c43c15027c5 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:12:51 +0100
Subject: [PATCH 07/36] fix indent

---
 library/TokenUtils.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
index dbf72bb..7a6ebd9 100644
--- a/library/TokenUtils.php
+++ b/library/TokenUtils.php
@@ -136,7 +136,7 @@ class TokenUtils
       ldap_add(
         $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
         [
-          'ou'      => 'tokens',
+          'ou'          => 'tokens',
           'objectClass' => 'organizationalUnit',
         ]
       );
-- 
GitLab


From fa198df864afa633993246c450fcd86b43e201c5 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:14:46 +0100
Subject: [PATCH 08/36] fix params

---
 library/TokenUtils.php     | 12 ++++++------
 plugins/tasks/Reminder.php |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
index 7a6ebd9..8552514 100644
--- a/library/TokenUtils.php
+++ b/library/TokenUtils.php
@@ -12,7 +12,7 @@ class TokenUtils
    * @return string
    * @throws Exception
    */
-  public static function generateToken (string $userDN, int $timeStamp): string
+  public static function generateToken (string $userDN, int $timeStamp, TaskGateway $gateway): string
   {
     $token = NULL;
     // Salt has been generated with APG.
@@ -28,7 +28,7 @@ class TokenUtils
     $token = Utils::base64urlEncode($token_hmac);
 
     // Save token within LDAP
-    self::saveTokenInLdap($userDN, $token, $timeStamp);
+    self::saveTokenInLdap($userDN, $token, $timeStamp, $gateway);
 
     return $token;
   }
@@ -64,17 +64,17 @@ class TokenUtils
 
 
     // Verify if token ou branch exists
-    if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
+    if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"], $gateway)) {
       // Create the branch
-      self::createBranchToken();
+      self::createBranchToken($gateway);
     }
 
     // The user token DN creation
     $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
     // Verify if a token already exists for specified user and remove it to create new one correctly.
-    if (self::tokenBranchExist($userTokenDN)) {
+    if (self::tokenBranchExist($userTokenDN, $gateway)) {
       // Remove the user token
-      self::removeUserToken($userTokenDN);
+      self::removeUserToken($userTokenDN, $gateway);
     }
 
     // Add token to LDAP for specific UID
diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php
index 8cda3fc..1faeff7 100644
--- a/plugins/tasks/Reminder.php
+++ b/plugins/tasks/Reminder.php
@@ -113,7 +113,7 @@ class Reminder implements EndpointInterface
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
+            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
             // Edit the mailForm with the url link containing the token
             $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
@@ -140,7 +140,7 @@ class Reminder implements EndpointInterface
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire);
+            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
             // Edit the mailForm with the url link containing the token
             $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
-- 
GitLab


From 205066a6d6c375b5a45fb3d5bbb40add26121f6e Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:15:32 +0100
Subject: [PATCH 09/36] fix self

---
 library/Utils.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/Utils.php b/library/Utils.php
index d1afd6d..1c132ac 100644
--- a/library/Utils.php
+++ b/library/Utils.php
@@ -17,7 +17,7 @@ class Utils
     $filtered = array_filter($array, function ($item) {
       if (is_array($item)) {
         // Recursively filter the sub-array
-        $item = $this->recursiveArrayFilter($item);
+        $item = self::recursiveArrayFilter($item);
         // Only retain non-empty arrays
         return !empty($item);
       } else {
-- 
GitLab


From 5556c8f2edf8b44683a67be2e225c9a1d512a560 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:08:50 +0100
Subject: [PATCH 10/36] Refactor libraries

---
 plugins/tasks/Notifications.php | 66 +++++++++++++++++----------------
 1 file changed, 34 insertions(+), 32 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 3b1d774..e17ffe3 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -4,7 +4,6 @@ class Notifications implements EndpointInterface
 {
 
   private TaskGateway $gateway;
-  private string $errorMessage = 'No matching audited attributes with monitored attributes, safely removed!';
 
   public function __construct (TaskGateway $gateway)
   {
@@ -70,7 +69,26 @@ class Notifications implements EndpointInterface
         // Generate the mail form with all mail controller requirements
         $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask);
 
-        $matchingAttrs = $this->getMatchingAttrs($notificationsMainTask, $task);
+        // Simply retrieve the list of audited attributes
+        $auditAttributes = $this->decodeAuditAttributes($task);
+
+        // Recovering monitored attributes list from the defined notification task.
+        $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+        // Reformat supann
+        $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+
+        // Simply remove keys with 'count' reported by ldap.
+        $this->gateway->unsetCountKeys($monitoredAttrs);
+        $this->gateway->unsetCountKeys($monitoredSupannResource);
+
+        // Find matching attributes between audited and monitored attributes
+        $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
+
+        // Verify Supann resource state if applicable
+        if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+          // Adds it to the mating attrs for further notification process.
+          $matchingAttrs[] = 'supannRessourceEtat';
+        }
 
         if (!empty($matchingAttrs)) {
           // Fill an array with UID of audited user and related matching attributes
@@ -85,7 +103,7 @@ class Notifications implements EndpointInterface
 
         } else { // Simply remove the subTask has no notifications are required
           $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']);
-          $result[$task['dn']]['Status']  = $this->errorMessage;
+          $result[$task['dn']]['Status']  = 'No matching audited attributes with monitored attributes, safely removed!';
         }
       }
     }
@@ -97,32 +115,6 @@ class Notifications implements EndpointInterface
     return $result;
   }
 
-  private function getMatchingAttrs (array $notificationsMainTask, array $task): array
-  {
-    // Simply retrieve the list of audited attributes
-    $auditAttributes = $this->decodeAuditAttributes($task);
-
-    // Recovering monitored attributes list from the defined notification task.
-    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-    // Reformat supann
-    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
-
-    // Simply remove keys with 'count' reported by ldap.
-    $this->gateway->unsetCountKeys($monitoredAttrs);
-    $this->gateway->unsetCountKeys($monitoredSupannResource);
-
-    // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
-
-    // Verify Supann resource state if applicable
-    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-      // Adds it to the mating attrs for further notification process.
-      $matchingAttrs[] = 'supannRessourceEtat';
-    }
-
-      return $matchingAttrs;
-  }
-
   /**
    * Determine if Supann resource verification is needed.
    *
@@ -181,15 +173,25 @@ class Notifications implements EndpointInterface
    */
   private function verifySupannState (array $supannResource, array $auditedAttrs): bool
   {
+    $result = FALSE;
+
     //Construct Supann Resource State as string
-    $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
     if (!empty($supannResource['subState'][0])) {
-      $monitoredSupannState = $monitoredSupannState . ':' . $supannResource['subState'][0];
+      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0] . ':' . $supannResource['subState'][0];
+    } else {
+      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
     }
 
     // Get all the values only of a multidimensional array.
     $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
-    return in_array($monitoredSupannState, $auditedValues);
+
+    if (in_array($monitoredSupannState, $auditedValues)) {
+      $result = TRUE;
+    } else {
+      $result = FALSE;
+    }
+
+    return $result;
   }
 
   /**
-- 
GitLab


From f19b7447c217ceea236418bcb62e52fc19176116 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:05:43 +0100
Subject: [PATCH 11/36] Add mail utils

---
 library/MailUtils.php | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 library/MailUtils.php

diff --git a/library/MailUtils.php b/library/MailUtils.php
new file mode 100644
index 0000000..44b1f48
--- /dev/null
+++ b/library/MailUtils.php
@@ -0,0 +1,22 @@
+<?php
+
+class MailUtils
+{
+    private function __construct()
+    {
+    }
+
+    public static function sendMail($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
+    {
+        $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
+            $setBCC,
+            $recipients,
+            $body,
+            $signature,
+            $subject,
+            $receipt,
+            $attachments);
+
+        return $mail_controller->sendMail();
+    }
+}
\ No newline at end of file
-- 
GitLab


From 39a730ac76992861b362c54cd802881dffd3fccf Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:10:44 +0100
Subject: [PATCH 12/36] add method

---
 library/MailUtils.php | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/library/MailUtils.php b/library/MailUtils.php
index 44b1f48..81eaa33 100644
--- a/library/MailUtils.php
+++ b/library/MailUtils.php
@@ -19,4 +19,16 @@ class MailUtils
 
         return $mail_controller->sendMail();
     }
+
+    /**
+     * @return array
+     * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory
+     */
+    public static function getMailObjectConfiguration (TaskGateway $gateway): array
+    {
+        return $gateway->getLdapTasks(
+            "(objectClass=fdTasksConf)",
+            ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"]
+        );
+    }
 }
\ No newline at end of file
-- 
GitLab


From a8b73ad06f4826ec2ffdb64b7288711eb8070e2e Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:22:52 +0100
Subject: [PATCH 13/36] fix indent

---
 library/MailUtils.php | 52 +++++++++++++++++++++----------------------
 1 file changed, 26 insertions(+), 26 deletions(-)

diff --git a/library/MailUtils.php b/library/MailUtils.php
index 81eaa33..8779224 100644
--- a/library/MailUtils.php
+++ b/library/MailUtils.php
@@ -2,33 +2,33 @@
 
 class MailUtils
 {
-    private function __construct()
-    {
-    }
+  private function __construct()
+  {
+  }
 
-    public static function sendMail($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
-    {
-        $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
-            $setBCC,
-            $recipients,
-            $body,
-            $signature,
-            $subject,
-            $receipt,
-            $attachments);
+  public static function sendMail($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
+  {
+    $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
+      $setBCC,
+      $recipients,
+      $body,
+      $signature,
+      $subject,
+      $receipt,
+      $attachments);
 
-        return $mail_controller->sendMail();
-    }
+    return $mail_controller->sendMail();
+  }
 
-    /**
-     * @return array
-     * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory
-     */
-    public static function getMailObjectConfiguration (TaskGateway $gateway): array
-    {
-        return $gateway->getLdapTasks(
-            "(objectClass=fdTasksConf)",
-            ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"]
-        );
-    }
+  /**
+   * @return array
+   * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory
+   */
+  public static function getMailObjectConfiguration (TaskGateway $gateway): array
+  {
+    return $gateway->getLdapTasks(
+      "(objectClass=fdTasksConf)",
+      ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"]
+    );
+  }
 }
\ No newline at end of file
-- 
GitLab


From 412478656bfb7dc3f2648f65f0c99c0333b2095e Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:23:22 +0100
Subject: [PATCH 14/36] fix indent

---
 library/MailUtils.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/MailUtils.php b/library/MailUtils.php
index 8779224..5fc496a 100644
--- a/library/MailUtils.php
+++ b/library/MailUtils.php
@@ -2,11 +2,11 @@
 
 class MailUtils
 {
-  private function __construct()
+  private function __construct ()
   {
   }
 
-  public static function sendMail($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
+  public static function sendMail ($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
   {
     $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
       $setBCC,
-- 
GitLab


From e2cd05a614211add6da698ac522e5bd7993d5f77 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 10:54:58 +0100
Subject: [PATCH 15/36] redesign

---
 plugins/tasks/Notifications.php | 109 ++++++++++++++++++++++----------
 1 file changed, 75 insertions(+), 34 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index e17ffe3..bd8222d 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -4,6 +4,7 @@ class Notifications implements EndpointInterface
 {
 
   private TaskGateway $gateway;
+  private string $errorMessage = 'No matching audited attributes with monitored attributes, safely removed!';
 
   public function __construct (TaskGateway $gateway)
   {
@@ -61,7 +62,6 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
-
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -69,26 +69,7 @@ class Notifications implements EndpointInterface
         // Generate the mail form with all mail controller requirements
         $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask);
 
-        // Simply retrieve the list of audited attributes
-        $auditAttributes = $this->decodeAuditAttributes($task);
-
-        // Recovering monitored attributes list from the defined notification task.
-        $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-        // Reformat supann
-        $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
-
-        // Simply remove keys with 'count' reported by ldap.
-        $this->gateway->unsetCountKeys($monitoredAttrs);
-        $this->gateway->unsetCountKeys($monitoredSupannResource);
-
-        // Find matching attributes between audited and monitored attributes
-        $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
-
-        // Verify Supann resource state if applicable
-        if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-        }
+        $matchingAttrs = $this->getMatchingAttrs($notificationsMainTask, $task);
 
         if (!empty($matchingAttrs)) {
           // Fill an array with UID of audited user and related matching attributes
@@ -103,7 +84,7 @@ class Notifications implements EndpointInterface
 
         } else { // Simply remove the subTask has no notifications are required
           $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']);
-          $result[$task['dn']]['Status']  = 'No matching audited attributes with monitored attributes, safely removed!';
+          $result[$task['dn']]['Status']  = $this->errorMessage;
         }
       }
     }
@@ -115,6 +96,32 @@ class Notifications implements EndpointInterface
     return $result;
   }
 
+  private function getMatchingAttrs (array $notificationsMainTask, array $task): array
+  {
+      // Simply retrieve the list of audited attributes
+      $auditAttributes = $this->decodeAuditAttributes($task);
+
+      // Recovering monitored attributes list from the defined notification task.
+      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+      // Reformat supann
+      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+
+      // Simply remove keys with 'count' reported by ldap.
+      $this->gateway->unsetCountKeys($monitoredAttrs);
+      $this->gateway->unsetCountKeys($monitoredSupannResource);
+
+      // Find matching attributes between audited and monitored attributes
+      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+
+      // Verify Supann resource state if applicable
+      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+          // Adds it to the mating attrs for further notification process.
+          $matchingAttrs[] = 'supannRessourceEtat';
+      }
+
+      return $matchingAttrs;
+  }
+
   /**
    * Determine if Supann resource verification is needed.
    *
@@ -165,6 +172,30 @@ class Notifications implements EndpointInterface
     return $auditAttributes;
   }
 
+  /**
+   * Find matching attributes between audit and monitored attributes.
+   *
+   * @param array|null $auditAttributes
+   * @param array $monitoredAttrs
+   * @return array
+   */
+  private function findMatchingAttributes (?array $auditAttributes, array $monitoredAttrs): array
+  {
+    $matchingAttrs = [];
+
+    if (!empty($auditAttributes)) {
+      foreach ($auditAttributes as $attributeName) {
+        foreach ($monitoredAttrs as $monitoredAttr) {
+          if (!empty($attributeName) && array_key_exists($monitoredAttr, $attributeName)) {
+            $matchingAttrs[] = $monitoredAttr;
+          }
+        }
+      }
+    }
+
+    return $matchingAttrs;
+  }
+
   /**
    * @param array $supannResource
    * @param array $auditedAttrs
@@ -173,25 +204,35 @@ class Notifications implements EndpointInterface
    */
   private function verifySupannState (array $supannResource, array $auditedAttrs): bool
   {
-    $result = FALSE;
-
     //Construct Supann Resource State as string
+    $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
     if (!empty($supannResource['subState'][0])) {
-      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0] . ':' . $supannResource['subState'][0];
-    } else {
-      $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0];
+      $monitoredSupannState = $monitoredSupannState . ':' . $supannResource['subState'][0];
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
+    return in_array($monitoredSupannState, $auditedValues);
+  }
 
-    if (in_array($monitoredSupannState, $auditedValues)) {
-      $result = TRUE;
-    } else {
-      $result = FALSE;
+  /**
+   * @param $array
+   * @return array
+   * Note : simply return all values of a multi-dimensional array.
+   */
+  public function getArrayValuesRecursive ($array)
+  {
+    $values = [];
+    foreach ($array as $value) {
+      if (is_array($value)) {
+        // If value is an array, merge its values recursively
+        $values = array_merge($values, $this->getArrayValuesRecursive($value));
+      } else {
+        // If value is not an array, add it to the result
+        $values[] = $value;
+      }
     }
-
-    return $result;
+    return $values;
   }
 
   /**
-- 
GitLab


From a4f9d3576e6db502e96402d491814ef88f120ac1 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:08:50 +0100
Subject: [PATCH 16/36] Refactor libraries

---
 plugins/tasks/Notifications.php | 79 ++++++++-------------------------
 1 file changed, 18 insertions(+), 61 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index bd8222d..3b1d774 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -62,6 +62,7 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
+
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -98,26 +99,26 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-      // Simply retrieve the list of audited attributes
-      $auditAttributes = $this->decodeAuditAttributes($task);
+    // Simply retrieve the list of audited attributes
+    $auditAttributes = $this->decodeAuditAttributes($task);
 
-      // Recovering monitored attributes list from the defined notification task.
-      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-      // Reformat supann
-      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+    // Recovering monitored attributes list from the defined notification task.
+    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+    // Reformat supann
+    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-      // Simply remove keys with 'count' reported by ldap.
-      $this->gateway->unsetCountKeys($monitoredAttrs);
-      $this->gateway->unsetCountKeys($monitoredSupannResource);
+    // Simply remove keys with 'count' reported by ldap.
+    $this->gateway->unsetCountKeys($monitoredAttrs);
+    $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-      // Find matching attributes between audited and monitored attributes
-      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    // Find matching attributes between audited and monitored attributes
+    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
 
-      // Verify Supann resource state if applicable
-      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-      }
+    // Verify Supann resource state if applicable
+    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+      // Adds it to the mating attrs for further notification process.
+      $matchingAttrs[] = 'supannRessourceEtat';
+    }
 
       return $matchingAttrs;
   }
@@ -172,30 +173,6 @@ class Notifications implements EndpointInterface
     return $auditAttributes;
   }
 
-  /**
-   * Find matching attributes between audit and monitored attributes.
-   *
-   * @param array|null $auditAttributes
-   * @param array $monitoredAttrs
-   * @return array
-   */
-  private function findMatchingAttributes (?array $auditAttributes, array $monitoredAttrs): array
-  {
-    $matchingAttrs = [];
-
-    if (!empty($auditAttributes)) {
-      foreach ($auditAttributes as $attributeName) {
-        foreach ($monitoredAttrs as $monitoredAttr) {
-          if (!empty($attributeName) && array_key_exists($monitoredAttr, $attributeName)) {
-            $matchingAttrs[] = $monitoredAttr;
-          }
-        }
-      }
-    }
-
-    return $matchingAttrs;
-  }
-
   /**
    * @param array $supannResource
    * @param array $auditedAttrs
@@ -211,30 +188,10 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-  /**
-   * @param $array
-   * @return array
-   * Note : simply return all values of a multi-dimensional array.
-   */
-  public function getArrayValuesRecursive ($array)
-  {
-    $values = [];
-    foreach ($array as $value) {
-      if (is_array($value)) {
-        // If value is an array, merge its values recursively
-        $values = array_merge($values, $this->getArrayValuesRecursive($value));
-      } else {
-        // If value is not an array, add it to the result
-        $values[] = $value;
-      }
-    }
-    return $values;
-  }
-
   /**
    * @param string $mainTaskDn
    * @return array
-- 
GitLab


From fa1bf39ff442a36b87c588bb1327d37961d76059 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:26:42 +0100
Subject: [PATCH 17/36] rebase

---
 plugins/tasks/Notifications.php | 35 ++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 3b1d774..c292220 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -62,7 +62,6 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
-
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -99,26 +98,26 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-    // Simply retrieve the list of audited attributes
-    $auditAttributes = $this->decodeAuditAttributes($task);
+      // Simply retrieve the list of audited attributes
+      $auditAttributes = $this->decodeAuditAttributes($task);
 
-    // Recovering monitored attributes list from the defined notification task.
-    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-    // Reformat supann
-    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+      // Recovering monitored attributes list from the defined notification task.
+      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+      // Reformat supann
+      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-    // Simply remove keys with 'count' reported by ldap.
-    $this->gateway->unsetCountKeys($monitoredAttrs);
-    $this->gateway->unsetCountKeys($monitoredSupannResource);
+      // Simply remove keys with 'count' reported by ldap.
+      $this->gateway->unsetCountKeys($monitoredAttrs);
+      $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-    // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
+      // Find matching attributes between audited and monitored attributes
+      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
 
-    // Verify Supann resource state if applicable
-    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-      // Adds it to the mating attrs for further notification process.
-      $matchingAttrs[] = 'supannRessourceEtat';
-    }
+      // Verify Supann resource state if applicable
+      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+          // Adds it to the mating attrs for further notification process.
+          $matchingAttrs[] = 'supannRessourceEtat';
+      }
 
       return $matchingAttrs;
   }
@@ -188,7 +187,7 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-- 
GitLab


From 529216f0b156369725a7e93b91055832cafccf63 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:29:49 +0100
Subject: [PATCH 18/36] fix indent

---
 plugins/tasks/Notifications.php | 34 ++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index c292220..0374f41 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -98,28 +98,28 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-      // Simply retrieve the list of audited attributes
-      $auditAttributes = $this->decodeAuditAttributes($task);
+    // Simply retrieve the list of audited attributes
+    $auditAttributes = $this->decodeAuditAttributes($task);
 
-      // Recovering monitored attributes list from the defined notification task.
-      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-      // Reformat supann
-      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+    // Recovering monitored attributes list from the defined notification task.
+    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+    // Reformat supann
+    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-      // Simply remove keys with 'count' reported by ldap.
-      $this->gateway->unsetCountKeys($monitoredAttrs);
-      $this->gateway->unsetCountKeys($monitoredSupannResource);
+    // Simply remove keys with 'count' reported by ldap.
+    $this->gateway->unsetCountKeys($monitoredAttrs);
+    $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-      // Find matching attributes between audited and monitored attributes
-      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    // Find matching attributes between audited and monitored attributes
+    $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
 
-      // Verify Supann resource state if applicable
-      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-      }
+    // Verify Supann resource state if applicable
+    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+      // Adds it to the mating attrs for further notification process.
+      $matchingAttrs[] = 'supannRessourceEtat';
+    }
 
-      return $matchingAttrs;
+    return $matchingAttrs;
   }
 
   /**
-- 
GitLab


From a14653b2defc9e1bb48bba60bd59091bff013911 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 13:31:12 +0100
Subject: [PATCH 19/36] use lib

---
 plugins/tasks/Notifications.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 0374f41..a817a4f 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -111,7 +111,7 @@ class Notifications implements EndpointInterface
     $this->gateway->unsetCountKeys($monitoredSupannResource);
 
     // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
 
     // Verify Supann resource state if applicable
     if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
@@ -187,7 +187,7 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = $this->getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-- 
GitLab


From 61f162457703716d440deea8c8e00fb95642e0d3 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 11:11:09 +0100
Subject: [PATCH 20/36] use maps

---
 library/Utils.php | 26 ++++++++------------------
 1 file changed, 8 insertions(+), 18 deletions(-)

diff --git a/library/Utils.php b/library/Utils.php
index 1c132ac..24e8309 100644
--- a/library/Utils.php
+++ b/library/Utils.php
@@ -13,20 +13,12 @@ class Utils
    */
   public static function recursiveArrayFilter (array $array): array
   {
-    // First filter the array for non-empty elements
-    $filtered = array_filter($array, function ($item) {
-      if (is_array($item)) {
-        // Recursively filter the sub-array
-        $item = self::recursiveArrayFilter($item);
-        // Only retain non-empty arrays
-        return !empty($item);
-      } else {
-        // Retain non-empty scalar values
+    return array_filter($array, function ($item) {
+        if (is_array($item)) {
+            $item = self::recursiveArrayFilter($item);
+        }
         return !empty($item);
-      }
     });
-
-    return $filtered;
   }
 
   /**
@@ -40,12 +32,10 @@ class Utils
   {
     $matching = [];
 
-    if (!empty($elements)) {
-      foreach ($elements as $element) {
-        foreach ($keys as $key) {
-          if (!empty($element) && array_key_exists($key, $element)) {
-            $matching[] = $key;
-          }
+    foreach ($elements as $element) {
+      foreach ($keys as $key) {
+        if (!empty($element) && array_key_exists($key, $element)) {
+          $matching[] = $key;
         }
       }
     }
-- 
GitLab


From 631414a30e102fee095b273d167eb341e6019f35 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 11:12:26 +0100
Subject: [PATCH 21/36] indent

---
 library/Utils.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/library/Utils.php b/library/Utils.php
index 24e8309..7e31d9b 100644
--- a/library/Utils.php
+++ b/library/Utils.php
@@ -14,10 +14,10 @@ class Utils
   public static function recursiveArrayFilter (array $array): array
   {
     return array_filter($array, function ($item) {
-        if (is_array($item)) {
-            $item = self::recursiveArrayFilter($item);
-        }
-        return !empty($item);
+      if (is_array($item)) {
+          $item = self::recursiveArrayFilter($item);
+      }
+      return !empty($item);
     });
   }
 
-- 
GitLab


From 44ac67831faa50ad78a7d189bdf20b202e08cc5d Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 11:56:04 +0100
Subject: [PATCH 22/36] make non static

---
 library/Utils.php                             | 75 -------------------
 library/{ => plugins}/MailUtils.php           |  6 +-
 .../ReminderTokenUtils.php}                   | 40 ++++++----
 library/plugins/Utils.php                     | 59 +++++++++++++++
 plugins/tasks/Audit.php                       |  4 +-
 plugins/tasks/Notifications.php               |  4 +-
 plugins/tasks/Reminder.php                    | 16 ++--
 7 files changed, 102 insertions(+), 102 deletions(-)
 delete mode 100644 library/Utils.php
 rename library/{ => plugins}/MailUtils.php (70%)
 rename library/{TokenUtils.php => plugins/ReminderTokenUtils.php} (77%)
 create mode 100644 library/plugins/Utils.php

diff --git a/library/Utils.php b/library/Utils.php
deleted file mode 100644
index 7e31d9b..0000000
--- a/library/Utils.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-class Utils
-{
-  private function __construct ()
-  {
-  }
-
-  /**
-   * @param array $array
-   * @return array
-   * Note : Recursively filters out empty values and arrays at any depth.
-   */
-  public static function recursiveArrayFilter (array $array): array
-  {
-    return array_filter($array, function ($item) {
-      if (is_array($item)) {
-          $item = self::recursiveArrayFilter($item);
-      }
-      return !empty($item);
-    });
-  }
-
-  /**
-   * Find matching keys between 2 lists.
-   *
-   * @param array|null $elements
-   * @param array $keys
-   * @return array
-   */
-  public static function findMatchingKeys (?array $elements, array $keys): array
-  {
-    $matching = [];
-
-    foreach ($elements as $element) {
-      foreach ($keys as $key) {
-        if (!empty($element) && array_key_exists($key, $element)) {
-          $matching[] = $key;
-        }
-      }
-    }
-
-    return $matching;
-  }
-
-  /**
-   * @param $array
-   * @return array
-   * Note : simply return all values of a multi-dimensional array.
-   */
-  public static function getArrayValuesRecursive ($array)
-  {
-    $values = [];
-    foreach ($array as $value) {
-      if (is_array($value)) {
-        // If value is an array, merge its values recursively
-        $values = array_merge($values, self::getArrayValuesRecursive($value));
-      } else {
-        // If value is not an array, add it to the result
-        $values[] = $value;
-      }
-    }
-    return $values;
-  }
-
-  /**
-   * @param string $text
-   * @return string
-   * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
-   */
-  public static function base64urlEncode (string $text): string
-  {
-    return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
-  }
-}
\ No newline at end of file
diff --git a/library/MailUtils.php b/library/plugins/MailUtils.php
similarity index 70%
rename from library/MailUtils.php
rename to library/plugins/MailUtils.php
index 5fc496a..87f4c64 100644
--- a/library/MailUtils.php
+++ b/library/plugins/MailUtils.php
@@ -2,11 +2,11 @@
 
 class MailUtils
 {
-  private function __construct ()
+  public function __construct ()
   {
   }
 
-  public static function sendMail ($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
+  public function sendMail ($setFrom, $setBCC, $recipients, $body, $signature, $subject, $receipt, $attachments)
   {
     $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
       $setBCC,
@@ -24,7 +24,7 @@ class MailUtils
    * @return array
    * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory
    */
-  public static function getMailObjectConfiguration (TaskGateway $gateway): array
+  public function getMailObjectConfiguration (TaskGateway $gateway): array
   {
     return $gateway->getLdapTasks(
       "(objectClass=fdTasksConf)",
diff --git a/library/TokenUtils.php b/library/plugins/ReminderTokenUtils.php
similarity index 77%
rename from library/TokenUtils.php
rename to library/plugins/ReminderTokenUtils.php
index 8552514..4ead9b4 100644
--- a/library/TokenUtils.php
+++ b/library/plugins/ReminderTokenUtils.php
@@ -1,8 +1,8 @@
 <?php
 
-class TokenUtils
+class ReminderTokenUtils
 {
-  private function __construct ()
+  public function __construct ()
   {
   }
 
@@ -12,7 +12,7 @@ class TokenUtils
    * @return string
    * @throws Exception
    */
-  public static function generateToken (string $userDN, int $timeStamp, TaskGateway $gateway): string
+  public function generateToken (string $userDN, int $timeStamp, TaskGateway $gateway): string
   {
     $token = NULL;
     // Salt has been generated with APG.
@@ -25,10 +25,10 @@ class TokenUtils
     $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
 
     // We need to have a token allowed to be used within an URL.
-    $token = Utils::base64urlEncode($token_hmac);
+    $token = $this->base64urlEncode($token_hmac);
 
     // Save token within LDAP
-    self::saveTokenInLdap($userDN, $token, $timeStamp, $gateway);
+    $this->saveTokenInLdap($userDN, $token, $timeStamp, $gateway);
 
     return $token;
   }
@@ -41,7 +41,7 @@ class TokenUtils
    * @return bool
    * @throws Exception
    */
-  public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
+  private function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
   {
     $result = FALSE;
 
@@ -64,17 +64,17 @@ class TokenUtils
 
 
     // Verify if token ou branch exists
-    if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"], $gateway)) {
+    if (!$this->tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"], $gateway)) {
       // Create the branch
-      self::createBranchToken($gateway);
+      $this->createBranchToken($gateway);
     }
 
     // The user token DN creation
     $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
     // Verify if a token already exists for specified user and remove it to create new one correctly.
-    if (self::tokenBranchExist($userTokenDN, $gateway)) {
+    if ($this->tokenBranchExist($userTokenDN, $gateway)) {
       // Remove the user token
-      self::removeUserToken($userTokenDN, $gateway);
+      $this->removeUserToken($userTokenDN, $gateway);
     }
 
     // Add token to LDAP for specific UID
@@ -95,7 +95,7 @@ class TokenUtils
    * @return int
    * Note : Simply return the difference between first and second call. (First call can be null).
    */
-  public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
+  public function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
   {
     // if firstCall is empty, secondCall is the timestamp expiry for the token.
     $result = $secondCall;
@@ -115,7 +115,7 @@ class TokenUtils
    * @return void
    * Note : Simply remove the token for specific user DN
    */
-  public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
+  private function removeUserToken ($userTokenDN, TaskGateway $gateway): void
   {
     // Add token to LDAP for specific UID
     try {
@@ -130,7 +130,7 @@ class TokenUtils
    * Create ou=pluginManager LDAP branch
    * @throws Exception
    */
-  public static function createBranchToken (TaskGateway $gateway): void
+  private function createBranchToken (TaskGateway $gateway): void
   {
     try {
       ldap_add(
@@ -153,7 +153,7 @@ class TokenUtils
    * @param string $taskDN
    * @return array
    */
-  public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
+  public function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
   {
     //Only take the cn of the main task name :
     preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
@@ -173,7 +173,7 @@ class TokenUtils
    * @return bool
    * Note : Simply inspect if the branch for token is existing.
    */
-  public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
+  private function tokenBranchExist (string $dn, TaskGateway $gateway): bool
   {
     $result = FALSE;
 
@@ -195,4 +195,14 @@ class TokenUtils
 
     return $result;
   }
+
+    /**
+     * @param string $text
+     * @return string
+     * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
+     */
+    private function base64urlEncode (string $text): string
+    {
+        return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+    }
 }
\ No newline at end of file
diff --git a/library/plugins/Utils.php b/library/plugins/Utils.php
new file mode 100644
index 0000000..10eafe6
--- /dev/null
+++ b/library/plugins/Utils.php
@@ -0,0 +1,59 @@
+<?php
+
+class Utils
+{
+  public function __construct ()
+  {
+  }
+
+  /**
+   * @param array $array
+   * @return array
+   * Note : Recursively filters out empty values and arrays at any depth.
+   */
+  public function recursiveArrayFilter (array $array): array
+  {
+    return array_filter($array, function ($item) {
+      if (is_array($item)) {
+          $item = $this->recursiveArrayFilter($item);
+      }
+      return !empty($item);
+    });
+  }
+
+  /**
+   * Find matching keys between 2 lists.
+   *
+   * @param array|null $elements
+   * @param array $keys
+   * @return array
+   */
+  public function findMatchingKeys (?array $elements, array $keys): array
+  {
+    $matching = [];
+
+    if (!empty($elements)) {
+      foreach ($elements as $element) {
+        foreach ($keys as $key) {
+          if (!empty($element) && array_key_exists($key, $element)) {
+            $matching[] = $key;
+          }
+        }
+      }
+    }
+
+    return $matching;
+  }
+
+  /**
+   * @param $array
+   * @return array
+   * Note : simply return all values of a multi-dimensional array.
+   */
+  public function getArrayValuesRecursive ($array)
+  {
+    return array_reduce($array, function ($carry, $value) {
+      return array_merge($carry, is_array($value) ? $this->getArrayValuesRecursive($value) : [$value]);
+    }, []);
+  }
+}
\ No newline at end of file
diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php
index c4bbe40..9481831 100644
--- a/plugins/tasks/Audit.php
+++ b/plugins/tasks/Audit.php
@@ -4,10 +4,12 @@ class Audit implements EndpointInterface
 {
 
   private TaskGateway $gateway;
+  private Utils $utils;
 
   public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
+    $this->utils = new Utils();
   }
 
   /**
@@ -47,7 +49,7 @@ class Audit implements EndpointInterface
     $result = $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit'));
 
     // Recursive function to filter out empty arrays at any depth
-    $nonEmptyResults = Utils::recursiveArrayFilter($result);
+    $nonEmptyResults = $this->utils->recursiveArrayFilter($result);
 
     if (!empty($nonEmptyResults)) {
       return $nonEmptyResults;
diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index a817a4f..2dd5e65 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -5,10 +5,12 @@ class Notifications implements EndpointInterface
 
   private TaskGateway $gateway;
   private string $errorMessage = 'No matching audited attributes with monitored attributes, safely removed!';
+  private Utils $utils;
 
   public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
+    $this->utils = new Utils();
   }
 
   /**
@@ -187,7 +189,7 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = $this->utils->getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php
index 1faeff7..ceb619a 100644
--- a/plugins/tasks/Reminder.php
+++ b/plugins/tasks/Reminder.php
@@ -4,10 +4,12 @@ class Reminder implements EndpointInterface
 {
 
   private TaskGateway $gateway;
-
+  private ReminderTokenUtils $reminderTokenUtils;
+  
   public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
+    $this->reminderTokenUtils = new ReminderTokenUtils();
   }
 
   /**
@@ -109,13 +111,13 @@ class Reminder implements EndpointInterface
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0];
 
             // Create timeStamp expiration for token
-            $tokenExpire = TokenUtils::getTokenExpiration($task['fdtasksgranularhelper'][0],
+            $tokenExpire = $this->reminderTokenUtils->getTokenExpiration($task['fdtasksgranularhelper'][0],
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
+            $token = $this->reminderTokenUtils->generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
             // Edit the mailForm with the url link containing the token
-            $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
+            $tokenMailTemplateForm = $this->reminderTokenUtils->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm;
 
@@ -136,13 +138,13 @@ class Reminder implements EndpointInterface
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0];
 
             // Create timeStamp expiration for token
-            $tokenExpire = TokenUtils::getTokenExpiration($task['fdtasksgranularhelper'][0],
+            $tokenExpire = $this->reminderTokenUtils->getTokenExpiration($task['fdtasksgranularhelper'][0],
               $remindersMainTask[0]['fdtasksreminderfirstcall'][0],
               $remindersMainTask[0]['fdtasksremindersecondcall'][0]);
             // Create token for SubTask
-            $token = TokenUtils::generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
+            $token = $this->reminderTokenUtils->generateToken($task['fdtasksgranulardn'][0], $tokenExpire, $this->gateway);
             // Edit the mailForm with the url link containing the token
-            $tokenMailTemplateForm = TokenUtils::generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
+            $tokenMailTemplateForm = $this->reminderTokenUtils->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName);
             // Recipient email form
             $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm;
 
-- 
GitLab


From ea1c485ebf68e71e4f884cc26d8b59a9a340e892 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 11:57:20 +0100
Subject: [PATCH 23/36] fix indent

---
 library/plugins/ReminderTokenUtils.php | 18 +++++++++---------
 plugins/tasks/Reminder.php             |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/library/plugins/ReminderTokenUtils.php b/library/plugins/ReminderTokenUtils.php
index 4ead9b4..bb08d24 100644
--- a/library/plugins/ReminderTokenUtils.php
+++ b/library/plugins/ReminderTokenUtils.php
@@ -196,13 +196,13 @@ class ReminderTokenUtils
     return $result;
   }
 
-    /**
-     * @param string $text
-     * @return string
-     * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
-     */
-    private function base64urlEncode (string $text): string
-    {
-        return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
-    }
+  /**
+   * @param string $text
+   * @return string
+   * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
+   */
+  private function base64urlEncode (string $text): string
+  {
+    return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+  }
 }
\ No newline at end of file
diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php
index ceb619a..180eca7 100644
--- a/plugins/tasks/Reminder.php
+++ b/plugins/tasks/Reminder.php
@@ -5,7 +5,7 @@ class Reminder implements EndpointInterface
 
   private TaskGateway $gateway;
   private ReminderTokenUtils $reminderTokenUtils;
-  
+
   public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
-- 
GitLab


From b862b3270ec2fb7632411e87e49b870d1fb53525 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:08:50 +0100
Subject: [PATCH 24/36] Refactor libraries

---
 library/TokenUtils.php          | 197 ++++++++++++++++++++++++++++++++
 library/Utils.php               |  83 ++++++++++++++
 plugins/tasks/Notifications.php |   2 +-
 3 files changed, 281 insertions(+), 1 deletion(-)
 create mode 100644 library/TokenUtils.php
 create mode 100644 library/Utils.php

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
new file mode 100644
index 0000000..d19392c
--- /dev/null
+++ b/library/TokenUtils.php
@@ -0,0 +1,197 @@
+<?php
+
+class TokenUtils
+{
+    private function __construct() {
+    }
+
+    /**
+     * @param string $userDN
+     * @param int $timeStamp
+     * @return string
+     * @throws Exception
+     */
+    public static function generateToken (string $userDN, int $timeStamp): string
+    {
+        $token = NULL;
+        // Salt has been generated with APG.
+        $salt    = '8onOlEsItKond';
+        $payload = json_encode($userDN . $salt);
+        // This allows the token to be different every time.
+        $time = time();
+
+        // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
+        $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
+
+        // We need to have a token allowed to be used within an URL.
+        $token = Utils::base64urlEncode($token_hmac);
+
+        // Save token within LDAP
+        self::saveTokenInLdap($userDN, $token, $timeStamp);
+
+        return $token;
+    }
+
+    /**
+     * @param string $userDN
+     * @param string $token
+     * NOTE : UID is the full DN of the user. (uid=...).
+     * @param int $days
+     * @return bool
+     * @throws Exception
+     */
+    public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
+    {
+        $result = FALSE;
+
+        $currentTimestamp = time();
+        // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
+        $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
+
+        preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
+        $uid = $matches[1];
+        $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+
+        $ldap_entry["objectClass"]      = ['top', 'fdTokenEntry'];
+        $ldap_entry["fdTokenUserDN"]    = $userDN;
+        $ldap_entry["fdTokenType"]      = 'reminder';
+        $ldap_entry["fdToken"]          = $token;
+        $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
+        $ldap_entry["cn"]               = $uid;
+
+        // set the dn for the token, only take what's between "uid=" and ",ou="
+
+
+        // Verify if token ou branch exists
+        if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
+            // Create the branch
+            self::createBranchToken();
+        }
+
+        // The user token DN creation
+        $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
+        // Verify if a token already exists for specified user and remove it to create new one correctly.
+        if (self::tokenBranchExist($userTokenDN)) {
+            // Remove the user token
+            self::removeUserToken($userTokenDN);
+        }
+
+        // Add token to LDAP for specific UID
+        try {
+            $result = ldap_add($gateway->ds, $dn, $ldap_entry); // bool returned
+        } catch (Exception $e) {
+            echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
+            exit;
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param int $subTaskCall
+     * @param int $firstCall
+     * @param int $secondCall
+     * @return int
+     * Note : Simply return the difference between first and second call. (First call can be null).
+     */
+    public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
+    {
+        // if firstCall is empty, secondCall is the timestamp expiry for the token.
+        $result = $secondCall;
+
+        if (!empty($firstCall)) {
+            // Verification if the subTask is the second reminder or the first reminder.
+            if ($subTaskCall === $firstCall) {
+                $result = $firstCall - $secondCall;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param $userTokenDN
+     * @return void
+     * Note : Simply remove the token for specific user DN
+     */
+    public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
+    {
+        // Add token to LDAP for specific UID
+        try {
+            $result = ldap_delete($gateway->ds, $userTokenDN); // bool returned
+        } catch (Exception $e) {
+            echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
+            exit;
+        }
+    }
+
+    /**
+     * Create ou=pluginManager LDAP branch
+     * @throws Exception
+     */
+    public static function createBranchToken (TaskGateway $gateway): void
+    {
+        try {
+            ldap_add(
+                $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
+                [
+                    'ou'          => 'tokens',
+                    'objectClass' => 'organizationalUnit',
+                ]
+            );
+        } catch (Exception $e) {
+
+            echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
+            exit;
+        }
+    }
+
+    /**
+     * @param string $token
+     * @param array $mailTemplateForm
+     * @param string $taskDN
+     * @return array
+     */
+    public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
+    {
+        //Only take the cn of the main task name :
+        preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
+        $taskName = $matches[1];
+
+        // Remove the API URI
+        $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
+        $url        = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
+
+        $mailTemplateForm['body'] .= $url;
+
+        return $mailTemplateForm;
+    }
+
+    /**
+     * @param string $dn
+     * @return bool
+     * Note : Simply inspect if the branch for token is existing.
+     */
+    public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
+    {
+        $result = FALSE;
+
+        try {
+            $search = ldap_search($gateway->ds, $dn, "(objectClass=*)");
+            // Check if the search was successful
+            if ($search) {
+                // Get the number of entries found
+                $entries = ldap_get_entries($gateway->ds, $search);
+
+                // If entries are found, set result to true
+                if ($entries["count"] > 0) {
+                    $result = TRUE;
+                }
+            }
+        } catch (Exception $e) {
+            $result = FALSE;
+        }
+
+        return $result;
+    }
+}
\ No newline at end of file
diff --git a/library/Utils.php b/library/Utils.php
new file mode 100644
index 0000000..c241a0a
--- /dev/null
+++ b/library/Utils.php
@@ -0,0 +1,83 @@
+<?php
+
+class Utils
+{
+    private function __construct() {}
+
+    /**
+     * @param array $array
+     * @return array
+     * Note : Recursively filters out empty values and arrays at any depth.
+     */
+    public static function recursiveArrayFilter (array $array): array
+    {
+        // First filter the array for non-empty elements
+        $filtered = array_filter($array, function ($item) {
+            if (is_array($item)) {
+                // Recursively filter the sub-array
+                $item = $this->recursiveArrayFilter($item);
+                // Only retain non-empty arrays
+                return !empty($item);
+            } else {
+                // Retain non-empty scalar values
+                return !empty($item);
+            }
+        });
+
+        return $filtered;
+    }
+
+    /**
+     * Find matching keys between 2 lists.
+     *
+     * @param array|null $elements
+     * @param array $keys
+     * @return array
+     */
+    public static function findMatchingKeys (?array $elements, array $keys): array
+    {
+        $matching = [];
+
+        if (!empty($elements)) {
+            foreach ($elements as $element) {
+                foreach ($keys as $key) {
+                    if (!empty($element) && array_key_exists($key, $element)) {
+                        $matching[] = $key;
+                    }
+                }
+            }
+        }
+
+        return $matching;
+    }
+
+    /**
+     * @param $array
+     * @return array
+     * Note : simply return all values of a multi-dimensional array.
+     */
+    public static function getArrayValuesRecursive ($array)
+    {
+        $values = [];
+        foreach ($array as $value) {
+            if (is_array($value)) {
+                // If value is an array, merge its values recursively
+                $values = array_merge($values, self::getArrayValuesRecursive($value));
+            } else {
+                // If value is not an array, add it to the result
+                $values[] = $value;
+            }
+        }
+        return $values;
+    }
+
+    /**
+     * @param string $text
+     * @return string
+     * Note : This come from jwtToken, as it is completely private - it is cloned here for now.
+     */
+    public static function base64urlEncode (string $text): string
+    {
+        return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text));
+    }
+}
\ No newline at end of file
diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 2dd5e65..b7ba372 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -189,7 +189,7 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = $this->utils->getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-- 
GitLab


From ac6c657cc14e9144e3970bfc352060bedbc14c55 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 10:54:58 +0100
Subject: [PATCH 25/36] redesign

---
 plugins/tasks/Notifications.php | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index b7ba372..1e248ec 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -100,26 +100,26 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-    // Simply retrieve the list of audited attributes
-    $auditAttributes = $this->decodeAuditAttributes($task);
+      // Simply retrieve the list of audited attributes
+      $auditAttributes = $this->decodeAuditAttributes($task);
 
-    // Recovering monitored attributes list from the defined notification task.
-    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-    // Reformat supann
-    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+      // Recovering monitored attributes list from the defined notification task.
+      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+      // Reformat supann
+      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-    // Simply remove keys with 'count' reported by ldap.
-    $this->gateway->unsetCountKeys($monitoredAttrs);
-    $this->gateway->unsetCountKeys($monitoredSupannResource);
+      // Simply remove keys with 'count' reported by ldap.
+      $this->gateway->unsetCountKeys($monitoredAttrs);
+      $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-    // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
+      // Find matching attributes between audited and monitored attributes
+      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
 
-    // Verify Supann resource state if applicable
-    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-      // Adds it to the mating attrs for further notification process.
-      $matchingAttrs[] = 'supannRessourceEtat';
-    }
+      // Verify Supann resource state if applicable
+      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+          // Adds it to the mating attrs for further notification process.
+          $matchingAttrs[] = 'supannRessourceEtat';
+      }
 
     return $matchingAttrs;
   }
-- 
GitLab


From ccacddcfa0c7d871244f010981d19f221fdbf860 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Mon, 24 Mar 2025 12:08:50 +0100
Subject: [PATCH 26/36] Refactor libraries

---
 plugins/tasks/Notifications.php | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 1e248ec..b3172ab 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -64,6 +64,7 @@ class Notifications implements EndpointInterface
     foreach ($notificationsSubTasks as $task) {
       // If the tasks must be treated - status and scheduled - process the sub-tasks
       if ($this->gateway->statusAndScheduleCheck($task)) {
+
         // Retrieve data from the main task
         $notificationsMainTask     = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]);
         $notificationsMainTaskName = $task['fdtasksgranularmaster'][0];
@@ -100,26 +101,26 @@ class Notifications implements EndpointInterface
 
   private function getMatchingAttrs (array $notificationsMainTask, array $task): array
   {
-      // Simply retrieve the list of audited attributes
-      $auditAttributes = $this->decodeAuditAttributes($task);
+    // Simply retrieve the list of audited attributes
+    $auditAttributes = $this->decodeAuditAttributes($task);
 
-      // Recovering monitored attributes list from the defined notification task.
-      $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
-      // Reformat supann
-      $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
+    // Recovering monitored attributes list from the defined notification task.
+    $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
+    // Reformat supann
+    $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
 
-      // Simply remove keys with 'count' reported by ldap.
-      $this->gateway->unsetCountKeys($monitoredAttrs);
-      $this->gateway->unsetCountKeys($monitoredSupannResource);
+    // Simply remove keys with 'count' reported by ldap.
+    $this->gateway->unsetCountKeys($monitoredAttrs);
+    $this->gateway->unsetCountKeys($monitoredSupannResource);
 
-      // Find matching attributes between audited and monitored attributes
-      $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs);
+    // Find matching attributes between audited and monitored attributes
+    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
 
-      // Verify Supann resource state if applicable
-      if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
-          // Adds it to the mating attrs for further notification process.
-          $matchingAttrs[] = 'supannRessourceEtat';
-      }
+    // Verify Supann resource state if applicable
+    if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
+      // Adds it to the mating attrs for further notification process.
+      $matchingAttrs[] = 'supannRessourceEtat';
+    }
 
     return $matchingAttrs;
   }
-- 
GitLab


From 34fe87ec572e668c9e2e06cab2d913b362247b75 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 12:17:58 +0100
Subject: [PATCH 27/36] use utils

---
 plugins/tasks/Notifications.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index b3172ab..3856226 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -114,7 +114,7 @@ class Notifications implements EndpointInterface
     $this->gateway->unsetCountKeys($monitoredSupannResource);
 
     // Find matching attributes between audited and monitored attributes
-    $matchingAttrs = Utils::findMatchingKeys($auditAttributes, $monitoredAttrs);
+    $matchingAttrs = $this->utils->findMatchingKeys($auditAttributes, $monitoredAttrs);
 
     // Verify Supann resource state if applicable
     if ($this->shouldVerifySupannResource($monitoredSupannResource, $auditAttributes)) {
@@ -190,7 +190,7 @@ class Notifications implements EndpointInterface
     }
 
     // Get all the values only of a multidimensional array.
-    $auditedValues = Utils::getArrayValuesRecursive($auditedAttrs);
+    $auditedValues = $this->utils->getArrayValuesRecursive($auditedAttrs);
     return in_array($monitoredSupannState, $auditedValues);
   }
 
-- 
GitLab


From ea674237ca2d23ecf06d257e52f231b815ac0738 Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Tue, 25 Mar 2025 11:56:04 +0100
Subject: [PATCH 28/36] make non static

---
 library/TokenUtils.php | 197 -----------------------------------------
 1 file changed, 197 deletions(-)
 delete mode 100644 library/TokenUtils.php

diff --git a/library/TokenUtils.php b/library/TokenUtils.php
deleted file mode 100644
index d19392c..0000000
--- a/library/TokenUtils.php
+++ /dev/null
@@ -1,197 +0,0 @@
-<?php
-
-class TokenUtils
-{
-    private function __construct() {
-    }
-
-    /**
-     * @param string $userDN
-     * @param int $timeStamp
-     * @return string
-     * @throws Exception
-     */
-    public static function generateToken (string $userDN, int $timeStamp): string
-    {
-        $token = NULL;
-        // Salt has been generated with APG.
-        $salt    = '8onOlEsItKond';
-        $payload = json_encode($userDN . $salt);
-        // This allows the token to be different every time.
-        $time = time();
-
-        // Create hmac with sha256 alg and the key provided for JWT token signature in ENV.
-        $token_hmac = hash_hmac("sha256", $time . $payload, $_ENV["SECRET_KEY"], TRUE);
-
-        // We need to have a token allowed to be used within an URL.
-        $token = Utils::base64urlEncode($token_hmac);
-
-        // Save token within LDAP
-        self::saveTokenInLdap($userDN, $token, $timeStamp);
-
-        return $token;
-    }
-
-    /**
-     * @param string $userDN
-     * @param string $token
-     * NOTE : UID is the full DN of the user. (uid=...).
-     * @param int $days
-     * @return bool
-     * @throws Exception
-     */
-    public static function saveTokenInLdap (string $userDN, string $token, int $days, TaskGateway $gateway): bool
-    {
-        $result = FALSE;
-
-        $currentTimestamp = time();
-        // Calculate the future timestamp by adding days to the current timestamp (We actually adds number of seconds).
-        $futureTimestamp = $currentTimestamp + ($days * 24 * 60 * 60);
-
-        preg_match('/uid=([^,]+),ou=/', $userDN, $matches);
-        $uid = $matches[1];
-        $dn  = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-
-        $ldap_entry["objectClass"]      = ['top', 'fdTokenEntry'];
-        $ldap_entry["fdTokenUserDN"]    = $userDN;
-        $ldap_entry["fdTokenType"]      = 'reminder';
-        $ldap_entry["fdToken"]          = $token;
-        $ldap_entry["fdTokenTimestamp"] = $futureTimestamp;
-        $ldap_entry["cn"]               = $uid;
-
-        // set the dn for the token, only take what's between "uid=" and ",ou="
-
-
-        // Verify if token ou branch exists
-        if (!self::tokenBranchExist('ou=tokens' . ',' . $_ENV["LDAP_BASE"])) {
-            // Create the branch
-            self::createBranchToken();
-        }
-
-        // The user token DN creation
-        $userTokenDN = 'cn=' . $uid . ',ou=tokens' . ',' . $_ENV["LDAP_BASE"];
-        // Verify if a token already exists for specified user and remove it to create new one correctly.
-        if (self::tokenBranchExist($userTokenDN)) {
-            // Remove the user token
-            self::removeUserToken($userTokenDN);
-        }
-
-        // Add token to LDAP for specific UID
-        try {
-            $result = ldap_add($gateway->ds, $dn, $ldap_entry); // bool returned
-        } catch (Exception $e) {
-            echo json_encode(["Ldap Error - Token could not be saved!" => "$e"]); // string returned
-            exit;
-        }
-
-        return $result;
-    }
-
-    /**
-     * @param int $subTaskCall
-     * @param int $firstCall
-     * @param int $secondCall
-     * @return int
-     * Note : Simply return the difference between first and second call. (First call can be null).
-     */
-    public static function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int
-    {
-        // if firstCall is empty, secondCall is the timestamp expiry for the token.
-        $result = $secondCall;
-
-        if (!empty($firstCall)) {
-            // Verification if the subTask is the second reminder or the first reminder.
-            if ($subTaskCall === $firstCall) {
-                $result = $firstCall - $secondCall;
-            }
-        }
-
-        return $result;
-    }
-
-    /**
-     * @param $userTokenDN
-     * @return void
-     * Note : Simply remove the token for specific user DN
-     */
-    public static function removeUserToken ($userTokenDN, TaskGateway $gateway): void
-    {
-        // Add token to LDAP for specific UID
-        try {
-            $result = ldap_delete($gateway->ds, $userTokenDN); // bool returned
-        } catch (Exception $e) {
-            echo json_encode(["Ldap Error - User token could not be removed!" => "$e"]); // string returned
-            exit;
-        }
-    }
-
-    /**
-     * Create ou=pluginManager LDAP branch
-     * @throws Exception
-     */
-    public static function createBranchToken (TaskGateway $gateway): void
-    {
-        try {
-            ldap_add(
-                $gateway->ds, 'ou=tokens' . ',' . $_ENV["LDAP_BASE"],
-                [
-                    'ou'          => 'tokens',
-                    'objectClass' => 'organizationalUnit',
-                ]
-            );
-        } catch (Exception $e) {
-
-            echo json_encode(["Ldap Error - Impossible to create the token branch" => "$e"]); // string returned
-            exit;
-        }
-    }
-
-    /**
-     * @param string $token
-     * @param array $mailTemplateForm
-     * @param string $taskDN
-     * @return array
-     */
-    public static function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array
-    {
-        //Only take the cn of the main task name :
-        preg_match('/cn=([^,]+),ou=/', $taskDN, $matches);
-        $taskName = $matches[1];
-
-        // Remove the API URI
-        $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']);
-        $url        = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName;
-
-        $mailTemplateForm['body'] .= $url;
-
-        return $mailTemplateForm;
-    }
-
-    /**
-     * @param string $dn
-     * @return bool
-     * Note : Simply inspect if the branch for token is existing.
-     */
-    public static function tokenBranchExist (string $dn, TaskGateway $gateway): bool
-    {
-        $result = FALSE;
-
-        try {
-            $search = ldap_search($gateway->ds, $dn, "(objectClass=*)");
-            // Check if the search was successful
-            if ($search) {
-                // Get the number of entries found
-                $entries = ldap_get_entries($gateway->ds, $search);
-
-                // If entries are found, set result to true
-                if ($entries["count"] > 0) {
-                    $result = TRUE;
-                }
-            }
-        } catch (Exception $e) {
-            $result = FALSE;
-        }
-
-        return $result;
-    }
-}
\ No newline at end of file
-- 
GitLab


From b180c86d9dc5f69fcf0d7ab853ab6adf3fbf994d Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Mon, 24 Mar 2025 10:46:00 +0000
Subject: [PATCH 29/36] :sparkles: Feat(gateway) - simply add gateway to get
 methods

---
 .gitignore                 | 7 +++++++
 library/TaskController.php | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index a474600..84558c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,9 @@
 .vendor/
 .composer.lock
+filelist
+phpstan.neon
+.idea/fusiondirectory-orchestrator.iml
+.idea/modules.xml
+.idea/php.xml
+.idea/vcs.xml
+.idea/codeStyles/codeStyleConfig.xml
diff --git a/library/TaskController.php b/library/TaskController.php
index 849f5b6..2b4ddf3 100644
--- a/library/TaskController.php
+++ b/library/TaskController.php
@@ -55,7 +55,7 @@ class TaskController
           switch ($objectType) {
             case $objectType:
               if (class_exists($objectType)) {
-                $endpoint = new $objectType;
+                $endpoint = new $objectType($this->gateway);
                 $result   = $endpoint->processEndPointGet();
               }
               break;
-- 
GitLab


From 70656e85e2b4aa054b1a191b4a490cc45473fafd Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Mon, 24 Mar 2025 15:21:23 +0000
Subject: [PATCH 30/36] :sparkles: feat(archive) - implement Archive endpoint
 with GET, PATCH, POST, and DELETE methods

---
 plugins/tasks/Archive.php | 98 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 plugins/tasks/Archive.php

diff --git a/plugins/tasks/Archive.php b/plugins/tasks/Archive.php
new file mode 100644
index 0000000..e7838f8
--- /dev/null
+++ b/plugins/tasks/Archive.php
@@ -0,0 +1,98 @@
+<?php
+
+class Archive implements EndpointInterface
+{
+  private TaskGateway $gateway;
+
+  public function __construct(TaskGateway $gateway)
+  {
+    $this->gateway = $gateway;
+  }
+
+  /**
+   * @return array
+   * Part of the interface of orchestrator plugin to treat GET method
+   */
+  public function processEndPointGet(): array
+  {
+    // Retrieve tasks of type 'archive'
+    return $this->gateway->getObjectTypeTask('archive');
+  }
+
+  /**
+   * @param array|null $data
+   * @return array
+   * @throws Exception
+   * Note: Part of the interface of orchestrator plugin to treat PATCH method
+   */
+  public function processEndPointPatch(array $data = NULL): array
+  {
+    $result = [];
+    $archiveTasks = $this->gateway->getObjectTypeTask('archive');
+
+    // Initialize the webservice object
+    // TODO
+    $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/archive', 'POST');
+    $webservice->setCurlSettings();
+
+    foreach ($archiveTasks as $task) {
+      // Verify the task status and schedule
+      if ($this->gateway->statusAndScheduleCheck($task)) {
+        // Retrieve the user's supannAccountStatus
+        $userStatus = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
+
+        // Check if the user meets the "toBeArchived" condition
+        // TODO
+        if ($userStatus === 'toBeArchived') {
+          // Trigger the archive method via the webservice
+          $archiveResult = $webservice->triggerArchive($task['fdtasksgranulardn'][0]);
+
+          // Update the task status based on the result
+          if ($archiveResult === TRUE) {
+            $result[$task['dn']]['result'] = "User successfully archived.";
+            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2'); // Mark task as completed
+          } else {
+            $result[$task['dn']]['result'] = "Error archiving user.";
+            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '3'); // Mark task as failed
+          }
+        } else {
+          $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
+        }
+      }
+    }
+
+    return $result;
+  }
+
+  /**
+   * @param array|null $data
+   * @return array
+   * Note: Part of the interface of orchestrator plugin to treat POST method
+   */
+  public function processEndPointPost(array $data = NULL): array
+  {
+    return [];
+  }
+
+  /**
+   * @param array|null $data
+   * @return array
+   * Note: Part of the interface of orchestrator plugin to treat DELETE method
+   */
+  public function processEndPointDelete(array $data = NULL): array
+  {
+    return [];
+  }
+
+  /**
+   * Retrieve the supannAccountStatus of a user
+   * @param string $userDn
+   * @return string|null
+   */
+  private function getUserSupannAccountStatus(string $userDn): ?string
+  {
+    // Logic to retrieve the supannAccountStatus attribute of the user
+    $user = $this->gateway->getLdapEntry($userDn, ['supannAccountStatus']);
+    return $user['supannAccountStatus'][0] ?? NULL;
+  }
+}
\ No newline at end of file
-- 
GitLab


From c302e724d14ccfbbca799cb01228a2f795f621d9 Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Mon, 24 Mar 2025 16:44:58 +0000
Subject: [PATCH 31/36] :sparkles: feat(archive) - enhance Archive
 functionality with WebServiceCall integration and improved error handling

---
 .idea/.gitignore          |  8 ++++++++
 plugins/tasks/Archive.php | 30 ++++++++++++++++++++----------
 2 files changed, 28 insertions(+), 10 deletions(-)
 create mode 100755 .idea/.gitignore

diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100755
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/plugins/tasks/Archive.php b/plugins/tasks/Archive.php
index e7838f8..77492f8 100644
--- a/plugins/tasks/Archive.php
+++ b/plugins/tasks/Archive.php
@@ -1,5 +1,7 @@
 <?php
 
+use FusionDirectory\Rest\WebServiceCall;
+
 class Archive implements EndpointInterface
 {
   private TaskGateway $gateway;
@@ -30,11 +32,6 @@ class Archive implements EndpointInterface
     $result = [];
     $archiveTasks = $this->gateway->getObjectTypeTask('archive');
 
-    // Initialize the webservice object
-    // TODO
-    $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/archive', 'POST');
-    $webservice->setCurlSettings();
-
     foreach ($archiveTasks as $task) {
       // Verify the task status and schedule
       if ($this->gateway->statusAndScheduleCheck($task)) {
@@ -42,19 +39,32 @@ class Archive implements EndpointInterface
         $userStatus = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
 
         // Check if the user meets the "toBeArchived" condition
-        // TODO
         if ($userStatus === 'toBeArchived') {
-          // Trigger the archive method via the webservice
-          $archiveResult = $webservice->triggerArchive($task['fdtasksgranulardn'][0]);
+          // Construct the archive URL
+          $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
+
+          // Initialize the WebServiceCall object
+          $webServiceCall = new WebServiceCall($archiveUrl, 'POST');
+          $webServiceCall->setCurlSettings();
 
-          // Update the task status based on the result
-          if ($archiveResult === TRUE) {
+          // Execute the request
+          $response = curl_exec($webServiceCall->ch);
+
+          // Handle any cURL errors
+          $webServiceCall->handleCurlError($webServiceCall->ch);
+
+          // Decode and process the response
+          $responseData = json_decode($response, true);
+          if ($responseData && isset($responseData['success']) && $responseData['success'] === true) {
             $result[$task['dn']]['result'] = "User successfully archived.";
             $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2'); // Mark task as completed
           } else {
             $result[$task['dn']]['result'] = "Error archiving user.";
             $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '3'); // Mark task as failed
           }
+
+          // Close the cURL resource
+          curl_close($webServiceCall->ch);
         } else {
           $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
         }
-- 
GitLab


From a6cb8a2bd311287c1b8681ed4d0c99d5603b84a1 Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Tue, 25 Mar 2025 00:40:12 +0000
Subject: [PATCH 32/36] :sparkles: feat(archive) - refactor archiving logic to
 streamline user status checks and enhance error handling (still error from
 API)

---
 plugins/tasks/Archive.php | 107 ++++++++++++++++++++++++--------------
 1 file changed, 69 insertions(+), 38 deletions(-)

diff --git a/plugins/tasks/Archive.php b/plugins/tasks/Archive.php
index 77492f8..f2bd8fb 100644
--- a/plugins/tasks/Archive.php
+++ b/plugins/tasks/Archive.php
@@ -32,43 +32,45 @@ class Archive implements EndpointInterface
     $result = [];
     $archiveTasks = $this->gateway->getObjectTypeTask('archive');
 
-    foreach ($archiveTasks as $task) {
-      // Verify the task status and schedule
-      if ($this->gateway->statusAndScheduleCheck($task)) {
-        // Retrieve the user's supannAccountStatus
-        $userStatus = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
-
-        // Check if the user meets the "toBeArchived" condition
-        if ($userStatus === 'toBeArchived') {
-          // Construct the archive URL
-          $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
-
-          // Initialize the WebServiceCall object
-          $webServiceCall = new WebServiceCall($archiveUrl, 'POST');
-          $webServiceCall->setCurlSettings();
-
-          // Execute the request
-          $response = curl_exec($webServiceCall->ch);
-
-          // Handle any cURL errors
-          $webServiceCall->handleCurlError($webServiceCall->ch);
-
-          // Decode and process the response
-          $responseData = json_decode($response, true);
-          if ($responseData && isset($responseData['success']) && $responseData['success'] === true) {
-            $result[$task['dn']]['result'] = "User successfully archived.";
-            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2'); // Mark task as completed
-          } else {
-            $result[$task['dn']]['result'] = "Error archiving user.";
-            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '3'); // Mark task as failed
-          }
+    // Initialize the WebServiceCall object for login
+    $webServiceCall = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST');
+    $webServiceCall->setCurlSettings(); // Perform login and set the token
 
-          // Close the cURL resource
-          curl_close($webServiceCall->ch);
-        } else {
-          $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
+    foreach ($archiveTasks as $task) {
+        try {
+            if (!$this->gateway->statusAndScheduleCheck($task)) {
+                // Skip this task if it does not meet the status and schedule criteria
+                continue;
+            }
+
+            // Receive null or 'toBeArchived'
+            $supannState = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
+
+            if ($supannState !== 'toBeArchived') {
+                // The task does not meet the criteria for archiving and can therefore be suppressed
+                $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
+                $this->gateway->removeSubTask($task['dn']);
+                continue;
+            }
+
+            // Set the archive endpoint and method using the same WebServiceCall object
+            $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
+            $webServiceCall->setCurlSettings($archiveUrl, [], 'POST'); // Update settings for the archive request
+            $response = $webServiceCall->execute();
+
+            print_r([$response]);
+              exit;
+
+            if (isset($response['success']) && $response['success'] === true) {
+                $result[$task['dn']]['result'] = "User successfully archived.";
+                $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2');
+            } else {
+                throw new Exception("Invalid API response format");
+            }
+        } catch (Exception $e) {
+            $result[$task['dn']]['result'] = "Error archiving user: " . $e->getMessage();
+            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $e->getMessage());
         }
-      }
     }
 
     return $result;
@@ -101,8 +103,37 @@ class Archive implements EndpointInterface
    */
   private function getUserSupannAccountStatus(string $userDn): ?string
   {
-    // Logic to retrieve the supannAccountStatus attribute of the user
-    $user = $this->gateway->getLdapEntry($userDn, ['supannAccountStatus']);
-    return $user['supannAccountStatus'][0] ?? NULL;
+      $supannState = $this->gateway->getLdapTasks(
+          '(objectClass=supannPerson)',
+          ['supannRessourceEtatDate'],
+          '',
+          $userDn
+      );
+  
+      if ($this->hasToBeArchived($supannState)) {
+          return 'toBeArchived';
+      }
+  
+      return null;
+  }
+
+  private function hasToBeArchived(array $supannState): bool
+  {
+      if (!isset($supannState[0]['supannressourceetatdate']) || !is_array($supannState[0]['supannressourceetatdate'])) {
+          return false;
+      }
+
+      foreach ($supannState[0]['supannressourceetatdate'] as $key => $value) {
+          // Skip non-numeric keys (e.g., 'count')
+          if (!is_numeric($key)) {
+              continue;
+          }
+
+          if (strpos($value, '{COMPTE}I:toBeArchived') !== false) {
+              return true;
+          }
+      }
+
+      return false;
   }
 }
\ No newline at end of file
-- 
GitLab


From 7e0f72fd81c7e038cb1e444a4f315d17209ae106 Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Tue, 25 Mar 2025 11:57:08 +0000
Subject: [PATCH 33/36] :sparkles: feat(archive) - update archive request
 settings and improve error handling for unexpected HTTP status codes

---
 plugins/tasks/Archive.php | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/plugins/tasks/Archive.php b/plugins/tasks/Archive.php
index f2bd8fb..593c99d 100644
--- a/plugins/tasks/Archive.php
+++ b/plugins/tasks/Archive.php
@@ -55,17 +55,15 @@ class Archive implements EndpointInterface
 
             // Set the archive endpoint and method using the same WebServiceCall object
             $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
-            $webServiceCall->setCurlSettings($archiveUrl, [], 'POST'); // Update settings for the archive request
+            $webServiceCall->setCurlSettings($archiveUrl, NULL, 'POST'); // Update settings for the archive request
             $response = $webServiceCall->execute();
 
-            print_r([$response]);
-              exit;
-
-            if (isset($response['success']) && $response['success'] === true) {
+            // Check if the HTTP status code is 204
+            if ($webServiceCall->getHttpStatusCode() === 204) {
                 $result[$task['dn']]['result'] = "User successfully archived.";
                 $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2');
             } else {
-                throw new Exception("Invalid API response format");
+                throw new Exception("Unexpected HTTP status code: " . $webServiceCall->getHttpStatusCode());
             }
         } catch (Exception $e) {
             $result[$task['dn']]['result'] = "Error archiving user: " . $e->getMessage();
-- 
GitLab


From 4a63edcaf44cd1cef378c9869196f5258b827e0a Mon Sep 17 00:00:00 2001
From: Thibault Dockx <thibault.dockx@fusiondirectory.org>
Date: Tue, 25 Mar 2025 15:17:33 +0000
Subject: [PATCH 34/36] :art: style(archive) - apply consistent spacing in
 method signatures and improve code readability

---
 plugins/tasks/Archive.php | 112 +++++++++++++++++++-------------------
 1 file changed, 56 insertions(+), 56 deletions(-)

diff --git a/plugins/tasks/Archive.php b/plugins/tasks/Archive.php
index 593c99d..2ef3e2f 100644
--- a/plugins/tasks/Archive.php
+++ b/plugins/tasks/Archive.php
@@ -6,7 +6,7 @@ class Archive implements EndpointInterface
 {
   private TaskGateway $gateway;
 
-  public function __construct(TaskGateway $gateway)
+  public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
   }
@@ -15,7 +15,7 @@ class Archive implements EndpointInterface
    * @return array
    * Part of the interface of orchestrator plugin to treat GET method
    */
-  public function processEndPointGet(): array
+  public function processEndPointGet (): array
   {
     // Retrieve tasks of type 'archive'
     return $this->gateway->getObjectTypeTask('archive');
@@ -27,7 +27,7 @@ class Archive implements EndpointInterface
    * @throws Exception
    * Note: Part of the interface of orchestrator plugin to treat PATCH method
    */
-  public function processEndPointPatch(array $data = NULL): array
+  public function processEndPointPatch (array $data = NULL): array
   {
     $result = [];
     $archiveTasks = $this->gateway->getObjectTypeTask('archive');
@@ -37,38 +37,38 @@ class Archive implements EndpointInterface
     $webServiceCall->setCurlSettings(); // Perform login and set the token
 
     foreach ($archiveTasks as $task) {
-        try {
-            if (!$this->gateway->statusAndScheduleCheck($task)) {
-                // Skip this task if it does not meet the status and schedule criteria
-                continue;
-            }
-
-            // Receive null or 'toBeArchived'
-            $supannState = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
-
-            if ($supannState !== 'toBeArchived') {
-                // The task does not meet the criteria for archiving and can therefore be suppressed
-                $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
-                $this->gateway->removeSubTask($task['dn']);
-                continue;
-            }
-
-            // Set the archive endpoint and method using the same WebServiceCall object
-            $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
-            $webServiceCall->setCurlSettings($archiveUrl, NULL, 'POST'); // Update settings for the archive request
-            $response = $webServiceCall->execute();
-
-            // Check if the HTTP status code is 204
-            if ($webServiceCall->getHttpStatusCode() === 204) {
-                $result[$task['dn']]['result'] = "User successfully archived.";
-                $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2');
-            } else {
-                throw new Exception("Unexpected HTTP status code: " . $webServiceCall->getHttpStatusCode());
-            }
-        } catch (Exception $e) {
-            $result[$task['dn']]['result'] = "Error archiving user: " . $e->getMessage();
-            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $e->getMessage());
+      try {
+        if (!$this->gateway->statusAndScheduleCheck($task)) {
+            // Skip this task if it does not meet the status and schedule criteria
+            continue;
         }
+
+          // Receive null or 'toBeArchived'
+          $supannState = $this->getUserSupannAccountStatus($task['fdtasksgranulardn'][0]);
+
+        if ($supannState !== 'toBeArchived') {
+            // The task does not meet the criteria for archiving and can therefore be suppressed
+            $result[$task['dn']]['result'] = "User does not meet the criteria for archiving.";
+            $this->gateway->removeSubTask($task['dn']);
+            continue;
+        }
+
+          // Set the archive endpoint and method using the same WebServiceCall object
+          $archiveUrl = $_ENV['FUSION_DIRECTORY_API_URL'] . '/archive/user/' . rawurlencode($task['fdtasksgranulardn'][0]);
+          $webServiceCall->setCurlSettings($archiveUrl, NULL, 'POST'); // Update settings for the archive request
+          $response = $webServiceCall->execute();
+
+          // Check if the HTTP status code is 204
+        if ($webServiceCall->getHttpStatusCode() === 204) {
+            $result[$task['dn']]['result'] = "User successfully archived.";
+            $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2');
+        } else {
+            throw new Exception("Unexpected HTTP status code: " . $webServiceCall->getHttpStatusCode());
+        }
+      } catch (Exception $e) {
+          $result[$task['dn']]['result'] = "Error archiving user: " . $e->getMessage();
+          $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $e->getMessage());
+      }
     }
 
     return $result;
@@ -79,7 +79,7 @@ class Archive implements EndpointInterface
    * @return array
    * Note: Part of the interface of orchestrator plugin to treat POST method
    */
-  public function processEndPointPost(array $data = NULL): array
+  public function processEndPointPost (array $data = NULL): array
   {
     return [];
   }
@@ -89,7 +89,7 @@ class Archive implements EndpointInterface
    * @return array
    * Note: Part of the interface of orchestrator plugin to treat DELETE method
    */
-  public function processEndPointDelete(array $data = NULL): array
+  public function processEndPointDelete (array $data = NULL): array
   {
     return [];
   }
@@ -99,7 +99,7 @@ class Archive implements EndpointInterface
    * @param string $userDn
    * @return string|null
    */
-  private function getUserSupannAccountStatus(string $userDn): ?string
+  private function getUserSupannAccountStatus (string $userDn): ?string
   {
       $supannState = $this->gateway->getLdapTasks(
           '(objectClass=supannPerson)',
@@ -107,31 +107,31 @@ class Archive implements EndpointInterface
           '',
           $userDn
       );
-  
-      if ($this->hasToBeArchived($supannState)) {
-          return 'toBeArchived';
-      }
-  
-      return null;
+
+    if ($this->hasToBeArchived($supannState)) {
+        return 'toBeArchived';
+    }
+
+      return NULL;
   }
 
-  private function hasToBeArchived(array $supannState): bool
+  private function hasToBeArchived (array $supannState): bool
   {
-      if (!isset($supannState[0]['supannressourceetatdate']) || !is_array($supannState[0]['supannressourceetatdate'])) {
-          return false;
-      }
+    if (!isset($supannState[0]['supannressourceetatdate']) || !is_array($supannState[0]['supannressourceetatdate'])) {
+        return FALSE;
+    }
 
-      foreach ($supannState[0]['supannressourceetatdate'] as $key => $value) {
-          // Skip non-numeric keys (e.g., 'count')
-          if (!is_numeric($key)) {
-              continue;
-          }
+    foreach ($supannState[0]['supannressourceetatdate'] as $key => $value) {
+        // Skip non-numeric keys (e.g., 'count')
+      if (!is_numeric($key)) {
+          continue;
+      }
 
-          if (strpos($value, '{COMPTE}I:toBeArchived') !== false) {
-              return true;
-          }
+      if (strpos($value, '{COMPTE}I:toBeArchived') !== FALSE) {
+          return TRUE;
       }
+    }
 
-      return false;
+      return FALSE;
   }
 }
\ No newline at end of file
-- 
GitLab


From ea4fc2c287a4743900e2afb5aa7591cbf653eaaa Mon Sep 17 00:00:00 2001
From: Benoit Mortier <benoit.mortier@fusiondirectory.org>
Date: Tue, 25 Mar 2025 18:11:35 +0100
Subject: [PATCH 35/36] :ambulance: fix(centos) remove centos from the builds

Signed-off-by: Benoit Mortier <benoit.mortier@fusiondirectory.org>
---
 .gitlab-ci.yml | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1e84902..8356ce6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -93,10 +93,3 @@ trigger-ci-ubuntu-focal:
     project: ubuntu/focal-fusiondirectory-orchestrator-dev
     branch: "main"
 
-trigger-ci-centos-7:
-  stage: trigger
-  only:
-    - dev
-  trigger:
-    project: centos/centos7-fusiondirectory-orchestrator-dev
-    branch: "main"
-- 
GitLab


From 1a411f0ab44c88476ae71bb431dc46523653bf5b Mon Sep 17 00:00:00 2001
From: Oana-Eliza Alexa <43857161+alexaeliza@users.noreply.github.com>
Date: Thu, 27 Mar 2025 14:30:05 +0100
Subject: [PATCH 36/36] use utils

---
 plugins/tasks/Notifications.php | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php
index 3856226..a01196c 100644
--- a/plugins/tasks/Notifications.php
+++ b/plugins/tasks/Notifications.php
@@ -6,11 +6,13 @@ class Notifications implements EndpointInterface
   private TaskGateway $gateway;
   private string $errorMessage = 'No matching audited attributes with monitored attributes, safely removed!';
   private Utils $utils;
+  private MailUtils $mailUtils;
 
   public function __construct (TaskGateway $gateway)
   {
     $this->gateway = $gateway;
     $this->utils = new Utils();
+    $this->mailUtils = new MailUtils();
   }
 
   /**
@@ -320,19 +322,14 @@ class Notifications implements EndpointInterface
 
     foreach ($notifications as $data) {
       $numberOfRecipients = count($data['mailForm']['recipients']);
-
-      $mail_controller = new \FusionDirectory\Mail\MailLib(
-        $data['mailForm']['setFrom'],
-        NULL,
-        $data['mailForm']['recipients'],
-        $data['mailForm']['body'],
-        $data['mailForm']['signature'],
-        $data['mailForm']['subject'],
-        $data['mailForm']['receipt'],
-        NULL
-      );
-
-      $mailSentResult = $mail_controller->sendMail();
+      $setFrom = $data['mailForm']['setFrom'];
+      $recipients = $data['mailForm']['recipients'];
+      $body  = $data['mailForm']['body'];
+      $signature = $data['mailForm']['signature'];
+      $subject = $data['mailForm']['subject'];
+      $receipt = $data['mailForm']['receipt'];
+
+      $mailSentResult = $this->mailUtils->sendMail($setFrom, NULL, $recipients, $body, $signature, $subject, $receipt, NULL);
       $result[]       = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf);
 
       // Verification anti-spam max mails to be sent and quit loop if matched.
-- 
GitLab