Files
myep-team/src/Security/BpnAuthenticator.php
T

155 lines
5.3 KiB
PHP

<?php
namespace App\Security;
use App\BusProNet\ApiClient;
use App\BusProNet\ApiClientException;
use App\BusProNet\Model\CrmAttributesResponse;
use App\BusProNet\Model\ProfileResponse;
use App\BusProNet\UserDataHandler;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
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\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class BpnAuthenticator extends AbstractLoginFormAuthenticator implements AuthenticationEntryPointInterface
{
use TargetPathTrait;
public function __construct(
private readonly UrlGeneratorInterface $urlGenerator,
private readonly EntityManagerInterface $entityManager,
private readonly ApiClient $apiClient,
private readonly LoggerInterface $logger,
private readonly UserDataHandler $userDataHandler
) {
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate('app_security_login');
}
public function authenticate(Request $request): Passport
{
$email = trim($request->request->get('_username', ''));
$passwordPlain = trim($request->request->get('_password', ''));
// Very lame hashing applied here as required by BPN
$password = md5($passwordPlain);
$csrfToken = $request->request->get('_csrf_token', '');
return new SelfValidatingPassport(
new UserBadge($email, function () use ($email, $password, $request) {
try {
$response = $this->apiClient->getProfile($email, $password);
} catch (ApiClientException $e) {
return null;
}
if (false === $response instanceof ProfileResponse) {
return null;
}
// Final checks and local user loading/creation
$preferredRole = $request->request->get('_role');
$user = $this->getOrCreateLocalUser($response, $email, $password, $preferredRole);
if (null === $user) {
return null;
}
// Store BPN password in session for later use
$request->getSession()->set('bpn_password', $password);
return $user;
}),
[new CsrfTokenBadge('authenticate', $csrfToken)]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
/** @var User $user */
$user = $token->getUser();
$user->setLastLoginAt(new \DateTimeImmutable());
$this->logger->info('Login');
$this->entityManager->flush();
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
$url = $this->urlGenerator->generate($user->getDefaultRoute());
return new RedirectResponse($url);
}
private function getOrCreateLocalUser(
ProfileResponse $profileResponse,
string $email,
string $password,
?string $preferredRole
): ?User {
// Fetch CRM attributes, early return in case of an API error
try {
/** @var CrmAttributesResponse $crmAttributes */
$crmAttributes = $this->apiClient->getCrmAttributes($email, $password);
} catch (ApiClientException $e) {
return null;
}
// Collect user's roles from CRM attributes
$roles = $this
->userDataHandler
->collectRoles($crmAttributes, $preferredRole)
;
// User is expected to have at least one role
if (0 === count($roles)) {
return null;
}
// Flatten selected CRM attributes
$crmSelections = $crmAttributes->toArray();
// Determine teamer status from CRM attributes
$isTeamer = $crmAttributes->isTeamer();
// Check if user is already present in local database
$user = $this
->userDataHandler
->findLocalUser($profileResponse)
;
// Update existing user's roles and teamer data and return it
if (null !== $user) {
$this
->userDataHandler
->updateLocalUser($user, $profileResponse, $roles, $isTeamer, $crmSelections, $crmAttributes->getHotelCode())
;
return $user;
}
return $this
->userDataHandler
->createLocalUser($profileResponse, $roles, $isTeamer, $crmSelections, $crmAttributes->getHotelCode())
;
}
}