diff --git a/src/Api/V1/Controller/AbstractApiController.php b/src/Api/V1/Controller/AbstractApiController.php index fb0a90c..47f5548 100644 --- a/src/Api/V1/Controller/AbstractApiController.php +++ b/src/Api/V1/Controller/AbstractApiController.php @@ -8,7 +8,7 @@ use Symfony\Component\HttpFoundation\{JsonResponse, Response}; 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 { diff --git a/src/Api/V1/Controller/SecurityController.php b/src/Api/V1/Controller/SecurityController.php index 46c3a6f..7f123db 100644 --- a/src/Api/V1/Controller/SecurityController.php +++ b/src/Api/V1/Controller/SecurityController.php @@ -2,12 +2,42 @@ 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\Security\Core\Encoder\UserPasswordEncoderInterface; class SecurityController extends AbstractApiController { - public function login(Request $request): JsonResponse - { - // @todo implement login procedure + public function login( + Request $request, + 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()); } } \ No newline at end of file diff --git a/src/Api/V1/DTO/ApiResponse.php b/src/Api/V1/DTO/ApiResponse.php index b31a6ed..92dcd7f 100644 --- a/src/Api/V1/DTO/ApiResponse.php +++ b/src/Api/V1/DTO/ApiResponse.php @@ -15,28 +15,28 @@ class ApiResponse /** * @var int HTTP response status code * - * @Groups({"api_v1"}) + * @Groups({"api"}) */ private $code; /** * @var string Status text: 'success' (1xx-3xx), 'error' (4xx), 'fail' (5xx) or 'unknown' * - * @Groups({"api_v1"}) + * @Groups({"api"}) */ private $status; /** * @var string|null Used for 'fail' and 'error' * - * @Groups({"api_v1"}) + * @Groups({"api"}) */ private $message; /** * @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; diff --git a/src/Api/V1/DTO/ListPage.php b/src/Api/V1/DTO/ListPage.php index 6c81cfd..3ddd47b 100644 --- a/src/Api/V1/DTO/ListPage.php +++ b/src/Api/V1/DTO/ListPage.php @@ -10,35 +10,35 @@ class ListPage /** * @var int * - * @Serializer\Groups({"api_v1"}) + * @Serializer\Groups({"api"}) */ private $numberOfPages; /** * @var int * - * @Serializer\Groups({"api_v1"}) + * @Serializer\Groups({"api"}) */ private $currentPage; /** * @var int * - * @Serializer\Groups({"api_v1"}) + * @Serializer\Groups({"api"}) */ private $numberOfResults; /** * @var int * - * @Serializer\Groups({"api_v1"}) + * @Serializer\Groups({"api"}) */ private $maxPerPage; /** * @var \Traversable * - * @Serializer\Groups({"api_v1"}) + * @Serializer\Groups({"api"}) */ protected $items; diff --git a/src/Entity/ApiToken.php b/src/Entity/ApiToken.php index 61ef9d1..eb58869 100644 --- a/src/Entity/ApiToken.php +++ b/src/Entity/ApiToken.php @@ -3,6 +3,7 @@ namespace App\Entity; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * @ORM\Table(name="api_tokens", schema="users") @@ -21,6 +22,8 @@ class ApiToken /** * @var string * + * @Serializer\Groups({"api", "api_v1_login"}) + * * @ORM\Id() * @ORM\Column(name="key", type="string", length=32) */ diff --git a/src/Repository/ApiTokenRepository.php b/src/Repository/ApiTokenRepository.php index 0a7847b..523be7c 100644 --- a/src/Repository/ApiTokenRepository.php +++ b/src/Repository/ApiTokenRepository.php @@ -4,6 +4,7 @@ namespace App\Repository; use App\Entity\{ApiToken, User}; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\Query\Expr\Join; use Symfony\Bridge\Doctrine\RegistryInterface; class ApiTokenRepository extends ServiceEntityRepository @@ -20,9 +21,11 @@ class ApiTokenRepository extends ServiceEntityRepository public function findUserByTokenKey(string $tokenKey): ?User { - $qb = $this->createQueryBuilder('at'); + $qb = $this->getEntityManager()->createQueryBuilder(); $qb - ->select('at.user') + ->select('u') + ->from(User::class, 'u') + ->innerJoin(ApiToken::class, 'at', Join::WITH, 'at.user = u') ->where('at.key = :tokenKey') ->setParameter('tokenKey', $tokenKey) ;