Skip to content

Commit 52ca0db

Browse files
authored
Merge pull request #62 from rremy/master
feat: implement BasicAuthenticationSecurityStrategy
2 parents 29e9dd8 + cd0bdc5 commit 52ca0db

17 files changed

+432
-153
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [8.1.0] - 2021-12-15
8+
### Added
9+
- Basic Authentication strategy added
10+
711
## [8.0.0] - 2021-11-19
812
### Added
913
- Bearer Authentication strategy added

example/gen/doc/petstore3.json

+4
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,10 @@
11611161
"type": "apiKey",
11621162
"name": "api_key",
11631163
"in": "header"
1164+
},
1165+
"basic_auth": {
1166+
"type": "http",
1167+
"scheme": "basic"
11641168
}
11651169
}
11661170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file was generated by docler-labs/api-client-generator.
7+
*
8+
* Do not edit it manually.
9+
*/
10+
11+
namespace OpenApi\PetStoreClient\Request;
12+
13+
class AuthenticationCredentials
14+
{
15+
/** @var string */
16+
private $username;
17+
18+
/** @var string */
19+
private $password;
20+
21+
public function __construct(string $username, string $password)
22+
{
23+
$this->username = $username;
24+
$this->password = $password;
25+
}
26+
27+
public function getUsername(): string
28+
{
29+
return $this->username;
30+
}
31+
32+
public function getPassword(): string
33+
{
34+
return $this->password;
35+
}
36+
}

example/petstore3.json

+4
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,10 @@
11611161
"type": "apiKey",
11621162
"name": "api_key",
11631163
"in": "header"
1164+
},
1165+
"basic_auth": {
1166+
"type": "http",
1167+
"scheme": "basic"
11641168
}
11651169
}
11661170
}

src/Command/GenerateCommand.php

+35-6
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
namespace DoclerLabs\ApiClientGenerator\Command;
66

