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.
Test Setup Failed
Push — master ( 54ac76...9a6bba )
by Steve
12:15
created

Audit::activateLogTarget()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 0
loc 11
ccs 4
cts 4
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 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 $user_id
329 18
     * @return string
330 3
     */
331
    public function getUserIdentifier($user_id)
332 15
    {
333
        if (!$user_id) {
334
            return Yii::t('audit', 'Guest');
335
        }
336
        if ($this->userIdentifierCallback && is_callable($this->userIdentifierCallback)) {
337
            return call_user_func($this->userIdentifierCallback, $user_id);
338 120
        }
339
        return $user_id;
340 120
    }
341 3
342
    /**
343 117
     * @return int|mixed|null|string
344
     */
345
    public function getUserId()
346
    {
347
        if ($this->userIdCallback && is_callable($this->userIdCallback)) {
348
            return call_user_func($this->userIdCallback);
349
        }
350 3
        return (Yii::$app instanceof \yii\web\Application && Yii::$app->user) ? Yii::$app->user->id : null;
351
    }
352 3
353
    /**
354
     * Returns a list of all available panel identifiers
355
     * @return string[]
356
     */
357
    public function getPanelIdentifiers()
358
    {
359
        return array_unique(array_merge(array_keys($this->panels), array_keys($this->_corePanels)));
360 78
    }
361
362 78
    /**
363 78
     * Tries to assemble the configuration for the panels that the user wants for auditing
364 78
     * @param string[]          Set of panel identifiers that should be loaded
365 78
     * @return Panel[]
366 78
     */
367
    public function loadPanels($list)
368
    {
369
        $panels = [];
370
        foreach ($list as $panel) {
371
            $panels[$panel] = $this->getPanel($panel);
372
        }
373
        return $panels;
374 114
    }
375
376 114
    /**
377 114
     * @param string $identifier
378 114
     * @return null|Panel
379 3
     * @throws InvalidConfigException
380 3
     */
381
    public function getPanel($identifier)
382 114
    {
383 114
        $config = null;
384
        if (isset($this->panels[$identifier]))
385 114
            $config = $this->panels[$identifier];
386 78
        elseif (isset($this->_corePanels[$identifier]))
387 78
            $config = $this->_corePanels[$identifier];
388 78
389
        if (!$config)
390
            throw new InvalidConfigException("'$identifier' is not a valid panel identifier");
391 54
392
        if (is_array($config)) {
393
            $config['module'] = $this;
394
            $config['id'] = $identifier;
395
            return Yii::createObject($config);
396
        }
397
398 78
        return $config;
399
    }
400 78
401 78
    /**
402 78
     * Make sure the configured panels array is a uniform set of <identifier> => <config> entries.
403
     * @throws InvalidConfigException
404 78
     */
405 78
    protected function normalizePanelConfiguration()
406 78
    {
407 78
        $panels = [];
408
        foreach ($this->panels as $key => $value) {
409
            if (is_numeric($key)) {
410
                // The $value contains the identifier of a core panel
411
                if (!isset($this->_corePanels[$value]))
412 78
                    throw new InvalidConfigException("'$value' is not a valid panel identifier");
413 78
                $panels[$value] = $this->_corePanels[$value];
414
            }
415
            else {
416 78
                // The key contains the identifier and the value is either a class name or a full array
417 78
                $panels[$key] = is_string($value) ? ['class' => $value] : $value;
418
            }
419
        }
420
        $this->panels = ArrayHelper::merge($panels, $this->panelsMerge);
421
422
        // We now need one more iteration to add core classes to the panels added via the merge, if needed
423 78
        array_walk($this->panels, function(&$value, $key) {
424 78
           if (!isset($value['class'])) {
425
               if (isset($this->_corePanels[$key]))
426
                   $value = ArrayHelper::merge($value, $this->_corePanels[$key]);
427
               else
428
                   throw new InvalidConfigException("Invalid configuration for '$key'. No 'class' specified.");
429 87
           }
430
        });
431 87
    }
432 81
433 81
    /**
434 81
     * @return int|null|string
435 81
     */
436 78
    public static function findModuleIdentifier()
437 78
    {
438 78
        foreach (Yii::$app->modules as $name => $module) {
439
            $class = null;
440 3
            if (is_string($module))
441
                $class = $module;
442 81
            elseif (is_array($module)) {
443 81
                if (isset($module['class']))
444 81
                    $class = $module['class'];
445 6
            } else
446 6
                /** @var Module $module */
447
                $class = $module::className();
448
449
            $parts = explode('\\', $class);
450
            if ($class && strtolower(end($parts)) == 'audit')
451
                return $name;
452
        }
453 27
        return null;
454
    }
455 27
456 27
    /**
457 27
     * @param string $className
458 27
     * @return bool|string
459
     */
460 27
    public static function findPanelIdentifier($className)
461
    {
462
        $audit = Audit::getInstance();
463
        foreach ($audit->panels as $panel) {
464
            if ($panel->className() == $className) {
465
                return $panel->id;
466
            }
467
        }
468
        return false;
469
    }
470
471
    /**
472
     * Verifies a route against a given list and returns whether it matches or not.
473 84
     * Entries in the list are allowed to end with a '*', which means that a substring will be used for the match
474
     * instead of a full compare.
475 84
     *
476 84
     * @param string $route An application rout
477 84
     * @param string[] $list List of routes to compare against.
478 84
     * @return bool
479 54
     */
480 54
    protected function routeMatches($route, $list)
481 54
    {
482 24
        $list = ArrayHelper::toArray($list);
483
        foreach ($list as $compare) {
484 78
            $len = strlen($compare);
485 18
            if ($compare[$len - 1] == '*') {
486 18
                $compare = rtrim($compare, '*');
487 18
                if (substr($route, 0, $len - 1) === $compare)
488 6
                    return true;
489
            }
490 66
491 66
            if ($compare[0] == '*') {
492 60
                $compare = ltrim($compare, '*');
493 36
                if (substr($route, - ($len - 1)) === $compare)
494
                    return true;
495
            }
496
497
            if ($route === $compare)
498
                return true;
499
        }
500
        return false;
501
    }
502
503
}
504