GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#176)
by
unknown
04:55
created

Audit::__call()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 7
ccs 1
cts 1
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * This serves as both the Module for the MVC part of the audit and the configuration/entry point for the actual
4
 * audit process.
5
 *
6
 * @author    Steve Guns <[email protected]>
7
 * @package   com.bedezign.yii2.audit
8
 * @copyright 2014-2015 B&E DeZign
9
 */
10
11
namespace bedezign\yii2\audit;
12
13
use bedezign\yii2\audit\components\panels\Panel;
14
use bedezign\yii2\audit\models\AuditEntry;
15
use Yii;
16
use yii\base\ActionEvent;
17
use yii\base\Application;
18
use yii\base\InvalidConfigException;
19
use yii\base\InvalidParamException;
20
use yii\base\Module;
21
use yii\helpers\ArrayHelper;
22
23
/**
24
 * Audit main module.
25
 *
26
 * This module is also responsible for starting the audit process.
27
 * To configure it you need to do 2 things:
28
 * - add a module configuration entry:
29
 *     'modules' => [
30
 *        'audit' => 'bedezign\yii2\audit\Audit',
31
 *     ]
32
 *   or optionally with configuration:
33
 *     'modules' => [
34
 *        'audit' => [
35
 *            'class' => 'bedezign\yii2\audit\Audit',
36
 *            'ignoreActions' => ['debug/*']
37
 *     ]
38
 * - If you want to auto track actions, be sure to add the module to the application bootstrapping:
39
 *    'bootstrap' => ['audit'],
40
 *
41
 * @package bedezign\yii2\audit
42
 * @property AuditEntry $entry
43
 *
44
 * @method void data($type, $data)                                                                      @see ExtraDataPanel::trackData()
45
 * @method \bedezign\yii2\audit\models\AuditError exception(\Exception $exception)                      @see ErrorPanel::log()
46
 * @method \bedezign\yii2\audit\models\AuditError errorMessage($message, $code, $file, $line, $trace)   @see ErrorPanel::logMessage()
47
 */
