Commit e1f2353d authored by Alexandre Morin's avatar Alexandre Morin

Merge branch 'hotfix/2.1.2' into 'master'

Hotfix/2.1.2

See merge request !46
parents 321c60fe 251d996a
......@@ -92,6 +92,11 @@ stopWordsFilePath = "%laabsDirectory%/data/stopwords/stopwords_fr.txt"
; Chain journal with timestamp file. The timestamp dependency must be configured.
chainWithTimestamp = false;
; Any path on this list will no be logged in the application journal
;ignorePaths = "['organization/organization/readTodisplay']"
; Any operation on this list will no be logged in the application journal
;ignoreMethods = "['read']"
[auth]
; Encryption algorithm (MD5, SHA256, SHA512...)
......
......@@ -38,6 +38,8 @@ class Document extends \dependency\xml\Document
protected $layout;
protected $classes;
protected $plugins;
protected $headers;
protected $layoutData;
public $XPath;
public $translator;
public $dateTimeFormatter;
......
......@@ -57,6 +57,7 @@ class event
*/
public function add($path, array $variables = null, $input = null, $output = null, $status = false, $info = null)
{
// Event creation
$event = \laabs::newInstance('audit/event');
$event->eventId = \laabs::newId();
$event->eventDate = \laabs::newTimestamp();
......
......@@ -115,6 +115,17 @@ class journal
$eventLine[] = (string) $event->accountId;
$eventLine[] = (string) $event->path;
$eventLine[] = (string) $event->status;
if (isset($event->output)) {
$output = json_decode($event->output);
if ($output) {
$messages = [];
foreach ($output as $value) {
$messages[] = $value->fullMessage;
}
$eventLine[] = implode(' ', $messages);
}
}
fputcsv($journalFile, $eventLine);
}
......
......@@ -34,8 +34,9 @@ class logger
public $currentAuditFile;
public $servicePath;
public $input;
public $ignoreReads = false;
public $ignorePaths;
public $output;
public $ignoreMethods = [];
public $ignorePaths = [];
/**
* Constructor
......@@ -45,7 +46,14 @@ class logger
{
$this->sdoFactory = $sdoFactory;
$this->ignorePaths = array("audit/*");
if (isset(\laabs::configuration('audit')['ignoreMethods'])) {
$this->ignoreMethods = \laabs::configuration('audit')['ignoreMethods'];
}
if (isset(\laabs::configuration('audit')['ignorePaths'])) {
$this->ignorePaths = \laabs::configuration('audit')['ignorePaths'];
}
$this->ignorePaths[] = ("audit/*");
}
/**
......@@ -123,22 +131,20 @@ class logger
*/
public function notifyServicePath(&$servicePath, &$serviceMessage = null)
{
if ($servicePath->method == 'read' && $this->ignoreReads) {
if (in_array($servicePath->method, $this->ignoreMethods)) {
return;
}
$fullpath = $servicePath->domain . LAABS_URI_SEPARATOR . $servicePath->interface . LAABS_URI_SEPARATOR . $servicePath->path;
// TO DO : add admin to set ignore path
if ($this->ignorePaths) {
foreach ($this->ignorePaths as $ignorePath) {
if (fnmatch($ignorePath, $fullpath)) {
return;
}
foreach ($this->ignorePaths as $ignorePath) {
if (fnmatch($ignorePath, $servicePath->domain . LAABS_URI_SEPARATOR . $servicePath->interface . LAABS_URI_SEPARATOR . $servicePath->name)) {
return;
}
}
$this->servicePath = $servicePath;
//var_dump($servicePath);
// Extract revealant info from input message
if ($serviceMessage) {
$this->input = array();
......@@ -184,36 +190,17 @@ class logger
return;
}
// Extract revealant info from output message
$output = null;
if ($serviceReturn) {
switch (true) {
case (is_scalar($serviceReturn) && ctype_print($serviceReturn)) :
case is_bool($serviceReturn):
case is_numeric($serviceReturn):
$output = $serviceReturn;
break;
case (is_object($serviceReturn) && method_exists($serviceReturn, '__toString')) :
$output = (string) $serviceReturn;
break;
case is_array($serviceReturn):
$output = count($serviceReturn);
}
}
if (count($this->input) == 0) {
$input = null;
} else {
$input = $this->input;
if (is_array($this->output)) {
$this->output = json_encode($this->output);
}
\laabs::callService(
'audit/event/create', $this->servicePath->getName(), $this->servicePath->variables, $input, $output, true
'audit/event/create', $this->servicePath->getName(), $this->servicePath->variables, $this->input, $this->output, true
);
$this->servicePath = null;
$this->output = null;
$this->input = null;
}
/**
......@@ -239,4 +226,23 @@ class logger
);
}
/**
* Log a given output by observation
* @param object &$output
*
* @return void
*
* @subject bundle\audit\AUDIT_ENTRY_OUTPUT
*/
public function notifyOutput(&$output)
{
if (isset($output['variables'])) {
$output['fullMessage'] = vsprintf($output['message'], $output['variables']);
} else {
$output['fullMessage'] = $output['message'];
}
$this->output[] = $output;
}
}
......@@ -18,4 +18,5 @@
* along with bundle audit. If not, see <http://www.gnu.org/licenses/>.
*/
namespace bundle\audit;
define('bundle\audit\AUDIT_ENTRY', 'auditEntry');
\ No newline at end of file
define('bundle\audit\AUDIT_ENTRY', 'auditEntry');
define('bundle\audit\AUDIT_ENTRY_OUTPUT', 'auditEntryOutput');
\ No newline at end of file
......@@ -162,6 +162,7 @@ class scheduling
public function execute($schedulingId)
{
$status = true;
$info = null;
$scheduling = $this->sdoFactory->read("batchProcessing/scheduling", $schedulingId);
$task = $this->readTask($scheduling->taskId);
......@@ -184,13 +185,12 @@ class scheduling
$pathRouter = new \core\Route\PathRouter($task->route);
\core\Observer\Dispatcher::notify(LAABS_SERVICE_PATH,$pathRouter->path);
if (!empty($scheduling->parameters)) {
$info = \laabs::callServiceArgs($task->route, (array) $scheduling->parameters);
\laabs::callServiceArgs($task->route, (array) $scheduling->parameters);
} else {
$info = \laabs::callService($task->route);
\laabs::callService($task->route);
}
\laabs::notify(LAABS_SERVICE_RETURN, $info);
} catch (\Exception $info) {
$this->changeStatus($schedulingId, "error");
$status = false;
......@@ -210,6 +210,14 @@ class scheduling
$this->update($scheduling);
$observerPool = \core\Observer\Dispatcher::getPool(\bundle\audit\AUDIT_ENTRY_OUTPUT);
foreach ($observerPool as $key=>$value) {
if ($value instanceof \bundle\audit\Observer\logger) {
$info = $value->output;
break;
}
}
$this->logSchedulingController->add($schedulingId, $scheduling->executedBy, $launchedBy, $status, $info);
return $scheduling;
......
......@@ -57,6 +57,10 @@ trait archiveComplianceTrait
$queryPart["status"] = "status!='error' AND status!='disposed'";
$queryPart["parentArchiveId"] = "parentArchiveId=null";
$totalNbArchivesToCheck = 0;
$totalNbArchivesInSample = 0;
$totalarchivesChecked = 0;
foreach ($serviceLevels as $serviceLevel) {
if (($serviceLevel->samplingFrequency <= 0) || ($serviceLevel->samplingRate <= 0)) {
continue;
......@@ -102,6 +106,9 @@ trait archiveComplianceTrait
$success = $this->checkArchiveIntegrity($archive);
if (!$success) {
$logMessage = ["message" => "Error on integrity check"];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
break;
} else {
$archivesChecked++;
......@@ -121,8 +128,21 @@ trait archiveComplianceTrait
$eventInfo['archivesChecked'] = $archivesChecked;
$this->lifeCycleJournalController->logEvent('recordsManagement/periodicIntegrityCheck', 'recordsManagement/serviceLevel', $serviceLevel->serviceLevelId, $eventInfo, $success);
$totalNbArchivesToCheck += nbArchivesToCheck;
$totalNbArchivesInSample += nbArchivesInSample;
$totalarchivesChecked += archivesChecked;
}
$logMessage = ["message" => "%s archive(s) to check", "variables"=> $totalNbArchivesToCheck];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
$logMessage = ["message" => "%s archive(s) in sample", "variables"=> $totalNbArchivesInSample];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
$logMessage = ["message" => "%s archive(s) checked", "variables"=> $totalarchivesChecked];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
return true;
}
......@@ -146,7 +166,7 @@ trait archiveComplianceTrait
}
/**
* Check integrity of one or several archives giving their identifiers
* @param object $archiveIds An array of archive identifier or an archive identifier
* @param object $archiveIds An array of archive identifier or an archive identifier
*
* @return recordsManagement/archive[] Array of archive object
*/
......@@ -168,6 +188,20 @@ trait archiveComplianceTrait
}
}
$logMessage = ["message" => "%s archives cheked", "variables"=> count($archives)];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
$logMessage = ["message" => "%s archives are valid", "variables"=> count($res['success'])];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
$logMessage = ["message" => "%s archives are not valid", "variables"=> count($res['error'])];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
if (count($res[error])) {
$logMessage = ["message" => "Invalid archive identifier : %s", "variables"=> implode(', ', $res['error'])];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
}
return $res;
}
......
......@@ -330,6 +330,9 @@ class log implements archiveDescriptionInterface
// Create timestamp resource
$timestampResource = $digitalResourceController->createFromFile($timestampFileName);
$digitalResourceController->getHash($timestampResource, "SHA256");
$logMessage = ["message" => "Timestamp file generated"];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
$timestampResource->archiveId = $journalResource->archiveId;
$timestampResource->relatedResId = $journalResource->resId;
......@@ -354,6 +357,29 @@ class log implements archiveDescriptionInterface
$archive->fullTextIndexation = "none";
}
return $archiveController->deposit($archive, 'journal/'.$log->type.'/<date("Y")>/<date("m")>');
try {
$archive = $archiveController->deposit($archive, 'journal/'.$log->type.'/<date("Y")>/<date("m")>');
$logMessage = ["message" => "New journal identifier : %s", "variables" => $archive->archiveId];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
} catch (\Exception $e) {
$logMessage = ["message" => "Error on journal creation"];
\laabs::notify(\bundle\audit\AUDIT_ENTRY_OUTPUT, $logMessage);
throw $e;
}
return $archive->archiveId;
}
public function contents ($type, $archiveId, $resourceId) {
$archiveController = \laabs::newController('recordsManagement/archive');
$res = $archiveController->consultation($archiveId, $resourceId);
$journal = $type . PHP_EOL;
$journal .= $archiveId . ',' . $resourceId . PHP_EOL;
$journal .= file_get_contents($res->address[0]->repository->repositoryUri . $res->address[0]->path);
return $journal;
}
}
......@@ -50,5 +50,12 @@ interface logInterface
* @action recordsManagement/log/depositJournal
*/
public function createDepositjournal($journalFileName, $fromDate, $toDate, $type, $processName = null, $timestampFileName = null);
/**
* @param string $archiveId Archiver identifier
* @param string $resourceId Resource identifier
*
* @action recordsManagement/log/contents
*/
public function contents_type__archiveId__resourceId_();
}
......@@ -86,6 +86,7 @@ class event
{
$events = array();
$routes = array();
$translator = $this->view->translator;
$bundles = \laabs::bundles();
foreach ($bundles as $bundle) {
......@@ -101,29 +102,17 @@ class event
}
$routes = array_unique($routes);
foreach ($routes as $route) {
$event = new \stdClass();
$event->path = $route;
if (strpos($route, 'read') || strpos($route, 'get')) {
$event->class = 'read';
} elseif (strpos($route, 'create') || strpos($route, 'add') || strpos($route, 'new')) {
$event->class = 'create';
} elseif (strpos($route, 'update') || strpos($route, 'modify')) {
$event->class = 'update';
} elseif (strpos($route, 'delete')) {
$event->class = 'delete';
} else {
$event->class = 'all';
}
$event->label = $translator->getText($event->path, false, "audit/messages");
$events[] = $event;
}
$this->view->addContentFile("audit/search.html");
$this->view->setSource("events", $events);
$this->view->merge();
$this->view->translate();
$this->view->merge();
$this->view->addScriptSrc(
<<<EOD
......@@ -170,7 +159,7 @@ EOD
$dataTable->setSorting(array(array(2, 'desc')));
} else {
$dataTable->setUnsortableColumns(4);
$dataTable->setSorting(array(array(1, 'desc')));
$dataTable->setSorting(array(array(0, 'desc')));
}
return $this->view->saveHtml();
......@@ -188,23 +177,36 @@ EOD
$this->view->translate();
// Fix error on event info
if(isset($event->info )) {
foreach (json_decode($event->info) as $name => $value) {
$event->info2[] = array('name'=> $name, 'value'=> $value);
$nameTraduction = $this->view->translator->getText($name, false, "audit/messages");
$event->info2[] = array('name'=> $nameTraduction, 'value'=> $value);
}
}
if (isset($event->input)) {
if (is_array($event->input)) {
foreach ($event->input as $name => $value) {
if (is_string($value) && strlen($value) > 70) {
$event->input[$name] = substr($value, 0, 70)."...";
if ($event->output) {
$output = [];
$outputObject = json_decode($event->output);
if (is_array($outputObject)) {
foreach ($outputObject as $outputMessage) {
if (isset($outputMessage->message)) {
$outputMessage->message = $this->view->translator->getText($outputMessage->message, false, "audit/messages");
if (isset($outputMessage->variables)) {
$output[] = vsprintf($outputMessage->message, $outputMessage->variables);
} else {
$output[] = $outputMessage->message;
}
}
}
$event->output = $output;
} elseif (is_string($event->output)) {
$event->output = [$this->view->translator->getText($event->output, false, "audit/messages")];
}
}
$event->output = $this->view->translator->getText($event->output, false, "audit/messages");
$event->pathTraduction = $this->view->translator->getText($event->path, false, "audit/messages");
$this->view->setSource("event", $event);
$this->view->merge();
......
......@@ -72,7 +72,6 @@ class authentication
{
$view = $this->view;
$view->addHeaders();
$view->addContentFile("auth/userAccount/login/form.html");
$view->setSource('logo', $this->logoUri);
$view->translate();
......
......@@ -84,7 +84,6 @@ EOD
public function getlogSchedulings($logSchedulings)
{
$this->view->addContentFile("batchProcessing/logScheduling/result.html");
$this->view->translate();
$this->view->setSource("logSchedulings", $logSchedulings);
$this->view->merge();
......@@ -107,6 +106,27 @@ EOD
public function getlogScheduling($logScheduling)
{
$this->view->addContentFile("batchProcessing/logScheduling/modalEvent.html");
if ($logScheduling->info) {
$info = [];
$infoObject = json_decode($logScheduling->info);
if (is_array($infoObject)) {
foreach ($infoObject as $infoMessage) {
if (isset($infoMessage->message)) {
$infoMessage->message = $this->view->translator->getText($infoMessage->message, false, "audit/messages");
if (isset($infoMessage->variables)) {
$info[] = vsprintf($infoMessage->message, $infoMessage->variables);
} else {
$info[] = $infoMessage->message;
}
}
}
$logScheduling->info = $info;
} elseif (is_string($logScheduling->info)) {
$logScheduling->info = [$this->view->translator->getText($logScheduling->info, false, "audit/messages")];
}
}
$this->view->translate();
$this->view->setSource("logScheduling", $logScheduling);
......
......@@ -80,7 +80,7 @@ class orgTree
return strcmp($a->reference, $b->reference);
});
if(\laabs::getToken("ORGANIZATION")){
if(\laabs::getToken("ORGANIZATION") && \laabs::getToken("ORGANIZATION")->orgRoleCodes){
$addOrganizationRight = in_array('owner',\laabs::getToken("ORGANIZATION")->orgRoleCodes);
} else {
$addOrganizationRight = true;
......
......@@ -74,7 +74,7 @@ class log
$dataTable->setSorting(array(array(1, 'desc')));
foreach ($logs as $log) {
$log->type = $this->view->translator->getText($log->type, false, 'recordsManagement/log');
$log->typeTranslate = $this->view->translator->getText($log->type, false, 'recordsManagement/log');
$log->resId = \laabs::callService('recordsManagement/archives/readArchivecontents_archive_', (string) $log->archiveId)->digitalResources[0]->resId;
}
......@@ -85,6 +85,50 @@ class log
return $this->view->saveHtml();
}
/**
*
*/
public function contents($res)
{
$journal = explode(PHP_EOL, $res);
$id = str_getcsv($journal[1]);
$head = str_getcsv($journal[2]);
$events = [];
for ($i = 3; $i < count($journal) - 1; $i++) {
$events[] = str_getcsv($journal[$i]);
}
$type = $journal[0];
$typeTranslate = $this->view->translator->getText($type, false, 'recordsManagement/log');
$this->view->addContentFile("recordsManagement/log/view.html");
$this->view->translate();
$dataTable = $this->view->getElementsByClass("dataTable")->item(0)->plugin['dataTable'];
$dataTable->setPaginationType("full_numbers");
$dataTable->setSorting(array(array(0, 'desc')));
if ($type === "lifeCycle") {
$dataTable->setUnsortableColumns(2);
} else if ( $type === "application") {
for ($i = 0; $i < count($events); $i++) {
$events[$i][2] = $this->view->translator->getText($events[$i][2], false, 'audit/messages');
}
}
$this->view->setSource("archiveId", $id[0]);
$this->view->setSource("resourceId", $id[1]);
$this->view->setSource("type", $type);
$this->view->setSource("typeTranslate", $typeTranslate);
$this->view->setSource("head", $head);
$this->view->setSource("events", $events);
$this->view->merge();
return $this->view->saveHtml();
}
/**
* View log
* @param recordsManagement/log $log The log object
......
......@@ -155,6 +155,63 @@ msgstr "Compte de service"
msgid "No account found"
msgstr "Aucun compte trouvé"
msgid "Journal detail"
msgstr "Détail du journal"
msgid "Identifier"
msgstr "Identifiant"
msgid "To date"
msgstr "Date de début"
msgid "From date"
msgstr "Date de fin"
msgid "Download journal"
msgstr "Télécharger le journal (csv)"
msgid "Path"
msgstr "Route"
msgid "remoteIp"
msgstr "Ip distant"
msgid "New journal identifier : %s"
msgstr "Identifiant du journal archivé : %s"
msgid "Timestamp file generated"
msgstr "Fichier d'horodatage généré"
msgid "%s archives cheked"
msgstr "%s archives vérifiées"
msgid "%s archives are valid"
msgstr "%s archives sont valides"
msgid "%s archives are not valid"
msgstr "%s archives ne sont pas valides"
msgid "Invalid archive identifier : %s"
msgstr "Identifiant des archives invalides : %s"
msgid "Error on integrity check"
msgstr "Erreur lors de la vérification d'intégrité"
msgid "%s archive(s) to check"
msgstr "%s archive(s) à vérifier"
msgid "%s archive(s) in sample"
msgstr "%s archive(s) dans l'échantillon"
msgid "%s archive(s) checked"
msgstr "%s archive(s) vérifiée(s)"
msgid "%s message(s) validate"
msgstr "%s bordereau(x) validé(s)"
msgid "%s message(s) processed"
msgstr "%s bordereau(x) traité(s)"
msgid "recordsManagement/accessRule/readIndex"
msgstr "Lecture des règles de communicabilité"
......@@ -372,6 +429,9 @@ msgstr "Suppression d'un journal de l'application"
msgid "recordsManagement/log/form"
msgstr "Recherche des journaux de l'application"
msgid "recordsManagement/log/contents_type__archiveId__resourceId_"
msgstr "Lecture du detail d'un journal"
msgid "recordsManagement/retentionRule/readIndex"
msgstr "Lecture de toutes les règles de conservation"
......
<div class="container-fluid" data-translate-catalog="audit/messages">
<div class="row">
<div class="col-xs-12">
<h1 class="page-header">
Event
</h1>
<div class="container-fluid" data-translate-catalog="audit/messages">
<div class="row">
<div class="col-xs-12">
<h1 class="page-header">
Event
</h1>
</div>
</div>
</div>
<div class="container-fluid" data-translate-catalog="audit/messages">
<div class="row">
<div class="col-xs-12">
<div class="row">
<dl class="dl dl-horizontal">
<dt><strong>Date</strong></dt> <dd id="audit_eventDate"><?merge event.eventDate ?></dd>
<dt><strong>Account</strong></dt> <dd id="audit_account"><?merge event.accountId ?></dd>
<dt><strong>Event</strong></dt> <dd id="audit_event"><?merge event.pathTraduction ?></dd>
<dt><strong>Path</strong></dt> <dd id="audit_event"><?merge event.path ?></dd>
<dt><strong>Status</strong></dt>
<dd>
<?merge event.status.ifeq(1) ?><strong class="text-success fa fa-check" disabled="">&nbsp;</strong>
<?merge event.status.ifne(1) ?><strong class="text-danger fa fa-times" disabled="">&nbsp;</strong>
</dd>
</dl>
<div class="col-xs-12">
<?merge event.variables.bool() ?>
<div id="audit_variables">
<hr/>
<h4 class="col-xs-offset-1">Variables</h4>
<dl class='dl dl-horizontal'>
<?merge event.variables.parse(1) ?>
<span><dt><?merge event.variables.current().key()?></dt> <dd> <?merge . ?></dd></span>
</dl>
</div>
</div>
</div>
<div class="container-fluid" data-translate-catalog="audit/messages">
<div class="row">
<div class="col-xs-12">
<div class="panel">
<div class="row">
<dl class="dl dl-horizontal">
<dt><strong>Date</strong></dt> <dd id="audit_eventDate"><?merge event.eventDate ?></dd>
<dt><strong>Account</strong></dt> <dd id="audit_account"><?merge event.accountId ?></dd>
<dt><strong>Event</strong></dt> <dd id="audit_event"><?merge event.path ?></dd>
<dt><strong>Status</strong></dt>
<dd>
<