#2 API token authentication implemented. Login method implemented. Some refactoring. ApiTokenRepository::findUserByTokenKey() query bug fix.

This commit is contained in:
Alexey Skobkin 2018-06-27 01:28:42 +03:00
parent 84f2d5ece9
commit b35da23e8e
6 changed files with 51 additions and 15 deletions

View file

@ -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
{ {

View file

@ -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());
} }
} }

View file

@ -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;

View file

@ -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;

View file

@ -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)
*/ */

View file

@ -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)
; ;