ldc-gen-game-web/app/Http/Controllers/Fo/LoginController.php

443 lines
16 KiB
PHP

<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\AuthenticateRequest;
use App\Http\Requests\Fo\RegisterRequest;
use App\Http\Requests\Fo\UpdatePasswordRequest;
use App\Http\Requests\Fo\ForgotPasswordRequest;
use App\Lib\ToolboxHelper;
use App\Mail\PasswordReset;
use App\Mail\VerifyEmail;
use App\Models\Customer;
use App\Models\Settings;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class LoginController extends Controller
{
/**
* Show login form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function loginForm()
{
if (!ToolboxHelper::isLoginAccessAllowed()) {
abort(\response()->redirectToRoute('home'));
}
return view('front.basic.login');
}
/**
* Authenticate user
*
* @param AuthenticateRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function loginFormSubmit(AuthenticateRequest $request)
{
$this->authenticateUser($request);
$this->authenticateCustomer($request);
return back()->withInput($request->except('password'))
->withErrors('Les identifiants entrés sont incorrects.');
}
/**
* Log out
*
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
public function logout()
{
auth('web')->logout();
auth('customer')->logout();
return redirect()->route('home');
}
/**
* Confirm Email
*
* @param mixed $verifyToken
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function confirmEmail($verifyToken)
{
$result = false;
/** @var \App\Models\Customer $customer */
if (!($customer = Customer::select('hashcode')->where('email_verify_token', $verifyToken)->first())) {
return view('front.basic.confirm-email', compact('result'));
}
try {
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
if ($crmCustomer->activate()) {
$result = true;
}
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerAlreadyActive())) {
return view('front.auth.activate')
->with(
'message',
'Votre compte est déjà activé, vous pouvez dès à présent vous connecter avec celui-ci.'
);
}
// ! Une erreur critique est survenue avec la CRM.
report($e);
} catch (CrmException $e) {
// ! Une erreur critique est survenue avec la CRM.
report($e);
} //end try
return view('front.basic.confirm-email', compact('result'));
}
/**
* Forgot Password form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function forgotPassword()
{
return view('front.basic.forgot-password');
}
/**
* forgotPassword submit
*
* @param ForgotPasswordRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function forgotPasswordSubmit(ForgotPasswordRequest $request)
{
if (!($crmCustomer = $this->getCustomerFromCrm($request->email))) {
return back()->withInput($request->only('email'))
->withErrors('Le mail que vous avez entré n\'est pas enregistré dans nos services.');
}
/** @var \App\Models\Customer $customer */
$customer = $this->createLocalCustomer($crmCustomer);
$customer->password_token = Str::uuid();
$customer->saveOrFail();
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
Mail::to($request->email)->send(new PasswordReset($customer, $settings));
return redirect()->route('login')
->with('success', 'Un mail de réinitialisation de votre mot de passe vous a été envoyé(e).');
}
/**
* Forgot password form show
*
* @param Request $request
* @param mixed $token
* @return \Illuminate\Http\RedirectResponse
*/
public function forgotPasswordReset(Request $request, $token)
{
if (!$request->hasValidSignature(false)) {
return redirect()->route('login')
->withErrors('Le lien de changement de votre mot de passe n\'est plus valide, veuillez réessayer. ');
}
if (!Customer::where('password_token', $token)->exists()) {
return redirect()->route('forgot-password', ['tokenValid' => false])
->withErrors('Le lien de changement du mot de passe est incorrect.');
}
return view('front.basic.forgot-password', [
'tokenValid' => true,
'token' => $token
]);
}
/**
* Change password form submit
*
* @param UpdatePasswordRequest $request
* @param mixed $token
* @return \Illuminate\Http\RedirectResponse
*/
public function forgotPasswordResetSubmit(UpdatePasswordRequest $request, $token)
{
/** @var \App\Models\Customer $customer */
if (!$request->hasValidSignature(false) or !($customer = Customer::where('password_token', $token)->first())) {
return redirect()->route('forgot-password', ['tokenValid' => false])
->withErrors('Le lien de changement du mot de passe est incorrect.');
}
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
try {
// Ne devrait pas survenir, $crm->login throws ApiError::authFailed() .
if ($crm->login($customer->email, $request->password)) {
// Mauvais identifiants.
return back()->withErrors('Vous ne pouvez pas utiliser un de vos anciens mot de passe.');
}
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch (ApiErrorException $e) {
// * Continue
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM.
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
}
try {
if (!($crmCustomer = $crm->getCustomer($customer->hashcode))) {
return back()->withErrors('Changement impossible, compte inexistant.');
}
$crmCustomer->password = $request->password;
$crmCustomer->save();
$customer->password_token = null;
$customer->saveOrFail();
return redirect()->route('login')
->with('success', 'Vous pouvez désormais vous connecter avec votre nouveau mot de passe.');
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} catch (ValidationException $e) {
return back()->withErrors(sprintf('Nouveau mot de passe : %s', $e->getMessage()));
}
}
/**
* Try Login User if exists (email)
*
* Does nothing if email does not exists in users table.
*
* @param AuthenticateRequest $request
* @return void
* @throws \Symfony\Component\HttpKernel\Exception\HttpException If user is logged in.
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException If redirect route is not found.
*/
private function authenticateUser(AuthenticateRequest $request)
{
if (
User::whereEmail($request->email)->exists() and
\auth('web')->attempt([
'email' => $request->email,
'password' => $request->password
], $request->remember)
) {
abort(redirect()->route('bo.dashboard'));
}
}
/**
* Try Login Customer if exists (email)
*
* Does nothing if email does not exists in customers table.
*
* @param AuthenticateRequest $request
* @return void
* @throws \Symfony\Component\HttpKernel\Exception\HttpException If user is logged in.
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException If redirect route is not found.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.Missing
*/
private function authenticateCustomer(AuthenticateRequest $request)
{
/** @var \App\Models\Customer|null $customerLocal */
$customer = Customer::where('email', $request->email)->first();
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
try {
$crm->login($request->email, $request->password);
if (!($crmCustomer = $this->getCustomerFromCrm($request->email))) {
abort(back()->withInput($request->except('password'))
->withErrors('Une erreur est survenue veuillez réssayer plus tard.'));
}
// ? Test is user data valid ?
try {
/**
* On va tester de mettre a jour uniquement les champs
* requis pour mes br afin de detecter si ils doivent etre mis à jour
* la validation va se faire par la crm (ou le client).
*/
$crmCustomer->fill([
'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address,
'postcode' => $crmCustomer->postcode,
'country' => $crmCustomer->country,
'city' => $crmCustomer->city
]);
// * On valide les données selon les critères de registerController
$rules = collect((new RegisterRequest())->rules());
$validator = Validator::make(
[
'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address,
'postcode' => $crmCustomer->postcode,
'country' => $crmCustomer->country->value,
'city' => $crmCustomer->city
],
[
'birth_date' => $rules->get('birth_date'),
'address' => $rules->get('address'),
'postcode' => $rules->get('postcode'),
'country' => $rules->get('country'),
'city' => $rules->get('city')
]
);
if ($validator->fails()) {
throw new ValidationException();
} else {
Session::remove('needCrmInfoUpdate', true);
}
} catch (ValidationException $e) {
Session::push('needCrmInfoUpdate', true);
} catch (ApiErrorException $e) {
if (!$e->getApiError()->equals(ApiError::validationFailed())) {
throw $e;
}
Session::push('needCrmInfoUpdate', true);
} //end try
// * Si pas de compte local mais existe en CRM.
if (!$customer) {
$customer = $this->createLocalCustomer($crmCustomer);
}
abort($this->processLogin($customer, $request->remember));
} catch (ApiErrorException $e) {
abort($this->handleLoginApiErrorException($e, $request));
} catch (CrmException $e) {
\report($e);
abort(back()->withInput($request->except('password'))
->withErrors('La connexion est impossible pour le moment, veuillez réessayer plus tard.'));
} //end try
}
/**
* Handle Api error exception
*
* @param ApiErrorException $e
* @param AuthenticateRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
private function handleLoginApiErrorException(ApiErrorException $e, AuthenticateRequest $request)
{
/** @var Customers|null */
$customer = Customer::where('email', $request->email)->first();
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
switch ($e->getApiError()) {
case ApiError::authFailed():
// * Mauvais identifiants
return back()->withInput($request->except('password'))
->withErrors('Email ou mot de passe incorrect.');
case ApiError::customerNotActivated():
// * Le compte n'est pas activé
try {
if (!($crmCustomer = $crm->getCustomer($request->email))) {
Log::critical(sprintf('Customer %s could not be retrieved from crm', $request->email));
return back()->withErrors(
__('Une erreur est survenue, merci de réessayer ultérieurement.')
);
}
if (!$customer) {
$customer = $this->createLocalCustomer($crmCustomer);
}
$tokenMail = Str::uuid();
Mail::to($request->email)->send(new VerifyEmail($crmCustomer, $tokenMail));
$customer->email_verify_token = $tokenMail;
$customer->saveOrFail();
return back()
->withErrors(
__('Votre compte n\'est pas activé, vérifiez dans vos emails pour y trouver
le mail de confirmation. Un lien d\'activation vous a été renvoyé.')
);
} catch (CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} //end try
default:
report($e);
// ! Une erreur non gérée
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} //end switch
}
/**
* Get Local customer using email and update info from crm.
*
* @param string $email
* @return CrmCustomer|null
*/
private function getCustomerFromCrm(string $email): ?CrmCustomer
{
try {
return CrmCustomer::findWithEmail($email);
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::modelNotFound())) {
return null;
}
abort(back()->withErrors($e->getMessage()));
} catch (CrmException $e) {
report($e);
abort(back()->withErrors('Une erreur est survenue'));
}
}
/**
* Create a local customer
*
* @param CrmCustomer $crmCustomer
* @return Customer
*/
private function createLocalCustomer(CrmCustomer $crmCustomer): Customer
{
/** @var \App\Models\Customer $customer */
$customer = Customer::firstOrNew([
'email' => $crmCustomer->email
]);
$customer->hashcode = $crmCustomer->hashcode;
$customer->saveOrFail();
return $customer;
}
/**
* Login a customer
*
* @param Customer $customer
* @param boolean $remember
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
private function processLogin(Customer $customer, bool $remember)
{
/** @var \Illuminate\Auth\SessionGuard */
$guard = auth('customer');
$guard->login($customer, $remember);
$url = Session::pull('url.intended');
Session::forget('url.intended');
Session::save();
return $url ? redirect()->to($url) : response()->redirectToRoute('saisir-code');
}
}