-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add password broker handling to auth override (#67)
* refactor: Rename the tenant aware session handler to avoid confusion * build(composer): Bump minimum Laravel version to include my supporting PRs * feat: Add password broker handling to auth override * chore: Make sure the token repository has a fallback when not in multitenanted context
- Loading branch information
Showing
6 changed files
with
301 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 160 additions & 0 deletions
160
src/Overrides/Auth/TenantAwareDatabaseTokenRepository.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Sprout\Overrides\Auth; | ||
|
||
use Illuminate\Auth\Passwords\DatabaseTokenRepository; | ||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; | ||
use Illuminate\Database\Query\Builder; | ||
use Illuminate\Support\Carbon; | ||
use SensitiveParameter; | ||
use Sprout\Exceptions\TenancyMissing; | ||
use Sprout\Exceptions\TenantMissing; | ||
use function Sprout\sprout; | ||
|
||
/** | ||
* Tenant Aware Database Token Repository | ||
* | ||
* This is a database token repository that wraps the default | ||
* {@see \Illuminate\Auth\Passwords\DatabaseTokenRepository} to query based on | ||
* the current tenant. | ||
* | ||
* @package Overrides | ||
*/ | ||
class TenantAwareDatabaseTokenRepository extends DatabaseTokenRepository | ||
{ | ||
/** | ||
* Build the record payload for the table. | ||
* | ||
* @param string $email | ||
* @param string $token | ||
* | ||
* @return array<string, mixed> | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
protected function getPayload($email, #[SensitiveParameter] $token): array | ||
{ | ||
if (! sprout()->withinContext()) { | ||
return parent::getPayload($email, $token); | ||
} | ||
|
||
$tenancy = sprout()->getCurrentTenancy(); | ||
|
||
if ($tenancy === null) { | ||
throw TenancyMissing::make(); | ||
} | ||
|
||
if (! $tenancy->check()) { | ||
throw TenantMissing::make($tenancy->getName()); | ||
} | ||
|
||
return [ | ||
'tenancy' => $tenancy->getName(), | ||
'tenant_id' => $tenancy->key(), | ||
'email' => $email, | ||
'token' => $this->hasher->make($token), | ||
'created_at' => new Carbon(), | ||
]; | ||
} | ||
|
||
/** | ||
* Get the tenanted query | ||
* | ||
* @param string $email | ||
* | ||
* @return \Illuminate\Database\Query\Builder | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
protected function getTenantedQuery(string $email): Builder | ||
{ | ||
if (! sprout()->withinContext()) { | ||
return $this->getTable()->where('email', $email); | ||
} | ||
|
||
$tenancy = sprout()->getCurrentTenancy(); | ||
|
||
if ($tenancy === null) { | ||
throw TenancyMissing::make(); | ||
} | ||
|
||
if (! $tenancy->check()) { | ||
throw TenantMissing::make($tenancy->getName()); | ||
} | ||
|
||
return $this->getTable() | ||
->where('tenancy', $tenancy->getName()) | ||
->where('tenant_id', $tenancy->key()) | ||
->where('email', $email); | ||
} | ||
|
||
/** | ||
* Get the record for a user | ||
* | ||
* @param \Illuminate\Contracts\Auth\CanResetPassword $user | ||
* | ||
* @return object|null | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
protected function getExistingTenantedRecord(CanResetPasswordContract $user): ?object | ||
{ | ||
return $this->getTenantedQuery($user->getEmailForPasswordReset())->first(); | ||
} | ||
|
||
/** | ||
* Delete all existing reset tokens from the database. | ||
* | ||
* @param \Illuminate\Contracts\Auth\CanResetPassword $user | ||
* | ||
* @return int | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
protected function deleteExisting(CanResetPasswordContract $user): int | ||
{ | ||
return $this->getTenantedQuery($user->getEmailForPasswordReset())->delete(); | ||
} | ||
|
||
/** | ||
* Determine if a token record exists and is valid. | ||
* | ||
* @param \Illuminate\Contracts\Auth\CanResetPassword $user | ||
* @param string $token | ||
* | ||
* @return bool | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
public function exists(CanResetPasswordContract $user, #[SensitiveParameter] $token): bool | ||
{ | ||
$record = (array)$this->getExistingTenantedRecord($user); | ||
|
||
return $record && | ||
! $this->tokenExpired($record['created_at']) && | ||
$this->hasher->check($token, $record['token']); | ||
} | ||
|
||
/** | ||
* Determine if the given user recently created a password reset token. | ||
* | ||
* @param \Illuminate\Contracts\Auth\CanResetPassword $user | ||
* | ||
* @return bool | ||
* | ||
* @throws \Sprout\Exceptions\TenancyMissing | ||
* @throws \Sprout\Exceptions\TenantMissing | ||
*/ | ||
public function recentlyCreatedToken(CanResetPasswordContract $user): bool | ||
{ | ||
$record = (array)$this->getExistingTenantedRecord($user); | ||
|
||
return $record && $this->tokenRecentlyCreated($record['created_at']); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Sprout\Overrides\Auth; | ||
|
||
use Illuminate\Auth\Passwords\CacheTokenRepository; | ||
use Illuminate\Auth\Passwords\PasswordBrokerManager; | ||
use Illuminate\Auth\Passwords\TokenRepositoryInterface; | ||
|
||
/** | ||
* Tenant Aware Password Broker Manager | ||
* | ||
* This is an override of the default password broker manager to make it | ||
* create a tenant-aware {@see \Illuminate\Auth\Passwords\TokenRepositoryInterface}. | ||
* | ||
* This is an unfortunate necessity as there's no other way to control the | ||
* token repository that is created. | ||
* | ||
* @package Overrides | ||
*/ | ||
class TenantAwarePasswordBrokerManager extends PasswordBrokerManager | ||
{ | ||
/** | ||
* Create a token repository instance based on the current configuration. | ||
* | ||
* @param array<string, mixed> $config | ||
* | ||
* @return \Illuminate\Auth\Passwords\TokenRepositoryInterface | ||
*/ | ||
protected function createTokenRepository(array $config): TokenRepositoryInterface | ||
{ | ||
// @phpstan-ignore-next-line | ||
$key = $this->app['config']['app.key']; | ||
|
||
if (str_starts_with($key, 'base64:')) { | ||
$key = base64_decode(substr($key, 7)); | ||
} | ||
|
||
if (isset($config['driver']) && $config['driver'] === 'cache') { | ||
return new CacheTokenRepository( | ||
$this->app['cache']->store($config['store'] ?? null), // @phpstan-ignore-line | ||
$this->app['hash'], // @phpstan-ignore-line | ||
$key, | ||
($config['expire'] ?? 60) * 60, | ||
$config['throttle'] ?? 0, // @phpstan-ignore-line | ||
$config['prefix'] ?? '', // @phpstan-ignore-line | ||
); | ||
} | ||
|
||
$connection = $config['connection'] ?? null; | ||
|
||
return new TenantAwareDatabaseTokenRepository( | ||
$this->app['db']->connection($connection), // @phpstan-ignore-line | ||
$this->app['hash'], // @phpstan-ignore-line | ||
$config['table'], // @phpstan-ignore-line | ||
$key, | ||
$config['expire'],// @phpstan-ignore-line | ||
$config['throttle'] ?? 0// @phpstan-ignore-line | ||
); | ||
} | ||
|
||
/** | ||
* Flush the resolved brokers | ||
* | ||
* @return $this | ||
*/ | ||
public function flush(): self | ||
{ | ||
$this->brokers = []; | ||
|
||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters