Skip to content

Commit

Permalink
Merge pull request #67 from OpenClassrooms/authenticate_specific_user
Browse files Browse the repository at this point in the history
feat(api tester): enable specific user authentication for given endpoint
  • Loading branch information
Dzov authored Nov 19, 2024
2 parents 189326d + 614c0e1 commit 2f54edb
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 72 deletions.
1 change: 1 addition & 0 deletions src/Authenticator/Authenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function authenticate(Auth $config, Api $api, Requester $requester): Toke
$body['access_token'],
explode(' ', $config->getBody()['scope'] ?? ''),
$body['refresh_token'] ?? null,
$config->getFilters(),
$body['token_type'] ?? null,
$body['expires_in'] ?? null,
);
Expand Down
12 changes: 12 additions & 0 deletions src/Config/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ final class Auth
*/
private array $body = [];

private ?Filters $filters = null;

public function __construct(
private readonly string $name
) {
Expand Down Expand Up @@ -57,4 +59,14 @@ public function setBody(array $body): void
{
$this->body = $body;
}

public function getFilters(): ?Filters
{
return $this->filters;
}

public function setFilters(Filters $filters): void
{
$this->filters = $filters;
}
}
52 changes: 52 additions & 0 deletions src/Config/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace APITester\Config;

use APITester\Util\Filterable;
use Symfony\Component\Yaml\Tag\TaggedValue;
use Symfony\Component\Yaml\Yaml;

final class Filters
Expand Down Expand Up @@ -108,6 +110,39 @@ public function writeBaseline(array $exclude): void
);
}

public function includes(Filterable $object): bool
{
$include = true;
foreach ($this->getInclude() as $item) {
$include = true;
foreach ($item as $key => $value) {
[$operator, $value] = $this->handleTags($value);
if (!$object->has($key, $value, $operator)) {
$include = false;
continue 2;
}
}
break;
}

if (!$include) {
return false;
}

foreach ($this->getExclude() as $item) {
foreach ($item as $key => $value) {
[$operator, $value] = $this->handleTags($value);
if (!$object->has($key, $value, $operator)) {
continue 2;
}
}
$include = false;
break;
}

return $include;
}

/**
* @return array{'exclude': ?array<int, array<string, string>>}
*/
Expand All @@ -116,4 +151,21 @@ private function getBaseLineData(): array
/** @var array{'exclude': ?array<int, array<string, string>>} */
return Yaml::parseFile($this->getBaseline());
}

/**
* @return array{0: string, 1: string|int}
*/
private function handleTags(string|int|TaggedValue $value): array
{
$operator = '=';

if ($value instanceof TaggedValue) {
if ($value->getTag() === 'NOT') {
$operator = '!=';
}
$value = (string) $value->getValue();
}

return [$operator, $value];
}
}
12 changes: 7 additions & 5 deletions src/Definition/Example/OperationExample.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public function setParameter(
foreach ($value as $attribute => $attributeValue) {
$this->{$paramProp}[$name][$attribute] = (string) $attributeValue;
}

return $this;
}

Expand Down Expand Up @@ -388,11 +389,12 @@ public function setAuthenticationHeaders(Tokens $tokens, bool $ignoreScope = fal
$token = $tokens->first();
} else {
/** @var Token|null $token */
$token = $tokens->where(
'scopes',
'includes',
$scopes
)->first();
$token = $tokens->filter(
static fn (Token $token) => $token->getFilters()?->includes($operation) ?? false
)
->first() ?? $tokens->where('scopes', 'includes', $scopes)
->first()
;
}

if ($token !== null) {
Expand Down
8 changes: 8 additions & 0 deletions src/Definition/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace APITester\Definition;

use APITester\Config\Filters;

final class Token
{
private readonly string $type;
Expand All @@ -19,13 +21,19 @@ public function __construct(
private readonly string $accessToken,
private readonly array $scopes = [],
private readonly ?string $refreshToken = null,
private readonly ?Filters $filters = null,
?string $type = null,
?int $expiresIn = null
) {
$this->type = $type ?? 'Bearer';
$this->expiresIn = $expiresIn ?? 3600;
}

public function getFilters(): ?Filters
{
return $this->filters;
}

public function getAccessToken(): string
{
return $this->accessToken;
Expand Down
64 changes: 1 addition & 63 deletions src/Test/Suite.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@
use APITester\Preparator\Exception\PreparatorLoadingException;
use APITester\Preparator\TestCasesPreparator;
use APITester\Requester\Requester;
use APITester\Util\Filterable;
use APITester\Util\Traits\TimeBoundTrait;
use Illuminate\Support\Collection;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\Yaml\Tag\TaggedValue;

/**
* @internal
Expand Down Expand Up @@ -87,43 +85,6 @@ public function setLogger(LoggerInterface $logger): void
$this->logger = $logger;
}

/**
* @param array<array<string, string>> $includeFilters
* @param array<array<string, string>> $excludeFilters
*/
public function includes(Filterable $object, array $includeFilters = [], array $excludeFilters = []): bool
{
$include = true;
foreach ($includeFilters as $item) {
$include = true;
foreach ($item as $key => $value) {
[$operator, $value] = $this->handleTags($value);
if (!$object->has($key, $value, $operator)) {
$include = false;
continue 2;
}
}
break;
}

if (!$include) {
return false;
}

foreach ($excludeFilters as $item) {
foreach ($item as $key => $value) {
[$operator, $value] = $this->handleTags($value);
if (!$object->has($key, $value, $operator)) {
continue 2;
}
}
$include = false;
break;
}

return $include;
}

/**
* @param array<array<string, string>> $filter
*
Expand Down Expand Up @@ -233,13 +194,7 @@ private function prepareTestCases(): void

private function filterOperation(Operations $operations): Operations
{
return $operations->filter(
fn (Operation $operation) => $this->includes(
$operation,
$this->filters->getInclude(),
$this->filters->getExclude(),
)
);
return $operations->filter(fn (Operation $operation) => $this->filters->includes($operation));
}

/**
Expand Down Expand Up @@ -301,21 +256,4 @@ private function indexInPart(?string $part, int $index, int $total): bool

return false;
}

/**
* @return array{0: string, 1: string|int}
*/
private function handleTags(string|int|\Symfony\Component\Yaml\Tag\TaggedValue $value): array
{
$operator = '=';

if ($value instanceof TaggedValue) {
if ($value->getTag() === 'NOT') {
$operator = '!=';
}
$value = (string) $value->getValue();
}

return [$operator, $value];
}
}
4 changes: 2 additions & 2 deletions src/Test/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ final class TestCase implements \JsonSerializable, Filterable

private ?string $operation;

private ?string $preparator;
private string $preparator;

private Validator $validator;

Expand Down Expand Up @@ -238,7 +238,7 @@ public function setOperation(string $operation): void
$this->operation = $operation;
}

public function getPreparator(): ?string
public function getPreparator(): string
{
return $this->preparator;
}
Expand Down
Loading

0 comments on commit 2f54edb

Please sign in to comment.