<?php
namespace Fonial\FrontendBundle\Controller;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Fonial\DataBundle\Entity\AccountSetting;
use Fonial\DataBundle\Entity\AccountUserSetting;
use Fonial\DataBundle\Entity\GlobalSetting;
use Fonial\DataBundle\Services\ConfigHelper;
use Fonial\FrontendBundle\Security\LoginFormAuthenticator;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use User\UserBundle\Entity\User;
/**
* TODO: make this controller use real di... this maybe needs to get rid of FOS Bundle...
* @method User getUser()
*/
class SecurityController extends AbstractController
{
private ConfigHelper $configHelper;
private SessionInterface $session;
private EntityManagerInterface $em;
public function __construct(
ConfigHelper $configHelper,
SessionInterface $session,
EntityManagerInterface $em
) {
$this->configHelper = $configHelper;
$this->session = $session;
$this->em = $em;
}
/**
* This method basically just sets the _locale into the session if not available, then calls the original method
*
* @param AuthenticationUtils $authenticationUtils
* @return Response
*/
public function loginAction(AuthenticationUtils $authenticationUtils): Response
{
// REDIRECT TO DASHBOARD IF ALREADY LOGGED IN
if (!$this->session->get('_locale')) {
$this->session->set('_locale', $this->configHelper->getConfigParameter('kernel.default_locale'));
}
if ($this->getUser() !== null) {
// REDIRECT TO DASHBOARD IF ALREADY LOGGED IN
return $this->redirectToRoute('fonial_frontend_dashboard_index');
}
/** @var GlobalSetting $loginFooter */
$loginFooter = $this->em->getRepository(GlobalSetting::class)->findOneBy([
'key' => GlobalSetting::CUSTOM_FOOTER_LOGIN,
]);
$this->session->remove(GlobalSetting::CUSTOM_FOOTER_LOGIN);
if (null !== $loginFooter) {
$this->session->set(GlobalSetting::CUSTOM_FOOTER_LOGIN, $loginFooter->getValue());
}
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
return $this->renderLogin([
'last_username' => $lastUsername,
'error' => $error,
]);
}
protected function renderLogin(array $data): ?Response
{
if ($data['error']) {
// If we have an error, get more user info to give mor specific info
/** @var User $user */
$user = $this->em->getRepository(User::class)->findOneBy([
'username' => $data['last_username']
]);
if ($user) {
// Check, if there is a already lockout time or just some failed logins
if ($user->getFailedLoginTimeout()) {
$data['loginIsTimedOut'] = true;
$data['remainingTime'] = $user->getFailedLoginTimeout()->diff(new DateTime())->format('%I:%S');
} else {
$securitySettings = $this->configHelper->getConfigParameter('security');
$remainingAttempts = (int)$securitySettings['brute_force_max_logins'] - $user->getFailedLoginCount();
$data['passwordIsWrong'] = true;
$data['remainingAttempts'] = $remainingAttempts;
}
}
}
$data['isBackend'] = $_SERVER['SYMFONY_ENVIRONMENT'] === 'app_backend';
// TODO trigger warning
$data['alert'] = array(
'type' => 'Warning',
'message' => 'Anbindung an das Telefonnetz derzeit gestört!',
'info' => 'https://www.fonial.de',
);
$signUpServiceConfig = $this->configHelper->getConfigParameter('signUpService');
if (is_array($signUpServiceConfig) && !empty($signUpServiceConfig) && isset($signUpServiceConfig['webservice_url'])) {
$data['signUpServiceUrl'] = $signUpServiceConfig['webservice_url'];
if (!str_starts_with($data['signUpServiceUrl'], 'http')) {
$data['signUpServiceUrl'] = 'https://' . $data['signUpServiceUrl'];
}
}
return $this->render('@FonialFrontend/Security/login.html.twig', $data);
}
public function remoteloginAction(
string $guid,
string $sessionId,
LoginFormAuthenticator $loginAuthenticator,
GuardAuthenticatorHandler $guardHandler,
Request $request
): ?Response
{
/** @var ?User $user */
$user = $this->em->getRepository(User::class)->findOneBy(['guid' => $guid]);
/** @var ?AccountSetting $sessionSetting */
$sessionSetting = $this->em->getRepository(AccountUserSetting::class)->findOneBy([
'user' => $user,
'data0' => $sessionId
]);
if (!($sessionSetting instanceof AccountUserSetting)) {
return $this->redirectToRoute('fonial_frontend_login');
}
$this->em->remove($sessionSetting);
$this->em->flush();
//Now lets log them in
$response = $guardHandler->authenticateUserAndHandleSuccess(
user: $user,
request: $request,
authenticator: $loginAuthenticator,
providerKey: 'main'
);
/** @var GlobalSetting $customSidebar */
$customSidebar = $this->em->getRepository(GlobalSetting::class)->findOneBy([
'key' => GlobalSetting::CUSTOM_SIDEBAR,
]);
$this->session->remove(GlobalSetting::CUSTOM_SIDEBAR);
if (null !== $customSidebar) {
$this->session->set(GlobalSetting::CUSTOM_SIDEBAR, $customSidebar->getValue());
}
$response?->headers->setCookie(new Cookie('adminLogin', 1));
return $response;
}
public function logoutAction()
{
throw new RuntimeException('You must activate the logout in your security firewall configuration.');
}
}