Skip to content

Commit 174456a

Browse files
committed
Merge branch 'main' into route_tidy
2 parents edaf511 + c1f747b commit 174456a

12 files changed

+167
-6
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
vendor/
2+
composer.lock

.lando.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: laravelfauth
2+
recipe: lamp
3+
config:
4+
via: "cli"
5+
php: "8.2"
6+
7+
services:
8+
9+
php83:
10+
type: php:8.3
11+
via: "cli"
12+
13+
php81:
14+
type: php:8.1
15+
via: "cli"
16+
17+
18+
tooling:
19+
cleanup:
20+
cmd: rm -rf vendor composer.lock
21+
22+
setup81:
23+
service: php81
24+
cmd: rm -rf vendor composer.lock && composer install
25+
26+
setup83:
27+
service: php83
28+
cmd: rm -rf vendor composer.lock && composer install
29+
30+
setup82:
31+
service: appserver
32+
cmd: rm -rf vendor composer.lock && composer install
33+
34+
35+
36+
37+
lara11:
38+
service: php83
39+
cmd:
40+
- rm -rf vendor composer.lock
41+
- composer require laravel/laravel:^11
42+
- composer install
43+
- composer phpunit
44+
- composer phpstan
45+
# this is for removing a specific version
46+
- composer remove laravel/laravel
47+
48+
lara10:
49+
service: php82
50+
cmd:
51+
- rm -rf vendor composer.lock
52+
- composer require laravel/laravel:^10
53+
- composer install
54+
- composer phpunit
55+
- composer phpstan
56+
# this is for removing a specific version
57+
- composer remove laravel/laravel
58+
59+
lara9:
60+
service: php81
61+
cmd:
62+
- rm -rf vendor composer.lock
63+
- composer require laravel/laravel:^9
64+
- composer install
65+
- composer phpunit
66+
- composer phpstan
67+
# this is for removing a specific version
68+
- composer remove laravel/laravel

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,25 @@ Enable this & configure this as Follows:
139139

140140
this will force a user to update their password, note -- all existing users will initially be foreced to, this can be ignored by running the following command:
141141

142+
From V5.0.0 - there is a new validation rule that can be added to validate that a password has not been used before.
143+
`Phpsa\FilamentAuthentication\Rules\PreventPasswordReuseRule` - this will use the value from config `filament-authentication.password_renew.prevent_password_reuse` 0 to disable, any number of previous to block out fro re-use.
144+
145+
-- If using socialite / Filament-socialite etc, you will need to override the `public function needsRenewal(): bool` method in the trait,
146+
EG:
147+
```php
148+
use CanRenewPassword {
149+
CanRenewPassword::needsRenewal as traitNeedsRenewal;
150+
}
151+
152+
public function needsRenewal(): bool
153+
{
154+
if ($this->password === null && SocialiteUser::where('user_id', $this->id)->exists()) {
155+
return false;
156+
}
157+
return $this->traitNeedsRenewal();
158+
}
159+
```
160+
142161
## Authentication Log
143162

144163
Introduced in V4.2.0 - this allows you to log each user login attempt.

UPGRADE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# Upgrading V4 - V5
2+
3+
Breaking Changes:
4+
- Impersonation - links now generated based on panels, so route name and path will be slightly different
5+
- Password Renew - added phash to the table to store previous passwords, this will be used to validate if a password has been used before.
6+
7+
8+
19
Upgrading V2 - V3
210

311
- Widget / panels no longer auto-published

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@
2424
],
2525
"minimum-stability": "dev",
2626
"require": {
27-
"php": "^8.0",
27+
"php": "^8.1",
2828
"filament/filament": "^3.0",
29+
"illuminate/support": "^9.0|^10|^11",
2930
"lab404/laravel-impersonate": "^1.7",
3031
"spatie/laravel-package-tools": "^1.13",
3132
"spatie/laravel-permission": "^5.5|^6.0"
3233
},
3334
"require-dev": {
34-
"laravel/pint": "^1.2"
35+
"laravel/pint": "^1.2",
36+
"orchestra/testbench": "^7.0|^8.0|^9.0"
3537
},
3638
"config": {
3739
"sort-packages": true

config/filament-authentication.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
'prune' => 365,
6464
//renew password days period, 0 to disable
6565
'renew_password_days_period' => 90,
66+
//prevent password reuse for x times, 0 to disable
67+
'prevent_password_reuse' => 0,
6668
],
6769

