ldc-gen-game-web/app/Auth/CrmUserProvider.php
2026-10-20 12:27:49 +02:00

197 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Auth;
use App\Models\Customer;
use ErrorException;
use Exception;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider as UserProviderContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use LDC\LDCCRM\CrmApiV4;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use RuntimeException;
use SensitiveParameter;
use Throwable;
/**
* Crm user provider to integrate nicely with Laravel
*/
class CrmUserProvider extends EloquentUserProvider implements UserProviderContract
{
/**
* The model to use, specified in auth config guard.
*/
private string $usedModel = Customer::class;
/**
* Retrieve a user by the given credentials.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// * Let laravel find a model
$model = parent::retrieveByCredentials($credentials) ?? self::retrieveTrashed($credentials);
try {
/** @var CrmApiV4 */
$crm = resolve('crmAPI');
// Si pas de compte local.
if (! $model) {
$crmCustomer = $crm->getCustomer($credentials['email']);
if (! $crmCustomer) {
// * Customer is not found on CRM.
return null;
}
$model = $this->createLocalCustomer($crmCustomer);
}
return $model;
} catch (ApiErrorException $e) {
// * Customer is not found.
if ($e->getApiError()->equals(ApiError::modelNotFound)) {
return null;
}
report($e);
Log::error('Unhandled response ' . $e->getApiError()->label());
} catch (CrmException | Exception $e) {
report($e);
// Une erreur critique est survenue avec la CRM.
return null;
} // end try
Log::error(self::class . '::retrieveByCredentials reached end of possibilities.');
return null;
}
/**
* Validate a user against the given credentials.
*
* @throws RuntimeException If model provided is not a Customer or connection to server fails.
* @throws CrmException On CRM call error.
* @throws Exception On CRM call error.
*/
public function validateCredentials(UserContract $model, array $credentials): ?bool
{
/** @var Customer $model */
if ($model::class !== $this->usedModel) {
throw new RuntimeException('Cannot authenticate any thing else than ' . $this->usedModel);
}
try {
/** @var CrmApiV4 */
$crm = resolve('crmAPI');
// * If has trait soft delete and customer trashed, then restore customer.
if (
\in_array(SoftDeletes::class, \class_uses_recursive($model)) && $model->trashed()
) {
$model->restore();
}
// * Ne devrait pas survenir, $crm->login throws ApiError::authFailed()
return $crm->login($credentials['email'], $credentials['password']);
} catch (ApiErrorException $e) {
if (
$e->getApiError()->equals(ApiError::modelNotFound) || ($e->getApiError()->equals(ApiError::authFailed) && ! $model->existsInCrm())
) {
$model->delete();
return false;
}
if ($e->getApiError()->equals(ApiError::authFailed)) {
return false;
}
// ! Assert Authenticable model must be valided (MustValidate trait).
if ($e->getApiError()->equals(ApiError::customerNotActivated)) {
$model->email_verified_at = null;
$model->save();
$model->sendEmailVerificationNotification();
Session::flash('warning', \trans('front.messages.account_not_activated'));
return true;
}
} catch (CrmException | Exception $e) {
report($e);
throw_if(config('app.debug'), $e);
return false;
} // end try
Log::error(self::class . '::validateCredentials reached end of possibilities.');
return null;
}
/**
* CRM customer passwordless (Laravel 11 introduces password rehashing after login).
*/
public function rehashPasswordIfRequired(
UserContract $user,
#[SensitiveParameter] array $credentials,
bool $force = false
): void {
//
}
/**
* Create a local customer.
*
* @throws Throwable If saving to DB failed.
*/
private function createLocalCustomer(\LDC\LDCCRM\Models\Customer $crmCustomer): Model
{
/** @var Customer $model */
$model = $this->usedModel::withTrashed()->firstOrNew([
'email' => $crmCustomer->email,
]);
$model->hashcode = $crmCustomer->hashcode;
$model->email_verified_at = $crmCustomer->status ? now() : null;
if ($model->trashed()) {
$model->restore();
}
$model->saveOrFail();
return $model;
}
/**
* Retrieve a trashed customer by the given credentials.
*
* @throws ErrorException If cannot connect to LDAP.
*/
private function retrieveTrashed(array $credentials): ?UserContract
{
// * Make changes
if ($credentials === []) {
return null;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a customer, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
/** @var Customer|null $query */
return $query->withTrashed()->first();
}
}