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