diff options
Diffstat (limited to '')
-rw-r--r-- | library/Icinga/Application/Hook/AuditHook.php | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/library/Icinga/Application/Hook/AuditHook.php b/library/Icinga/Application/Hook/AuditHook.php new file mode 100644 index 0000000..e6209da --- /dev/null +++ b/library/Icinga/Application/Hook/AuditHook.php @@ -0,0 +1,123 @@ +<?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); + } +} |