Passed
Branch master (933aa9)
by Tarmo
07:04
created

LogRequestProcessRequestTrait::getUri()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * /src/Entity/Traits/LogRequestProcessRequestTrait.php
5
 *
6
 * @author  TLe, Tarmo Leppänen <[email protected]>
7
 */
8
namespace App\Entity\Traits;
9
10
use App\Utils\JSON;
11
use LogicException;
12
use Symfony\Component\HttpFoundation\Request;
13
use Symfony\Component\Serializer\Annotation\Groups;
14
use function array_key_exists;
15
use function array_map;
16
use function array_walk;
17
use function basename;
18
use function explode;
19
use function is_array;
20
use function mb_strtolower;
21
use function parse_str;
22
use function preg_replace;
23
use function strpos;
24
25
/**
26
 * Trait LogRequestProcessRequestTrait
27
 *
28
 * @package App\Entity\Traits
29
 * @author  TLe, Tarmo Leppänen <[email protected]>
30
 */
31
trait LogRequestProcessRequestTrait
32
{
33
    /**
34
     * @var string
35
     */
36
    private $replaceValue = '*** REPLACED ***';
37
38
    /**
39
     * @var mixed[]
40
     *
41
     * @Groups({
42
     *      "LogRequest",
43
     *      "LogRequest.headers",
44
     *  })
45
     *
46
     * @ORM\Column(
47
     *      name="headers",
48
     *      type="array",
49
     *  )
50
     */
51
    private $headers;
52
53
    /**
54
     * @var string
55
     *
56
     * @Groups({
57
     *      "LogRequest",
58
     *      "LogRequest.method",
59
     *  })
60
     *
61
     * @ORM\Column(
62
     *      name="method",
63
     *      type="string",
64
     *      length=255,
65
     *      nullable=false,
66
     *  )
67
     */
68
    private $method;
69
70
    /**
71
     * @var string
72
     *
73
     * @Groups({
74
     *      "LogRequest",
75
     *      "LogRequest.scheme",
76
     *  })
77
     *
78
     * @ORM\Column(
79
     *      name="scheme",
80
     *      type="string",
81
     *      length=5,
82
     *      nullable=false,
83
     *  )
84
     */
85
    private $scheme;
86
87
    /**
88
     * @var string
89
     *
90
     * @Groups({
91
     *      "LogRequest",
92
     *      "LogRequest.basePath",
93
     *  })
94
     *
95
     * @ORM\Column(
96
     *      name="base_path",
97
     *      type="string",
98
     *      length=255,
99
     *      nullable=false,
100
     *  )
101
     */
102
    private $basePath;
103
104
    /**
105
     * @var string
106
     *
107
     * @Groups({
108
     *      "LogRequest",
109
     *      "LogRequest.script",
110
     *  })
111
     *
112
     * @ORM\Column(
113
     *      name="script",
114
     *      type="string",
115
     *      length=255,
116
     *      nullable=false,
117
     *  )
118
     */
119
    private $script;
120
121
    /**
122
     * @var string
123
     *
124
     * @Groups({
125
     *      "LogRequest",
126
     *      "LogRequest.path",
127
     *  })
128
     *
129
     * @ORM\Column(
130
     *      name="path",
131
     *      type="string",
132
     *      length=255,
133
     *      nullable=true,
134
     *  )
135
     */
136
    private $path;
137
138
    /**
139
     * @var string
140
     *
141
     * @Groups({
142
     *      "LogRequest",
143
     *      "LogRequest.queryString",
144
     *  })
145
     *
146
     * @ORM\Column(
147
     *      name="query_string",
148
     *      type="text",
149
     *      nullable=true,
150
     *  )
151
     */
152
    private $queryString;
153
154
    /**
155
     * @var string
156
     *
157
     * @Groups({
158
     *      "LogRequest",
159
     *      "LogRequest.uri",
160
     *  })
161
     *
162
     * @ORM\Column(
163
     *      name="uri",
164
     *      type="text",
165
     *      nullable=false,
166
     *  )
167
     */
168
    private $uri;
169
170
    /**
171
     * @var string
172
     *
173
     * @Groups({
174
     *      "LogRequest",
175
     *      "LogRequest.controller",
176
     *  })
177
     *
178
     * @ORM\Column(
179
     *      name="controller",
180
     *      type="string",
181
     *      length=255,
182
     *      nullable=true,
183
     *  )
184
     */
185
    private $controller;
186
187
    /**
188
     * @var string
189
     *
190
     * @Groups({
191
     *      "LogRequest",
192
     *      "LogRequest.contentType",
193
     *  })
194
     *
195
     * @ORM\Column(
196
     *      name="content_type",
197
     *      type="string",
198
     *      length=255,
199
     *      nullable=true,
200
     *  )
201
     */
202
    private $contentType;
203
204
    /**
205
     * @var string
206
     *
207
     * @Groups({
208
     *      "LogRequest",
209
     *      "LogRequest.contentTypeShort",
210
     *  })
211
     *
212
     * @ORM\Column(
213
     *      name="content_type_short",
214
     *      type="string",
215
     *      length=255,
216
     *      nullable=true,
217
     *  )
218
     */
219
    private $contentTypeShort;
220
221
    /**
222
     * @var bool
223
     *
224
     * @Groups({
225
     *      "LogRequest",
226
     *      "LogRequest.isXmlHttpRequest",
227
     *  })
228
     *
229
     * @ORM\Column(
230
     *      name="is_xml_http_request",
231
     *      type="boolean",
232
     *      nullable=false,
233
     *  )
234
     */
235
    private $xmlHttpRequest;
236
237
    /**
238
     * @var string
239
     *
240
     * @Groups({
241
     *      "LogRequest",
242
     *      "LogRequest.action",
243
     *  })
244
     *
245
     * @ORM\Column(
246
     *      name="action",
247
     *      type="string",
248
     *      length=255,
249
     *      nullable=true,
250
     *  )
251
     */
252
    private $action;
253
254
    /**
255
     * @var string
256
     *
257
     * @Groups({
258
     *      "LogRequest",
259
     *      "LogRequest.content",
260
     *  })
261
     *
262
     * @ORM\Column(
263
     *      name="content",
264
     *      type="text",
265
     *      nullable=true,
266
     *  )
267
     */
268
    private $content;
269
270
    /**
271
     * @var mixed[]
272
     *
273
     * @Groups({
274
     *      "LogRequest",
275
     *      "LogRequest.parameters",
276
     *  })
277
     *
278
     * @ORM\Column(
279
     *      name="parameters",
280
     *      type="array",
281
     *  )
282
     */
283
    private $parameters;
284
285
    /**
286
     * @return string
287
     */
288 1
    public function getUri(): string
289
    {
290 1
        return $this->uri;
291
    }
292
293
    /**
294
     * @return string
295
     */
296 1
    public function getMethod(): string
297
    {
298 1
        return $this->method;
299
    }
300
301
    /**
302
     * @return string
303
     */
304 1
    public function getScheme(): string
305
    {
306 1
        return $this->scheme;
307
    }
308
309
    /**
310
     * @return string
311
     */
312 1
    public function getBasePath(): string
313
    {
314 1
        return $this->basePath;
315
    }
316
317
    /**
318
     * @return string|null
319
     */
320 1
    public function getQueryString(): ?string
321
    {
322 1
        return $this->queryString;
323
    }
324
325
    /**
326
     * @return mixed[]
327
     */
328 5
    public function getHeaders(): array
329
    {
330 5
        return $this->headers;
331
    }
332
333
    /**
334
     * @return mixed[]
335
     */
336 5
    public function getParameters(): array
337
    {
338 5
        return $this->parameters;
339
    }
340
341
    /**
342
     * @return boolean
343
     */
344 1
    public function isXmlHttpRequest(): bool
345
    {
346 1
        return $this->xmlHttpRequest;
347
    }
348
349
    /**
350
     * @return string|null
351
     */
352 1
    public function getController(): ?string
353
    {
354 1
        return $this->controller;
355
    }
356
357
    /**
358
     * @return string|null
359
     */
360 1
    public function getAction(): ?string
361
    {
362 1
        return $this->action;
363
    }
364
365
    /**
366
     * @return string|null
367
     */
368 1
    public function getPath(): ?string
369
    {
370 1
        return $this->path;
371
    }
372
373
    /**
374
     * @return string
375
     */
376 1
    public function getScript(): string
377
    {
378 1
        return $this->script;
379
    }
380
381
    /**
382
     * @return string
383
     */
384 1
    public function getContent(): string
385
    {
386 1
        return $this->content;
387
    }
388
389
    /**
390
     * @return string|null
391
     */
392 1
    public function getContentType(): ?string
393
    {
394 1
        return $this->contentType;
395
    }
396
397
    /**
398
     * @return string|null
399
     */
400 1
    public function getContentTypeShort(): ?string
401
    {
402 1
        return $this->contentTypeShort;
403
    }
404
405
    /**
406
     * @param Request $request
407
     *
408
     * @throws LogicException
409
     */
410 948
    protected function processRequest(Request $request): void
411
    {
412 948
        $this->processRequestBaseInfo($request);
413 948
        $this->processHeadersAndParameters($request);
414
415 948
        $this->action = $this->determineAction($request);
416 948
        $this->content = $this->cleanContent((string)$request->getContent());
417 948
    }
418
419
    /**
420
     * @param Request $request
421
     *
422
     * @throws LogicException
423
     */
424 948
    private function processHeadersAndParameters(Request $request): void
425
    {
426 948
        $rawHeaders = $request->headers->all();
427
428
        // Clean possible sensitive data from parameters
429 948
        array_walk($rawHeaders, function (&$value, string $key): void {
430 946
            $this->cleanParameters($value, $key);
431 948
        });
432
433 948
        $this->headers = $rawHeaders;
434
435 948
        $rawParameters = $this->determineParameters($request);
436
437
        // Clean possible sensitive data from parameters
438 948
        array_walk($rawParameters, function (&$value, string $key): void {
439 810
            $this->cleanParameters($value, $key);
440 948
        });
441
442 948
        $this->parameters = $rawParameters;
443 948
    }
444
445
    /**
446
     * @param Request $request
447
     */
448 948
    private function processRequestBaseInfo(Request $request): void
449
    {
450 948
        $this->method = $request->getRealMethod();
451 948
        $this->scheme = $request->getScheme();
452 948
        $this->basePath = $request->getBasePath();
453 948
        $this->script = '/' . basename($request->getScriptName());
454 948
        $this->path = $request->getPathInfo();
455 948
        $this->queryString = $request->getRequestUri();
456 948
        $this->uri = $request->getUri();
457 948
        $this->controller = $request->get('_controller', '');
458 948
        $this->contentType = (string)$request->getMimeType($request->getContentType());
459 948
        $this->contentTypeShort = (string)$request->getContentType();
460 948
        $this->xmlHttpRequest = $request->isXmlHttpRequest();
461 948
    }
462
463
    /**
464
     * @param Request $request
465
     *
466
     * @return string
467
     */
468 948
    private function determineAction(Request $request): string
469
    {
470 948
        $rawAction = $request->get('_controller', '');
471 948
        $rawAction = explode(strpos($rawAction, '::') ? '::' : ':', $rawAction);
472
473 948
        return $rawAction[1] ?? '';
474
    }
475
476
    /**
477
     * Getter method to convert current request parameters to array.
478
     *
479
     * @param Request $request
480
     *
481
     * @return mixed[]
482
     *
483
     * @throws LogicException
484
     */
485 948
    private function determineParameters(Request $request): array
486
    {
487 948
        $rawContent = (string)$request->getContent();
488
489
        // By default just get whole parameter bag
490 948
        $output = $request->request->all();
491
492
        // Content given so parse it
493 948
        if ($rawContent) {
494
            // First try to convert content to array from JSON
495
            try {
496 809
                $output = JSON::decode($rawContent, true);
497
            } /** @noinspection BadExceptionsProcessingInspection */
498 1
            catch (LogicException $error) { // Oh noes content isn't JSON so just parse it
499 1
                $output = [];
500
501 1
                parse_str($rawContent, $output);
502
            }
503
        }
504
505 948
        return (array)$output;
506
    }
507
508
    /**
509
     * Helper method to clean parameters / header array of any sensitive data.
510
     *
511
     * @param mixed  $value
512
     * @param string $key
513
     */
514 946
    private function cleanParameters(&$value, string $key): void
515
    {
516
        // What keys we should replace so that any sensitive data is not logged
517
        $replacements = [
518 946
            'password' => $this->replaceValue,
519 946
            'token' => $this->replaceValue,
520 946
            'authorization' => $this->replaceValue,
521 946
            'cookie' => $this->replaceValue,
522
        ];
523
524
        // Normalize current key
525 946
        $key = mb_strtolower($key);
526
527
        // Replace current value
528 946
        if (array_key_exists($key, $replacements)) {
529 834
            $value = $this->cleanContent($replacements[$key]);
530
        }
531
532
        // Recursive call
533 946
        if (is_array($value)) {
534 942
            array_walk($value, function (&$value, string $key): void {
535 942
                $this->cleanParameters($value, $key);
536 942
            });
537
        }
538 946
    }
539
540
    /**
541
     * Method to clean raw request content of any sensitive data.
542
     *
543
     * @param string $inputContent
544
     *
545
     * @return string
546
     */
547
    private function cleanContent(string $inputContent): string
548
    {
549 948
        $iterator = function ($search) use (&$inputContent): void {
550 948
            $inputContent = preg_replace('/(' . $search . '":)\s*"(.*)"/', '$1"*** REPLACED ***"', $inputContent);
551 948
        };
552
553 948
        static $replacements = [
554
            'password',
555
            'token',
556
            'authorization',
557
            'cookie',
558
        ];
559
560 948
        array_map($iterator, $replacements);
561
562 948
        return $inputContent;
563
    }
564
}
565