mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
Updated AuthenticateAction to generate and return a JWT
This commit is contained in:
parent
a60080b1ce
commit
9573e9f4ef
6 changed files with 63 additions and 18 deletions
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
use Acelaya\ZsmAnnotatedServices\Factory\V3\AnnotatedFactory;
|
||||
use Shlinkio\Shlink\Rest\Action;
|
||||
use Shlinkio\Shlink\Rest\Authentication\JWTService;
|
||||
use Shlinkio\Shlink\Rest\Middleware;
|
||||
use Shlinkio\Shlink\Rest\Service;
|
||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||
|
@ -9,6 +10,7 @@ return [
|
|||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
JWTService::class => AnnotatedFactory::class,
|
||||
Service\RestTokenService::class => AnnotatedFactory::class,
|
||||
Service\ApiKeyService::class => AnnotatedFactory::class,
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
|||
use Firebase\JWT\JWT;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use Shlinkio\Shlink\Rest\Authentication\JWTService;
|
||||
use Shlinkio\Shlink\Rest\Authentication\JWTServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||
|
@ -21,18 +23,27 @@ class AuthenticateAction extends AbstractRestAction
|
|||
* @var ApiKeyService|ApiKeyServiceInterface
|
||||
*/
|
||||
private $apiKeyService;
|
||||
/**
|
||||
* @var JWTServiceInterface
|
||||
*/
|
||||
private $jwtService;
|
||||
|
||||
/**
|
||||
* AuthenticateAction constructor.
|
||||
* @param ApiKeyServiceInterface|ApiKeyService $apiKeyService
|
||||
* @param JWTServiceInterface|JWTService $jwtService
|
||||
* @param TranslatorInterface $translator
|
||||
*
|
||||
* @Inject({ApiKeyService::class, "translator"})
|
||||
* @Inject({ApiKeyService::class, JWTService::class, "translator"})
|
||||
*/
|
||||
public function __construct(ApiKeyServiceInterface $apiKeyService, TranslatorInterface $translator)
|
||||
{
|
||||
public function __construct(
|
||||
ApiKeyServiceInterface $apiKeyService,
|
||||
JWTServiceInterface $jwtService,
|
||||
TranslatorInterface $translator
|
||||
) {
|
||||
$this->translator = $translator;
|
||||
$this->apiKeyService = $apiKeyService;
|
||||
$this->jwtService = $jwtService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,15 +65,16 @@ class AuthenticateAction extends AbstractRestAction
|
|||
}
|
||||
|
||||
// Authenticate using provided API key
|
||||
if (! $this->apiKeyService->check($authData['apiKey'])) {
|
||||
$apiKey = $this->apiKeyService->getByKey($authData['apiKey']);
|
||||
if (! $apiKey->isValid()) {
|
||||
return new JsonResponse([
|
||||
'error' => RestUtils::INVALID_API_KEY_ERROR,
|
||||
'message' => $this->translator->translate('Provided API key does not exist or is invalid.'),
|
||||
], 401);
|
||||
}
|
||||
|
||||
// TODO Generate a JSON Web Token that will be used for authorization in next requests
|
||||
|
||||
return new JsonResponse(['token' => '']);
|
||||
// Generate a JSON Web Token that will be used for authorization in next requests
|
||||
$token = $this->jwtService->create($apiKey);
|
||||
return new JsonResponse(['token' => $token]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace Shlinkio\Shlink\Rest\Authentication;
|
||||
|
||||
use Acelaya\ZsmAnnotatedServices\Annotation\Inject;
|
||||
use Firebase\JWT\JWT;
|
||||
use Shlinkio\Shlink\Core\Options\AppOptions;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
|
@ -16,6 +17,8 @@ class JWTService implements JWTServiceInterface
|
|||
/**
|
||||
* JWTService constructor.
|
||||
* @param AppOptions $appOptions
|
||||
*
|
||||
* @Inject({AppOptions::class})
|
||||
*/
|
||||
public function __construct(AppOptions $appOptions)
|
||||
{
|
||||
|
|
|
@ -52,9 +52,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
|||
public function check($key)
|
||||
{
|
||||
/** @var ApiKey $apiKey */
|
||||
$apiKey = $this->em->getRepository(ApiKey::class)->findOneBy([
|
||||
'key' => $key,
|
||||
]);
|
||||
$apiKey = $this->getByKey($key);
|
||||
if (! isset($apiKey)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -71,9 +69,7 @@ class ApiKeyService implements ApiKeyServiceInterface
|
|||
public function disable($key)
|
||||
{
|
||||
/** @var ApiKey $apiKey */
|
||||
$apiKey = $this->em->getRepository(ApiKey::class)->findOneBy([
|
||||
'key' => $key,
|
||||
]);
|
||||
$apiKey = $this->getByKey($key);
|
||||
if (! isset($apiKey)) {
|
||||
throw new InvalidArgumentException(sprintf('API key "%s" does not exist and can\'t be disabled', $key));
|
||||
}
|
||||
|
@ -94,4 +90,17 @@ class ApiKeyService implements ApiKeyServiceInterface
|
|||
$conditions = $enabledOnly ? ['enabled' => true] : [];
|
||||
return $this->em->getRepository(ApiKey::class)->findBy($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find one API key by its key string
|
||||
*
|
||||
* @param string $key
|
||||
* @return ApiKey|null
|
||||
*/
|
||||
public function getByKey($key)
|
||||
{
|
||||
return $this->em->getRepository(ApiKey::class)->findOneBy([
|
||||
'key' => $key,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,4 +36,12 @@ interface ApiKeyServiceInterface
|
|||
* @return ApiKey[]
|
||||
*/
|
||||
public function listKeys($enabledOnly = false);
|
||||
|
||||
/**
|
||||
* Tries to find one API key by its key string
|
||||
*
|
||||
* @param string $key
|
||||
* @return ApiKey|null
|
||||
*/
|
||||
public function getByKey($key);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace ShlinkioTest\Shlink\Rest\Action;
|
|||
use PHPUnit_Framework_TestCase as TestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Shlinkio\Shlink\Rest\Action\AuthenticateAction;
|
||||
use Shlinkio\Shlink\Rest\Authentication\JWTService;
|
||||
use Shlinkio\Shlink\Rest\Entity\ApiKey;
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
||||
use Zend\Diactoros\Response;
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
|
@ -19,11 +21,20 @@ class AuthenticateActionTest extends TestCase
|
|||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $apiKeyService;
|
||||
/**
|
||||
* @var ObjectProphecy
|
||||
*/
|
||||
protected $jwtService;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->apiKeyService = $this->prophesize(ApiKeyService::class);
|
||||
$this->action = new AuthenticateAction($this->apiKeyService->reveal(), Translator::factory([]));
|
||||
$this->jwtService = $this->prophesize(JWTService::class);
|
||||
$this->action = new AuthenticateAction(
|
||||
$this->apiKeyService->reveal(),
|
||||
$this->jwtService->reveal(),
|
||||
Translator::factory([])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,8 +51,8 @@ class AuthenticateActionTest extends TestCase
|
|||
*/
|
||||
public function properApiKeyReturnsTokenInResponse()
|
||||
{
|
||||
$this->apiKeyService->check('foo')->willReturn(true)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->setId(5))
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'apiKey' => 'foo',
|
||||
|
@ -58,8 +69,8 @@ class AuthenticateActionTest extends TestCase
|
|||
*/
|
||||
public function invalidApiKeyReturnsErrorResponse()
|
||||
{
|
||||
$this->apiKeyService->check('foo')->willReturn(false)
|
||||
->shouldBeCalledTimes(1);
|
||||
$this->apiKeyService->getByKey('foo')->willReturn((new ApiKey())->setEnabled(false))
|
||||
->shouldBeCalledTimes(1);
|
||||
|
||||
$request = ServerRequestFactory::fromGlobals()->withParsedBody([
|
||||
'apiKey' => 'foo',
|
||||
|
|
Loading…
Add table
Reference in a new issue