6870

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::table(config('filament-authentication.password_renew.table_name'), function (Blueprint $table): void {
12+
$table->string('phash', 255)->nullable()->after('renewable_id');
13+
});
14+
}
15+
16+
};

src/FilamentAuthenticationProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public function configurePackage(Package $package): void
2424
->hasRoute('web')
2525
->hasMigration('create_filament_authentication_tables')
2626
->hasMigration('create_filament_password_renew_table')
27+
->hasMigration('tracks_filament_password_hashes')
2728
->hasConfigFile('filament-authentication')
2829
->hasCommand(InstallCommand::class)
2930
->hasCommand(UpdateUserPasswordToUpdatedCommand::class)

src/Models/PasswordRenewLog.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class PasswordRenewLog extends Model
2323
{
2424
use Prunable;
2525

26+
//no guarding
27+
protected $guarded = [];
28+
2629
public function getConnectionName()
2730
{
2831
return config('filament-authentication.password_renew.db_connection', null) ?? $this->connection;

src/Pages/Auth/RenewPassword.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Filament\Pages\Concerns\InteractsWithFormActions;
1515
use Yebor974\Filament\RenewPassword\RenewPasswordPlugin;
1616
use Illuminate\Validation\Rules\Password as PasswordRule;
17+
use Phpsa\FilamentAuthentication\Rules\PreventPasswordReuseRule;
1718
use Phpsa\FilamentAuthentication\Traits\CanRenewPassword;
1819

1920
class RenewPassword extends SimplePage
@@ -34,8 +35,7 @@ public function mount(): void
3435
{
3536
$user = Filament::auth()->user();
3637

37-
if (
38-
! in_array(CanRenewPassword::class, class_uses_recursive($user))
38+
if (! in_array(CanRenewPassword::class, class_uses_recursive($user))
3939
|| ! $user->needsRenewal()
4040
) {
4141
redirect()->intended(Filament::getUrl());
@@ -93,7 +93,7 @@ protected function getForms(): array
9393
->password()
9494
->revealable(filament()->arePasswordsRevealable())
9595
->required()
96-
->rules(['different:data.currentPassword', PasswordRule::default()]),
96+
->rules(['different:data.currentPassword', PasswordRule::default(), new PreventPasswordReuseRule()]),
9797
TextInput::make('PasswordConfirmation')
9898
->label(__('filament-authentication::filament-authentication.field.user.confirm_password'))
9999
->password()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
namespace Phpsa\FilamentAuthentication\Rules;
3+
4+
use Closure;
5+
use Illuminate\Contracts\Auth\Authenticatable;
6+
use Illuminate\Contracts\Validation\ValidationRule;
7+
use Illuminate\Support\Facades\Hash;
8+
use Phpsa\FilamentAuthentication\Models\PasswordRenewLog;
9+
10+
class PreventPasswordReuseRule implements ValidationRule
11+
{
12+
13+
public function __construct(?Authenticatable $user = null)
14+
{
15+
$this->user = $user ?? auth()->user();
16+
}
17+
18+
public function validate(string $attribute, mixed $value, Closure $fail): void
19+
{
20+
21+
//if config is disabled we don't wanna
22+
if ((int) config('filament-authentication.password_renew.prevent_password_reuse') <= 0) {
23+
return;
24+
}
25+
26+
$previous = PasswordRenewLog::where('renewable_id', $this->user->getAuthIdentifier())
27+
->where('renewable_type', get_class($this->user))
28+
->latest()
29+
->limit(config('filament-authentication.password_renew.prevent_password_reuse'))
30+
->pluck('phash')
31+
->filter(fn($hash) => Hash::check($value, $hash))
32+
->isNotEmpty();
33+
34+
if ($previous) {
35+
$fail('You cannot use the same password as a previous one.');
36+
}
37+
}
38+
}

src/Traits/CanRenewPassword.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public function initializeCanRenewPassword()
1111
$field = method_exists($user, 'getAuthPasswordName') ? $user->getAuthPasswordName() : 'password';
1212

1313
if ($user->isDirty($field)) {
14-
$user->renewables()->where('created_at', now())->firstOrCreate([]);
14+
$user->renewables()->where('created_at', now())->firstOrCreate([
15+
'phash' => $user->getOriginal($field),
16+
]);
1517
}
1618
});
1719
}
@@ -28,6 +30,7 @@ public function latestRenewable()
2830

2931
public function needsRenewal(): bool
3032
{
33+
3134
$period = config('filament-authentication.password_renew.renew_password_days_period');
3235

3336
if (! is_numeric($period) || $period <= 0) {

0 commit comments

Comments
 (0)