feat(LoginController): Ajoute l'authentification EsyWeb avec 2FA et avatar.

This commit is contained in:
Serreau Jovann
2025-11-12 10:34:54 +01:00
parent 001edac143
commit 258108bf40

View File

@@ -0,0 +1,158 @@
<?php
namespace App\Controller\Api\Private\EsyWeb;
use App\Entity\Account;
use App\Entity\EsyWeb\WebsiteKey;
use App\Repository\AccountRepository;
use App\Repository\EsyWeb\WebsiteDnsRepository;
use App\Repository\EsyWeb\WebsiteKeyRepository;
use App\Service\Mailer\Mailer;
use Doctrine\ORM\EntityManagerInterface;
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
class LoginController extends AbstractController
{
private const API_KEY_TYPE = 'api_key';
#[Route('/api/avatar', name: 'api_avatar', methods: ['GET'])]
public function avatar(?Account $account)
{
$avatar = new InitialAvatar();
if(!$account instanceof Account) {
$avatar->name("NC");
} else {
$avatar->name($account->getUserIdentifier());
}
$image = $avatar->generateSvg();
return new Response(
$image->toXMLString(),
Response::HTTP_OK,
['Content-Type' => 'image/svg+xml']
);
}
#[Route('/api/private/esyweb/resend2fa', name: 'api_pivate_esyweb_resend2fa', methods: ['POST'])]
public function resend2fa(Request $request,Mailer $mailer,AccountRepository $accountRepository): Response
{
$content = json_decode($request->getContent());
$account = $content->account;
$code = $content->faCode;
$account = $accountRepository->find($account);
if(!$account instanceof Account) {
return new JsonResponse([]);
}
$mailer->send(
$account->getEmail(),
$account->getUsername(),
'[Mainframe] - Double authentication',
'mails/artemis/2fa.twig',
['code' => $code, 'account' => $account]
);
return new JsonResponse([]);
}
#[Route('/api/private/esyweb/login', name: 'api_private_esyweb_login', methods: ['POST'])]
public function status(
Request $request,
WebsiteDnsRepository $websiteDnsRepository,
WebsiteKeyRepository $websiteKeyRepository,
AccountRepository $accountRepository,
UserPasswordHasherInterface $passwordHasher,
Security $security,
Mailer $mailer,
UploaderHelper $uploaderHelper,
): JsonResponse {
$errorResponse = function (): JsonResponse {
return $this->json(['login' => false], Response::HTTP_UNAUTHORIZED);
};
$dns = $request->headers->get('EsyWebDns');
$apiKey = $request->headers->get('EsyWebApiKey');
if (!$dns || !$apiKey) {
// Note: The ApiSubscriber should ideally handle the *missing* headers.
// This handles the case where the headers might be present but empty strings.
return $errorResponse();
}
$websiteDns = $websiteDnsRepository->findOneBy(['dns' => $dns]);
if (!$websiteDns) {
return $errorResponse();
}
// 3. Find WebsiteKey (checking against the constant for type)
/** @var WebsiteKey|null $websiteApiKey */
$websiteApiKey = $websiteKeyRepository->findOneBy([
'apiKey' => $apiKey,
'type' => self::API_KEY_TYPE
]);
if (!$websiteApiKey) {
return $errorResponse();
}
// 4. Cross-reference the Website objects
$websiteFromDns = $websiteDns->getWebsite();
$websiteFromKey = $websiteApiKey->getWebsitre();
if (!$websiteFromDns || !$websiteFromKey || $websiteFromDns->getId() !== $websiteFromKey->getId()) {
// This check ensures both the DNS and API Key belong to the same website
return $errorResponse();
}
$content = json_decode($request->getContent());
if(!isset($content->username)) {
return $errorResponse();
}
if(!isset($content->password)) {
return $errorResponse();
}
$username = base64_decode($content->username);
$password = base64_decode($content->password);
$account = $accountRepository->findOneBy(['username'=>$username]);
if (!$account) {
return $errorResponse();
}
$isPasswordValid = $passwordHasher->isPasswordValid(
$account, // The UserInterface object
$password // The plain-text password submitted by the user
);
if (!$isPasswordValid) {
return $errorResponse();
}
$security->login($account);
$code = random_int(1000, 9999);
$mailer->send(
$account->getEmail(),
$account->getUsername(),
'[Mainframe] - Double authentication',
'mails/artemis/2fa.twig',
['code' => $code, 'account' => $account]
);
if($_ENV['APP_ENV'] == "dev")
$host = $_ENV['DEV_URL'];
else
$host = $request->getSchemeAndHttpHost();
return $this->json(['login' => true,'2fa_required'=>true,'2faCode'=>$code,'user_data'=>[
'username' => $account->getUsername(),
'email' => $account->getEmail(),
'role' => $account->getRoles()[0],
'avatar' => $host. (($account->getAvatarOriginalName() != null)?$uploaderHelper->asset($account,'avatar'):$this->generateUrl('api_avatar',['id'=>$account->getId()]))
]], Response::HTTP_OK);
}
}