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.
Passed
Push — master ( 1db939...f19805 )
by Steve
05:44
created

Audit::shouldTrack()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
nc 12
nop 2
dl 0
loc 14
ccs 6
cts 6
cp 1
crap 7
rs 8.8333
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 string[] Action or list of actions to track if they cause an error. '*' is allowed as the first or last character to use as wildcard.
74
     */
75
    public $trackErrorActions = ['*'];
76
77
    /**
78
     * @var string[] Action or list of actions to ignore if they cause an error. '*' is allowed as the first or last character to use as wildcard (eg 'debug/*').
79
     */
80
    public $ignoreErrorActions = [];
81
82
    /**
83
     * @var int Maximum age (in days) of the audit entries before they are truncated
84
     */
85
    public $maxAge = null;
86
87
    /**
88
     * @var string[] IP address or list of IP addresses with access to the viewer, null for everyone (if the IP matches)
89
     * An IP address can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix.
90
     * For example, '192.168.*' matches all IP addresses in the segment '192.168.'.
91
     */
92
    public $accessIps = null;
93
94
    /**
95
     * @var string[] Role or list of roles with access to the viewer, null for everyone (if the user matches)
96
     */
97
    public $accessRoles = ['admin'];
98
99
    /**
100
     * @var int[] User ID or list of user IDs with access to the viewer, null for everyone (if the role matches)
101
     */
102
    public $accessUsers = null;
103
104
    /**
105
     * @var bool Compress extra data generated or just keep in text? For people who don't like binary data in the DB
106
     */
107
    public $compressData = true;
108
109
    /**
110
     * @var string The callback to use to convert a user id into an identifier (username, email, ...). Can also be html.
111
     */
112
    public $userIdentifierCallback = false;
113
114
    /**
115
     * @var string The callback to get a user id.
116
     */
117
    public $userIdCallback = false;
118
119
    /**
120
     * @var string Will be called to translate text in the user filter into a (or more) user id's
121
     */
122
    public $userFilterCallback = false;
123
124
    /**
125
     * @var bool The module does batch saving of the data records by default. You can disable this if you are experiencing
126
     * `max_allowed_packet` errors when logging huge data quantities. Records will be saved per piece instead of all at once
127
     */
128
    public $batchSave = true;
129
130
    /**
131
     * @var array Default log levels to filter and process
132
     */
133
    public $logConfig = ['levels' => ['error', 'warning', 'info', 'profile']];
134
135
136
    /**
137
     * @var array|Panel[] list of panels that should be active/tracking/available during the auditing phase.
138
     * If the value is a simple string, it is the identifier of an internal panel to activate (with default settings)
139
     * If the entry is a '<key>' => '<string>|<array>' it is either a new panel or a panel override (if you specify a core id).
140
     * It is important that the key is unique, as this is the identifier used to store any data associated with the panel.
141
     *
142
     * Please note:
143
     * - If you just want to change the configuration for a core panel, use the `$panelConfiguration`, it will be merged into this one
144
     * - If you add custom panels, please namespace them ("mynamespace/panelname").
145
     */
146
    public $panels = [
147
        'audit/request',
148
        'audit/db',
149
        'audit/log',
150
        'audit/mail',
151
        'audit/profiling',
152
        'audit/trail',
153
        'audit/javascript',
154
        // 'audit/asset',
155
        // 'audit/config',
156
157
        // These provide special functionality and get loaded to activate it
158
        'audit/error',      // Links the extra error reporting functions (`exception()` and `errorMessage()`)
159
        'audit/extra',      // Links the data functions (`data()`)
160
        'audit/curl',       // Links the curl tracking function (`curlBegin()`, `curlEnd()` and `curlExec()`)
161
    ];
162
163
    /**
164
     * Everything you add in here will be merged with the basic panel configuration.
165
     * This gives you an easy way to just add or modify panels/configurations without having to re-specify every panel.
166
     * This only accepts regular definitions ('<key>' => '<array>'), but the core class will be added if needed
167
     * Take a look at the [module configuration](docs/module-configuration.md) for more information.
168
     */
169
    public $panelsMerge = [];
170
171
    /**
172
     * @var LogTarget
173
     */
174
    public $logTarget;
175
176
    // Things required to keep the module yii2-debug compatible
177
    /* @see \yii\debug\Module::$traceLine (since 2.0.7) */
178
    public $traceLine = \yii\debug\Module::DEFAULT_IDE_TRACELINE;
179
     /* @see \yii\debug\Module::$tracePathMappings (since 2.1.6) */
180
    public $tracePathMappings = [];
181
182
    /**
183
     * @var array
184
     */
185
    private $_corePanels = [
186
        // Tracking/logging panels
187
        'audit/request'    => ['class' => 'bedezign\yii2\audit\panels\RequestPanel'],
188
        'audit/db'         => ['class' => 'bedezign\yii2\audit\panels\DbPanel'],
189
        'audit/log'        => ['class' => 'bedezign\yii2\audit\panels\LogPanel'],
190
        'audit/asset'      => ['class' => 'bedezign\yii2\audit\panels\AssetPanel'],
191
        'audit/config'     => ['class' => 'bedezign\yii2\audit\panels\ConfigPanel'],
192
        'audit/profiling'  => ['class' => 'bedezign\yii2\audit\panels\ProfilingPanel'],
193
194
        // Special other panels
195
        'audit/error'      => ['class' => 'bedezign\yii2\audit\panels\ErrorPanel'],
196
        'audit/javascript' => ['class' => 'bedezign\yii2\audit\panels\JavascriptPanel'],
197
        'audit/trail'      => ['class' => 'bedezign\yii2\audit\panels\TrailPanel'],
198
        'audit/mail'       => ['class' => 'bedezign\yii2\audit\panels\MailPanel'],
199
        'audit/extra'      => ['class' => 'bedezign\yii2\audit\panels\ExtraDataPanel'],
200
        'audit/curl'       => ['class' => 'bedezign\yii2\audit\panels\CurlPanel'],
201
        'audit/soap'       => ['class' => 'bedezign\yii2\audit\panels\SoapPanel'],
202
    ];
203
204
    /**
205
     * @var array
206 78
     */
207
    private $_panelFunctions = [];
208 78
209 78
    /**
210
     * @var \bedezign\yii2\audit\models\AuditEntry If activated this is the active entry
211
     */
212
    private $_entry = null;
213 78
214 78
    private static $_me = null;
215
216
    /**
217
     * @throws InvalidConfigException
218
     */
219 78
    public function init()
220
    {
221 78
        parent::init();
222
        $app = Yii::$app;
223
224 78
        // check if the module has been installed (prevents errors while installing)
225 75
        try {
226 75
            $this->getDb()->getTableSchema(AuditEntry::tableName());
227 3
        } catch (\Exception $e) {
228
            return;
229
        }
230
231 78
        // Before action triggers a new audit entry
232 78
        $app->on(Application::EVENT_BEFORE_ACTION, [$this, 'onBeforeAction']);
233 78
        // After request finalizes the audit entry.
234
        $app->on(Application::EVENT_AFTER_REQUEST, [$this, 'onAfterRequest']);
235
236
        $this->activateLogTarget();
237
238
        // Boot all active panels
239 84
        $this->normalizePanelConfiguration();
240
        $this->panels = $this->loadPanels(array_keys($this->panels));
241 84
    }
242 9
243
    public function shouldTrack($event, $isError = false)
244 75
    {
245 54
        $trackActions = $isError ? $this->trackErrorActions : $this->trackActions;
246
        $ignoreActions = $isError ? $this->ignoreErrorActions : $this->ignoreActions;
247
248 39
        if (!empty($trackActions) && !$this->routeMatches($event->action->uniqueId, $trackActions)) {
249 39
            return false;
250
        }
251
        if (!empty($ignoreActions) && $this->routeMatches($event->action->uniqueId, $ignoreActions)) {
252
            return false;
253
        }
254 57
255
        return true;
256 57
    }
257 57
258 57
    /**
259 57
     * Called to evaluate if the current request should be logged
260
     * @param ActionEvent $event
261
     */
262
    public function onBeforeAction($event)
263
    {
264
        if (!$this->shouldTrack($event)) {
265
            return;
266 78
        }
267
268 78
        // Still here, start audit
269 78
        $this->getEntry(true);
270
    }
271 78
272 78
    /**
273
     *
274
     */
275
    public function onAfterRequest()
276
    {
277
        if ($this->_entry) {
278
            $this->_entry->finalize();
279
        }
280
    }
281
282
    /**
283
     * Allows panels to register functions that can be called directly on the module
284
     * @param string    $name
285
     * @param callable  $callback
286
     */
287 21
    public function registerFunction($name, $callback)
288
    {
289 21
        if (isset($this->_panelFunctions[$name]))
290 21
            throw new InvalidParamException("The '$name'-function has already been defined.");
291
292 21
        $this->_panelFunctions[$name] = $callback;
293
    }
