Completed
Push — develop ( ce277d...4aac6d )
by Abdelrahman
01:22
created

PasswordResetBroker::validateNewPassword()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A PasswordResetBroker::createToken() 0 6 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Rinvex\Auth\Services;
6
7
use Closure;
8
use UnexpectedValueException;
9
use Illuminate\Contracts\Auth\UserProvider;
10
use Rinvex\Auth\Contracts\CanResetPasswordContract;
11
use Rinvex\Auth\Contracts\PasswordResetBrokerContract;
12
13
class PasswordResetBroker implements PasswordResetBrokerContract
0 ignored issues
show
Bug introduced by
There is at least one abstract method in this class. Maybe declare it as abstract, or implement the remaining methods: validateNewPassword, validator
Loading history...
14
{
15
    /**
16
     * The application key.
17
     *
18
     * @var string
19
     */
20
    protected $key;
21
22
    /**
23
     * The user provider implementation.
24
     *
25
     * @var \Illuminate\Contracts\Auth\UserProvider
26
     */
27
    protected $users;
28
29
    /**
30
     * The number of minutes that the reset token should be considered valid.
31
     *
32
     * @var int
33
     */
34
    protected $expiration;
35
36
    /**
37
     * Create a new verification broker instance.
38
     *
39
     * @param \Illuminate\Contracts\Auth\UserProvider $users
40
     * @param string                                  $key
41
     * @param int                                     $expiration
42
     */
43
    public function __construct(UserProvider $users, $key, $expiration)
44
    {
45
        $this->key = $key;
46
        $this->users = $users;
47
        $this->expiration = $expiration;
48
    }
49
50
    /**
51
     * Send a password reset link to a user.
52
     *
53
     * @param array $credentials
54
     *
55
     * @return string
56
     */
57
    public function sendResetLink(array $credentials): string
58
    {
59
        // First we will check to see if we found a user at the given credentials and
60
        // if we did not we will redirect back to this current URI with a piece of
61
        // "flash" data in the session to indicate to the developers the errors.
62
        $user = $this->getUser($credentials);
63
64
        if (is_null($user)) {
65
            return static::INVALID_USER;
66
        }
67
68
        $expiration = now()->addMinutes($this->expiration)->timestamp;
69
70
        // Once we have the reset token, we are ready to send the message out to this
71
        // user with a link to reset their password. We will then redirect back to
72
        // the current URI having nothing set in the session to indicate errors.
73
        $user->sendPasswordResetNotification($this->createToken($user, $expiration), $expiration);
0 ignored issues
show
Documentation introduced by
$user is of type object<Illuminate\Contracts\Auth\Authenticatable>, but the function expects a object<Rinvex\Auth\Contr...nResetPasswordContract>.

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...
74
75
        return static::RESET_LINK_SENT;
76
    }
77
78
    /**
79
     * Reset the password for the given token.
80
     *
81
     * @param array    $credentials
82
     * @param \Closure $callback
83
     *
84
     * @return mixed
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string|\Illuminate\Contracts\Auth\Authenticatable.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
85
     */
86
    public function reset(array $credentials, Closure $callback)
87
    {
88
        $user = $this->validateReset($credentials);
89
90
        // If the responses from the validate method is not a user instance, we will
91
        // assume that it is a redirect and simply return it from this method and
92
        // the user is properly redirected having an error message on the post.
93
        if (! $user instanceof CanResetPasswordContract) {
94
            return $user;
95
        }
96
97
        $password = $credentials['password'];
98
99
        // Once the reset has been validated, we'll call the given callback with the
100
        // new password. This gives the user an opportunity to store the password
101
        // in their persistent storage.
102
        $callback($user, $password);
103
104
        return static::PASSWORD_RESET;
105
    }
106
107
    /**
108
     * Get the user for the given credentials.
109
     *
110
     * @param array $credentials
111
     *
112
     * @throws \UnexpectedValueException
113
     *
114
     * @return \Rinvex\Auth\Contracts\CanResetPasswordContract|null
0 ignored issues
show
Documentation introduced by
Should the return type not be \Illuminate\Contracts\Auth\Authenticatable|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
115
     */
116
    public function getUser(array $credentials): ?CanResetPasswordContract
117
    {
118
        $user = $this->users->retrieveByCredentials(array_only($credentials, ['email']));
0 ignored issues
show
Deprecated Code introduced by
The function array_only() has been deprecated with message: Arr::only() should be used directly instead. Will be removed in Laravel 6.0.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
119
120
        if ($user && ! $user instanceof CanResetPasswordContract) {
121
            throw new UnexpectedValueException('User must implement CanResetPassword interface.');
122
        }
123
124
        return $user;
125
    }
126
127
    /**
128
     * Create a new password reset token for the given user.
129
     *
130
     * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user
131
     * @param int                                             $expiration
132
     *
133
     * @return string
134
     */
135
    public function createToken(CanResetPasswordContract $user, $expiration): string
136
    {
137
        $payload = $this->buildPayload($user, $user->getEmailForPasswordReset(), $expiration);
138
139
        return hash_hmac('sha256', $payload, $this->getKey());
140
    }
141
142
    /**
143
     * Validate the given password reset token.
144
     *
145
     * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user
146
     * @param array                                           $credentials
147
     *
148
     * @return bool
149
     */
150
    public function validateToken(CanResetPasswordContract $user, array $credentials): bool
151
    {
152
        $payload = $this->buildPayload($user, $credentials['email'], $credentials['expiration']);
153
154
        return hash_equals($credentials['token'], hash_hmac('sha256', $payload, $this->getKey()));
155
    }
156
157
    /**
158
     * Validate the given expiration timestamp.
159
     *
160
     * @param int $expiration
161
     *
162
     * @return bool
163
     */
164
    public function validateTimestamp($expiration): bool
165
    {
166
        return now()->createFromTimestamp($expiration)->isFuture();
167
    }
168
169
    /**
170
     * Return the application key.
171
     *
172
     * @return string
173
     */
174
    public function getKey(): string
175
    {
176
        if (starts_with($this->key, 'base64:')) {
0 ignored issues
show
Deprecated Code introduced by
The function starts_with() has been deprecated with message: Str::startsWith() should be used directly instead. Will be removed in Laravel 6.0.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
177
            return base64_decode(mb_substr($this->key, 7));
178
        }
179
180
        return $this->key;
181
    }
182
183
    /**
184
     * Returns the payload string containing.
185
     *
186
     * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user
187
     * @param string                                          $email
188
     * @param int                                             $expiration
189
     *
190
     * @return string
191
     */
192
    protected function buildPayload(CanResetPasswordContract $user, $email, $expiration): string
193
    {
194
        return implode(';', [
195
            $email,
196
            $expiration,
197
            $user->getKey(),
0 ignored issues
show
Bug introduced by
The method getKey() does not seem to exist on object<Rinvex\Auth\Contr...nResetPasswordContract>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
198
            $user->password,
0 ignored issues
show
Bug introduced by
Accessing password on the interface Rinvex\Auth\Contracts\CanResetPasswordContract suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
199
        ]);
200
    }
201
202
    /**
203
     * Validate a password reset for the given credentials.
204
     *
205
     * @param array $credentials
206
     *
207
     * @return \Illuminate\Contracts\Auth\CanResetPassword|string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|\Illuminate\Contracts\Auth\Authenticatable?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
208
     */
209
    protected function validateReset(array $credentials)
210
    {
211
        if (is_null($user = $this->getUser($credentials))) {
212
            return static::INVALID_USER;
213
        }
214
215
        if (! $this->validateToken($user, $credentials)) {
0 ignored issues
show
Documentation introduced by
$user is of type object<Illuminate\Contracts\Auth\Authenticatable>, but the function expects a object<Rinvex\Auth\Contr...nResetPasswordContract>.

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...
216
            return static::INVALID_TOKEN;
217
        }
218
219
        if (! $this->validateTimestamp($credentials['expiration'])) {
220
            return static::EXPIRED_TOKEN;
221
        }
222
223
        return $user;
224
    }
225
}
226