This change improves the handling of hashcodes by utilizing the CrmHashcode value object.
150 lines
5.1 KiB
PHP
150 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Auth;
|
|
|
|
use App\Exceptions\Crm\CrmAuthenticationFailedException;
|
|
use App\Exceptions\Crm\CrmCustomerNotActivatedException;
|
|
use App\Exceptions\CrmServiceException;
|
|
use App\Models\Customer;
|
|
use App\Services\CrmService;
|
|
use Domain\Models\ValueObjects\CrmCustomer;
|
|
use Domain\Models\ValueObjects\Email;
|
|
use Illuminate\Auth\EloquentUserProvider;
|
|
use Illuminate\Contracts\Auth\Authenticatable;
|
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
|
use Illuminate\Contracts\Auth\UserProvider;
|
|
use Illuminate\Contracts\Hashing\Hasher;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Shared\ValueObjects\CrmHashcode;
|
|
use UnexpectedValueException;
|
|
|
|
class CrmUserProvider extends EloquentUserProvider implements UserProvider
|
|
{
|
|
private ?CrmCustomer $crmCustomer = null;
|
|
|
|
/**
|
|
* @param class-string<\Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Database\Eloquent\Model> $model
|
|
*/
|
|
public function __construct(
|
|
Hasher $hasher,
|
|
string $model,
|
|
private readonly CrmService $crmService
|
|
) {
|
|
parent::__construct($hasher, $model);
|
|
}
|
|
|
|
/**
|
|
* The model to use, specified in auth config guard.
|
|
*
|
|
* @var class-string<\App\Models\Customer>
|
|
*/
|
|
private $usedModel = Customer::class;
|
|
|
|
/**
|
|
* Retrieve a user by the given credentials.
|
|
*
|
|
* @param array<string, mixed> $credentials
|
|
*
|
|
* @throws \RuntimeException If connection to server fails.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException On CRM call error.
|
|
* @throws \Exception On CRM call error.
|
|
*/
|
|
#[\Override]
|
|
public function retrieveByCredentials(array $credentials): ?Authenticatable
|
|
{
|
|
$model = parent::retrieveByCredentials($credentials);
|
|
$result = $this->crmService->getCustomerFromEmail(Arr::string($credentials, 'email'));
|
|
|
|
if ($result instanceof CrmServiceException) {
|
|
report($result);
|
|
throw ValidationException::withMessages([
|
|
\trans('An error has occurred. Please try again later'),
|
|
]);
|
|
}
|
|
|
|
$this->crmCustomer = $result;
|
|
|
|
if ($this->crmCustomer instanceof CrmCustomer && $model === null) {
|
|
$model = $this->createLocalCustomer($this->crmCustomer);
|
|
}
|
|
|
|
return $this->crmCustomer !== null ? $model : null;
|
|
}
|
|
|
|
/**
|
|
* Validate a user against the given credentials.
|
|
*
|
|
* @param array<string, mixed> $credentials
|
|
*
|
|
* @throws \RuntimeException If model provided is not a Customer or connection to server fails.
|
|
* @throws \LDC\LDCCRM\Exceptions\CrmException On CRM call error.
|
|
* @throws \Exception On CRM call error.
|
|
*/
|
|
#[\Override]
|
|
public function validateCredentials(Authenticatable $model, array $credentials): bool
|
|
{
|
|
if ($model::class !== $this->usedModel) {
|
|
throw new UnexpectedValueException("Cannot authenticate any thing else than '{$this->usedModel}'");
|
|
}
|
|
|
|
if (! in_array(MustVerifyEmail::class, \class_implements($model))) {
|
|
throw new UnexpectedValueException("Model '{$this->usedModel}' should implement 'MustVerifyEmail'");
|
|
}
|
|
/** @var \Illuminate\Contracts\Auth\Authenticatable&\Illuminate\Contracts\Auth\MustVerifyEmail $model */
|
|
$requestEmail = Arr::string($credentials, 'email');
|
|
$requestPassword = Arr::string($credentials, 'password');
|
|
|
|
try {
|
|
$this->crmService->login(
|
|
$requestEmail,
|
|
$requestPassword
|
|
);
|
|
|
|
return true;
|
|
} catch (CrmCustomerNotActivatedException) {
|
|
$model->sendEmailVerificationNotification();
|
|
|
|
return true;
|
|
} catch (CrmAuthenticationFailedException) {
|
|
return false;
|
|
} catch (CrmServiceException $e) {
|
|
report($e);
|
|
throw ValidationException::withMessages([
|
|
trans('An error has occurred. Please try again later'),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/** @param array<string, mixed> $credentials */
|
|
#[\Override]
|
|
public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false): void
|
|
{
|
|
// No hashing.
|
|
}
|
|
|
|
private function createLocalCustomer(CrmCustomer $crmCustomer): Model & Authenticatable
|
|
{
|
|
$email = $crmCustomer->email ?
|
|
new Email($crmCustomer->email) :
|
|
throw new UnexpectedValueException('Email is required ti create a customer');
|
|
|
|
$hashcode = $crmCustomer->hashcode ?
|
|
$crmCustomer->hashcode :
|
|
throw new UnexpectedValueException('Hashcode is required to create a customer');
|
|
|
|
/** @var \App\Models\Customer&\Illuminate\Contracts\Auth\Authenticatable $model */
|
|
$model = $this->usedModel::query()
|
|
->where('email', $crmCustomer->email)
|
|
->firstOrNew();
|
|
|
|
$model->email = $email;
|
|
$model->hashcode = CrmHashcode::fromString($hashcode);
|
|
$model->saveOrFail();
|
|
|
|
return $model;
|
|
}
|
|
}
|