From 6e454ef673ba16b54056bba05c454381f6e39b60 Mon Sep 17 00:00:00 2001 From: Baptiste Leduc Date: Fri, 22 Nov 2019 15:11:54 +0100 Subject: [PATCH] =?UTF-8?q?Better=20Bearer=20handling=20&=20Added=20Basic?= =?UTF-8?q?=20auth=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Authentication/ConstructGenerator.php | 17 ++++- .../Authentication/GetPluginGenerator.php | 69 +++++++++++++++++-- .../.jane-openapi | 0 .../Authentication/BasicAuthentication.php | 32 +++++++++ .../expected/Client.php | 0 .../expected/Endpoint/GetFoo.php | 0 .../expected/Model/Foo.php | 0 .../expected/Normalizer/FooNormalizer.php | 0 .../expected/Normalizer/NormalizerFactory.php | 0 .../swagger.json | 2 +- .../authentication-http-bearer/.jane-openapi | 9 +++ .../Authentication/BearerAuthentication.php | 28 ++++++++ .../expected/Client.php | 34 +++++++++ .../expected/Endpoint/GetFoo.php | 36 ++++++++++ .../expected/Model/Foo.php | 34 +++++++++ .../expected/Normalizer/FooNormalizer.php | 42 +++++++++++ .../expected/Normalizer/NormalizerFactory.php | 14 ++++ .../authentication-http-bearer/swagger.json | 49 +++++++++++++ .../Authentication/BasicAuthentication.php | 16 ----- 19 files changed, 360 insertions(+), 22 deletions(-) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/.jane-openapi (100%) create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Authentication/BasicAuthentication.php rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/expected/Client.php (100%) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/expected/Endpoint/GetFoo.php (100%) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/expected/Model/Foo.php (100%) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/expected/Normalizer/FooNormalizer.php (100%) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/expected/Normalizer/NormalizerFactory.php (100%) rename src/OpenApi/Tests/fixtures/{authentication-http => authentication-http-basic}/swagger.json (97%) create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/.jane-openapi create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Authentication/BearerAuthentication.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Client.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Endpoint/GetFoo.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Model/Foo.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/FooNormalizer.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/NormalizerFactory.php create mode 100644 src/OpenApi/Tests/fixtures/authentication-http-bearer/swagger.json delete mode 100644 src/OpenApi/Tests/fixtures/authentication-http/expected/Authentication/BasicAuthentication.php diff --git a/src/OpenApi/Generator/Authentication/ConstructGenerator.php b/src/OpenApi/Generator/Authentication/ConstructGenerator.php index e927afd01e..bb89df9d57 100644 --- a/src/OpenApi/Generator/Authentication/ConstructGenerator.php +++ b/src/OpenApi/Generator/Authentication/ConstructGenerator.php @@ -3,6 +3,7 @@ namespace Jane\OpenApi\Generator\Authentication; use Jane\OpenApi\Guesser\Guess\SecuritySchemeGuess; +use Jane\OpenApi\JsonSchema\Model\HTTPSecurityScheme; use PhpParser\Node\Name; use PhpParser\Node\Param; use PhpParser\Node\Stmt; @@ -16,7 +17,21 @@ protected function createConstruct(SecuritySchemeGuess $securityScheme): array $needs = []; switch ($securityScheme->getType()) { case SecuritySchemeGuess::TYPE_HTTP: - $needs['token'] = new Name('string'); + /** @var HTTPSecurityScheme $object */ + $object = $securityScheme->getObject(); + $scheme = $object->getScheme() ?? 'Bearer'; + $scheme = ucfirst(mb_strtolower($scheme)); + + switch ($scheme) { + case 'Bearer': + $needs['token'] = new Name('string'); + break; + case 'Basic': + $needs['username'] = new Name('string'); + $needs['password'] = new Name('string'); + break; + } + break; } diff --git a/src/OpenApi/Generator/Authentication/GetPluginGenerator.php b/src/OpenApi/Generator/Authentication/GetPluginGenerator.php index c0541726b4..22c0ec2501 100644 --- a/src/OpenApi/Generator/Authentication/GetPluginGenerator.php +++ b/src/OpenApi/Generator/Authentication/GetPluginGenerator.php @@ -3,14 +3,16 @@ namespace Jane\OpenApi\Generator\Authentication; use Http\Client\Common\Plugin; -use Http\Message\Authentication\Bearer; +use Http\Message\Authentication; use Jane\OpenApi\Guesser\Guess\SecuritySchemeGuess; use Jane\OpenApi\JsonSchema\Model\HTTPSecurityScheme; use PhpParser\Node; +use PhpParser\Node\Param; use PhpParser\Node\Stmt; use PhpParser\Node\Name; use PhpParser\Node\Expr; use PhpParser\Node\Scalar; +use Psr\Http\Message\RequestInterface; trait GetPluginGenerator { @@ -21,17 +23,76 @@ protected function createGetPlugin(SecuritySchemeGuess $securityScheme): Stmt\Cl case SecuritySchemeGuess::TYPE_HTTP: /** @var HTTPSecurityScheme $object */ $object = $securityScheme->getObject(); + $scheme = $object->getScheme() ?? 'Bearer'; + $scheme = ucfirst(mb_strtolower($scheme)); - // @todo handle this ~ - // $scheme = $object->getScheme() ?? 'Bearer'; + $needs = []; + $anonymousClass = [ + 'properties' => [], + 'constructParameters' => [], + 'constructStatements' => [], + 'fetchedValue' => null, + ]; + + switch ($scheme) { + case 'Bearer': + $needs['token'] = new Name('string'); + $anonymousClass['fetchedValue'] = new Expr\PropertyFetch(new Expr\Variable('this'), new Scalar\String_('token')); + break; + case 'Basic': + $needs['username'] = new Name('string'); + $needs['password'] = new Name('string'); + $anonymousClass['fetchedValue'] = new Expr\FuncCall(new Name('base64_encode'), [ + new Node\Arg(new Expr\FuncCall(new Name('sprintf'), [ + new Node\Arg(new Scalar\String_('%s:%s')), + new Node\Arg(new Expr\PropertyFetch(new Expr\Variable('this'), new Scalar\String_('username'))), + new Node\Arg(new Expr\PropertyFetch(new Expr\Variable('this'), new Scalar\String_('password'))), + ])), + ]); + break; + } + foreach ($needs as $field => $type) { + $anonymousClass['properties'][] = new Stmt\Property(Stmt\Class_::MODIFIER_PRIVATE, [new Stmt\PropertyProperty($field)]); + $anonymousClass['constructParameters'][] = new Param(new Expr\Variable($field), null, $type); + $anonymousClass['constructStatements'][] = new Stmt\Expression(new Expr\Assign(new Expr\PropertyFetch(new Expr\Variable('this'), new Scalar\String_($field)), new Expr\Variable($field))); + } + + $pluginClass = new Stmt\Class_(null, [ + 'implements' => [new Name\FullyQualified(Authentication::class)], + 'stmts' => array_merge( + $anonymousClass['properties'], + [ + new Stmt\ClassMethod('__construct', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => $anonymousClass['constructParameters'], + 'stmts' => $anonymousClass['constructStatements'], + ]), + new Stmt\ClassMethod('authenticate', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [new Param(new Expr\Variable('request'), null, new Name\FullyQualified(RequestInterface::class))], + 'stmts' => [ + new Stmt\Expression(new Expr\Assign(new Expr\Variable('header'), new Expr\FuncCall(new Name('sprintf'), [ + new Node\Arg(new Scalar\String_($scheme . ' %s')), + new Node\Arg($anonymousClass['fetchedValue']), + ]))), + new Stmt\Return_(new Expr\MethodCall(new Expr\Variable('request'), 'withHeader', [ + new Node\Arg(new Scalar\String_('Authorization')), + new Node\Arg(new Expr\Variable('header')), + ])), + ], + ]), + ] + ), + ]); $stmts[] = new Stmt\Return_(new Expr\New_(new Name\FullyQualified(Plugin\AuthenticationPlugin::class), [ - new Node\Arg(new Expr\New_(new Name\FullyQualified(Bearer::class), [ + new Node\Arg(new Expr\New_($pluginClass, [ new Node\Arg(new Expr\PropertyFetch(new Expr\Variable('this'), new Scalar\String_('token'))), ])), ])); break; case SecuritySchemeGuess::TYPE_API_KEY: + // @todo break; } diff --git a/src/OpenApi/Tests/fixtures/authentication-http/.jane-openapi b/src/OpenApi/Tests/fixtures/authentication-http-basic/.jane-openapi similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/.jane-openapi rename to src/OpenApi/Tests/fixtures/authentication-http-basic/.jane-openapi diff --git a/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Authentication/BasicAuthentication.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Authentication/BasicAuthentication.php new file mode 100644 index 0000000000..474c26892a --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Authentication/BasicAuthentication.php @@ -0,0 +1,32 @@ +{'username'} = $username; + $this->{'password'} = $password; + } + public function getPlugin() : \Http\Client\Common\Plugin + { + return new \Http\Client\Common\Plugin\AuthenticationPlugin(new class($this->{'token'}) implements \Http\Message\Authentication + { + private $username; + private $password; + public function __construct(string $username, string $password) + { + $this->{'username'} = $username; + $this->{'password'} = $password; + } + public function authenticate(\Psr\Http\Message\RequestInterface $request) + { + $header = sprintf('Basic %s', base64_encode(sprintf('%s:%s', $this->{'username'}, $this->{'password'}))); + return $request->withHeader('Authorization', $header); + } + }); + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http/expected/Client.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Client.php similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/expected/Client.php rename to src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Client.php diff --git a/src/OpenApi/Tests/fixtures/authentication-http/expected/Endpoint/GetFoo.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Endpoint/GetFoo.php similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/expected/Endpoint/GetFoo.php rename to src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Endpoint/GetFoo.php diff --git a/src/OpenApi/Tests/fixtures/authentication-http/expected/Model/Foo.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Model/Foo.php similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/expected/Model/Foo.php rename to src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Model/Foo.php diff --git a/src/OpenApi/Tests/fixtures/authentication-http/expected/Normalizer/FooNormalizer.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Normalizer/FooNormalizer.php similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/expected/Normalizer/FooNormalizer.php rename to src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Normalizer/FooNormalizer.php diff --git a/src/OpenApi/Tests/fixtures/authentication-http/expected/Normalizer/NormalizerFactory.php b/src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Normalizer/NormalizerFactory.php similarity index 100% rename from src/OpenApi/Tests/fixtures/authentication-http/expected/Normalizer/NormalizerFactory.php rename to src/OpenApi/Tests/fixtures/authentication-http-basic/expected/Normalizer/NormalizerFactory.php diff --git a/src/OpenApi/Tests/fixtures/authentication-http/swagger.json b/src/OpenApi/Tests/fixtures/authentication-http-basic/swagger.json similarity index 97% rename from src/OpenApi/Tests/fixtures/authentication-http/swagger.json rename to src/OpenApi/Tests/fixtures/authentication-http-basic/swagger.json index db0b0bb224..d936a48447 100644 --- a/src/OpenApi/Tests/fixtures/authentication-http/swagger.json +++ b/src/OpenApi/Tests/fixtures/authentication-http-basic/swagger.json @@ -42,7 +42,7 @@ "securitySchemes": { "Basic": { "type": "http", - "scheme": "Bearer" + "scheme": "basic" } } } diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/.jane-openapi b/src/OpenApi/Tests/fixtures/authentication-http-bearer/.jane-openapi new file mode 100644 index 0000000000..8dac983b0f --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/.jane-openapi @@ -0,0 +1,9 @@ + __DIR__ . '/swagger.json', + 'namespace' => 'Jane\OpenApi\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'use-fixer' => false, +// 'client' => 'psr18', +]; diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Authentication/BearerAuthentication.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Authentication/BearerAuthentication.php new file mode 100644 index 0000000000..def9749b71 --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Authentication/BearerAuthentication.php @@ -0,0 +1,28 @@ +{'token'} = $token; + } + public function getPlugin() : \Http\Client\Common\Plugin + { + return new \Http\Client\Common\Plugin\AuthenticationPlugin(new class($this->{'token'}) implements \Http\Message\Authentication + { + private $token; + public function __construct(string $token) + { + $this->{'token'} = $token; + } + public function authenticate(\Psr\Http\Message\RequestInterface $request) + { + $header = sprintf('Bearer %s', $this->{'token'}); + return $request->withHeader('Authorization', $header); + } + }); + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Client.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Client.php new file mode 100644 index 0000000000..22e5d465b3 --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Client.php @@ -0,0 +1,34 @@ +executePsr7Endpoint(new \Jane\OpenApi\Tests\Expected\Endpoint\GetFoo(), $fetch); + } + public static function create($httpClient = null, \Jane\OpenApiRuntime\Client\Authentication $authentication = null) + { + if (null === $httpClient) { + $httpClient = \Http\Discovery\HttpClientDiscovery::find(); + $plugins = array(); + $uri = \Http\Discovery\UriFactoryDiscovery::find()->createUri('https://www.foo-host.com/base-path'); + $plugins[] = new \Http\Client\Common\Plugin\AddHostPlugin($uri); + $plugins[] = new \Http\Client\Common\Plugin\AddPathPlugin($uri); + if (null !== $authentication) { + $plugins[] = $authentication->getPlugin(); + } + $httpClient = new \Http\Client\Common\PluginClient($httpClient, $plugins); + } + $messageFactory = \Http\Discovery\MessageFactoryDiscovery::find(); + $streamFactory = \Http\Discovery\StreamFactoryDiscovery::find(); + $serializer = new \Symfony\Component\Serializer\Serializer(\Jane\OpenApi\Tests\Expected\Normalizer\NormalizerFactory::create(), array(new \Symfony\Component\Serializer\Encoder\JsonEncoder(new \Symfony\Component\Serializer\Encoder\JsonEncode(), new \Symfony\Component\Serializer\Encoder\JsonDecode()))); + return new static($httpClient, $messageFactory, $serializer, $streamFactory); + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Endpoint/GetFoo.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Endpoint/GetFoo.php new file mode 100644 index 0000000000..63c2e747d8 --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Endpoint/GetFoo.php @@ -0,0 +1,36 @@ + array('application/json')); + } + /** + * {@inheritdoc} + * + * + * @return null|\Jane\OpenApi\Tests\Expected\Model\Foo + */ + protected function transformResponseBody(string $body, int $status, \Symfony\Component\Serializer\SerializerInterface $serializer, ?string $contentType = null) + { + if (200 === $status && mb_strpos($contentType, 'application/json') !== false) { + return $serializer->deserialize($body, 'Jane\\OpenApi\\Tests\\Expected\\Model\\Foo', 'json'); + } + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Model/Foo.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Model/Foo.php new file mode 100644 index 0000000000..16fd403f62 --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Model/Foo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/FooNormalizer.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000000..5b7f4dac89 --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,42 @@ +setFoo($data->{'foo'}); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = new \stdClass(); + $data->{'foo'} = $object->getFoo(); + return $data; + } +} \ No newline at end of file diff --git a/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/NormalizerFactory.php b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/NormalizerFactory.php new file mode 100644 index 0000000000..65ae62e6dd --- /dev/null +++ b/src/OpenApi/Tests/fixtures/authentication-http-bearer/expected/Normalizer/NormalizerFactory.php @@ -0,0 +1,14 @@ +{'token'} = $token; - } - public function getPlugin() : \Http\Client\Common\Plugin - { - return new \Http\Client\Common\Plugin\AuthenticationPlugin(new \Http\Message\Authentication\Bearer($this->{'token'})); - } -} \ No newline at end of file