Skip to content

Commit

Permalink
Better Bearer handling & Added Basic auth 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
Korbeil committed Nov 22, 2019
1 parent 2b2ff34 commit 6e454ef
Show file tree
Hide file tree
Showing 19 changed files with 360 additions and 22 deletions.
17 changes: 16 additions & 1 deletion src/OpenApi/Generator/Authentication/ConstructGenerator.php
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
69 changes: 65 additions & 4 deletions src/OpenApi/Generator/Authentication/GetPluginGenerator.php
Expand Up @@ -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
{
Expand All @@ -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;
}

Expand Down
@@ -0,0 +1,32 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Authentication;

class BasicAuthentication implements \Jane\OpenApiRuntime\Client\Authentication
{
private $username;
private $password;
public function __construct(string $username, string $password)
{
$this->{'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);
}
});
}
}
Expand Up @@ -42,7 +42,7 @@
"securitySchemes": {
"Basic": {
"type": "http",
"scheme": "Bearer"
"scheme": "basic"
}
}
}
Expand Down
@@ -0,0 +1,9 @@
<?php

return [
'openapi-file' => __DIR__ . '/swagger.json',
'namespace' => 'Jane\OpenApi\Tests\Expected',
'directory' => __DIR__ . '/generated',
'use-fixer' => false,
// 'client' => 'psr18',
];
@@ -0,0 +1,28 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Authentication;

class BearerAuthentication implements \Jane\OpenApiRuntime\Client\Authentication
{
private $token;
public function __construct(string $token)
{
$this->{'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);
}
});
}
}
@@ -0,0 +1,34 @@
<?php

namespace Jane\OpenApi\Tests\Expected;

class Client extends \Jane\OpenApiRuntime\Client\Psr7HttplugClient
{
/**
* @param string $fetch Fetch mode to use (can be OBJECT or RESPONSE)
*
* @return null|\Jane\OpenApi\Tests\Expected\Model\Foo|\Psr\Http\Message\ResponseInterface
*/
public function getFoo(string $fetch = self::FETCH_OBJECT)
{
return $this->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);
}
}
@@ -0,0 +1,36 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Endpoint;

class GetFoo extends \Jane\OpenApiRuntime\Client\BaseEndpoint implements \Jane\OpenApiRuntime\Client\Psr7Endpoint
{
use \Jane\OpenApiRuntime\Client\Psr7EndpointTrait;
public function getMethod() : string
{
return 'GET';
}
public function getUri() : string
{
return '/foo';
}
public function getBody(\Symfony\Component\Serializer\SerializerInterface $serializer, $streamFactory = null) : array
{
return array(array(), null);
}
public function getExtraHeaders() : array
{
return array('Accept' => 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');
}
}
}
@@ -0,0 +1,34 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Model;

class Foo
{
/**
*
*
* @var string
*/
protected $foo;
/**
*
*
* @return string
*/
public function getFoo() : string
{
return $this->foo;
}
/**
*
*
* @param string $foo
*
* @return self
*/
public function setFoo(string $foo) : self
{
$this->foo = $foo;
return $this;
}
}
@@ -0,0 +1,42 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Normalizer;

use Jane\JsonSchemaRuntime\Reference;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class FooNormalizer implements DenormalizerInterface, NormalizerInterface, DenormalizerAwareInterface, NormalizerAwareInterface
{
use DenormalizerAwareTrait;
use NormalizerAwareTrait;
public function supportsDenormalization($data, $type, $format = null)
{
return $type === 'Jane\\OpenApi\\Tests\\Expected\\Model\\Foo';
}
public function supportsNormalization($data, $format = null)
{
return is_object($data) && get_class($data) === 'Jane\\OpenApi\\Tests\\Expected\\Model\\Foo';
}
public function denormalize($data, $class, $format = null, array $context = array())
{
if (!is_object($data)) {
throw new InvalidArgumentException();
}
$object = new \Jane\OpenApi\Tests\Expected\Model\Foo();
if (property_exists($data, 'foo')) {
$object->setFoo($data->{'foo'});
}
return $object;
}
public function normalize($object, $format = null, array $context = array())
{
$data = new \stdClass();
$data->{'foo'} = $object->getFoo();
return $data;
}
}
@@ -0,0 +1,14 @@
<?php

namespace Jane\OpenApi\Tests\Expected\Normalizer;

class NormalizerFactory
{
public static function create()
{
$normalizers = array();
$normalizers[] = new \Symfony\Component\Serializer\Normalizer\ArrayDenormalizer();
$normalizers[] = new FooNormalizer();
return $normalizers;
}
}

0 comments on commit 6e454ef

Please sign in to comment.