Completed
Push — feature/EVO-7278-tracking-info... ( b6e89d )
by
unknown
128:50 queued 120:43
created

ActivityManager::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 2
1
<?php
2
/**
3
 * Keeping all activity in one place to be controlled
4
 */
5
6
namespace Graviton\AuditTrackingBundle\Manager;
7
8
use Graviton\AuditTrackingBundle\Document\AuditTracking;
9
use Graviton\RestBundle\Event\ModelEvent;
10
use Guzzle\Http\Message\Header;
11
use Symfony\Bridge\Doctrine\ManagerRegistry;
12
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
13
use Symfony\Component\HttpFoundation\RequestStack;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpFoundation\Request;
16
17
/**
18
 * Class ActivityManager
19
 * @package Graviton\AuditTrackingBundle\Manager
20
 *
21
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
22
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
23
 * @link     http://swisscom.ch
24
 */
25
class ActivityManager
26
{
27
    /** Max char length of saved content data */
28
    const CONTENT_MAX_LENGTH = 2048;
29
30
    /** @var bool If log is enabled */
31
    private $enabled = false;
32
33
    /** @var Request $request */
34
    private $request;
35
36
    /** @var array */
37
    private $configurations;
38
39
    /** @var AuditTracking */
40
    private $document;
41
42
    /** @var array Events that shall be stored */
43
    private $events = [];
44
45
    /** @var string  */
46
    private $globalRequestLocation = '';
47
48
    /**
49
     * DBActivityListener constructor.
50
     *
51
     * @param RequestStack  $requestStack Sf request data
52
     * @param AuditTracking $document     DocumentCollection for event
53
     */
54 4
    public function __construct(
55
        RequestStack  $requestStack,
56
        AuditTracking $document
57
    ) {
58 4
        $this->request = $requestStack ? $requestStack->getCurrentRequest() : false;
0 ignored issues
show
Documentation Bug introduced by
It seems like $requestStack ? $request...urrentRequest() : false can also be of type false. However, the property $request is declared as type object<Symfony\Component\HttpFoundation\Request>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
59 4
        $this->document = $document;
60 4
    }
61
62
    /**
63
     * Set permission and access configuration
64
     *
65
     * @param array $configurations key value config
66
     * @return void
67
     */
68 4
    public function setConfiguration(array $configurations)
69
    {
70 4
        $this->configurations = $configurations;
71 4
        if ($this->runTracking()) {
72
            $this->enabled = true;
73
        }
74 4
    }
75
76
    /**
77
     * Return casted value from configuration.
78
     *
79
     * @param string $key  Configuration key
80
     * @param string $cast Type of object is expected to be returned
81
     * @return int|string|bool|array
82
     * @throws ParameterNotFoundException
83
     */
84 2
    public function getConfigValue($key, $cast = 'string')
85
    {
86 2
        if (array_key_exists($key, $this->configurations)) {
87 2
            if ('bool' == $cast) {
88 2
                return (boolean) $this->configurations[$key];
89 2
            }if ('array' == $cast) {
90 2
                return (array) $this->configurations[$key];
91 2
            } elseif ('string' == $cast) {
92 2
                return (string) $this->configurations[$key];
93 2
            } elseif ('int' == $cast) {
94 2
                return (int) $this->configurations[$key];
95
            }
96
        }
97
        throw new ParameterNotFoundException('ActivityManager could not find required configuration: '.$key);
98
    }
99
100
    /**
101
     * Check if this the Call has to be logged
102
     *
103
     * @return bool
104
     */
105 4
    private function runTracking()
0 ignored issues
show
Coding Style introduced by
function runTracking() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
106
    {
107
        //Ignore if no request, import fixtures.
108 4
        if (!$this->request) {
109 4
            return false;
110
        }
111
112
        // Check if enable
113
        if (!$this->getConfigValue('log_enabled', 'bool')) {
114
            return false;
115
        }
116
        
117
        // We never log tracking service calls
118
        $excludeUrls = $this->getConfigValue('exlude_urls', 'array');
119
        if ($excludeUrls) {
120
            $currentUrl = $this->request->getRequestUri();
121
            foreach ($excludeUrls as $url) {
0 ignored issues
show
Bug introduced by
The expression $excludeUrls of type boolean|array|string|integer is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
122
                if (substr($currentUrl, 0, strlen($url)) == $url) {
123
                    return false;
124
                }
125
            }
126
        }
127
128
        // Check if we wanna log test and localhost calls
129
        if (!$this->getConfigValue('log_test_calls', 'bool')
130
            && !in_array($this->request->getHost(), ['localhost', '127.0.0.1'])) {
131
            return false;
132
        }
133
134
        return true;
135
    }
136
137
    /**
138
     * Incoming request done by user
139
     * @param Request $request sf response priority 1
140
     * @return void
141
     */
142
    public function registerRequestEvent(Request $request)
143
    {
144
        if (!$this->enabled) {
145
            return;
146
        }
147
        // Check if this request event shall be registered
148
        $saveEvents = $this->getConfigValue('requests', 'array');
149
        $method = $request->getMethod();
150
        $this->globalRequestLocation = $request->getRequestUri();
151
        if (!in_array($method, $saveEvents)) {
152
            return;
153
        }
154
155
        $content = substr($request->getContent(), 0, self::CONTENT_MAX_LENGTH);
156
157
        $data = ['ip' => $request->getClientIp()];
158
159
        if ($this->getConfigValue('request_headers', 'bool')) {
160
            $data['headers'] = $request->headers->all();
161
        }
162
        if ($length=$this->getConfigValue('request_content', 'int')) {
163
            $cnt = mb_check_encoding($content, 'UTF-8') ? $content : 'Content omitted, since it is not utf-8';
164
            $data['content'] = ($length==1) ? $cnt : substr($cnt, 0, $length);
165
        }
166
167
        /** @var AuditTracking $event */
168
        $event = new $this->document();
169
        $event->setAction('request');
170
        $event->setType($method);
171
        $event->setData((object) $data);
172
        $event->setLocation($request->getRequestUri());
173
        $event->setCreatedAt(new \DateTime());
174
        $this->events[] = $event;
175
    }
176
177
    /**
178
     * The response returned to user
179
     *
180
     * @param Response $response sf response
181
     * @return void
182
     */
183
    public function registerResponseEvent(Response $response)
184
    {
185
        if (!$this->enabled) {
186
            return;
187
        }
188
        if (!$this->getConfigValue('response', 'bool')) {
189
            return;
190
        }
191
192
        $data = [];
193
        $statusCode = '0';
194
195
        if (method_exists($response, 'getStatusCode')) {
196
            $statusCode = $response->getStatusCode();
197
        }
198
        if ($length=$this->getConfigValue('response_content', 'int') && method_exists($response, 'getContent')) {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $length = ($this->getCon...esponse, 'getContent')), Probably Intended Meaning: ($length = $this->getCon...response, 'getContent')
Loading history...
199
            $cnt = mb_check_encoding($response->getContent(), 'UTF-8') ?
200
                $response->getContent() : 'Content omitted, since it is not utf-8';
201
            $data['content'] = ($length==1) ? $cnt : substr($cnt, 0, $length);
202
        }
203
        if ($this->getConfigValue('response_content', 'bool')) {
204
            $data['header']  = $response->headers->all();
205
        }
206
207
        // Header links
208
        $location = $this->extractHeaderLink($response->headers->get('link'), 'self');
209
210
        /** @var AuditTracking $audit */
211
        $audit = new $this->document();
212
        $audit->setAction('response');
213
        $audit->setType($statusCode);
214
        $audit->setData((object) $data);
215
        $audit->setLocation($location);
216
        $audit->setCreatedAt(new \DateTime());
217
        $this->events[] = $audit;
218
    }
