#2 API token authentication implemented. Login method implemented. Some refactoring. ApiTokenRepository::findUserByTokenKey() query bug fix.
This commit is contained in:
parent
84f2d5ece9
commit
b35da23e8e
|
@ -8,7 +8,7 @@ use Symfony\Component\HttpFoundation\{JsonResponse, Response};
|
||||||
|
|
||||||
abstract class AbstractApiController extends Controller
|
abstract class AbstractApiController extends Controller
|
||||||
{
|
{
|
||||||
protected const DEFAULT_SERIALIZER_GROUPS = ['api_v1'];
|
protected const DEFAULT_SERIALIZER_GROUPS = ['api'];
|
||||||
|
|
||||||
protected function createJsonResponse($data, array $groups = [], int $code = Response::HTTP_OK, string $message = null, string $status = ''): JsonResponse
|
protected function createJsonResponse($data, array $groups = [], int $code = Response::HTTP_OK, string $message = null, string $status = ''): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,12 +2,42 @@
|
||||||
|
|
||||||
namespace App\Api\V1\Controller;
|
namespace App\Api\V1\Controller;
|
||||||
|
|
||||||
|
use App\Entity\{ApiToken, User};
|
||||||
|
use App\Repository\{ApiTokenRepository, UserRepository};
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Component\HttpFoundation\{JsonResponse, Request};
|
use Symfony\Component\HttpFoundation\{JsonResponse, Request};
|
||||||
|
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
|
||||||
|
|
||||||
class SecurityController extends AbstractApiController
|
class SecurityController extends AbstractApiController
|
||||||
{
|
{
|
||||||
public function login(Request $request): JsonResponse
|
public function login(
|
||||||
{
|
Request $request,
|
||||||
// @todo implement login procedure
|
EntityManagerInterface $em,
|
||||||
|
UserRepository $userRepo,
|
||||||
|
ApiTokenRepository $tokenRepo,
|
||||||
|
UserPasswordEncoderInterface $passwordEncoder
|
||||||
|
): JsonResponse {
|
||||||
|
$username = $request->request->get('username');
|
||||||
|
$password = $request->request->get('password');
|
||||||
|
|
||||||
|
/** @var User $user */
|
||||||
|
if (null === $user = $userRepo->findOneBy(['username' => $username])) {
|
||||||
|
return $this->createJsonResponse(null, [], JsonResponse::HTTP_UNAUTHORIZED, 'User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$passwordEncoder->isPasswordValid($user, $password)) {
|
||||||
|
return $this->createJsonResponse(null, [], JsonResponse::HTTP_UNAUTHORIZED, 'Invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiToken = new ApiToken($user);
|
||||||
|
$tokenRepo->add($apiToken);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$em->flush();
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
return $this->createJsonResponse(null, [], JsonResponse::HTTP_INTERNAL_SERVER_ERROR, 'Token persisting error');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->createJsonResponse($apiToken->getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,28 +15,28 @@ class ApiResponse
|
||||||
/**
|
/**
|
||||||
* @var int HTTP response status code
|
* @var int HTTP response status code
|
||||||
*
|
*
|
||||||
* @Groups({"api_v1"})
|
* @Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $code;
|
private $code;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string Status text: 'success' (1xx-3xx), 'error' (4xx), 'fail' (5xx) or 'unknown'
|
* @var string Status text: 'success' (1xx-3xx), 'error' (4xx), 'fail' (5xx) or 'unknown'
|
||||||
*
|
*
|
||||||
* @Groups({"api_v1"})
|
* @Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $status;
|
private $status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null Used for 'fail' and 'error'
|
* @var string|null Used for 'fail' and 'error'
|
||||||
*
|
*
|
||||||
* @Groups({"api_v1"})
|
* @Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $message;
|
private $message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|\object|array|null Response body. In case of 'error' or 'fail' contains cause or exception name.
|
* @var string|\object|array|null Response body. In case of 'error' or 'fail' contains cause or exception name.
|
||||||
*
|
*
|
||||||
* @Groups({"api_v1"})
|
* @Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $data;
|
private $data;
|
||||||
|
|
||||||
|
|
|
@ -10,35 +10,35 @@ class ListPage
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @Serializer\Groups({"api_v1"})
|
* @Serializer\Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $numberOfPages;
|
private $numberOfPages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @Serializer\Groups({"api_v1"})
|
* @Serializer\Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $currentPage;
|
private $currentPage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @Serializer\Groups({"api_v1"})
|
* @Serializer\Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $numberOfResults;
|
private $numberOfResults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @Serializer\Groups({"api_v1"})
|
* @Serializer\Groups({"api"})
|
||||||
*/
|
*/
|
||||||
private $maxPerPage;
|
private $maxPerPage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \Traversable
|
* @var \Traversable
|
||||||
*
|
*
|
||||||
* @Serializer\Groups({"api_v1"})
|
* @Serializer\Groups({"api"})
|
||||||
*/
|
*/
|
||||||
protected $items;
|
protected $items;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ORM\Table(name="api_tokens", schema="users")
|
* @ORM\Table(name="api_tokens", schema="users")
|
||||||
|
@ -21,6 +22,8 @@ class ApiToken
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
|
* @Serializer\Groups({"api", "api_v1_login"})
|
||||||
|
*
|
||||||
* @ORM\Id()
|
* @ORM\Id()
|
||||||
* @ORM\Column(name="key", type="string", length=32)
|
* @ORM\Column(name="key", type="string", length=32)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Repository;
|
||||||
|
|
||||||
use App\Entity\{ApiToken, User};
|
use App\Entity\{ApiToken, User};
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\ORM\Query\Expr\Join;
|
||||||
use Symfony\Bridge\Doctrine\RegistryInterface;
|
use Symfony\Bridge\Doctrine\RegistryInterface;
|
||||||
|
|
||||||
class ApiTokenRepository extends ServiceEntityRepository
|
class ApiTokenRepository extends ServiceEntityRepository
|
||||||
|
@ -20,9 +21,11 @@ class ApiTokenRepository extends ServiceEntityRepository
|
||||||
|
|
||||||
public function findUserByTokenKey(string $tokenKey): ?User
|
public function findUserByTokenKey(string $tokenKey): ?User
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('at');
|
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||||
$qb
|
$qb
|
||||||
->select('at.user')
|
->select('u')
|
||||||
|
->from(User::class, 'u')
|
||||||
|
->innerJoin(ApiToken::class, 'at', Join::WITH, 'at.user = u')
|
||||||
->where('at.key = :tokenKey')
|
->where('at.key = :tokenKey')
|
||||||
->setParameter('tokenKey', $tokenKey)
|
->setParameter('tokenKey', $tokenKey)
|
||||||
;
|
;
|
||||||
|
|
Loading…
Reference in a new issue