294
295
    public function hasMethod($name, $checkBehaviors = true)
296
    {
297
        if (isset($this->_panelFunctions[$name])) {
298 237
            return true;
299
        }
300 237
        return parent::hasMethod($name, $checkBehaviors);
301
    }
302
303
    /**
304
     * @param \yii\debug\Panel $panel
305
     */
306
    public function registerPanel(\yii\debug\Panel $panel)
307
    {
308 171
        $this->panels[$panel->id] = $panel;
309
    }
310 171
311 171
    /**
312 171
     * @param string $name
313 171
     * @param array $params
314 117
     * @return mixed
315 117
     */
316 171
    public function __call($name, $params)
317 171
    {
318
        if (!isset($this->_panelFunctions[$name]))
319
            throw new \yii\base\InvalidCallException("Unknown panel function '$name'");
320
321
        return call_user_func_array($this->_panelFunctions[$name], $params);
322
    }
323
324 18
    public function activateLogTarget()
325
    {
326 18
        $app = Yii::$app;
327
328
        // Activate the logging target
329 18
        if (empty($app->getLog()->targets['audit'])) {
330 3
            $this->logTarget = $app->getLog()->targets['audit'] = new LogTarget($this, $this->logConfig);
331
        } else {
332 15
            $this->logTarget = $app->getLog()->targets['audit'];
333
        }
334
    }
335
336
    /**
337
     * @return \yii\db\Connection the database connection.
338 120
     */
339
    public function getDb()
340 120
    {
341 3
        return Yii::$app->{$this->db};
342
    }
343 117
344
    public static function getInstance()
345
    {
346
        if (static::$_me) {
0 ignored issues
show
Bug introduced by
Since $_me is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $_me to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
347
            return self::$_me;
348
        }
349
350 3
        // This code assumes the audit module is already loaded and can thus look for a derived instance
351
        $loadedModules = Yii::$app->loadedModules;
352 3
        foreach ($loadedModules as $module) {
353
             if ($module instanceof self) {
354
                 return self::$_me = $module;
355
             }
356
        }
357
358
        // If we're still here, fall back onto the default implementation
359
        return parent::getInstance();
360 78
    }
361
362 78
363 78
    /**
364 78
     * @param bool $create
365 78
     * @param bool $new
366 78
     * @return AuditEntry
367
     */
368
    public function getEntry($create = false, $new = false)
369
    {
370
        $entry = new AuditEntry();
371
        $tableSchema = $entry->getDb()->schema->getTableSchema($entry->tableName());
372
        if ($tableSchema) {
373
            if ((!$this->_entry && $create) || $new) {
374 114
                $this->_entry = AuditEntry::create(true);
375
            }
376 114
        }
377 114
        return $this->_entry;
378 114
    }
379 3
380 3
    /**
381
     * @param AuditEntry $entry
382 114
     */
383 114
    public function setEntry($entry)
384
    {
385 114
        $this->_entry = $entry;
386 78
    }
387 78
388 78
    /**
389
     * @param $user_id
390
     * @return string
391 54
     */
392
    public function getUserIdentifier($user_id)
393
    {
394
        if (!$user_id) {
395
            return Yii::t('audit', 'Guest');
396
        }
397
        if ($this->userIdentifierCallback && is_callable($this->userIdentifierCallback)) {
398 78
            return call_user_func($this->userIdentifierCallback, $user_id);
399
        }
400 78
        return $user_id;
401 78
    }
402 78
403
    /**
404 78
     * @return int|mixed|null|string
405 78
     */
406 78
    public function getUserId()
407 78
    {
408
        if ($this->userIdCallback && is_callable($this->userIdCallback)) {
409
            return call_user_func($this->userIdCallback);
410
        }
411
        return (Yii::$app instanceof \yii\web\Application && Yii::$app->user) ? Yii::$app->user->id : null;
412 78
    }
413 78
414
    /**
415
     * Returns a list of all available panel identifiers
416 78
     * @return string[]
417 78
     */
418
    public function getPanelIdentifiers()
419
    {
420
        return array_unique(array_merge(array_keys($this->panels), array_keys($this->_corePanels)));
421
    }
422
423 78
    /**
424 78
     * Tries to assemble the configuration for the panels that the user wants for auditing
425
     * @param string[]          Set of panel identifiers that should be loaded
426
     * @return Panel[]
427
     */
428
    public function loadPanels($list)
