-
-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Log in subscribers when broadcasting a subscription update (#1306)
- Loading branch information
1 parent
552f576
commit de1c2e8
Showing
14 changed files
with
348 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
src/Subscriptions/Iterators/AuthenticatingSyncIterator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
namespace Nuwave\Lighthouse\Subscriptions\Iterators; | ||
|
||
use Closure; | ||
use Exception; | ||
use Illuminate\Contracts\Auth\Factory; | ||
use Illuminate\Contracts\Config\Repository; | ||
use Illuminate\Support\Collection; | ||
use Nuwave\Lighthouse\Subscriptions\Contracts\SubscriptionIterator; | ||
use Nuwave\Lighthouse\Subscriptions\Subscriber; | ||
use Nuwave\Lighthouse\Subscriptions\SubscriptionGuard; | ||
|
||
/** | ||
* Logs in the subscriber as their subscription is resolved. | ||
*/ | ||
class AuthenticatingSyncIterator implements SubscriptionIterator | ||
{ | ||
/** | ||
* @var \Illuminate\Contracts\Config\Repository | ||
*/ | ||
private $configRepository; | ||
|
||
/** | ||
* @var \Illuminate\Contracts\Auth\Factory | ||
*/ | ||
private $authFactory; | ||
|
||
public function __construct(Repository $configRepository, Factory $authFactory) | ||
{ | ||
$this->authFactory = $authFactory; | ||
$this->configRepository = $configRepository; | ||
} | ||
|
||
public function process(Collection $subscribers, Closure $handleSubscriber, Closure $handleError = null): void | ||
{ | ||
// Store the previous default guard name so we can restore it after we're done | ||
$previousGuardName = $this->configRepository->get('auth.defaults.guard'); | ||
|
||
// Set our subscription guard as the default guard for the application | ||
$this->authFactory->shouldUse(SubscriptionGuard::GUARD_NAME); | ||
|
||
/** @var \Nuwave\Lighthouse\Subscriptions\SubscriptionGuard $guard */ | ||
$guard = $this->authFactory->guard(SubscriptionGuard::GUARD_NAME); | ||
|
||
$subscribers->each(static function (Subscriber $item) use ($handleSubscriber, $handleError, $guard): void { | ||
// If there is an authenticated user set in the context, set that user as the authenticated user | ||
if ($item->context->user()) { | ||
$guard->setUser($item->context->user()); | ||
} | ||
|
||
try { | ||
$handleSubscriber($item); | ||
} catch (Exception $e) { | ||
if (! $handleError) { | ||
throw $e; | ||
} | ||
|
||
$handleError($e); | ||
} finally { | ||
// Unset the authenticated user after each iteration to restore the guard to a unauthenticated state | ||
$guard->reset(); | ||
} | ||
}); | ||
|
||
// Restore the previous default guard name | ||
$this->authFactory->shouldUse($previousGuardName); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
namespace Nuwave\Lighthouse\Subscriptions; | ||
|
||
use Illuminate\Auth\GuardHelpers; | ||
use Illuminate\Contracts\Auth\Guard; | ||
use RuntimeException; | ||
|
||
class SubscriptionGuard implements Guard | ||
{ | ||
use GuardHelpers; | ||
|
||
public const GUARD_NAME = 'lighthouse_subscriptions'; | ||
|
||
public function user() | ||
{ | ||
return $this->user; | ||
} | ||
|
||
public function reset() | ||
{ | ||
$this->user = null; | ||
} | ||
|
||
public function validate(array $credentials = []) | ||
{ | ||
throw new RuntimeException('The Lighthouse subscription guard cannot be used for credential based authentication.'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
tests/Unit/Subscriptions/Iterators/AuthenticatingSyncIteratorTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
<?php | ||
|
||
namespace Tests\Unit\Subscriptions\Iterators; | ||
|
||
use Illuminate\Auth\AuthManager; | ||
use Illuminate\Contracts\Auth\Authenticatable; | ||
use Mockery; | ||
use Mockery\MockInterface; | ||
use Nuwave\Lighthouse\Subscriptions\Iterators\AuthenticatingSyncIterator; | ||
use Nuwave\Lighthouse\Subscriptions\Subscriber; | ||
use Nuwave\Lighthouse\Subscriptions\SubscriptionGuard; | ||
|
||
class AuthenticatingSyncIteratorTest extends IteratorTest | ||
{ | ||
public function testIsWellBehavedIterator(): void | ||
{ | ||
$iterator = $this->app->make(AuthenticatingSyncIterator::class); | ||
|
||
$this->assertIteratesOverItemsWithCallback($iterator); | ||
$this->assertPassesExceptionToHandler($iterator); | ||
} | ||
|
||
public function testSetsAndResetsGuardContextAfterEachIteration(): void | ||
{ | ||
$subscriberCount = 3; | ||
|
||
// Give each subscriber a user stub with an ID based on the index of the subscriber in the collection | ||
$subscribers = $this | ||
->subscribers($subscriberCount) | ||
->map(static function (Subscriber $subscriber, int $index): Subscriber { | ||
$subscriber->context->user = new AuthenticatingSyncIteratorAuthenticatableStub($index + 1); | ||
|
||
return $subscriber; | ||
}); | ||
|
||
$guard = Mockery::mock(SubscriptionGuard::class, static function (MockInterface $mock) use ($subscribers) { | ||
$subscribers->each(static function (Subscriber $subscriber) use ($mock) { | ||
$user = $subscriber->context->user(); | ||
|
||
$mock | ||
->shouldReceive('setUser') | ||
->with($user) | ||
->once(); | ||
|
||
$mock | ||
->shouldReceive('user') | ||
->andReturn($user) | ||
->once(); | ||
|
||
$mock | ||
->shouldReceive('reset') | ||
->once(); | ||
}); | ||
}); | ||
|
||
$authManager = $this->app->make(AuthManager::class); | ||
|
||
$authManager->extend(SubscriptionGuard::GUARD_NAME, static function () use ($guard) { | ||
return $guard; | ||
}); | ||
|
||
$processedItems = []; | ||
$authenticatedUsers = []; | ||
$guardBeforeIteration = $authManager->guard(); | ||
|
||
$iterator = $this->app->make(AuthenticatingSyncIterator::class); | ||
$iterator->process( | ||
$subscribers, | ||
static function (Subscriber $subscriber) use (&$processedItems, &$authenticatedUsers, $authManager): void { | ||
$processedItems[] = $subscriber; | ||
$authenticatedUsers[] = $authManager->user(); | ||
} | ||
); | ||
|
||
$this->assertCount($subscriberCount, $processedItems); | ||
$this->assertSame($subscribers->pluck('context.user')->all(), $authenticatedUsers); | ||
$this->assertSame($guardBeforeIteration, $authManager->guard()); | ||
} | ||
} | ||
|
||
class AuthenticatingSyncIteratorAuthenticatableStub implements Authenticatable | ||
{ | ||
/** | ||
* @var int | ||
*/ | ||
private $id; | ||
|
||
public function __construct(int $id) | ||
{ | ||
$this->id = $id; | ||
} | ||
|
||
public function getAuthIdentifierName() | ||
{ | ||
} | ||
|
||
public function getAuthIdentifier() | ||
{ | ||
return $this->id; | ||
} | ||
|
||
public function getAuthPassword() | ||
{ | ||
} | ||
|
||
public function getRememberToken() | ||
{ | ||
} | ||
|
||
public function setRememberToken($value) | ||
{ | ||
} | ||
|
||
public function getRememberTokenName() | ||
{ | ||
} | ||
} |
Oops, something went wrong.