219
220
    /**
221
     * Capture possible un-handled exceptions in php
222
     *
223
     * @param \Exception $exception The exception thrown in service.
224
     * @return void
225
     */
226
    public function registerExceptionEvent(\Exception $exception)
227
    {
228
        if (!$this->enabled) {
229
            return;
230
        }
231
        if (!$this->getConfigValue('exceptions', 'bool')) {
232
            return;
233
        }
234
        $data = (object) [
235
            'message'   => $exception->getMessage(),
236
            'trace'     => $exception->getTraceAsString()
237
        ];
238
239
        /** @var AuditTracking $audit */
240
        $audit = new $this->document();
241
        $audit->setAction('exception');
242
        $audit->setType($exception->getCode());
243
        $audit->setData($data);
244
        $audit->setLocation(get_class($exception));
245
        $audit->setCreatedAt(new \DateTime());
246
        $this->events[] = $audit;
247
    }
248
249
    /**
250
     * Any database events, update, save or delete
251
     *
252
     * Available $event->getCollection() would give you the full object.
253
     *
254
     * @param ModelEvent $event Document object changed
255
     * @return void
256
     */
257
    public function registerDocumentModelEvent(ModelEvent $event)
258
    {
259
        if (!$this->enabled) {
260
            return;
261
        }
262
        if ((!($dbEvents = $this->getConfigValue('database', 'array')))) {
263
            return;
264
        }
265
        if (!in_array($event->getAction(), $dbEvents)) {
266
            return;
267
        }
268
269
        $data = (object) [
270
            'class' => $event->getCollectionClass()
271
        ];
272
273
        /** @var AuditTracking $audit */
274
        $audit = new $this->document();
275
        $audit->setAction($event->getAction());
276
        $audit->setType('collection');
277
        $audit->setData($data);
278
        $audit->setLocation($this->globalRequestLocation);
279
        $audit->setCollectionId($event->getCollectionId());
280
        $audit->setCollectionName($event->getCollectionName());
281
        $audit->setCreatedAt(new \DateTime());
282
283
        $this->events[] = $audit;
284
    }
285
286
    /**
287
     * Parse and extract customer header links
288
     *
289
     * @param string $strHeaderLink sf header links
290
     * @param string $extract       desired key to be found
291
     * @return string
292
     */
293 2
    private function extractHeaderLink($strHeaderLink, $extract = 'self')
294
    {
295 2
        if (!$strHeaderLink) {
296
            return '';
297
        }
298
299 2
        $parts = [];
300 2
        foreach (explode(',', $strHeaderLink) as $link) {
301 2
            $link = explode(';', $link);
302 2
            if (count($link)==2) {
303 2
                $parts[str_replace(['rel=','"'], '', trim($link[1]))] =  str_replace(['<','>'], '', $link[0]);
304 1
            }
305 1
        }
306
307 2
        return  array_key_exists($extract, $parts) ? $parts[$extract] : '';
308
    }
309
310
    /**
311
     * Get events AuditTracking
312
     *
313
     * @return array
314
     */
315
    public function getEvents()
316
    {
317
        return $this->events;
318
    }
319
}
320