Skip to content

Commit

Permalink
feat(api tester): enable specific user authentication for given endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Dzov committed Nov 6, 2024
1 parent 189326d commit 46fa00c
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 92 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;
}
}
58 changes: 58 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 @@ -116,4 +118,60 @@ private function getBaseLineData(): array
/** @var array{'exclude': ?array<int, array<string, string>>} */
return Yaml::parseFile($this->getBaseline());
}

/**
* @param array<array<string, string>> $includeFilters
* @param array<array<string, string>> $excludeFilters
*/
public function includes(Filterable $object): bool

Check failure on line 126 in src/Config/Filters.php

View workflow job for this annotation

GitHub Actions / PHPStan

PHPDoc tag @param references unknown parameter: $excludeFilters

Check failure on line 126 in src/Config/Filters.php

View workflow job for this annotation

GitHub Actions / PHPStan

PHPDoc tag @param references unknown parameter: $includeFilters
{
$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{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];
}
}
46 changes: 17 additions & 29 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 @@ -204,13 +205,11 @@ public function getPathParameters(): array
if ($this->parent !== null) {
$definitionParamsCount = $this->parent
->getPathParameters()
->count()
;
->count();
if ($this->forceRandom || ($this->autoComplete && \count($this->pathParameters) < $definitionParamsCount)) {
$randomPathParams = $this->parent
->getPathParameters()
->getRandomExamples()
;
->getRandomExamples();
$this->pathParameters = array_merge($randomPathParams, $this->pathParameters);
}
}
Expand All @@ -236,15 +235,13 @@ public function getQueryParameters(): array
if ($this->parent !== null) {
$definitionParamsCount = $this->parent
->getQueryParameters()
->count()
;
->count();
if ($this->forceRandom || ($this->autoComplete && \count(
$this->queryParameters
) < $definitionParamsCount)) {
$this->queryParameters
) < $definitionParamsCount)) {
$randomQueryParams = $this->parent
->getQueryParameters()
->getRandomExamples()
;
->getRandomExamples();
$this->queryParameters = array_merge($randomQueryParams, $this->queryParameters);
}
}
Expand Down Expand Up @@ -272,14 +269,13 @@ public function getHeaders(): array
if ($this->getBody() !== null && !isset($this->headers['content-type'])) {
$this->headers['content-type'] = $this
->getBody()
->getMediaType()
;
->getMediaType();
}

if ($this->parent !== null) {
$definitionHeadersCount = $this->parent
->getHeaders()
->count() + 1 // content-type
->getHeaders()
->count() + 1 // content-type
;

if ($this->parent->getSecurities()->count() > 0) {
Expand All @@ -293,8 +289,7 @@ public function getHeaders(): array
'content-type',
'authorization',
'range',
])
;
]);
$this->headers = array_merge($randomHeaders, $this->headers);
}
}
Expand Down Expand Up @@ -380,19 +375,15 @@ public function setAuthenticationHeaders(Tokens $tokens, bool $ignoreScope = fal
$scopes = $security->getScopes()
->where('name', '!=', 'current_user')
->select('name')
->toArray()
;
->toArray();

if ($ignoreScope) {
/** @var Token|null $token */
$token = $tokens->first();
} else {
/** @var Token|null $token */
$token = $tokens->where(
'scopes',
'includes',
$scopes
)->first();
$token = $tokens->filter(fn (Token $token) => $token->getFilters()?->includes($operation) ?? false)
->first() ?? $tokens->where('scopes', 'includes', $scopes)->first();
}

if ($token !== null) {
Expand Down Expand Up @@ -475,12 +466,10 @@ public function getPath(): string
$example = null;
if ($this->parent !== null && $this->forceRandom) {
$example = $this->parent
->getRandomExample()
;
->getRandomExample();
} elseif ($this->parent !== null && $this->autoComplete) {
$example = $this->parent
->getExample()
;
->getExample();
}

if ($example !== null && (\count($pathParameters) === 0 || $this->forceRandom)) {
Expand Down Expand Up @@ -522,8 +511,7 @@ public function getStringBody(): string
}

return $this->getBody()
->getStringContent()
;
->getStringContent();
}

private function getParametersProp(string $type): string
Expand Down
9 changes: 9 additions & 0 deletions src/Definition/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace APITester\Definition;

use APITester\Config\Filters;
use APITester\Util\Filterable;

final class Token
{
private readonly string $type;
Expand All @@ -19,13 +22,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];
}
}

0 comments on commit 46fa00c

Please sign in to comment.