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
Pull Request — master (#191)
by Herbert
10:39
created

Audit::loadPanels()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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