77
use DoclerLabs\ApiClientGenerator\CodeGeneratorFacade;
8+
use DoclerLabs\ApiClientGenerator\Generator\Security\BasicAuthenticationSecurityStrategy;
89
use DoclerLabs\ApiClientGenerator\Input\Configuration;
910
use DoclerLabs\ApiClientGenerator\Input\FileReader;
1011
use DoclerLabs\ApiClientGenerator\Input\Parser;
1112
use DoclerLabs\ApiClientGenerator\Input\Specification;
1213
use DoclerLabs\ApiClientGenerator\MetaTemplateFacade;
14+
use DoclerLabs\ApiClientGenerator\Output\Copy\Request\AuthenticationCredentials;
1315
use DoclerLabs\ApiClientGenerator\Output\Copy\Serializer\ContentType\FormUrlencodedContentTypeSerializer;
1416
use DoclerLabs\ApiClientGenerator\Output\Copy\Serializer\ContentType\JsonContentTypeSerializer;
1517
use DoclerLabs\ApiClientGenerator\Output\Copy\Serializer\ContentType\VdnApiJsonContentTypeSerializer;
@@ -155,7 +157,7 @@ private function copyStaticPhpFiles(StyleInterface $ss, Specification $specifica
155157

156158
$ss->progressStart($originalFiles->count());
157159
foreach ($originalFiles as $originalFile) {
158-
if (empty($blacklistedFiles[$originalFile->getBasename()])) {
160+
if (!in_array($originalFile->getBasename(), $blacklistedFiles, true)) {
159161
$destinationPath = sprintf(
160162
'%s/%s/%s',
161163
$this->configuration->getOutputDirectory(),
@@ -204,7 +206,7 @@ static function (): bool {
204206
}
205207
}
206208

207-
private function getBlacklistedFiles(Specification $specification): array
209+
private function getUnusedSerializers(Specification $specification): array
208210
{
209211
$contentTypeMapping = [
210212
XmlContentTypeSerializer::MIME_TYPE => XmlContentTypeSerializer::class,
@@ -215,13 +217,40 @@ private function getBlacklistedFiles(Specification $specification): array
215217

216218
$allContentTypes = $specification->getAllContentTypes();
217219

218-
return array_flip(array_map(
219-
fn ($class) => basename((string)(new ReflectionClass($class))->getFileName()),
220+
return array_values(
220221
array_filter(
221222
$contentTypeMapping,
222-
fn ($key) => !in_array($key, $allContentTypes),
223+
static fn (string $key) => !in_array($key, $allContentTypes, true),
223224
ARRAY_FILTER_USE_KEY
224225
)
225-
));
226+
);
227+
}
228+
229+
/**
230+
* @return string[]
231+
*/
232+
private function getUnusedValueObjects(Specification $specification): array
233+
{
234+
$unusedClasses = [];
235+
236+
if (!$specification->isSecuritySchemeEnabled(BasicAuthenticationSecurityStrategy::SCHEME)) {
237+
$unusedClasses[] = AuthenticationCredentials::class;
238+
}
239+
240+
return $unusedClasses;
241+
}
242+
243+
/**
244+
* @return string[]
245+
*/
246+
private function getBlacklistedFiles(Specification $specification): array
247+
{
248+
return array_map(
249+
static fn ($class) => basename((string)(new ReflectionClass($class))->getFileName()),
250+
array_merge(
251+
$this->getUnusedSerializers($specification),
252+
$this->getUnusedValueObjects($specification)
253+
)
254+
);
226255
}
227256
}

src/Entity/ImportCollection.php

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ public function add(string $item): self
1515
return $this;
1616
}
1717

18+
public function append(self $anotherCollection): self
19+
{
20+
$this->items += $anotherCollection->items;
21+
22+
return $this;
23+
}
24+
1825
public function toArray(): array
1926
{
2027
return array_unique($this->items);

src/Generator/RequestGenerator.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use DoclerLabs\ApiClientGenerator\Entity\Field;
1010
use DoclerLabs\ApiClientGenerator\Entity\Operation;
1111
use DoclerLabs\ApiClientGenerator\Entity\Request;
12+
use DoclerLabs\ApiClientGenerator\Generator\Security\SecurityStrategyAbstract;
1213
use DoclerLabs\ApiClientGenerator\Input\Specification;
1314
use DoclerLabs\ApiClientGenerator\Naming\CopiedNamespace;
1415
use DoclerLabs\ApiClientGenerator\Naming\RequestNaming;
@@ -22,13 +23,13 @@ class RequestGenerator extends MutatorAccessorClassGeneratorAbstract
2223
public const NAMESPACE_SUBPATH = '\\Request';
2324
public const SUBDIRECTORY = 'Request/';
2425

25-
/** @var SecurityStrategyInterface[] */
26+
/** @var SecurityStrategyAbstract[] */
2627
private array $securityStrategies;
2728

2829
public function __construct(
2930
string $baseNamespace,
3031
CodeBuilder $builder,
31-
SecurityStrategyInterface ...$securityStrategies
32+
SecurityStrategyAbstract ...$securityStrategies
3233
) {
3334
parent::__construct($baseNamespace, $builder);
3435

@@ -62,6 +63,10 @@ protected function generateRequest(
6263
->addStmt($this->generateGetRoute($request))
6364
->addStmts($this->generateGetParametersMethods($request, $operation, $specification));
6465

66+
foreach ($this->securityStrategies as $securityStrategy) {
67+
$this->getImports()->append($securityStrategy->getImports($this->baseNamespace));
68+
}
69+
6570
$this->registerFile($fileRegistry, $classBuilder, self::SUBDIRECTORY, self::NAMESPACE_SUBPATH);
6671
}
6772

@@ -105,7 +110,6 @@ protected function generateProperties(Request $request, Operation $operation, Sp
105110
$statements[] = $this->builder->localProperty('contentType', 'string', 'string', false, $default);
106111

107112
foreach ($this->securityStrategies as $securityStrategy) {
108-
/** @var SecurityStrategyInterface $securityStrategy */
109113
array_push($statements, ...$securityStrategy->getProperties($operation, $specification));
110114
}
111115

@@ -143,7 +147,6 @@ protected function generateConstructor(
143147
}
144148

145149
foreach ($this->securityStrategies as $securityStrategy) {
146-
/** @var SecurityStrategyInterface $securityStrategy */
147150
array_push($params, ...$securityStrategy->getConstructorParams($operation, $specification));
148151
array_push($paramInits, ...$securityStrategy->getConstructorParamInits($operation, $specification));
149152
}
@@ -377,7 +380,6 @@ private function getSecurityHeaders(Operation $operation, Specification $specifi
377380
$headers = [];
378381

379382
foreach ($this->securityStrategies as $securityStrategy) {
380-
/** @var SecurityStrategyInterface $securityStrategy */
381383
$headers += $securityStrategy->getSecurityHeaders($operation, $specification);
382384
}
383385

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoclerLabs\ApiClientGenerator\Generator\Security;
6+
7+
use DoclerLabs\ApiClientGenerator\Ast\Builder\CodeBuilder;
8+
use DoclerLabs\ApiClientGenerator\Entity\ImportCollection;
9+
use DoclerLabs\ApiClientGenerator\Entity\Operation;
10+
use DoclerLabs\ApiClientGenerator\Input\Specification;
11+
use DoclerLabs\ApiClientGenerator\Naming\CopiedNamespace;
12+
use DoclerLabs\ApiClientGenerator\Output\Copy\Request\AuthenticationCredentials;
13+
use PhpParser\Node\Expr;
14+
15+
class BasicAuthenticationSecurityStrategy extends SecurityStrategyAbstract
16+
{
17+
public const SCHEME = 'basic';
18+
private const PROPERTY_CREDENTIALS = 'credentials';
19+
private const TYPE = 'http';
20+
21+
private CodeBuilder $builder;
22+
23+
public function __construct(CodeBuilder $builder)
24+
{
25+
$this->builder = $builder;
26+
}
27+
28+
public function getImports(string $baseNamespace): ImportCollection
29+
{
30+
return parent::getImports($baseNamespace)
31+
->add(CopiedNamespace::getImport($baseNamespace, AuthenticationCredentials::class));
32+
}
33+
34+
public function getProperties(Operation $operation, Specification $specification): array
35+
{
36+
$statements = [];
37+
38+
if ($this->isAuthenticationAvailable($operation, $specification)) {
39+
$statements[] = $this->builder->localProperty(
40+
self::PROPERTY_CREDENTIALS,
41+
'AuthenticationCredentials',
42+
'AuthenticationCredentials'
43+
);
44+
}
45+
46+
return $statements;
47+
}
48+
49+
public function getConstructorParams(Operation $operation, Specification $specification): array
50+
{
51+
$params = [];
52+
53+
if ($this->isAuthenticationAvailable($operation, $specification)) {
54+
$params[] = $this->builder
55+
->param(self::PROPERTY_CREDENTIALS)
56+
->setType('AuthenticationCredentials')
57+
->getNode();
58+
}
59+
60+
return $params;
61+
}
62+
63+
public function getConstructorParamInits(Operation $operation, Specification $specification): array
64+
{
65+
$paramInits = [];
66+
67+
if ($this->isAuthenticationAvailable($operation, $specification)) {
68+
$paramInits[] = $this->builder->assign(
69+
$this->builder->localPropertyFetch(self::PROPERTY_CREDENTIALS),
70+
$this->builder->var(self::PROPERTY_CREDENTIALS)
71+
);
72+
}
73+
74+
return $paramInits;
75+
}
76+
77+
protected function getScheme(): string
78+
{
79+
return self::SCHEME;
80+
}
81+
82+
protected function getType(): string
83+
{
84+
return self::TYPE;
85+
}
86+
87+
protected function getAuthorizationHeader(): Expr
88+
{
89+
return $this->builder->funcCall(
90+
'sprintf',
91+
[
92+
'Basic %s',
93+
$this->builder->funcCall(
94+
'base64_encode',
95+
[
96+
$this->builder->funcCall(
97+
'sprintf',
98+
[
99+
'%s:%s',
100+
$this->builder->methodCall(
101+
$this->builder->localPropertyFetch(self::PROPERTY_CREDENTIALS),
102+
'getUsername'
103+
),
104+
$this->builder->methodCall(
105+
$this->builder->localPropertyFetch(self::PROPERTY_CREDENTIALS),
106+
'getPassword'
107+
),
108+
]
109+
),
110+
]
111+
),
112+
]
113+
);
114+
}
115+
}

0 commit comments

Comments
 (0)