429 87
    {
430
        $panels = [];
431 87
        foreach ($list as $panel) {
432 81
            $panels[$panel] = $this->getPanel($panel);
433 81
        }
434 81
        return $panels;
435 81
    }
436 78
437 78
    /**
438 78
     * @param string $identifier
439
     * @return null|Panel
440 3
     * @throws InvalidConfigException
441
     */
442 81
    public function getPanel($identifier)
443 81
    {
444 81
        $config = null;
445 6
        if (isset($this->panels[$identifier]))
446 6
            $config = $this->panels[$identifier];
447
        elseif (isset($this->_corePanels[$identifier]))
448
            $config = $this->_corePanels[$identifier];
449
450
        if (!$config)
451
            throw new InvalidConfigException("'$identifier' is not a valid panel identifier");
452
453 27
        if (is_array($config)) {
454
            $config['module'] = $this;
455 27
            $config['id'] = $identifier;
456 27
            return Yii::createObject($config);
457 27
        }
458 27
459
        return $config;
460 27
    }
461
462
    /**
463
     * Make sure the configured panels array is a uniform set of <identifier> => <config> entries.
464
     * @throws InvalidConfigException
465
     */
466
    protected function normalizePanelConfiguration()
467
    {
468
        $panels = [];
469
        foreach ($this->panels as $key => $value) {
470
            if (is_numeric($key)) {
471
                // The $value contains the identifier of a core panel
472
                if (!isset($this->_corePanels[$value]))
473 84
                    throw new InvalidConfigException("'$value' is not a valid panel identifier");
474
                $panels[$value] = $this->_corePanels[$value];
475 84
            }
476 84
            else {
477 84
                // The key contains the identifier and the value is either a class name or a full array
478 84
                $panels[$key] = is_string($value) ? ['class' => $value] : $value;
479 54
            }
480 54
        }
481 54
        $this->panels = ArrayHelper::merge($panels, $this->panelsMerge);
482 24
483
        // We now need one more iteration to add core classes to the panels added via the merge, if needed
484 78
        array_walk($this->panels, function(&$value, $key) {
485 18
           if (!isset($value['class'])) {
486 18
               if (isset($this->_corePanels[$key]))
487 18
                   $value = ArrayHelper::merge($value, $this->_corePanels[$key]);
488 6
               else
489
                   throw new InvalidConfigException("Invalid configuration for '$key'. No 'class' specified.");
490 66
           }
491 66
        });
492 60
    }
493 36
494
    /**
495
     * @return int|null|string
496
     */
497
    public static function findModuleIdentifier()
498
    {
499
        foreach (Yii::$app->modules as $name => $module) {
500
            $class = null;
501
            if (is_string($module))
502
                $class = $module;
503
            elseif (is_array($module)) {
504
                if (isset($module['class']))
505
                    $class = $module['class'];
506
            } else
507
                /** @var Module $module */
508
                $class = $module::className();
509
510
            $parts = explode('\\', $class);
511
            if ($class && strtolower(end($parts)) == 'audit')
512
                return $name;
513
        }
514
        return null;
515
    }
516
517
    /**
518
     * @param string $className
519
     * @return bool|string
520
     */
521
    public static function findPanelIdentifier($className)
522
    {
523
        $audit = Audit::getInstance();
524
        foreach ($audit->panels as $panel) {
525
            if ($panel->className() == $className) {
526
                return $panel->id;
527
            }
528
        }
529
        return false;
530
    }
531
532
    /**
533
     * Verifies a route against a given list and returns whether it matches or not.
534
     * Entries in the list are allowed to end with a '*', which means that a substring will be used for the match
535
     * instead of a full compare.
536
     *
537
     * @param string $route An application rout
538
     * @param string[] $list List of routes to compare against.
539
     * @return bool
540
     */
541
    protected function routeMatches($route, $list)
542
    {
543
        $list = ArrayHelper::toArray($list);
544
        foreach ($list as $compare) {
545
            $len = strlen($compare);
546
            if ($compare[$len - 1] == '*') {
547
                $compare = rtrim($compare, '*');
548
                if (substr($route, 0, $len - 1) === $compare)
549
                    return true;
550
            }
551
552
            if ($compare[0] == '*') {
553
                $compare = ltrim($compare, '*');
554
                if (substr($route, - ($len - 1)) === $compare)
555
                    return true;
556
            }
557
558
            if ($route === $compare)
559
                return true;
560
        }
561
        return false;
562
    }
563
564
}
565