Passed
Push — master ( 5fb0b3...a1dc53 )
by Dante
01:22
created

LogTrait::maskPasswordField()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 4
nop 2
dl 0
loc 8
rs 10
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 */
10
11
namespace BEdita\SDK;
12
13
use Monolog\Handler\StreamHandler;
14
use Monolog\Logger;
15
use Psr\Http\Message\RequestInterface;
16
use Psr\Http\Message\ResponseInterface;
17
18
/**
19
 * Basic SDK logging functions
20
 */
21
trait LogTrait
22
{
23
    /**
24
     * internal Logger
25
     *
26
     * @var null|Logger
27
     */
28
    protected $logger = null;
29
30
    /**
31
     * Get configured logger, may be null
32
     *
33
     * @return Logger|null
34
     * @codeCoverageIgnore
35
     */
36
    public function getLogger(): ?Logger
37
    {
38
        return $this->logger;
39
    }
40
41
    /**
42
     * Initialize and configure logger
43
     *
44
     * @param array $options Configuration options, 'log_file' key with log file path is mandatory
45
     * @return bool True on successful initialization, false otherwise
46
     */
47
    public function initLogger(array $options): bool
48
    {
49
        // 'path' to log file is mandatory
50
        if (empty($options['log_file'])) {
51
            return false;
52
        }
53
54
        $this->logger = new Logger('be4-php-sdk');
55
        $this->logger->pushHandler(new StreamHandler($options['log_file'], Logger::DEBUG));
56
57
        return true;
58
    }
59
60
    /**
61
     * Perform request log
62
     *
63
     * @param RequestInterface $request The request to log
64
     * @return void
65
     */
66
    public function logRequest(RequestInterface $request): void
67
    {
68
        if (!$this->logger) {
69
            return;
70
        }
71
72
        $msg = sprintf(
73
            'Request: %s %s - Headers %s - Body %s',
74
            $request->getMethod(),
75
            $request->getUri(),
76
            $this->requestHeadersCleanup($request),
77
            $this->requestBodyCleanup($request)
78
        );
79
        $this->logger->info($msg);
80
    }
81
82
    /**
83
     * Return request body without sensitive information.
84
     *
85
     * @param RequestInterface $request The request to log
86
     * @return string
87
     */
88
    protected function requestBodyCleanup(RequestInterface $request): string
89
    {
90
        $body = $request->getBody();
91
        if (empty((string)$body)) {
92
            return '(empty)';
93
        }
94
95
        $data = json_decode($body, true);
96
        foreach (['password', 'old_password', 'confirm-password'] as $field) {
97
            $this->maskPasswordField($data, $field);
98
        }
99
100
        return json_encode($data);
101
    }
102
103
    /**
104
     * Mask password fields in $data.
105
     *
106
     * @param array $data The data
107
     * @param string $field The field
108
     * @return void
109
     */
110
    public function maskPasswordField(array &$data, string $field): void
111
    {
112
        $mask = '***************';
113
        if (!empty($data[$field])) {
114
            $data[$field] = $mask;
115
        }
116
        if (!empty($data['data']['attributes'][$field])) {
117
            $data['data']['attributes'][$field] = $mask;
118
        }
119
    }
120
121
    /**
122
     * Return request headers as string without sensitive information.
123
     *
124
     * @param RequestInterface $request The request to log
125
     * @return string
126
     */
127
    protected function requestHeadersCleanup(RequestInterface $request): string
128
    {
129
        $headers = $request->getHeaders();
130
        foreach (['Authorization', 'X-Api-Key'] as $h) {
131
            if (!empty($headers[$h]) && !empty(array_diff($headers[$h], ['']))) {
132
                $headers[$h] = ['***************'];
133
            }
134
        }
135
136
        return json_encode($headers);
137
    }
138
139
    /**
140
     * Perform response log
141
     *
142
     * @param ResponseInterface $response The response to log
143
     * @return void
144
     */
145
    public function logResponse(ResponseInterface $response): void
146
    {
147
        if (!$this->logger) {
148
            return;
149
        }
150
151
        $msg = sprintf(
152
            'Response: %s %s - Headers %s - Body %s',
153
            $response->getStatusCode(),
154
            $response->getReasonPhrase(),
155
            json_encode($response->getHeaders()),
156
            $this->responseBodyCleanup($response)
157
        );
158
        $this->logger->info($msg);
159
    }
160
161
    /**
162
     * Return response body without sensitive information.
163
     *
164
     * @param ResponseInterface $response The response to log
165
     * @return string
166
     */
167
    protected function responseBodyCleanup(ResponseInterface $response): string
168
    {
169
        $body = $response->getBody();
170
        if (empty((string)$body)) {
171
            return '(empty)';
172
        }
173
174
        $data = json_decode($body, true);
175
        foreach (['jwt', 'renew'] as $tok) {
176
            if (!empty($data['meta'][$tok])) {
177
                $data['meta'][$tok] = '***************';
178
            }
179
        }
180
181
        return json_encode($data);
182
    }
183
}
184