<?php
namespace Nen\Bundle\KennisbankPlatformBundle\Security;
use App\Entity\User;
use Carbon\Carbon;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\QueryParameterTokenExtractor;
use Nen\Bundle\KennisbankPlatformBundle\Entity\PartnerLoginUser;
use Nen\Bundle\KennisbankPlatformBundle\Repository\UserRepository;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class NenAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'login';
private UrlGeneratorInterface $urlGenerator;
private EntityManagerInterface $em;
private JWTEncoderInterface $jwtEncoder;
private UserRepository $repository;
public function __construct(
UrlGeneratorInterface $urlGenerator,
EntityManagerInterface $entityManager,
JWTEncoderInterface $jwtEncoder,
UserRepository $repository
) {
$this->urlGenerator = $urlGenerator;
$this->em = $entityManager;
$this->jwtEncoder = $jwtEncoder;
$this->repository = $repository;
}
public function supports(Request $request): bool
{
if (self::LOGIN_ROUTE !== $request->attributes->get('_route')) {
return false;
}
if (!$request->isMethod('POST')) {
return false;
}
return true;
}
public function authenticate(Request $request): PassportInterface
{
$credentials = [
'username' => $request->request->get('username', ''),
'password' => $request->request->get('password', '')
];
$request->getSession()->set(Security::LAST_USERNAME, $credentials['username']);
return new Passport(
new UserBadge($credentials['username']),
new PasswordCredentials($credentials['password']),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
new PasswordUpgradeBadge($credentials['password'], $this->repository),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$this->updateLoginInformation($token);
$this->handleJwtToken($request, $token);
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
$continue = $targetPath;
} else {
$continue = $this->urlGenerator->generate('mijnomgeving_index');
}
if ($request->isXmlHttpRequest()) {
$response = new JsonResponse(['url' => $continue]);
} else {
$response = new RedirectResponse($continue);
}
$this->handleRememberUsername($request, $response);
return $response;
}
private function updateLoginInformation(TokenInterface $token): void
{
/** @var User $user */
$user = $token->getUser();
$user->setLoginAt(new DateTime());
$this->em->flush();
}
private function handleJwtToken(Request $request, TokenInterface $token): void
{
$extractor = new QueryParameterTokenExtractor('jwt');
$jwtToken = $extractor->extract($request);
if ($jwtToken === false) {
return;
}
try {
$data = $this->jwtEncoder->decode($jwtToken);
} catch (JWTDecodeFailureException $e) {
return;
}
$repository = $this->em->getRepository(PartnerLoginUser::class);
$partnerLogin = $repository->findOneBy(
[
'partnerName' => $data['clientName'],
'partnerUserId' => $data['userId'],
]
);
if ($partnerLogin !== null) {
return;
}
/** @var User $user */
$user = $token->getUser();
$partnerLoginUser = new PartnerLoginUser();
$partnerLoginUser->setPartnerName($data['clientName']);
$partnerLoginUser->setPartnerUserId($data['userId']);
$partnerLoginUser->setUser($user);
$this->em->persist($partnerLoginUser);
$this->em->flush();
}
private function handleRememberUsername(Request $request, Response $response): void
{
if (!$request->request->getBoolean('remember_username')) {
$response->headers->removeCookie('wmn_username');
return;
}
$cookie = new Cookie('wmn_username', $request->request->get('username'), Carbon::now()->addYear());
$response->headers->setCookie($cookie);
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
}