197 lines
6.2 KiB
PHP
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();
|
|
}
|
|
}
|