124 lines
4.3 KiB
PHP
124 lines
4.3 KiB
PHP
<?php
|
|
/* Icinga Web 2 | (c) 2018 Icinga Development Team | GPLv2+ */
|
|
|
|
namespace Icinga\Application\Hook;
|
|
|
|
use Exception;
|
|
use InvalidArgumentException;
|
|
use Icinga\Authentication\Auth;
|
|
use Icinga\Application\Hook;
|
|
use Icinga\Application\Logger;
|
|
|
|
abstract class AuditHook
|
|
{
|
|
/**
|
|
* Log an activity to the audit log
|
|
*
|
|
* Propagates the given message details to all known hook implementations.
|
|
*
|
|
* @param string $type An arbitrary name identifying the type of activity
|
|
* @param string $message A detailed description possibly referencing parameters in $data
|
|
* @param array $data Additional information (How this is stored or used is up to each implementation)
|
|
* @param string $identity An arbitrary name identifying the responsible subject, defaults to the current user
|
|
* @param int $time A timestamp defining when the activity occurred, defaults to now
|
|
*/
|
|
public static function logActivity($type, $message, array $data = null, $identity = null, $time = null)
|
|
{
|
|
if (! Hook::has('audit')) {
|
|
return;
|
|
}
|
|
|
|
if ($identity === null) {
|
|
$identity = Auth::getInstance()->getUser()->getUsername();
|
|
}
|
|
|
|
if ($time === null) {
|
|
$time = time();
|
|
}
|
|
|
|
foreach (Hook::all('audit') as $hook) {
|
|
/** @var self $hook */
|
|
try {
|
|
$formattedMessage = $message;
|
|
if ($data !== null) {
|
|
// Calling formatMessage on each hook is intended and allows
|
|
// intercepting message formatting while keeping it implicit
|
|
$formattedMessage = $hook->formatMessage($message, $data);
|
|
}
|
|
|
|
$hook->logMessage($time, $identity, $type, $formattedMessage, $data);
|
|
} catch (Exception $e) {
|
|
Logger::error(
|
|
'Failed to propagate audit message to hook "%s". An error occurred: %s',
|
|
get_class($hook),
|
|
$e
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log a message to the audit log
|
|
*
|
|
* @param int $time A timestamp defining when the activity occurred
|
|
* @param string $identity An arbitrary name identifying the responsible subject
|
|
* @param string $type An arbitrary name identifying the type of activity
|
|
* @param string $message A detailed description of the activity
|
|
* @param array $data Additional activity information
|
|
*/
|
|
abstract public function logMessage($time, $identity, $type, $message, array $data = null);
|
|
|
|
/**
|
|
* Substitute the given message with its accompanying data
|
|
*
|
|
* @param string $message
|
|
* @param array $messageData
|
|
*
|
|
* @return string
|
|
*/
|
|
public function formatMessage($message, array $messageData)
|
|
{
|
|
return preg_replace_callback('/{{(.+?)}}/', function ($match) use ($messageData) {
|
|
return $this->extractMessageValue(explode('.', $match[1]), $messageData);
|
|
}, $message);
|
|
}
|
|
|
|
/**
|
|
* Extract the given value path from the given message data
|
|
*
|
|
* @param array $path
|
|
* @param array $messageData
|
|
*
|
|
* @return mixed
|
|
*
|
|
* @throws InvalidArgumentException In case of an invalid or missing format parameter
|
|
*/
|
|
protected function extractMessageValue(array $path, array $messageData)
|
|
{
|
|
$key = array_shift($path);
|
|
if (array_key_exists($key, $messageData)) {
|
|
$value = $messageData[$key];
|
|
} else {
|
|
throw new InvalidArgumentException("Missing format parameter '$key'");
|
|
}
|
|
|
|
if (empty($path)) {
|
|
if (! is_scalar($value)) {
|
|
throw new InvalidArgumentException(
|
|
'Invalid format parameter. Expected scalar for path "' . join('.', $path) . '".'
|
|
. ' Got "' . gettype($value) . '" instead'
|
|
);
|
|
}
|
|
|
|
return $value;
|
|
} elseif (! is_array($value)) {
|
|
throw new InvalidArgumentException(
|
|
'Invalid format parameter. Expected array for path "'. join('.', $path) . '".'
|
|
. ' Got "' . gettype($value) . '" instead'
|
|
);
|
|
}
|
|
|
|
return $this->extractMessageValue($path, $value);
|
|
}
|
|
}
|