Skip to content

Commit 80db8a0

Browse files
committed
Merge branch 'feat/attempt-callbacks' into 8.x
2 parents 58a6d2d + 696cbc7 commit 80db8a0

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

src/Illuminate/Auth/SessionGuard.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Illuminate\Contracts\Cookie\QueueingFactory as CookieJar;
1818
use Illuminate\Contracts\Events\Dispatcher;
1919
use Illuminate\Contracts\Session\Session;
20+
use Illuminate\Support\Arr;
2021
use Illuminate\Support\Facades\Hash;
2122
use Illuminate\Support\Str;
2223
use Illuminate\Support\Traits\Macroable;
@@ -374,6 +375,34 @@ public function attempt(array $credentials = [], $remember = false)
374375
return false;
375376
}
376377

378+
/**
379+
* Attempt to authenticate a user with credentials and additional callbacks.
380+
*
381+
* @param array $credentials
382+
* @param false $remember
383+
* @param array|callable $callbacks
384+
* @return bool
385+
*/
386+
public function attemptWith(array $credentials = [], $remember = false, $callbacks = null)
387+
{
388+
$this->fireAttemptEvent($credentials, $remember);
389+
390+
$this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
391+
392+
// This method does the exact same thing as attempt, but also executes callbacks after
393+
// the user is retrieved and validated. If one of the callbacks returns falsy we do
394+
// not login the user. Instead, we will fail the specific authentication attempt.
395+
if ($this->hasValidCredentials($user, $credentials) && $this->shouldLogin($callbacks, $user)) {
396+
$this->login($user, $remember);
397+
398+
return true;
399+
}
400+
401+
$this->fireFailedEvent($user, $credentials);
402+
403+
return false;
404+
}
405+
377406
/**
378407
* Determine if the user matches the credentials.
379408
*
@@ -392,6 +421,24 @@ protected function hasValidCredentials($user, $credentials)
392421
return $validated;
393422
}
394423

424+
/**
425+
* Determine if the user should login by executing the given callbacks.
426+
*
427+
* @param array|callable|null $callbacks
428+
* @param \Illuminate\Contracts\Auth\Authenticatable $user
429+
* @return bool
430+
*/
431+
protected function shouldLogin($callbacks, AuthenticatableContract $user)
432+
{
433+
foreach (Arr::wrap($callbacks) as $callback) {
434+
if (! $callback($user, $this)) {
435+
return false;
436+
}
437+
}
438+
439+
return true;
440+
}
441+
395442
/**
396443
* Log the given user ID into the application.
397444
*

tests/Auth/AuthGuardTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,48 @@ public function testAttemptReturnsFalseIfUserNotGiven()
126126
$this->assertFalse($mock->attempt(['foo']));
127127
}
128128

129+
public function testAttemptAndWithCallbacks()
130+
{
131+
[$session, $provider, $request, $cookie] = $this->getMocks();
132+
$mock = $this->getMockBuilder(SessionGuard::class)->onlyMethods(['getName'])->setConstructorArgs(['default', $provider, $session, $request])->getMock();
133+
$mock->setDispatcher($events = m::mock(Dispatcher::class));
134+
$user = m::mock(Authenticatable::class);
135+
$events->shouldReceive('dispatch')->times(3)->with(m::type(Attempting::class));
136+
$events->shouldReceive('dispatch')->once()->with(m::type(Login::class));
137+
$events->shouldReceive('dispatch')->once()->with(m::type(Authenticated::class));
138+
$events->shouldReceive('dispatch')->twice()->with(m::type(Validated::class));
139+
$events->shouldReceive('dispatch')->twice()->with(m::type(Failed::class));
140+
$mock->expects($this->once())->method('getName')->willReturn('foo');
141+
$user->shouldReceive('getAuthIdentifier')->once()->andReturn('bar');
142+
$mock->getSession()->shouldReceive('put')->with('foo', 'bar')->once();
143+
$session->shouldReceive('migrate')->once();
144+
$mock->getProvider()->shouldReceive('retrieveByCredentials')->times(3)->with(['foo'])->andReturn($user);
145+
$mock->getProvider()->shouldReceive('validateCredentials')->twice()->andReturnTrue();
146+
$mock->getProvider()->shouldReceive('validateCredentials')->once()->andReturnFalse();
147+
148+
$this->assertTrue($mock->attemptWith(['foo'], false, function ($user, $guard) {
149+
static::assertInstanceOf(Authenticatable::class, $user);
150+
static::assertInstanceOf(SessionGuard::class, $guard);
151+
152+
return true;
153+
}));
154+
155+
$this->assertFalse($mock->attemptWith(['foo'], false, function ($user, $guard) {
156+
static::assertInstanceOf(Authenticatable::class, $user);
157+
static::assertInstanceOf(SessionGuard::class, $guard);
158+
159+
return false;
160+
}));
161+
162+
$executed = false;
163+
164+
$this->assertFalse($mock->attemptWith(['foo'], false, function () use (&$executed) {
165+
return $executed = true;
166+
}));
167+
168+
$this->assertFalse($executed);
169+
}
170+
129171
public function testLoginStoresIdentifierInSession()
130172
{
131173
[$session, $provider, $request, $cookie] = $this->getMocks();

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy