Completed
Push — master ( a0753c...dbfa15 )
by Pascal
01:31
created

TraceUser::shouldTraceUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Protonemedia\LaravelTracer\Middleware;
4
5
use Closure;
6
use Illuminate\Cache\RateLimiter;
7
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
8
use Illuminate\Http\Request;
9
use Illuminate\Routing\Route;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Collection;
12
use Protonemedia\LaravelTracer\UserRequest;
13
use Symfony\Component\HttpFoundation\Response;
14
15
class TraceUser
16
{
17
    /**
18
     * Rate Limiter
19
     *
20
     * @var \Illuminate\Cache\RateLimiter
21
     */
22
    private $limiter;
23
24
    /**
25
     * Sets the Rate Limiter.
26
     *
27
     * @param \Illuminate\Cache\RateLimiter $limiter
28
     */
29
    public function __construct(RateLimiter $limiter)
30
    {
31
        $this->limiter = $limiter;
32
    }
33
34
    /**
35
     * Handle an incoming request and trace the user
36
     * if the current user is authenticated.
37
     *
38
     * @param  \Illuminate\Http\Request  $request
39
     * @param  \Closure  $next
40
     * @return mixed
41
     */
42
    public function handle($request, Closure $next)
43
    {
44
        $response = $next($request);
45
46
        if (!$user = $request->user()) {
47
            return $response;
48
        }
49
50
        if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
51
            return $response;
52
        }
53
54
        $this->traceUserRequest($user, $request, $response);
55
56
        return $response;
57
    }
58
59
    /**
60
     * Stores the user request in the database.
61
     *
62
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
63
     * @param  \Illuminate\Http\Request  $request
64
     * @param  \Symfony\Component\HttpFoundation\Response  $response
65
     *
66
     * @return \Protonemedia\LaravelTracer\UserRequest|null
67
     */
68
    private function traceUserRequest(UserContract $user, Request $request, Response $response):  ? UserRequest
69
    {
70
        $qualified = $this->qualify($request, $request->route());
0 ignored issues
show
Documentation introduced by
$request->route() is of type object|string, but the function expects a object<Illuminate\Routing\Route>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
71
72
        if ($this->tooManyAttempts($qualified)) {
73
            return null;
74
        }
75
76
        if (!$this->shouldTraceUser($request, $response)) {
77
            return null;
78
        }
79
80
        return UserRequest::create([
81
            'user_id'         => $user->getAuthIdentifier(),
82
            'qualified_route' => $qualified['name'],
83
        ]);
84
    }
85
86
    /**
87
     * Returns the qualified name for the given request and route data.
88
     *
89
     * @param  \Illuminate\Http\Request  $request
90
     * @param  \Illuminate\Routing\Route   $route
91
     * @return mixed
92
     */
93
    private function qualify(Request $request, Route $route) : array
94
    {
95
        if (!$qualified = QualifyRoute::getByUri($route->uri())) {
96
            return [
97
                'name'                 => $route->getName() ?: $request->path(),
98
                'seconds_between_logs' => null,
99
            ];
100
        }
101
102
        Collection::make($route->parameters())->map(function ($value, $parameter) use (&$qualified) {
103
            $qualified['name'] = str_replace("{{$parameter}}", $value, $qualified['name']);
104
        });
105
106
        return $qualified;
107
    }
108
109
    /**
110
     * Returns a boolean wether this request has been attemped to
111
     * trace too many times.
112
     *
113
     * @param  array  $qualified
114
     * @return boolean
115
     */
116
    private function tooManyAttempts(array $qualified): bool
117
    {
118
        if (is_null($qualified['seconds_between_logs'])) {
119
            $qualified['seconds_between_logs'] = config('laravel-tracer.seconds_between_logs');
120
        }
121
122
        if (!$qualified['seconds_between_logs']) {
123
            return false;
124
        }
125
126
        if ($this->limiter->tooManyAttempts($qualified['name'], 1)) {
127
            return true;
128
        }
129
130
        $this->limiter->hit($qualified['name'], $qualified['seconds_between_logs']);
131
132
        return false;
133
    }
134
135
    private function shouldTraceUser(Request $request, Response $response): bool
136
    {
137
        if (!$callable = config('laravel-tracer.should_trace_user')) {
138
            return true;
139
        }
140
141
        return app()->call($callable, [$request, $response]);
142
    }
143
}
144