48
class Audit extends Module
49
{
50
    /**
51
     * @var string|boolean the layout that should be applied for views within this module. This refers to a view name
52
     * relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]]
53
     * will be taken. If this is false, layout will be disabled within this module.
54
     */
55
    public $layout = 'main';
56
57
    /**
58
     * @var string name of the component to use for database access
59
     */
60
    public $db = 'db';
61
62
    /**
63
     * @var string[] Action or list of actions to track. '*' is allowed as the first or last character to use as wildcard.
64
     */
65
    public $trackActions = ['*'];
66
67
    /**
68
     * @var string[] Action or list of actions to ignore. '*' is allowed as the first or last character to use as wildcard (eg 'debug/*').
69
     */
70
    public $ignoreActions = [];
71
72
    /**
73
     * @var int Maximum age (in days) of the audit entries before they are truncated
74
     */
75
    public $maxAge = null;
76
77
    /**
78
     * @var string[] IP address or list of IP addresses with access to the viewer, null for everyone (if the IP matches)
79
     * An IP address can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix.
80
     * For example, '192.168.*' matches all IP addresses in the segment '192.168.'.
81
     */
82
    public $accessIps = null;
83
84
    /**
85
     * @var string[] Role or list of roles with access to the viewer, null for everyone (if the user matches)
86
     */
87
    public $accessRoles = ['admin'];
88
89
    /**
90
     * @var int[] User ID or list of user IDs with access to the viewer, null for everyone (if the role matches)
91
     */
92
    public $accessUsers = null;
93
94
    /**
95
     * @var bool Compress extra data generated or just keep in text? For people who don't like binary data in the DB
96
     */
97
    public $compressData = true;
98
99
    /**
100
     * @var string The callback to use to convert a user id into an identifier (username, email, ...). Can also be html.
101
     */
102
    public $userIdentifierCallback = false;
103
104
    /**
105
     * @var string The callback to get a user id.
106
     */
107
    public $userIdCallback = false;
108
109
    /**
110
     * @var string Will be called to translate text in the user filter into a (or more) user id's
111
     */
112
    public $userFilterCallback = false;
113
114
    /**
115
     * @var bool The module does batch saving of the data records by default. You can disable this if you are experiencing
116
     * `max_allowed_packet` errors when logging huge data quantities. Records will be saved per piece instead of all at once
117
     */
118
    public $batchSave = true;
119
120
    /**
121
     * @var array|Panel[] list of panels that should be active/tracking/available during the auditing phase.
122
     * If the value is a simple string, it is the identifier of an internal panel to activate (with default settings)
123
     * If the entry is a '<key>' => '<string>|<array>' it is either a new panel or a panel override (if you specify a core id).
124
     * It is important that the key is unique, as this is the identifier used to store any data associated with the panel.
125
     *
126
     * Please note:
127
     * - If you just want to change the configuration for a core panel, use the `$panelConfiguration`, it will be merged into this one
128
     * - If you add custom panels, please namespace them ("mynamespace/panelname").
129
     */
130
    public $panels = [
131
        'audit/request',
132
        'audit/db',
133
        'audit/log',
134
        'audit/mail',
135
        'audit/profiling',
136
        'audit/trail',
137
        'audit/javascript',
138
        // 'audit/asset',
139
        // 'audit/config',
140
141
        // These provide special functionality and get loaded to activate it
142
        'audit/error',      // Links the extra error reporting functions (`exception()` and `errorMessage()`)
143
        'audit/extra',      // Links the data functions (`data()`)
144
        'audit/curl',       // Links the curl tracking function (`curlBegin()`, `curlEnd()` and `curlExec()`)
145
    ];
146
147
    /**
148
     * Everything you add in here will be merged with the basic panel configuration.
149
     * This gives you an easy way to just add or modify panels/configurations without having to re-specify every panel.
150
     * This only accepts regular definitions ('<key>' => '<array>'), but the core class will be added if needed
151
     * Take a look at the [module configuration](docs/module-configuration.md) for more information.
152
     */
153
    public $panelsMerge = [];
154
155
    /**
156
     * @var LogTarget
157
     */
158
    public $logTarget;
159
160
    /**
161
     * @var array
162
     */
163
    private $_corePanels = [
164
        // Tracking/logging panels
165
        'audit/request'    => ['class' => 'bedezign\yii2\audit\panels\RequestPanel'],
166
        'audit/db'         => ['class' => 'bedezign\yii2\audit\panels\DbPanel'],
167
        'audit/log'        => ['class' => 'bedezign\yii2\audit\panels\LogPanel'],
168
        'audit/asset'      => ['class' => 'bedezign\yii2\audit\panels\AssetPanel'],
169
        'audit/config'     => ['class' => 'bedezign\yii2\audit\panels\ConfigPanel'],
170
        'audit/profiling'  => ['class' => 'bedezign\yii2\audit\panels\ProfilingPanel'],
171
172
        // Special other panels
173
        'audit/error'      => ['class' => 'bedezign\yii2\audit\panels\ErrorPanel'],
174
        'audit/javascript' => ['class' => 'bedezign\yii2\audit\panels\JavascriptPanel'],
175
        'audit/trail'      => ['class' => 'bedezign\yii2\audit\panels\TrailPanel'],
176
        'audit/mail'       => ['class' => 'bedezign\yii2\audit\panels\MailPanel'],
177
        'audit/extra'      => ['class' => 'bedezign\yii2\audit\panels\ExtraDataPanel'],
178
        'audit/curl'       => ['class' => 'bedezign\yii2\audit\panels\CurlPanel'],
179
        'audit/soap'       => ['class' => 'bedezign\yii2\audit\panels\SoapPanel'],
180
    ];
181
182
    /**
183
     * @var array
184
     */
185
    private $_panelFunctions = [];
186
187
    /**
188
     * @var \bedezign\yii2\audit\models\AuditEntry If activated this is the active entry
189
     */
190
    private $_entry = null;
191 280
192
    /**
193 114
     * @throws InvalidConfigException
194 280
     */
195
    public function init()
196
    {
197 114
        parent::init();
198
        $app = Yii::$app;
199 114
200
        // Before action triggers a new audit entry
201
        $app->on(Application::EVENT_BEFORE_ACTION, [$this, 'onBeforeAction']);
202 114
        // After request finalizes the audit entry.
203
        $app->on(Application::EVENT_AFTER_REQUEST, [$this, 'onAfterRequest']);
204
205 114
        // Activate the logging target
206 114
        $this->logTarget = $app->getLog()->targets['audit'] = new LogTarget($this);
207 114
208
        // Boot all active panels
209
        $this->normalizePanelConfiguration();
210
        $this->panels = $this->loadPanels(array_keys($this->panels));
211
    }
212
213 54
    /**
214
     * Called to evaluate if the current request should be logged
215 42
     * @param ActionEvent $event
216 9
     */
217
    public function onBeforeAction($event)
218 33
    {
219 42
        if (!empty($this->trackActions) && !$this->routeMatches($event->action->uniqueId, $this->trackActions)) {
220
            return;
221
        }
222 13
        if (!empty($this->ignoreActions) && $this->routeMatches($event->action->uniqueId, $this->ignoreActions)) {
223 39
            return;
224
        }
225
        // Still here, start audit
226
        $this->getEntry(true);
227
    }
228 50
229
    /**
230 50
     *
231 18
     */
232 18
    public function onAfterRequest()
233 50
    {
234
        if ($this->_entry) {
235
            $this->_entry->finalize();
236
        }
237
    }
238
239
    /**
240 114
     * Allows panels to register functions that can be called directly on the module
241
     * @param string    $name
242 114
     * @param callable  $callback
243 114
     */
244
    public function registerFunction($name, $callback)
245 114
    {
246 114
        if (isset($this->_panelFunctions[$name]))
247
            throw new InvalidParamException("The '$name'-function has already been defined.");
248
249
        $this->_panelFunctions[$name] = $callback;
250
    }
251
252
    /**
253
     * @param \yii\debug\Panel $panel
254
     */
255
    public function registerPanel(\yii\debug\Panel $panel)
256
    {
257
        $this->panels[$panel->id] = $panel;
258
    }
259
260
    /**
261 21
     * @param string $name
262
     * @param array $params
263 21
     * @return mixed
264 7
     */
265
    public function __call($name, $params)
266 7
    {
267
        if (!isset($this->_panelFunctions[$name]))
268
            throw new \yii\base\InvalidCallException("Unknown panel function '$name'");
269
270
        return call_user_func_array($this->_panelFunctions[$name], $params);
271
    }
272 70
273
    /**
274 70
     * @return \yii\db\Connection the database connection.
275
     */
276
    public function getDb()
277
    {
278
        return Yii::$app->{$this->db};
279
    }
280
281
    /**
282 91
     * @param bool $create
283
     * @param bool $new
284 91
     * @return AuditEntry|static
285 39
     */
286 39
    public function getEntry($create = false, $new = false)
287 91
    {
288 32
        $entry = new AuditEntry();
289
        $tableSchema = $entry->getDb()->schema->getTableSchema($entry->tableName());
290
        if ($tableSchema) {
291
            if ((!$this->_entry && $create) || $new) {
292
                $this->_entry = AuditEntry::create(true);
293
            }
294 14
        }
295
        return $this->_entry;
296 14
    }
297
298
    /**
299 12
     * @param $user_id
300 1
     * @return string
301
     */
302 11
    public function getUserIdentifier($user_id)
303
    {
304
        if (!$user_id) {
305
            return Yii::t('audit', 'Guest');
306
        }
307
        if ($this->userIdentifierCallback && is_callable($this->userIdentifierCallback)) {
308
            return call_user_func($this->userIdentifierCallback, $user_id);
309 1
        }
310
        return $user_id;
311 1
    }
312
313
    /**
314
     * @return int|mixed|null|string
315
     */
316
    public function getUserId()
317
    {
318
        if ($this->userIdCallback && is_callable($this->userIdCallback)) {
319 280
            return call_user_func($this->userIdCallback);
320
        }
321 280
        return (Yii::$app instanceof Application && Yii::$app->user) ? Yii::$app->user->id : null;
322 114
    }
323 114
324 114
    /**
325 280
     * Returns a list of all available panel identifiers
326
     * @return string[]
327
     */
328
    public function getPanelIdentifiers()
329
    {
330
        return array_unique(array_merge(array_keys($this->panels), array_keys($this->_corePanels)));
331
    }
332
333 142
    /**
334
     * Tries to assemble the configuration for the panels that the user wants for auditing
335 114
     * @param string[]          Set of panel identifiers that should be loaded
336 114
     * @return Panel[]
337 114
     */
338 1
    public function loadPanels($list)
339 1
    {
340
        $panels = [];
341 114
        foreach ($list as $panel) {
342 114
            $panels[$panel] = $this->getPanel($panel);
343
        }
344 114
        return $panels;
345 114
    }
346 114
347 114
    /**
348
     * @param string $identifier
349
     * @return null|Panel
350 46
     * @throws InvalidConfigException
351
     */
352
    public function getPanel($identifier)
353
    {
354
        $config = null;
355
        if (isset($this->panels[$identifier]))
356
            $config = $this->panels[$identifier];
357 280
        elseif (isset($this->_corePanels[$identifier]))
358
            $config = $this->_corePanels[$identifier];
359 280
360 280
        if (!$config)
361 114
            throw new InvalidConfigException("'$identifier' is not a valid panel identifier");
362
363 114
        if (is_array($config)) {
364 114
            $config['module'] = $this;
365 114
            $config['id'] = $identifier;
366 114
            return Yii::createObject($config);
367
        }
368
369
        return $config;
370
    }
371 114
372 114
    /**
373
     * Make sure the configured panels array is a uniform set of <identifier> => <config> entries.
374
     * @throws InvalidConfigException
375 114
     */
376 114
    protected function normalizePanelConfiguration()
377
    {
378
        $panels = [];
379
        foreach ($this->panels as $key => $value) {
380
            if (is_numeric($key)) {
381
                // The $value contains the identifier of a core panel
382 114
                if (!isset($this->_corePanels[$value]))
383 280
                    throw new InvalidConfigException("'$value' is not a valid panel identifier");
384
                $panels[$value] = $this->_corePanels[$value];
385
            }
386
            else {
387
                // The key contains the identifier and the value is either a class name or a full array
388 280
                $panels[$key] = is_string($value) ? ['class' => $value] : $value;
389
            }
390 274
        }
391 278
        $this->panels = ArrayHelper::merge($panels, $this->panelsMerge);
392 114
393 114
        // We now need one more iteration to add core classes to the panels added via the merge, if needed
394 114
        array_walk($this->panels, function(&$value, $key) {
395 280
           if (!isset($value['class'])) {
396 280
               if (isset($this->_corePanels[$key]))
397 114
                   $value = ArrayHelper::merge($value, $this->_corePanels[$key]);
398
               else
399 165
                   throw new InvalidConfigException("Invalid configuration for '$key'. No 'class' specified.");
400
           }
401 114
        });
402 114
    }
403 278
404 162
    /**
405 6
     * @return int|null|string
406
     */
407
    public static function findModuleIdentifier()
408
    {
409
        foreach (Yii::$app->modules as $name => $module) {
410
            $class = null;
411
            if (is_string($module))
412 27
                $class = $module;
413
            elseif (is_array($module)) {
414 9
                if (isset($module['class']))
415 27
                    $class = $module['class'];
416 9
            } else
417 27
                /** @var Module $module */
418
                $class = $module::className();
419 9
420
            $parts = explode('\\', $class);
421
            if ($class && strtolower(end($parts)) == 'audit')
422
                return $name;
423
        }
424
        return null;
425
    }
426
427
    /**
428
     * @param string $className
429
     * @return bool|string
430
     */
431
    public static function findPanelIdentifier($className)
432 64
    {
433
        $audit = Audit::getInstance();
434 28
        foreach ($audit->panels as $panel) {
435 28
            if ($panel->className() == $className) {
436 28
                return $panel->id;
437 56
            }
438 18
        }
439 18
        return false;
440 22
    }
441 8
442
    /**
443 62
     * Verifies a route against a given list and returns whether it matches or not.
444 6
     * Entries in the list are allowed to end with a '*', which means that a substring will be used for the match
445 6
     * instead of a full compare.
446 14
     *
447 2
     * @param string $route An application rout
448
     * @param string[] $list List of routes to compare against.
449 50
     * @return bool
450 26
     */
451 36
    protected function routeMatches($route, $list)
452 36
    {
453
        $list = ArrayHelper::toArray($list);
454
        foreach ($list as $compare) {
455
            $len = strlen($compare);
456
            if ($compare[$len - 1] == '*') {
457
                $compare = rtrim($compare, '*');
458
                if (substr($route, 0, $len - 1) === $compare)
459
                    return true;
460
            }
461
462
            if ($compare[0] == '*') {
463
                $compare = ltrim($compare, '*');
464
                if (substr($route, - ($len - 1)) === $compare)
465
                    return true;
466
            }
467
468
            if ($route === $compare)
469
                return true;
470
        }
471
        return false;
472
    }
473
474
}
475