Skip to content

Commit

Permalink
Wizardy tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dakujem committed Nov 6, 2020
1 parent de72068 commit 3f1fe3a
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 120 deletions.
47 changes: 23 additions & 24 deletions src/Factory/AuthFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,16 @@
*/
class AuthFactory
{
protected ?string $secret;
/** @var callable|null fn(string):callable */
protected $decoderFactory;
protected ?ResponseFactory $responseFactory;
/** @var callable fn(string):callable */
protected $decoderProvider;

public function __construct(
//
// TODO make this not work with "secret", but with the decoder factory only instead
//
?string $secret,
?ResponseFactory $responseFactory,
?callable $defaultDecoderProvider = null
?callable $decoderFactory,
?ResponseFactory $responseFactory
) {
$this->secret = $secret;
$this->decoderFactory = $decoderFactory;
$this->responseFactory = $responseFactory;
$this->decoderProvider = $defaultDecoderProvider;
}

/**
Expand All @@ -64,11 +58,11 @@ public function decodeTokens(
?string $errorAttribute = null,
?Logger $logger = null
): MiddlewareInterface {
if ($this->secret === null) {
throw new LogicException('Secret not provided.');
if ($this->decoderFactory === null) {
throw new LogicException('Decoder factory not provided.');
}
return new TokenMiddleware(
($this->decoderProvider ?? static::defaultDecoderProvider())($this->secret),
($this->decoderFactory)(),
(function () use ($headerName, $cookieName): Generator {
$headerName !== null && yield Man::headerExtractor($headerName);
$cookieName !== null && yield Man::cookieExtractor($cookieName);
Expand Down Expand Up @@ -166,16 +160,21 @@ public function inspectTokens(
});
}

protected static function defaultDecoderProvider(): callable
/**
* Creates a default decoder factory.
* The factory can be used for the constructor.
*
* @param string $secret secret key for JWT decoder
* @return callable fn():FirebaseJwtDecoder
*/
public static function defaultDecoderFactory(string $secret): callable
{
return function (string $secret) {
if (!class_exists(JWT::class)) {
throw new LogicException(
'Firebase JWT is not installed. ' .
'Require require firebase/php-jwt package (`composer require firebase/php-jwt:"^5.0"`).'
);
}
return new FirebaseJwtDecoder($secret);
};
if (!class_exists(JWT::class)) {
throw new LogicException(
'Firebase JWT is not installed. ' .
'Require require firebase/php-jwt package (`composer require firebase/php-jwt:"^5.0"`).'
);
}
return fn(): FirebaseJwtDecoder => new FirebaseJwtDecoder($secret);
}
}
14 changes: 8 additions & 6 deletions src/Factory/AuthWizard.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ final class AuthWizard
*
* @param string|null $secret
* @param ResponseFactory|null $responseFactory
* @param mixed ...$args
* @return AuthFactory
*/
public static function factory(?string $secret, ?ResponseFactory $responseFactory, ...$args): AuthFactory
public static function factory(?string $secret, ?ResponseFactory $responseFactory): AuthFactory
{
return new AuthFactory($secret, $responseFactory, ...$args);
return new AuthFactory(
$secret !== null ? AuthFactory::defaultDecoderFactory($secret) : null,
$responseFactory
);
}

/**
Expand Down Expand Up @@ -79,14 +81,14 @@ public static function assertTokens(
* @see AuthFactory::inspectTokens()
*
* @param ResponseFactory $responseFactory
* @param callable $inspector // fn(Token,callable,callable):Response
* @param callable $inspector fn(Token,callable,callable):Response
* @param string|null $tokenAttribute
* @param string|null $errorAttribute
* @return PredicateMiddleware
*/
public static function probeTokens(
public static function inspectTokens(
ResponseFactory $responseFactory,
callable $inspector, // fn(Token):bool
callable $inspector,
?string $tokenAttribute = null,
?string $errorAttribute = null
): MiddlewareInterface {
Expand Down
2 changes: 2 additions & 0 deletions src/TokenManipulators.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public static function cookieExtractor(string $cookieName = self::COOKIE_NAME):

/**
* Create an extractor that extracts tokens from a request attribute of choice.
* This extractor is trivial, it does not trim or parse the value.
* Since the attributes are not user input, it simply assumes the correct raw token format.
*
* @param string $attributeName
* @return callable
Expand Down
84 changes: 84 additions & 0 deletions tests/AttributeReadersTest.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Dakujem\Middleware\Test;

require_once __DIR__ . '/bootstrap.php';

use Dakujem\Middleware\TokenManipulators;
use Slim\Psr7\Factory\RequestFactory;
use Tester\Assert;
use Tester\TestCase;
use TypeError;

/**
* Test of a pair of factories for callables that read data from Request attributes.
*
* @see TokenManipulators::attributeTokenProvider()
* @see TokenManipulators::attributeExtractor()
*
* @author Andrej Rypak (dakujem) <xrypak@gmail.com>
*/
class _AttributeReadersTest extends TestCase
{
public function testAttributeTokenProvider()
{
// a test token ...
$token = (object)[
'sub' => 42,
];

// create a test request with a bunch of attributes
$request = (new RequestFactory())->createRequest('GET', '/')
->withAttribute('token', $token)
->withAttribute('', $token)
->withAttribute('string', 'type error')
->withAttribute('array', ['type error'])
->withAttribute('foo', $token);

Assert::same($token, TokenManipulators::attributeTokenProvider()($request), 'Fetch `token` attribute by default.');
Assert::same($token, TokenManipulators::attributeTokenProvider('token')($request));
Assert::same($token, TokenManipulators::attributeTokenProvider('foo')($request));
Assert::same($token, TokenManipulators::attributeTokenProvider('')($request)); // an empty string should still be valid

Assert::null(TokenManipulators::attributeTokenProvider('other')($request));

Assert::throws(function () use ($request) {
TokenManipulators::attributeTokenProvider('string')($request);
}, TypeError::class);
Assert::throws(function () use ($request) {
TokenManipulators::attributeTokenProvider('array')($request);
}, TypeError::class);
}

public function testAttributeExtractor()
{
$token = 'any string';

// create a test request with a bunch of attributes
$request = (new RequestFactory())->createRequest('GET', '/')
->withAttribute('token', $token)
->withAttribute('foo', $token)
->withAttribute('', $token)
->withAttribute('array', ['type error'])
->withAttribute('object', (object)['type error']);

Assert::same($token, TokenManipulators::attributeExtractor()($request), 'Fetch from `token` attribute by default.');
Assert::same($token, TokenManipulators::attributeExtractor('token')($request));
Assert::same($token, TokenManipulators::attributeExtractor('foo')($request));
Assert::same($token, TokenManipulators::attributeExtractor('')($request)); // an empty string should still be valid

Assert::null(TokenManipulators::attributeExtractor('other')($request));

Assert::throws(function () use ($request) {
TokenManipulators::attributeExtractor('object')($request);
}, TypeError::class);
Assert::throws(function () use ($request) {
TokenManipulators::attributeExtractor('array')($request);
}, TypeError::class);
}
}

// run the test
(new _AttributeReadersTest)->run();
54 changes: 0 additions & 54 deletions tests/AttributeTokenProviderTest.phpt

This file was deleted.

0 comments on commit 3f1fe3a

Please sign in to comment.