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 ( 488490...12777f )
by Brett
10:51 queued 07:50
created

Audit::findModuleIdentifier()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.049

Importance

Changes 0
Metric Value
cc 7
nc 9
nop 0
dl 0
loc 19
ccs 9
cts 10
cp 0.9
crap 7.049
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 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
        $this->activateLogTarget();
224 78
225 75
        // Boot all active panels
226 75
        $this->normalizePanelConfiguration();
227 3
        $this->panels = $this->loadPanels(array_keys($this->panels));
228
    }
229
230
    /**
231 78
     * Called to evaluate if the current request should be logged
232 78
     * @param ActionEvent $event
233 78
     */
234
    public function onBeforeAction($event)
235
    {
236
        if (!empty($this->trackActions) && !$this->routeMatches($event->action->uniqueId, $this->trackActions)) {
237
            return;
238
        }
239 84
        if (!empty($this->ignoreActions) && $this->routeMatches($event->action->uniqueId, $this->ignoreActions)) {
240
            return;
241 84
        }
242 9
        // Still here, start audit
243
        $this->getEntry(true);
244 75
    }
245 54
246
    /**
247
     *
248 39
     */
249 39
    public function onAfterRequest()
250
    {
251
        if ($this->_entry) {
252
            $this->_entry->finalize();
253
        }
254 57
    }
255
256 57
    /**
257 57
     * Allows panels to register functions that can be called directly on the module
258 57
     * @param string    $name
259 57
     * @param callable  $callback
260
     */
261
    public function registerFunction($name, $callback)
262
    {
263
        if (isset($this->_panelFunctions[$name]))
264
            throw new InvalidParamException("The '$name'-function has already been defined.");
265
266 78
        $this->_panelFunctions[$name] = $callback;
267
    }
268 78
269 78
    /**
270
     * @param \yii\debug\Panel $panel
271 78
     */
272 78
    public function registerPanel(\yii\debug\Panel $panel)
273
    {
274
        $this->panels[$panel->id] = $panel;
275
    }
276
277
    /**
278
     * @param string $name
279
     * @param array $params
280
     * @return mixed
281
     */
282
    public function __call($name, $params)
283
    {
284
        if (!isset($this->_panelFunctions[$name]))
285
            throw new \yii\base\InvalidCallException("Unknown panel function '$name'");
286
287 21
        return call_user_func_array($this->_panelFunctions[$name], $params);
288
    }
289 21
290 21
    public function activateLogTarget()
291
    {
292 21
        $app = Yii::$app;
293
294
        // Activate the logging target
295
        if (empty($app->getLog()->targets['audit'])) {
296
            $this->logTarget = $app->getLog()->targets['audit'] = new LogTarget($this, $this->logConfig);
297
        } else {
298 237
            $this->logTarget = $app->getLog()->targets['audit'];
299
        }
300 237
    }
301
302
    /**
303
     * @return \yii\db\Connection the database connection.
304
     */
305
    public function getDb()
306
    {
307
        return Yii::$app->{$this->db};
308 171
    }
309
310 171
    /**
311 171
     * @param bool $create
312 171
     * @param bool $new
313 171
     * @return AuditEntry
314 117
     */
315 117
    public function getEntry($create = false, $new = false)
316 171
    {
317 171
        $entry = new AuditEntry();
318
        $tableSchema = $entry->getDb()->schema->getTableSchema($entry->tableName());
319
        if ($tableSchema) {
320
            if ((!$this->_entry && $create) || $new) {
321
                $this->_entry = AuditEntry::create(true);
322
            }
323
        }
324 18
        return $this->_entry;
325
    }
326 18
327
    /**
328
     * @param AuditEntry $entry
329 18
     */
330 3
    public function setEntry($entry)
331
    {
332 15
        $this->_entry = $entry;
333
    }
334
    
335
    /**
336
     * @param $user_id
337
     * @return string
338 120
     */
339
    public function getUserIdentifier($user_id)
340 120
    {
341 3
        if (!$user_id) {
342
            return Yii::t('audit', 'Guest');
343 117
        }
344
        if ($this->userIdentifierCallback && is_callable($this->userIdentifierCallback)) {
345
            return call_user_func($this->userIdentifierCallback, $user_id);
346
        }
347
        return $user_id;
348
    }
349
350 3
    /**
351
     * @return int|mixed|null|string
352 3
     */
353
    public function getUserId()
354
    {
355
        if ($this->userIdCallback && is_callable($this->userIdCallback)) {
356
            return call_user_func($this->userIdCallback);
357
        }
358
        return (Yii::$app instanceof \yii\web\Application && Yii::$app->user) ? Yii::$app->user->id : null;
359
    }
360 78
361
    /**
362 78
     * Returns a list of all available panel identifiers
363 78
     * @return string[]
364 78
     */
365 78
    public function getPanelIdentifiers()
