TraceUser::tooManyAttempts()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
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 ProtoneMedia\LaravelTracer\QualifiedRoute;
10
use ProtoneMedia\LaravelTracer\UserRequest;
11
use Symfony\Component\HttpFoundation\Response;
12
13
class TraceUser
14
{
15
    /**
16
     * Rate Limiter
17
     *
18
     * @var \Illuminate\Cache\RateLimiter
19
     */
20
    private $limiter;
21
22
    /**
23
     * Sets the Rate Limiter.
24
     *
25
     * @param \Illuminate\Cache\RateLimiter $limiter
26
     */
27
    public function __construct(RateLimiter $limiter)
28
    {
29
        $this->limiter = $limiter;
30
    }
31
32
    /**
33
     * Handle an incoming request and trace the user
34
     * if the current user is authenticated.
35
     *
36
     * @param  \Illuminate\Http\Request  $request
37
     * @param  \Closure  $next
38
     * @return mixed
39
     */
40
    public function handle($request, Closure $next)
41
    {
42
        $response = $next($request);
43
44
        if (!$user = $request->user()) {
45
            return $response;
46
        }
47
48
        if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
49
            return $response;
50
        }
51
52
        $this->traceUserRequest($user, $request, $response);
53
54
        return $response;
55
    }
56
57
    /**
58
     * Stores the user request in the database.
59
     *
60
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
61
     * @param  \Illuminate\Http\Request  $request
62
     * @param  \Symfony\Component\HttpFoundation\Response  $response
63
     *
64
     * @return \ProtoneMedia\LaravelTracer\UserRequest|null
65
     */
66
    private function traceUserRequest(UserContract $user, Request $request, Response $response): ? UserRequest
67
    {
68
        $qualified = $request->qualifiedRoute();
69
70
        if ($this->tooManyAttempts($qualified)) {
71
            return null;
72
        }
73
74
        if (!$this->shouldTraceUser($request, $response)) {
75
            return null;
76
        }
77
78
        return UserRequest::create([
79
            'user_id'         => $user->getAuthIdentifier(),
80
            'qualified_route' => $qualified->name(),
81
        ]);
82
    }
83
84
    /**
85
     * Returns a boolean wether this request has been attemped to
86
     * trace too many times.
87
     *
88
     * @param  \ProtoneMedia\LaravelTracer\QualifiedRoute  $qualified
89
     * @return boolean
90
     */
91
    private function tooManyAttempts(QualifiedRoute $qualified): bool
92
    {
93
        if (!$secondsBetweenLogs = $qualified->secondsBetweenLogs()) {
94
            return false;
95
        }
96
97
        if ($this->limiter->tooManyAttempts($qualified->name(), 1)) {
98
            return true;
99
        }
100
101
        $this->limiter->hit($qualified->name(), $secondsBetweenLogs);
0 ignored issues
show
Bug introduced by
It seems like $secondsBetweenLogs can also be of type true; however, parameter $decaySeconds of Illuminate\Cache\RateLimiter::hit() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
        $this->limiter->hit($qualified->name(), /** @scrutinizer ignore-type */ $secondsBetweenLogs);
Loading history...
102
103
        return false;
104
    }
105
106
    /**
107
     * Calls the callable method that can be specified in the config file.
108
     *
109
     * @param  \Illuminate\Http\Request  $request
110
     * @param  \Symfony\Component\HttpFoundation\Response $response
111
     *
112
     * @return boolean
113
     */
114
    private function shouldTraceUser(Request $request, Response $response): bool
115
    {
116
        if (!$callable = config('laravel-tracer.should_trace_user')) {
117
            return true;
118
        }
119
120
        return app()->call($callable, [$request, $response]);
121
    }
122
}
123