Completed
Push — master ( 5c9faa...76a394 )
by Basil
08:05
created

ErrorHandlerTrait::coverSensitiveValues()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 4
nop 2
1
<?php
2
3
namespace luya\traits;
4
5
use Yii;
6
use yii\helpers\Json;
7
use Curl\Curl;
8
use luya\helpers\Url;
9
use luya\helpers\ObjectHelper;
10
use luya\helpers\StringHelper;
11
12
/**
13
 * ErrorHandler trait to extend the renderException method with an api call if enabled.
14
 *
15
 * @author Basil Suter <[email protected]>
16
 * @since 1.0.0
17
 */
18
trait ErrorHandlerTrait
19
{
20
    /**
21
     * @var string The url of the error api without trailing slash. Make sure you have installed the error api
22
     * module on the requested api url (https://luya.io/guide/module/luyadev---luya-module-errorapi).
23
     *
24
     * An example when using the erroapi module, the url could look like this `https://luya.io/errorapi`.
25
     */
26
    public $api;
27
28
    /**
29
     * @var boolean Enable the transfer of exceptions to the defined `$api` server.
30
     */
31
    public $transferException = false;
32
33
    /**
34
     * @var \Curl\Curl|null The curl object from the last error api call.
35
     * @since 1.0.5
36
     */
37
    public $lastTransferCall;
38
    
39
    /**
40
     * @var array An array of exceptions which are whitelisted and therefore NOT TRANSFERED.
41
     * @since 1.0.5
42
     */
43
    public $whitelist = ['yii\web\NotFoundHttpException'];
44
    
45
    /**
46
     * @var array
47
     * @since 1.0.6
48
     */
49
    public $sensitiveKeys = ['password', 'pwd', 'pass', 'passwort', 'pw', 'token', 'hash', 'authorization'];
50
    
51
    /**
52
     * Send a custom message to the api server event its not related to an exception.
53
     *
54
     * Sometimes you just want to pass informations from your application, this method allows you to transfer
55
     * a message to the error api server.
56
     *
57
     * Example of sending a message
58
     *
59
     * ```php
60
     * Yii::$app->errorHandler->transferMessage('Something went wrong here!', __FILE__, __LINE__);
61
     * ```
62
     *
63
     * @param string $message The message you want to send to the error api server.
64
     * @param string $file The you are currently send the message (use __FILE__)
65
     * @param string $line The line you want to submit (use __LINE__)
66
     * @return bool|null
67
     */
68
    public function transferMessage($message, $file = __FILE__, $line = __LINE__)
69
    {
70
        return $this->apiServerSendData($this->getExceptionArray([
71
            'message' => $message,
72
            'file' => $file,
73
            'line' => $line,
74
        ]));
75
    }
76
    
77
    /**
78
     * Send the array data to the api server.
79
     *
80
     * @param array $data The array to be sent to the server.
81
     * @return boolean|null true/false if data has been sent to the api successfull or not, null if the transfer is disabled.
82
     */
83
    private function apiServerSendData(array $data)
84
    {
85
        if ($this->transferException) {
86
            $curl = new Curl();
87
            $curl->setOpt(CURLOPT_CONNECTTIMEOUT, 2);
88
            $curl->setOpt(CURLOPT_TIMEOUT, 2);
89
            $curl->post(Url::ensureHttp(rtrim($this->api, '/')).'/create', [
90
                'error_json' => Json::encode($data),
91
            ]);
92
            
93
            $this->lastTransferCall = $curl;
94
            
95
            return $curl->isSuccess();
96
        }
97
        
98
        return null;
99
    }
100
    
101
    /**
102
     * @inheritdoc
103
     */
104
    public function renderException($exception)
105
    {
106
        if (!ObjectHelper::isInstanceOf($exception, $this->whitelist, false) && $this->transferException) {
107
            $this->apiServerSendData($this->getExceptionArray($exception));
108
        }
109
        
110
        return parent::renderException($exception);
111
    }
112
113
    /**
114
     * Get an readable array to transfer from an exception
115
     *
116
     * @param mixed $exception Exception object
117
     * @return array An array with transformed exception data
118
     */
119
    public function getExceptionArray($exception)
120
    {
121
        $_message = 'Uknonwn exception object, not instance of \Exception.';
122
        $_file = 'unknown';
123
        $_line = 0;
124
        $_trace = [];
125
        $_previousException = [];
126
        
127
        if (is_object($exception)) {
128
            $prev = $exception->getPrevious();
129
            
130
            if (!empty($prev)) {
131
                $_previousException = [
132
                    'message' => $prev->getMessage(),
133
                    'file' => $prev->getFile(),
134
                    'line' => $prev->getLine(),
135
                    'trace' => $this->buildTrace($prev),
136
                ];
137
            }
138
            
139
            $_trace = $this->buildTrace($exception);
140
            $_message = $exception->getMessage();
141
            $_file = $exception->getFile();
142
            $_line = $exception->getLine();
143
        } elseif (is_string($exception)) {
144
            $_message = 'exception string: ' . $exception;
145
        } elseif (is_array($exception)) {
146
            $_message = isset($exception['message']) ? $exception['message'] : 'exception array dump: ' . print_r($exception, true);
147
            $_file = isset($exception['file']) ? $exception['file'] : __FILE__;
148
            $_line = isset($exception['line']) ? $exception['line'] : __LINE__;
149
        }
150
151
        return [
152
            'message' => $_message,
153
            'file' => $_file,
154
            'line' => $_line,
155
            'requestUri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null,
156
            'serverName' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null,
157
            'date' => date('d.m.Y H:i'),
158
            'trace' => $_trace,
159
            'previousException' => $_previousException,
160
            'ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
161
            'get' => isset($_GET) ? $this->coverSensitiveValues($_GET, $this->sensitiveKeys) : [],
162
            'post' => isset($_POST) ? $this->coverSensitiveValues($_POST, $this->sensitiveKeys) : [],
163
            'session' => isset($_SESSION) ? $this->coverSensitiveValues($_SESSION, $this->sensitiveKeys) : [],
164
            'server' => isset($_SERVER) ? $this->coverSensitiveValues($_SERVER, $this->sensitiveKeys) : [],
165
            'profiling' => Yii::getLogger()->profiling,
166
            'logger' => Yii::getLogger()->messages,
167
        ];
168
    }
169
    
170
    /**
171
     * Cover senstive values from a given list of keys.
172
     * 
173
     * This applys only for the first key inside the array and does not work recursive.
174
     * 
175
     * The main purpose is to remove passwords transferd to api when existing in post, get or session.
176
     * 
177
     * @param array $data
178
     * @param array $key
0 ignored issues
show
Bug introduced by
There is no parameter named $key. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
179
     * @since 1.0.6
180
     */
181
    public function coverSensitiveValues(array $data, array $keys)
182
    {
183
        $clean = [];
184
        foreach ($keys as $key) {
185
            $kw = strtolower($key);
186
            foreach ($data as $k => $v) {
187
                if ($kw == strtolower($k) || StringHelper::startsWith(strtolower($k), $kw)) {
188
                    $v = str_repeat("*", strlen($v));
189
                    $clean[$k] = $v;
190
                }
191
            }
192
        }
193
        
194
        // the later overrides the former
195
        return array_merge($data, $clean);
196
    }
197
    
198
    /**
199
     * Build trace array from exception.
200
     *
201
     * @param object $exception
202
     * @return array
203
     */
204
    private function buildTrace($exception)
205
    {
206
        $_trace = [];
207
        foreach ($exception->getTrace() as $key => $item) {
208
            $_trace[$key] = [
209
                'file' => isset($item['file']) ? $item['file'] : null,
210
                'line' => isset($item['line']) ? $item['line'] : null,
211
                'function' => isset($item['function']) ? $item['function'] : null,
212
                'class' => isset($item['class']) ? $item['class'] : null,
213
            ];
214
        }
215
        
216
        return $_trace;
217
    }
218
}
219