1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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);
}
}
|