366 78
    {
367
        return array_unique(array_merge(array_keys($this->panels), array_keys($this->_corePanels)));
368
    }
369
370
    /**
371
     * Tries to assemble the configuration for the panels that the user wants for auditing
372
     * @param string[]          Set of panel identifiers that should be loaded
373
     * @return Panel[]
374 114
     */
375
    public function loadPanels($list)
376 114
    {
377 114
        $panels = [];
378 114
        foreach ($list as $panel) {
379 3
            $panels[$panel] = $this->getPanel($panel);
380 3
        }
381
        return $panels;
382 114
    }
383 114
384
    /**
385 114
     * @param string $identifier
386 78
     * @return null|Panel
387 78
     * @throws InvalidConfigException
388 78
     */
389
    public function getPanel($identifier)
390
    {
391 54
        $config = null;
392
        if (isset($this->panels[$identifier]))
393
            $config = $this->panels[$identifier];
394
        elseif (isset($this->_corePanels[$identifier]))
395
            $config = $this->_corePanels[$identifier];
396
397
        if (!$config)
398 78
            throw new InvalidConfigException("'$identifier' is not a valid panel identifier");
399
400 78
        if (is_array($config)) {
401 78
            $config['module'] = $this;
402 78
            $config['id'] = $identifier;
403
            return Yii::createObject($config);
404 78
        }
405 78
406 78
        return $config;
407 78
    }
408
409
    /**
410
     * Make sure the configured panels array is a uniform set of <identifier> => <config> entries.
411
     * @throws InvalidConfigException
412 78
     */
413 78
    protected function normalizePanelConfiguration()
414
    {
415
        $panels = [];
416 78
        foreach ($this->panels as $key => $value) {
417 78
            if (is_numeric($key)) {
418
                // The $value contains the identifier of a core panel
419
                if (!isset($this->_corePanels[$value]))
420
                    throw new InvalidConfigException("'$value' is not a valid panel identifier");
421
                $panels[$value] = $this->_corePanels[$value];
422
            }
423 78
            else {
424 78
                // The key contains the identifier and the value is either a class name or a full array
425
                $panels[$key] = is_string($value) ? ['class' => $value] : $value;
426
            }
427
        }
428
        $this->panels = ArrayHelper::merge($panels, $this->panelsMerge);
429 87
430
        // We now need one more iteration to add core classes to the panels added via the merge, if needed
431 87
        array_walk($this->panels, function(&$value, $key) {
432 81
           if (!isset($value['class'])) {
433 81
               if (isset($this->_corePanels[$key]))
434 81
                   $value = ArrayHelper::merge($value, $this->_corePanels[$key]);
435 81
               else
436 78
                   throw new InvalidConfigException("Invalid configuration for '$key'. No 'class' specified.");
437 78
           }
438 78
        });
439
    }
440 3
441
    /**
442 81
     * @return int|null|string
443 81
     */
444 81
    public static function findModuleIdentifier()
445 6
    {
446 6
        foreach (Yii::$app->modules as $name => $module) {
447
            $class = null;
448
            if (is_string($module))
449
                $class = $module;
450
            elseif (is_array($module)) {
451
                if (isset($module['class']))
452
                    $class = $module['class'];
453 27
            } else
454
                /** @var Module $module */
455 27
                $class = $module::className();
456 27
457 27
            $parts = explode('\\', $class);
458 27
            if ($class && strtolower(end($parts)) == 'audit')
459
                return $name;
460 27
        }
461
        return null;
462
    }
463
464
    /**
465
     * @param string $className
466
     * @return bool|string
467
     */
468
    public static function findPanelIdentifier($className)
469
    {
470
        $audit = Audit::getInstance();
471
        foreach ($audit->panels as $panel) {
472
            if ($panel->className() == $className) {
473 84
                return $panel->id;
474
            }
475 84
        }
476 84
        return false;
477 84
    }
478 84
479 54
    /**
480 54
     * Verifies a route against a given list and returns whether it matches or not.
481 54
     * Entries in the list are allowed to end with a '*', which means that a substring will be used for the match
482 24
     * instead of a full compare.
483
     *
484 78
     * @param string $route An application rout
485 18
     * @param string[] $list List of routes to compare against.
486 18
     * @return bool
487 18
     */
488 6
    protected function routeMatches($route, $list)
489
    {
490 66
        $list = ArrayHelper::toArray($list);
491 66
        foreach ($list as $compare) {
492 60
            $len = strlen($compare);
493 36
            if ($compare[$len - 1] == '*') {
494
                $compare = rtrim($compare, '*');
495
                if (substr($route, 0, $len - 1) === $compare)
496
                    return true;
497
            }
498
499
            if ($compare[0] == '*') {
500
                $compare = ltrim($compare, '*');
501
                if (substr($route, - ($len - 1)) === $compare)
502
                    return true;
503
            }
504
505
            if ($route === $compare)
506
                return true;
507
        }
508
        return false;
509
    }
510
511
}
512