Completed
Push — develop ( d4e5ea...7012a0 )
by Sean
07:19
created

JWTGuard::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 0
cts 6
cp 0
rs 9.4286
cc 1
eloc 4
nc 1
nop 3
crap 2
1
<?php
2
3
/*
4
 * This file is part of jwt-auth
5
 *
6
 * (c) Sean Tymon <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Tymon\JWTAuth;
13
14
use Illuminate\Http\Request;
15
use Illuminate\Auth\GuardHelpers;
16
use Illuminate\Contracts\Auth\Guard;
17
use Illuminate\Contracts\Auth\UserProvider;
18
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
19
20
class JWTGuard implements Guard
21
{
22
    use GuardHelpers;
23
24
    /**
25
     * The user we last attempted to retrieve.
26
     *
27
     * @var \Illuminate\Contracts\Auth\Authenticatable
28
     */
29
    protected $lastAttempted;
30
31
    /**
32
     * The JWTAuth instance.
33
     *
34
     * @var \Tymon\JWTAuth\JWTAuth
35
     */
36
    protected $auth;
37
38
    /**
39
     * The request instance.
40
     *
41
     * @var \Illuminate\Http\Request
42
     */
43
    protected $request;
44
45
    public function __construct(JWTAuth $auth, UserProvider $provider, Request $request)
1 ignored issue
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
46
    {
47
        $this->auth = $auth;
48
        $this->provider = $provider;
49
        $this->request = $request;
50
    }
51
52
    /**
53
     * Get the currently authenticated user.
54
     *
55
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
56
     */
57
    public function user()
58
    {
59
        if (! is_null($this->user)) {
60
            return $this->user;
61
        }
62
63
        if (! $this->requireToken()->check()) {
64
            return null;
65
        }
66
67
        $id = $this->auth->getPayload()->get('sub');
68
69
        return $this->user = $this->provider->retrieveById($id);
70
    }
71
72
    /**
73
     * Validate a user's credentials.
74
     *
75
     * @param  array  $credentials
76
     *
77
     * @return boolean
78
     */
79
    public function validate(array $credentials = [], $login = true)
80
    {
81
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
82
83
        if ($this->hasValidCredentials($user, $credentials)) {
84
85
            if ($login) {
86
                $this->setUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->provider->retriev...edentials($credentials) on line 81 can be null; however, Illuminate\Auth\GuardHelpers::setUser() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
87
88
                return $this->auth->fromUser($user);
0 ignored issues
show
Documentation introduced by
$user is of type object<Illuminate\Contra...h\Authenticatable>|null, but the function expects a object<Tymon\JWTAuth\Contracts\JWTSubject>.

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...
Bug Best Practice introduced by
The return type of return $this->auth->fromUser($user); (string) is incompatible with the return type declared by the interface Illuminate\Contracts\Auth\Guard::validate of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
89
            }
90
91
            return true;
92
        }
93
94
        return false;
95
    }
96
97
    /**
98
     * Logout the user, thus invalidating the token.
99
     *
100
     * @param  boolean  $forceForever
101
     *
102
     * @return void
103
     */
104
    public function logout($forceForever = false)
105
    {
106
        $this->requireToken()->invalidate($forceForever);
107
108
        $this->user = null;
109
        $this->auth->unsetToken();
110
    }
111
112
    /**
113
     * Get the user provider used by the guard.
114
     *
115
     * @return \Illuminate\Contracts\Auth\UserProvider
116
     */
117
    public function getProvider()
118
    {
119
        return $this->provider;
120
    }
121
122
    /**
123
     * Set the user provider used by the guard.
124
     *
125
     * @param  \Illuminate\Contracts\Auth\UserProvider  $provider
126
     *
127
     * @return void
128
     */
129
    public function setProvider(UserProvider $provider)
130
    {
131
        $this->provider = $provider;
132
    }
133
134
    /**
135
     * Return the currently cached user.
136
     *
137
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
138
     */
139
    public function getUser()
140
    {
141
        return $this->user;
142
    }
143
144
    /**
145
     * Get the current request instance.
146
     *
147
     * @return \Symfony\Component\HttpFoundation\Request
148
     */
149
    public function getRequest()
150
    {
151
        return $this->request ?: Request::createFromGlobals();
152
    }
153
154
    /**
155
     * Set the current request instance.
156
     *
157
     * @param  \Symfony\Component\HttpFoundation\Request  $request
158
     *
159
     * @return $this
160
     */
161
    public function setRequest(Request $request)
1 ignored issue
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
162
    {
163
        $this->request = $request;
164
165
        return $this;
166
    }
167
168
    /**
169
     * Get the last user we attempted to authenticate.
170
     *
171
     * @return \Illuminate\Contracts\Auth\Authenticatable
172
     */
173
    public function getLastAttempted()
174
    {
175
        return $this->lastAttempted;
176
    }
177
178
    /**
179
     * Determine if the user matches the credentials.
180
     *
181
     * @param  mixed  $user
182
     * @param  array  $credentials
183
     *
184
     * @return boolean
185
     */
186
    protected function hasValidCredentials($user, $credentials)
187
    {
188
        return ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
189
    }
190
191
    /**
192
     * Ensure that a token is available in the request
193
     *
194
     * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
195
     *
196
     * @return \Tymon\JWTAuth\JWTAuth
197
     */
198
    protected function requireToken()
199
    {
200
        if (! $this->auth->getToken()) {
201
            throw new BadRequestHttpException($e->getMessage());
202
        }
203
204
        return $this->auth;
205
    }
206
}
207