Verified Commit d4b52ca5 authored by bmortier's avatar bmortier
Browse files

Merge branch 'dev' into 51-add-ubuntu-focal-in-the-ci-trigger

Showing with 310 additions and 137 deletions
+310 -137
...@@ -18,9 +18,13 @@ TOKEN_EXPIRY="300" ...@@ -18,9 +18,13 @@ TOKEN_EXPIRY="300"
REFRESH_EXPIRY="432000" REFRESH_EXPIRY="432000"
#Information related to PHP Mailer #Information related to PHP Mailer
MAIL_AUTH="TRUE"
# If mail auth is TRUE, below user and pass are required
MAIL_USER="fusiondirectory" MAIL_USER="fusiondirectory"
MAIL_PASS="<mail_pass>" MAIL_PASS="<mail_pass>"
MAIL_HOST="mail.fusiondirectory.org" 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_SEC="<ssl/tls>"
MAIL_PORT="25" MAIL_PORT="25"
......
<?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
...@@ -50,7 +50,7 @@ class TaskGateway ...@@ -50,7 +50,7 @@ class TaskGateway
break; break;
case $object_type: 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); $this->unsetCountKeys($list_tasks);
break; break;
...@@ -145,12 +145,13 @@ class TaskGateway ...@@ -145,12 +145,13 @@ class TaskGateway
$this->unsetCountKeys($tasks); $this->unsetCountKeys($tasks);
if (!empty($tasks)) { if (!empty($tasks)) {
// Initiate the object webservice. // 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. // Required to prepare future webservice call. E.g. Retrieval of mandatory token.
$webservice->setCurlSettings(); $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'); $now = new DateTime('now');
foreach ($tasks as $task) { foreach ($tasks as $task) {
...@@ -186,21 +187,23 @@ class TaskGateway ...@@ -186,21 +187,23 @@ class TaskGateway
} }
break; break;
case 'Weekly' : case 'Weekly' :
if ($interval->d >= 7) { if ($interval->days >= 7) {
$result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']);
} else { } else {
$result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.';
} }
break; break;
case 'Daily' : case 'Daily' :
if ($interval->d >= 1) { if ($interval->days >= 1) {
$result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']); $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']);
} else { } else {
$result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.';
} }
break; break;
case 'Hourly' : 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']); $result[$task['dn']]['result'] = $webservice->activateCyclicTasks($task['dn']);
} else { } else {
$result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.'; $result[$task['dn']]['lastExecFailed'] = 'This cyclic task has yet to reached its next execution cycle.';
...@@ -210,7 +213,7 @@ class TaskGateway ...@@ -210,7 +213,7 @@ class TaskGateway
} }
// Case where cyclic tasks where found but the schedule is no ready. // Case where cyclic tasks where found but the schedule is no ready.
} else { } 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 { } else {
...@@ -329,7 +332,6 @@ class TaskGateway ...@@ -329,7 +332,6 @@ class TaskGateway
*/ */
public function updateLastMailExecTime (string $dn) public function updateLastMailExecTime (string $dn)
{ {
// prepare data
$ldap_entry["fdTasksConfLastExecTime"] = time(); $ldap_entry["fdTasksConfLastExecTime"] = time();
// Add data to LDAP // Add data to LDAP
......
<?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
*/
public function processEndPointDelete (array $data = NULL): array
{
return [];
}
/**
* @param array|NULL $data
* @return array
* @throws Exception
*/
public function processEndPointPatch (array $data = NULL): array
{
$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 $auditSubTasks
* @return array
* @throws Exception
*/
public function processAuditDeletion (array $auditSubTasks): array
{
$result = [];
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];
// 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
* Note : This will return a validation of audit log suppression
* @throws Exception
*/
public function checkAuditPassedRetention ($auditRetention, $subTaskDN, $subTaskCN): array
{
$auditLib = new FusionDirectory\Audit\AuditLib($auditRetention, $this->returnLdapAuditEntries(), $this->gateway, $subTaskDN, $subTaskCN);
return $auditLib->checkAuditPassedRetentionOrchestrator();
}
/**
* @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;
}
}
\ No newline at end of file
...@@ -61,7 +61,7 @@ class LifeCycle implements EndpointInterface ...@@ -61,7 +61,7 @@ class LifeCycle implements EndpointInterface
// Array representing the status of the subtask. // Array representing the status of the subtask.
$result = []; $result = [];
// Initiate the object webservice. // 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. // Required to prepare future webservice call. E.g. Retrieval of mandatory token.
$webservice->setCurlSettings(); $webservice->setCurlSettings();
......
...@@ -45,7 +45,7 @@ class Mail implements EndpointInterface ...@@ -45,7 +45,7 @@ class Mail implements EndpointInterface
*/ */
public function processEndPointPatch (array $data = NULL): array 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 ...@@ -56,7 +56,7 @@ class Mail implements EndpointInterface
public function processMailTasks (array $tasks): array public function processMailTasks (array $tasks): array
{ {
$result = []; $result = [];
// DEBUGGING
$fdTasksConf = $this->getMailObjectConfiguration(); $fdTasksConf = $this->getMailObjectConfiguration();
$maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf); $maxMailsConfig = $this->returnMaximumMailToBeSend($fdTasksConf);
...@@ -67,6 +67,7 @@ class Mail implements EndpointInterface ...@@ -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. // Note : if list_tasks is empty, the controller receive null as result and will log/process it properly.
foreach ($tasks as $mail) { foreach ($tasks as $mail) {
// verify status before processing (to be checked with schedule as well). // verify status before processing (to be checked with schedule as well).
if ($mail["fdtasksgranularstatus"][0] == 1 && $this->gateway->verifySchedule($mail["fdtasksgranularschedule"][0])) { if ($mail["fdtasksgranularstatus"][0] == 1 && $this->gateway->verifySchedule($mail["fdtasksgranularschedule"][0])) {
...@@ -99,7 +100,7 @@ class Mail implements EndpointInterface ...@@ -99,7 +100,7 @@ class Mail implements EndpointInterface
$attachments = NULL; $attachments = NULL;
} }
$mail_controller = new MailController($setFrom, $mail_controller = new \FusionDirectory\Mail\MailLib($setFrom,
$setBCC, $setBCC,
$recipients, $recipients,
$body, $body,
......
...@@ -68,14 +68,27 @@ class Notifications implements EndpointInterface ...@@ -68,14 +68,27 @@ class Notifications implements EndpointInterface
// Generate the mail form with all mail controller requirements // Generate the mail form with all mail controller requirements
$mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask); $mailTemplateForm = $this->generateMainTaskMailTemplate($notificationsMainTask);
// Simply retrieve the list of audited attributes // Simply retrieve the list of audited attributes
$auditAttributes = $this->retrieveAuditedAttributes($task); $auditAttributes = $this->decodeAuditAttributes($task);
// Recovering monitored attributes list from the defined notification task.
$monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes']; $monitoredAttrs = $notificationsMainTask[0]['fdtasksnotificationsattributes'];
// Reformat supann
$monitoredSupannResource = $this->getSupannResourceState($notificationsMainTask[0]);
// Simply remove keys with 'count' reported by ldap.
$this->gateway->unsetCountKeys($monitoredAttrs); $this->gateway->unsetCountKeys($monitoredAttrs);
$this->gateway->unsetCountKeys($monitoredSupannResource);
// Verify if there is a match between audited attributes and monitored attributes from main task. // Find matching attributes between audited and monitored attributes
$matchingAttrs = array_intersect($auditAttributes, $monitoredAttrs); $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)) { if (!empty($matchingAttrs)) {
// Fill an array with UID of audited user and related matching attributes // Fill an array with UID of audited user and related matching attributes
...@@ -102,6 +115,129 @@ class Notifications implements EndpointInterface ...@@ -102,6 +115,129 @@ class Notifications implements EndpointInterface
return $result; 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
*/
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 * @param string $mainTaskDn
* @return array * @return array
...@@ -110,7 +246,8 @@ class Notifications implements EndpointInterface ...@@ -110,7 +246,8 @@ class Notifications implements EndpointInterface
{ {
// Retrieve data from the main task // Retrieve data from the main task
return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails', return $this->gateway->getLdapTasks('(objectClass=fdTasksNotifications)', ['fdTasksNotificationsListOfRecipientsMails',
'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender'], 'fdTasksNotificationsAttributes', 'fdTasksNotificationsMailTemplate', 'fdTasksNotificationsEmailSender',
'fdTasksNotificationsSubState', 'fdTasksNotificationsState', 'fdTasksNotificationsResource'],
'', $mainTaskDn); '', $mainTaskDn);
} }
...@@ -148,7 +285,8 @@ class Notifications implements EndpointInterface ...@@ -148,7 +285,8 @@ class Notifications implements EndpointInterface
*/ */
protected function retrieveAuditedAttributes (array $notificationTask): array protected function retrieveAuditedAttributes (array $notificationTask): array
{ {
$auditAttributes = []; $auditAttributes = [];
$auditInformation = [];
// Retrieve audit data attributes from the list of references set in the sub-task // Retrieve audit data attributes from the list of references set in the sub-task
if (!empty($notificationTask['fdtasksgranularref'])) { if (!empty($notificationTask['fdtasksgranularref'])) {
...@@ -159,13 +297,14 @@ class Notifications implements EndpointInterface ...@@ -159,13 +297,14 @@ class Notifications implements EndpointInterface
$auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))', $auditInformation[] = $this->gateway->getLdapTasks('(&(objectClass=fdAuditEvent))',
['fdAuditAttributes'], '', $auditDN); ['fdAuditAttributes'], '', $auditDN);
} }
// Again remove key: count retrieved from LDAP. // Again remove key: count retrieved from LDAP.
$this->gateway->unsetCountKeys($auditInformation); $this->gateway->unsetCountKeys($auditInformation);
// It is possible that an audit does not contain any attributes changes, condition is required. // It is possible that an audit does not contain any attributes changes, condition is required.
foreach ($auditInformation as $attr) { foreach ($auditInformation as $attr) {
if (!empty($attr[0]['fdauditattributes'])) { if (!empty($attr[0]['fdauditattributes'])) {
// Clear and compact received results from above ldap search // Clear and compact received results from above ldap search
$auditAttributes = $attr[0]['fdauditattributes']; $auditAttributes[] = $attr[0]['fdauditattributes'];
} }
} }
} }
...@@ -226,7 +365,7 @@ class Notifications implements EndpointInterface ...@@ -226,7 +365,7 @@ class Notifications implements EndpointInterface
foreach ($notifications as $data) { foreach ($notifications as $data) {
$numberOfRecipients = count($data['mailForm']['recipients']); $numberOfRecipients = count($data['mailForm']['recipients']);
$mail_controller = new MailController( $mail_controller = new \FusionDirectory\Mail\MailLib(
$data['mailForm']['setFrom'], $data['mailForm']['setFrom'],
NULL, NULL,
$data['mailForm']['recipients'], $data['mailForm']['recipients'],
...@@ -289,5 +428,4 @@ class Notifications implements EndpointInterface ...@@ -289,5 +428,4 @@ class Notifications implements EndpointInterface
return $result; return $result;
} }
} }
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment