Skip to content

Commit

Permalink
[spiral/auth-http] Adding TokenStorageScope (#931)
Browse files Browse the repository at this point in the history
  • Loading branch information
msmakouz committed Apr 26, 2023
1 parent d2a547a commit ae80144
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 6 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# CHANGELOG

## Unreleased
- **Medium Impact Changes**
- [spiral/scaffolder] Method `baseDirectory` of `Spiral\Scaffolder\Config\ScaffolderConfig` class is deprecated.
- **Other Features**
- Added `Spiral\Auth\TokenStorageScope`, this class can be used to get the concrete implementation of
the token storage in a current container scope.
- [spiral/auth-http] Added a `Spiral\Auth\TokenStorageInterface` binding in the `Spiral\Auth\Middleware\AuthMiddleware`
with the used TokenStorage.
- [spiral/scaffolder] Added new public method `declarationDirectory` to the `Spiral\Scaffolder\Config\ScaffolderConfig`
class that returns the directory path of the specified declaration, or default directory path if not specified.
- **Medium Impact Changes**
- [spiral/scaffolder] Method `baseDirectory` of `Spiral\Scaffolder\Config\ScaffolderConfig` class is deprecated.

## 3.7.1 - 2023-04-21
- **Bug Fixes**
Expand Down
2 changes: 0 additions & 2 deletions src/Auth/src/TokenStorageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ interface TokenStorageInterface
/**
* Load token by id, must return null if token not found.
*
*
* @throws TokenStorageException
*/
public function load(string $id): ?TokenInterface;
Expand All @@ -31,7 +30,6 @@ public function create(array $payload, \DateTimeInterface $expiresAt = null): To
/**
* Delete token from the persistent storage.
*
*
* @throws TokenStorageException
*/
public function delete(TokenInterface $token): void;
Expand Down
5 changes: 4 additions & 1 deletion src/AuthHttp/src/Middleware/AuthMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public function process(Request $request, RequestHandlerInterface $handler): Res
$authContext = $this->initContext($request, new AuthContext($this->actorProvider, $this->eventDispatcher));

$response = $this->scope->runScope(
[AuthContextInterface::class => $authContext],
[
AuthContextInterface::class => $authContext,
TokenStorageInterface::class => $this->tokenStorage,
],
static fn () => $handler->handle($request->withAttribute(self::ATTRIBUTE, $authContext))
);

Expand Down
61 changes: 61 additions & 0 deletions src/Framework/Auth/TokenStorageScope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Spiral\Auth;

use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
use Spiral\Auth\Exception\TokenStorageException;
use Spiral\Core\Container\SingletonInterface;
use Spiral\Core\Exception\ScopeException;

final class TokenStorageScope implements TokenStorageInterface, SingletonInterface
{
public function __construct(
private readonly ContainerInterface $container
) {
}

/**
* Load token by id, must return null if token not found.
*
* @throws TokenStorageException
*/
public function load(string $id): ?TokenInterface
{
return $this->getTokenStorage()->load($id);
}

/**
* Create token based on the payload provided by actor provider.
*
* @throws TokenStorageException
*/
public function create(array $payload, \DateTimeInterface $expiresAt = null): TokenInterface
{
return $this->getTokenStorage()->create($payload, $expiresAt);
}

/**
* Delete token from the persistent storage.
*
* @throws TokenStorageException
*/
public function delete(TokenInterface $token): void
{
$this->getTokenStorage()->delete($token);
}

/**
* @throws ScopeException
*/
private function getTokenStorage(): TokenStorageInterface
{
try {
return $this->container->get(TokenStorageInterface::class);
} catch (NotFoundExceptionInterface $e) {
throw new ScopeException('Unable to resolve token storage, invalid scope', $e->getCode(), $e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Framework\Auth;
namespace Framework\Auth\Config;

use Spiral\Auth\Config\AuthConfig;
use Spiral\Auth\TokenStorageInterface as SessionTokenStorageInterface;
Expand Down
68 changes: 68 additions & 0 deletions tests/Framework/Auth/TokenStorageScopeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Auth;

use PHPUnit\Framework\TestCase;
use Spiral\Auth\TokenInterface;
use Spiral\Auth\TokenStorageInterface;
use Spiral\Auth\TokenStorageScope;
use Spiral\Core\Container;

final class TokenStorageScopeTest extends TestCase
{
public function testLoad(): void
{
$storage = $this->createMock(TokenStorageInterface::class);
$storage
->expects($this->once())
->method('load')
->with('foo')
->willReturn($token = $this->createMock(TokenInterface::class));

$container = new Container();
$container->bind(TokenStorageInterface::class, $storage);

$scope = new TokenStorageScope($container);

$this->assertSame($token, $scope->load('foo'));
}

public function testCreate(): void
{
$expiresAt = new \DateTimeImmutable();

$storage = $this->createMock(TokenStorageInterface::class);
$storage
->expects($this->once())
->method('create')
->with(['foo' => 'bar'], $expiresAt)
->willReturn($token = $this->createMock(TokenInterface::class));

$container = new Container();
$container->bind(TokenStorageInterface::class, $storage);

$scope = new TokenStorageScope($container);

$this->assertSame($token, $scope->create(['foo' => 'bar'], $expiresAt));
}

public function testDelete(): void
{
$token = $this->createMock(TokenInterface::class);

$storage = $this->createMock(TokenStorageInterface::class);
$storage
->expects($this->once())
->method('delete')
->with($token);

$container = new Container();
$container->bind(TokenStorageInterface::class, $storage);

$scope = new TokenStorageScope($container);

$scope->delete($token);
}
}
47 changes: 47 additions & 0 deletions tests/Framework/AuthHttp/Middleware/AuthMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\AuthHttp\Middleware;

use Spiral\Auth\Middleware\AuthMiddleware;
use Spiral\Auth\TokenStorageInterface;
use Spiral\Auth\TokenStorageScope;
use Spiral\Core\Container\Autowire;
use Spiral\Tests\Framework\HttpTestCase;

final class AuthMiddlewareTest extends HttpTestCase
{
public function setUp(): void
{
parent::setUp();

$this->enableMiddlewares();
}

public function testTokenStorageInterfaceShouldBeBound(): void
{
$storage = $this->createMock(TokenStorageInterface::class);
$this->getContainer()->bind(
AuthMiddleware::class,
new Autowire(AuthMiddleware::class, ['tokenStorage' => $storage])
);
$this->setHttpHandler(function () use ($storage): void {
$scope = $this->getContainer()->get(TokenStorageScope::class);
$ref = new \ReflectionMethod($scope, 'getTokenStorage');

$this->assertInstanceOf($storage::class, $ref->invoke($scope));
$this->assertSame($storage, $ref->invoke($scope));
});

$scope = $this->getContainer()->get(TokenStorageScope::class);
$ref = new \ReflectionMethod($scope, 'getTokenStorage');
$this->assertNotInstanceOf($storage::class, $ref->invoke($scope));

$this->getHttp()->get('/');

$scope = $this->getContainer()->get(TokenStorageScope::class);
$ref = new \ReflectionMethod($scope, 'getTokenStorage');
$this->assertNotInstanceOf($storage::class, $ref->invoke($scope));
}
}

0 comments on commit ae80144

Please sign in to comment.