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
Push — master ( 4b1269...268271 )
by Brett
07:34
created

Audit::onBeforeAction()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 6
nc 3
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 5
rs 8.8571
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 Default log levels to filter and process
122
     */
123
    public $logConfig = ['levels' => ['error', 'warning', 'info', 'profile']];
124
125
126
    /**
127
     * @var array|Panel[] list of panels that should be active/tracking/available during the auditing phase.
128
     * If the value is a simple string, it is the identifier of an internal panel to activate (with default settings)
129
     * If the entry is a '<key>' => '<string>|<array>' it is either a new panel or a panel override (if you specify a core id).
130
     * It is important that the key is unique, as this is the identifier used to store any data associated with the panel.
131
     *
132
     * Please note:
133
     * - If you just want to change the configuration for a core panel, use the `$panelConfiguration`, it will be merged into this one
134
     * - If you add custom panels, please namespace them ("mynamespace/panelname").
135
     */
136
    public $panels = [
137
        'audit/request',
138
        'audit/db',
139
        'audit/log',
140
        'audit/mail',
141
        'audit/profiling',
142
        'audit/trail',
143
        'audit/javascript',
144
        // 'audit/asset',
145
        // 'audit/config',
146
147
        // These provide special functionality and get loaded to activate it
148
        'audit/error',      // Links the extra error reporting functions (`exception()` and `errorMessage()`)
149
        'audit/extra',      // Links the data functions (`data()`)
150
        'audit/curl',       // Links the curl tracking function (`curlBegin()`, `curlEnd()` and `curlExec()`)
151
    ];
152
153
    /**
154
     * Everything you add in here will be merged with the basic panel configuration.
155
     * This gives you an easy way to just add or modify panels/configurations without having to re-specify every panel.
156
     * This only accepts regular definitions ('<key>' => '<array>'), but the core class will be added if needed
157
     * Take a look at the [module configuration](docs/module-configuration.md) for more information.
158
     */
159
    public $panelsMerge = [];
160
161
    /**
162
     * @var LogTarget
163
     */
164
    public $logTarget;
165
166
    /**
167
     * @see \yii\debug\Module::$traceLine
168
     */
169
    public $traceLine = \yii\debug\Module::DEFAULT_IDE_TRACELINE;
170
171
    /**
172
     * @var array
173
     */
174
    private $_corePanels = [
175
        // Tracking/logging panels
176
        'audit/request'    => ['class' => 'bedezign\yii2\audit\panels\RequestPanel'],
177
        'audit/db'         => ['class' => 'bedezign\yii2\audit\panels\DbPanel'],
178
        'audit/log'        => ['class' => 'bedezign\yii2\audit\panels\LogPanel'],
179
        'audit/asset'      => ['class' => 'bedezign\yii2\audit\panels\AssetPanel'],
180
        'audit/config'     => ['class' => 'bedezign\yii2\audit\panels\ConfigPanel'],
181
        'audit/profiling'  => ['class' => 'bedezign\yii2\audit\panels\ProfilingPanel'],
182
183
        // Special other panels
184
        'audit/error'      => ['class' => 'bedezign\yii2\audit\panels\ErrorPanel'],
185
        'audit/javascript' => ['class' => 'bedezign\yii2\audit\panels\JavascriptPanel'],
186
        'audit/trail'      => ['class' => 'bedezign\yii2\audit\panels\TrailPanel'],
187
        'audit/mail'       => ['class' => 'bedezign\yii2\audit\panels\MailPanel'],
188
        'audit/extra'      => ['class' => 'bedezign\yii2\audit\panels\ExtraDataPanel'],
189
        'audit/curl'       => ['class' => 'bedezign\yii2\audit\panels\CurlPanel'],
190
        'audit/soap'       => ['class' => 'bedezign\yii2\audit\panels\SoapPanel'],
191
    ];
192
193
    /**
194
     * @var array
195
     */
196
    private $_panelFunctions = [];
197
198
    /**
199
     * @var \bedezign\yii2\audit\models\AuditEntry If activated this is the active entry
200
     */
201
    private $_entry = null;
202
203
    /**
204
     * @throws InvalidConfigException
205
     */
206 78
    public function init()
207
    {
208 78
        parent::init();
209 78
        $app = Yii::$app;
210
211
        // check if the module has been installed (prevents errors while installing)
212
        try {
213 78
            $this->getDb()->getTableSchema(AuditEntry::tableName());
214 78
        } catch (\Exception $e) {
215
            return;
216
        }
217
218
        // Before action triggers a new audit entry
219 78
        $app->on(Application::EVENT_BEFORE_ACTION, [$this, 'onBeforeAction']);
220
        // After request finalizes the audit entry.
221 78
        $app->on(Application::EVENT_AFTER_REQUEST, [$this, 'onAfterRequest']);
222
223
        // Activate the logging target
224 78
        if (empty($app->getLog()->targets['audit'])) {
225 75
            $this->logTarget = $app->getLog()->targets['audit'] = new LogTarget($this, $this->logConfig);
226 75
        } else {
227 3
            $this->logTarget = $app->getLog()->targets['audit'];
228
        }
229
230
        // Boot all active panels
231 78
        $this->normalizePanelConfiguration();
232 78
        $this->panels = $this->loadPanels(array_keys($this->panels));
233 78
    }
234
235
    /**
236
     * Called to evaluate if the current request should be logged
237
     * @param ActionEvent $event
238
     */
239 84
    public function onBeforeAction($event)
240
    {
241 84
        if (!empty($this->trackActions) && !$this->routeMatches($event->action->uniqueId, $this->trackActions)) {
242 9
            return;
243
        }
244 75
        if (!empty($this->ignoreActions) && $this->routeMatches($event->action->uniqueId, $this->ignoreActions)) {
245 54
            return;
246
        }
247
        // Still here, start audit
248 39
        $this->getEntry(true);
249 39
    }
250
251
    /**
252
     *
253
     */
254 57
    public function onAfterRequest()
255
    {
256 57
        if ($this->_entry) {
257 57
            $this->_entry->finalize();
258 57
        }
259 57
    }
260
261
    /**
262
     * Allows panels to register functions that can be called directly on the module
263
     * @param string    $name
264
     * @param callable  $callback
265
     */
266 78
    public function registerFunction($name, $callback)
267
    {
268 78
        if (isset($this->_panelFunctions[$name]))
269 78
            throw new InvalidParamException("The '$name'-function has already been defined.");
270
271 78
        $this->_panelFunctions[$name] = $callback;
272 78
    }
273
274
    /**
275
     * @param \yii\debug\Panel $panel
276
     */
277
    public function registerPanel(\yii\debug\Panel $panel)
278
    {
279
        $this->panels[$panel->id] = $panel;
280
    }
281
282
    /**
283
     * @param string $name
284
     * @param array $params
285
     * @return mixed
286
     */
287 21
    public function __call($name, $params)
288
    {
289 21
        if (!isset($this->_panelFunctions[$name]))
290 21
            throw new \yii\base\InvalidCallException("Unknown panel function '$name'");
291
292 21
        return call_user_func_array($this->_panelFunctions[$name], $params);
293
    }
294
295
    /**
296
     * @return \yii\db\Connection the database connection.
297
     */
298 237
    public function getDb()
299
    {
300 237
        return Yii::$app->{$this->db};
301
    }
302
303
    /**
304
     * @param bool $create
305
     * @param bool $new
306
     * @return AuditEntry
307
     */
308 171
    public function getEntry($create = false, $new = false)
309
    {
310 171
        $entry = new AuditEntry();
311 171
        $tableSchema = $entry->getDb()->schema->getTableSchema($entry->tableName());
312 171
        if ($tableSchema) {
313 171
            if ((!$this->_entry && $create) || $new) {
314 117
                $this->_entry = AuditEntry::create(true);
315 117
            }
316 171
        }
317 171
        return $this->_entry;
318
    }
319
320
    /**
321
     * @param $user_id
322
     * @return string
323
     */
324 18
    public function getUserIdentifier($user_id)
325
    {
326 18
        if (!$user_id) {
327
            return Yii::t('audit', 'Guest');
328
        }
329 18
        if ($this->userIdentifierCallback && is_callable($this->userIdentifierCallback)) {
330 3
            return call_user_func($this->userIdentifierCallback, $user_id);
331
        }
332 15
        return $user_id;
333
    }
334
335
    /**
336
     * @return int|mixed|null|string
337
     */
338 120
    public function getUserId()
339
    {
340 120
        if ($this->userIdCallback && is_callable($this->userIdCallback)) {
341 3
            return call_user_func($this->userIdCallback);
342
        }
343 117
        return (Yii::$app instanceof \yii\web\Application && Yii::$app->user) ? Yii::$app->user->id : null;
344
    }
345
346
    /**
347
     * Returns a list of all available panel identifiers
348
     * @return string[]
349
     */
350 3
    public function getPanelIdentifiers()
351
    {
352 3
        return array_unique(array_merge(array_keys($this->panels), array_keys($this->_corePanels)));
353
    }
354
355
    /**
356
     * Tries to assemble the configuration for the panels that the user wants for auditing
357
     * @param string[]          Set of panel identifiers that should be loaded
358
     * @return Panel[]
359
     */
360 78
    public function loadPanels($list)
361
    {
362 78
        $panels = [];
363 78
        foreach ($list as $panel) {
364 78
            $panels[$panel] = $this->getPanel($panel);
365 78
        }
366 78
        return $panels;
367
    }
368
369
    /**
370
     * @param string $identifier
371
     * @return null|Panel
372
     * @throws InvalidConfigException
373
     */
374 114
    public function getPanel($identifier)
375
    {
376 114
        $config = null;
377 114
        if (isset($this->panels[$identifier]))
378 114
            $config = $this->panels[$identifier];
379 3
        elseif (isset($this->_corePanels[$identifier]))
380 3
            $config = $this->_corePanels[$identifier];
381
382 114
        if (!$config)
383 114
            throw new InvalidConfigException("'$identifier' is not a valid panel identifier");
384
385 114
        if (is_array($config)) {
386 78
            $config['module'] = $this;
387 78
            $config['id'] = $identifier;
388 78
            return Yii::createObject($config);
389
        }
390
391 54
        return $config;
392
    }
393
394
    /**
395
     * Make sure the configured panels array is a uniform set of <identifier> => <config> entries.
396
     * @throws InvalidConfigException
397
     */
398 78
    protected function normalizePanelConfiguration()
399
    {
400 78
        $panels = [];
401 78
        foreach ($this->panels as $key => $value) {
402 78
            if (is_numeric($key)) {
403
                // The $value contains the identifier of a core panel
404 78
                if (!isset($this->_corePanels[$value]))
405 78
                    throw new InvalidConfigException("'$value' is not a valid panel identifier");
406 78
                $panels[$value] = $this->_corePanels[$value];
407 78
            }
408
            else {
409
                // The key contains the identifier and the value is either a class name or a full array
410
                $panels[$key] = is_string($value) ? ['class' => $value] : $value;
411
            }
412 78
        }
413 78
        $this->panels = ArrayHelper::merge($panels, $this->panelsMerge);
414
415
        // We now need one more iteration to add core classes to the panels added via the merge, if needed
416 78
        array_walk($this->panels, function(&$value, $key) {
417 78
           if (!isset($value['class'])) {
418
               if (isset($this->_corePanels[$key]))
419
                   $value = ArrayHelper::merge($value, $this->_corePanels[$key]);
420
               else
421
                   throw new InvalidConfigException("Invalid configuration for '$key'. No 'class' specified.");
422
           }
423 78
        });
424 78
    }
425
426
    /**
427
     * @return int|null|string
428
     */
429 87
    public static function findModuleIdentifier()
430
    {
431 87
        foreach (Yii::$app->modules as $name => $module) {
432 81
            $class = null;
433 81
            if (is_string($module))
434 81
                $class = $module;
435 81
            elseif (is_array($module)) {
436 78
                if (isset($module['class']))
437 78
                    $class = $module['class'];
438 78
            } else
439
                /** @var Module $module */
440 3
                $class = $module::className();
441
442 81
            $parts = explode('\\', $class);
443 81
            if ($class && strtolower(end($parts)) == 'audit')
444 81
                return $name;
445 6
        }
446 6
        return null;
447
    }
448
449
    /**
450
     * @param string $className
451
     * @return bool|string
452
     */
453 27
    public static function findPanelIdentifier($className)
454
    {
455 27
        $audit = Audit::getInstance();
456 27
        foreach ($audit->panels as $panel) {
457 27
            if ($panel->className() == $className) {
458 27
                return $panel->id;
459
            }
460 27
        }
461
        return false;
462
    }
463
464
    /**
465
     * Verifies a route against a given list and returns whether it matches or not.
466
     * Entries in the list are allowed to end with a '*', which means that a substring will be used for the match
467
     * instead of a full compare.
468
     *
469
     * @param string $route An application rout
470
     * @param string[] $list List of routes to compare against.
471
     * @return bool
472
     */
473 84
    protected function routeMatches($route, $list)
474
    {
475 84
        $list = ArrayHelper::toArray($list);
476 84
        foreach ($list as $compare) {
477 84
            $len = strlen($compare);
478 84
            if ($compare[$len - 1] == '*') {
479 54
                $compare = rtrim($compare, '*');
480 54
                if (substr($route, 0, $len - 1) === $compare)
481 54
                    return true;
482 24
            }
483
484 78
            if ($compare[0] == '*') {
485 18
                $compare = ltrim($compare, '*');
486 18
                if (substr($route, - ($len - 1)) === $compare)
487 18
                    return true;
488 6
            }
489
490 66
            if ($route === $compare)
491 66
                return true;
492 60
        }
493 36
        return false;
494
    }
495
496
}
497