From 4fcdf119156f10eadedae4109b8a5a3ad392d9c9 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 1 Apr 2024 12:32:24 +0100 Subject: [PATCH 001/111] :sparkles: Feat(TaskGW) - Adds UTC timezone to lastExec Adds UTC timezone to lastExec --- library/TaskGateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 1820d49..d846286 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -307,7 +307,7 @@ class TaskGateway // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); // Is used to verify cyclic schedule with date format. - $now = new DateTime(); + $now = new DateTime('now', new DateTimeZone('UTC')); foreach ($tasks as $task) { // Transform schedule time (it is a simple string) -- GitLab From ab50751742e6765298bcef4ad4479558289e8acb Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 4 Apr 2024 11:48:59 +0100 Subject: [PATCH 002/111] :sparkles: Feat(TaskGW) - First step towards notifications processing First step towards notifications processing --- library/TaskController.php | 3 +++ library/TaskGateway.php | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/library/TaskController.php b/library/TaskController.php index af549d8..de57860 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -48,6 +48,9 @@ class TaskController case 'activateCyclicTasks': $result = $this->gateway->activateCyclicTasks(); break; + case 'notifications': + $result = $this->gateway->processNotifications($task); + break; } if (!empty($result)) { echo json_encode($result, JSON_PRETTY_PRINT); diff --git a/library/TaskGateway.php b/library/TaskGateway.php index d846286..34592c3 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -31,6 +31,11 @@ class TaskGateway unset($list_tasks["count"]); break; + case "notifications": + $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Notifications))"); + unset($list_tasks["count"]); + break; + case "removeSubTasks": case "activateCyclicTasks": // No need to get any parent tasks here, but to note break logic - we will return an array. @@ -52,6 +57,31 @@ class TaskGateway return $list_tasks; } + public function processNotifications(array $notificationsSubTasks): array + { + $result = []; + foreach ($notificationsSubTasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks + if ($task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0])) { + + // Retrieve data from the main task + $notificationsMainTask = $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', + 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksLastExec'], + '', $task['fdtasksgranularmaster'][0]); + + // Retrieve audit data for specified user DN + $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent)(fdAuditObjectType='.$task['fdtasksgranulardn'][0].'))', + ['fdTasksNotificationsListOfRecipientsMails', 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksLastExec'], + '', $task['fdtasksgranularmaster'][0]); + + //debugging + $result[] = $notificationsMainTask; + } + } + + return $result; + } + /** * @param array $list_tasks * @return array -- GitLab From 871b2cc5d61ef4afe0eebb1cf4c9e2382305a21b Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 4 Apr 2024 17:31:31 +0100 Subject: [PATCH 003/111] :sparkles: Feat(TaskGW) - Matching monitored attributes functional Matching monitored attributes operational again audited attributes --- library/TaskGateway.php | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 34592c3..a2bfadb 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -66,16 +66,32 @@ class TaskGateway // Retrieve data from the main task $notificationsMainTask = $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', - 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksLastExec'], + 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate'], '', $task['fdtasksgranularmaster'][0]); - // Retrieve audit data for specified user DN - $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent)(fdAuditObjectType='.$task['fdtasksgranulardn'][0].'))', - ['fdTasksNotificationsListOfRecipientsMails', 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksLastExec'], - '', $task['fdtasksgranularmaster'][0]); + // Retrieve audit data attributes from the list of references set in the sub-task + if (!empty($task['fdtasksgranularref'])){ + // Ldap always return a count which we have to remove. + unset($task['fdtasksgranularref']['count']); + + foreach ($task['fdtasksgranularref'] as $auditDN) { + $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', + ['fdAuditAttributes'],'', $auditDN); + } - //debugging - $result[] = $notificationsMainTask; + // Clear and compact received results from above ldap search + $auditAttributes = $auditInformation[0]['fdauditattributes']; + unset($auditAttributes['count']); + $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; + unset($monitoredAttrs['count']); + + // Verify if there is a match between audited attributes and monitored attributes from main task. + $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); + if (!empty($matchingAttrs)) { + // Fill an array with UID of audited user and related matching attributes + $result[$task['fdtasksgranulardn'][0]] = $matchingAttrs; + } + } } } -- GitLab From 4f84156d8c14a2fc7193cee6a4a68cd8d42e4be9 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 4 Apr 2024 18:53:40 +0100 Subject: [PATCH 004/111] :sparkles: Feat(TaskGW) - All data per main task - before refactor All data per main task - before refactor --- library/TaskGateway.php | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index a2bfadb..3d858bd 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -57,10 +57,14 @@ class TaskGateway return $list_tasks; } - public function processNotifications(array $notificationsSubTasks): array + public function processNotifications (array $notificationsSubTasks): array { $result = []; + // It will contain all required notifications to be sent per main task. + $notifications = []; + foreach ($notificationsSubTasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks if ($task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0])) { @@ -69,14 +73,33 @@ class TaskGateway 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate'], '', $task['fdtasksgranularmaster'][0]); + // Generate email configuration for each result of subtasks having the same main task. + // The loop will overwrite static information, but each main task contains the same static information. + if (!empty($notificationsMainTask[0]['fdtasksnotificationsmailtemplate'])) { + + $mainTaskName = $task['fdtasksgranularmaster'][0]; + $mailTemplate = $notificationsMainTask[0]['fdtasksnotificationsmailtemplate'][0]; + + $mailInfos = $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplate); + $mailContent = $mailInfos[0]; + + // Set the notification array with all required variable for all sub-tasks of same main task origin. + $notifications[$mainTaskName]['setFrom'] = 'toBeDefine@example.com'; // To clearly be retrieved from LDAP. + $notifications[$mainTaskName]['recipients'] = $notificationsMainTask[0]["fdtasksnotificationslistofrecipientsmails"]; + $notifications[$mainTaskName]['body'] = $mailContent["fdmailtemplatebody"][0]; + $notifications[$mainTaskName]['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $notifications[$mainTaskName]['subject'] = $mailContent["fdmailtemplatesubject"][0]; + $notifications[$mainTaskName]['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; + } + // Retrieve audit data attributes from the list of references set in the sub-task - if (!empty($task['fdtasksgranularref'])){ + if (!empty($task['fdtasksgranularref'])) { // Ldap always return a count which we have to remove. unset($task['fdtasksgranularref']['count']); foreach ($task['fdtasksgranularref'] as $auditDN) { $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', - ['fdAuditAttributes'],'', $auditDN); + ['fdAuditAttributes'], '', $auditDN); } // Clear and compact received results from above ldap search @@ -89,12 +112,15 @@ class TaskGateway $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); if (!empty($matchingAttrs)) { // Fill an array with UID of audited user and related matching attributes - $result[$task['fdtasksgranulardn'][0]] = $matchingAttrs; + $notifications[$mainTaskName][$task['fdtasksgranulardn'][0]] = $matchingAttrs; } } } } + if (!empty($notifications)) { + $result[] = $notifications; + } return $result; } -- GitLab From ccefa3e123d6364a111d3a192d8c3b60c6b19487 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 8 Apr 2024 11:14:49 +0100 Subject: [PATCH 005/111] :ambulance: Feat(TaskGW) - verify schedule in UTC Verify schedule in UTC --- library/TaskGateway.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 3d858bd..ac125ff 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -526,7 +526,8 @@ class TaskGateway // Verification of the schedule in complete string format and compare. public function verifySchedule (string $schedule): bool { - $schedule = strtotime($schedule); + // Avoid strtotime evaluating in local time zone and transfer data to UTC + $schedule = strtotime($schedule.' +0000'); if ($schedule < time()) { return TRUE; } -- GitLab From c45373d94a312312bb327c17f71d982a45eb9e06 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 8 Apr 2024 11:41:35 +0100 Subject: [PATCH 006/111] :ambulance: Feat(TaskGW) - reverse changes Reverse changes from previous commit. --- library/TaskGateway.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index ac125ff..3d858bd 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -526,8 +526,7 @@ class TaskGateway // Verification of the schedule in complete string format and compare. public function verifySchedule (string $schedule): bool { - // Avoid strtotime evaluating in local time zone and transfer data to UTC - $schedule = strtotime($schedule.' +0000'); + $schedule = strtotime($schedule); if ($schedule < time()) { return TRUE; } -- GitLab From 8e208ff10f84a46cba7f5d5882de3aa0a6aaf5e9 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 8 Apr 2024 17:55:11 +0100 Subject: [PATCH 007/111] :ambulance: Feat(TaskGW) - Progress Progress - error during task update with CN 2nd arg. --- library/TaskGateway.php | 115 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 9 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 3d858bd..445746c 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -102,8 +102,11 @@ class TaskGateway ['fdAuditAttributes'], '', $auditDN); } + // It is possible that an audit does not contain any attributes changes. + if (!empty($auditInformation[0]['fdauditattributes'])) { + $auditAttributes = $auditInformation[0]['fdauditattributes']; + } // Clear and compact received results from above ldap search - $auditAttributes = $auditInformation[0]['fdauditattributes']; unset($auditAttributes['count']); $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; unset($monitoredAttrs['count']); @@ -112,33 +115,124 @@ class TaskGateway $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); if (!empty($matchingAttrs)) { // Fill an array with UID of audited user and related matching attributes - $notifications[$mainTaskName][$task['fdtasksgranulardn'][0]] = $matchingAttrs; + $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]] = $matchingAttrs; + // Require to be set for updating the status of the task later on. + $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; } } } } if (!empty($notifications)) { - $result[] = $notifications; + $result[] = $this->sendNotificationsMail($notifications); + } + return $result; + } + + protected function sendNotificationsMail (array $notifications): array + { + $result = []; + // Re-use of the same mail processing template logic + $fdTasksConf = $this->getMailObjectConfiguration(); + $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); + + // Each main tasks having notifications required will be processed one by one, resulting in on email per main tasks. + foreach ($notifications as $notification => $data) { + $maxMailsIncrement = 0; + + // unset count from returned ldap values + unset($data['recipients']['count']); + + // This will retrieve the list of subTasks DN required to be updated with a status : + $subTasksDN = []; + //Create the body of the notification mail combining template and members with audited attributes. + foreach ($data['uid'] as $uid => $attributes) { + // Append the UID to the data['body'] string + $data['body'] .= "\nUID: $uid\n"; + // Retrieve the DN of subtasks within array to later update the status of subtasks. + $subTasksDN[] = $attributes['dn']; + //remove the DN from list of attributes + unset($attributes['dn']); + // Iterate over the attributes under the current UID and append each value to the body. + foreach ($attributes as $attr) { + $data['body'] .= "Attribute: $attr\n"; + } + } + + $mail_controller = new MailController( + $data['setFrom'], + null, + $data['recipients'], + $data['body'], + $data['signature'], + $data['subject'], + $data['receipt'], + null + ); + + $mailSentResult = $mail_controller->sendMail(); + + if ($mailSentResult[0] == "SUCCESS") { + + foreach ($subTasksDN as $dn) { + // ERROR HERE WITH THE UPDATE TASKS STATUS !! + $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, '', "2"); + $result[$dn]['mailStatus'] = 'Notification was successfully sent'; + $result[$dn]['updateLastExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); + } + + } else { + foreach ($subTasksDN as $dn) { + $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $mail["cn"][0], $mailSentResult[0]); + $result[$dn]['mailStatus'] = $mailSentResult; + } + } + + // Verification anti-spam max mails to be sent and quit loop if matched. + $maxMailsIncrement += 1; + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } } + return $result; } /** - * @param array $list_tasks * @return array + * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory */ - public function processMailTasks (array $list_tasks): array + private function getMailObjectConfiguration (): array { - $result = []; - $fdTasksConf = $this->getLdapTasks( "(objectClass=fdTasksConf)", ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] ); + return $fdTasksConf; + } + + /** + * @param array $fdTasksConf + * @return int + * Note : Allows a safety check in case mail configuration backed within FD has been missed. + */ + public function returnMaximumMailToBeSend (array $fdTasksConf): int + { // set the maximum mails to be sent to the configured value or 50 if not set. - $maxMailsConfig = $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; + return $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; + } + + /** + * @param array $list_tasks + * @return array + */ + public function processMailTasks (array $list_tasks): array + { + $result = []; + + $fdTasksConf = $this->getMailObjectConfiguration(); + $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); if ($this->verifySpamProtection($fdTasksConf)) { foreach ($list_tasks as $mail) { @@ -585,7 +679,10 @@ class TaskGateway public function updateTaskStatus (string $dn, string $cn, string $status) { // prepare data - $ldap_entry["cn"] = $cn; + if (!empty($dn)) { + $ldap_entry["cn"] = $cn; + } + // Status subject to change $ldap_entry["fdTasksGranularStatus"] = $status; -- GitLab From 6c72587013af9062ff375b565ac7fc7df2ba04de Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 8 Apr 2024 22:26:07 +0100 Subject: [PATCH 008/111] :ambulance: Feat(TaskGW) - Progress 2 First refactor of Notifications processing --- library/TaskGateway.php | 225 ++++++++++++++++++++++++++-------------- 1 file changed, 145 insertions(+), 80 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 445746c..a9a794f 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -57,68 +57,112 @@ class TaskGateway return $list_tasks; } - public function processNotifications (array $notificationsSubTasks): array + /** + * @param array $task + * @return bool + */ + public function statusAndScheduleCheck (array $task): bool { - $result = []; - // It will contain all required notifications to be sent per main task. - $notifications = []; + return $task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0]); + } - foreach ($notificationsSubTasks as $task) { + /** + * @param string $mainTaskDn + * @return array + */ + public function getNotificationsMainTask (string $mainTaskDn): array + { + // Retrieve data from the main task + $notificationsMainTask = $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', + 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], + '', $mainTaskDn); - // If the tasks must be treated - status and scheduled - process the sub-tasks - if ($task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0])) { + return $notificationsMainTask; + } - // Retrieve data from the main task - $notificationsMainTask = $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', - 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate'], - '', $task['fdtasksgranularmaster'][0]); + public function retrieveMailTemplateInfos (string $templateName): array + { + return $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $templateName); + } + + private function generateMainTaskMailTemplate (array $mainTask, string $mainTaskName): array + { + // Generate email configuration for each result of subtasks having the same main task. + $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; + unset($recipients['count']); + $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; + $mailTemplateName = $mainTask[0]['fdtasksnotificationsmailtemplate'][0]; + + $mailInfos = $this->retrieveMailTemplateInfos($mailTemplateName); + $mailContent = $mailInfos[0]; + + // Set the notification array with all required variable for all sub-tasks of same main task origin. + $mailForm[$mainTaskName]['setFrom'] = $sender; + $mailForm[$mainTaskName]['recipients'] = $recipients; + $mailForm[$mainTaskName]['body'] = $mailContent["fdmailtemplatebody"][0]; + $mailForm[$mainTaskName]['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $mailForm[$mainTaskName]['subject'] = $mailContent["fdmailtemplatesubject"][0]; + $mailForm[$mainTaskName]['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; + + return $mailForm; + } - // Generate email configuration for each result of subtasks having the same main task. - // The loop will overwrite static information, but each main task contains the same static information. - if (!empty($notificationsMainTask[0]['fdtasksnotificationsmailtemplate'])) { + protected function retrieveAuditedAttributes (array $notificationTask): array + { + $auditAttributes = []; + // Retrieve audit data attributes from the list of references set in the sub-task + if (!empty($notificationTask['fdtasksgranularref'])) { + // Ldap always return a count which we have to remove. + unset($notificationTask['fdtasksgranularref']['count']); - $mainTaskName = $task['fdtasksgranularmaster'][0]; - $mailTemplate = $notificationsMainTask[0]['fdtasksnotificationsmailtemplate'][0]; + foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { - $mailInfos = $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplate); - $mailContent = $mailInfos[0]; + $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', + ['fdAuditAttributes'], '', $auditDN); + } - // Set the notification array with all required variable for all sub-tasks of same main task origin. - $notifications[$mainTaskName]['setFrom'] = 'toBeDefine@example.com'; // To clearly be retrieved from LDAP. - $notifications[$mainTaskName]['recipients'] = $notificationsMainTask[0]["fdtasksnotificationslistofrecipientsmails"]; - $notifications[$mainTaskName]['body'] = $mailContent["fdmailtemplatebody"][0]; - $notifications[$mainTaskName]['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; - $notifications[$mainTaskName]['subject'] = $mailContent["fdmailtemplatesubject"][0]; - $notifications[$mainTaskName]['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; - } + // It is possible that an audit does not contain any attributes changes, condition is required. + if (!empty($auditInformation[0]['fdauditattributes'])) { + $auditAttributes = $auditInformation[0]['fdauditattributes']; + } + // Clear and compact received results from above ldap search + unset($auditAttributes['count']); + } + return $auditAttributes; + } - // Retrieve audit data attributes from the list of references set in the sub-task - if (!empty($task['fdtasksgranularref'])) { - // Ldap always return a count which we have to remove. - unset($task['fdtasksgranularref']['count']); + public function processNotifications (array $notificationsSubTasks): array + { + $result = []; + // It will contain all required notifications to be sent per main task. + $notifications = []; - foreach ($task['fdtasksgranularref'] as $auditDN) { - $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', - ['fdAuditAttributes'], '', $auditDN); - } + foreach ($notificationsSubTasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks + if ($this->statusAndScheduleCheck($task)) { - // It is possible that an audit does not contain any attributes changes. - if (!empty($auditInformation[0]['fdauditattributes'])) { - $auditAttributes = $auditInformation[0]['fdauditattributes']; - } - // Clear and compact received results from above ldap search - unset($auditAttributes['count']); - $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; - unset($monitoredAttrs['count']); - - // Verify if there is a match between audited attributes and monitored attributes from main task. - $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); - if (!empty($matchingAttrs)) { - // Fill an array with UID of audited user and related matching attributes - $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]] = $matchingAttrs; - // Require to be set for updating the status of the task later on. - $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; - } + // Retrieve data from the main task + $notificationsMainTask = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]); + $notificationsMainTaskName = $task['fdtasksgranularmaster'][0]; + + // Generate the mail form with all mail controller requirements + $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask, $notificationsMainTaskName); + // Simply retrieve the list of audited attributes + $auditAttributes = $this->retrieveAuditedAttributes($task); + + $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; + unset($monitoredAttrs['count']); + + // Verify if there is a match between audited attributes and monitored attributes from main task. + $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); + + //TO CONTINUE DEBUG HERE ! + if (!empty($matchingAttrs)) { + // Fill an array with UID of audited user and related matching attributes + $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]] = $matchingAttrs; + // Require to be set for updating the status of the task later on. + $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; + $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; } } } @@ -126,10 +170,12 @@ class TaskGateway if (!empty($notifications)) { $result[] = $this->sendNotificationsMail($notifications); } + return $result; } - protected function sendNotificationsMail (array $notifications): array + protected + function sendNotificationsMail (array $notifications): array { $result = []; // Re-use of the same mail processing template logic @@ -143,21 +189,22 @@ class TaskGateway // unset count from returned ldap values unset($data['recipients']['count']); - // This will retrieve the list of subTasks DN required to be updated with a status : - $subTasksDN = []; //Create the body of the notification mail combining template and members with audited attributes. foreach ($data['uid'] as $uid => $attributes) { // Append the UID to the data['body'] string $data['body'] .= "\nUID: $uid\n"; // Retrieve the DN of subtasks within array to later update the status of subtasks. - $subTasksDN[] = $attributes['dn']; + $dn[] = $attributes['dn']; + $cn[] = $attributes['cn']; //remove the DN from list of attributes unset($attributes['dn']); + unset($attributes['cn']); // Iterate over the attributes under the current UID and append each value to the body. foreach ($attributes as $attr) { $data['body'] .= "Attribute: $attr\n"; } } + print_r($data); $mail_controller = new MailController( $data['setFrom'], @@ -170,21 +217,25 @@ class TaskGateway null ); - $mailSentResult = $mail_controller->sendMail(); + //$mailSentResult = $mail_controller->sendMail(); if ($mailSentResult[0] == "SUCCESS") { - foreach ($subTasksDN as $dn) { - // ERROR HERE WITH THE UPDATE TASKS STATUS !! - $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, '', "2"); - $result[$dn]['mailStatus'] = 'Notification was successfully sent'; - $result[$dn]['updateLastExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); + foreach ($dn as $index => $dnValue) { + // Access the corresponding element in the $cn array using the same index + $cnValue = $cn[$index]; + // Update task status for the current $dn + $result[$dnValue]['statusUpdate'] = $this->updateTaskStatus($dnValue, $cnValue, "2"); + + $result[$dnValue]['mailStatus'] = 'Notification was successfully sent'; + $result[$dnValue]['updateLastExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); } } else { - foreach ($subTasksDN as $dn) { - $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $mail["cn"][0], $mailSentResult[0]); - $result[$dn]['mailStatus'] = $mailSentResult; + foreach ($dn as $index => $dnValue) { + $cnValue = $cn[$index]; + $result[$dnValue]['statusUpdate'] = $this->updateTaskStatus($dnValue, $cnValue, $mailSentResult[0]); + $result[$dnValue]['mailStatus'] = $mailSentResult; } } @@ -202,7 +253,8 @@ class TaskGateway * @return array * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory */ - private function getMailObjectConfiguration (): array + private + function getMailObjectConfiguration (): array { $fdTasksConf = $this->getLdapTasks( "(objectClass=fdTasksConf)", @@ -217,7 +269,8 @@ class TaskGateway * @return int * Note : Allows a safety check in case mail configuration backed within FD has been missed. */ - public function returnMaximumMailToBeSend (array $fdTasksConf): int + public + function returnMaximumMailToBeSend (array $fdTasksConf): int { // set the maximum mails to be sent to the configured value or 50 if not set. return $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; @@ -227,7 +280,8 @@ class TaskGateway * @param array $list_tasks * @return array */ - public function processMailTasks (array $list_tasks): array + public + function processMailTasks (array $list_tasks): array { $result = []; @@ -313,7 +367,8 @@ class TaskGateway * @return array * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. */ - public function processLifeCycleTasks (array $list_tasks): array + public + function processLifeCycleTasks (array $list_tasks): array { // Array representing the status of the subtask. $result = []; @@ -324,7 +379,7 @@ class TaskGateway foreach ($list_tasks as $task) { // If the tasks must be treated - status and scheduled - process the sub-tasks - if ($task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0])) { + if ($this->statusAndScheduleCheck($task)) { // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes $lifeCycleBehavior = $this->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', @@ -382,7 +437,8 @@ class TaskGateway * @param string $userDN * @return bool|string */ - protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) + protected + function updateLifeCycle (array $lifeCycleBehavior, string $userDN) { // Extracting values of desired post-state behavior $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; @@ -417,7 +473,8 @@ class TaskGateway * @param bool|string $subTaskDn * @return bool|string */ - protected function removeSubTask ($subTaskDn) + protected + function removeSubTask ($subTaskDn) { try { $result = ldap_delete($this->ds, $subTaskDn); @@ -432,7 +489,8 @@ class TaskGateway * @return array * Note Search for all sub-tasks having status equals to 2 (completed). */ - public function removeCompletedTasks (): array + public + function removeCompletedTasks (): array { $result = []; $subTasksCompleted = $this->getLdapTasks( @@ -456,7 +514,8 @@ class TaskGateway * @return array * @throws Exception */ - public function activateCyclicTasks (): array + public + function activateCyclicTasks (): array { $result = []; $tasks = $this->getLdapTasks( @@ -549,7 +608,8 @@ class TaskGateway * if there is indeed a difference and therefore must update the user information. * In case the comparison is impossible due to the use not having a status listed, it will report false. */ - protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool + protected + function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool { $result = FALSE; // Regular expression in order to extract the supann format within an array @@ -598,7 +658,8 @@ class TaskGateway * Note : Method which verify the last executed e-mails sent * Verify if the time interval is respected in order to protect from SPAM */ - public function verifySpamProtection (array $fdTasksConf): bool + public + function verifySpamProtection (array $fdTasksConf): bool { $lastExec = $fdTasksConf[0]["fdtasksconflastexectime"][0] ?? NULL; $spamInterval = $fdTasksConf[0]["fdtasksconfintervalemails"][0] ?? NULL; @@ -617,8 +678,9 @@ class TaskGateway * @param string $schedule * @return bool */ - // Verification of the schedule in complete string format and compare. - public function verifySchedule (string $schedule): bool +// Verification of the schedule in complete string format and compare. + public + function verifySchedule (string $schedule): bool { $schedule = strtotime($schedule); if ($schedule < time()) { @@ -636,7 +698,8 @@ class TaskGateway * @return array * NOTE : Filter in ldap_search cannot be an empty string or NULL, if not filters are required, use (objectClass=*). */ - public function getLdapTasks (string $filter = '', array $attrs = [], string $attachmentsCN = NULL, string $dn = NULL): array + public + function getLdapTasks (string $filter = '', array $attrs = [], string $attachmentsCN = NULL, string $dn = NULL): array { $result = []; @@ -676,7 +739,8 @@ class TaskGateway * @return bool|string * Note : Update the status of the tasks. */ - public function updateTaskStatus (string $dn, string $cn, string $status) + public + function updateTaskStatus (string $dn, string $cn, string $status) { // prepare data if (!empty($dn)) { @@ -701,7 +765,8 @@ class TaskGateway * @return bool|string * Note: Update the attribute lastExecTime from fdTasksConf. */ - public function updateLastMailExecTime (string $dn) + public + function updateLastMailExecTime (string $dn) { // prepare data $ldap_entry["fdTasksConfLastExecTime"] = time(); -- GitLab From 07b2602261d536d9c8ba6951919655f853804454 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 00:51:53 +0100 Subject: [PATCH 009/111] :sparkles: Feat(TaskGW) - Progress 3 Refactoring further, completing notifcations array with enhance values. --- library/TaskGateway.php | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index a9a794f..6fc0f9e 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -156,13 +156,18 @@ class TaskGateway // Verify if there is a match between audited attributes and monitored attributes from main task. $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); - //TO CONTINUE DEBUG HERE ! if (!empty($matchingAttrs)) { // Fill an array with UID of audited user and related matching attributes - $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]] = $matchingAttrs; + $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['attrs'] = $matchingAttrs; + // Require to be set for updating the status of the task later on. - $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; - $notifications[$mainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; + $notifications[$notificationsMainTaskName][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; + $notifications[$notificationsMainTaskName][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; + + // shifting the array as first key is identical to main task name + $notifications[$notificationsMainTaskName]['mailForm'] = array_shift($mailTemplateForm); + //Overwrite array notifications with complementing mail form body with uid and related attributes. + $notifications = $this->completeNotificationsBody($notifications); } } } @@ -174,8 +179,31 @@ class TaskGateway return $result; } - protected - function sendNotificationsMail (array $notifications): array + /** + * @param array $notifications + * @return array + * Note : This method is present to add to the mailForm body the proper uid and attrs info. + */ + private function completeNotificationsBody (array $notifications): array + { + // Iterate through each uid and its attrs + $uidAttrsText = []; + foreach ($notifications[key($notifications)]['uid'] as $uidKey => $uidValue) { + $uidName = $uidKey; + $attrs = []; + foreach ($uidValue['attrs'] as $attr) { + $attrs[] = $attr; + } + $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; + } + + // Add uid names and related attrs to mailForm['body'] + $notifications[key($notifications)]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); + + return $notifications; + } + + protected function sendNotificationsMail (array $notifications): array { $result = []; // Re-use of the same mail processing template logic @@ -186,9 +214,7 @@ class TaskGateway foreach ($notifications as $notification => $data) { $maxMailsIncrement = 0; - // unset count from returned ldap values - unset($data['recipients']['count']); - + //CONTINUE DEBUG HERE //Create the body of the notification mail combining template and members with audited attributes. foreach ($data['uid'] as $uid => $attributes) { // Append the UID to the data['body'] string @@ -204,7 +230,6 @@ class TaskGateway $data['body'] .= "Attribute: $attr\n"; } } - print_r($data); $mail_controller = new MailController( $data['setFrom'], -- GitLab From c6b4f29c97da60429f220c9705aa56934f48772d Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 13:46:51 +0100 Subject: [PATCH 010/111] :sparkles: Feat(TaskGW) - Notifications well processed Current notifications and time logic operational. --- library/MailController.php | 15 ++- library/TaskGateway.php | 191 ++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index cec945b..c3c36ba 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -43,6 +43,9 @@ class MailController public function sendMail (): array { + // Our returned array + $errors = []; + $this->mail->isSMTP(); $this->mail->Host = $_ENV["MAIL_HOST"]; @@ -83,11 +86,15 @@ class MailController // add it to keep SMTP connection open after each email sent $this->mail->SMTPKeepAlive = TRUE; - unset($this->recipients["count"]); - - // Our returned array - $errors = []; + if (!empty($this->recipients["count"])){ + unset($this->recipients["count"]); + } + /* We have an anti-spam logic applied above the mail controller. In case of mail template, only one email is within + the recipient address, in case of notifications (e.g), multiple address exists. Therefore, the counting of anti-spam + increment is applied prior of this controller added by the numbers of recipients. See notifications logic in a send + method. + */ foreach ($this->recipients as $mail) { $this->mail->addAddress($mail); diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 6fc0f9e..4c0baab 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -60,6 +60,7 @@ class TaskGateway /** * @param array $task * @return bool + * @throws Exception */ public function statusAndScheduleCheck (array $task): bool { @@ -73,11 +74,9 @@ class TaskGateway public function getNotificationsMainTask (string $mainTaskDn): array { // Retrieve data from the main task - $notificationsMainTask = $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', + return $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], '', $mainTaskDn); - - return $notificationsMainTask; } public function retrieveMailTemplateInfos (string $templateName): array @@ -161,13 +160,16 @@ class TaskGateway $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['attrs'] = $matchingAttrs; // Require to be set for updating the status of the task later on. - $notifications[$notificationsMainTaskName][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; - $notifications[$notificationsMainTaskName][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; + $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; + $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; // shifting the array as first key is identical to main task name $notifications[$notificationsMainTaskName]['mailForm'] = array_shift($mailTemplateForm); //Overwrite array notifications with complementing mail form body with uid and related attributes. $notifications = $this->completeNotificationsBody($notifications); + } else { // Simply remove the subTask has no notifications are required + $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; } } } @@ -190,7 +192,7 @@ class TaskGateway $uidAttrsText = []; foreach ($notifications[key($notifications)]['uid'] as $uidKey => $uidValue) { $uidName = $uidKey; - $attrs = []; + $attrs = []; foreach ($uidValue['attrs'] as $attr) { $attrs[] = $attr; } @@ -210,64 +212,65 @@ class TaskGateway $fdTasksConf = $this->getMailObjectConfiguration(); $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); - // Each main tasks having notifications required will be processed one by one, resulting in on email per main tasks. - foreach ($notifications as $notification => $data) { - $maxMailsIncrement = 0; - - //CONTINUE DEBUG HERE - //Create the body of the notification mail combining template and members with audited attributes. - foreach ($data['uid'] as $uid => $attributes) { - // Append the UID to the data['body'] string - $data['body'] .= "\nUID: $uid\n"; - // Retrieve the DN of subtasks within array to later update the status of subtasks. - $dn[] = $attributes['dn']; - $cn[] = $attributes['cn']; - //remove the DN from list of attributes - unset($attributes['dn']); - unset($attributes['cn']); - // Iterate over the attributes under the current UID and append each value to the body. - foreach ($attributes as $attr) { - $data['body'] .= "Attribute: $attr\n"; - } - } + /* + Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is + sent per main task. + */ + $maxMailsIncrement = 0; + + foreach ($notifications as $data) { + $numberOfRecipients = count($data['mailForm']['recipients']); $mail_controller = new MailController( - $data['setFrom'], + $data['mailForm']['setFrom'], null, - $data['recipients'], - $data['body'], - $data['signature'], - $data['subject'], - $data['receipt'], + $data['mailForm']['recipients'], + $data['mailForm']['body'], + $data['mailForm']['signature'], + $data['mailForm']['subject'], + $data['mailForm']['receipt'], null ); - //$mailSentResult = $mail_controller->sendMail(); - - if ($mailSentResult[0] == "SUCCESS") { + $mailSentResult = $mail_controller->sendMail(); + $result = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); - foreach ($dn as $index => $dnValue) { - // Access the corresponding element in the $cn array using the same index - $cnValue = $cn[$index]; - // Update task status for the current $dn - $result[$dnValue]['statusUpdate'] = $this->updateTaskStatus($dnValue, $cnValue, "2"); + // Verification anti-spam max mails to be sent and quit loop if matched. + $maxMailsIncrement += $numberOfRecipients; + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } + } - $result[$dnValue]['mailStatus'] = 'Notification was successfully sent'; - $result[$dnValue]['updateLastExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); - } + return $result; + } - } else { - foreach ($dn as $index => $dnValue) { - $cnValue = $cn[$index]; - $result[$dnValue]['statusUpdate'] = $this->updateTaskStatus($dnValue, $cnValue, $mailSentResult[0]); - $result[$dnValue]['mailStatus'] = $mailSentResult; - } + protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array + { + $result = []; + if ($serverResults[0] == "SUCCESS") { + foreach ($subTask['uid'] as $details) { + + // CN of the main task + $cn = $details['cn']; + // DN of the main task + $dn = $details['dn']; + + // Update task status for the current $dn + $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, "2"); + $result[$dn]['mailStatus'] = 'Notification was successfully sent'; + $result[$dn]['updateLastMailExec'] = $this->updateLastMailExecTime($mailTaskBackend[0]["dn"]); } + } else { + foreach ($subTask['uid'] as $details) { - // Verification anti-spam max mails to be sent and quit loop if matched. - $maxMailsIncrement += 1; - if ($maxMailsIncrement == $maxMailsConfig) { - break; + // CN of the main task + $cn = $details['cn']; + // DN of the main task + $dn = $details['dn']; + + $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, $serverResults[0]); + $result[$dn]['mailStatus'] = $serverResults; } } @@ -278,15 +281,12 @@ class TaskGateway * @return array * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory */ - private - function getMailObjectConfiguration (): array + private function getMailObjectConfiguration (): array { - $fdTasksConf = $this->getLdapTasks( + return $this->getLdapTasks( "(objectClass=fdTasksConf)", ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] ); - - return $fdTasksConf; } /** @@ -294,8 +294,7 @@ class TaskGateway * @return int * Note : Allows a safety check in case mail configuration backed within FD has been missed. */ - public - function returnMaximumMailToBeSend (array $fdTasksConf): int + public function returnMaximumMailToBeSend (array $fdTasksConf): int { // set the maximum mails to be sent to the configured value or 50 if not set. return $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; @@ -304,26 +303,26 @@ class TaskGateway /** * @param array $list_tasks * @return array + * @throws Exception */ - public - function processMailTasks (array $list_tasks): array + public function processMailTasks (array $list_tasks): array { $result = []; $fdTasksConf = $this->getMailObjectConfiguration(); $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); + // Increment for anti=spam, starts at 0, each mail task only contain one email, addition if simply + one. + $maxMailsIncrement = 0; + if ($this->verifySpamProtection($fdTasksConf)) { foreach ($list_tasks as $mail) { - $maxMailsIncrement = 0; - // verify status before processing (to be checked with schedule as well). if ($mail["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($mail["fdtasksgranularschedule"][0])) { // Search for the related attached mail object. - $cn = $mail["fdtasksgranularref"][0]; - $mailInfos = $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $cn); + $mailInfos = $this->retrieveMailTemplateInfos($mail["fdtasksgranularref"][0]); $mailContent = $mailInfos[0]; // Only takes arrays related to files attachments for the mail template selected @@ -365,17 +364,17 @@ class TaskGateway if ($mailSentResult[0] == "SUCCESS") { // The third arguments "2" is the status code of success for mail as of now 18/11/22 - $result[$mail["dn"]]['statusUpdate'] = $this->updateTaskStatus($mail["dn"], $mail["cn"][0], "2"); - $result[$mail["dn"]]['mailStatus'] = 'mail : ' . $mail["dn"] . ' was successfully sent'; - $result[$mail["dn"]]['updateLastExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); + $result[$mail["dn"]]['statusUpdate'] = $this->updateTaskStatus($mail["dn"], $mail["cn"][0], "2"); + $result[$mail["dn"]]['mailStatus'] = 'mail : ' . $mail["dn"] . ' was successfully sent'; + $result[$mail["dn"]]['updateLastMailExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); } else { $result[$mail["dn"]]['statusUpdate'] = $this->updateTaskStatus($mail["dn"], $mail["cn"][0], $mailSentResult[0]); - $result[$mail["dn"]] = $mailSentResult; + $result[$mail["dn"]]['Error'] = $mailSentResult; } // Verification anti-spam max mails to be sent and quit loop if matched - $maxMailsIncrement += 1; + $maxMailsIncrement += 1; //Only one as recipients in mail object is always one email. if ($maxMailsIncrement == $maxMailsConfig) { break; } @@ -389,11 +388,11 @@ class TaskGateway /** * @param array $list_tasks - * @return array + * @return array[]|string[] + * @throws Exception * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. */ - public - function processLifeCycleTasks (array $list_tasks): array + public function processLifeCycleTasks (array $list_tasks): array { // Array representing the status of the subtask. $result = []; @@ -462,8 +461,7 @@ class TaskGateway * @param string $userDN * @return bool|string */ - protected - function updateLifeCycle (array $lifeCycleBehavior, string $userDN) + protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) { // Extracting values of desired post-state behavior $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; @@ -498,8 +496,7 @@ class TaskGateway * @param bool|string $subTaskDn * @return bool|string */ - protected - function removeSubTask ($subTaskDn) + protected function removeSubTask ($subTaskDn) { try { $result = ldap_delete($this->ds, $subTaskDn); @@ -514,8 +511,7 @@ class TaskGateway * @return array * Note Search for all sub-tasks having status equals to 2 (completed). */ - public - function removeCompletedTasks (): array + public function removeCompletedTasks (): array { $result = []; $subTasksCompleted = $this->getLdapTasks( @@ -539,8 +535,7 @@ class TaskGateway * @return array * @throws Exception */ - public - function activateCyclicTasks (): array + public function activateCyclicTasks (): array { $result = []; $tasks = $this->getLdapTasks( @@ -557,7 +552,7 @@ class TaskGateway // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); // Is used to verify cyclic schedule with date format. - $now = new DateTime('now', new DateTimeZone('UTC')); + $now = new DateTime('now'); foreach ($tasks as $task) { // Transform schedule time (it is a simple string) @@ -572,6 +567,7 @@ class TaskGateway // Case where the tasks were once run, verification of the cyclic schedule and last exec. } else if (!empty($task['fdtasksrepeatableschedule'][0])) { $lastExec = new DateTime($task['fdtaskslastexec'][0]); + // Efficient way to verify timelapse $interval = $now->diff($lastExec); @@ -605,7 +601,7 @@ class TaskGateway } break; case 'Hourly' : - if ($interval->h >= 7) { + if ($interval->h >= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; @@ -633,8 +629,7 @@ class TaskGateway * if there is indeed a difference and therefore must update the user information. * In case the comparison is impossible due to the use not having a status listed, it will report false. */ - protected - function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool + protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool { $result = FALSE; // Regular expression in order to extract the supann format within an array @@ -683,8 +678,7 @@ class TaskGateway * Note : Method which verify the last executed e-mails sent * Verify if the time interval is respected in order to protect from SPAM */ - public - function verifySpamProtection (array $fdTasksConf): bool + public function verifySpamProtection (array $fdTasksConf): bool { $lastExec = $fdTasksConf[0]["fdtasksconflastexectime"][0] ?? NULL; $spamInterval = $fdTasksConf[0]["fdtasksconfintervalemails"][0] ?? NULL; @@ -702,14 +696,16 @@ class TaskGateway /** * @param string $schedule * @return bool + * @throws Exception */ // Verification of the schedule in complete string format and compare. - public - function verifySchedule (string $schedule): bool + public function verifySchedule (string $schedule): bool { - $schedule = strtotime($schedule); - if ($schedule < time()) { - return TRUE; + $currentDateTime = new DateTime('now'); // Get current datetime in UTC + $scheduledDateTime = new DateTime($schedule); // Parse scheduled datetime string in UTC + + if ($scheduledDateTime < $currentDateTime) { + return TRUE; // Schedule has passed } return FALSE; @@ -723,8 +719,7 @@ class TaskGateway * @return array * NOTE : Filter in ldap_search cannot be an empty string or NULL, if not filters are required, use (objectClass=*). */ - public - function getLdapTasks (string $filter = '', array $attrs = [], string $attachmentsCN = NULL, string $dn = NULL): array + public function getLdapTasks (string $filter = '', array $attrs = [], string $attachmentsCN = NULL, string $dn = NULL): array { $result = []; @@ -764,8 +759,7 @@ class TaskGateway * @return bool|string * Note : Update the status of the tasks. */ - public - function updateTaskStatus (string $dn, string $cn, string $status) + public function updateTaskStatus (string $dn, string $cn, string $status) { // prepare data if (!empty($dn)) { @@ -790,8 +784,7 @@ class TaskGateway * @return bool|string * Note: Update the attribute lastExecTime from fdTasksConf. */ - public - function updateLastMailExecTime (string $dn) + public function updateLastMailExecTime (string $dn) { // prepare data $ldap_entry["fdTasksConfLastExecTime"] = time(); -- GitLab From bca5fa0aa0b39f041aa7f3a9195d8a53b3b7d077 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 16:51:36 +0100 Subject: [PATCH 011/111] :sparkles: Feat(TaskGW) - before modifications before modification errors handling --- library/TaskGateway.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 4c0baab..d7574c8 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -167,7 +167,9 @@ class TaskGateway $notifications[$notificationsMainTaskName]['mailForm'] = array_shift($mailTemplateForm); //Overwrite array notifications with complementing mail form body with uid and related attributes. $notifications = $this->completeNotificationsBody($notifications); - } else { // Simply remove the subTask has no notifications are required + + } + else { // Simply remove the subTask has no notifications are required $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; } @@ -217,6 +219,8 @@ class TaskGateway sent per main task. */ $maxMailsIncrement = 0; + print_r($notifications); + exit; foreach ($notifications as $data) { $numberOfRecipients = count($data['mailForm']['recipients']); @@ -233,7 +237,7 @@ class TaskGateway ); $mailSentResult = $mail_controller->sendMail(); - $result = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); + $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); // Verification anti-spam max mails to be sent and quit loop if matched. $maxMailsIncrement += $numberOfRecipients; -- GitLab From 204e9a505a3ad4f75da76971a3d3e26dfabd81f5 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 17:54:26 +0100 Subject: [PATCH 012/111] :sparkles: Feat(TaskGW) - refactor of mail creation Successful refactor of mail body creation --- library/TaskGateway.php | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index d7574c8..78252dc 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -84,7 +84,7 @@ class TaskGateway return $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $templateName); } - private function generateMainTaskMailTemplate (array $mainTask, string $mainTaskName): array + private function generateMainTaskMailTemplate (array $mainTask): array { // Generate email configuration for each result of subtasks having the same main task. $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; @@ -96,12 +96,12 @@ class TaskGateway $mailContent = $mailInfos[0]; // Set the notification array with all required variable for all sub-tasks of same main task origin. - $mailForm[$mainTaskName]['setFrom'] = $sender; - $mailForm[$mainTaskName]['recipients'] = $recipients; - $mailForm[$mainTaskName]['body'] = $mailContent["fdmailtemplatebody"][0]; - $mailForm[$mainTaskName]['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; - $mailForm[$mainTaskName]['subject'] = $mailContent["fdmailtemplatesubject"][0]; - $mailForm[$mainTaskName]['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; + $mailForm['setFrom'] = $sender; + $mailForm['recipients'] = $recipients; + $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; + $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; + $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; return $mailForm; } @@ -145,7 +145,7 @@ class TaskGateway $notificationsMainTaskName = $task['fdtasksgranularmaster'][0]; // Generate the mail form with all mail controller requirements - $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask, $notificationsMainTaskName); + $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask); // Simply retrieve the list of audited attributes $auditAttributes = $this->retrieveAuditedAttributes($task); @@ -162,14 +162,11 @@ class TaskGateway // Require to be set for updating the status of the task later on. $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; + $notifications[$notificationsMainTaskName]['mailForm'] = $mailTemplateForm; + // Overwrite array notifications with complementing mail form body with uid and related attributes. + $notifications = $this->completeNotificationsBody($notifications, $notificationsMainTaskName); - // shifting the array as first key is identical to main task name - $notifications[$notificationsMainTaskName]['mailForm'] = array_shift($mailTemplateForm); - //Overwrite array notifications with complementing mail form body with uid and related attributes. - $notifications = $this->completeNotificationsBody($notifications); - - } - else { // Simply remove the subTask has no notifications are required + } else { // Simply remove the subTask has no notifications are required $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; } @@ -188,21 +185,22 @@ class TaskGateway * @return array * Note : This method is present to add to the mailForm body the proper uid and attrs info. */ - private function completeNotificationsBody (array $notifications): array + private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array { // Iterate through each uid and its attrs $uidAttrsText = []; - foreach ($notifications[key($notifications)]['uid'] as $uidKey => $uidValue) { + + foreach ($notifications[$notificationsMainTaskName]['uid'] as $uidKey => $uidValue) { $uidName = $uidKey; $attrs = []; foreach ($uidValue['attrs'] as $attr) { $attrs[] = $attr; } $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; - } + } // Add uid names and related attrs to mailForm['body'] - $notifications[key($notifications)]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); + $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); return $notifications; } @@ -219,8 +217,6 @@ class TaskGateway sent per main task. */ $maxMailsIncrement = 0; - print_r($notifications); - exit; foreach ($notifications as $data) { $numberOfRecipients = count($data['mailForm']['recipients']); -- GitLab From e32a4909ee92be6fd74ae1c962db6fec35f6a9ff Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 21:31:33 +0100 Subject: [PATCH 013/111] :sparkles: Feat(TaskGW) - refactor of subTasks Successful refactor of subTask array --- library/TaskGateway.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 78252dc..fc47734 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -157,12 +157,12 @@ class TaskGateway if (!empty($matchingAttrs)) { // Fill an array with UID of audited user and related matching attributes - $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['attrs'] = $matchingAttrs; + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; // Require to be set for updating the status of the task later on. - $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['dn'] = $task['dn']; - $notifications[$notificationsMainTaskName]['uid'][$task['fdtasksgranulardn'][0]]['cn'] = $task['cn'][0]; - $notifications[$notificationsMainTaskName]['mailForm'] = $mailTemplateForm; + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + $notifications[$notificationsMainTaskName]['mailForm'] = $mailTemplateForm; // Overwrite array notifications with complementing mail form body with uid and related attributes. $notifications = $this->completeNotificationsBody($notifications, $notificationsMainTaskName); @@ -187,18 +187,19 @@ class TaskGateway */ private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array { - // Iterate through each uid and its attrs + // Iterate through each subTask and related attrs $uidAttrsText = []; - foreach ($notifications[$notificationsMainTaskName]['uid'] as $uidKey => $uidValue) { - $uidName = $uidKey; + foreach ($notifications[$notificationsMainTaskName]['subTask'] as $value) { + $uidName = $value['uid']; $attrs = []; - foreach ($uidValue['attrs'] as $attr) { + + foreach ($value['attrs'] as $attr) { $attrs[] = $attr; } $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; - } + // Add uid names and related attrs to mailForm['body'] $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); @@ -249,10 +250,10 @@ class TaskGateway { $result = []; if ($serverResults[0] == "SUCCESS") { - foreach ($subTask['uid'] as $details) { + foreach ($subTask['subTask'] as $subTask => $details) { // CN of the main task - $cn = $details['cn']; + $cn = $subTask; // DN of the main task $dn = $details['dn']; @@ -262,10 +263,10 @@ class TaskGateway $result[$dn]['updateLastMailExec'] = $this->updateLastMailExecTime($mailTaskBackend[0]["dn"]); } } else { - foreach ($subTask['uid'] as $details) { + foreach ($subTask['subTask'] as $subTask => $details) { // CN of the main task - $cn = $details['cn']; + $cn = $subTask; // DN of the main task $dn = $details['dn']; -- GitLab From c72797a01a1a7177e83767cdfee26eb183de4bda Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 9 Apr 2024 23:42:31 +0100 Subject: [PATCH 014/111] :sparkles: Feat(TaskGW) - removes duplicate attributes within notification Remove duplication information within email notifications per main task. --- library/TaskGateway.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index fc47734..8bcc001 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -200,6 +200,8 @@ class TaskGateway $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; } + // Make the array unique, avoiding uid and same attribute duplication. + $uidAttrsText = array_unique($uidAttrsText); // Add uid names and related attrs to mailForm['body'] $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); -- GitLab From 109bd478ef003b5e221362d493215b680b3dbf8d Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 11 Apr 2024 10:52:57 +0100 Subject: [PATCH 015/111] :ambulance: Fixes(TaskGW) - phpcs fixes phpcs fixes --- library/TaskGateway.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 8bcc001..a6511fa 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -226,13 +226,13 @@ class TaskGateway $mail_controller = new MailController( $data['mailForm']['setFrom'], - null, + NULL, $data['mailForm']['recipients'], $data['mailForm']['body'], $data['mailForm']['signature'], $data['mailForm']['subject'], $data['mailForm']['receipt'], - null + NULL ); $mailSentResult = $mail_controller->sendMail(); @@ -701,7 +701,7 @@ class TaskGateway * @return bool * @throws Exception */ -// Verification of the schedule in complete string format and compare. + // Verification of the schedule in complete string format and compare. public function verifySchedule (string $schedule): bool { $currentDateTime = new DateTime('now'); // Get current datetime in UTC -- GitLab From 3b09570f66d92d73d30ba27595a608276d659571 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 11 Apr 2024 10:55:52 +0100 Subject: [PATCH 016/111] :ambulance: Fixes(mailController) - phpcs fixes phpcs fixes --- library/MailController.php | 4 ++-- library/TaskGateway.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index c3c36ba..e02e595 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -24,7 +24,7 @@ class MailController string $body, ?string $signature, string $subject, - bool $receipt = NULL, + bool $receipt = NULL, array $attachments = NULL ) { @@ -86,7 +86,7 @@ class MailController // add it to keep SMTP connection open after each email sent $this->mail->SMTPKeepAlive = TRUE; - if (!empty($this->recipients["count"])){ + if (!empty($this->recipients["count"])) { unset($this->recipients["count"]); } diff --git a/library/TaskGateway.php b/library/TaskGateway.php index a6511fa..18c498f 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -86,7 +86,7 @@ class TaskGateway private function generateMainTaskMailTemplate (array $mainTask): array { - // Generate email configuration for each result of subtasks having the same main task. + // Generate email configuration for each result of subtasks having the same main task.w $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; unset($recipients['count']); $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; -- GitLab From 9cfa92d4fd234676cb8c7f6cf11420dea1a1e994 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 11 Apr 2024 11:03:47 +0100 Subject: [PATCH 017/111] :ambulance: Fixes(mailController) - phpcs fixes phpcs fixes --- library/MailController.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index e02e595..63104c4 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -18,14 +18,13 @@ class MailController private PHPMailer $mail; function __construct ( - string $setFrom, + string $setFrom, ?string $setBCC, - array $recipients, - string $body, + array $recipients, + string $body, ?string $signature, - string $subject, - bool $receipt = NULL, - array $attachments = NULL + string $subject, + bool $receipt = NULL, array $attachments = NULL ) { // The TRUE value passed it to enable the exception handling properly. @@ -41,7 +40,8 @@ class MailController } - public function sendMail (): array + public + function sendMail (): array { // Our returned array $errors = []; -- GitLab From 77d32b04067e030ae635cea3f2df842a214b6719 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 11 Apr 2024 11:11:48 +0100 Subject: [PATCH 018/111] :ambulance: Fixes(mailController) - phpcs fixes phpcs fixes --- library/MailController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index 63104c4..3934da9 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -7,14 +7,14 @@ use PHPMailer\PHPMailer\Exception; class MailController { - protected string $setFrom; + protected string $setFrom; protected ?string $setBCC; - protected array $recipients; - protected string $body; + protected array $recipients; + protected string $body; protected ?string $signature; - protected string $subject; - protected ?bool $receipt; - protected ?array $attachments; + protected string $subject; + protected ?bool $receipt; + protected ?array $attachments; private PHPMailer $mail; function __construct ( -- GitLab From f3ec0124ca39c2b9dce15cdce6b3287f536fa691 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 11 Apr 2024 11:13:50 +0100 Subject: [PATCH 019/111] :ambulance: Fixes(mailController) - phpcs fixes phpcs fixes --- library/MailController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index 3934da9..1735a0b 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -40,8 +40,7 @@ class MailController } - public - function sendMail (): array + public function sendMail (): array { // Our returned array $errors = []; -- GitLab From 6c6fc5e5d12e1c3230986614d2fc4bb1573df925 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 15 Apr 2024 21:46:52 +0100 Subject: [PATCH 020/111] :sparkles: Feat(Orchestrator) - First way to add potential plugins Function plugins added with current logic. --- library/TaskController.php | 17 +++++++++------ plugins/getUsers.php | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 plugins/getUsers.php diff --git a/library/TaskController.php b/library/TaskController.php index de57860..237d0bd 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -22,12 +22,12 @@ class TaskController // Otherwise return the tasks object specified } else { - $task = $this->gateway->getTask($object_type); - if (!$task) { - - $this->respondNotFound($object_type); - return; - } +// $task = $this->gateway->getTask($object_type); +// if (!$task) { +// +// $this->respondNotFound($object_type); +// return; +// } switch ($method) { case "GET": @@ -51,6 +51,11 @@ class TaskController case 'notifications': $result = $this->gateway->processNotifications($task); break; + case $object_type: + if (class_exists($object_type)) { + $endpoint = new $object_type; + $result = $endpoint->processEndPoint(); + } } if (!empty($result)) { echo json_encode($result, JSON_PRETTY_PRINT); diff --git a/plugins/getUsers.php b/plugins/getUsers.php new file mode 100644 index 0000000..a531db3 --- /dev/null +++ b/plugins/getUsers.php @@ -0,0 +1,43 @@ +<?php +class getUsers extends TaskGateway +{ + protected $ds; + + public function __construct() + { + $ldap_connect = new Ldap($_ENV["FD_LDAP_MASTER_URL"], $_ENV["LDAP_ADMIN"], $_ENV["LDAP_PWD"]); + $this->ds = $ldap_connect->getConnection(); + } + + public function processEndPoint() + { + return $this->customLdapSearch("(&(objectClass=person))", ['cn']); + } + + // This custom ldap search should be within parent as simplified version of what already exists. Refactor required. + public function customLdapSearch (string $filter = '', array $attrs = [], string $dn = NULL): array + { + $result = []; + + if (empty($dn)) { + $dn = $_ENV["LDAP_BASE"]; + } + + try { + $sr = ldap_search($this->ds, $dn, $filter, $attrs); + $info = ldap_get_entries($this->ds, $sr); + } catch (Exception $e) { + // build array for return response + $result = [json_encode(["Ldap Error" => "$e"])]; // string returned + } + + // Verify if the above ldap search succeeded. + if (!empty($info) && is_array($info) && $info["count"] >= 1) { + return $info; + } + + return $result; + } + + +} -- GitLab From d66a722234b961971ce1e58b9c36441c3418e2e3 Mon Sep 17 00:00:00 2001 From: Jonathan Swaelens <jonathan.swaelens@fusiondirectory.org> Date: Wed, 8 May 2024 17:07:54 +0200 Subject: [PATCH 021/111] :sparkles: feat(php): Use overload method instead of load for dotenv Load function doesn't overwrite existing env variable even if specified in the configuration file. Signed-off-by: Jonathan Swaelens <jonathan.swaelens@fusiondirectory.org> --- config/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/bootstrap.php b/config/bootstrap.php index baf9d5b..a47b50b 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -49,6 +49,6 @@ set_error_handler(static function (int $errno, string $errstr, string $errfile, set_exception_handler("ErrorHandler::handleException"); $dotenv = Dotenv\Dotenv::create('/etc/fusiondirectory-orchestrator', 'orchestrator.conf'); -$dotenv->load(); +$dotenv->overload(); header("Content-type: application/json; charset=UTF-8"); -- GitLab From 60a3018c064eb42230278d7bbb759090a7c81231 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Wed, 15 May 2024 16:31:48 +0100 Subject: [PATCH 022/111] :sparkles: Feat(Orchestrator) - Iteration Iteration --- api/index.php | 12 ++++++++---- library/TaskController.php | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/api/index.php b/api/index.php index 13839e8..ac03219 100644 --- a/api/index.php +++ b/api/index.php @@ -3,16 +3,20 @@ declare(strict_types=1); require __DIR__ . "/../config/bootstrap.php"; -$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); - +// Parsing of the URI received as WEB request. +$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); $parts = explode("/", $path); - // We only need the name of the resource $resource = $parts[3]; // And the tasks object required Ex: http://orchestrator/api/task(3)/object(4)/ // Example : mail is an object type of tasks $object_type = $parts[4] ?? NULL; +// Parsing of the raw data potentially passed as json REST data to the API +$rawBody = file_get_contents('php://input'); +// Decode the JSON data and set to null if no body received +$jsonBody = !empty ($rawBody) ? json_decode($rawBody, TRUE) : null; + switch ($resource) { case "login" : @@ -60,4 +64,4 @@ $task_gateway = new TaskGateway($ldap_connect); $controller = new TaskController($task_gateway); // Process Request Passing Resources Attributes Values ($id) -$controller->processRequest($_SERVER['REQUEST_METHOD'], $object_type); +$controller->processRequest($_SERVER['REQUEST_METHOD'], $object_type, $jsonBody); diff --git a/library/TaskController.php b/library/TaskController.php index 237d0bd..29a14e1 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -9,7 +9,7 @@ class TaskController $this->gateway = $gateway; } - public function processRequest (string $method, ?string $object_type): void + public function processRequest (string $method, ?string $object_type, $jsonBody = null): void { // If no specific tasks object specified, return all tasks if ($object_type === NULL) { @@ -22,16 +22,35 @@ class TaskController // Otherwise return the tasks object specified } else { -// $task = $this->gateway->getTask($object_type); -// if (!$task) { -// -// $this->respondNotFound($object_type); -// return; -// } + // $task = $this->gateway->getTask($object_type); + // if (!$task) { + // + // $this->respondNotFound($object_type); + // return; + // } switch ($method) { + case "GET": - echo json_encode($task); + switch ($object_type) { + case $object_type: + if (class_exists($object_type)) { + $endpoint = new $object_type; + $result = $endpoint->processEndPointGet(); + } + break; + default: + echo json_encode($task); + break; + } + + if (!empty($result)) { + echo json_encode($result, JSON_PRETTY_PRINT); + + } else { + // To be modified and enhance, no results does not always mean no emails in current logic. + echo json_encode("Nothing to do."); + } break; case "PATCH": @@ -54,8 +73,9 @@ class TaskController case $object_type: if (class_exists($object_type)) { $endpoint = new $object_type; - $result = $endpoint->processEndPoint(); + $result = $endpoint->processEndPointPatch($jsonBody); } + break; } if (!empty($result)) { echo json_encode($result, JSON_PRETTY_PRINT); -- GitLab From d84421df696de3811962835d634938cf18392146 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Wed, 15 May 2024 21:43:45 +0100 Subject: [PATCH 023/111] :sparkles: Feat(Orchestrator) - Iteration 2 Iteration 2 --- library/TaskController.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/TaskController.php b/library/TaskController.php index 29a14e1..d29d7d0 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -9,7 +9,7 @@ class TaskController $this->gateway = $gateway; } - public function processRequest (string $method, ?string $object_type, $jsonBody = null): void + public function processRequest (string $method, ?string $object_type, $jsonBody = NULL): void { // If no specific tasks object specified, return all tasks if ($object_type === NULL) { @@ -40,6 +40,13 @@ class TaskController } break; default: + $task = $this->gateway->getTask($object_type); + + if (!$task) { + $this->respondNotFound($object_type); + return; + } + echo json_encode($task); break; } -- GitLab From f9e874ca88064d79210590580b2388b5f02f202a Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 16 May 2024 15:20:44 +0100 Subject: [PATCH 024/111] :sparkles: Feat(Orchestrator) - Iteration 3 Iteration 3 --- library/TaskController.php | 95 ++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/library/TaskController.php b/library/TaskController.php index d29d7d0..ef3eb8c 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -9,10 +9,20 @@ class TaskController $this->gateway = $gateway; } - public function processRequest (string $method, ?string $object_type, $jsonBody = NULL): void + protected function parseJsonResult ($result = NULL): void + { + if (!empty($result)) { + echo json_encode($result, JSON_PRETTY_PRINT); + } else { + // No result received + echo json_encode("No results received from the endpoint"); + } + } + + public function processRequest (string $method, ?string $objectType, $jsonBody = NULL): void { // If no specific tasks object specified, return all tasks - if ($object_type === NULL) { + if ($objectType === NULL) { if ($method == "GET") { echo json_encode($this->gateway->getTask(NULL)); @@ -20,53 +30,30 @@ class TaskController $this->respondMethodAllowed("GET"); } - // Otherwise return the tasks object specified + // Otherwise continue the process of the specific task / object type specified } else { - // $task = $this->gateway->getTask($object_type); - // if (!$task) { - // - // $this->respondNotFound($object_type); - // return; - // } - switch ($method) { - + // GET methods case "GET": - switch ($object_type) { - case $object_type: - if (class_exists($object_type)) { - $endpoint = new $object_type; + switch ($objectType) { + case $objectType: + if (class_exists($objectType)) { + $endpoint = new $objectType; $result = $endpoint->processEndPointGet(); } break; - default: - $task = $this->gateway->getTask($object_type); - - if (!$task) { - $this->respondNotFound($object_type); - return; - } - - echo json_encode($task); - break; - } - - if (!empty($result)) { - echo json_encode($result, JSON_PRETTY_PRINT); - - } else { - // To be modified and enhance, no results does not always mean no emails in current logic. - echo json_encode("Nothing to do."); } + $this->parseJsonResult($result); break; + // PATCH methods case "PATCH": - switch ($object_type) { + switch ($objectType) { case "mail": - $result = $this->gateway->processMailTasks($task); + $result = $this->gateway->processMailTasks($this->getObjectTypeTask($objectType)); break; case 'lifeCycle': - $result = $this->gateway->processLifeCycleTasks($task); + $result = $this->gateway->processLifeCycleTasks($this->getObjectTypeTask($objectType)); break; case 'removeSubTasks': $result = $this->gateway->removeCompletedTasks(); @@ -75,23 +62,16 @@ class TaskController $result = $this->gateway->activateCyclicTasks(); break; case 'notifications': - $result = $this->gateway->processNotifications($task); + $result = $this->gateway->processNotifications($this->getObjectTypeTask($objectType)); break; - case $object_type: - if (class_exists($object_type)) { - $endpoint = new $object_type; + case $objectType: + if (class_exists($objectType)) { + $endpoint = new $objectType; $result = $endpoint->processEndPointPatch($jsonBody); } break; } - if (!empty($result)) { - echo json_encode($result, JSON_PRETTY_PRINT); - - } else { - // To be modified and enhance, no results does not always mean no emails in current logic. - echo json_encode("No emails were sent."); - } - + $this->parseJsonResult($result); break; case "DELETE": @@ -103,17 +83,32 @@ class TaskController } } + /** + * @param $objectType + * @return array|string[]|void + */ + private function getObjectTypeTask ($objectType) + { + $task = $this->gateway->getTask($objectType); + if (!$task) { + $this->respondNotFound($objectType); + exit; + } + + return $task; + } + private function respondMethodAllowed (string $allowed_methods): void { http_response_code(405); header("Allow: $allowed_methods"); } - private function respondNotFound (string $object_type): void + private function respondNotFound (string $objectType): void { http_response_code(404); // Task ID is easier to be used - requires unique ID attributes during task creation (FD-Interface) - echo json_encode(["message" => "Task object type : $object_type not found"]); + echo json_encode(["message" => "Task object type : $objectType not found"]); } } \ No newline at end of file -- GitLab From d87bac8dd341ac15144a2ad6b414ec10a3a74782 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 16 May 2024 17:40:30 +0100 Subject: [PATCH 025/111] :sparkles: Feat(Orchestrator) - Iteration 4 Resolve a bug with notifications endpoints. --- library/TaskGateway.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 18c498f..0a09baa 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -106,27 +106,36 @@ class TaskGateway return $mailForm; } + /** + * @param array $notificationTask + * @return array + * NOTE : receive a unique tasks of type notification (one subtask at a time) + */ protected function retrieveAuditedAttributes (array $notificationTask): array { $auditAttributes = []; + // Retrieve audit data attributes from the list of references set in the sub-task if (!empty($notificationTask['fdtasksgranularref'])) { // Ldap always return a count which we have to remove. unset($notificationTask['fdtasksgranularref']['count']); foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { - - $auditInformation = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', + $auditInformation[] = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', ['fdAuditAttributes'], '', $auditDN); } // It is possible that an audit does not contain any attributes changes, condition is required. - if (!empty($auditInformation[0]['fdauditattributes'])) { - $auditAttributes = $auditInformation[0]['fdauditattributes']; + foreach ($auditInformation as $audit => $attrs) { + unset($attrs['count']); + if (!empty($attrs[0]['fdauditattributes'])) { + // Clear and compact received results from above ldap search + unset($attrs[0]['fdauditattributes']['count']); + $auditAttributes[] = $attrs[0]['fdauditattributes']; + } } - // Clear and compact received results from above ldap search - unset($auditAttributes['count']); } + return $auditAttributes; } -- GitLab From 4681e3bb3051de0b0e7fb83d558c430f4d0704e0 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 16 May 2024 22:58:31 +0100 Subject: [PATCH 026/111] :sparkles: Feat(Orchestrator) - Iteration 5 Resolve issue with mail controller always seeing TRUE for notification --- library/MailController.php | 6 ++-- library/TaskGateway.php | 67 +++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/library/MailController.php b/library/MailController.php index 1735a0b..4de771e 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -13,7 +13,7 @@ class MailController protected string $body; protected ?string $signature; protected string $subject; - protected ?bool $receipt; + protected ?string $receipt; protected ?array $attachments; private PHPMailer $mail; @@ -24,7 +24,7 @@ class MailController string $body, ?string $signature, string $subject, - bool $receipt = NULL, array $attachments = NULL + string $receipt = NULL, array $attachments = NULL ) { // The TRUE value passed it to enable the exception handling properly. @@ -72,7 +72,7 @@ class MailController $this->mail->addBCC($this->setBCC); } - if (!empty($this->receipt)) { + if ($this->receipt === TRUE) { $this->mail->addCustomHeader('Disposition-Notification-To', $this->setFrom); } $this->mail->Subject = $this->subject; diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 0a09baa..eb0f44d 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -23,17 +23,17 @@ class TaskGateway switch ($object_type) { case "mail": $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Mail Object))"); - unset($list_tasks["count"]); + $this->unsetCountKeys($list_tasks); break; case "lifeCycle": $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Life Cycle))"); - unset($list_tasks["count"]); + $this->unsetCountKeys($list_tasks); break; case "notifications": $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Notifications))"); - unset($list_tasks["count"]); + $this->unsetCountKeys($list_tasks);; break; case "removeSubTasks": @@ -76,7 +76,7 @@ class TaskGateway // Retrieve data from the main task return $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], - '', $mainTaskDn); + '', $mainTaskDn); } public function retrieveMailTemplateInfos (string $templateName): array @@ -106,6 +106,22 @@ class TaskGateway return $mailForm; } + /** + * @param $array + * @return void + * Simple take an array as referenced and loop to remove all key having count + */ + public function unsetCountKeys (&$array) + { + foreach ($array as $key => &$value) { + if (is_array($value)) { + $this->unsetCountKeys($value); + } elseif ($key === 'count') { + unset($array[$key]); + } + } + } + /** * @param array $notificationTask * @return array @@ -117,21 +133,20 @@ class TaskGateway // Retrieve audit data attributes from the list of references set in the sub-task if (!empty($notificationTask['fdtasksgranularref'])) { - // Ldap always return a count which we have to remove. - unset($notificationTask['fdtasksgranularref']['count']); + // Remove count keys (count is shared by ldap). + $this->unsetCountKeys($notificationTask); foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { $auditInformation[] = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', - ['fdAuditAttributes'], '', $auditDN); + ['fdAuditAttributes'], '', $auditDN); } - + // Again remove key: count retrieved from LDAP. + $this->unsetCountKeys($auditInformation); // It is possible that an audit does not contain any attributes changes, condition is required. - foreach ($auditInformation as $audit => $attrs) { - unset($attrs['count']); - if (!empty($attrs[0]['fdauditattributes'])) { + foreach ($auditInformation as $audit => $attr) { + if (!empty($attr[0]['fdauditattributes'])) { // Clear and compact received results from above ldap search - unset($attrs[0]['fdauditattributes']['count']); - $auditAttributes[] = $attrs[0]['fdauditattributes']; + $auditAttributes = $attr[0]['fdauditattributes']; } } } @@ -159,7 +174,7 @@ class TaskGateway $auditAttributes = $this->retrieveAuditedAttributes($task); $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; - unset($monitoredAttrs['count']); + $this->unsetCountKeys($monitoredAttrs); // Verify if there is a match between audited attributes and monitored attributes from main task. $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); @@ -340,7 +355,7 @@ class TaskGateway // Only takes arrays related to files attachments for the mail template selected unset($mailInfos[0]); // Re-order keys - unset($mailInfos['count']); + $this->unsetCountKeys($mailInfos); $mailAttachments = array_values($mailInfos); $setFrom = $mail["fdtasksgranularmailfrom"][0]; @@ -363,13 +378,13 @@ class TaskGateway } $mail_controller = new MailController($setFrom, - $setBCC, - $recipients, - $body, - $signature, - $subject, - $receipt, - $attachments); + $setBCC, + $recipients, + $body, + $signature, + $subject, + $receipt, + $attachments); $mailSentResult = $mail_controller->sendMail(); @@ -421,11 +436,11 @@ class TaskGateway $lifeCycleBehavior = $this->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], - '', $task['fdtasksgranularmaster'][0]); + '', $task['fdtasksgranularmaster'][0]); // Simply retrieve the current supannStatus of the user DN related to the task at hand. $currentUserLifeCycle = $this->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], - '', $task['fdtasksgranulardn'][0]); + '', $task['fdtasksgranulardn'][0]); // Compare both the required schedule and the current user status - returning TRUE if modification is required. if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { @@ -531,7 +546,7 @@ class TaskGateway ["dn"] ); // remove the count key from the arrays, keeping only DN. - unset($subTasksCompleted['count']); + $this->unsetCountKeys($subTasksCompleted); if (!empty($subTasksCompleted)) { foreach ($subTasksCompleted as $subTasks) { $result[$subTasks['dn']]['result'] = $this->removeSubTask($subTasks['dn']); @@ -555,7 +570,7 @@ class TaskGateway ["dn", "fdTasksRepeatableSchedule", "fdTasksLastExec", "fdTasksScheduleDate"] ); // remove the count key from the arrays, keeping only DN. - unset($tasks['count']); + $this->unsetCountKeys($tasks); if (!empty($tasks)) { // Initiate the object webservice. -- GitLab From b849bb179cff886a1c4a019c62c42b30aea120ae Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 20 May 2024 17:42:06 +0100 Subject: [PATCH 027/111] :sparkles: Feat(Orchestrator) - Iteration 6 Resolve issue with GET within tasks object without objectType passed. --- library/TaskController.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/library/TaskController.php b/library/TaskController.php index ef3eb8c..22db508 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -19,19 +19,27 @@ class TaskController } } + /** + * @param string $method + * @param string|null $objectType + * @param $jsonBody + * @return void + * @throws Exception + * NOTE : objectType is actually the task type. + */ public function processRequest (string $method, ?string $objectType, $jsonBody = NULL): void { // If no specific tasks object specified, return all tasks - if ($objectType === NULL) { + if ($objectType == NULL) { if ($method == "GET") { echo json_encode($this->gateway->getTask(NULL)); } else { $this->respondMethodAllowed("GET"); } - // Otherwise continue the process of the specific task / object type specified } else { + // Define an empty array as returning result. switch ($method) { // GET methods case "GET": @@ -43,7 +51,10 @@ class TaskController } break; } - $this->parseJsonResult($result); + + if (!empty($result)) { + $this->parseJsonResult($result); + } break; // PATCH methods -- GitLab From cbd69f09b93c1e627edd9010c3a9d5d55d097344 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 21 May 2024 17:59:13 +0100 Subject: [PATCH 028/111] :sparkles: Feat(Orchestrator) - Iteration 7 Added a search LDAP method as well as forcing timezone from fusiondirectory configuration. --- api/index.php | 6 ++++++ library/Ldap.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/api/index.php b/api/index.php index ac03219..1680833 100644 --- a/api/index.php +++ b/api/index.php @@ -43,6 +43,12 @@ switch ($resource) { // Retrieve an authenticated ldap connection $ldap_connect = new Ldap($_ENV["FD_LDAP_MASTER_URL"], $_ENV["LDAP_ADMIN"], $_ENV["LDAP_PWD"]); +// Set timezone according to what's referenced in FusionDirectory configuration +$timezone = $ldap_connect->searchInLdap($ldap_connect->getConnection(), + '(objectClass=FusionDirectoryConf)', ['fdTimezone'], "cn=config,ou=fusiondirectory,".$_ENV["LDAP_BASE"]); +// Set default timezone retrieved. +date_default_timezone_set($timezone[0]['fdtimezone'][0]); + // Retrieve all user info based on the dsa common name (CN). $user_gateway = new UserGateway($ldap_connect); diff --git a/library/Ldap.php b/library/Ldap.php index 1565d65..a3b2fcb 100644 --- a/library/Ldap.php +++ b/library/Ldap.php @@ -32,4 +32,36 @@ class Ldap return $ds; } + + /** + * @param $ds + * @param string $filter + * @param array $attrs + * @param string|NULL $dn + * @return array + * Note : A generic method allowing to search in LDAP. + */ + public function searchInLdap ($ds, string $filter = '', array $attrs = [], string $dn = NULL): array + { + $result = []; + + if (empty($dn)) { + $dn = $_ENV["LDAP_BASE"]; + } + + try { + $sr = ldap_search($ds, $dn, $filter, $attrs); + $info = ldap_get_entries($ds, $sr); + } catch (Exception $e) { + // build array for return response + $result = [json_encode(["Ldap Error" => "$e"])]; // string returned + } + + // Verify if the above ldap search succeeded. + if (!empty($info) && is_array($info) && $info["count"] >= 1) { + return $info; + } + + return $result; + } } \ No newline at end of file -- GitLab From bc3b0ea5b7d4028408d6c1870d14cbe3fb6f87a9 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Fri, 31 May 2024 15:33:47 +0100 Subject: [PATCH 029/111] :sparkles: Feat(Orchestrator) - Iteration 8 - endpoint interface Mail is now integrated as part of a plugin within plugins/tasks New interface implementation. --- library/TaskController.php | 57 +++--- library/TaskGateway.php | 178 +++---------------- library/interfaces/EndpointInterface.php | 27 +++ plugins/tasks/mail.php | 212 +++++++++++++++++++++++ 4 files changed, 284 insertions(+), 190 deletions(-) create mode 100644 library/interfaces/EndpointInterface.php create mode 100644 plugins/tasks/mail.php diff --git a/library/TaskController.php b/library/TaskController.php index 22db508..26aacc0 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -1,5 +1,11 @@ <?php +/* + * The controller is part of the presentation layer in an MVC (Model-View-Controller) architecture. + * Its primary role is to handle incoming HTTP requests, process user inputs, and determine which business logic (services or models) to invoke. + * It then decides what response to send back to the client. + */ + class TaskController { private TaskGateway $gateway; @@ -15,18 +21,18 @@ class TaskController echo json_encode($result, JSON_PRETTY_PRINT); } else { // No result received - echo json_encode("No results received from the endpoint"); + echo json_encode("No results received from the endpoint, maybe nothing to be processed ?"); } } - /** - * @param string $method - * @param string|null $objectType - * @param $jsonBody - * @return void - * @throws Exception - * NOTE : objectType is actually the task type. - */ + /** + * @param string $method + * @param string|null $objectType + * @param $jsonBody + * @return void + * @throws Exception + * NOTE : objectType is actually the task type. + */ public function processRequest (string $method, ?string $objectType, $jsonBody = NULL): void { // If no specific tasks object specified, return all tasks @@ -39,10 +45,11 @@ class TaskController } // Otherwise continue the process of the specific task / object type specified } else { - // Define an empty array as returning result. + // Define an empty array as returning result. switch ($method) { // GET methods case "GET": + // The switch here is created to have potential additions later on, switch ($objectType) { case $objectType: if (class_exists($objectType)) { @@ -51,18 +58,15 @@ class TaskController } break; } - - if (!empty($result)) { - $this->parseJsonResult($result); - } + $this->parseJsonResult($result); break; // PATCH methods case "PATCH": switch ($objectType) { - case "mail": - $result = $this->gateway->processMailTasks($this->getObjectTypeTask($objectType)); - break; + // case "mail": + // $result = $this->gateway->processMailTasks($this->getObjectTypeTask($objectType)); + // break; case 'lifeCycle': $result = $this->gateway->processLifeCycleTasks($this->getObjectTypeTask($objectType)); break; @@ -77,7 +81,7 @@ class TaskController break; case $objectType: if (class_exists($objectType)) { - $endpoint = new $objectType; + $endpoint = new $objectType($this->gateway); $result = $endpoint->processEndPointPatch($jsonBody); } break; @@ -94,28 +98,13 @@ class TaskController } } - /** - * @param $objectType - * @return array|string[]|void - */ - private function getObjectTypeTask ($objectType) - { - $task = $this->gateway->getTask($objectType); - if (!$task) { - $this->respondNotFound($objectType); - exit; - } - - return $task; - } - private function respondMethodAllowed (string $allowed_methods): void { http_response_code(405); header("Allow: $allowed_methods"); } - private function respondNotFound (string $objectType): void + public static function respondNotFound (string $objectType): void { http_response_code(404); // Task ID is easier to be used - requires unique ID attributes during task creation (FD-Interface) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index eb0f44d..0760502 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -2,10 +2,13 @@ /** * Note : Tasks engine for FusionDirectory. + * The gateway, often known as a data gateway or data access layer, is responsible for abstracting and encapsulating the interaction with an external system or a data source. + * (e.g., an LDAP, an API, or another service). + * It provides a unified interface for these operations. */ class TaskGateway { - private $ds; + public $ds; // Variable type can be LDAP : enhancement public function __construct ($ldap_connect) @@ -21,10 +24,6 @@ class TaskGateway public function getTask (?string $object_type): array { switch ($object_type) { - case "mail": - $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Mail Object))"); - $this->unsetCountKeys($list_tasks); - break; case "lifeCycle": $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Life Cycle))"); @@ -47,6 +46,11 @@ class TaskGateway $list_tasks = $this->getLdapTasks("(objectClass=fdTasks)", ["cn", "objectClass"]); break; + case $object_type: + $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=" . $object_type . ")"); + $this->unsetCountKeys($list_tasks); + break; + //Will match any object type passed not found. default: // return empty array which will be interpreted as FALSE by parent. @@ -79,10 +83,6 @@ class TaskGateway '', $mainTaskDn); } - public function retrieveMailTemplateInfos (string $templateName): array - { - return $this->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $templateName); - } private function generateMainTaskMailTemplate (array $mainTask): array { @@ -304,115 +304,6 @@ class TaskGateway return $result; } - /** - * @return array - * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory - */ - private function getMailObjectConfiguration (): array - { - return $this->getLdapTasks( - "(objectClass=fdTasksConf)", - ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] - ); - } - - /** - * @param array $fdTasksConf - * @return int - * Note : Allows a safety check in case mail configuration backed within FD has been missed. - */ - public function returnMaximumMailToBeSend (array $fdTasksConf): int - { - // set the maximum mails to be sent to the configured value or 50 if not set. - return $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; - } - - /** - * @param array $list_tasks - * @return array - * @throws Exception - */ - public function processMailTasks (array $list_tasks): array - { - $result = []; - - $fdTasksConf = $this->getMailObjectConfiguration(); - $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); - - // Increment for anti=spam, starts at 0, each mail task only contain one email, addition if simply + one. - $maxMailsIncrement = 0; - - if ($this->verifySpamProtection($fdTasksConf)) { - foreach ($list_tasks as $mail) { - - // verify status before processing (to be checked with schedule as well). - if ($mail["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($mail["fdtasksgranularschedule"][0])) { - - // Search for the related attached mail object. - $mailInfos = $this->retrieveMailTemplateInfos($mail["fdtasksgranularref"][0]); - $mailContent = $mailInfos[0]; - - // Only takes arrays related to files attachments for the mail template selected - unset($mailInfos[0]); - // Re-order keys - $this->unsetCountKeys($mailInfos); - $mailAttachments = array_values($mailInfos); - - $setFrom = $mail["fdtasksgranularmailfrom"][0]; - $setBCC = $mail["fdtasksgranularmailbcc"][0] ?? NULL; - $recipients = $mail["fdtasksgranularmail"]; - $body = $mailContent["fdmailtemplatebody"][0]; - $signature = $mailContent["fdmailtemplatesignature"][0] ?? NULL; - $subject = $mailContent["fdmailtemplatesubject"][0]; - $receipt = $mailContent["fdmailtemplatereadreceipt"][0]; - - foreach ($mailAttachments as $file) { - $fileInfo['cn'] = $file['cn'][0]; - $fileInfo['content'] = $file['fdmailattachmentscontent'][0]; - $attachments[] = $fileInfo; - } - - // Required before passing the array to the constructor mail. - if (empty($attachments)) { - $attachments = NULL; - } - - $mail_controller = new MailController($setFrom, - $setBCC, - $recipients, - $body, - $signature, - $subject, - $receipt, - $attachments); - - $mailSentResult = $mail_controller->sendMail(); - - if ($mailSentResult[0] == "SUCCESS") { - - // The third arguments "2" is the status code of success for mail as of now 18/11/22 - $result[$mail["dn"]]['statusUpdate'] = $this->updateTaskStatus($mail["dn"], $mail["cn"][0], "2"); - $result[$mail["dn"]]['mailStatus'] = 'mail : ' . $mail["dn"] . ' was successfully sent'; - $result[$mail["dn"]]['updateLastMailExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); - - } else { - $result[$mail["dn"]]['statusUpdate'] = $this->updateTaskStatus($mail["dn"], $mail["cn"][0], $mailSentResult[0]); - $result[$mail["dn"]]['Error'] = $mailSentResult; - } - - // Verification anti-spam max mails to be sent and quit loop if matched - $maxMailsIncrement += 1; //Only one as recipients in mail object is always one email. - if ($maxMailsIncrement == $maxMailsConfig) { - break; - } - - } - } - } - - return $result; - } - /** * @param array $list_tasks * @return array[]|string[] @@ -699,37 +590,17 @@ class TaskGateway return $result; } - /** - * @param array $fdTasksConf - * @return bool - * Note : Method which verify the last executed e-mails sent - * Verify if the time interval is respected in order to protect from SPAM - */ - public function verifySpamProtection (array $fdTasksConf): bool - { - $lastExec = $fdTasksConf[0]["fdtasksconflastexectime"][0] ?? NULL; - $spamInterval = $fdTasksConf[0]["fdtasksconfintervalemails"][0] ?? NULL; - - // Multiplication is required to have the seconds - $spamInterval = $spamInterval * 60; - $antispam = $lastExec + $spamInterval; - if ($antispam <= time()) { - return TRUE; - } - - return FALSE; - } - /** * @param string $schedule * @return bool * @throws Exception + * Note : Verification of the schedule in complete string format and compare. + * DateTime will use the system timezone by default. */ - // Verification of the schedule in complete string format and compare. public function verifySchedule (string $schedule): bool { - $currentDateTime = new DateTime('now'); // Get current datetime in UTC - $scheduledDateTime = new DateTime($schedule); // Parse scheduled datetime string in UTC + $currentDateTime = new DateTime('now'); // Get current datetime in locale timezone + $scheduledDateTime = new DateTime($schedule); // Parse scheduled datetime string in local timezone if ($scheduledDateTime < $currentDateTime) { return TRUE; // Schedule has passed @@ -807,23 +678,18 @@ class TaskGateway } /** - * @param string $dn - * @return bool|string - * Note: Update the attribute lastExecTime from fdTasksConf. + * @param $objectType + * @return array|string[]|void */ - public function updateLastMailExecTime (string $dn) + public function getObjectTypeTask ($objectType) { - // prepare data - $ldap_entry["fdTasksConfLastExecTime"] = time(); - - // Add data to LDAP - try { - $result = ldap_modify($this->ds, $dn, $ldap_entry); - } catch (Exception $e) { - - $result = json_encode(["Ldap Error" => "$e"]); + $task = $this->getTask($objectType); + if (!$task) { + taskController::respondNotFound($objectType); + exit; } - return $result; + + return $task; } } \ No newline at end of file diff --git a/library/interfaces/EndpointInterface.php b/library/interfaces/EndpointInterface.php new file mode 100644 index 0000000..d26f6a4 --- /dev/null +++ b/library/interfaces/EndpointInterface.php @@ -0,0 +1,27 @@ +<?php + +interface EndpointInterface { + + public function __construct($gateway); + + /** + * @return array + * Part of the interface of orchestrator plugin to treat GET method + */ + public function processEndPointGet () : array; + + /** + * @return array + */ + public function processEndPointPost () : array; + + /** + * @return array + */ + public function processEndPointPatch () : array; + + /** + * @return array + */ + public function processEndPointDelete() : array; +} diff --git a/plugins/tasks/mail.php b/plugins/tasks/mail.php new file mode 100644 index 0000000..ad4cb41 --- /dev/null +++ b/plugins/tasks/mail.php @@ -0,0 +1,212 @@ +<?php + + +class Mail implements EndpointInterface +{ + private TaskGateway $gateway; + + function __construct ($gateway) + { + $this->gateway = $gateway; + } + + /** + * @return array + * Part of the interface of orchestrator plugin to treat GET method + */ + public function processEndPointGet (): array + { + return []; + } + + /** + * @return array + * Note : Part of the interface of orchestrator plugin to treat POST method + */ + public function processEndPointPost (): array + { + return []; + } + + /** + * @return array + * Note : Part of the interface of orchestrator plugin to treat DELETE method + */ + public function processEndPointDelete (): array + { + return []; + } + + /** + * @return array + * @throws Exception + * Note : Part of the interface of orchestrator plugin to treat PATCH method + */ + public function processEndPointPatch (): array + { + return $this->processMailTasks($this->gateway->getObjectTypeTask('Mail Object)')); + } + + /** + * @param array $tasks + * @return array + * @throws Exception + */ + public function processMailTasks (array $tasks): array + { + $result = []; + + $fdTasksConf = $this->getMailObjectConfiguration(); + $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); + + // Increment for anti=spam, starts at 0, each mail task only contain one email, addition if simply + one. + $maxMailsIncrement = 0; + + if ($this->verifySpamProtection($fdTasksConf)) { + // Note : if list_tasks is empty, the controller receive null as result and will log/process it properly. + foreach ($tasks as $mail) { + + // verify status before processing (to be checked with schedule as well). + if ($mail["fdtasksgranularstatus"][0] == 1 && $this->gateway->verifySchedule($mail["fdtasksgranularschedule"][0])) { + + // Search for the related attached mail object. + $mailInfos = $this->retrieveMailTemplateInfos($mail["fdtasksgranularref"][0]); + $mailContent = $mailInfos[0]; + + // Only takes arrays related to files attachments for the mail template selected + unset($mailInfos[0]); + // Remove count from array. + $this->gateway->unsetCountKeys($mailInfos); + $mailAttachments = array_values($mailInfos); + + $setFrom = $mail["fdtasksgranularmailfrom"][0]; + $setBCC = $mail["fdtasksgranularmailbcc"][0] ?? NULL; + $recipients = $mail["fdtasksgranularmail"]; + $body = $mailContent["fdmailtemplatebody"][0]; + $signature = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $subject = $mailContent["fdmailtemplatesubject"][0]; + $receipt = $mailContent["fdmailtemplatereadreceipt"][0]; + + foreach ($mailAttachments as $file) { + $fileInfo['cn'] = $file['cn'][0]; + $fileInfo['content'] = $file['fdmailattachmentscontent'][0]; + $attachments[] = $fileInfo; + } + + // Required before passing the array to the constructor mail. + if (empty($attachments)) { + $attachments = NULL; + } + + $mail_controller = new MailController($setFrom, + $setBCC, + $recipients, + $body, + $signature, + $subject, + $receipt, + $attachments); + + $mailSentResult = $mail_controller->sendMail(); + + if ($mailSentResult[0] == "SUCCESS") { + + // The third arguments "2" is the status code of success for mail as of now 18/11/22 + $result[$mail["dn"]]['statusUpdate'] = $this->gateway->updateTaskStatus($mail["dn"], $mail["cn"][0], "2"); + $result[$mail["dn"]]['mailStatus'] = 'mail : ' . $mail["dn"] . ' was successfully sent'; + $result[$mail["dn"]]['updateLastMailExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); + + } else { + $result[$mail["dn"]]['statusUpdate'] = $this->gateway->updateTaskStatus($mail["dn"], $mail["cn"][0], $mailSentResult[0]); + $result[$mail["dn"]]['Error'] = $mailSentResult; + } + + // Verification anti-spam max mails to be sent and quit loop if matched + $maxMailsIncrement += 1; //Only one as recipients in mail object is always one email. + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } + + } + } + } + + return $result; + } + + /** + * @return array + * Note : A simple retrieval methods of the mail backend configuration set in FusionDirectory + */ + private function getMailObjectConfiguration (): array + { + return $this->gateway->getLdapTasks( + "(objectClass=fdTasksConf)", + ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] + ); + } + + /** + * @param array $fdTasksConf + * @return int + * Note : Allows a safety check in case mail configuration backed within FD has been missed. (50). + */ + public function returnMaximumMailToBeSend (array $fdTasksConf): int + { + // set the maximum mails to be sent to the configured value or 50 if not set. + return $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; + } + + /** + * @param array $fdTasksConf + * @return bool + * Note : Method which verify the last executed e-mails sent + * Verify if the time interval is respected in order to protect from SPAM + */ + public function verifySpamProtection (array $fdTasksConf): bool + { + $lastExec = $fdTasksConf[0]["fdtasksconflastexectime"][0] ?? NULL; + $spamInterval = $fdTasksConf[0]["fdtasksconfintervalemails"][0] ?? NULL; + + // Multiplication is required to have the seconds + $spamInterval = $spamInterval * 60; + $antispam = $lastExec + $spamInterval; + if ($antispam <= time()) { + return TRUE; + } + + return FALSE; + } + + /** + * @param string $templateName + * @return array + * Note :simply retrieve all information linked to a mail template object. + */ + public function retrieveMailTemplateInfos (string $templateName): array + { + return $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $templateName); + } + + /** + * @param string $dn + * @return bool|string + * Note: Update the attribute lastExecTime from fdTasksConf. + */ + public function updateLastMailExecTime (string $dn) + { + // prepare data + $ldap_entry["fdTasksConfLastExecTime"] = time(); + + // Add data to LDAP + try { + $result = ldap_modify($this->gateway->ds, $dn, $ldap_entry); + } catch (Exception $e) { + + $result = json_encode(["Ldap Error" => "$e"]); + } + return $result; + } + + +} -- GitLab From e70060792a3ce0be4768dc886a81268c3a44b6ef Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Fri, 31 May 2024 17:46:16 +0100 Subject: [PATCH 030/111] :sparkles: Feat(Orchestrator) - Iteration 9 - lifecycle and autoloader update lifecycle is not integrated as plugin --- config/bootstrap.php | 28 +-- library/TaskController.php | 9 +- library/TaskGateway.php | 312 +++++++++++++-------------- plugins/tasks/LifeCycle.php | 208 ++++++++++++++++++ plugins/tasks/{mail.php => Mail.php} | 0 5 files changed, 382 insertions(+), 175 deletions(-) create mode 100644 plugins/tasks/LifeCycle.php rename plugins/tasks/{mail.php => Mail.php} (100%) diff --git a/config/bootstrap.php b/config/bootstrap.php index baf9d5b..793d219 100644 --- a/config/bootstrap.php +++ b/config/bootstrap.php @@ -8,21 +8,23 @@ function autoload ($class) { // Integrator is required - require '/usr/share/php/FusionDirectory/autoloader.php'; + require_once '/usr/share/php/FusionDirectory/autoloader.php'; + // avoid error handler requirements error, as it should be one of the first to load ? + require_once dirname(__DIR__).'/library/ErrorHandler.php'; if (strpos($class, 'PHPMailer') !== FALSE) { - require("/usr/share/php/libphp-phpmailer/src/Exception.php"); - require("/usr/share/php/libphp-phpmailer/src/PHPMailer.php"); - require("/usr/share/php/libphp-phpmailer/src/SMTP.php"); + require_once("/usr/share/php/libphp-phpmailer/src/Exception.php"); + require_once("/usr/share/php/libphp-phpmailer/src/PHPMailer.php"); + require_once("/usr/share/php/libphp-phpmailer/src/SMTP.php"); } - $relative_class = str_replace('\\', '/', $class) . '.php'; - $base_dirs = ['/usr/share/php', dirname(__DIR__)]; - $files = []; + $relative_class = str_replace('\\', '/', $class) . '.php'; + $base_dirs = ['/usr/share/php', dirname(__DIR__)]; + $files = []; foreach ($base_dirs as $base_dir) { - $dir = new RecursiveDirectoryIterator($base_dir); - $iter = new RecursiveIteratorIterator($dir); - $regex = new RegexIterator($iter, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); + $dir = new RecursiveDirectoryIterator($base_dir); + $iter = new RecursiveIteratorIterator($dir); + $regex = new RegexIterator($iter, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); foreach ($regex as $file) { $files[] = $file[0]; @@ -30,12 +32,12 @@ function autoload ($class) } foreach ($files as $file) { - if (strpos($file, $relative_class) !== FALSE) { - - require $file; + if (stripos(strtolower($file), strtolower($relative_class)) !== FALSE) { + require_once $file; break; } } + } spl_autoload_register('autoload'); diff --git a/library/TaskController.php b/library/TaskController.php index 26aacc0..f42e435 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -35,6 +35,9 @@ class TaskController */ public function processRequest (string $method, ?string $objectType, $jsonBody = NULL): void { + // Allow result to be nullable. + $result = null; + // If no specific tasks object specified, return all tasks if ($objectType == NULL) { if ($method == "GET") { @@ -64,12 +67,6 @@ class TaskController // PATCH methods case "PATCH": switch ($objectType) { - // case "mail": - // $result = $this->gateway->processMailTasks($this->getObjectTypeTask($objectType)); - // break; - case 'lifeCycle': - $result = $this->gateway->processLifeCycleTasks($this->getObjectTypeTask($objectType)); - break; case 'removeSubTasks': $result = $this->gateway->removeCompletedTasks(); break; diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 0760502..d920d70 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -304,117 +304,117 @@ class TaskGateway return $result; } - /** - * @param array $list_tasks - * @return array[]|string[] - * @throws Exception - * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. - */ - public function processLifeCycleTasks (array $list_tasks): array - { - // Array representing the status of the subtask. - $result = []; - // Initiate the object webservice. - $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); - // Required to prepare future webservice call. E.g. Retrieval of mandatory token. - $webservice->setCurlSettings(); - - foreach ($list_tasks as $task) { - // If the tasks must be treated - status and scheduled - process the sub-tasks - if ($this->statusAndScheduleCheck($task)) { - - // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes - $lifeCycleBehavior = $this->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', - 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', - 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], - '', $task['fdtasksgranularmaster'][0]); - - // Simply retrieve the current supannStatus of the user DN related to the task at hand. - $currentUserLifeCycle = $this->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], - '', $task['fdtasksgranulardn'][0]); - - // Compare both the required schedule and the current user status - returning TRUE if modification is required. - if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { - - // This will call a method to modify the ressourcesSupannEtatDate of the DN linked to the subTask - $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0]); - if ($lifeCycleResult === TRUE) { - - $result[$task['dn']]['results'] = json_encode("Account states have been successfully modified for " . $task['fdtasksgranulardn'][0]); - // Status of the task must be updated to success - $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], '2'); - - // Here the user is refresh in order to activate methods based on supann Status changes. - $result[$task['dn']]['refreshUser'] = $webservice->refreshUserInfo($task['fdtasksgranulardn'][0]); - - // In case the modification failed - } else { - $result[$task['dn']]['results'] = json_encode("Error updating " . $task['fdtasksgranulardn'][0] . "-" . $lifeCycleResult); - // Update of the task status error message - $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], $lifeCycleResult); - } - // Verification if the sub-task status has been updated correctly - if ($updateResult === TRUE) { - $result[$task['dn']]['statusUpdate'] = 'Success'; - } else { - $result[$task['dn']]['statusUpdate'] = $updateResult; - } - // Remove the subtask has it is not required to update it nor to process it. - } else { - $result[$task['dn']]['results'] = 'Sub-task removed for : ' . $task['fdtasksgranulardn'][0] . ' with result : ' - . $this->removeSubTask($task['dn']); - $result[$task['dn']]['statusUpdate'] = 'No updates required, sub-task will be removed.'; - } - } - } - // If array is empty, no tasks of type life cycle needs to be treated. - if (empty($result)) { - $result = 'No tasks of type "Life Cycle" requires processing.'; - } - return [$result]; - } - - /** - * @param array $lifeCycleBehavior - * @param string $userDN - * @return bool|string - */ - protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) - { - // Extracting values of desired post-state behavior - $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; - $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; - $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional - $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional - - // Require the date of today to update the start of the new resources (If change of status). - $currentDate = new DateTime(); - // Date of today + numbers of days to add for end date. - $newEndDate = new DateTime(); - $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); - - // Prepare the ldap entry to be modified - $ldapEntry = []; - $ldapEntry['supannRessourceEtatDate'] = "{" . $newEntry['Resource'] . "}" - . $newEntry['State'] . ":" - . $newEntry['SubState'] . ":" . - $currentDate->format('Ymd') . ":" - . $newEndDate->format('Ymd'); - - try { - $result = ldap_modify($this->ds, $userDN, $ldapEntry); - } catch (Exception $e) { - $result = json_encode(["Ldap Error" => "$e"]); - } - - return $result; - } +// /** +// * @param array $list_tasks +// * @return array[]|string[] +// * @throws Exception +// * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. +// */ +// public function processLifeCycleTasks (array $list_tasks): array +// { +// // Array representing the status of the subtask. +// $result = []; +// // Initiate the object webservice. +// $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); +// // Required to prepare future webservice call. E.g. Retrieval of mandatory token. +// $webservice->setCurlSettings(); +// +// foreach ($list_tasks as $task) { +// // If the tasks must be treated - status and scheduled - process the sub-tasks +// if ($this->statusAndScheduleCheck($task)) { +// +// // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes +// $lifeCycleBehavior = $this->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', +// 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', +// 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], +// '', $task['fdtasksgranularmaster'][0]); +// +// // Simply retrieve the current supannStatus of the user DN related to the task at hand. +// $currentUserLifeCycle = $this->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], +// '', $task['fdtasksgranulardn'][0]); +// +// // Compare both the required schedule and the current user status - returning TRUE if modification is required. +// if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { +// +// // This will call a method to modify the ressourcesSupannEtatDate of the DN linked to the subTask +// $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0]); +// if ($lifeCycleResult === TRUE) { +// +// $result[$task['dn']]['results'] = json_encode("Account states have been successfully modified for " . $task['fdtasksgranulardn'][0]); +// // Status of the task must be updated to success +// $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], '2'); +// +// // Here the user is refresh in order to activate methods based on supann Status changes. +// $result[$task['dn']]['refreshUser'] = $webservice->refreshUserInfo($task['fdtasksgranulardn'][0]); +// +// // In case the modification failed +// } else { +// $result[$task['dn']]['results'] = json_encode("Error updating " . $task['fdtasksgranulardn'][0] . "-" . $lifeCycleResult); +// // Update of the task status error message +// $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], $lifeCycleResult); +// } +// // Verification if the sub-task status has been updated correctly +// if ($updateResult === TRUE) { +// $result[$task['dn']]['statusUpdate'] = 'Success'; +// } else { +// $result[$task['dn']]['statusUpdate'] = $updateResult; +// } +// // Remove the subtask has it is not required to update it nor to process it. +// } else { +// $result[$task['dn']]['results'] = 'Sub-task removed for : ' . $task['fdtasksgranulardn'][0] . ' with result : ' +// . $this->removeSubTask($task['dn']); +// $result[$task['dn']]['statusUpdate'] = 'No updates required, sub-task will be removed.'; +// } +// } +// } +// // If array is empty, no tasks of type life cycle needs to be treated. +// if (empty($result)) { +// $result = 'No tasks of type "Life Cycle" requires processing.'; +// } +// return [$result]; +// } + +// /** +// * @param array $lifeCycleBehavior +// * @param string $userDN +// * @return bool|string +// */ +// protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) +// { +// // Extracting values of desired post-state behavior +// $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; +// $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; +// $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional +// $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional +// +// // Require the date of today to update the start of the new resources (If change of status). +// $currentDate = new DateTime(); +// // Date of today + numbers of days to add for end date. +// $newEndDate = new DateTime(); +// $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); +// +// // Prepare the ldap entry to be modified +// $ldapEntry = []; +// $ldapEntry['supannRessourceEtatDate'] = "{" . $newEntry['Resource'] . "}" +// . $newEntry['State'] . ":" +// . $newEntry['SubState'] . ":" . +// $currentDate->format('Ymd') . ":" +// . $newEndDate->format('Ymd'); +// +// try { +// $result = ldap_modify($this->ds, $userDN, $ldapEntry); +// } catch (Exception $e) { +// $result = json_encode(["Ldap Error" => "$e"]); +// } +// +// return $result; +// } /** * @param bool|string $subTaskDn * @return bool|string */ - protected function removeSubTask ($subTaskDn) + public function removeSubTask ($subTaskDn) { try { $result = ldap_delete($this->ds, $subTaskDn); @@ -539,56 +539,56 @@ class TaskGateway return $result; } - /** - * @param array $lifeCycleBehavior - * @param array $currentUserLifeCycle - * @return bool - * Note receive the life cycle behavior desired and compare it the received current user life cycle, returning TRUE - * if there is indeed a difference and therefore must update the user information. - * In case the comparison is impossible due to the use not having a status listed, it will report false. - */ - protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool - { - $result = FALSE; - // Regular expression in order to extract the supann format within an array - $pattern = '/\{(\w+)\}(\w):([^:]*)(?::([^:]*))?(?::([^:]*))?(?::([^:]*))?/'; - - // In case the tasks is launched without supann being activated on the user account, return error - if (empty($currentUserLifeCycle[0]['supannressourceetatdate'][0])) { - return FALSE; - } - // Perform the regular expression match - preg_match($pattern, $currentUserLifeCycle[0]['supannressourceetatdate'][0], $matches); - - // Extracting values of current user - $userSupann['Resource'] = $matches[1] ?? ''; - $userSupann['State'] = $matches[2] ?? ''; - $userSupann['SubState'] = $matches[3] ?? ''; - // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. - $userSupann['EndDate'] = $matches[5] ?? ''; - - // Extracting values of desired pre-state behavior - $preStateSupann['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepreresource'][0]; - $preStateSupann['State'] = $lifeCycleBehavior[0]['fdtaskslifecycleprestate'][0]; - $preStateSupann['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepresubstate'][0] ?? ''; //SubState is optional - - // Verifying if the user end date for selected resource is overdue - if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { - // Comparing value in a nesting conditions - if ($userSupann['Resource'] == $preStateSupann['Resource']) { - if ($userSupann['State'] == $preStateSupann['State']) { - // as SubState is optional, if both resource and state match at this point, modification is allowed. - if (empty($preStateSupann['SubState'])) { - $result = TRUE; - } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { - $result = TRUE; - } - } - } - } - - return $result; - } +// /** +// * @param array $lifeCycleBehavior +// * @param array $currentUserLifeCycle +// * @return bool +// * Note receive the life cycle behavior desired and compare it the received current user life cycle, returning TRUE +// * if there is indeed a difference and therefore must update the user information. +// * In case the comparison is impossible due to the use not having a status listed, it will report false. +// */ +// protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool +// { +// $result = FALSE; +// // Regular expression in order to extract the supann format within an array +// $pattern = '/\{(\w+)\}(\w):([^:]*)(?::([^:]*))?(?::([^:]*))?(?::([^:]*))?/'; +// +// // In case the tasks is launched without supann being activated on the user account, return error +// if (empty($currentUserLifeCycle[0]['supannressourceetatdate'][0])) { +// return FALSE; +// } +// // Perform the regular expression match +// preg_match($pattern, $currentUserLifeCycle[0]['supannressourceetatdate'][0], $matches); +// +// // Extracting values of current user +// $userSupann['Resource'] = $matches[1] ?? ''; +// $userSupann['State'] = $matches[2] ?? ''; +// $userSupann['SubState'] = $matches[3] ?? ''; +// // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. +// $userSupann['EndDate'] = $matches[5] ?? ''; +// +// // Extracting values of desired pre-state behavior +// $preStateSupann['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepreresource'][0]; +// $preStateSupann['State'] = $lifeCycleBehavior[0]['fdtaskslifecycleprestate'][0]; +// $preStateSupann['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepresubstate'][0] ?? ''; //SubState is optional +// +// // Verifying if the user end date for selected resource is overdue +// if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { +// // Comparing value in a nesting conditions +// if ($userSupann['Resource'] == $preStateSupann['Resource']) { +// if ($userSupann['State'] == $preStateSupann['State']) { +// // as SubState is optional, if both resource and state match at this point, modification is allowed. +// if (empty($preStateSupann['SubState'])) { +// $result = TRUE; +// } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { +// $result = TRUE; +// } +// } +// } +// } +// +// return $result; +// } /** * @param string $schedule diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php new file mode 100644 index 0000000..b475e42 --- /dev/null +++ b/plugins/tasks/LifeCycle.php @@ -0,0 +1,208 @@ +<?php + + +class LifeCycle implements EndpointInterface +{ + private TaskGateway $gateway; + + function __construct ($gateway) + { + $this->gateway = $gateway; + } + + /** + * @return array + * Part of the interface of orchestrator plugin to treat GET method + */ + public function processEndPointGet (): array + { + return []; + } + + /** + * @return array + * Note : Part of the interface of orchestrator plugin to treat POST method + */ + public function processEndPointPost (): array + { + return []; + } + + /** + * @return array + * Note : Part of the interface of orchestrator plugin to treat DELETE method + */ + public function processEndPointDelete (): array + { + return []; + } + + /** + * @return array + * @throws Exception + * Note : Part of the interface of orchestrator plugin to treat PATCH method + */ + public function processEndPointPatch (): array + { + echo json_encode('within lifeCycle patch'); + return $this->processLifeCycleTasks($this->gateway->getObjectTypeTask('lifeCycle')); + } + + /** + * @param array $list_tasks + * @return array[]|string[] + * @throws Exception + * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. + */ + public function processLifeCycleTasks (array $list_tasks): array + { + // Array representing the status of the subtask. + $result = []; + // Initiate the object webservice. + $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); + // Required to prepare future webservice call. E.g. Retrieval of mandatory token. + $webservice->setCurlSettings(); + + foreach ($list_tasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks + if ($this->gateway->statusAndScheduleCheck($task)) { + + // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes + $lifeCycleBehavior = $this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', + 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', + 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], + '', $task['fdtasksgranularmaster'][0]); + + // Simply retrieve the current supannStatus of the user DN related to the task at hand. + $currentUserLifeCycle = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], + '', $task['fdtasksgranulardn'][0]); + + // Compare both the required schedule and the current user status - returning TRUE if modification is required. + if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { + + // This will call a method to modify the ressourcesSupannEtatDate of the DN linked to the subTask + $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0]); + if ($lifeCycleResult === TRUE) { + + $result[$task['dn']]['results'] = json_encode("Account states have been successfully modified for " . $task['fdtasksgranulardn'][0]); + // Status of the task must be updated to success + $updateResult = $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2'); + + // Here the user is refresh in order to activate methods based on supann Status changes. + $result[$task['dn']]['refreshUser'] = $webservice->refreshUserInfo($task['fdtasksgranulardn'][0]); + + // In case the modification failed + } else { + $result[$task['dn']]['results'] = json_encode("Error updating " . $task['fdtasksgranulardn'][0] . "-" . $lifeCycleResult); + // Update of the task status error message + $updateResult = $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $lifeCycleResult); + } + // Verification if the sub-task status has been updated correctly + if ($updateResult === TRUE) { + $result[$task['dn']]['statusUpdate'] = 'Success'; + } else { + $result[$task['dn']]['statusUpdate'] = $updateResult; + } + // Remove the subtask has it is not required to update it nor to process it. + } else { + $result[$task['dn']]['results'] = 'Sub-task removed for : ' . $task['fdtasksgranulardn'][0] . ' with result : ' + . $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['statusUpdate'] = 'No updates required, sub-task will be removed.'; + } + } + } + // If array is empty, no tasks of type life cycle needs to be treated. + if (empty($result)) { + $result = 'No tasks of type "Life Cycle" requires processing.'; + } + return [$result]; + } + + /** + * @param array $lifeCycleBehavior + * @param array $currentUserLifeCycle + * @return bool + * Note receive the life cycle behavior desired and compare it the received current user life cycle, returning TRUE + * if there is indeed a difference and therefore must update the user information. + * In case the comparison is impossible due to the use not having a status listed, it will report false. + */ + protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool + { + $result = FALSE; + // Regular expression in order to extract the supann format within an array + $pattern = '/\{(\w+)\}(\w):([^:]*)(?::([^:]*))?(?::([^:]*))?(?::([^:]*))?/'; + + // In case the tasks is launched without supann being activated on the user account, return error + if (empty($currentUserLifeCycle[0]['supannressourceetatdate'][0])) { + return FALSE; + } + // Perform the regular expression match + preg_match($pattern, $currentUserLifeCycle[0]['supannressourceetatdate'][0], $matches); + + // Extracting values of current user + $userSupann['Resource'] = $matches[1] ?? ''; + $userSupann['State'] = $matches[2] ?? ''; + $userSupann['SubState'] = $matches[3] ?? ''; + // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. + $userSupann['EndDate'] = $matches[5] ?? ''; + + // Extracting values of desired pre-state behavior + $preStateSupann['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepreresource'][0]; + $preStateSupann['State'] = $lifeCycleBehavior[0]['fdtaskslifecycleprestate'][0]; + $preStateSupann['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepresubstate'][0] ?? ''; //SubState is optional + + // Verifying if the user end date for selected resource is overdue + if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { + // Comparing value in a nesting conditions + if ($userSupann['Resource'] == $preStateSupann['Resource']) { + if ($userSupann['State'] == $preStateSupann['State']) { + // as SubState is optional, if both resource and state match at this point, modification is allowed. + if (empty($preStateSupann['SubState'])) { + $result = TRUE; + } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { + $result = TRUE; + } + } + } + } + + return $result; + } + + /** + * @param array $lifeCycleBehavior + * @param string $userDN + * @return bool|string + */ + protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) + { + // Extracting values of desired post-state behavior + $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; + $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; + $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional + $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional + + // Require the date of today to update the start of the new resources (If change of status). + $currentDate = new DateTime(); + // Date of today + numbers of days to add for end date. + $newEndDate = new DateTime(); + $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); + + // Prepare the ldap entry to be modified + $ldapEntry = []; + $ldapEntry['supannRessourceEtatDate'] = "{" . $newEntry['Resource'] . "}" + . $newEntry['State'] . ":" + . $newEntry['SubState'] . ":" . + $currentDate->format('Ymd') . ":" + . $newEndDate->format('Ymd'); + + try { + $result = ldap_modify($this->gateway->ds, $userDN, $ldapEntry); + } catch (Exception $e) { + $result = json_encode(["Ldap Error" => "$e"]); + } + + return $result; + } + +} diff --git a/plugins/tasks/mail.php b/plugins/tasks/Mail.php similarity index 100% rename from plugins/tasks/mail.php rename to plugins/tasks/Mail.php -- GitLab From 2ea6256085a591c56906dbb4ab65d7757fff3c59 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Fri, 31 May 2024 18:08:04 +0100 Subject: [PATCH 031/111] :sparkles: Feat(Orchestrator) - Iteration 10 - part 1 notifications as plugin notifications as plugin --- library/interfaces/EndpointInterface.php | 5 ++- plugins/tasks/LifeCycle.php | 2 +- plugins/tasks/Mail.php | 2 +- plugins/tasks/notifications.php | 45 ++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 plugins/tasks/notifications.php diff --git a/library/interfaces/EndpointInterface.php b/library/interfaces/EndpointInterface.php index d26f6a4..aafb0b9 100644 --- a/library/interfaces/EndpointInterface.php +++ b/library/interfaces/EndpointInterface.php @@ -2,7 +2,7 @@ interface EndpointInterface { - public function __construct($gateway); + public function __construct(TaskGateway $gateway); /** * @return array @@ -12,16 +12,19 @@ interface EndpointInterface { /** * @return array + * Part of the interface of orchestrator plugin to treat POST method */ public function processEndPointPost () : array; /** * @return array + * Part of the interface of orchestrator plugin to treat PATCH method */ public function processEndPointPatch () : array; /** * @return array + * Part of the interface of orchestrator plugin to treat DELETE method */ public function processEndPointDelete() : array; } diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index b475e42..333562e 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -5,7 +5,7 @@ class LifeCycle implements EndpointInterface { private TaskGateway $gateway; - function __construct ($gateway) + function __construct (TaskGateway $gateway) { $this->gateway = $gateway; } diff --git a/plugins/tasks/Mail.php b/plugins/tasks/Mail.php index ad4cb41..538c150 100644 --- a/plugins/tasks/Mail.php +++ b/plugins/tasks/Mail.php @@ -5,7 +5,7 @@ class Mail implements EndpointInterface { private TaskGateway $gateway; - function __construct ($gateway) + function __construct (TaskGateway $gateway) { $this->gateway = $gateway; } diff --git a/plugins/tasks/notifications.php b/plugins/tasks/notifications.php new file mode 100644 index 0000000..c8bf02f --- /dev/null +++ b/plugins/tasks/notifications.php @@ -0,0 +1,45 @@ +<?php + +class notifications 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 + { + return []; + } + + /** + * @return array + */ + public function processEndPointPost (): array + { + return []; + } + + /** + * @return array + */ + public function processEndPointPatch (): array + { + return []; + } + + /** + * @return array + */ + public function processEndPointDelete (): array + { + return []; + } +} \ No newline at end of file -- GitLab From 47253456e70b30a0acb59f2e97d1e6847223d735 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Fri, 31 May 2024 18:11:31 +0100 Subject: [PATCH 032/111] :sparkles: Feat(Orchestrator) - Iteration 11 - part 2 notifications as plugin notifications as plugin --- library/TaskController.php | 3 -- library/TaskGateway.php | 50 ------------------------------- plugins/tasks/notifications.php | 52 ++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 54 deletions(-) diff --git a/library/TaskController.php b/library/TaskController.php index f42e435..9f05878 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -73,9 +73,6 @@ class TaskController case 'activateCyclicTasks': $result = $this->gateway->activateCyclicTasks(); break; - case 'notifications': - $result = $this->gateway->processNotifications($this->getObjectTypeTask($objectType)); - break; case $objectType: if (class_exists($objectType)) { $endpoint = new $objectType($this->gateway); diff --git a/library/TaskGateway.php b/library/TaskGateway.php index d920d70..4413238 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -154,56 +154,6 @@ class TaskGateway return $auditAttributes; } - public function processNotifications (array $notificationsSubTasks): array - { - $result = []; - // It will contain all required notifications to be sent per main task. - $notifications = []; - - foreach ($notificationsSubTasks as $task) { - // If the tasks must be treated - status and scheduled - process the sub-tasks - if ($this->statusAndScheduleCheck($task)) { - - // Retrieve data from the main task - $notificationsMainTask = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]); - $notificationsMainTaskName = $task['fdtasksgranularmaster'][0]; - - // Generate the mail form with all mail controller requirements - $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask); - // Simply retrieve the list of audited attributes - $auditAttributes = $this->retrieveAuditedAttributes($task); - - $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; - $this->unsetCountKeys($monitoredAttrs); - - // Verify if there is a match between audited attributes and monitored attributes from main task. - $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); - - if (!empty($matchingAttrs)) { - // Fill an array with UID of audited user and related matching attributes - $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; - - // Require to be set for updating the status of the task later on. - $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; - $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; - $notifications[$notificationsMainTaskName]['mailForm'] = $mailTemplateForm; - // Overwrite array notifications with complementing mail form body with uid and related attributes. - $notifications = $this->completeNotificationsBody($notifications, $notificationsMainTaskName); - - } else { // Simply remove the subTask has no notifications are required - $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); - $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; - } - } - } - - if (!empty($notifications)) { - $result[] = $this->sendNotificationsMail($notifications); - } - - return $result; - } - /** * @param array $notifications * @return array diff --git a/plugins/tasks/notifications.php b/plugins/tasks/notifications.php index c8bf02f..82330f4 100644 --- a/plugins/tasks/notifications.php +++ b/plugins/tasks/notifications.php @@ -32,7 +32,7 @@ class notifications implements EndpointInterface */ public function processEndPointPatch (): array { - return []; + return $this->processNotifications($this->gateway->getObjectTypeTask('notifications)')); } /** @@ -42,4 +42,54 @@ class notifications implements EndpointInterface { return []; } + + public function processNotifications (array $notificationsSubTasks): array + { + $result = []; + // It will contain all required notifications to be sent per main task. + $notifications = []; + + foreach ($notificationsSubTasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks + if ($this->statusAndScheduleCheck($task)) { + + // Retrieve data from the main task + $notificationsMainTask = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]); + $notificationsMainTaskName = $task['fdtasksgranularmaster'][0]; + + // Generate the mail form with all mail controller requirements + $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask); + // Simply retrieve the list of audited attributes + $auditAttributes = $this->retrieveAuditedAttributes($task); + + $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; + $this->unsetCountKeys($monitoredAttrs); + + // Verify if there is a match between audited attributes and monitored attributes from main task. + $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); + + if (!empty($matchingAttrs)) { + // Fill an array with UID of audited user and related matching attributes + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; + + // Require to be set for updating the status of the task later on. + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + $notifications[$notificationsMainTaskName]['mailForm'] = $mailTemplateForm; + // Overwrite array notifications with complementing mail form body with uid and related attributes. + $notifications = $this->completeNotificationsBody($notifications, $notificationsMainTaskName); + + } else { // Simply remove the subTask has no notifications are required + $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; + } + } + } + + if (!empty($notifications)) { + $result[] = $this->sendNotificationsMail($notifications); + } + + return $result; + } } \ No newline at end of file -- GitLab From 4bf4d673363643cdcb75e98ea765cbb7c70a8010 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 16:00:29 +0100 Subject: [PATCH 033/111] :sparkles: Feat(Orchestrator) - Iteration 12 - last part notifications as plugin notifications as plugin and interface enhancement --- library/TaskController.php | 5 +- library/TaskGateway.php | 345 ++++++++++++----------- library/interfaces/EndpointInterface.php | 22 +- plugins/tasks/Mail.php | 30 +- plugins/tasks/notifications.php | 210 +++++++++++++- 5 files changed, 410 insertions(+), 202 deletions(-) diff --git a/library/TaskController.php b/library/TaskController.php index 9f05878..3679009 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -52,7 +52,6 @@ class TaskController switch ($method) { // GET methods case "GET": - // The switch here is created to have potential additions later on, switch ($objectType) { case $objectType: if (class_exists($objectType)) { @@ -60,6 +59,10 @@ class TaskController $result = $endpoint->processEndPointGet(); } break; + + default: + // TODO: Default result in a non getProcess method found - could enhance below error report. + $this->respondMethodAllowed("GET, PATCH, DELETE"); } $this->parseJsonResult($result); break; diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 4413238..1373e7f 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -8,6 +8,9 @@ */ class TaskGateway { + /** + * @var resource|null + */ public $ds; // Variable type can be LDAP : enhancement @@ -32,7 +35,7 @@ class TaskGateway case "notifications": $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Notifications))"); - $this->unsetCountKeys($list_tasks);; + $this->unsetCountKeys($list_tasks); break; case "removeSubTasks": @@ -71,40 +74,40 @@ class TaskGateway return $task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0]); } - /** - * @param string $mainTaskDn - * @return array - */ - public function getNotificationsMainTask (string $mainTaskDn): array - { - // Retrieve data from the main task - return $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', - 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], - '', $mainTaskDn); - } +// /** +// * @param string $mainTaskDn +// * @return array +// */ +// public function getNotificationsMainTask (string $mainTaskDn): array +// { +// // Retrieve data from the main task +// return $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', +// 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], +// '', $mainTaskDn); +// } - private function generateMainTaskMailTemplate (array $mainTask): array - { - // Generate email configuration for each result of subtasks having the same main task.w - $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; - unset($recipients['count']); - $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; - $mailTemplateName = $mainTask[0]['fdtasksnotificationsmailtemplate'][0]; - - $mailInfos = $this->retrieveMailTemplateInfos($mailTemplateName); - $mailContent = $mailInfos[0]; - - // Set the notification array with all required variable for all sub-tasks of same main task origin. - $mailForm['setFrom'] = $sender; - $mailForm['recipients'] = $recipients; - $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; - $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; - $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; - $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; - - return $mailForm; - } +// private function generateMainTaskMailTemplate (array $mainTask): array +// { +// // Generate email configuration for each result of subtasks having the same main task.w +// $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; +// unset($recipients['count']); +// $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; +// $mailTemplateName = $mainTask[0]['fdtasksnotificationsmailtemplate'][0]; +// +// $mailInfos = $this->retrieveMailTemplateInfos($mailTemplateName); +// $mailContent = $mailInfos[0]; +// +// // Set the notification array with all required variable for all sub-tasks of same main task origin. +// $mailForm['setFrom'] = $sender; +// $mailForm['recipients'] = $recipients; +// $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; +// $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; +// $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; +// $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; +// +// return $mailForm; +// } /** * @param $array @@ -122,137 +125,137 @@ class TaskGateway } } - /** - * @param array $notificationTask - * @return array - * NOTE : receive a unique tasks of type notification (one subtask at a time) - */ - protected function retrieveAuditedAttributes (array $notificationTask): array - { - $auditAttributes = []; - - // Retrieve audit data attributes from the list of references set in the sub-task - if (!empty($notificationTask['fdtasksgranularref'])) { - // Remove count keys (count is shared by ldap). - $this->unsetCountKeys($notificationTask); - - foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { - $auditInformation[] = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', - ['fdAuditAttributes'], '', $auditDN); - } - // Again remove key: count retrieved from LDAP. - $this->unsetCountKeys($auditInformation); - // It is possible that an audit does not contain any attributes changes, condition is required. - foreach ($auditInformation as $audit => $attr) { - if (!empty($attr[0]['fdauditattributes'])) { - // Clear and compact received results from above ldap search - $auditAttributes = $attr[0]['fdauditattributes']; - } - } - } - - return $auditAttributes; - } - - /** - * @param array $notifications - * @return array - * Note : This method is present to add to the mailForm body the proper uid and attrs info. - */ - private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array - { - // Iterate through each subTask and related attrs - $uidAttrsText = []; - - foreach ($notifications[$notificationsMainTaskName]['subTask'] as $value) { - $uidName = $value['uid']; - $attrs = []; - - foreach ($value['attrs'] as $attr) { - $attrs[] = $attr; - } - $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; - } - - // Make the array unique, avoiding uid and same attribute duplication. - $uidAttrsText = array_unique($uidAttrsText); - // Add uid names and related attrs to mailForm['body'] - $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); - - return $notifications; - } - - protected function sendNotificationsMail (array $notifications): array - { - $result = []; - // Re-use of the same mail processing template logic - $fdTasksConf = $this->getMailObjectConfiguration(); - $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); - - /* - Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is - sent per main task. - */ - $maxMailsIncrement = 0; - - foreach ($notifications as $data) { - $numberOfRecipients = count($data['mailForm']['recipients']); - - $mail_controller = new MailController( - $data['mailForm']['setFrom'], - NULL, - $data['mailForm']['recipients'], - $data['mailForm']['body'], - $data['mailForm']['signature'], - $data['mailForm']['subject'], - $data['mailForm']['receipt'], - NULL - ); - - $mailSentResult = $mail_controller->sendMail(); - $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); - - // Verification anti-spam max mails to be sent and quit loop if matched. - $maxMailsIncrement += $numberOfRecipients; - if ($maxMailsIncrement == $maxMailsConfig) { - break; - } - } - - return $result; - } - - protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array - { - $result = []; - if ($serverResults[0] == "SUCCESS") { - foreach ($subTask['subTask'] as $subTask => $details) { - - // CN of the main task - $cn = $subTask; - // DN of the main task - $dn = $details['dn']; - - // Update task status for the current $dn - $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, "2"); - $result[$dn]['mailStatus'] = 'Notification was successfully sent'; - $result[$dn]['updateLastMailExec'] = $this->updateLastMailExecTime($mailTaskBackend[0]["dn"]); - } - } else { - foreach ($subTask['subTask'] as $subTask => $details) { +// /** +// * @param array $notificationTask +// * @return array +// * NOTE : receive a unique tasks of type notification (one subtask at a time) +// */ +// protected function retrieveAuditedAttributes (array $notificationTask): array +// { +// $auditAttributes = []; +// +// // Retrieve audit data attributes from the list of references set in the sub-task +// if (!empty($notificationTask['fdtasksgranularref'])) { +// // Remove count keys (count is shared by ldap). +// $this->unsetCountKeys($notificationTask); +// +// foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { +// $auditInformation[] = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', +// ['fdAuditAttributes'], '', $auditDN); +// } +// // Again remove key: count retrieved from LDAP. +// $this->unsetCountKeys($auditInformation); +// // It is possible that an audit does not contain any attributes changes, condition is required. +// foreach ($auditInformation as $audit => $attr) { +// if (!empty($attr[0]['fdauditattributes'])) { +// // Clear and compact received results from above ldap search +// $auditAttributes = $attr[0]['fdauditattributes']; +// } +// } +// } +// +// return $auditAttributes; +// } - // CN of the main task - $cn = $subTask; - // DN of the main task - $dn = $details['dn']; +// /** +// * @param array $notifications +// * @return array +// * Note : This method is present to add to the mailForm body the proper uid and attrs info. +// */ +// private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array +// { +// // Iterate through each subTask and related attrs +// $uidAttrsText = []; +// +// foreach ($notifications[$notificationsMainTaskName]['subTask'] as $value) { +// $uidName = $value['uid']; +// $attrs = []; +// +// foreach ($value['attrs'] as $attr) { +// $attrs[] = $attr; +// } +// $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; +// } +// +// // Make the array unique, avoiding uid and same attribute duplication. +// $uidAttrsText = array_unique($uidAttrsText); +// // Add uid names and related attrs to mailForm['body'] +// $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); +// +// return $notifications; +// } - $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, $serverResults[0]); - $result[$dn]['mailStatus'] = $serverResults; - } - } +// protected function sendNotificationsMail (array $notifications): array +// { +// $result = []; +// // Re-use of the same mail processing template logic +// $fdTasksConf = $this->getMailObjectConfiguration(); +// $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); +// +// /* +// Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is +// sent per main task. +// */ +// $maxMailsIncrement = 0; +// +// foreach ($notifications as $data) { +// $numberOfRecipients = count($data['mailForm']['recipients']); +// +// $mail_controller = new MailController( +// $data['mailForm']['setFrom'], +// NULL, +// $data['mailForm']['recipients'], +// $data['mailForm']['body'], +// $data['mailForm']['signature'], +// $data['mailForm']['subject'], +// $data['mailForm']['receipt'], +// NULL +// ); +// +// $mailSentResult = $mail_controller->sendMail(); +// $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); +// +// // Verification anti-spam max mails to be sent and quit loop if matched. +// $maxMailsIncrement += $numberOfRecipients; +// if ($maxMailsIncrement == $maxMailsConfig) { +// break; +// } +// } +// +// return $result; +// } - return $result; - } +// protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array +// { +// $result = []; +// if ($serverResults[0] == "SUCCESS") { +// foreach ($subTask['subTask'] as $subTask => $details) { +// +// // CN of the main task +// $cn = $subTask; +// // DN of the main task +// $dn = $details['dn']; +// +// // Update task status for the current $dn +// $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, "2"); +// $result[$dn]['mailStatus'] = 'Notification was successfully sent'; +// $result[$dn]['updateLastMailExec'] = $this->updateLastMailExecTime($mailTaskBackend[0]["dn"]); +// } +// } else { +// foreach ($subTask['subTask'] as $subTask => $details) { +// +// // CN of the main task +// $cn = $subTask; +// // DN of the main task +// $dn = $details['dn']; +// +// $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, $serverResults[0]); +// $result[$dn]['mailStatus'] = $serverResults; +// } +// } +// +// return $result; +// } // /** // * @param array $list_tasks @@ -642,4 +645,24 @@ class TaskGateway return $task; } + /** + * @param string $dn + * @return bool|string + * Note: Update the attribute lastExecTime from fdTasksConf. + */ + public function updateLastMailExecTime (string $dn) + { + // prepare data + $ldap_entry["fdTasksConfLastExecTime"] = time(); + + // Add data to LDAP + try { + $result = ldap_modify($this->ds, $dn, $ldap_entry); + } catch (Exception $e) { + + $result = json_encode(["Ldap Error" => "$e"]); + } + return $result; + } + } \ No newline at end of file diff --git a/library/interfaces/EndpointInterface.php b/library/interfaces/EndpointInterface.php index aafb0b9..c9f8f30 100644 --- a/library/interfaces/EndpointInterface.php +++ b/library/interfaces/EndpointInterface.php @@ -1,30 +1,34 @@ <?php -interface EndpointInterface { +interface EndpointInterface +{ - public function __construct(TaskGateway $gateway); + public function __construct (TaskGateway $gateway); /** * @return array * Part of the interface of orchestrator plugin to treat GET method */ - public function processEndPointGet () : array; + public function processEndPointGet (): array; /** + * @param array|NULL $data * @return array - * Part of the interface of orchestrator plugin to treat POST method + * Note : Part of the interface of orchestrator plugin to treat POST method */ - public function processEndPointPost () : array; + public function processEndPointPost (array $data = NULL): array; /** + * @param array|NULL $data * @return array - * Part of the interface of orchestrator plugin to treat PATCH method + * Note : Part of the interface of orchestrator plugin to treat PATCH method */ - public function processEndPointPatch () : array; + public function processEndPointPatch (array $data = NULL): array; /** + * @param array|NULL $data * @return array - * Part of the interface of orchestrator plugin to treat DELETE method + * Note : Part of the interface of orchestrator plugin to treat DELETE method */ - public function processEndPointDelete() : array; + public function processEndPointDelete (array $data = NULL): array; } diff --git a/plugins/tasks/Mail.php b/plugins/tasks/Mail.php index 538c150..fd757fd 100644 --- a/plugins/tasks/Mail.php +++ b/plugins/tasks/Mail.php @@ -23,7 +23,7 @@ class Mail implements EndpointInterface * @return array * Note : Part of the interface of orchestrator plugin to treat POST method */ - public function processEndPointPost (): array + public function processEndPointPost (array $data = NULL): array { return []; } @@ -32,17 +32,18 @@ class Mail implements EndpointInterface * @return array * Note : Part of the interface of orchestrator plugin to treat DELETE method */ - public function processEndPointDelete (): array + public function processEndPointDelete (array $data = NULL): array { return []; } /** + * @param array|NULL $data * @return array * @throws Exception * Note : Part of the interface of orchestrator plugin to treat PATCH method */ - public function processEndPointPatch (): array + public function processEndPointPatch (array $data = NULL): array { return $this->processMailTasks($this->gateway->getObjectTypeTask('Mail Object)')); } @@ -114,7 +115,7 @@ class Mail implements EndpointInterface // The third arguments "2" is the status code of success for mail as of now 18/11/22 $result[$mail["dn"]]['statusUpdate'] = $this->gateway->updateTaskStatus($mail["dn"], $mail["cn"][0], "2"); $result[$mail["dn"]]['mailStatus'] = 'mail : ' . $mail["dn"] . ' was successfully sent'; - $result[$mail["dn"]]['updateLastMailExec'] = $this->updateLastMailExecTime($fdTasksConf[0]["dn"]); + $result[$mail["dn"]]['updateLastMailExec'] = $this->gateway->updateLastMailExecTime($fdTasksConf[0]["dn"]); } else { $result[$mail["dn"]]['statusUpdate'] = $this->gateway->updateTaskStatus($mail["dn"], $mail["cn"][0], $mailSentResult[0]); @@ -188,25 +189,4 @@ class Mail implements EndpointInterface return $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $templateName); } - /** - * @param string $dn - * @return bool|string - * Note: Update the attribute lastExecTime from fdTasksConf. - */ - public function updateLastMailExecTime (string $dn) - { - // prepare data - $ldap_entry["fdTasksConfLastExecTime"] = time(); - - // Add data to LDAP - try { - $result = ldap_modify($this->gateway->ds, $dn, $ldap_entry); - } catch (Exception $e) { - - $result = json_encode(["Ldap Error" => "$e"]); - } - return $result; - } - - } diff --git a/plugins/tasks/notifications.php b/plugins/tasks/notifications.php index 82330f4..91e63e5 100644 --- a/plugins/tasks/notifications.php +++ b/plugins/tasks/notifications.php @@ -20,29 +20,38 @@ class notifications implements EndpointInterface } /** + * @param array|null $data * @return array */ - public function processEndPointPost (): array + public function processEndPointPost (array $data = NULL): array { return []; } /** + * @param array|NULL $data * @return array + * @throws Exception */ - public function processEndPointPatch (): array + public function processEndPointPatch (array $data = NULL): array { return $this->processNotifications($this->gateway->getObjectTypeTask('notifications)')); } /** + * @param array|NULL $data * @return array */ - public function processEndPointDelete (): array + public function processEndPointDelete (array $data = NULL): array { return []; } + /** + * @param array $notificationsSubTasks + * @return array + * @throws Exception + */ public function processNotifications (array $notificationsSubTasks): array { $result = []; @@ -51,7 +60,7 @@ class notifications implements EndpointInterface foreach ($notificationsSubTasks as $task) { // If the tasks must be treated - status and scheduled - process the sub-tasks - if ($this->statusAndScheduleCheck($task)) { + if ($this->gateway->statusAndScheduleCheck($task)) { // Retrieve data from the main task $notificationsMainTask = $this->getNotificationsMainTask($task['fdtasksgranularmaster'][0]); @@ -63,7 +72,7 @@ class notifications implements EndpointInterface $auditAttributes = $this->retrieveAuditedAttributes($task); $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; - $this->unsetCountKeys($monitoredAttrs); + $this->gateway->unsetCountKeys($monitoredAttrs); // Verify if there is a match between audited attributes and monitored attributes from main task. $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); @@ -80,7 +89,7 @@ class notifications implements EndpointInterface $notifications = $this->completeNotificationsBody($notifications, $notificationsMainTaskName); } else { // Simply remove the subTask has no notifications are required - $result[$task['dn']]['Removed'] = $this->removeSubTask($task['dn']); + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; } } @@ -92,4 +101,193 @@ class notifications implements EndpointInterface return $result; } + + /** + * @param string $mainTaskDn + * @return array + */ + public function getNotificationsMainTask (string $mainTaskDn): array + { + // Retrieve data from the main task + return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', + 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], + '', $mainTaskDn); + } + + /** + * @param array $mainTask + * @return array + * Note : Simply generate the email to be sent as notification. + */ + private function generateMainTaskMailTemplate (array $mainTask): array + { + // Generate email configuration for each result of subtasks having the same main task.w + $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; + $this->gateway->unsetCountKeys($recipients); + $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; + $mailTemplateName = $mainTask[0]['fdtasksnotificationsmailtemplate'][0]; + + $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); + $mailContent = $mailInfos[0]; + + // Set the notification array with all required variable for all sub-tasks of same main task origin. + $mailForm['setFrom'] = $sender; + $mailForm['recipients'] = $recipients; + $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; + $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; + $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; + + return $mailForm; + } + + /** + * @param array $notificationTask + * @return array + * NOTE : receive a unique tasks of type notification (one subtask at a time) + */ + protected function retrieveAuditedAttributes (array $notificationTask): array + { + $auditAttributes = []; + + // Retrieve audit data attributes from the list of references set in the sub-task + if (!empty($notificationTask['fdtasksgranularref'])) { + // Remove count keys (count is shared by ldap). + $this->gateway->unsetCountKeys($notificationTask); + + foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { + $auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))', + ['fdAuditAttributes'], '', $auditDN); + } + // Again remove key: count retrieved from LDAP. + $this->gateway->unsetCountKeys($auditInformation); + // It is possible that an audit does not contain any attributes changes, condition is required. + foreach ($auditInformation as $attr) { + if (!empty($attr[0]['fdauditattributes'])) { + // Clear and compact received results from above ldap search + $auditAttributes = $attr[0]['fdauditattributes']; + } + } + } + + return $auditAttributes; + } + + /** + * @param array $notifications + * @param string $notificationsMainTaskName + * @return array + * Note : This method is present to add to the mailForm body the proper uid and attrs info. + */ + private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array + { + // Iterate through each subTask and related attrs + $uidAttrsText = []; + + foreach ($notifications[$notificationsMainTaskName]['subTask'] as $value) { + $uidName = $value['uid']; + $attrs = []; + + foreach ($value['attrs'] as $attr) { + $attrs[] = $attr; + } + $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; + } + + // Make the array unique, avoiding uid and same attribute duplication. + $uidAttrsText = array_unique($uidAttrsText); + // Add uid names and related attrs to mailForm['body'] + $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); + + return $notifications; + } + + /** + * @param array $notifications + * @return array + * Note : Collect information and send notification email. + */ + protected function sendNotificationsMail (array $notifications): array + { + $result = []; + // Re-use of the same mail processing template logic + $fdTasksConf = $this->gateway->getLdapTasks( + "(objectClass=fdTasksConf)", + ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] + ); + $maxMailsConfig = $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; + + /* + Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is + sent per main task. + */ + $maxMailsIncrement = 0; + + foreach ($notifications as $data) { + $numberOfRecipients = count($data['mailForm']['recipients']); + + $mail_controller = new MailController( + $data['mailForm']['setFrom'], + NULL, + $data['mailForm']['recipients'], + $data['mailForm']['body'], + $data['mailForm']['signature'], + $data['mailForm']['subject'], + $data['mailForm']['receipt'], + NULL + ); + + $mailSentResult = $mail_controller->sendMail(); + $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); + + // Verification anti-spam max mails to be sent and quit loop if matched. + $maxMailsIncrement += $numberOfRecipients; + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } + } + + return $result; + } + + /** + * @param array $serverResults + * @param array $subTask + * @param array $mailTaskBackend + * @return array + * Note : + */ + protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array + { + $result = []; + if ($serverResults[0] == "SUCCESS") { + foreach ($subTask['subTask'] as $subTask => $details) { + + // CN of the main task + $cn = $subTask; + // DN of the main task + $dn = $details['dn']; + + // Update task status for the current $dn + $result[$dn]['statusUpdate'] = $this->gateway->updateTaskStatus($dn, $cn, "2"); + $result[$dn]['mailStatus'] = 'Notification was successfully sent'; + $result[$dn]['updateLastMailExec'] = $this->gateway->updateLastMailExecTime($mailTaskBackend[0]["dn"]); + } + } else { + foreach ($subTask['subTask'] as $subTask => $details) { + + // CN of the main task + $cn = $subTask; + // DN of the main task + $dn = $details['dn']; + + $result[$dn]['statusUpdate'] = $this->gateway->updateTaskStatus($dn, $cn, $serverResults[0]); + $result[$dn]['mailStatus'] = $serverResults; + } + } + + return $result; + } + + } \ No newline at end of file -- GitLab From d3228feca4054709f417a0217e4915675194e340 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 16:04:26 +0100 Subject: [PATCH 034/111] :sparkles: Feat(Orchestrator) - Iteration 13 Lifecycle adjustments --- plugins/tasks/LifeCycle.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index 333562e..8c23dce 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -20,29 +20,32 @@ class LifeCycle implements EndpointInterface } /** + * @param array|null $data * @return array * Note : Part of the interface of orchestrator plugin to treat POST method */ - public function processEndPointPost (): array + 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 + public function processEndPointDelete (array $data = NULL): array { return []; } /** + * @param array|null $data * @return array * @throws Exception * Note : Part of the interface of orchestrator plugin to treat PATCH method */ - public function processEndPointPatch (): array + public function processEndPointPatch (array $data = NULL): array { echo json_encode('within lifeCycle patch'); return $this->processLifeCycleTasks($this->gateway->getObjectTypeTask('lifeCycle')); -- GitLab From 494187e5217c2e821b0b282f23497bd4f86c0a21 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 16:45:45 +0100 Subject: [PATCH 035/111] :sparkles: Feat(Orchestrator) - Iteration 14 Removal of obsolete code --- library/TaskGateway.php | 325 ------------------ plugins/tasks/LifeCycle.php | 1 - .../{notifications.php => Notifications.php} | 4 +- 3 files changed, 2 insertions(+), 328 deletions(-) rename plugins/tasks/{notifications.php => Notifications.php} (99%) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 1373e7f..50f12cf 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -74,41 +74,6 @@ class TaskGateway return $task["fdtasksgranularstatus"][0] == 1 && $this->verifySchedule($task["fdtasksgranularschedule"][0]); } -// /** -// * @param string $mainTaskDn -// * @return array -// */ -// public function getNotificationsMainTask (string $mainTaskDn): array -// { -// // Retrieve data from the main task -// return $this->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', -// 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], -// '', $mainTaskDn); -// } - - -// private function generateMainTaskMailTemplate (array $mainTask): array -// { -// // Generate email configuration for each result of subtasks having the same main task.w -// $recipients = $mainTask[0]["fdtasksnotificationslistofrecipientsmails"]; -// unset($recipients['count']); -// $sender = $mainTask[0]["fdtasksnotificationsemailsender"][0]; -// $mailTemplateName = $mainTask[0]['fdtasksnotificationsmailtemplate'][0]; -// -// $mailInfos = $this->retrieveMailTemplateInfos($mailTemplateName); -// $mailContent = $mailInfos[0]; -// -// // Set the notification array with all required variable for all sub-tasks of same main task origin. -// $mailForm['setFrom'] = $sender; -// $mailForm['recipients'] = $recipients; -// $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; -// $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; -// $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; -// $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; -// -// return $mailForm; -// } - /** * @param $array * @return void @@ -125,244 +90,6 @@ class TaskGateway } } -// /** -// * @param array $notificationTask -// * @return array -// * NOTE : receive a unique tasks of type notification (one subtask at a time) -// */ -// protected function retrieveAuditedAttributes (array $notificationTask): array -// { -// $auditAttributes = []; -// -// // Retrieve audit data attributes from the list of references set in the sub-task -// if (!empty($notificationTask['fdtasksgranularref'])) { -// // Remove count keys (count is shared by ldap). -// $this->unsetCountKeys($notificationTask); -// -// foreach ($notificationTask['fdtasksgranularref'] as $auditDN) { -// $auditInformation[] = $this->getLdapTasks('(&(objectClass=fdAuditEvent))', -// ['fdAuditAttributes'], '', $auditDN); -// } -// // Again remove key: count retrieved from LDAP. -// $this->unsetCountKeys($auditInformation); -// // It is possible that an audit does not contain any attributes changes, condition is required. -// foreach ($auditInformation as $audit => $attr) { -// if (!empty($attr[0]['fdauditattributes'])) { -// // Clear and compact received results from above ldap search -// $auditAttributes = $attr[0]['fdauditattributes']; -// } -// } -// } -// -// return $auditAttributes; -// } - -// /** -// * @param array $notifications -// * @return array -// * Note : This method is present to add to the mailForm body the proper uid and attrs info. -// */ -// private function completeNotificationsBody (array $notifications, string $notificationsMainTaskName): array -// { -// // Iterate through each subTask and related attrs -// $uidAttrsText = []; -// -// foreach ($notifications[$notificationsMainTaskName]['subTask'] as $value) { -// $uidName = $value['uid']; -// $attrs = []; -// -// foreach ($value['attrs'] as $attr) { -// $attrs[] = $attr; -// } -// $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; -// } -// -// // Make the array unique, avoiding uid and same attribute duplication. -// $uidAttrsText = array_unique($uidAttrsText); -// // Add uid names and related attrs to mailForm['body'] -// $notifications[$notificationsMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); -// -// return $notifications; -// } - -// protected function sendNotificationsMail (array $notifications): array -// { -// $result = []; -// // Re-use of the same mail processing template logic -// $fdTasksConf = $this->getMailObjectConfiguration(); -// $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); -// -// /* -// Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is -// sent per main task. -// */ -// $maxMailsIncrement = 0; -// -// foreach ($notifications as $data) { -// $numberOfRecipients = count($data['mailForm']['recipients']); -// -// $mail_controller = new MailController( -// $data['mailForm']['setFrom'], -// NULL, -// $data['mailForm']['recipients'], -// $data['mailForm']['body'], -// $data['mailForm']['signature'], -// $data['mailForm']['subject'], -// $data['mailForm']['receipt'], -// NULL -// ); -// -// $mailSentResult = $mail_controller->sendMail(); -// $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); -// -// // Verification anti-spam max mails to be sent and quit loop if matched. -// $maxMailsIncrement += $numberOfRecipients; -// if ($maxMailsIncrement == $maxMailsConfig) { -// break; -// } -// } -// -// return $result; -// } - -// protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array -// { -// $result = []; -// if ($serverResults[0] == "SUCCESS") { -// foreach ($subTask['subTask'] as $subTask => $details) { -// -// // CN of the main task -// $cn = $subTask; -// // DN of the main task -// $dn = $details['dn']; -// -// // Update task status for the current $dn -// $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, "2"); -// $result[$dn]['mailStatus'] = 'Notification was successfully sent'; -// $result[$dn]['updateLastMailExec'] = $this->updateLastMailExecTime($mailTaskBackend[0]["dn"]); -// } -// } else { -// foreach ($subTask['subTask'] as $subTask => $details) { -// -// // CN of the main task -// $cn = $subTask; -// // DN of the main task -// $dn = $details['dn']; -// -// $result[$dn]['statusUpdate'] = $this->updateTaskStatus($dn, $cn, $serverResults[0]); -// $result[$dn]['mailStatus'] = $serverResults; -// } -// } -// -// return $result; -// } - -// /** -// * @param array $list_tasks -// * @return array[]|string[] -// * @throws Exception -// * Note : Verify the status and schedule as well as searching for the correct life cycle behavior from main task. -// */ -// public function processLifeCycleTasks (array $list_tasks): array -// { -// // Array representing the status of the subtask. -// $result = []; -// // Initiate the object webservice. -// $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); -// // Required to prepare future webservice call. E.g. Retrieval of mandatory token. -// $webservice->setCurlSettings(); -// -// foreach ($list_tasks as $task) { -// // If the tasks must be treated - status and scheduled - process the sub-tasks -// if ($this->statusAndScheduleCheck($task)) { -// -// // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes -// $lifeCycleBehavior = $this->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', -// 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', -// 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], -// '', $task['fdtasksgranularmaster'][0]); -// -// // Simply retrieve the current supannStatus of the user DN related to the task at hand. -// $currentUserLifeCycle = $this->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], -// '', $task['fdtasksgranulardn'][0]); -// -// // Compare both the required schedule and the current user status - returning TRUE if modification is required. -// if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { -// -// // This will call a method to modify the ressourcesSupannEtatDate of the DN linked to the subTask -// $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0]); -// if ($lifeCycleResult === TRUE) { -// -// $result[$task['dn']]['results'] = json_encode("Account states have been successfully modified for " . $task['fdtasksgranulardn'][0]); -// // Status of the task must be updated to success -// $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], '2'); -// -// // Here the user is refresh in order to activate methods based on supann Status changes. -// $result[$task['dn']]['refreshUser'] = $webservice->refreshUserInfo($task['fdtasksgranulardn'][0]); -// -// // In case the modification failed -// } else { -// $result[$task['dn']]['results'] = json_encode("Error updating " . $task['fdtasksgranulardn'][0] . "-" . $lifeCycleResult); -// // Update of the task status error message -// $updateResult = $this->updateTaskStatus($task['dn'], $task['cn'][0], $lifeCycleResult); -// } -// // Verification if the sub-task status has been updated correctly -// if ($updateResult === TRUE) { -// $result[$task['dn']]['statusUpdate'] = 'Success'; -// } else { -// $result[$task['dn']]['statusUpdate'] = $updateResult; -// } -// // Remove the subtask has it is not required to update it nor to process it. -// } else { -// $result[$task['dn']]['results'] = 'Sub-task removed for : ' . $task['fdtasksgranulardn'][0] . ' with result : ' -// . $this->removeSubTask($task['dn']); -// $result[$task['dn']]['statusUpdate'] = 'No updates required, sub-task will be removed.'; -// } -// } -// } -// // If array is empty, no tasks of type life cycle needs to be treated. -// if (empty($result)) { -// $result = 'No tasks of type "Life Cycle" requires processing.'; -// } -// return [$result]; -// } - -// /** -// * @param array $lifeCycleBehavior -// * @param string $userDN -// * @return bool|string -// */ -// protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) -// { -// // Extracting values of desired post-state behavior -// $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; -// $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; -// $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional -// $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional -// -// // Require the date of today to update the start of the new resources (If change of status). -// $currentDate = new DateTime(); -// // Date of today + numbers of days to add for end date. -// $newEndDate = new DateTime(); -// $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); -// -// // Prepare the ldap entry to be modified -// $ldapEntry = []; -// $ldapEntry['supannRessourceEtatDate'] = "{" . $newEntry['Resource'] . "}" -// . $newEntry['State'] . ":" -// . $newEntry['SubState'] . ":" . -// $currentDate->format('Ymd') . ":" -// . $newEndDate->format('Ymd'); -// -// try { -// $result = ldap_modify($this->ds, $userDN, $ldapEntry); -// } catch (Exception $e) { -// $result = json_encode(["Ldap Error" => "$e"]); -// } -// -// return $result; -// } - /** * @param bool|string $subTaskDn * @return bool|string @@ -492,57 +219,6 @@ class TaskGateway return $result; } -// /** -// * @param array $lifeCycleBehavior -// * @param array $currentUserLifeCycle -// * @return bool -// * Note receive the life cycle behavior desired and compare it the received current user life cycle, returning TRUE -// * if there is indeed a difference and therefore must update the user information. -// * In case the comparison is impossible due to the use not having a status listed, it will report false. -// */ -// protected function isLifeCycleRequiringModification (array $lifeCycleBehavior, array $currentUserLifeCycle): bool -// { -// $result = FALSE; -// // Regular expression in order to extract the supann format within an array -// $pattern = '/\{(\w+)\}(\w):([^:]*)(?::([^:]*))?(?::([^:]*))?(?::([^:]*))?/'; -// -// // In case the tasks is launched without supann being activated on the user account, return error -// if (empty($currentUserLifeCycle[0]['supannressourceetatdate'][0])) { -// return FALSE; -// } -// // Perform the regular expression match -// preg_match($pattern, $currentUserLifeCycle[0]['supannressourceetatdate'][0], $matches); -// -// // Extracting values of current user -// $userSupann['Resource'] = $matches[1] ?? ''; -// $userSupann['State'] = $matches[2] ?? ''; -// $userSupann['SubState'] = $matches[3] ?? ''; -// // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. -// $userSupann['EndDate'] = $matches[5] ?? ''; -// -// // Extracting values of desired pre-state behavior -// $preStateSupann['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepreresource'][0]; -// $preStateSupann['State'] = $lifeCycleBehavior[0]['fdtaskslifecycleprestate'][0]; -// $preStateSupann['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepresubstate'][0] ?? ''; //SubState is optional -// -// // Verifying if the user end date for selected resource is overdue -// if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { -// // Comparing value in a nesting conditions -// if ($userSupann['Resource'] == $preStateSupann['Resource']) { -// if ($userSupann['State'] == $preStateSupann['State']) { -// // as SubState is optional, if both resource and state match at this point, modification is allowed. -// if (empty($preStateSupann['SubState'])) { -// $result = TRUE; -// } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { -// $result = TRUE; -// } -// } -// } -// } -// -// return $result; -// } - /** * @param string $schedule * @return bool @@ -664,5 +340,4 @@ class TaskGateway } return $result; } - } \ No newline at end of file diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index 8c23dce..c207d20 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -47,7 +47,6 @@ class LifeCycle implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - echo json_encode('within lifeCycle patch'); return $this->processLifeCycleTasks($this->gateway->getObjectTypeTask('lifeCycle')); } diff --git a/plugins/tasks/notifications.php b/plugins/tasks/Notifications.php similarity index 99% rename from plugins/tasks/notifications.php rename to plugins/tasks/Notifications.php index 91e63e5..8a45981 100644 --- a/plugins/tasks/notifications.php +++ b/plugins/tasks/Notifications.php @@ -1,6 +1,6 @@ <?php -class notifications implements EndpointInterface +class Notifications implements EndpointInterface { private TaskGateway $gateway; @@ -35,7 +35,7 @@ class notifications implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - return $this->processNotifications($this->gateway->getObjectTypeTask('notifications)')); + return $this->processNotifications($this->gateway->getObjectTypeTask('notifications')); } /** -- GitLab From b00323dbe8965d986d239f9d79f6e4a5e4fbe106 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 17:03:31 +0100 Subject: [PATCH 036/111] :sparkles: Feat(Orchestrator) - Iteration 15 Iteration 15 --- library/TaskGateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 50f12cf..b241a33 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -314,7 +314,7 @@ class TaskGateway { $task = $this->getTask($objectType); if (!$task) { - taskController::respondNotFound($objectType); + TaskController::respondNotFound($objectType); exit; } -- GitLab From e2d9cd0043d1b15065ff4889479b3c8a1aabd67e Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 17:09:31 +0100 Subject: [PATCH 037/111] :sparkles: delete obsolete class --- plugins/getUsers.php | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 plugins/getUsers.php diff --git a/plugins/getUsers.php b/plugins/getUsers.php deleted file mode 100644 index a531db3..0000000 --- a/plugins/getUsers.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -class getUsers extends TaskGateway -{ - protected $ds; - - public function __construct() - { - $ldap_connect = new Ldap($_ENV["FD_LDAP_MASTER_URL"], $_ENV["LDAP_ADMIN"], $_ENV["LDAP_PWD"]); - $this->ds = $ldap_connect->getConnection(); - } - - public function processEndPoint() - { - return $this->customLdapSearch("(&(objectClass=person))", ['cn']); - } - - // This custom ldap search should be within parent as simplified version of what already exists. Refactor required. - public function customLdapSearch (string $filter = '', array $attrs = [], string $dn = NULL): array - { - $result = []; - - if (empty($dn)) { - $dn = $_ENV["LDAP_BASE"]; - } - - try { - $sr = ldap_search($this->ds, $dn, $filter, $attrs); - $info = ldap_get_entries($this->ds, $sr); - } catch (Exception $e) { - // build array for return response - $result = [json_encode(["Ldap Error" => "$e"])]; // string returned - } - - // Verify if the above ldap search succeeded. - if (!empty($info) && is_array($info) && $info["count"] >= 1) { - return $info; - } - - return $result; - } - - -} -- GitLab From 69991636ad373acc0a05d6a9ecef2dedae09f9d0 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 17:11:48 +0100 Subject: [PATCH 038/111] :sparkles: Feat(Orchestrator) - Iteration 16 Iteration 16 --- library/MailController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/MailController.php b/library/MailController.php index 4de771e..af42adf 100644 --- a/library/MailController.php +++ b/library/MailController.php @@ -72,7 +72,7 @@ class MailController $this->mail->addBCC($this->setBCC); } - if ($this->receipt === TRUE) { + if ($this->receipt === 'TRUE') { $this->mail->addCustomHeader('Disposition-Notification-To', $this->setFrom); } $this->mail->Subject = $this->subject; -- GitLab From 003f47f114b526e6c0c0544878749f9b54d81d14 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 3 Jun 2024 19:33:20 +0100 Subject: [PATCH 039/111] :sparkles: Feat(Orchestrator) - phpcs phpcs --- api/index.php | 2 +- library/Ldap.php | 38 +++++++++++++++++++------------------- library/TaskController.php | 2 +- library/TaskGateway.php | 1 + 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/api/index.php b/api/index.php index 1680833..2bea3a3 100644 --- a/api/index.php +++ b/api/index.php @@ -15,7 +15,7 @@ $object_type = $parts[4] ?? NULL; // Parsing of the raw data potentially passed as json REST data to the API $rawBody = file_get_contents('php://input'); // Decode the JSON data and set to null if no body received -$jsonBody = !empty ($rawBody) ? json_decode($rawBody, TRUE) : null; +$jsonBody = !empty ($rawBody) ? json_decode($rawBody, TRUE) : NULL; switch ($resource) { diff --git a/library/Ldap.php b/library/Ldap.php index a3b2fcb..27fbb72 100644 --- a/library/Ldap.php +++ b/library/Ldap.php @@ -41,27 +41,27 @@ class Ldap * @return array * Note : A generic method allowing to search in LDAP. */ - public function searchInLdap ($ds, string $filter = '', array $attrs = [], string $dn = NULL): array - { - $result = []; - - if (empty($dn)) { - $dn = $_ENV["LDAP_BASE"]; - } + public function searchInLdap ($ds, string $filter = '', array $attrs = [], string $dn = NULL): array + { + $result = []; - try { - $sr = ldap_search($ds, $dn, $filter, $attrs); - $info = ldap_get_entries($ds, $sr); - } catch (Exception $e) { - // build array for return response - $result = [json_encode(["Ldap Error" => "$e"])]; // string returned - } + if (empty($dn)) { + $dn = $_ENV["LDAP_BASE"]; + } - // Verify if the above ldap search succeeded. - if (!empty($info) && is_array($info) && $info["count"] >= 1) { - return $info; - } + try { + $sr = ldap_search($ds, $dn, $filter, $attrs); + $info = ldap_get_entries($ds, $sr); + } catch (Exception $e) { + // build array for return response + $result = [json_encode(["Ldap Error" => "$e"])]; // string returned + } - return $result; + // Verify if the above ldap search succeeded. + if (!empty($info) && is_array($info) && $info["count"] >= 1) { + return $info; } + + return $result; + } } \ No newline at end of file diff --git a/library/TaskController.php b/library/TaskController.php index 3679009..64a9325 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -36,7 +36,7 @@ class TaskController public function processRequest (string $method, ?string $objectType, $jsonBody = NULL): void { // Allow result to be nullable. - $result = null; + $result = NULL; // If no specific tasks object specified, return all tasks if ($objectType == NULL) { diff --git a/library/TaskGateway.php b/library/TaskGateway.php index b241a33..5fb6352 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -88,6 +88,7 @@ class TaskGateway unset($array[$key]); } } + unset($value); //unset the reference after the loop for security best practise. } /** -- GitLab From 947717ed40fabfffdf626379becf2b702b887fd4 Mon Sep 17 00:00:00 2001 From: Benoit Mortier <benoit.mortier@fusiondirectory.org> Date: Thu, 11 Jul 2024 18:43:03 +0200 Subject: [PATCH 040/111] :sparkles: feat(ci) add ubuntu focal in the ci trigger Signed-off-by: Benoit Mortier <benoit.mortier@fusiondirectory.org> --- .gitlab-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a124507..1e84902 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -85,6 +85,14 @@ trigger-ci-debian-bullseye: project: debian/bullseye-fusiondirectory-orchestrator-dev branch: "main" +trigger-ci-ubuntu-focal: + stage: trigger + only: + - dev + trigger: + project: ubuntu/focal-fusiondirectory-orchestrator-dev + branch: "main" + trigger-ci-centos-7: stage: trigger only: -- GitLab From 92fa6a0c9f644f464c23efdcd7ea1dd66c60b8d6 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 15 Aug 2024 14:39:06 +0100 Subject: [PATCH 041/111] :sparkles: Feat(Audit) - first commit First audit commit logic as plugin --- plugins/tasks/Audit.php | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 plugins/tasks/Audit.php diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php new file mode 100644 index 0000000..d342831 --- /dev/null +++ b/plugins/tasks/Audit.php @@ -0,0 +1,75 @@ +<?php + +class Audit 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 + { + return []; + } + + /** + * @param array|null $data + * @return array + */ + public function processEndPointPost (array $data = NULL): array + { + return []; + } + + /** + * @param array|NULL $data + * @return array + * @throws Exception + */ + public function processEndPointPatch (array $data = NULL): array + { + return $this->processAuditDeletion($this->gateway->getObjectTypeTask('audit')); + } + + /** + * @param array|NULL $data + * @return array + */ + public function processEndPointDelete (array $data = NULL): array + { + return []; + } + + /** + * @param array $auditSubTasks + * @return array + * @throws Exception + */ + public function processAuditDeletion (array $auditSubTasks): array + { + $result = []; + + // todo - Logic to iterate through audit timestamp and delete passed time. + + return $result; + } + + /** + * @param string $mainTaskDn + * @return array + */ + public function getAuditMainTask (string $mainTaskDn): array + { + // Retrieve data from the main task + return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['attr1', + 'attr2', 'attr3'], '', $mainTaskDn); + } + +} \ No newline at end of file -- GitLab From 71d80a30fc0944a43ebce5dbdc8d3fa0dfe209c8 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 15 Aug 2024 17:08:44 +0100 Subject: [PATCH 042/111] :sparkles: Feat(Audit) - simply endpoint check Simple endpoint check response using binary client. --- plugins/tasks/Audit.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index d342831..6ac5269 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -57,6 +57,7 @@ class Audit implements EndpointInterface $result = []; // todo - Logic to iterate through audit timestamp and delete passed time. + $result = ['Yeaah']; return $result; } -- GitLab From b30ff56cca02326fa6f0632bc73b81930a3a878f Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 19 Aug 2024 12:25:17 +0100 Subject: [PATCH 043/111] :ambulance: (taskGateway) - fixes getTasks Fixing getTasks filter typo --- library/TaskGateway.php | 2 +- plugins/tasks/Audit.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 5fb6352..62939ee 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -50,7 +50,7 @@ class TaskGateway break; case $object_type: - $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=" . $object_type . ")"); + $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=" . $object_type . "))"); $this->unsetCountKeys($list_tasks); break; diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 6ac5269..cf8bd56 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -35,7 +35,7 @@ class Audit implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - return $this->processAuditDeletion($this->gateway->getObjectTypeTask('audit')); + return $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit')); } /** @@ -57,7 +57,7 @@ class Audit implements EndpointInterface $result = []; // todo - Logic to iterate through audit timestamp and delete passed time. - $result = ['Yeaah']; + $result = $auditSubTasks; return $result; } -- GitLab From cd194c536f107630008ecb6d2195ab869d7fead2 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 19 Aug 2024 16:40:31 +0100 Subject: [PATCH 044/111] :sparkles: Audit - Enhancing logic Enhancing logic with generalized timestamp --- plugins/tasks/Audit.php | 65 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index cf8bd56..2238ffa 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -57,20 +57,79 @@ class Audit implements EndpointInterface $result = []; // todo - Logic to iterate through audit timestamp and delete passed time. - $result = $auditSubTasks; + + foreach ($auditSubTasks 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. + $auditMainTask = $this->getAuditMainTask($task['fdtasksgranularmaster'][0]); + // Simply get the days to retain audit. + $auditRetention = $auditMainTask[0]['fdaudittasksretention'][0]; + + $result = $this->deleteAuditPassedRetention($auditRetention); + } + + } return $result; } + /** + * @param $auditRetention + * @return array + * Note : This will return a validation of audit log suppression + */ + public function deleteAuditPassedRetention ($auditRetention): array + { + $result = []; + + // Date time object will use the timezone defined in FD, code is in index.php + $date = new DateTime(); + // today in Human Readable format. + $todayHR = $date->format('Y-m-d H:i:s'); + + // Search in LDAP for audit entries + $audit = $this->gateway->getLdapTasks('(objectClass=fdAuditEvent)', ['fdAuditDateTime'], '', ''); + // Remove the count key from the audit array. + $this->gateway->unsetCountKeys($audit); + + foreach ($audit as $record) { + // Record in Human Readable date time object + $recordHR = $this->generalizeLdapTimeToPhpObject($record['fdauditdatetime'][0]); + + $result[] = $recordHR; + } + + return $result; + } + + public function generalizeLdapTimeToPhpObject ($generalizeLdapDateTime): array + { + // Extract the date part (first 8 characters: YYYYMMDD), we do not care about hour and seconds. + $datePart = substr($generalizeLdapDateTime, 0, 8); + + // Create a DateTime object using only the date part, carefully setting the timezone to UTC. Audit timestamp is UTC + $datetime = DateTime::createFromFormat('Ymd', $datePart, new DateTimeZone('UTC')); + + // Check if the DateTime object was created successfully + if (!$datetime) { + return ['Error in Time conversion from Audit record with timestamp :' . $generalizeLdapDateTime]; + } + + return []; + + } + /** * @param string $mainTaskDn * @return array + * Note : Simply return attributes from the main related audit tasks. */ public function getAuditMainTask (string $mainTaskDn): array { // Retrieve data from the main task - return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['attr1', - 'attr2', 'attr3'], '', $mainTaskDn); + return $this->gateway->getLdapTasks('(objectClass=fdAuditTasks)', ['fdAuditTasksRetention'], '', $mainTaskDn); } } \ No newline at end of file -- GitLab From 35b2e42b79629d1121aee145dd79980630059bdf Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 19 Aug 2024 18:19:16 +0100 Subject: [PATCH 045/111] :sparkles: Audit - removal operational Removal of passed audit retention is ok. Return of result must be enhanced. --- library/TaskGateway.php | 10 +++++---- plugins/tasks/Audit.php | 46 +++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 62939ee..21b72ed 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -186,21 +186,23 @@ class TaskGateway } break; case 'Weekly' : - if ($interval->d >= 7) { + if ($interval->days >= 7) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; } break; case 'Daily' : - if ($interval->d >= 1) { + if ($interval->days >= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; } break; case 'Hourly' : - if ($interval->h >= 1) { + // When checking for hourly schedules, consider both the days and hours + $totalHours = $interval->days * 24 + $interval->h; + if ($totalHours>= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; @@ -210,7 +212,7 @@ class TaskGateway } // Case where cyclic tasks where found but the schedule is no ready. } else { - $result[$task['dn']]['Status'] = 'This cyclic task has yet to reach its scheduled date.'; + $result[$task['dn']]['Status'] = 'This cyclic task has yet to reach its next execution cycle.'; } } } else { diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 2238ffa..b7374f2 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -67,27 +67,31 @@ class Audit implements EndpointInterface // Simply get the days to retain audit. $auditRetention = $auditMainTask[0]['fdaudittasksretention'][0]; - $result = $this->deleteAuditPassedRetention($auditRetention); + $result[] = $this->checkAuditPassedRetention($auditRetention); } } - return $result; + if (!empty($result)) { + return $result; + } else { + return ['No audit requiring removal']; + } + } /** * @param $auditRetention * @return array * Note : This will return a validation of audit log suppression + * @throws Exception */ - public function deleteAuditPassedRetention ($auditRetention): array + public function checkAuditPassedRetention ($auditRetention): array { $result = []; // Date time object will use the timezone defined in FD, code is in index.php - $date = new DateTime(); - // today in Human Readable format. - $todayHR = $date->format('Y-m-d H:i:s'); + $today = new DateTime(); // Search in LDAP for audit entries $audit = $this->gateway->getLdapTasks('(objectClass=fdAuditEvent)', ['fdAuditDateTime'], '', ''); @@ -96,29 +100,45 @@ class Audit implements EndpointInterface foreach ($audit as $record) { // Record in Human Readable date time object - $recordHR = $this->generalizeLdapTimeToPhpObject($record['fdauditdatetime'][0]); + $auditDateTime = $this->generalizeLdapTimeToPhpObject($record['fdauditdatetime'][0]); + + $interval = $today->diff($auditDateTime); + + // Check if the interval is greater than auditRetention setting + if ($interval->days > $auditRetention) { + // If greater, delete the DN audit entry, we reuse removeSubTask method from gateway. + $result[$record['dn']]['result'] = $this->gateway->removeSubTask($record['dn']); + } - $result[] = $recordHR; } return $result; } - public function generalizeLdapTimeToPhpObject ($generalizeLdapDateTime): array + + /** + * @param $generalizeLdapDateTime + * @return DateTime|string[] + * @throws Exception + * Note : Simply take a generalized Ldap time (with UTC = Z) and transform it to php object dateTime. + */ + public function generalizeLdapTimeToPhpObject ($generalizeLdapDateTime) { // Extract the date part (first 8 characters: YYYYMMDD), we do not care about hour and seconds. - $datePart = substr($generalizeLdapDateTime, 0, 8); + $auditTimeFormatted = substr($generalizeLdapDateTime, 0, 8); // Create a DateTime object using only the date part, carefully setting the timezone to UTC. Audit timestamp is UTC - $datetime = DateTime::createFromFormat('Ymd', $datePart, new DateTimeZone('UTC')); + $auditDate = DateTime::createFromFormat('Ymd', $auditTimeFormatted, new DateTimeZone('UTC')); // Check if the DateTime object was created successfully - if (!$datetime) { + if (!$auditDate) { return ['Error in Time conversion from Audit record with timestamp :' . $generalizeLdapDateTime]; } - return []; + // Transform dateTime object from UTC to local defined dateTime. (Timezone is set in index.php). + $auditDate->setTimezone(new DateTimeZone(date_default_timezone_get())); + return $auditDate; } /** -- GitLab From 3d9bf1649415ee2423998a821139b4ba74d6ff1a Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 20 Aug 2024 12:03:55 +0100 Subject: [PATCH 046/111] :sparkles: Audit - enhance log result Enhancing log result output. --- plugins/tasks/Audit.php | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index b7374f2..72321b3 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -35,7 +35,32 @@ class Audit implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - return $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit')); + $result[] = $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit')); + + // Recursive function to filter out empty arrays at any depth + $nonEmptyResults = $this->recursiveArrayFilter($result); + + if (!empty($nonEmptyResults)) { + return $nonEmptyResults; + } else { + return ['No audit requiring removal']; + } + + } + + /** + * @param array $array + * @return array + * Note : A simple method iterating an array and returning non empty values. + */ + private function recursiveArrayFilter(array $array): array + { + $filtered = array_filter($array, function($item) { + // Check if item is an array, if so recursively filter it + return is_array($item) ? !empty($this->recursiveArrayFilter($item)) : !empty($item); + }); + + return $filtered; } /** @@ -72,11 +97,7 @@ class Audit implements EndpointInterface } - if (!empty($result)) { - return $result; - } else { - return ['No audit requiring removal']; - } + return $result; } -- GitLab From 42aa35da6c8e41fbcfbc280560e9687c4535a6ea Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 20 Aug 2024 15:32:35 +0100 Subject: [PATCH 047/111] :sparkles: Audit - Correct log output Correct log output, remain to update subtasks status. --- plugins/tasks/Audit.php | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 72321b3..cb800d7 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -35,7 +35,7 @@ class Audit implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - $result[] = $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit')); + $result = $this->processAuditDeletion($this->gateway->getObjectTypeTask('Audit')); // Recursive function to filter out empty arrays at any depth $nonEmptyResults = $this->recursiveArrayFilter($result); @@ -51,18 +51,27 @@ class Audit implements EndpointInterface /** * @param array $array * @return array - * Note : A simple method iterating an array and returning non empty values. + * Note : Recursively filters out empty values and arrays at any depth. */ - private function recursiveArrayFilter(array $array): array + private function recursiveArrayFilter (array $array): array { - $filtered = array_filter($array, function($item) { - // Check if item is an array, if so recursively filter it - return is_array($item) ? !empty($this->recursiveArrayFilter($item)) : !empty($item); + // 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; } + /** * @param array|NULL $data * @return array @@ -81,8 +90,6 @@ class Audit implements EndpointInterface { $result = []; - // todo - Logic to iterate through audit timestamp and delete passed time. - foreach ($auditSubTasks as $task) { // If the tasks must be treated - status and scheduled - process the sub-tasks if ($this->gateway->statusAndScheduleCheck($task)) { @@ -125,8 +132,8 @@ class Audit implements EndpointInterface $interval = $today->diff($auditDateTime); - // Check if the interval is greater than auditRetention setting - if ($interval->days > $auditRetention) { + // Check if the interval is equal or greater than auditRetention setting + if ($interval->days >= $auditRetention) { // If greater, delete the DN audit entry, we reuse removeSubTask method from gateway. $result[$record['dn']]['result'] = $this->gateway->removeSubTask($record['dn']); } -- GitLab From 2f961416f0b5ee222619352fbc00bec244832e02 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 20 Aug 2024 16:54:59 +0100 Subject: [PATCH 048/111] :sparkles: Audit - subTasks updates SubTasks updates --- plugins/tasks/Audit.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index cb800d7..22787e7 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -99,7 +99,8 @@ class Audit implements EndpointInterface // Simply get the days to retain audit. $auditRetention = $auditMainTask[0]['fdaudittasksretention'][0]; - $result[] = $this->checkAuditPassedRetention($auditRetention); + // Verification of all audit and their potential removal based on retention days passed, also update subtasks. + $result[] = $this->checkAuditPassedRetention($auditRetention, $task['dn'], $task['cn'][0]); } } @@ -114,14 +115,14 @@ class Audit implements EndpointInterface * Note : This will return a validation of audit log suppression * @throws Exception */ - public function checkAuditPassedRetention ($auditRetention): array + public function checkAuditPassedRetention ($auditRetention, $subTaskDN, $subTaskCN): array { $result = []; // Date time object will use the timezone defined in FD, code is in index.php $today = new DateTime(); - // Search in LDAP for audit entries + // Search in LDAP for audit entries (All entries ! This can be pretty heavy. $audit = $this->gateway->getLdapTasks('(objectClass=fdAuditEvent)', ['fdAuditDateTime'], '', ''); // Remove the count key from the audit array. $this->gateway->unsetCountKeys($audit); @@ -134,10 +135,19 @@ class Audit implements EndpointInterface // Check if the interval is equal or greater than auditRetention setting if ($interval->days >= $auditRetention) { - // If greater, delete the DN audit entry, we reuse removeSubTask method from gateway. - $result[$record['dn']]['result'] = $this->gateway->removeSubTask($record['dn']); + // If greater, delete the DN audit entry, we reuse removeSubTask method from gateway and get ldap response.(bool). + $result[$subTaskCN]['result'] = $this->gateway->removeSubTask($record['dn']); + $result[$subTaskCN]['info'] = 'Audit record removed.'; + + // Update tasks accordingly if LDAP succeeded. TRUE Boolean returned by ldap. + if ($result[$subTaskCN]['result']) { + // Update the subtask with the status completed a.k.a "2". + $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, "2"); + } else { + // Update the task with the LDAP potential error code. + $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, $result[$record['dn']]['result']); + } } - } return $result; -- GitLab From 42a05e9717df275c2cfdf5160b801027e67aac4e Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 20 Aug 2024 17:27:22 +0100 Subject: [PATCH 049/111] :sparkles: Audit - proper msg when no audit found Proper msg logic when no audit found. --- plugins/tasks/Audit.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 22787e7..5fc5206 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -106,7 +106,6 @@ class Audit implements EndpointInterface } return $result; - } /** @@ -127,6 +126,13 @@ class Audit implements EndpointInterface // Remove the count key from the audit array. $this->gateway->unsetCountKeys($audit); + // In case no audit exists, we have to update the tasks as well. Meaning below loop won't be reached. + if (empty($audit)) { + $result[$subTaskCN]['result'] = TRUE; + $result[$subTaskCN]['info'] = 'No audit to be removed.'; + $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, "2"); + } + foreach ($audit as $record) { // Record in Human Readable date time object $auditDateTime = $this->generalizeLdapTimeToPhpObject($record['fdauditdatetime'][0]); -- GitLab From 69595aa537dd44aedb23f9419aa3690c62986fc7 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:08:25 +0100 Subject: [PATCH 050/111] :sparkles: quick backup quick backup --- library/TaskGateway.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 21b72ed..643fac9 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -145,12 +145,13 @@ class TaskGateway $this->unsetCountKeys($tasks); if (!empty($tasks)) { + // Initiate the object webservice. $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); - // Is used to verify cyclic schedule with date format. + // Is used to verify cyclic schedule with date format. This use de local timezone - not UTC $now = new DateTime('now'); foreach ($tasks as $task) { -- GitLab From f44b078d6df007ad617f78df96d09819c770b7e0 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:31:42 +0100 Subject: [PATCH 051/111] :ambulance: Fixes webService call namespace --- library/TaskGateway.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 5fb6352..f1c28c2 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -145,8 +145,9 @@ class TaskGateway $this->unsetCountKeys($tasks); if (!empty($tasks)) { + // Initiate the object webservice. - $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); + $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); @@ -186,21 +187,23 @@ class TaskGateway } break; case 'Weekly' : - if ($interval->d >= 7) { + if ($interval->days >= 7) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; } break; case 'Daily' : - if ($interval->d >= 1) { + if ($interval->days >= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; } break; case 'Hourly' : - if ($interval->h >= 1) { + // When checking for hourly schedules, consider both the days and hours + $totalHours = $interval->days * 24 + $interval->h; + if ($totalHours >= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; -- GitLab From b23003ddf7c9c48a4f5ef51dfcda8afd680180cd Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:35:31 +0100 Subject: [PATCH 052/111] :ambulance: Fixes webService call namespace --- library/TaskGateway.php | 2 +- plugins/tasks/LifeCycle.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 643fac9..3213cec 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -147,7 +147,7 @@ class TaskGateway if (!empty($tasks)) { // Initiate the object webservice. - $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); + $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index c207d20..c548c08 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -61,7 +61,7 @@ class LifeCycle implements EndpointInterface // Array representing the status of the subtask. $result = []; // Initiate the object webservice. - $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); + $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); -- GitLab From 8c05474b15bb0ba0e2e5f1f29f6b2c560d09c649 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:36:27 +0100 Subject: [PATCH 053/111] :ambulance: Fixes webService call namespace In plugins --- plugins/tasks/LifeCycle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index c207d20..c548c08 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -61,7 +61,7 @@ class LifeCycle implements EndpointInterface // Array representing the status of the subtask. $result = []; // Initiate the object webservice. - $webservice = new WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); + $webservice = new FusionDirectory\Rest\WebServiceCall($_ENV['FUSION_DIRECTORY_API_URL'] . '/login', 'POST'); // Required to prepare future webservice call. E.g. Retrieval of mandatory token. $webservice->setCurlSettings(); -- GitLab From 328e10a862d7bb5fe9de52c1c282ed0e6e895ef1 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:40:04 +0100 Subject: [PATCH 054/111] :ambulance: Fix typo --- library/TaskGateway.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 3213cec..115ab16 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -203,7 +203,7 @@ class TaskGateway case 'Hourly' : // When checking for hourly schedules, consider both the days and hours $totalHours = $interval->days * 24 + $interval->h; - if ($totalHours>= 1) { + if ($totalHours >= 1) { $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); } else { $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; -- GitLab From 50fc43af4a442f8957d83f6bb17502d69fe6c75b Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 22 Aug 2024 14:50:15 +0100 Subject: [PATCH 055/111] :ambulance: stable commit stable commit --- plugins/tasks/Audit.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 5fc5206..ba326cc 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -91,6 +91,7 @@ class Audit implements EndpointInterface $result = []; foreach ($auditSubTasks as $task) { + // If the tasks must be treated - status and scheduled - process the sub-tasks if ($this->gateway->statusAndScheduleCheck($task)) { -- GitLab From 1ec066145499ba011b4c804500d65e7043bbf157 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 26 Aug 2024 11:56:34 +0100 Subject: [PATCH 056/111] :sparkles: Feat(Audit) - integration of lib part 1 Integration of audit lib integrator part 1 --- plugins/tasks/Audit.php | 108 +++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index ba326cc..375f7f4 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -28,6 +28,15 @@ class Audit implements EndpointInterface return []; } + /** + * @param array|NULL $data + * @return array + */ + public function processEndPointDelete (array $data = NULL): array + { + return []; + } + /** * @param array|NULL $data * @return array @@ -45,40 +54,6 @@ class Audit implements EndpointInterface } else { return ['No audit requiring removal']; } - - } - - /** - * @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; - } - - - /** - * @param array|NULL $data - * @return array - */ - public function processEndPointDelete (array $data = NULL): array - { - return []; } /** @@ -103,12 +78,22 @@ class Audit implements EndpointInterface // Verification of all audit and their potential removal based on retention days passed, also update subtasks. $result[] = $this->checkAuditPassedRetention($auditRetention, $task['dn'], $task['cn'][0]); } - } return $result; } + /** + * @param string $mainTaskDn + * @return array + * Note : Simply return attributes from the main related audit tasks. + */ + public function getAuditMainTask (string $mainTaskDn): array + { + // Retrieve data from the main task + return $this->gateway->getLdapTasks('(objectClass=fdAuditTasks)', ['fdAuditTasksRetention'], '', $mainTaskDn); + } + /** * @param $auditRetention * @return array @@ -122,11 +107,7 @@ class Audit implements EndpointInterface // Date time object will use the timezone defined in FD, code is in index.php $today = new DateTime(); - // Search in LDAP for audit entries (All entries ! This can be pretty heavy. - $audit = $this->gateway->getLdapTasks('(objectClass=fdAuditEvent)', ['fdAuditDateTime'], '', ''); - // Remove the count key from the audit array. - $this->gateway->unsetCountKeys($audit); - + $audit = $this->returnLdapAuditEntries(); // In case no audit exists, we have to update the tasks as well. Meaning below loop won't be reached. if (empty($audit)) { $result[$subTaskCN]['result'] = TRUE; @@ -160,6 +141,42 @@ class Audit implements EndpointInterface return $result; } + /** + * @return array + * NOTE : simply return the list of audit entries existing in LDAP + */ + public function returnLdapAuditEntries () : array + { + // Search in LDAP for audit entries (All entries ! This can be pretty heavy. + $audit = $this->gateway->getLdapTasks('(objectClass=fdAuditEvent)', ['fdAuditDateTime'], '', ''); + // Remove the count key from the audit array. + $this->gateway->unsetCountKeys($audit); + + 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; + } /** * @param $generalizeLdapDateTime @@ -186,15 +203,4 @@ class Audit implements EndpointInterface return $auditDate; } - /** - * @param string $mainTaskDn - * @return array - * Note : Simply return attributes from the main related audit tasks. - */ - public function getAuditMainTask (string $mainTaskDn): array - { - // Retrieve data from the main task - return $this->gateway->getLdapTasks('(objectClass=fdAuditTasks)', ['fdAuditTasksRetention'], '', $mainTaskDn); - } - } \ No newline at end of file -- GitLab From b2af582070556e53e31c48b53636ebfd64df1d24 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 26 Aug 2024 16:38:37 +0100 Subject: [PATCH 057/111] :sparkles: Feat(Audit) - integration of lib part 2 Integration of audit lib integrator part 2 --- plugins/tasks/Audit.php | 65 ++--------------------------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/plugins/tasks/Audit.php b/plugins/tasks/Audit.php index 375f7f4..6e51ae7 100644 --- a/plugins/tasks/Audit.php +++ b/plugins/tasks/Audit.php @@ -102,43 +102,8 @@ class Audit implements EndpointInterface */ public function checkAuditPassedRetention ($auditRetention, $subTaskDN, $subTaskCN): array { - $result = []; - - // Date time object will use the timezone defined in FD, code is in index.php - $today = new DateTime(); - - $audit = $this->returnLdapAuditEntries(); - // In case no audit exists, we have to update the tasks as well. Meaning below loop won't be reached. - if (empty($audit)) { - $result[$subTaskCN]['result'] = TRUE; - $result[$subTaskCN]['info'] = 'No audit to be removed.'; - $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, "2"); - } - - foreach ($audit as $record) { - // Record in Human Readable date time object - $auditDateTime = $this->generalizeLdapTimeToPhpObject($record['fdauditdatetime'][0]); - - $interval = $today->diff($auditDateTime); - - // Check if the interval is equal or greater than auditRetention setting - if ($interval->days >= $auditRetention) { - // If greater, delete the DN audit entry, we reuse removeSubTask method from gateway and get ldap response.(bool). - $result[$subTaskCN]['result'] = $this->gateway->removeSubTask($record['dn']); - $result[$subTaskCN]['info'] = 'Audit record removed.'; - - // Update tasks accordingly if LDAP succeeded. TRUE Boolean returned by ldap. - if ($result[$subTaskCN]['result']) { - // Update the subtask with the status completed a.k.a "2". - $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, "2"); - } else { - // Update the task with the LDAP potential error code. - $result[$subTaskCN]['statusUpdate'] = $this->gateway->updateTaskStatus($subTaskDN, $subTaskCN, $result[$record['dn']]['result']); - } - } - } - - return $result; + $auditLib = new FusionDirectory\Audit\AuditLib($auditRetention, $this->returnLdapAuditEntries(), $this->gateway, $subTaskDN, $subTaskCN); + return $auditLib->checkAuditPassedRetentionOrchestrator(); } /** @@ -177,30 +142,4 @@ class Audit implements EndpointInterface return $filtered; } - - /** - * @param $generalizeLdapDateTime - * @return DateTime|string[] - * @throws Exception - * Note : Simply take a generalized Ldap time (with UTC = Z) and transform it to php object dateTime. - */ - public function generalizeLdapTimeToPhpObject ($generalizeLdapDateTime) - { - // Extract the date part (first 8 characters: YYYYMMDD), we do not care about hour and seconds. - $auditTimeFormatted = substr($generalizeLdapDateTime, 0, 8); - - // Create a DateTime object using only the date part, carefully setting the timezone to UTC. Audit timestamp is UTC - $auditDate = DateTime::createFromFormat('Ymd', $auditTimeFormatted, new DateTimeZone('UTC')); - - // Check if the DateTime object was created successfully - if (!$auditDate) { - return ['Error in Time conversion from Audit record with timestamp :' . $generalizeLdapDateTime]; - } - - // Transform dateTime object from UTC to local defined dateTime. (Timezone is set in index.php). - $auditDate->setTimezone(new DateTimeZone(date_default_timezone_get())); - - return $auditDate; - } - } \ No newline at end of file -- GitLab From d8ca24caf3a5caf258bd565f5fb161b5c347a682 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 29 Aug 2024 16:29:33 +0100 Subject: [PATCH 058/111] :sparkles: Feat(Audit) - Removal of mailController Removal of Mail controller and fix typo in mail object. --- library/MailController.php | 117 -------------------------------- library/TaskGateway.php | 1 - plugins/tasks/Mail.php | 7 +- plugins/tasks/Notifications.php | 2 +- 4 files changed, 5 insertions(+), 122 deletions(-) delete mode 100644 library/MailController.php diff --git a/library/MailController.php b/library/MailController.php deleted file mode 100644 index af42adf..0000000 --- a/library/MailController.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -use PHPMailer\PHPMailer\PHPMailer; -use PHPMailer\PHPMailer\SMTP; -use PHPMailer\PHPMailer\Exception; - -class MailController -{ - - protected string $setFrom; - protected ?string $setBCC; - protected array $recipients; - protected string $body; - protected ?string $signature; - protected string $subject; - protected ?string $receipt; - protected ?array $attachments; - private PHPMailer $mail; - - function __construct ( - string $setFrom, - ?string $setBCC, - array $recipients, - string $body, - ?string $signature, - string $subject, - string $receipt = NULL, array $attachments = NULL - ) - { - // The TRUE value passed it to enable the exception handling properly. - $this->mail = new PHPMailer(TRUE); - $this->setFrom = $setFrom; - $this->setBCC = $setBCC; - $this->recipients = $recipients; - $this->body = $body; - $this->signature = $signature; - $this->subject = $subject; - $this->receipt = $receipt; - $this->attachments = $attachments; - - } - - public function sendMail (): array - { - // Our returned array - $errors = []; - - $this->mail->isSMTP(); - $this->mail->Host = $_ENV["MAIL_HOST"]; - - /* - * In case there are FQDN errors responses by the SMTP server, try below. - * $this->mail->Helo = '['.$_SERVER['SERVER_ADDR'].']'; - */ - - $this->mail->SMTPAuth = TRUE; - $this->mail->Username = $_ENV["MAIL_USER"]; - $this->mail->Password = $_ENV["MAIL_PASS"]; - $this->mail->SMTPSecure = $_ENV["MAIL_SEC"]; - $this->mail->Port = $_ENV["MAIL_PORT"]; - $this->mail->AuthType = 'LOGIN'; - - if (!empty($this->attachments)) { - foreach ($this->attachments as $attachment) { - $this->mail->addStringAttachment($attachment['content'], $attachment['cn']); - } - } - - $this->mail->setFrom($this->setFrom); - - if (!empty($this->setBCC)) { - $this->mail->addBCC($this->setBCC); - } - - if ($this->receipt === 'TRUE') { - $this->mail->addCustomHeader('Disposition-Notification-To', $this->setFrom); - } - $this->mail->Subject = $this->subject; - $this->mail->Body = $this->body; - - if (!empty($this->signature)) { - $this->mail->Body .= "\n\n" . $this->signature; - } - - // add it to keep SMTP connection open after each email sent - $this->mail->SMTPKeepAlive = TRUE; - - if (!empty($this->recipients["count"])) { - unset($this->recipients["count"]); - } - - /* We have an anti-spam logic applied above the mail controller. In case of mail template, only one email is within - the recipient address, in case of notifications (e.g), multiple address exists. Therefore, the counting of anti-spam - increment is applied prior of this controller added by the numbers of recipients. See notifications logic in a send - method. - */ - foreach ($this->recipients as $mail) { - $this->mail->addAddress($mail); - - try { - $this->mail->send(); - - } catch (Exception $e) { - $errors[] = $this->mail->ErrorInfo; - - } - $this->mail->clearAddresses(); - - if (empty($errors)) { - $errors[] = "SUCCESS"; - } - } - - $this->mail->smtpClose(); - return $errors; - } -} \ No newline at end of file diff --git a/library/TaskGateway.php b/library/TaskGateway.php index 115ab16..ac801be 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -332,7 +332,6 @@ class TaskGateway */ public function updateLastMailExecTime (string $dn) { - // prepare data $ldap_entry["fdTasksConfLastExecTime"] = time(); // Add data to LDAP diff --git a/plugins/tasks/Mail.php b/plugins/tasks/Mail.php index fd757fd..fe760e4 100644 --- a/plugins/tasks/Mail.php +++ b/plugins/tasks/Mail.php @@ -45,7 +45,7 @@ class Mail implements EndpointInterface */ public function processEndPointPatch (array $data = NULL): array { - return $this->processMailTasks($this->gateway->getObjectTypeTask('Mail Object)')); + return $this->processMailTasks($this->gateway->getObjectTypeTask('Mail Object')); } /** @@ -56,7 +56,7 @@ class Mail implements EndpointInterface public function processMailTasks (array $tasks): array { $result = []; - + // DEBUGGING $fdTasksConf = $this->getMailObjectConfiguration(); $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); @@ -67,6 +67,7 @@ class Mail implements EndpointInterface // Note : if list_tasks is empty, the controller receive null as result and will log/process it properly. foreach ($tasks as $mail) { + // verify status before processing (to be checked with schedule as well). if ($mail["fdtasksgranularstatus"][0] == 1 && $this->gateway->verifySchedule($mail["fdtasksgranularschedule"][0])) { @@ -99,7 +100,7 @@ class Mail implements EndpointInterface $attachments = NULL; } - $mail_controller = new MailController($setFrom, + $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom, $setBCC, $recipients, $body, diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 8a45981..64e48b6 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -226,7 +226,7 @@ class Notifications implements EndpointInterface foreach ($notifications as $data) { $numberOfRecipients = count($data['mailForm']['recipients']); - $mail_controller = new MailController( + $mail_controller = new \FusionDirectory\Mail\MailLib( $data['mailForm']['setFrom'], NULL, $data['mailForm']['recipients'], -- GitLab From ffa2725adc1914f52a36fcad327116dc4db2cdb2 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 12 Sep 2024 14:48:45 +0100 Subject: [PATCH 059/111] :sparkles: Feat(Orchestrator) - Adding two new environment var. Adds 2 new env var to allow mail to be secure or not. With auth or without. --- contrib/orchestrator.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/orchestrator.conf b/contrib/orchestrator.conf index fb017e8..fdf91b0 100644 --- a/contrib/orchestrator.conf +++ b/contrib/orchestrator.conf @@ -18,9 +18,13 @@ TOKEN_EXPIRY="300" REFRESH_EXPIRY="432000" #Information related to PHP Mailer +MAIL_AUTH="TRUE" +# If mail auth is TRUE, below user and pass are required MAIL_USER="fusiondirectory" MAIL_PASS="<mail_pass>" MAIL_HOST="mail.fusiondirectory.org" +MAIL_SEC_VERIFY="TRUE" +# If mail sec verify is set to true, mail_sec is required MAIL_SEC="<ssl/tls>" MAIL_PORT="25" -- GitLab From eb5531e248945450bf0d4beca0265e30def2e64c Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 16 Sep 2024 16:59:22 +0100 Subject: [PATCH 060/111] :sparkles: Feat(Orchestrator) - Adding first concept of supann awareness First concept of supann awareness within notifications. --- plugins/tasks/Notifications.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 64e48b6..478082d 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -72,7 +72,20 @@ class Notifications implements EndpointInterface $auditAttributes = $this->retrieveAuditedAttributes($task); $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; + + // Management of Supann Status + $monitoredSupannResource['resource'] = $notificationsMainTask[0]['fdTasksNotificationsResource']; + $monitoredSupannResource['state'] = $notificationsMainTask[0]['fdTasksNotificationsState']; + $monitoredSupannResource['subState'] = $notificationsMainTask[0]['fdTasksNotificationsSubState']; + $this->gateway->unsetCountKeys($monitoredAttrs); + $this->gateway->unsetCountKeys($monitoredSupannResource); + + // Condition if supannResource is set + if ($monitoredSupannResource['resource'] !== 'None') { + // Verify if there was a modification of supannRessourceEtatDate in audit. + $matchingSupann = array_intersect($auditAttributes, ['supannRessourceEtatDate']); + } // Verify if there is a match between audited attributes and monitored attributes from main task. $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); -- GitLab From 7ab2a70a94a7fff9a5a5cb0d8ee113e19cff4c57 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 17 Sep 2024 18:26:16 +0100 Subject: [PATCH 061/111] :sparkles: Feat(Orchestrator) - supann aware but can be notified if another supann if reached Supann aware but supannRessourceEtat can be a different one than the target. --- plugins/tasks/Notifications.php | 46 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 478082d..78d620b 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -74,17 +74,25 @@ class Notifications implements EndpointInterface $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; // Management of Supann Status - $monitoredSupannResource['resource'] = $notificationsMainTask[0]['fdTasksNotificationsResource']; - $monitoredSupannResource['state'] = $notificationsMainTask[0]['fdTasksNotificationsState']; - $monitoredSupannResource['subState'] = $notificationsMainTask[0]['fdTasksNotificationsSubState']; + $monitoredSupannResource['resource'] = $notificationsMainTask[0]['fdtasksnotificationsresource']; + $monitoredSupannResource['state'] = $notificationsMainTask[0]['fdtasksnotificationsstate']; + $monitoredSupannResource['subState'] = $notificationsMainTask[0]['fdtasksnotificationssubstate'] ?? NULL; + + // Simply remove keys with 'count' reported by ldap. $this->gateway->unsetCountKeys($monitoredAttrs); $this->gateway->unsetCountKeys($monitoredSupannResource); - // Condition if supannResource is set + // Verification if supannRessourceEtatDate with criteria from main tasks are matched. if ($monitoredSupannResource['resource'] !== 'None') { - // Verify if there was a modification of supannRessourceEtatDate in audit. - $matchingSupann = array_intersect($auditAttributes, ['supannRessourceEtatDate']); + if ($this->verifySupannState($monitoredSupannResource, $task['fdtasksgranulardn'][0])) { + // Simply inject supannRessourceEtat within monitoredAttrs to be taken into account for below logic + // NOTE: This is subject to change. This can always be a match if the state is present in the user but another + // supannRessourceEtat is reached. + + array_push($monitoredAttrs, 'supannRessourceEtat'); + + } } // Verify if there is a match between audited attributes and monitored attributes from main task. @@ -115,6 +123,29 @@ class Notifications implements EndpointInterface return $result; } + private function verifySupannState (array $supannResource, string $uid) : bool + { + $result = false; + // search the supannStates for the targeted uid by reusing getLdapTasks logic + $uidSupannStates = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtat'], '', $uid); + $this->gateway->unsetCountKeys($uidSupannStates); + + //Construct Supann Resource State as string + if (!empty($supannResource['subState'][0])) { + $monitoredSupannState = '{'.$supannResource['resource'][0].'}'.$supannResource['state'][0].':'.$supannResource['subState'][0]; + } else { + $monitoredSupannState = '{'.$supannResource['resource'][0].'}'.$supannResource['state'][0]; + } + + foreach ($uidSupannStates[0]['supannressourceetat'] as $supannResource) { + if ($supannResource === $monitoredSupannState) { + $result = true; + } + } + + return $result; + } + /** * @param string $mainTaskDn * @return array @@ -123,7 +154,8 @@ class Notifications implements EndpointInterface { // Retrieve data from the main task return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', - 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], + 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender', + 'fdTasksNotificationsSubState', 'fdTasksNotificationsState', 'fdTasksNotificationsResource'], '', $mainTaskDn); } -- GitLab From 5ca9c13f24d9887055139ee318c291c9e13d3baa Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 17 Sep 2024 18:26:48 +0100 Subject: [PATCH 062/111] :sparkles: Feat(Orchestrator) - small code fixes Small code fixes. --- plugins/tasks/Notifications.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 78d620b..6807622 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -123,26 +123,26 @@ class Notifications implements EndpointInterface return $result; } - private function verifySupannState (array $supannResource, string $uid) : bool + private function verifySupannState (array $supannResource, string $uid): bool { - $result = false; + $result = FALSE; // search the supannStates for the targeted uid by reusing getLdapTasks logic $uidSupannStates = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtat'], '', $uid); $this->gateway->unsetCountKeys($uidSupannStates); //Construct Supann Resource State as string if (!empty($supannResource['subState'][0])) { - $monitoredSupannState = '{'.$supannResource['resource'][0].'}'.$supannResource['state'][0].':'.$supannResource['subState'][0]; + $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0] . ':' . $supannResource['subState'][0]; } else { - $monitoredSupannState = '{'.$supannResource['resource'][0].'}'.$supannResource['state'][0]; + $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0]; } foreach ($uidSupannStates[0]['supannressourceetat'] as $supannResource) { if ($supannResource === $monitoredSupannState) { - $result = true; + $result = TRUE; } } - + return $result; } -- GitLab From 0807e97247be9087d78bd6575670843b97f2be19 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 19:54:40 +0100 Subject: [PATCH 063/111] :sparkles: Feat(Orchestrator) - all attrs monitored All attrs monitored. --- plugins/tasks/Notifications.php | 88 ++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 6807622..7385005 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -68,9 +68,17 @@ 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->retrieveAuditedAttributes($task); + // Simply retrieve the list of audited attributes, it is a json format and require decoding. + $auditAttributesJson = $this->retrieveAuditedAttributes($task); + $auditAttributes = NULL; // To set the var and reset its value. + + // Decoding the json_format into an associative array, implode allows to put all values of array together.(forming the json correctly). + foreach ($auditAttributesJson as $auditAttribute) { + $auditAttributes[] = json_decode(implode($auditAttribute), TRUE); + } + + // Recovering monitored attributes list from the defined notification task. $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; // Management of Supann Status @@ -83,21 +91,25 @@ class Notifications implements EndpointInterface $this->gateway->unsetCountKeys($monitoredAttrs); $this->gateway->unsetCountKeys($monitoredSupannResource); - // Verification if supannRessourceEtatDate with criteria from main tasks are matched. - if ($monitoredSupannResource['resource'] !== 'None') { - if ($this->verifySupannState($monitoredSupannResource, $task['fdtasksgranulardn'][0])) { - // Simply inject supannRessourceEtat within monitoredAttrs to be taken into account for below logic - // NOTE: This is subject to change. This can always be a match if the state is present in the user but another - // supannRessourceEtat is reached. - - array_push($monitoredAttrs, 'supannRessourceEtat'); - + // Verify if there is a match between audited attributes and monitored attributes from main task. (values to values, not keys). + $matchingAttrs = NULL; // Allows to define but reset variable as well. + if (!empty($auditAttributes)) { + foreach ($auditAttributes as $auditAttribute => $attributeName) { + foreach ($monitoredAttrs as $monitoredAttr) { + if (array_key_exists($monitoredAttr, $attributeName)) { + $matchingAttrs[] = $monitoredAttr; + } + } + } + // Verification if supannRessourceEtatDate with criteria from main tasks are matched. + if ($monitoredSupannResource['resource'][0] !== 'NONE') { + if ($this->verifySupannState($monitoredSupannResource, $auditAttributes)) { + // Simply create a match between audited and supannRessourceEtat, allowing further process below. + $matchingAttrs[] = 'supannRessourceEtat'; + } } } - // Verify if there is a match between audited attributes and monitored attributes from main task. - $matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); - if (!empty($matchingAttrs)) { // Fill an array with UID of audited user and related matching attributes $notifications[$notificationsMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; @@ -123,12 +135,15 @@ class Notifications implements EndpointInterface return $result; } - private function verifySupannState (array $supannResource, string $uid): bool + /** + * @param array $supannResource + * @param array $auditedAttrs + * @return bool + * Note : Create the supann format and check for a match. + */ + private function verifySupannState (array $supannResource, array $auditedAttrs): bool { $result = FALSE; - // search the supannStates for the targeted uid by reusing getLdapTasks logic - $uidSupannStates = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtat'], '', $uid); - $this->gateway->unsetCountKeys($uidSupannStates); //Construct Supann Resource State as string if (!empty($supannResource['subState'][0])) { @@ -137,15 +152,38 @@ class Notifications implements EndpointInterface $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0]; } - foreach ($uidSupannStates[0]['supannressourceetat'] as $supannResource) { - if ($supannResource === $monitoredSupannState) { - $result = TRUE; - } + // 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; } + /** + * @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 @@ -193,7 +231,8 @@ class Notifications implements EndpointInterface */ protected function retrieveAuditedAttributes (array $notificationTask): array { - $auditAttributes = []; + $auditAttributes = NULL; + $auditInformation = NULL; // Retrieve audit data attributes from the list of references set in the sub-task if (!empty($notificationTask['fdtasksgranularref'])) { @@ -204,13 +243,14 @@ class Notifications implements EndpointInterface $auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))', ['fdAuditAttributes'], '', $auditDN); } + // Again remove key: count retrieved from LDAP. $this->gateway->unsetCountKeys($auditInformation); // It is possible that an audit does not contain any attributes changes, condition is required. foreach ($auditInformation as $attr) { if (!empty($attr[0]['fdauditattributes'])) { // Clear and compact received results from above ldap search - $auditAttributes = $attr[0]['fdauditattributes']; + $auditAttributes[] = $attr[0]['fdauditattributes']; } } } -- GitLab From 3cd2dbc03322af4b8c154e83fbccab83eb2d3ad5 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 20:11:45 +0100 Subject: [PATCH 064/111] :sparkles: Feat(Orchestrator) - all attrs monitored and sent All attrs monitored and sent --- plugins/tasks/Notifications.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 7385005..6e20836 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -96,7 +96,8 @@ class Notifications implements EndpointInterface if (!empty($auditAttributes)) { foreach ($auditAttributes as $auditAttribute => $attributeName) { foreach ($monitoredAttrs as $monitoredAttr) { - if (array_key_exists($monitoredAttr, $attributeName)) { + // We actually need to verify attributeName as it can be NULL following specific audit elements ... + if ((!empty($attributeName)) && array_key_exists($monitoredAttr, $attributeName)) { $matchingAttrs[] = $monitoredAttr; } } -- GitLab From 04540db2c3092dbc965ba649e3511ace4563303a Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 22:40:04 +0100 Subject: [PATCH 065/111] :sparkles: Feat(Orchestrator) - small refactor small refactor --- plugins/tasks/Notifications.php | 119 +++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 6e20836..3089761 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -69,46 +69,25 @@ class Notifications implements EndpointInterface // Generate the mail form with all mail controller requirements $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask); - // Simply retrieve the list of audited attributes, it is a json format and require decoding. - $auditAttributesJson = $this->retrieveAuditedAttributes($task); - $auditAttributes = NULL; // To set the var and reset its value. - - // Decoding the json_format into an associative array, implode allows to put all values of array together.(forming the json correctly). - foreach ($auditAttributesJson as $auditAttribute) { - $auditAttributes[] = json_decode(implode($auditAttribute), TRUE); - } + // Simply retrieve the list of audited attributes + $auditAttributes = $this->decodeAuditAttributes($task); // Recovering monitored attributes list from the defined notification task. $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; - - // Management of Supann Status - $monitoredSupannResource['resource'] = $notificationsMainTask[0]['fdtasksnotificationsresource']; - $monitoredSupannResource['state'] = $notificationsMainTask[0]['fdtasksnotificationsstate']; - $monitoredSupannResource['subState'] = $notificationsMainTask[0]['fdtasksnotificationssubstate'] ?? NULL; - + // Reformat supann + $monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]); // Simply remove keys with 'count' reported by ldap. $this->gateway->unsetCountKeys($monitoredAttrs); $this->gateway->unsetCountKeys($monitoredSupannResource); - // Verify if there is a match between audited attributes and monitored attributes from main task. (values to values, not keys). - $matchingAttrs = NULL; // Allows to define but reset variable as well. - if (!empty($auditAttributes)) { - foreach ($auditAttributes as $auditAttribute => $attributeName) { - foreach ($monitoredAttrs as $monitoredAttr) { - // We actually need to verify attributeName as it can be NULL following specific audit elements ... - if ((!empty($attributeName)) && array_key_exists($monitoredAttr, $attributeName)) { - $matchingAttrs[] = $monitoredAttr; - } - } - } - // Verification if supannRessourceEtatDate with criteria from main tasks are matched. - if ($monitoredSupannResource['resource'][0] !== 'NONE') { - if ($this->verifySupannState($monitoredSupannResource, $auditAttributes)) { - // Simply create a match between audited and supannRessourceEtat, allowing further process below. - $matchingAttrs[] = 'supannRessourceEtat'; - } - } + // 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'; } if (!empty($matchingAttrs)) { @@ -136,6 +115,81 @@ class Notifications implements EndpointInterface return $result; } + /** + * Determine if Supann resource verification is needed. + * + * @param array $monitoredSupannResource + * @param array|null $auditAttributes + * @return bool + */ + private function shouldVerifySupannResource (array $monitoredSupannResource, ?array $auditAttributes): bool + { + if (!empty($auditAttributes)) { + return $monitoredSupannResource['resource'][0] !== 'NONE' && + $this->verifySupannState($monitoredSupannResource, $auditAttributes); + } + return FALSE; + } + + /** + * Get the Supann resource state. + * + * @param array $notificationsMainTask + * @return array + */ + private function getSupannResourceState (array $notificationsMainTask): array + { + return [ + 'resource' => $notificationsMainTask['fdtasksnotificationsresource'], + 'state' => $notificationsMainTask['fdtasksnotificationsstate'], + 'subState' => $notificationsMainTask['fdtasksnotificationssubstate'] ?? NULL + ]; + } + + /** + * Decode audit attributes from the task. + * + * @param array $task + * @return array|null + */ + private function decodeAuditAttributes (array $task): ?array + { + + $auditAttributesJson = $this->retrieveAuditedAttributes($task); + $auditAttributes = []; + + // Decoding the json_format into an associative array, implode allows to put all values of array together.(forming the json correctly). + foreach ($auditAttributesJson as $auditAttribute) { + $auditAttributes[] = json_decode(implode($auditAttribute), TRUE); + } + + 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 @@ -375,5 +429,4 @@ class Notifications implements EndpointInterface return $result; } - } \ No newline at end of file -- GitLab From ec310df4398f7cee4823f08e28fab2a9813b8c56 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 22:43:17 +0100 Subject: [PATCH 066/111] :sparkles: Feat(Orchestrator) - phpstan phpstan --- plugins/tasks/Notifications.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 3089761..e54b06d 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -152,7 +152,7 @@ class Notifications implements EndpointInterface * @param array $task * @return array|null */ - private function decodeAuditAttributes (array $task): ?array + private function decodeAuditAttributes (array $task): array { $auditAttributesJson = $this->retrieveAuditedAttributes($task); -- GitLab From dbebc8e9e1d19e92bb1ee5d366a7e446a1b3ca3a Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 22:55:07 +0100 Subject: [PATCH 067/111] :sparkles: Feat(Orchestrator) - small refactor and phpcs phpcs and refactor --- plugins/tasks/Notifications.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index e54b06d..5fec6a5 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -154,7 +154,6 @@ class Notifications implements EndpointInterface */ private function decodeAuditAttributes (array $task): array { - $auditAttributesJson = $this->retrieveAuditedAttributes($task); $auditAttributes = []; @@ -286,8 +285,8 @@ class Notifications implements EndpointInterface */ protected function retrieveAuditedAttributes (array $notificationTask): array { - $auditAttributes = NULL; - $auditInformation = NULL; + $auditAttributes = []; + $auditInformation = []; // Retrieve audit data attributes from the list of references set in the sub-task if (!empty($notificationTask['fdtasksgranularref'])) { -- GitLab From 6431bb73ec7734f340474d417fa680b6dcb4f12f Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 23 Sep 2024 22:57:27 +0100 Subject: [PATCH 068/111] :sparkles: Feat(Orchestrator) - phpstan fixes phpstan fixes --- plugins/tasks/Notifications.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Notifications.php b/plugins/tasks/Notifications.php index 5fec6a5..f78ef5e 100644 --- a/plugins/tasks/Notifications.php +++ b/plugins/tasks/Notifications.php @@ -150,7 +150,7 @@ class Notifications implements EndpointInterface * Decode audit attributes from the task. * * @param array $task - * @return array|null + * @return array */ private function decodeAuditAttributes (array $task): array { -- GitLab From 9f11ced093d54348ad5f7d2a3f0876f7414ad90b Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 1 Oct 2024 18:57:18 +0100 Subject: [PATCH 069/111] :sparkles: Feat(Orchestrator) - reminder endpoint step 1 reminder endpoint step 1 --- plugins/tasks/Reminder.php | 431 +++++++++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 plugins/tasks/Reminder.php diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php new file mode 100644 index 0000000..f5eb5fe --- /dev/null +++ b/plugins/tasks/Reminder.php @@ -0,0 +1,431 @@ +<?php + +class Reminder 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 + { + return []; + } + + /** + * @param array|null $data + * @return array + */ + public function processEndPointPost (array $data = NULL): array + { + return []; + } + + /** + * @param array|NULL $data + * @return array + * @throws Exception + */ + public function processEndPointPatch (array $data = NULL): array + { + return $this->processReminder($this->gateway->getObjectTypeTask('reminder')); + } + + /** + * @param array|NULL $data + * @return array + */ + public function processEndPointDelete (array $data = NULL): array + { + return []; + } + + /** + * @param array $notificationsSubTasks + * @return array + * @throws Exception + */ + public function processReminder (array $reminderSubTasks): array + { + $result = []; + // It will contain all required reminders to be potentially sent per main task. + $reminders = []; + + foreach ($reminderSubTasks 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 + $remindersMainTask = $this->getremindersMainTask($task['fdtasksgranularmaster'][0]); + $remindersMainTaskName = $task['fdtasksgranularmaster'][0]; + + // Generate the mail form with all mail controller requirements + $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask); + + // Simply retrieve the list of audited attributes + $auditAttributes = $this->decodeAuditAttributes($task); + + // Recovering monitored attributes list from the defined reminder task. + $monitoredAttrs = $remindersMainTask[0]['fdtasksremindersattributes']; + // Reformat supann + $monitoredSupannResource = $this->getSupannResourceState($remindersMainTask[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 reminder process. + $matchingAttrs[] = 'supannRessourceEtat'; + } + + if (!empty($matchingAttrs)) { + // Fill an array with UID of audited user and related matching attributes + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; + + // Require to be set for updating the status of the task later on. + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; + // Overwrite array reminders with complementing mail form body with uid and related attributes. + $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); + + } else { // Simply remove the subTask has no reminders are required + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; + } + } + } + + if (!empty($reminders)) { + $result[] = $this->sendremindersMail($reminders); + } + + return $result; + } + + /** + * Determine if Supann resource verification is needed. + * + * @param array $monitoredSupannResource + * @param array|null $auditAttributes + * @return bool + */ + private function shouldVerifySupannResource (array $monitoredSupannResource, ?array $auditAttributes): bool + { + if (!empty($auditAttributes)) { + return $monitoredSupannResource['resource'][0] !== 'NONE' && + $this->verifySupannState($monitoredSupannResource, $auditAttributes); + } + return FALSE; + } + + /** + * Get the Supann resource state. + * + * @param array $remindersMainTask + * @return array + */ + private function getSupannResourceState (array $remindersMainTask): array + { + return [ + 'resource' => $remindersMainTask['fdtasksremindersresource'], + 'state' => $remindersMainTask['fdtasksremindersstate'], + 'subState' => $remindersMainTask['fdtasksreminderssubstate'] ?? NULL + ]; + } + + /** + * Decode audit attributes from the task. + * + * @param array $task + * @return array + */ + private function decodeAuditAttributes (array $task): array + { + $auditAttributesJson = $this->retrieveAuditedAttributes($task); + $auditAttributes = []; + + // Decoding the json_format into an associative array, implode allows to put all values of array together.(forming the json correctly). + foreach ($auditAttributesJson as $auditAttribute) { + $auditAttributes[] = json_decode(implode($auditAttribute), TRUE); + } + + 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 + * @return bool + * Note : Create the supann format and check for a match. + */ + private function verifySupannState (array $supannResource, array $auditedAttrs): bool + { + $result = FALSE; + + //Construct Supann Resource State as string + if (!empty($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 = $this->getArrayValuesRecursive($auditedAttrs); + + if (in_array($monitoredSupannState, $auditedValues)) { + $result = TRUE; + } else { + $result = FALSE; + } + + return $result; + } + + /** + * @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 + */ + public function getremindersMainTask (string $mainTaskDn): array + { + // Retrieve data from the main task + return $this->gateway->getLdapTasks('(objectClass=fdTasksreminders)', ['fdTasksremindersListOfRecipientsMails', + 'fdTasksremindersAttributes', 'fdTasksremindersMailTemplate', 'fdTasksremindersEmailSender', + 'fdTasksremindersSubState', 'fdTasksremindersState', 'fdTasksremindersResource'], + '', $mainTaskDn); + } + + /** + * @param array $mainTask + * @return array + * Note : Simply generate the email to be sent as reminder. + */ + private function generateMainTaskMailTemplate (array $mainTask): array + { + // Generate email configuration for each result of subtasks having the same main task.w + $recipients = $mainTask[0]["fdtasksreminderslistofrecipientsmails"]; + $this->gateway->unsetCountKeys($recipients); + $sender = $mainTask[0]["fdtasksremindersemailsender"][0]; + $mailTemplateName = $mainTask[0]['fdtasksremindersmailtemplate'][0]; + + $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); + $mailContent = $mailInfos[0]; + + // Set the reminder array with all required variable for all sub-tasks of same main task origin. + $mailForm['setFrom'] = $sender; + $mailForm['recipients'] = $recipients; + $mailForm['body'] = $mailContent["fdmailtemplatebody"][0]; + $mailForm['signature'] = $mailContent["fdmailtemplatesignature"][0] ?? NULL; + $mailForm['subject'] = $mailContent["fdmailtemplatesubject"][0]; + $mailForm['receipt'] = $mailContent["fdmailtemplatereadreceipt"][0]; + + return $mailForm; + } + + /** + * @param array $reminderTask + * @return array + * NOTE : receive a unique tasks of type reminder (one subtask at a time) + */ + protected function retrieveAuditedAttributes (array $reminderTask): array + { + $auditAttributes = []; + $auditInformation = []; + + // Retrieve audit data attributes from the list of references set in the sub-task + if (!empty($reminderTask['fdtasksgranularref'])) { + // Remove count keys (count is shared by ldap). + $this->gateway->unsetCountKeys($reminderTask); + + foreach ($reminderTask['fdtasksgranularref'] as $auditDN) { + $auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))', + ['fdAuditAttributes'], '', $auditDN); + } + + // Again remove key: count retrieved from LDAP. + $this->gateway->unsetCountKeys($auditInformation); + // It is possible that an audit does not contain any attributes changes, condition is required. + foreach ($auditInformation as $attr) { + if (!empty($attr[0]['fdauditattributes'])) { + // Clear and compact received results from above ldap search + $auditAttributes[] = $attr[0]['fdauditattributes']; + } + } + } + + return $auditAttributes; + } + + /** + * @param array $reminders + * @param string $remindersMainTaskName + * @return array + * Note : This method is present to add to the mailForm body the proper uid and attrs info. + */ + private function completeremindersBody (array $reminders, string $remindersMainTaskName): array + { + // Iterate through each subTask and related attrs + $uidAttrsText = []; + + foreach ($reminders[$remindersMainTaskName]['subTask'] as $value) { + $uidName = $value['uid']; + $attrs = []; + + foreach ($value['attrs'] as $attr) { + $attrs[] = $attr; + } + $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; + } + + // Make the array unique, avoiding uid and same attribute duplication. + $uidAttrsText = array_unique($uidAttrsText); + // Add uid names and related attrs to mailForm['body'] + $reminders[$remindersMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); + + return $reminders; + } + + /** + * @param array $reminders + * @return array + * Note : Collect information and send reminder email. + */ + protected function sendremindersMail (array $reminders): array + { + $result = []; + // Re-use of the same mail processing template logic + $fdTasksConf = $this->gateway->getLdapTasks( + "(objectClass=fdTasksConf)", + ["fdTasksConfLastExecTime", "fdTasksConfIntervalEmails", "fdTasksConfMaxEmails"] + ); + $maxMailsConfig = $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; + + /* + Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is + sent per main task. + */ + $maxMailsIncrement = 0; + + foreach ($reminders 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(); + $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); + + // Verification anti-spam max mails to be sent and quit loop if matched. + $maxMailsIncrement += $numberOfRecipients; + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } + } + + return $result; + } + + /** + * @param array $serverResults + * @param array $subTask + * @param array $mailTaskBackend + * @return array + * Note : + */ + protected function processMailResponseAndUpdateTasks (array $serverResults, array $subTask, array $mailTaskBackend): array + { + $result = []; + if ($serverResults[0] == "SUCCESS") { + foreach ($subTask['subTask'] as $subTask => $details) { + + // CN of the main task + $cn = $subTask; + // DN of the main task + $dn = $details['dn']; + + // Update task status for the current $dn + $result[$dn]['statusUpdate'] = $this->gateway->updateTaskStatus($dn, $cn, "2"); + $result[$dn]['mailStatus'] = 'reminder was successfully sent'; + $result[$dn]['updateLastMailExec'] = $this->gateway->updateLastMailExecTime($mailTaskBackend[0]["dn"]); + } + } else { + foreach ($subTask['subTask'] as $subTask => $details) { + + // CN of the main task + $cn = $subTask; + // DN of the main task + $dn = $details['dn']; + + $result[$dn]['statusUpdate'] = $this->gateway->updateTaskStatus($dn, $cn, $serverResults[0]); + $result[$dn]['mailStatus'] = $serverResults; + } + } + + return $result; + } + +} \ No newline at end of file -- GitLab From c78c2c0df5784ffb32814f75b7e0bf76cc622488 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 3 Oct 2024 14:14:24 +0100 Subject: [PATCH 070/111] :sparkles: Feat(Orchestrator) - reminder backup save commit backup - gateway implemented. --- library/TaskGateway.php | 5 +++++ plugins/tasks/Reminder.php | 3 +++ 2 files changed, 8 insertions(+) diff --git a/library/TaskGateway.php b/library/TaskGateway.php index ac801be..823872e 100644 --- a/library/TaskGateway.php +++ b/library/TaskGateway.php @@ -38,6 +38,11 @@ class TaskGateway $this->unsetCountKeys($list_tasks); break; + case "reminder": + $list_tasks = $this->getLdapTasks("(&(objectClass=fdTasksGranular)(fdtasksgranulartype=Reminder))"); + $this->unsetCountKeys($list_tasks); + break; + case "removeSubTasks": case "activateCyclicTasks": // No need to get any parent tasks here, but to note break logic - we will return an array. diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index f5eb5fe..b3a4c68 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -58,6 +58,9 @@ class Reminder implements EndpointInterface // It will contain all required reminders to be potentially sent per main task. $reminders = []; + print_r($reminderSubTasks); + exit; + foreach ($reminderSubTasks as $task) { // If the tasks must be treated - status and scheduled - process the sub-tasks if ($this->gateway->statusAndScheduleCheck($task)) { -- GitLab From b80fe62ad1c9cdcc04e24c14dc3daab27fe5afc3 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 11:47:12 +0100 Subject: [PATCH 071/111] :sparkles: Feat(Orchestrator) - up to generate mail template save commit backup - up to generate mail template --- plugins/tasks/Reminder.php | 50 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index b3a4c68..5588da6 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -58,16 +58,46 @@ class Reminder implements EndpointInterface // It will contain all required reminders to be potentially sent per main task. $reminders = []; - print_r($reminderSubTasks); - exit; + //[fdtasksgranularmaster] => Array + // ( + // [0] => cn=Reminder,ou=tasks,dc=example,dc=com + // ) + // + // [3] => fdtasksgranularmaster + // [fdtasksgranulartype] => Array + // ( + // [0] => Reminder + // ) + // + // [4] => fdtasksgranulartype + // [fdtasksgranularhelper] => Array + // ( + // [0] => 30 + // ) + // + // [5] => fdtasksgranularhelper + // [fdtasksgranularschedule] => Array + // ( + // [0] => 20240926010000 + // ) + // + // [6] => fdtasksgranularschedule + // [fdtasksgranulardn] => Array + // ( + // [0] => uid=testing2,ou=people,dc=example,dc=com + // ) + // + // [7] => fdtasksgranulardn + // [dn] => cn=Reminder-SubTask-1728293363_4827,ou=tasks,dc=example,dc=com foreach ($reminderSubTasks 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 - $remindersMainTask = $this->getremindersMainTask($task['fdtasksgranularmaster'][0]); $remindersMainTaskName = $task['fdtasksgranularmaster'][0]; + $remindersMainTask = $this->getRemindersMainTask($remindersMainTaskName); + // Generate the mail form with all mail controller requirements $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask); @@ -245,13 +275,13 @@ class Reminder implements EndpointInterface * @param string $mainTaskDn * @return array */ - public function getremindersMainTask (string $mainTaskDn): array + public function getRemindersMainTask (string $mainTaskDn): array { - // Retrieve data from the main task - return $this->gateway->getLdapTasks('(objectClass=fdTasksreminders)', ['fdTasksremindersListOfRecipientsMails', - 'fdTasksremindersAttributes', 'fdTasksremindersMailTemplate', 'fdTasksremindersEmailSender', - 'fdTasksremindersSubState', 'fdTasksremindersState', 'fdTasksremindersResource'], - '', $mainTaskDn); + // Retrieve data from the main Reminder task + return $this->gateway->getLdapTasks('(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', + 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', + 'fdTasksReminderPPolicy', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', + 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers'], '', $mainTaskDn); } /** @@ -261,6 +291,8 @@ class Reminder implements EndpointInterface */ private function generateMainTaskMailTemplate (array $mainTask): array { + print_r($mainTask); + exit; // Generate email configuration for each result of subtasks having the same main task.w $recipients = $mainTask[0]["fdtasksreminderslistofrecipientsmails"]; $this->gateway->unsetCountKeys($recipients); -- GitLab From 379ea2f8d381453d0035aae46cfd31625e6d19b1 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 15:09:32 +0100 Subject: [PATCH 072/111] :sparkles: Feat(Orchestrator) - supann retrieved save commit backup - supann retrieved --- plugins/tasks/Reminder.php | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 5588da6..9ce97b9 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -102,18 +102,10 @@ class Reminder implements EndpointInterface // Generate the mail form with all mail controller requirements $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask); - // Simply retrieve the list of audited attributes - $auditAttributes = $this->decodeAuditAttributes($task); - - // Recovering monitored attributes list from the defined reminder task. - $monitoredAttrs = $remindersMainTask[0]['fdtasksremindersattributes']; - // Reformat supann $monitoredSupannResource = $this->getSupannResourceState($remindersMainTask[0]); - // Simply remove keys with 'count' reported by ldap. - $this->gateway->unsetCountKeys($monitoredAttrs); - $this->gateway->unsetCountKeys($monitoredSupannResource); - + print_r($monitoredSupannResource); + exit; // Find matching attributes between audited and monitored attributes $matchingAttrs = $this->findMatchingAttributes($auditAttributes, $monitoredAttrs); @@ -173,9 +165,9 @@ class Reminder implements EndpointInterface private function getSupannResourceState (array $remindersMainTask): array { return [ - 'resource' => $remindersMainTask['fdtasksremindersresource'], - 'state' => $remindersMainTask['fdtasksremindersstate'], - 'subState' => $remindersMainTask['fdtasksreminderssubstate'] ?? NULL + 'resource' => $remindersMainTask['fdtasksreminderresource'], + 'state' => $remindersMainTask['fdtasksreminderstate'], + 'subState' => $remindersMainTask['fdtasksremindersubstate'] ?? NULL ]; } @@ -291,13 +283,11 @@ class Reminder implements EndpointInterface */ private function generateMainTaskMailTemplate (array $mainTask): array { - print_r($mainTask); - exit; - // Generate email configuration for each result of subtasks having the same main task.w - $recipients = $mainTask[0]["fdtasksreminderslistofrecipientsmails"]; + // Generate email configuration for each result of subtasks having the same main task. + $recipients = $mainTask[0]["fdtasksreminderlistofrecipientsmails"]; $this->gateway->unsetCountKeys($recipients); - $sender = $mainTask[0]["fdtasksremindersemailsender"][0]; - $mailTemplateName = $mainTask[0]['fdtasksremindersmailtemplate'][0]; + $sender = $mainTask[0]['fdtasksreminderemailsender'][0]; + $mailTemplateName = $mainTask[0]['fdtasksremindermailtemplate'][0]; $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); $mailContent = $mailInfos[0]; -- GitLab From 5cd7d15e3ae003168ceda3cda0c15a75bdc2d353 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 16:18:22 +0100 Subject: [PATCH 073/111] :sparkles: Feat(Orchestrator) - next supann retrieved save commit backup - next supann --- plugins/tasks/Reminder.php | 144 +++++++------------------------------ 1 file changed, 27 insertions(+), 117 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 9ce97b9..3beae1d 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -58,7 +58,7 @@ class Reminder implements EndpointInterface // It will contain all required reminders to be potentially sent per main task. $reminders = []; - //[fdtasksgranularmaster] => Array + //[fdtasksgranularmaster] => Array // ( // [0] => cn=Reminder,ou=tasks,dc=example,dc=com // ) @@ -104,32 +104,18 @@ class Reminder implements EndpointInterface $monitoredSupannResource = $this->getSupannResourceState($remindersMainTask[0]); - print_r($monitoredSupannResource); - exit; - // 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 reminder process. - $matchingAttrs[] = 'supannRessourceEtat'; - } - - if (!empty($matchingAttrs)) { - // Fill an array with UID of audited user and related matching attributes - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['attrs'] = $matchingAttrs; - - // Require to be set for updating the status of the task later on. - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; - $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; - // Overwrite array reminders with complementing mail form body with uid and related attributes. - $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); - - } else { // Simply remove the subTask has no reminders are required - $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); - $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; - } + + // Require to be set for updating the status of the task later on. + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; + // Here we must have a logic to create the token for the subTask. + $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); + + // Removal subtask + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; + } } @@ -164,11 +150,21 @@ class Reminder implements EndpointInterface */ private function getSupannResourceState (array $remindersMainTask): array { - return [ + $supannArray = [ 'resource' => $remindersMainTask['fdtasksreminderresource'], 'state' => $remindersMainTask['fdtasksreminderstate'], 'subState' => $remindersMainTask['fdtasksremindersubstate'] ?? NULL ]; + + if ($remindersMainTask['fdTasksReminderAccountProlongation']) { + $supannArray[] = [ + 'nextResource' => $remindersMainTask['fdtasksremindernextresource'], + 'nextState' => $remindersMainTask['fdtasksremindernextstate'], + 'nextSubState' => $remindersMainTask['fdtasksremindernextsubstate'], + ]; + } + + return $supannArray; } /** @@ -190,30 +186,6 @@ class Reminder 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 @@ -270,10 +242,11 @@ class Reminder implements EndpointInterface public function getRemindersMainTask (string $mainTaskDn): array { // Retrieve data from the main Reminder task - return $this->gateway->getLdapTasks('(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', + return $this->gateway->getLdapTasks( '(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', 'fdTasksReminderPPolicy', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', - 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers'], '', $mainTaskDn); + 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', + 'fdTasksReminderNextState', 'fdTasksReminderNextSubState', 'fdTasksReminderSubState'], '', $mainTaskDn); } /** @@ -303,69 +276,6 @@ class Reminder implements EndpointInterface return $mailForm; } - /** - * @param array $reminderTask - * @return array - * NOTE : receive a unique tasks of type reminder (one subtask at a time) - */ - protected function retrieveAuditedAttributes (array $reminderTask): array - { - $auditAttributes = []; - $auditInformation = []; - - // Retrieve audit data attributes from the list of references set in the sub-task - if (!empty($reminderTask['fdtasksgranularref'])) { - // Remove count keys (count is shared by ldap). - $this->gateway->unsetCountKeys($reminderTask); - - foreach ($reminderTask['fdtasksgranularref'] as $auditDN) { - $auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))', - ['fdAuditAttributes'], '', $auditDN); - } - - // Again remove key: count retrieved from LDAP. - $this->gateway->unsetCountKeys($auditInformation); - // It is possible that an audit does not contain any attributes changes, condition is required. - foreach ($auditInformation as $attr) { - if (!empty($attr[0]['fdauditattributes'])) { - // Clear and compact received results from above ldap search - $auditAttributes[] = $attr[0]['fdauditattributes']; - } - } - } - - return $auditAttributes; - } - - /** - * @param array $reminders - * @param string $remindersMainTaskName - * @return array - * Note : This method is present to add to the mailForm body the proper uid and attrs info. - */ - private function completeremindersBody (array $reminders, string $remindersMainTaskName): array - { - // Iterate through each subTask and related attrs - $uidAttrsText = []; - - foreach ($reminders[$remindersMainTaskName]['subTask'] as $value) { - $uidName = $value['uid']; - $attrs = []; - - foreach ($value['attrs'] as $attr) { - $attrs[] = $attr; - } - $uidAttrsText[] = "\n$uidName attrs=[" . implode(', ', $attrs) . "]"; - } - - // Make the array unique, avoiding uid and same attribute duplication. - $uidAttrsText = array_unique($uidAttrsText); - // Add uid names and related attrs to mailForm['body'] - $reminders[$remindersMainTaskName]['mailForm']['body'] .= " " . implode(" ", $uidAttrsText); - - return $reminders; - } - /** * @param array $reminders * @return array -- GitLab From 0aef9692d47d1fa51ab64162e7909d1b2aec3b7d Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 17:12:00 +0100 Subject: [PATCH 074/111] :sparkles: Feat(Orchestrator) - concept of monitoreds attrs restored save commit backup - monitored attrs --- plugins/tasks/Reminder.php | 51 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 3beae1d..4da2dc5 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -97,12 +97,16 @@ class Reminder implements EndpointInterface // Retrieve data from the main task $remindersMainTaskName = $task['fdtasksgranularmaster'][0]; $remindersMainTask = $this->getRemindersMainTask($remindersMainTaskName); - + // remove the count keys + $this->gateway->unsetCountKeys($remindersMainTask); // Generate the mail form with all mail controller requirements $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask); - $monitoredSupannResource = $this->getSupannResourceState($remindersMainTask[0]); + // Get monitored resources + $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); + print_r($monitoredResources); + exit; // Require to be set for updating the status of the task later on. @@ -143,49 +147,38 @@ class Reminder implements EndpointInterface } /** - * Get the Supann resource state. + * Get the monitored resources for reminder to be activated. * * @param array $remindersMainTask * @return array */ - private function getSupannResourceState (array $remindersMainTask): array + private function getMonitoredResources (array $remindersMainTask): array { - $supannArray = [ + $monitoredResourcesArray = [ 'resource' => $remindersMainTask['fdtasksreminderresource'], 'state' => $remindersMainTask['fdtasksreminderstate'], 'subState' => $remindersMainTask['fdtasksremindersubstate'] ?? NULL ]; - if ($remindersMainTask['fdTasksReminderAccountProlongation']) { - $supannArray[] = [ - 'nextResource' => $remindersMainTask['fdtasksremindernextresource'], - 'nextState' => $remindersMainTask['fdtasksremindernextstate'], - 'nextSubState' => $remindersMainTask['fdtasksremindernextsubstate'], - ]; - } + // Boolean returned by ldap is a string. + if (isset($remindersMainTask['fdtasksreminderaccountprolongation'][0]) && $remindersMainTask['fdtasksreminderaccountprolongation'][0] === 'TRUE') { + // Add the potential next resources states to the array + if (isset($remindersMainTask['fdtasksremindernextresource'])) { - return $supannArray; - } + $monitoredResourcesArray['nextResource'] = $remindersMainTask['fdtasksremindernextresource']; + $monitoredResourcesArray['nextState'] = $remindersMainTask['fdtasksremindernextstate']; + $monitoredResourcesArray['nextSubState'] = $remindersMainTask['fdtasksremindernextsubstate'] ?? NULL; + } - /** - * Decode audit attributes from the task. - * - * @param array $task - * @return array - */ - private function decodeAuditAttributes (array $task): array - { - $auditAttributesJson = $this->retrieveAuditedAttributes($task); - $auditAttributes = []; + $monitoredResourcesArray['fdTasksReminderPosix'] = $remindersMainTask['fdtasksreminderposix'] ?? FALSE; + $monitoredResourcesArray['fdTasksReminderPPolicy'] = $remindersMainTask['fdtasksreminderppolicy'] ?? FALSE; - // Decoding the json_format into an associative array, implode allows to put all values of array together.(forming the json correctly). - foreach ($auditAttributesJson as $auditAttribute) { - $auditAttributes[] = json_decode(implode($auditAttribute), TRUE); } - return $auditAttributes; + return $monitoredResourcesArray; } + /** * @param array $supannResource * @param array $auditedAttrs @@ -281,7 +274,7 @@ class Reminder implements EndpointInterface * @return array * Note : Collect information and send reminder email. */ - protected function sendremindersMail (array $reminders): array + protected function sendRemindersMail (array $reminders): array { $result = []; // Re-use of the same mail processing template logic -- GitLab From 63e06a560f9d2205e2688a70cd5614defe4fed21 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 17:38:59 +0100 Subject: [PATCH 075/111] :sparkles: Feat(Orchestrator) - removal subTasks if no triggers save commit backup - removal of subTasks if no triggers --- plugins/tasks/Reminder.php | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 4da2dc5..8a5a986 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -105,27 +105,30 @@ class Reminder implements EndpointInterface // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); - print_r($monitoredResources); - exit; + if ($monitoredResources['resource'][0] === 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { + // Removal subtask + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; + } - // Require to be set for updating the status of the task later on. - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; - $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; - // Here we must have a logic to create the token for the subTask. - $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); - // Removal subtask - $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); - $result[$task['dn']]['Status'] = 'No matching audited attributes with monitored attributes, safely removed!'; + +// // Require to be set for updating the status of the task later on. +// $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; +// $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; +// $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; +// // Here we must have a logic to create the token for the subTask. +// $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); +// + } } - if (!empty($reminders)) { - $result[] = $this->sendremindersMail($reminders); - } +// if (!empty($reminders)) { +// $result[] = $this->sendRemindersMail($reminders); +// } return $result; } @@ -154,6 +157,7 @@ class Reminder implements EndpointInterface */ private function getMonitoredResources (array $remindersMainTask): array { + $monitoredResourcesArray = [ 'resource' => $remindersMainTask['fdtasksreminderresource'], 'state' => $remindersMainTask['fdtasksreminderstate'], @@ -175,6 +179,9 @@ class Reminder implements EndpointInterface } + // For development logic, add the prolongation attribute. It will be checked later in the logic process. + $monitoredResourcesArray['prolongation'] = $remindersMainTask['fdtasksreminderaccountprolongation'][0] ?? FALSE; + return $monitoredResourcesArray; } -- GitLab From 8921685e915e59998041760f8f0b535ff5e93020 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 7 Oct 2024 18:27:26 +0100 Subject: [PATCH 076/111] :sparkles: Feat(Orchestrator) - next step : verification of supann for passed DN save commit backup - see next step --- plugins/tasks/Reminder.php | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 8a5a986..1cb2048 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -106,12 +106,20 @@ class Reminder implements EndpointInterface // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); + // Case where no supann are monitored nor prolongation desired. (Useless subTask). if ($monitoredResources['resource'][0] === 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { // Removal subtask $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; } + // Case where supann is set monitored but no prolongation desired. + if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { + if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0]), $monitoredResources, $fdtasksgranularhelper[0]) { + + } + } + // // Require to be set for updating the status of the task later on. @@ -134,21 +142,20 @@ class Reminder implements EndpointInterface } /** - * Determine if Supann resource verification is needed. - * - * @param array $monitoredSupannResource - * @param array|null $auditAttributes + * @param $task * @return bool + * Note : Verify the account status of the DN with the requirements of main tasks. */ - private function shouldVerifySupannResource (array $monitoredSupannResource, ?array $auditAttributes): bool + private function supannAboutToExpire (string $DN, array $monitoredResources, int $days) : bool { - if (!empty($auditAttributes)) { - return $monitoredSupannResource['resource'][0] !== 'NONE' && - $this->verifySupannState($monitoredSupannResource, $auditAttributes); - } - return FALSE; + $result = FALSE; + + // Search the DN for supannRessourceState + + return $result; } + /** * Get the monitored resources for reminder to be activated. * -- GitLab From 1d743cfcc36c80cd6ccd18a64fcf9569723a33c3 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 8 Oct 2024 16:36:41 +0100 Subject: [PATCH 077/111] :sparkles: Feat(Orchestrator) - supann state verification save commit - supann state verif --- plugins/tasks/Reminder.php | 64 ++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 1cb2048..d4a54ba 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -115,7 +115,7 @@ class Reminder implements EndpointInterface // Case where supann is set monitored but no prolongation desired. if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { - if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0]), $monitoredResources, $fdtasksgranularhelper[0]) { + if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { } } @@ -146,15 +146,36 @@ class Reminder implements EndpointInterface * @return bool * Note : Verify the account status of the DN with the requirements of main tasks. */ - private function supannAboutToExpire (string $DN, array $monitoredResources, int $days) : bool + private function supannAboutToExpire (string $dn, array $monitoredResources, int $days) : bool { $result = FALSE; // Search the DN for supannRessourceState + $supannResources = $this->retrieveSupannResources($dn); + $this->verifySupannState($monitoredResources, $supannResources); return $result; } + /** + * @param $dn + * @return array + * Note : Simply return supann resource array from the specific passed DN. + */ + private function retrieveSupannResources($dn) : array + { + $supannResources = []; + $supannResources= $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate', 'supannRessourceEtat'], + '', $dn); + // Simply remove key "count" + $this->gateway->unsetCountKeys($supannResources); + // Removing unrequired keys + $supannResources = $supannResources[0]; + + return $supannResources; + + } + /** * Get the monitored resources for reminder to be activated. @@ -199,29 +220,46 @@ class Reminder implements EndpointInterface * @return bool * Note : Create the supann format and check for a match. */ - private function verifySupannState (array $supannResource, array $auditedAttrs): bool + private function verifySupannState (array $reminderSupann, array $dnSupann): bool { $result = FALSE; - //Construct Supann Resource State as string - if (!empty($supannResource['subState'][0])) { - $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0] . ':' . $supannResource['subState'][0]; + //Construct the reminder Supann Resource State as string + if (!empty($reminderSupann['subState'][0])) { + $monitoredSupannState = '{' . $reminderSupann['resource'][0] . '}' . $reminderSupann['state'][0] . ':' . $reminderSupann['subState'][0]; } else { - $monitoredSupannState = '{' . $supannResource['resource'][0] . '}' . $supannResource['state'][0]; + $monitoredSupannState = '{' . $reminderSupann['resource'][0] . '}' . $reminderSupann['state'][0]; } - // Get all the values only of a multidimensional array. - $auditedValues = $this->getArrayValuesRecursive($auditedAttrs); + if ($dnSupann['supannressourceetat'][0] === $monitoredSupannState) { + // verify the date + $DnSupannDateObject = $this->retrieveDateFromSupannResouceState($dnSupann['supannressourceetatdate'][0]); - if (in_array($monitoredSupannState, $auditedValues)) { - $result = TRUE; - } else { - $result = FALSE; + //Verification if the time is lower or equal than the reminder time. + if ($DnSupannDateObject !== FALSE) { + $today = new DateTime(); + $interval = $today->diff($DnSupannDateObject); + + if ($interval->days <= ) + + } } return $result; } + private function retrieveDateFromSupannResouceState ($supannEtatDate) : ?DateTime + { + // Simply take the last 8 digit + preg_match('/(\d{8})$/', $supannEtatDate, $matches); + + if (!empty($matches[0])) { + $dateString = $matches[0]; + } + + return DateTime::createFromFormat('Ymd', $dateString); + } + /** * @param $array * @return array -- GitLab From 6a99579e275f0372444d1c52b3f02fafc19e1d7c Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 8 Oct 2024 17:41:51 +0100 Subject: [PATCH 078/111] :sparkles: Feat(Orchestrator) - continuation of logic save commit - continuation of logic --- plugins/tasks/Reminder.php | 80 ++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index d4a54ba..3737de2 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -116,27 +116,32 @@ class Reminder implements EndpointInterface // Case where supann is set monitored but no prolongation desired. if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { + // About to expire, thefefore send email + // Require to be set for updating the status of the task later on. + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; + + } else { + // Not about to expire, delete subTask + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; } } - -// // Require to be set for updating the status of the task later on. -// $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; -// $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; -// $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; -// // Here we must have a logic to create the token for the subTask. -// $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); -// + // // Here we must have a logic to create the token for the subTask. + // $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); + // } } -// if (!empty($reminders)) { -// $result[] = $this->sendRemindersMail($reminders); -// } + if (!empty($reminders)) { + $result[] = $this->sendRemindersMail($reminders); + } return $result; } @@ -146,13 +151,27 @@ class Reminder implements EndpointInterface * @return bool * Note : Verify the account status of the DN with the requirements of main tasks. */ - private function supannAboutToExpire (string $dn, array $monitoredResources, int $days) : bool + private function supannAboutToExpire (string $dn, array $monitoredResources, int $days): bool { $result = FALSE; // Search the DN for supannRessourceState $supannResources = $this->retrieveSupannResources($dn); - $this->verifySupannState($monitoredResources, $supannResources); + if ($this->verifySupannState($monitoredResources, $supannResources)) { + // verify the date + $DnSupannDateObject = $this->retrieveDateFromSupannResouceState($supannResources['supannressourceetatdate'][0]); + + //Verification if the time is lower or equal than the reminder time. + if ($DnSupannDateObject !== FALSE) { + $today = new DateTime(); + $interval = $today->diff($DnSupannDateObject); + + // Interval can be negative if date is in the past - we make sure it is not in the past by using invert. + if ($interval->days <= $days && $interval->invert == 0) { + $result = TRUE; + } + } + } return $result; } @@ -162,15 +181,19 @@ class Reminder implements EndpointInterface * @return array * Note : Simply return supann resource array from the specific passed DN. */ - private function retrieveSupannResources($dn) : array + private function retrieveSupannResources ($dn): array { $supannResources = []; - $supannResources= $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate', 'supannRessourceEtat'], - '', $dn); + $supannResources = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate', 'supannRessourceEtat'], + '', $dn); // Simply remove key "count" $this->gateway->unsetCountKeys($supannResources); // Removing unrequired keys - $supannResources = $supannResources[0]; + print_r($supannResources); + + if (!empty($supannResources)) { + $supannResources = $supannResources[0]; + } return $supannResources; @@ -231,29 +254,28 @@ class Reminder implements EndpointInterface $monitoredSupannState = '{' . $reminderSupann['resource'][0] . '}' . $reminderSupann['state'][0]; } - if ($dnSupann['supannressourceetat'][0] === $monitoredSupannState) { - // verify the date - $DnSupannDateObject = $this->retrieveDateFromSupannResouceState($dnSupann['supannressourceetatdate'][0]); - //Verification if the time is lower or equal than the reminder time. - if ($DnSupannDateObject !== FALSE) { - $today = new DateTime(); - $interval = $today->diff($DnSupannDateObject); - - if ($interval->days <= ) + if (!empty ($dnSupann['supannressourceetat][0]']) && $dnSupann['supannressourceetat'][0] === $monitoredSupannState) { - } + $result = TRUE; } return $result; } - private function retrieveDateFromSupannResouceState ($supannEtatDate) : ?DateTime + /** + * @param $supannEtatDate + * @return DateTime|false + * Note : Simply transform string date of supann to a dateTime object. + * Can return bool (false) or dateTime object. + */ + private function retrieveDateFromSupannResouceState ($supannEtatDate) { + $dateString = NULL; // Simply take the last 8 digit preg_match('/(\d{8})$/', $supannEtatDate, $matches); - if (!empty($matches[0])) { + if (!empty($matches)) { $dateString = $matches[0]; } -- GitLab From c0d80135fe54049edc8effebda9618ac9559d580 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 8 Oct 2024 18:12:27 +0100 Subject: [PATCH 079/111] :sparkles: Feat(Orchestrator) - removal and send workign save commit - removal and send working - subtask updated --- plugins/tasks/Reminder.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 3737de2..18636b4 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -188,9 +188,8 @@ class Reminder implements EndpointInterface '', $dn); // Simply remove key "count" $this->gateway->unsetCountKeys($supannResources); - // Removing unrequired keys - print_r($supannResources); + // Removing unrequired keys if (!empty($supannResources)) { $supannResources = $supannResources[0]; } @@ -255,7 +254,7 @@ class Reminder implements EndpointInterface } - if (!empty ($dnSupann['supannressourceetat][0]']) && $dnSupann['supannressourceetat'][0] === $monitoredSupannState) { + if (!empty($dnSupann['supannressourceetat'][0]) && $dnSupann['supannressourceetat'][0] === $monitoredSupannState) { $result = TRUE; } -- GitLab From 53201d33a5637a8b1fe1a4b461cf63b977bb0e88 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 10 Oct 2024 18:29:45 +0100 Subject: [PATCH 080/111] :sparkles: Feat(Orchestrator) - before reminded mail template save commit - backup + logic to be though on mail separation and status update. --- plugins/tasks/Reminder.php | 89 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 18636b4..b3c852d 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -58,38 +58,6 @@ class Reminder implements EndpointInterface // It will contain all required reminders to be potentially sent per main task. $reminders = []; - //[fdtasksgranularmaster] => Array - // ( - // [0] => cn=Reminder,ou=tasks,dc=example,dc=com - // ) - // - // [3] => fdtasksgranularmaster - // [fdtasksgranulartype] => Array - // ( - // [0] => Reminder - // ) - // - // [4] => fdtasksgranulartype - // [fdtasksgranularhelper] => Array - // ( - // [0] => 30 - // ) - // - // [5] => fdtasksgranularhelper - // [fdtasksgranularschedule] => Array - // ( - // [0] => 20240926010000 - // ) - // - // [6] => fdtasksgranularschedule - // [fdtasksgranulardn] => Array - // ( - // [0] => uid=testing2,ou=people,dc=example,dc=com - // ) - // - // [7] => fdtasksgranulardn - // [dn] => cn=Reminder-SubTask-1728293363_4827,ou=tasks,dc=example,dc=com - foreach ($reminderSubTasks as $task) { // If the tasks must be treated - status and scheduled - process the sub-tasks if ($this->gateway->statusAndScheduleCheck($task)) { @@ -101,7 +69,10 @@ class Reminder implements EndpointInterface $this->gateway->unsetCountKeys($remindersMainTask); // Generate the mail form with all mail controller requirements - $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask); + $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, FALSE); + + // Retrieve email attribute for the monitored members requiring reminding. + $mailTemplateMonitoredMember = $this->getEmailFromReminded($task['fdtasksgranulardn'][0]); // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); @@ -116,12 +87,13 @@ class Reminder implements EndpointInterface // Case where supann is set monitored but no prolongation desired. if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'FALSE') { if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { - // About to expire, thefefore send email - // Require to be set for updating the status of the task later on. + // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; - $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateForm; + $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateRecipients; + // Add the reminded email form + $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateMonitoredMember; } else { // Not about to expire, delete subTask @@ -146,6 +118,28 @@ class Reminder implements EndpointInterface return $result; } + /** + * @param string $dn + * @return string + * Note : return the mail attribute from gosaMail objectclass. + */ + private function getEmailFromReminded (string $dn): string + { + // in case the DN do not have an email set. - Return string FALSE. + $result = "FALSE"; + $email = $this->gateway->getLdapTasks('(objectClass=gosaMailAccount)', ['mail'], + '', $dn); + // Simply remove key "count" + $this->gateway->unsetCountKeys($email); + + // Removing un-required keys (ldap return array with count and 0). + if (!empty($email[0]['mail'][0])) { + $result = $email[0]['mail'][0]; + } + + return $result; + } + /** * @param $task * @return bool @@ -155,11 +149,11 @@ class Reminder implements EndpointInterface { $result = FALSE; - // Search the DN for supannRessourceState + // Search the DN for supannResourceState $supannResources = $this->retrieveSupannResources($dn); if ($this->verifySupannState($monitoredResources, $supannResources)) { // verify the date - $DnSupannDateObject = $this->retrieveDateFromSupannResouceState($supannResources['supannressourceetatdate'][0]); + $DnSupannDateObject = $this->retrieveDateFromSupannResourceState($supannResources['supannressourceetatdate'][0]); //Verification if the time is lower or equal than the reminder time. if ($DnSupannDateObject !== FALSE) { @@ -189,7 +183,7 @@ class Reminder implements EndpointInterface // Simply remove key "count" $this->gateway->unsetCountKeys($supannResources); - // Removing unrequired keys + // Removing un-required keys if (!empty($supannResources)) { $supannResources = $supannResources[0]; } @@ -268,7 +262,7 @@ class Reminder implements EndpointInterface * Note : Simply transform string date of supann to a dateTime object. * Can return bool (false) or dateTime object. */ - private function retrieveDateFromSupannResouceState ($supannEtatDate) + private function retrieveDateFromSupannResourceState ($supannEtatDate) { $dateString = NULL; // Simply take the last 8 digit @@ -317,20 +311,27 @@ class Reminder implements EndpointInterface /** * @param array $mainTask + * @param bool $reminded * @return array * Note : Simply generate the email to be sent as reminder. + * Note 2 : The boolean is created to generate the token and is only sent to reminded. Not recipients. */ - private function generateMainTaskMailTemplate (array $mainTask): array + private function generateMainTaskMailTemplate (array $mainTask, bool $reminded): array { // Generate email configuration for each result of subtasks having the same main task. - $recipients = $mainTask[0]["fdtasksreminderlistofrecipientsmails"]; - $this->gateway->unsetCountKeys($recipients); $sender = $mainTask[0]['fdtasksreminderemailsender'][0]; $mailTemplateName = $mainTask[0]['fdtasksremindermailtemplate'][0]; $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); $mailContent = $mailInfos[0]; + if (!$reminded) { + $recipients = $mainTask[0]["fdtasksreminderlistofrecipientsmails"]; + $this->gateway->unsetCountKeys($recipients); + } else { + // reminded with token creation. + } + // Set the reminder array with all required variable for all sub-tasks of same main task origin. $mailForm['setFrom'] = $sender; $mailForm['recipients'] = $recipients; @@ -358,7 +359,7 @@ class Reminder implements EndpointInterface $maxMailsConfig = $fdTasksConf[0]["fdtasksconfmaxemails"][0] ?? 50; /* - Increment var starts a zero and added values will be the humber or recipients per main tasks, as one mail is + Increment var starts a zero and added values will be the number of recipients per main tasks, as one mail is sent per main task. */ $maxMailsIncrement = 0; -- GitLab From 06360271ab75aa9a257c7a5b534f3cd321e0e4ba Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 10 Oct 2024 19:13:21 +0100 Subject: [PATCH 081/111] :sparkles: Feat(Orchestrator) - before reminded mail template 2 save commit - backup + logic to be though on mail separation and status update 2 --- plugins/tasks/Reminder.php | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index b3c852d..a0117f3 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -69,10 +69,11 @@ class Reminder implements EndpointInterface $this->gateway->unsetCountKeys($remindersMainTask); // Generate the mail form with all mail controller requirements - $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, FALSE); + $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, NULL); // Retrieve email attribute for the monitored members requiring reminding. - $mailTemplateMonitoredMember = $this->getEmailFromReminded($task['fdtasksgranulardn'][0]); + $mailOfTheReminded = $this->getEmailFromReminded($task['fdtasksgranulardn'][0]); + $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, $mailOfTheReminded); // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); @@ -91,9 +92,9 @@ class Reminder implements EndpointInterface // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; - $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateRecipients; + $reminders[$remindersMainTaskName]['recipients'] = $mailTemplateRecipients; // Add the reminded email form - $reminders[$remindersMainTaskName]['mailForm'] = $mailTemplateMonitoredMember; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateReminded; } else { // Not about to expire, delete subTask @@ -101,18 +102,13 @@ class Reminder implements EndpointInterface $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; } } - - - // // Here we must have a logic to create the token for the subTask. - // $reminders = $this->completeremindersBody($reminders, $remindersMainTaskName); - // - - } } if (!empty($reminders)) { $result[] = $this->sendRemindersMail($reminders); +// $result[] = $this->sendRemindersMail($reminders, 'mailFormRecipients'); +// $result[] = $this->sendRemindersMail($reminders, 'mailFormReminded'); } return $result; @@ -316,7 +312,7 @@ class Reminder implements EndpointInterface * Note : Simply generate the email to be sent as reminder. * Note 2 : The boolean is created to generate the token and is only sent to reminded. Not recipients. */ - private function generateMainTaskMailTemplate (array $mainTask, bool $reminded): array + private function generateMainTaskMailTemplate (array $mainTask, ?string $remindedEmail): array { // Generate email configuration for each result of subtasks having the same main task. $sender = $mainTask[0]['fdtasksreminderemailsender'][0]; @@ -325,11 +321,12 @@ class Reminder implements EndpointInterface $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); $mailContent = $mailInfos[0]; - if (!$reminded) { + if (empty($remindedEmail)) { $recipients = $mainTask[0]["fdtasksreminderlistofrecipientsmails"]; $this->gateway->unsetCountKeys($recipients); } else { - // reminded with token creation. + // Recipients are processed by send mail and is expecting an array. + $recipients = [$remindedEmail]; } // Set the reminder array with all required variable for all sub-tasks of same main task origin. @@ -379,6 +376,7 @@ class Reminder implements EndpointInterface ); $mailSentResult = $mail_controller->sendMail(); + // Here we incremented as well the counter of spam to the backend. $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); // Verification anti-spam max mails to be sent and quit loop if matched. -- GitLab From 8809fef5df4044d27f818b22f38ea9502a5bcb5d Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 10 Oct 2024 19:16:56 +0100 Subject: [PATCH 082/111] :sparkles: Feat(Orchestrator) - how to send reminded email. save commit - backup + how to send reminded email brainstorm. --- plugins/tasks/Reminder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index a0117f3..7dbb3df 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -73,7 +73,7 @@ class Reminder implements EndpointInterface // Retrieve email attribute for the monitored members requiring reminding. $mailOfTheReminded = $this->getEmailFromReminded($task['fdtasksgranulardn'][0]); - $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, $mailOfTheReminded); + $mailTemplateReminded = $this->generateMainTaskMailTemplate($remindersMainTask, $mailOfTheReminded); // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); @@ -92,6 +92,7 @@ class Reminder implements EndpointInterface // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + // Recipient email form $reminders[$remindersMainTaskName]['recipients'] = $mailTemplateRecipients; // Add the reminded email form $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateReminded; -- GitLab From 6b1fe584af786bd2d9a9c06883d8546b350e2a2b Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 14 Oct 2024 16:19:38 +0100 Subject: [PATCH 083/111] :sparkles: Feat(Orchestrator) - reminded email send to both monit & forward save commit - forward and remind mail sent + update tasks --- plugins/tasks/Reminder.php | 81 +++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 7dbb3df..06a510b 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -68,12 +68,11 @@ class Reminder implements EndpointInterface // remove the count keys $this->gateway->unsetCountKeys($remindersMainTask); - // Generate the mail form with all mail controller requirements - $mailTemplateRecipients = $this->generateMainTaskMailTemplate($remindersMainTask, NULL); - // Retrieve email attribute for the monitored members requiring reminding. $mailOfTheReminded = $this->getEmailFromReminded($task['fdtasksgranulardn'][0]); - $mailTemplateReminded = $this->generateMainTaskMailTemplate($remindersMainTask, $mailOfTheReminded); + + // Generate the mail form with all mail controller requirements + $mailTemplateForm = $this->generateMainTaskMailTemplate($remindersMainTask, $mailOfTheReminded); // Get monitored resources $monitoredResources = $this->getMonitoredResources($remindersMainTask[0]); @@ -93,9 +92,7 @@ class Reminder implements EndpointInterface $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; // Recipient email form - $reminders[$remindersMainTaskName]['recipients'] = $mailTemplateRecipients; - // Add the reminded email form - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateReminded; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateForm; } else { // Not about to expire, delete subTask @@ -108,8 +105,6 @@ class Reminder implements EndpointInterface if (!empty($reminders)) { $result[] = $this->sendRemindersMail($reminders); -// $result[] = $this->sendRemindersMail($reminders, 'mailFormRecipients'); -// $result[] = $this->sendRemindersMail($reminders, 'mailFormReminded'); } return $result; @@ -308,12 +303,12 @@ class Reminder implements EndpointInterface /** * @param array $mainTask - * @param bool $reminded + * @param string $remindedEmail * @return array * Note : Simply generate the email to be sent as reminder. * Note 2 : The boolean is created to generate the token and is only sent to reminded. Not recipients. */ - private function generateMainTaskMailTemplate (array $mainTask, ?string $remindedEmail): array + private function generateMainTaskMailTemplate (array $mainTask, string $remindedEmail): array { // Generate email configuration for each result of subtasks having the same main task. $sender = $mainTask[0]['fdtasksreminderemailsender'][0]; @@ -322,14 +317,19 @@ class Reminder implements EndpointInterface $mailInfos = $this->gateway->getLdapTasks("(|(objectClass=fdMailTemplate)(objectClass=fdMailAttachments))", [], $mailTemplateName); $mailContent = $mailInfos[0]; - if (empty($remindedEmail)) { - $recipients = $mainTask[0]["fdtasksreminderlistofrecipientsmails"]; + // If no forward-to mail recipients is set, simply send the reminder to the monitored members. + if (!empty($mainTask[0]["fdtasksreminderlistofrecipientsmails"])) { + $recipients = array_merge($mainTask[0]["fdtasksreminderlistofrecipientsmails"], [$remindedEmail]); $this->gateway->unsetCountKeys($recipients); + + // There is no reason to send an email twice to the same person. Render the array unique. + $recipients = array_unique($recipients); } else { - // Recipients are processed by send mail and is expecting an array. - $recipients = [$remindedEmail]; + $recipients = $remindedEmail; } + // Render the array unique. + // Set the reminder array with all required variable for all sub-tasks of same main task origin. $mailForm['setFrom'] = $sender; $mailForm['recipients'] = $recipients; @@ -362,28 +362,35 @@ class Reminder implements EndpointInterface */ $maxMailsIncrement = 0; - foreach ($reminders 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(); - // Here we incremented as well the counter of spam to the backend. - $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $data, $fdTasksConf); - - // Verification anti-spam max mails to be sent and quit loop if matched. - $maxMailsIncrement += $numberOfRecipients; - if ($maxMailsIncrement == $maxMailsConfig) { - break; + // Each reminders + foreach ($reminders as $reminder) { + // Each main task reminder + foreach ($reminder as $reminderItem) { + // Each subTask reminder + foreach($reminderItem as $mailDetails) { + $numberOfRecipients = count($mailDetails['mail']['recipients']); + + $mail_controller = new \FusionDirectory\Mail\MailLib( + $mailDetails['mail']['setFrom'], + NULL, + $mailDetails['mail']['recipients'], + $mailDetails['mail']['body'], + $mailDetails['mail']['signature'], + $mailDetails['mail']['subject'], + $mailDetails['mail']['receipt'], + NULL + ); + + $mailSentResult = $mail_controller->sendMail(); + // Here we incremented as well the counter of spam to the backend. + $result[] = $this->processMailResponseAndUpdateTasks($mailSentResult, $reminder, $fdTasksConf); + + // Verification anti-spam max mails to be sent and quit loop if matched. + $maxMailsIncrement += $numberOfRecipients; + if ($maxMailsIncrement == $maxMailsConfig) { + break; + } + } } } -- GitLab From dac6bab22c8d0029e3f917bee4c3f180b1cd163e Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 14 Oct 2024 16:41:54 +0100 Subject: [PATCH 084/111] :sparkles: Feat(Orchestrator) - second reminder properly sent save commit - second reminder properly sent --- plugins/tasks/Reminder.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 06a510b..2e47d69 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -100,6 +100,23 @@ class Reminder implements EndpointInterface $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; } } + + // Case where supann is set and prolongation is desired. + if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'TRUE') { + if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { + + // Require to be set for updating the status of the task later on and sent the email. + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + // Recipient email form + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateForm; + + } else { + // Not about to expire, delete subTask + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; + } + } } } -- GitLab From ee575f34db0eaf511f7f56c96be5bc406e37210c Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 21 Oct 2024 12:08:35 +0100 Subject: [PATCH 085/111] :sparkles: Feat(Orchestrator) - token generation logic 1 save commit - token generation logic 1 --- plugins/tasks/Reminder.php | 102 +++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 4 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 2e47d69..ff02253 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -108,8 +108,18 @@ class Reminder implements EndpointInterface // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + + // Create timeStamp expiration for token + $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0], + $remindersMainTask['fdtasksreminderfirstcall'][0], + $remindersMainTask['fdtasksremindersecondcall'][0]); + // Create token for SubTask + $token = $this->generateToken($task['fdtasksgranulardn'][0], $timeStamp); + // Edit the mailForm with the url link containing the token + $tokenMailTemplateForm = $this->includeTokenToMailForm($token, $mailTemplateForm); // Recipient email form - $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $mailTemplateForm; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm; + } else { // Not about to expire, delete subTask @@ -127,6 +137,91 @@ class Reminder implements EndpointInterface return $result; } + private function includeTokenToMailForm($token, $mailTemplateForm) : array + { + + } + + /** + * @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 $uid + * @return array + */ + private function generateToken (string $uid, int $timeStamp): array + { + // Salt has been generated with APG. + $salt = '8onOlEsItKond'; + $payload = json_encode($uid . $salt); + + // Create hmac with sha256 alg and the key provided for JWT token signature in ENV. + $token_hmac = hash_hmac("sha256", $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($uid, $token, $timeStamp); + + exit; + } + + /** + * @param string $uid + * @param string $token + * NOTE : UID is the full DN of the user. (uid=...). + * @param int $timeStamp + */ + private function saveTokenInLdap (string $uid, string $token, int $timeStamp) + { + $result = []; + + // Status subject to change + $ldap_entry["fdTokenUserDN"] = $uid; + $ldap_entry["fdTokenType"] = 'reminder'; + $ldap_entry["fdToken"] = $token; + $ldap_entry["fdTokenTimestamp"] = $timeStamp; + + // Add status to LDAP + try { + $result = ldap_modify($this->gateway->ds, 'ou=fdTokenEntry'.$_ENV["LDAP_BASE"], $ldap_entry); // bool returned + } catch (Exception $e) { + $result = json_encode(["Ldap Error" => "$e"]); // string returned + } + + 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(["+", "/", "="], ["-", "_", ""], base64_encode($text)); + } + /** * @param string $dn * @return string @@ -204,7 +299,6 @@ class Reminder implements EndpointInterface /** * Get the monitored resources for reminder to be activated. - * * @param array $remindersMainTask * @return array */ @@ -315,7 +409,7 @@ class Reminder implements EndpointInterface 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', 'fdTasksReminderPPolicy', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', - 'fdTasksReminderNextState', 'fdTasksReminderNextSubState', 'fdTasksReminderSubState'], '', $mainTaskDn); + 'fdTasksReminderNextState', 'fdTasksReminderNextSubState', 'fdTasksReminderSubState', 'fdTasksReminderFirstCall', 'fdTasksReminderSecondCall'], '', $mainTaskDn); } /** @@ -384,7 +478,7 @@ class Reminder implements EndpointInterface // Each main task reminder foreach ($reminder as $reminderItem) { // Each subTask reminder - foreach($reminderItem as $mailDetails) { + foreach ($reminderItem as $mailDetails) { $numberOfRecipients = count($mailDetails['mail']['recipients']); $mail_controller = new \FusionDirectory\Mail\MailLib( -- GitLab From ca0f4ee5fbc9f35bc4ffb1d9fec58d01733dacb8 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 22 Oct 2024 10:58:53 +0100 Subject: [PATCH 086/111] :sparkles: Feat(Orchestrator) - token generation logic 2 save commit - token generation logic 2 --- plugins/tasks/Reminder.php | 136 +++++++++++++++++++++++++++++++------ 1 file changed, 116 insertions(+), 20 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index ff02253..73d0b2c 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -111,10 +111,10 @@ class Reminder implements EndpointInterface // Create timeStamp expiration for token $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0], - $remindersMainTask['fdtasksreminderfirstcall'][0], - $remindersMainTask['fdtasksremindersecondcall'][0]); + $remindersMainTask[0]['fdtasksreminderfirstcall'][0], + $remindersMainTask[0]['fdtasksremindersecondcall'][0]); // Create token for SubTask - $token = $this->generateToken($task['fdtasksgranulardn'][0], $timeStamp); + $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire); // Edit the mailForm with the url link containing the token $tokenMailTemplateForm = $this->includeTokenToMailForm($token, $mailTemplateForm); // Recipient email form @@ -137,7 +137,7 @@ class Reminder implements EndpointInterface return $result; } - private function includeTokenToMailForm($token, $mailTemplateForm) : array + private function includeTokenToMailForm ($token, $mailTemplateForm): array { } @@ -149,7 +149,7 @@ class Reminder implements EndpointInterface * @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 + private function getTokenExpiration (int $subTaskCall, int $firstCall, int $secondCall): int { // if firstCall is empty, secondCall is the timestamp expiry for the token. $result = $secondCall; @@ -165,25 +165,30 @@ class Reminder implements EndpointInterface } /** - * @param string $uid - * @return array + * @param string $userDN + * @param int $timeStamp + * @return string + * @throws Exception */ - private function generateToken (string $uid, int $timeStamp): array + private function generateToken (string $userDN, int $timeStamp): string { + $token = NULL; // Salt has been generated with APG. $salt = '8onOlEsItKond'; - $payload = json_encode($uid . $salt); + $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", $payload, $_ENV["SECRET_KEY"], TRUE); + $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($uid, $token, $timeStamp); + $this->saveTokenInLdap($userDN, $token, $timeStamp); - exit; + return $token; } /** @@ -191,22 +196,113 @@ class Reminder implements EndpointInterface * @param string $token * NOTE : UID is the full DN of the user. (uid=...). * @param int $timeStamp + * @return bool + * @throws Exception */ - private function saveTokenInLdap (string $uid, string $token, int $timeStamp) + private function saveTokenInLdap (string $userDN, string $token, int $timeStamp): bool { - $result = []; + $result = FALSE; + + preg_match('/uid=([^,]+),ou=/', $userDN, $matches); + $uid = $matches[1]; + $dn = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"]; - // Status subject to change - $ldap_entry["fdTokenUserDN"] = $uid; + $ldap_entry["objectClass"] = ['top', 'fdTokenEntry']; + $ldap_entry["fdTokenUserDN"] = $userDN; $ldap_entry["fdTokenType"] = 'reminder'; $ldap_entry["fdToken"] = $token; $ldap_entry["fdTokenTimestamp"] = $timeStamp; + $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; - // Add status to LDAP try { - $result = ldap_modify($this->gateway->ds, 'ou=fdTokenEntry'.$_ENV["LDAP_BASE"], $ldap_entry); // bool returned + $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 = json_encode(["Ldap Error" => "$e"]); // string returned + $result = FALSE; } return $result; @@ -405,7 +501,7 @@ class Reminder implements EndpointInterface public function getRemindersMainTask (string $mainTaskDn): array { // Retrieve data from the main Reminder task - return $this->gateway->getLdapTasks( '(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', + return $this->gateway->getLdapTasks( '(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', 'fdTasksReminderPPolicy', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', -- GitLab From 93a087d490729cacdb12f4912cde48f6be5cbf16 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 22 Oct 2024 11:36:11 +0100 Subject: [PATCH 087/111] :sparkles: Feat(Orchestrator) - url is being sent properly save commit - url is being sent properly. --- plugins/tasks/Reminder.php | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 73d0b2c..e1007f6 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -63,7 +63,7 @@ class Reminder implements EndpointInterface if ($this->gateway->statusAndScheduleCheck($task)) { // Retrieve data from the main task - $remindersMainTaskName = $task['fdtasksgranularmaster'][0]; + $remindersMainTaskName = $task['fdtasksgranularmaster'][0]; //dn $remindersMainTask = $this->getRemindersMainTask($remindersMainTaskName); // remove the count keys $this->gateway->unsetCountKeys($remindersMainTask); @@ -116,7 +116,7 @@ class Reminder implements EndpointInterface // Create token for SubTask $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire); // Edit the mailForm with the url link containing the token - $tokenMailTemplateForm = $this->includeTokenToMailForm($token, $mailTemplateForm); + $tokenMailTemplateForm = $this->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName); // Recipient email form $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm; @@ -137,9 +137,25 @@ class Reminder implements EndpointInterface return $result; } - private function includeTokenToMailForm ($token, $mailTemplateForm): array + /** + * @param string $token + * @param array $mailTemplateForm + * @param string $taskName + * @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 . '/account_prolongation.php?token=' . $token . '&task=' . $taskName; + + $mailTemplateForm['body'] .= $url; + + return $mailTemplateForm; } /** @@ -177,10 +193,10 @@ class Reminder implements EndpointInterface $salt = '8onOlEsItKond'; $payload = json_encode($userDN . $salt); // This allows the token to be different every time. - $time = 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); + $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); @@ -192,7 +208,7 @@ class Reminder implements EndpointInterface } /** - * @param string $uid + * @param string $userDN * @param string $token * NOTE : UID is the full DN of the user. (uid=...). * @param int $timeStamp @@ -205,14 +221,14 @@ class Reminder implements EndpointInterface preg_match('/uid=([^,]+),ou=/', $userDN, $matches); $uid = $matches[1]; - $dn = 'cn=' . $uid . ',' . 'ou=tokens' . ',' . $_ENV["LDAP_BASE"]; + $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"] = $timeStamp; - $ldap_entry["cn"] = $uid; + $ldap_entry["cn"] = $uid; // set the dn for the token, only take what's between "uid=" and ",ou=" @@ -224,7 +240,7 @@ class Reminder implements EndpointInterface } // The user token DN creation - $userTokenDN = 'cn='.$uid.',ou=tokens' . ',' . $_ENV["LDAP_BASE"]; + $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 @@ -247,7 +263,7 @@ class Reminder implements EndpointInterface * @return void * Note : Simply remove the token for specific user DN */ - private function removeUserToken($userTokenDN): void + private function removeUserToken ($userTokenDN): void { // Add token to LDAP for specific UID try { -- GitLab From 52938ac0a71d71197d852828a67c06dafc55dadd Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 22 Oct 2024 15:42:45 +0100 Subject: [PATCH 088/111] :sparkles: Feat(Orchestrator) - url is being sent properly save commit - change in url. --- plugins/tasks/Reminder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index e1007f6..6ee94f6 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -151,7 +151,7 @@ class Reminder implements EndpointInterface // Remove the API URI $cleanedUrl = preg_replace('#/rest\.php/v1$#', '', $_ENV['FUSION_DIRECTORY_API_URL']); - $url = $cleanedUrl . '/account_prolongation.php?token=' . $token . '&task=' . $taskName; + $url = $cleanedUrl . '/accountProlongation.php?token=' . $token . '&task=' . $taskName; $mailTemplateForm['body'] .= $url; -- GitLab From 24b9905929619dc07507bde675ddbd21b6d25620 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 22 Oct 2024 18:03:06 +0100 Subject: [PATCH 089/111] :sparkles: Feat(Orchestrator) - url is being sent properly save commit - change of the token generation. --- plugins/tasks/Reminder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 6ee94f6..e25c8d8 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -331,7 +331,7 @@ class Reminder implements EndpointInterface */ private function base64urlEncode (string $text): string { - return str_replace(["+", "/", "="], ["-", "_", ""], base64_encode($text)); + return str_replace(["+", "/", "="], ["A", "B", ""], base64_encode($text)); } /** -- GitLab From 52c35affb0983a95a525f83d8ec0ad59d0bc3ee6 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 22 Oct 2024 18:11:34 +0100 Subject: [PATCH 090/111] :sparkles: Feat(Orchestrator) - url is being sent properly save commit - timetamp to unix. --- plugins/tasks/Reminder.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index e25c8d8..5bb7406 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -215,10 +215,14 @@ class Reminder implements EndpointInterface * @return bool * @throws Exception */ - private function saveTokenInLdap (string $userDN, string $token, int $timeStamp): bool + 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"]; @@ -227,7 +231,7 @@ class Reminder implements EndpointInterface $ldap_entry["fdTokenUserDN"] = $userDN; $ldap_entry["fdTokenType"] = 'reminder'; $ldap_entry["fdToken"] = $token; - $ldap_entry["fdTokenTimestamp"] = $timeStamp; + $ldap_entry["fdTokenTimestamp"] = $futureTimestamp; $ldap_entry["cn"] = $uid; // set the dn for the token, only take what's between "uid=" and ",ou=" -- GitLab From 3f259ae4c6b3d32b7c84bdde34f600ef7f29ad35 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 29 Oct 2024 15:20:51 +0000 Subject: [PATCH 091/111] :sparkles: Feat(Orchestrator) - posix shadowExpire verifications Adds verification for posix shadowExpire. --- plugins/tasks/Reminder.php | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 5bb7406..d1c93e0 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -121,6 +121,34 @@ class Reminder implements EndpointInterface $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm; + } else { + // Not about to expire, delete subTask + $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); + $result[$task['dn']]['Status'] = 'No reminder triggers were found, therefore removing the sub-task!'; + } + } + + // Case where prolongation is set without supann. Case of posix and PPolicy + if ($monitoredResources['resource'][0] === 'NONE' && $monitoredResources['prolongation'] === 'TRUE') { + if ($this->posixAboutToExpire($task['fdtasksgranulardn'][0], $task['fdtasksgranularhelper'][0]) || + $this->pPolicyAboutToExpire($task['fdtasksgranulardn'][0], $task['fdtasksgranularhelper'][0])) { + + // Require to be set for updating the status of the task later on and sent the email. + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; + + // Create timeStamp expiration for token + $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0], + $remindersMainTask[0]['fdtasksreminderfirstcall'][0], + $remindersMainTask[0]['fdtasksremindersecondcall'][0]); + // Create token for SubTask + $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire); + // Edit the mailForm with the url link containing the token + $tokenMailTemplateForm = $this->generateTokenUrl($token, $mailTemplateForm, $remindersMainTaskName); + // Recipient email form + $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['mail'] = $tokenMailTemplateForm; + + } else { // Not about to expire, delete subTask $result[$task['dn']]['Removed'] = $this->gateway->removeSubTask($task['dn']); @@ -137,6 +165,67 @@ class Reminder implements EndpointInterface return $result; } + /** + * @param $dn + * @param $days + * @return bool + * Note : Compare the date of today and the shadowExpire epoch to see if expiration is soon to happen. + */ + private function posixAboutToExpire ($dn, $days) : bool + { + $result = FALSE; + + $userShadowExpire = $this->retrieveUserPosix($dn); + // Verification if shadowExpire was retrieved + if (!empty($userShadowExpire)) { + // Create the date of today + $today = new DateTime(); + // Create a proper timestamp for verification + $epoch = new DateTime("1970-01-01"); + // Add the shadowExpire days to the epoch + $epoch->add(new DateInterval("P{$userShadowExpire}D")); + + // Get the interval between today and the expiration of shadow expire. + $interval = $today->diff($epoch); + + // Interval can be negative if date is in the past - we make sure it is not in the past by using invert. + if ($interval->days <= $days && $interval->invert == 0) { + $result = TRUE; + } + } + + return $result; + } + + /** + * @param $dn + * @return string + * Note : Simply retrieve shadowExpire attribute for the DN specified. + */ + private function retrieveUserPosix ($dn) : string + { + $result = ''; + $userPosix = $this->gateway->getLdapTasks('(objectClass=shadowAccount)', ['shadowExpire'], + '', $dn); + + // Simply remove key "count" + $this->gateway->unsetCountKeys($userPosix); + + // Removing un-required keys + if (!empty($userPosix[0]['shadowexpire'][0])) { + $result = $userPosix[0]['shadowexpire'][0]; + } + + return $result; + } + + private function pPolicyAboutToExpire ($dn, $days) : bool + { + $result = FALSE; + + return $result; + } + /** * @param string $token * @param array $mailTemplateForm -- GitLab From ff0bb46c36d1a08272f92b0e59ad4fd71fac552c Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 29 Oct 2024 17:38:29 +0000 Subject: [PATCH 092/111] :sparkles: Feat(Orchestrator) - supann issues - we need to iterate through supann We need to iterate through supann - to be done ! --- plugins/tasks/Reminder.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index d1c93e0..7fa691c 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -104,7 +104,7 @@ class Reminder implements EndpointInterface // Case where supann is set and prolongation is desired. if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'TRUE') { if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { - + echo "within supann and prolongation"; // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; @@ -463,7 +463,6 @@ class Reminder implements EndpointInterface if ($this->verifySupannState($monitoredResources, $supannResources)) { // verify the date $DnSupannDateObject = $this->retrieveDateFromSupannResourceState($supannResources['supannressourceetatdate'][0]); - //Verification if the time is lower or equal than the reminder time. if ($DnSupannDateObject !== FALSE) { $today = new DateTime(); @@ -547,6 +546,9 @@ class Reminder implements EndpointInterface private function verifySupannState (array $reminderSupann, array $dnSupann): bool { $result = FALSE; + print_r($reminderSupann); + print_r($dnSupann); + //Construct the reminder Supann Resource State as string if (!empty($reminderSupann['subState'][0])) { @@ -555,6 +557,7 @@ class Reminder implements EndpointInterface $monitoredSupannState = '{' . $reminderSupann['resource'][0] . '}' . $reminderSupann['state'][0]; } + //TODO here we have an error, we have to iterate on the list of supannRessource and not taking 0 as default key !! if (!empty($dnSupann['supannressourceetat'][0]) && $dnSupann['supannressourceetat'][0] === $monitoredSupannState) { -- GitLab From e8e8dcce3cff515025991bc792fbd816ae589bce Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 31 Oct 2024 16:38:58 +0000 Subject: [PATCH 093/111] :sparkles: Feat(Orchestrator) - supann array corrections Adds supann array corrections --- plugins/tasks/Reminder.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 7fa691c..2b31d83 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -546,9 +546,6 @@ class Reminder implements EndpointInterface private function verifySupannState (array $reminderSupann, array $dnSupann): bool { $result = FALSE; - print_r($reminderSupann); - print_r($dnSupann); - //Construct the reminder Supann Resource State as string if (!empty($reminderSupann['subState'][0])) { @@ -557,11 +554,14 @@ class Reminder implements EndpointInterface $monitoredSupannState = '{' . $reminderSupann['resource'][0] . '}' . $reminderSupann['state'][0]; } - //TODO here we have an error, we have to iterate on the list of supannRessource and not taking 0 as default key !! - - if (!empty($dnSupann['supannressourceetat'][0]) && $dnSupann['supannressourceetat'][0] === $monitoredSupannState) { - - $result = TRUE; + if (!empty($dnSupann['supannressourceetat'])) { + // Simply iterate within the resource available till a match is found. + foreach($dnSupann['supannressourceetat'] as $resource) { + if ($monitoredSupannState === $resource) { + $result = TRUE; + break; + } + } } return $result; -- GitLab From e83f6afd5a473f011b39bd008797e8e0ff9fd018 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 31 Oct 2024 17:30:30 +0000 Subject: [PATCH 094/111] :sparkles: Feat(Orchestrator) - phpcs phpcs --- plugins/tasks/Reminder.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 2b31d83..b945bfb 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -508,7 +508,6 @@ class Reminder implements EndpointInterface */ private function getMonitoredResources (array $remindersMainTask): array { - $monitoredResourcesArray = [ 'resource' => $remindersMainTask['fdtasksreminderresource'], 'state' => $remindersMainTask['fdtasksreminderstate'], @@ -556,7 +555,7 @@ class Reminder implements EndpointInterface if (!empty($dnSupann['supannressourceetat'])) { // Simply iterate within the resource available till a match is found. - foreach($dnSupann['supannressourceetat'] as $resource) { + foreach ($dnSupann['supannressourceetat'] as $resource) { if ($monitoredSupannState === $resource) { $result = TRUE; break; -- GitLab From 80e1a0823d9ce506d0f19f5afcd5034e7001baba Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 4 Nov 2024 10:53:10 +0000 Subject: [PATCH 095/111] :sparkles: Feat(Orchestrator) - phpstan fixes phpstan fixes --- plugins/tasks/Reminder.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index b945bfb..ddf1758 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -48,7 +48,7 @@ class Reminder implements EndpointInterface } /** - * @param array $notificationsSubTasks + * @param array $reminderSubTasks * @return array * @throws Exception */ @@ -104,7 +104,6 @@ class Reminder implements EndpointInterface // Case where supann is set and prolongation is desired. if ($monitoredResources['resource'][0] !== 'NONE' && $monitoredResources['prolongation'] === 'TRUE') { if ($this->supannAboutToExpire($task['fdtasksgranulardn'][0], $monitoredResources, $task['fdtasksgranularhelper'][0])) { - echo "within supann and prolongation"; // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['uid'] = $task['fdtasksgranulardn'][0]; @@ -229,7 +228,7 @@ class Reminder implements EndpointInterface /** * @param string $token * @param array $mailTemplateForm - * @param string $taskName + * @param string $taskDN * @return array */ private function generateTokenUrl (string $token, array $mailTemplateForm, string $taskDN): array @@ -300,7 +299,7 @@ class Reminder implements EndpointInterface * @param string $userDN * @param string $token * NOTE : UID is the full DN of the user. (uid=...). - * @param int $timeStamp + * @param int $days * @return bool * @throws Exception */ @@ -537,8 +536,8 @@ class Reminder implements EndpointInterface /** - * @param array $supannResource - * @param array $auditedAttrs + * @param array $reminderSupann + * @param array $dnSupann * @return bool * Note : Create the supann format and check for a match. */ -- GitLab From 02864376a5e3d5097a5a1f9358dd4f36ab02c248 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 4 Nov 2024 17:24:52 +0000 Subject: [PATCH 096/111] :sparkles: Feat(Orchestrator) - removal of ppolicy Removal of ppolicy --- plugins/tasks/Reminder.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index ddf1758..0d42fac 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -127,10 +127,9 @@ class Reminder implements EndpointInterface } } - // Case where prolongation is set without supann. Case of posix and PPolicy + // Case where prolongation is set without supann. if ($monitoredResources['resource'][0] === 'NONE' && $monitoredResources['prolongation'] === 'TRUE') { - if ($this->posixAboutToExpire($task['fdtasksgranulardn'][0], $task['fdtasksgranularhelper'][0]) || - $this->pPolicyAboutToExpire($task['fdtasksgranulardn'][0], $task['fdtasksgranularhelper'][0])) { + if ($this->posixAboutToExpire($task['fdtasksgranulardn'][0], $task['fdtasksgranularhelper'][0])) { // Require to be set for updating the status of the task later on and sent the email. $reminders[$remindersMainTaskName]['subTask'][$task['cn'][0]]['dn'] = $task['dn']; @@ -218,13 +217,6 @@ class Reminder implements EndpointInterface return $result; } - private function pPolicyAboutToExpire ($dn, $days) : bool - { - $result = FALSE; - - return $result; - } - /** * @param string $token * @param array $mailTemplateForm @@ -522,9 +514,8 @@ class Reminder implements EndpointInterface $monitoredResourcesArray['nextState'] = $remindersMainTask['fdtasksremindernextstate']; $monitoredResourcesArray['nextSubState'] = $remindersMainTask['fdtasksremindernextsubstate'] ?? NULL; } - + // Posix attributes $monitoredResourcesArray['fdTasksReminderPosix'] = $remindersMainTask['fdtasksreminderposix'] ?? FALSE; - $monitoredResourcesArray['fdTasksReminderPPolicy'] = $remindersMainTask['fdtasksreminderppolicy'] ?? FALSE; } @@ -613,7 +604,7 @@ class Reminder implements EndpointInterface // Retrieve data from the main Reminder task return $this->gateway->getLdapTasks( '(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', - 'fdTasksReminderPPolicy', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', + 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', 'fdTasksReminderNextState', 'fdTasksReminderNextSubState', 'fdTasksReminderSubState', 'fdTasksReminderFirstCall', 'fdTasksReminderSecondCall'], '', $mainTaskDn); } -- GitLab From 532cf7b629cf1f077206569f2b61c76b209a9a3f Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 7 Nov 2024 09:29:46 +0000 Subject: [PATCH 097/111] :sparkles: Feat(Orchestrator) - lifecycle supann check logic Changes supann check logic --- plugins/tasks/LifeCycle.php | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index c548c08..d2903ae 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -138,31 +138,35 @@ class LifeCycle implements EndpointInterface if (empty($currentUserLifeCycle[0]['supannressourceetatdate'][0])) { return FALSE; } - // Perform the regular expression match - preg_match($pattern, $currentUserLifeCycle[0]['supannressourceetatdate'][0], $matches); - - // Extracting values of current user - $userSupann['Resource'] = $matches[1] ?? ''; - $userSupann['State'] = $matches[2] ?? ''; - $userSupann['SubState'] = $matches[3] ?? ''; - // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. - $userSupann['EndDate'] = $matches[5] ?? ''; // Extracting values of desired pre-state behavior $preStateSupann['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepreresource'][0]; $preStateSupann['State'] = $lifeCycleBehavior[0]['fdtaskslifecycleprestate'][0]; $preStateSupann['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepresubstate'][0] ?? ''; //SubState is optional - // Verifying if the user end date for selected resource is overdue - if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { - // Comparing value in a nesting conditions - if ($userSupann['Resource'] == $preStateSupann['Resource']) { - if ($userSupann['State'] == $preStateSupann['State']) { - // as SubState is optional, if both resource and state match at this point, modification is allowed. - if (empty($preStateSupann['SubState'])) { - $result = TRUE; - } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { - $result = TRUE; + // Iteration of all potential existing supann states of the user in order to find a match + foreach ($currentUserLifeCycle[0]['supannressourceetatdate'] as $resource) { + // Perform the regular expression match + preg_match($pattern, $resource, $matches); + + // Extracting values of current user + $userSupann['Resource'] = $matches[1] ?? ''; + $userSupann['State'] = $matches[2] ?? ''; + $userSupann['SubState'] = $matches[3] ?? ''; + // Array index 4 is skipped, we only use end date to apply our life cycle logic. Start date has no use here. + $userSupann['EndDate'] = $matches[5] ?? ''; + + // Verifying if the user end date for selected resource is overdue + if (!empty($userSupann['EndDate']) && strtotime($userSupann['EndDate']) <= time()) { + // Comparing value in a nesting conditions + if ($userSupann['Resource'] == $preStateSupann['Resource']) { + if ($userSupann['State'] == $preStateSupann['State']) { + // as SubState is optional, if both resource and state match at this point, modification is allowed. + if (empty($preStateSupann['SubState'])) { + $result = TRUE; + } else if ($preStateSupann['SubState'] == $userSupann['SubState']) { + $result = TRUE; + } } } } -- GitLab From b63555564cf8338b27a3900c6fa24608916f7bc3 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 7 Nov 2024 10:48:54 +0000 Subject: [PATCH 098/111] :sparkles: Feat(Orchestrator) - lifecycle small refactor Adds refactoring and comments --- plugins/tasks/LifeCycle.php | 84 ++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index d2903ae..7657715 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -70,20 +70,17 @@ class LifeCycle implements EndpointInterface if ($this->gateway->statusAndScheduleCheck($task)) { // Simply retrieve the lifeCycle behavior from the main related tasks, sending the dns and desired attributes - $lifeCycleBehavior = $this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', - 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', - 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], - '', $task['fdtasksgranularmaster'][0]); + $lifeCycleBehavior = $this->getLifeCycleBehaviorFromMainTask($task['fdtasksgranularmaster'][0]); // Simply retrieve the current supannStatus of the user DN related to the task at hand. - $currentUserLifeCycle = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], - '', $task['fdtasksgranulardn'][0]); + $currentUserLifeCycle = $this->getUserSupannHistory($task['fdtasksgranulardn'][0]); // Compare both the required schedule and the current user status - returning TRUE if modification is required. if ($this->isLifeCycleRequiringModification($lifeCycleBehavior, $currentUserLifeCycle)) { // This will call a method to modify the ressourcesSupannEtatDate of the DN linked to the subTask - $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0]); + $lifeCycleResult = $this->updateLifeCycle($lifeCycleBehavior, $task['fdtasksgranulardn'][0], $currentUserLifeCycle); + if ($lifeCycleResult === TRUE) { $result[$task['dn']]['results'] = json_encode("Account states have been successfully modified for " . $task['fdtasksgranulardn'][0]); @@ -178,10 +175,19 @@ class LifeCycle implements EndpointInterface /** * @param array $lifeCycleBehavior * @param string $userDN + * @param array $currentUserLifeCycle * @return bool|string + * Note receive the required behavior and the previous list of supann state to update in LDAP. */ - protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN) + protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN, array $currentUserLifeCycle) { + // Only keep the supann state from the received array and removing the count key + $userStateHistory = $currentUserLifeCycle[0]['supannressourceetatdate']; + $this->gateway->unsetCountKeys($userStateHistory); + + // Hosting the final entry of supann attributes to be pushed to LDAP + $ldapEntry = []; + // Extracting values of desired post-state behavior $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; @@ -195,12 +201,25 @@ class LifeCycle implements EndpointInterface $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); // Prepare the ldap entry to be modified - $ldapEntry = []; - $ldapEntry['supannRessourceEtatDate'] = "{" . $newEntry['Resource'] . "}" - . $newEntry['State'] . ":" - . $newEntry['SubState'] . ":" . - $currentDate->format('Ymd') . ":" - . $newEndDate->format('Ymd'); + $newEntry = "{" . $newEntry['Resource'] . "}" . $newEntry['State'] . ":" . $newEntry['SubState'] . ":" + . $currentDate->format('Ymd') . ":" . $newEndDate->format('Ymd'); + // Used to compare if the resource exists in history + $newResource = $this->returnSupannResourceBetweenBrackets($newEntry); + + // Iterate through the supann state and update the array of new entry while keeping history untouched + foreach ($userStateHistory as $userState => $value) { + // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate + $currentResource = $this->returnSupannResourceBetweenBrackets($value); + + // If resources matches, replace the resource with the new one. + if ($currentResource === $newResource) { + $userStateHistory[$userState] = $newEntry; + break; + } + } + + // Creation of the ldap entry + $ldapEntry['supannRessourceEtatDate'] = $userStateHistory; try { $result = ldap_modify($this->gateway->ds, $userDN, $ldapEntry); @@ -211,4 +230,39 @@ class LifeCycle implements EndpointInterface return $result; } -} + /** + * @param string $supannRessourceEtatDate + * @return string|null + * Note : Simple method to return the content between {} of a supannRessourceEtatDate. + */ + private function returnSupannResourceBetweenBrackets(string $supannRessourceEtatDate) : ?string + { + preg_match('/\{(.*?)\}/', $supannRessourceEtatDate, $matches); + return $matches[1] ?? null; + } + + /** + * @param string $taskDN + * @return array + * Note : Simply return attributes from main task, here supann desired behavior + */ + private function getLifeCycleBehaviorFromMainTask (string $taskDN) : array + { + return ($this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', + 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', + 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], + '', $taskDN)); + } + + /** + * @param $userDN + * @return array + * Note : simply return the current values of supannRessourceEtatDate of the specified user. + */ + private function getUserSupannHistory ($userDN) : array + { + return $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], + '', $userDN); + } + +} \ No newline at end of file -- GitLab From 0c88be1fad0d569ad4015ebd64edcb0cc4599119 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 7 Nov 2024 10:56:33 +0000 Subject: [PATCH 099/111] :sparkles: Feat(Orchestrator) - phpcs phpcs --- plugins/tasks/LifeCycle.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index 7657715..e0a05ac 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -235,10 +235,10 @@ class LifeCycle implements EndpointInterface * @return string|null * Note : Simple method to return the content between {} of a supannRessourceEtatDate. */ - private function returnSupannResourceBetweenBrackets(string $supannRessourceEtatDate) : ?string + private function returnSupannResourceBetweenBrackets (string $supannRessourceEtatDate) : ?string { preg_match('/\{(.*?)\}/', $supannRessourceEtatDate, $matches); - return $matches[1] ?? null; + return $matches[1] ?? NULL; } /** @@ -248,10 +248,10 @@ class LifeCycle implements EndpointInterface */ private function getLifeCycleBehaviorFromMainTask (string $taskDN) : array { - return ($this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', + return $this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], - '', $taskDN)); + '', $taskDN); } /** -- GitLab From 52d37304b754669a7dfe68b7bce67c8e7562c92e Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 11 Nov 2024 17:55:03 +0000 Subject: [PATCH 100/111] :sparkles: Feat(Orchestrator) - fixing multi supann status fixing multi supann status. --- plugins/tasks/Reminder.php | 39 +++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 0d42fac..f544efc 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -449,11 +449,14 @@ class Reminder implements EndpointInterface { $result = FALSE; - // Search the DN for supannResourceState + // Search the DN for supannRessourceEtatDate (With DATE) $supannResources = $this->retrieveSupannResources($dn); - if ($this->verifySupannState($monitoredResources, $supannResources)) { - // verify the date - $DnSupannDateObject = $this->retrieveDateFromSupannResourceState($supannResources['supannressourceetatdate'][0]); + // Get the matching resource (without date) + $matchedResource = $this->verifySupannState($monitoredResources, $supannResources); + + if ($matchedResource) { + // verify + $DnSupannDateObject = $this->retrieveDateFromSupannResourceState($supannResources['supannressourceetatdate'], $matchedResource); //Verification if the time is lower or equal than the reminder time. if ($DnSupannDateObject !== FALSE) { $today = new DateTime(); @@ -529,12 +532,13 @@ class Reminder implements EndpointInterface /** * @param array $reminderSupann * @param array $dnSupann - * @return bool + * @return string * Note : Create the supann format and check for a match. */ - private function verifySupannState (array $reminderSupann, array $dnSupann): bool + private function verifySupannState (array $reminderSupann, array $dnSupann): string { - $result = FALSE; + // Result will contain the supann resource matching. + $result = ''; //Construct the reminder Supann Resource State as string if (!empty($reminderSupann['subState'][0])) { @@ -547,7 +551,7 @@ class Reminder implements EndpointInterface // Simply iterate within the resource available till a match is found. foreach ($dnSupann['supannressourceetat'] as $resource) { if ($monitoredSupannState === $resource) { - $result = TRUE; + $result = $resource; break; } } @@ -557,16 +561,29 @@ class Reminder implements EndpointInterface } /** - * @param $supannEtatDate + * @param array $supannEtatDate + * @param string $resource * @return DateTime|false * Note : Simply transform string date of supann to a dateTime object. * Can return bool (false) or dateTime object. */ - private function retrieveDateFromSupannResourceState ($supannEtatDate) + private function retrieveDateFromSupannResourceState (array $supannEtatDate, string $resource) { $dateString = NULL; + $matchFound = null; // Variable to store the match if found + + // Create a regex pattern to match the exact resource at the beginning, followed by ":" or ":::". + $pattern = '/^' . preg_quote($resource, '/') . '(:|:::)?.*/'; + + foreach ($supannEtatDate as $resourceWithDate) { + if (preg_match($pattern, $resourceWithDate)) { + $matchFound = $resourceWithDate; + break; // Stop once a match is found + } + } + // Simply take the last 8 digit - preg_match('/(\d{8})$/', $supannEtatDate, $matches); + preg_match('/(\d{8})$/', $matchFound, $matches); if (!empty($matches)) { $dateString = $matches[0]; -- GitLab From 8bcc7271aa395fdbf108818eec3c1d035789c062 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 11 Nov 2024 21:03:16 +0000 Subject: [PATCH 101/111] :sparkles: Feat(Orchestrator) - fixing phpcs fixing phpcs --- plugins/tasks/Reminder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index f544efc..55fadf1 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -570,7 +570,7 @@ class Reminder implements EndpointInterface private function retrieveDateFromSupannResourceState (array $supannEtatDate, string $resource) { $dateString = NULL; - $matchFound = null; // Variable to store the match if found + $matchFound = NULL; // Variable to store the match if found // Create a regex pattern to match the exact resource at the beginning, followed by ":" or ":::". $pattern = '/^' . preg_quote($resource, '/') . '(:|:::)?.*/'; -- GitLab From 35ef51f2ce0f78afb2c3a3bbde463623248e01be Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 11 Nov 2024 21:12:20 +0000 Subject: [PATCH 102/111] :sparkles: Feat(Orchestrator) - removing debugging Removing debugging --- library/TaskController.php | 1 - plugins/tasks/Mail.php | 1 - 2 files changed, 2 deletions(-) diff --git a/library/TaskController.php b/library/TaskController.php index 64a9325..849f5b6 100644 --- a/library/TaskController.php +++ b/library/TaskController.php @@ -61,7 +61,6 @@ class TaskController break; default: - // TODO: Default result in a non getProcess method found - could enhance below error report. $this->respondMethodAllowed("GET, PATCH, DELETE"); } $this->parseJsonResult($result); diff --git a/plugins/tasks/Mail.php b/plugins/tasks/Mail.php index fe760e4..55ef06e 100644 --- a/plugins/tasks/Mail.php +++ b/plugins/tasks/Mail.php @@ -56,7 +56,6 @@ class Mail implements EndpointInterface public function processMailTasks (array $tasks): array { $result = []; - // DEBUGGING $fdTasksConf = $this->getMailObjectConfiguration(); $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); -- GitLab From 98225cc1a2f96228879d40e4d380541b64494344 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 12 Nov 2024 14:41:57 +0000 Subject: [PATCH 103/111] :sparkles: Feat(Orchestrator) - removing debugging Removing debugging --- plugins/tasks/Reminder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index 55fadf1..ed870da 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -622,7 +622,7 @@ class Reminder implements EndpointInterface return $this->gateway->getLdapTasks( '(objectClass=fdTasksReminder)', ['fdTasksReminderListOfRecipientsMails', 'fdTasksReminderResource', 'fdTasksReminderState', 'fdTasksReminderPosix', 'fdTasksReminderMailTemplate', 'fdTasksReminderSupannNewEndDate', 'fdTasksReminderRecipientsMembers', 'fdTasksReminderEmailSender', - 'fdTasksReminderManager', 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', + 'fdTasksReminderAccountProlongation', 'fdTasksReminderMembers', 'fdTasksReminderNextResource', 'fdTasksReminderNextState', 'fdTasksReminderNextSubState', 'fdTasksReminderSubState', 'fdTasksReminderFirstCall', 'fdTasksReminderSecondCall'], '', $mainTaskDn); } -- GitLab From 03572c0b87e3d32c70482ccfbc2477a51b444d3e Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Tue, 12 Nov 2024 18:10:28 +0000 Subject: [PATCH 104/111] :sparkles: Feat(Orchestrator) - logic of end date Adds better logic of endate but requires refactoring. --- plugins/tasks/LifeCycle.php | 61 ++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index e0a05ac..46e3558 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -181,6 +181,9 @@ class LifeCycle implements EndpointInterface */ protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN, array $currentUserLifeCycle) { + // Will contain the supann resource to be updated + $matchedResource = ''; + // Only keep the supann state from the received array and removing the count key $userStateHistory = $currentUserLifeCycle[0]['supannressourceetatdate']; $this->gateway->unsetCountKeys($userStateHistory); @@ -194,26 +197,38 @@ class LifeCycle implements EndpointInterface $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional - // Require the date of today to update the start of the new resources (If change of status). - $currentDate = new DateTime(); - // Date of today + numbers of days to add for end date. - $newEndDate = new DateTime(); - $newEndDate->modify("+" . $newEntry['EndDate'] . " days"); - // Prepare the ldap entry to be modified - $newEntry = "{" . $newEntry['Resource'] . "}" . $newEntry['State'] . ":" . $newEntry['SubState'] . ":" - . $currentDate->format('Ymd') . ":" . $newEndDate->format('Ymd'); + $newResource = "{" . $newEntry['Resource'] . "}" . $newEntry['State'] . ":" . $newEntry['SubState']; // Used to compare if the resource exists in history - $newResource = $this->returnSupannResourceBetweenBrackets($newEntry); + $newResourceName = $this->returnSupannResourceBetweenBrackets($newResource); + + // Iterate through the supann state and get a match + foreach ($userStateHistory as $value) { + // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate + $currentResource = $this->returnSupannResourceBetweenBrackets($value); - // Iterate through the supann state and update the array of new entry while keeping history untouched + // Get the resource matched + if ($currentResource === $newResourceName) { + $matchedResource = $value; + break; + } + } + + // Fetch the end date of the matched resource. + $currentEndDate = $this->extractCurrentEndDate($matchedResource); + // Create a DateTime object from the string + $currentEndDateObject = DateTime::createFromFormat("Ymd", $currentEndDate); + $currentEndDateObject->modify("+" . $newEntry['EndDate'] . " days"); + $finalRessourceEtatDate = $newResource . ':' . $currentEndDate . ':' . $currentEndDateObject->format('Ymd'); + + // Iterate again through the supann state and get a match foreach ($userStateHistory as $userState => $value) { // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate $currentResource = $this->returnSupannResourceBetweenBrackets($value); - // If resources matches, replace the resource with the new one. - if ($currentResource === $newResource) { - $userStateHistory[$userState] = $newEntry; + // Get the resource matched + if ($currentResource === $newResourceName) { + $userStateHistory[$userState] = $finalRessourceEtatDate; break; } } @@ -230,12 +245,24 @@ class LifeCycle implements EndpointInterface return $result; } + /** + * @param string|null $matchedResource + * @return string + * Note : Simply return the end date of a supann ressource etat date + */ + private function extractCurrentEndDate (?string $matchedResource): string + { + $parts = explode(":", $matchedResource); + // Get the last element, which is the date + return end($parts); + } + /** * @param string $supannRessourceEtatDate * @return string|null * Note : Simple method to return the content between {} of a supannRessourceEtatDate. */ - private function returnSupannResourceBetweenBrackets (string $supannRessourceEtatDate) : ?string + private function returnSupannResourceBetweenBrackets (string $supannRessourceEtatDate): ?string { preg_match('/\{(.*?)\}/', $supannRessourceEtatDate, $matches); return $matches[1] ?? NULL; @@ -246,12 +273,12 @@ class LifeCycle implements EndpointInterface * @return array * Note : Simply return attributes from main task, here supann desired behavior */ - private function getLifeCycleBehaviorFromMainTask (string $taskDN) : array + private function getLifeCycleBehaviorFromMainTask (string $taskDN): array { return $this->gateway->getLdapTasks('(objectClass=*)', ['fdTasksLifeCyclePreResource', 'fdTasksLifeCyclePreState', 'fdTasksLifeCyclePreSubState', 'fdTasksLifeCyclePostResource', 'fdTasksLifeCyclePostState', 'fdTasksLifeCyclePostSubState', 'fdTasksLifeCyclePostEndDate'], - '', $taskDN); + '', $taskDN); } /** @@ -259,7 +286,7 @@ class LifeCycle implements EndpointInterface * @return array * Note : simply return the current values of supannRessourceEtatDate of the specified user. */ - private function getUserSupannHistory ($userDN) : array + private function getUserSupannHistory ($userDN): array { return $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate'], '', $userDN); -- GitLab From a36a5c98bb35edc7bbd6762a05844ae7e0cd1d06 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 14 Nov 2024 17:38:51 +0000 Subject: [PATCH 105/111] :sparkles: Feat(Orchestrator) - small refactor Adds better readability --- plugins/tasks/LifeCycle.php | 108 +++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index 46e3558..0f1edda 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -181,68 +181,86 @@ class LifeCycle implements EndpointInterface */ protected function updateLifeCycle (array $lifeCycleBehavior, string $userDN, array $currentUserLifeCycle) { - // Will contain the supann resource to be updated - $matchedResource = ''; + // Init return value + $result = ''; + // Hosting the final entry of supann attributes to be pushed to LDAP + $ldapEntry = []; // Only keep the supann state from the received array and removing the count key $userStateHistory = $currentUserLifeCycle[0]['supannressourceetatdate']; $this->gateway->unsetCountKeys($userStateHistory); - // Hosting the final entry of supann attributes to be pushed to LDAP - $ldapEntry = []; - // Extracting values of desired post-state behavior - $newEntry['Resource'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostresource'][0]; - $newEntry['State'] = $lifeCycleBehavior[0]['fdtaskslifecyclepoststate'][0]; - $newEntry['SubState'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostsubstate'][0] ?? ''; //SubState is optional - $newEntry['EndDate'] = $lifeCycleBehavior[0]['fdtaskslifecyclepostenddate'][0] ?? 0; //EndDate is optional + $newEntry = $this->prepareNewEntry($lifeCycleBehavior[0]); - // Prepare the ldap entry to be modified + // Create the new resource without start / end date $newResource = "{" . $newEntry['Resource'] . "}" . $newEntry['State'] . ":" . $newEntry['SubState']; - // Used to compare if the resource exists in history + // Get the resource name, it will be used to compare if the resource exists in history $newResourceName = $this->returnSupannResourceBetweenBrackets($newResource); - // Iterate through the supann state and get a match - foreach ($userStateHistory as $value) { - // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate - $currentResource = $this->returnSupannResourceBetweenBrackets($value); - - // Get the resource matched - if ($currentResource === $newResourceName) { - $matchedResource = $value; - break; + // Find a matching resource in the user state history + $matchedResource = $this->findMatchedResource($userStateHistory, $newResourceName); + if ($matchedResource) { + + // Fetch the end date of the matched resource. + $currentEndDate = $this->extractCurrentEndDate($matchedResource); + // Create a DateTime object from the string + $currentEndDateObject = DateTime::createFromFormat("Ymd", $currentEndDate); + $currentEndDateObject->modify("+" . $newEntry['EndDate'] . " days"); + $finalRessourceEtatDate = $newResource . ':' . $currentEndDate . ':' . $currentEndDateObject->format('Ymd'); + + // Iterate again through the supann state and get a match + foreach ($userStateHistory as $userState => $value) { + // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate + $currentResource = $this->returnSupannResourceBetweenBrackets($value); + + // Get the resource matched + if ($currentResource === $newResourceName) { + $userStateHistory[$userState] = $finalRessourceEtatDate; + break; + } } - } - // Fetch the end date of the matched resource. - $currentEndDate = $this->extractCurrentEndDate($matchedResource); - // Create a DateTime object from the string - $currentEndDateObject = DateTime::createFromFormat("Ymd", $currentEndDate); - $currentEndDateObject->modify("+" . $newEntry['EndDate'] . " days"); - $finalRessourceEtatDate = $newResource . ':' . $currentEndDate . ':' . $currentEndDateObject->format('Ymd'); - - // Iterate again through the supann state and get a match - foreach ($userStateHistory as $userState => $value) { - // Extract resource in curly braces (brackets) from the current supannRessourceEtatDate - $currentResource = $this->returnSupannResourceBetweenBrackets($value); - - // Get the resource matched - if ($currentResource === $newResourceName) { - $userStateHistory[$userState] = $finalRessourceEtatDate; - break; + // Creation of the ldap entry + $ldapEntry['supannRessourceEtatDate'] = $userStateHistory; + try { + $result = ldap_modify($this->gateway->ds, $userDN, $ldapEntry); + } catch (Exception $e) { + $result = json_encode(["Ldap Error" => "$e"]); } } + return $result; + } - // Creation of the ldap entry - $ldapEntry['supannRessourceEtatDate'] = $userStateHistory; - - try { - $result = ldap_modify($this->gateway->ds, $userDN, $ldapEntry); - } catch (Exception $e) { - $result = json_encode(["Ldap Error" => "$e"]); + /** + * @param array $userStateHistory + * @param string $newResourceName + * @return string|null + * Note : Simple helper method to return the matched resource. + */ + private function findMatchedResource(array $userStateHistory, string $newResourceName): ?string + { + foreach ($userStateHistory as $value) { + if ($this->returnSupannResourceBetweenBrackets($value) === $newResourceName) { + return $value; + } } + return null; + } - return $result; + /** + * @param array $lifeCycleBehavior + * @return array + * Simple helper method for readiness. + */ + private function prepareNewEntry(array $lifeCycleBehavior): array + { + return [ + 'Resource' => $lifeCycleBehavior['fdtaskslifecyclepostresource'][0], + 'State' => $lifeCycleBehavior['fdtaskslifecyclepoststate'][0], + 'SubState' => $lifeCycleBehavior['fdtaskslifecyclepostsubstate'][0] ?? '', + 'EndDate' => $lifeCycleBehavior['fdtaskslifecyclepostenddate'][0] ?? 0, + ]; } /** -- GitLab From d1e5755b75a5d75b427e0500bed49e27c41575f9 Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Thu, 14 Nov 2024 17:44:12 +0000 Subject: [PATCH 106/111] :sparkles: Feat(Orchestrator) - phpcs PHPCS --- plugins/tasks/LifeCycle.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/tasks/LifeCycle.php b/plugins/tasks/LifeCycle.php index 0f1edda..02bec2a 100644 --- a/plugins/tasks/LifeCycle.php +++ b/plugins/tasks/LifeCycle.php @@ -238,14 +238,14 @@ class LifeCycle implements EndpointInterface * @return string|null * Note : Simple helper method to return the matched resource. */ - private function findMatchedResource(array $userStateHistory, string $newResourceName): ?string + private function findMatchedResource (array $userStateHistory, string $newResourceName): ?string { foreach ($userStateHistory as $value) { if ($this->returnSupannResourceBetweenBrackets($value) === $newResourceName) { return $value; } } - return null; + return NULL; } /** @@ -253,7 +253,7 @@ class LifeCycle implements EndpointInterface * @return array * Simple helper method for readiness. */ - private function prepareNewEntry(array $lifeCycleBehavior): array + private function prepareNewEntry (array $lifeCycleBehavior): array { return [ 'Resource' => $lifeCycleBehavior['fdtaskslifecyclepostresource'][0], -- GitLab From e207e852e74ecb5de90f18875a002cfa3a5bbcff Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Fri, 15 Nov 2024 11:44:18 +0000 Subject: [PATCH 107/111] :sparkles: Feat(Orchestrator) - Changes configuration file logic Enhance configuration file logic. --- contrib/orchestrator.conf | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/contrib/orchestrator.conf b/contrib/orchestrator.conf index fdf91b0..98445c1 100644 --- a/contrib/orchestrator.conf +++ b/contrib/orchestrator.conf @@ -4,11 +4,23 @@ #Information related to LDAP FD_LDAP_MASTER_URL="ldaps://ldap.fusiondirectory.org" +LDAP_BASE="dc=fusiondirectory,dc=org" LDAP_ADMIN="cn=admin,dc=fusiondirectory,dc=org" LDAP_PWD="<ldap_pass>" + +#Information related to the DSA login. Required by the fusiondirectory-orchestrator-client. #Only DSA authentication is allowed LDAP_OU_DSA="ou=dsa,dc=fusiondirectory,dc=org" -LDAP_BASE="dc=fusiondirectory,dc=org" +DSA_LOGIN="<dsa_login>" +DSA_PASS="<dsa_pass>" + +#Information related to the webservice of FusionDirectory. Required to call FusionDirectory webservice. +FUSION_DIRECTORY_API_URL="https://fd.fusiondirectory.org/rest.php/v1" +WEB_LOGIN="weblogin_username" +WEB_PASS="weblogin_password" + +#Information related to the FusionDirectory Orchestrator Endpoint used by the client +ORCHESTRATOR_API_URL="https://fd.fusiondirectory.org/orchestrator" #Information related to Token management #SECRET_KEY a 256 bit WEB keys required @@ -27,13 +39,3 @@ MAIL_SEC_VERIFY="TRUE" # If mail sec verify is set to true, mail_sec is required MAIL_SEC="<ssl/tls>" MAIL_PORT="25" - -#Information related to the DSA login. Required by the fusiondirectory-orchestrator-client. -DSA_LOGIN="<dsa_login>" -DSA_PASS="<dsa_pass>" -ORCHESTRATOR_API_URL="https://fd.fusiondirectory.org/orchestrator" - -#Information related to the webservice library of Orchestrator. Required to call FD webservice. -FUSION_DIRECTORY_API_URL="https://fd.fusiondirectory.org/rest.php/v1" -WEB_LOGIN="weblogin_username" -WEB_PASS="weblogin_password" \ No newline at end of file -- GitLab From eff1f9dc2e81b034bbc1358256057d6c330b7b9d Mon Sep 17 00:00:00 2001 From: Thibault Dockx <thibault.dockx@fusiondirectory.org> Date: Mon, 9 Dec 2024 12:10:57 +0000 Subject: [PATCH 108/111] :sparkles: feat(Orchestrator) Allows standalone member --- plugins/tasks/Reminder.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/tasks/Reminder.php b/plugins/tasks/Reminder.php index ed870da..ca46b55 100644 --- a/plugins/tasks/Reminder.php +++ b/plugins/tasks/Reminder.php @@ -110,8 +110,8 @@ class Reminder implements EndpointInterface // Create timeStamp expiration for token $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0], - $remindersMainTask[0]['fdtasksreminderfirstcall'][0], - $remindersMainTask[0]['fdtasksremindersecondcall'][0]); + $remindersMainTask[0]['fdtasksreminderfirstcall'][0], + $remindersMainTask[0]['fdtasksremindersecondcall'][0]); // Create token for SubTask $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire); // Edit the mailForm with the url link containing the token @@ -137,8 +137,8 @@ class Reminder implements EndpointInterface // Create timeStamp expiration for token $tokenExpire = $this->getTokenExpiration($task['fdtasksgranularhelper'][0], - $remindersMainTask[0]['fdtasksreminderfirstcall'][0], - $remindersMainTask[0]['fdtasksremindersecondcall'][0]); + $remindersMainTask[0]['fdtasksreminderfirstcall'][0], + $remindersMainTask[0]['fdtasksremindersecondcall'][0]); // Create token for SubTask $token = $this->generateToken($task['fdtasksgranulardn'][0], $tokenExpire); // Edit the mailForm with the url link containing the token @@ -204,7 +204,7 @@ class Reminder implements EndpointInterface { $result = ''; $userPosix = $this->gateway->getLdapTasks('(objectClass=shadowAccount)', ['shadowExpire'], - '', $dn); + '', $dn); // Simply remove key "count" $this->gateway->unsetCountKeys($userPosix); @@ -428,7 +428,7 @@ class Reminder implements EndpointInterface // in case the DN do not have an email set. - Return string FALSE. $result = "FALSE"; $email = $this->gateway->getLdapTasks('(objectClass=gosaMailAccount)', ['mail'], - '', $dn); + '', $dn); // Simply remove key "count" $this->gateway->unsetCountKeys($email); @@ -481,7 +481,7 @@ class Reminder implements EndpointInterface { $supannResources = []; $supannResources = $this->gateway->getLdapTasks('(objectClass=supannPerson)', ['supannRessourceEtatDate', 'supannRessourceEtat'], - '', $dn); + '', $dn); // Simply remove key "count" $this->gateway->unsetCountKeys($supannResources); @@ -693,6 +693,12 @@ class Reminder implements EndpointInterface foreach ($reminder as $reminderItem) { // Each subTask reminder foreach ($reminderItem as $mailDetails) { + + // It is not impossible that only one recipient exist, therefore it won't be an array. + if (!is_array($mailDetails['mail']['recipients'])) { + // Simply transform the string into an array + $mailDetails['mail']['recipients'] = [$mailDetails['mail']['recipients']]; + } $numberOfRecipients = count($mailDetails['mail']['recipients']); $mail_controller = new \FusionDirectory\Mail\MailLib( -- GitLab From bb0b3905c1300f7cfbff2009646ddd577bcd20ab Mon Sep 17 00:00:00 2001 From: Benoit Mortier <benoit.mortier@fusiondirectory.org> Date: Fri, 17 Jan 2025 18:20:40 +0100 Subject: [PATCH 109/111] :docs: fix(readme) fix the readme to add the new text and remove the crowfunding option no longer used Signed-off-by: Benoit Mortier <benoit.mortier@fusiondirectory.org> --- README.md | 62 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0de72f8..f536abf 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,48 @@ # FusionDirectory Orchestrator -FusionDirectory Orchestrator is a REST API orchestrator. -It is a part of our new workflow management system defined in FusionDirectory -Is purpose is to execute tasks defined in FusionDirectory. +FusionDirectory Orchestrator is a RESTful web service using JWT authentication, designed to manage and execute tasks efficiently. + +It supports multiple endpoints with plugin integration for custom processing or specialized tasks. + +Tasks are defined within FusionDirectory, with a client available to query endpoints and manage workflows. + +Common tasks include account lifecycle, notifications, reminders, mail automation, audit log management, and more. ## Features * Tasks management. * Tasks execution. +* Workflow management. * JWT Authentication methods ## Tasks management -FusionDirectory Orchestrator REST API allows the retrieval of existing created tasks. -It offers a simple view on the status of each task. +FusionDirectory Orchestrator REST API provides seamless management and retrieval of tasks created within FusionDirectory. +It offers a clear and concise view of the status of each task, including subtasks, allowing for detailed tracking and reporting. -It is possible to retrieve specialized tasks and see their content. +With its extensible design, the Orchestrator supports specialized tasks such as mail automation, notifications, reminders, +account lifecycle management, and audit log processing, enabling tailored workflows to meet diverse needs ## Tasks execution -One of the main aspects of FusionDirectory Orchestrator is to allow processing of some desired tasks. +One of the core functionalities of the **FusionDirectory Orchestrator** is the execution and processing of various tasks as defined within FusionDirectory. + +- **Mail Tasks**: + When triggered, tasks of type "Mail" will automatically send the relevant emails if the scheduled conditions are met, ensuring timely communication. + +- **Life Cycle Tasks**: + These tasks are responsible for updating specialized attributes, such as *supann* attributes, in accordance with defined lifecycle processes. + +- **Notification Tasks**: + When attributes are modified, "Notification" tasks will send automated email alerts to keep users informed of changes. + +- **Reminder Tasks**: + These tasks send reminders to users, potentially including links to extend or prolong their account, ensuring critical actions are not missed. -For example : +- **Audit Tasks**: + Tasks of this type allow for the management of audit logs, including the deletion of logs based on configurable retention policies, ensuring compliance and data management. -* In case of a task of type "Mail", the list of related emails will be sent if scheduled is matched. -* In case of a task of type "Life Cycle", the specialized supann attributes will be updated accordingly. +The **Orchestrator client** provides a user-friendly interface to activate and manage these tasks, allowing for seamless workflow execution and efficient task orchestration across the system. ## Get help @@ -36,12 +54,10 @@ There are a couple of ways you can try [to get help][get help]. Professional support is provided through of subscription. -We have two type of subscription : +* [FusionDirectory Subscription][subscription-fusiondirectory] : Global subscription for FusionDirectory -* [FusionDirectory][subscription-fusiondirectory] : Global subscription for FusionDirectory and all the plugins -* [FusionDirectory Plus][subscription-fusiondirectory-plus] : Expert Support on Education, Deployment and Infrastructure plugins - -The subscription provides access to FusionDirectory's stable enterprise repository, providing reliable software updates and security enhancements, as well as technical help and support. +The subscription provides access to FusionDirectory's enterprise repository, tested and pre-packaged versions with patches between versions, +providing reliable software updates and security enhancements, as well as technical help and support. Choose the plan that's right for you. Our subscriptions are flexible and scalable according to your needs @@ -59,9 +75,7 @@ If you like us and want to send us a small contribution, you can use the followi * [donate-kofi] -* [donate-opencollective] - -* [donate-communitybridge] +* [donate-github] ## License @@ -69,13 +83,11 @@ If you like us and want to send us a small contribution, you can use the followi [FusionDirectory]: https://www.fusiondirectory.org/ -[fusiondirectory-install]: https://fusiondirectory-user-manual.readthedocs.io/en/1.4/fusiondirectory/install/index.html +[fusiondirectory-install]: https://fusiondirectory-user-manual.readthedocs.io/en/latest/fusiondirectory/install/index.html [get help]: https://fusiondirectory-user-manual.readthedocs.io/en/latest/support/index.html -[subscription-fusiondirectory]: https://www.fusiondirectory.org/en/subscription-fusiondirectory/ - -[subscription-fusiondirectory-plus]: https://www.fusiondirectory.org/en/subscriptions-fusiondirectory-plus/ +[subscription-fusiondirectory]: https://www.fusiondirectory.org/en/iam-tool-service-subscriptions/ [register]: https://register.fusiondirectory.org @@ -83,9 +95,5 @@ If you like us and want to send us a small contribution, you can use the followi [donate-kofi]: https://ko-fi.com/fusiondirectory -[donate-opencollective]: https://opencollective.com/fusiondirectory - -[donate-communitybridge]: https://funding.communitybridge.org/projects/fusiondirectory - - +[donate-github]: https://github.com/fusiondirectory -- GitLab From b993cdedd308bc1f9a92cbb49085ab542334438a Mon Sep 17 00:00:00 2001 From: Benoit Mortier <benoit.mortier@fusiondirectory.org> Date: Fri, 17 Jan 2025 18:22:47 +0100 Subject: [PATCH 110/111] :docs: fix(readme) added new text and removed subscription and crowfunding no longer used Signed-off-by: Benoit Mortier <benoit.mortier@fusiondirectory.org> --- README.md | 62 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 0de72f8..f536abf 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,48 @@ # FusionDirectory Orchestrator -FusionDirectory Orchestrator is a REST API orchestrator. -It is a part of our new workflow management system defined in FusionDirectory -Is purpose is to execute tasks defined in FusionDirectory. +FusionDirectory Orchestrator is a RESTful web service using JWT authentication, designed to manage and execute tasks efficiently. + +It supports multiple endpoints with plugin integration for custom processing or specialized tasks. + +Tasks are defined within FusionDirectory, with a client available to query endpoints and manage workflows. + +Common tasks include account lifecycle, notifications, reminders, mail automation, audit log management, and more. ## Features * Tasks management. * Tasks execution. +* Workflow management. * JWT Authentication methods ## Tasks management -FusionDirectory Orchestrator REST API allows the retrieval of existing created tasks. -It offers a simple view on the status of each task. +FusionDirectory Orchestrator REST API provides seamless management and retrieval of tasks created within FusionDirectory. +It offers a clear and concise view of the status of each task, including subtasks, allowing for detailed tracking and reporting. -It is possible to retrieve specialized tasks and see their content. +With its extensible design, the Orchestrator supports specialized tasks such as mail automation, notifications, reminders, +account lifecycle management, and audit log processing, enabling tailored workflows to meet diverse needs ## Tasks execution -One of the main aspects of FusionDirectory Orchestrator is to allow processing of some desired tasks. +One of the core functionalities of the **FusionDirectory Orchestrator** is the execution and processing of various tasks as defined within FusionDirectory. + +- **Mail Tasks**: + When triggered, tasks of type "Mail" will automatically send the relevant emails if the scheduled conditions are met, ensuring timely communication. + +- **Life Cycle Tasks**: + These tasks are responsible for updating specialized attributes, such as *supann* attributes, in accordance with defined lifecycle processes. + +- **Notification Tasks**: + When attributes are modified, "Notification" tasks will send automated email alerts to keep users informed of changes. + +- **Reminder Tasks**: + These tasks send reminders to users, potentially including links to extend or prolong their account, ensuring critical actions are not missed. -For example : +- **Audit Tasks**: + Tasks of this type allow for the management of audit logs, including the deletion of logs based on configurable retention policies, ensuring compliance and data management. -* In case of a task of type "Mail", the list of related emails will be sent if scheduled is matched. -* In case of a task of type "Life Cycle", the specialized supann attributes will be updated accordingly. +The **Orchestrator client** provides a user-friendly interface to activate and manage these tasks, allowing for seamless workflow execution and efficient task orchestration across the system. ## Get help @@ -36,12 +54,10 @@ There are a couple of ways you can try [to get help][get help]. Professional support is provided through of subscription. -We have two type of subscription : +* [FusionDirectory Subscription][subscription-fusiondirectory] : Global subscription for FusionDirectory -* [FusionDirectory][subscription-fusiondirectory] : Global subscription for FusionDirectory and all the plugins -* [FusionDirectory Plus][subscription-fusiondirectory-plus] : Expert Support on Education, Deployment and Infrastructure plugins - -The subscription provides access to FusionDirectory's stable enterprise repository, providing reliable software updates and security enhancements, as well as technical help and support. +The subscription provides access to FusionDirectory's enterprise repository, tested and pre-packaged versions with patches between versions, +providing reliable software updates and security enhancements, as well as technical help and support. Choose the plan that's right for you. Our subscriptions are flexible and scalable according to your needs @@ -59,9 +75,7 @@ If you like us and want to send us a small contribution, you can use the followi * [donate-kofi] -* [donate-opencollective] - -* [donate-communitybridge] +* [donate-github] ## License @@ -69,13 +83,11 @@ If you like us and want to send us a small contribution, you can use the followi [FusionDirectory]: https://www.fusiondirectory.org/ -[fusiondirectory-install]: https://fusiondirectory-user-manual.readthedocs.io/en/1.4/fusiondirectory/install/index.html +[fusiondirectory-install]: https://fusiondirectory-user-manual.readthedocs.io/en/latest/fusiondirectory/install/index.html [get help]: https://fusiondirectory-user-manual.readthedocs.io/en/latest/support/index.html -[subscription-fusiondirectory]: https://www.fusiondirectory.org/en/subscription-fusiondirectory/ - -[subscription-fusiondirectory-plus]: https://www.fusiondirectory.org/en/subscriptions-fusiondirectory-plus/ +[subscription-fusiondirectory]: https://www.fusiondirectory.org/en/iam-tool-service-subscriptions/ [register]: https://register.fusiondirectory.org @@ -83,9 +95,5 @@ If you like us and want to send us a small contribution, you can use the followi [donate-kofi]: https://ko-fi.com/fusiondirectory -[donate-opencollective]: https://opencollective.com/fusiondirectory - -[donate-communitybridge]: https://funding.communitybridge.org/projects/fusiondirectory - - +[donate-github]: https://github.com/fusiondirectory -- GitLab From 58252ea4a24a5c9fe0689f8acd8eb9b17bd2dc09 Mon Sep 17 00:00:00 2001 From: Benoit Mortier <benoit.mortier@fusiondirectory.org> Date: Fri, 31 Jan 2025 16:26:53 +0100 Subject: [PATCH 111/111] :sparkles: feat(Changelog) add Changelog for FusionDirectory Orchestrator 1.1 Signed-off-by: Benoit Mortier <benoit.mortier@fusiondirectory.org> --- Changelog.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Changelog.md b/Changelog.md index 3c10721..8bc3885 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,35 @@ +## %"FusionDirectory Orchestrator 1.1" - 2025-01-31 + +### Added + +#### fusiondirectory-orchestrator +- fusiondirectory-orchestrator#39 [Orchestrator] - New endpoint notifications in order to manage notifications tasks +- fusiondirectory-orchestrator#43 [Orchestrator] - Create a possible plugin endpoints, allowing new endpoints developed to be considered as plugin base. +- fusiondirectory-orchestrator#48 [Orchestrator] - Add options mailAuth and mail_sec_verify in orchestrator configuration file +- fusiondirectory-orchestrator#52 [Orchestrator] - AUDIT - automatic deletion from audit tasks +- fusiondirectory-orchestrator#57 [Orchestrator] - New endpoint for userReminder allowing to send notification to use with token to extend their account longevity +- fusiondirectory-orchestrator#62 [Orchestrator] - lifeCycle prolongation adds time from date of process and not from resource end date +- fusiondirectory-orchestrator#64 [Orchestrator] - Reminder - Removal of fdTasksReminderManager +- fusiondirectory-orchestrator#65 [Orchestrator] - Format of the orchestrator.conf updates + +### Changed + +#### fusiondirectory-orchestrator +- fusiondirectory-orchestrator#55 [Orchestrator] - Notifications must be updated to be aware of supannStatus values + +### Removed + +#### fusiondirectory-orchestrator +- fusiondirectory-orchestrator#37 [Orchestrator] - Analyze the library mail to integrate it to integrator + +### Fixed + +#### fusiondirectory-orchestrator +- fusiondirectory-orchestrator#46 [Orchestrator] - taskGateway verify schedule strtotime issues +- fusiondirectory-orchestrator#47 Use overload instead of load for dotenv +- fusiondirectory-orchestrator#61 [Orchestrator] - LifeCycle array supann is analyzed with static numbering +- fusiondirectory-orchestrator#66 [Orchestrator] - user-reminder - issue when only one members is set + ## %"FusionDirectory Orchestrator 1.0" - 2024-04-05 ### Added -- GitLab