From f8d7c5209eaf350d6cdb6ce82579a9b377689f74 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 15 Aug 2023 14:27:45 +0200 Subject: [PATCH] rate limit things --- app/Http/Livewire/ForcePasswordReset.php | 3 + app/Http/Livewire/Waitlist.php | 6 ++ app/Providers/FortifyServiceProvider.php | 8 +++ bootstrap/helpers/shared.php | 5 +- composer.json | 1 + composer.lock | 55 ++++++++++++++++++- config/fortify.php | 1 + resources/views/auth/login.blade.php | 1 - .../livewire/force-password-reset.blade.php | 6 +- resources/views/livewire/waitlist.blade.php | 9 ++- routes/web.php | 4 +- 11 files changed, 90 insertions(+), 9 deletions(-) diff --git a/app/Http/Livewire/ForcePasswordReset.php b/app/Http/Livewire/ForcePasswordReset.php index effceaa0f..96a273e80 100644 --- a/app/Http/Livewire/ForcePasswordReset.php +++ b/app/Http/Livewire/ForcePasswordReset.php @@ -3,10 +3,12 @@ namespace App\Http\Livewire; use Illuminate\Support\Facades\Hash; +use DanHarrin\LivewireRateLimiting\WithRateLimiting; use Livewire\Component; class ForcePasswordReset extends Component { + use WithRateLimiting; public string $email; public string $password; public string $password_confirmation; @@ -21,6 +23,7 @@ public function mount() { } public function submit() { try { + $this->rateLimit(10); $this->validate(); auth()->user()->forceFill([ 'password' => Hash::make($this->password), diff --git a/app/Http/Livewire/Waitlist.php b/app/Http/Livewire/Waitlist.php index 7f26351e1..d3d984ff1 100644 --- a/app/Http/Livewire/Waitlist.php +++ b/app/Http/Livewire/Waitlist.php @@ -3,6 +3,7 @@ namespace App\Http\Livewire; use App\Jobs\SendConfirmationForWaitlistJob; +use App\Models\User; use App\Models\Waitlist as ModelsWaitlist; use Livewire\Component; @@ -24,6 +25,11 @@ public function submit() { $this->validate(); try { + $already_registered = User::whereEmail($this->email)->first(); + if ($already_registered) { + $this->emit('success', 'You are already registered (Thank you 💜).'); + return; + } $found = ModelsWaitlist::where('email', $this->email)->first(); ray($found); if ($found) { diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index 947055d9f..da9282e13 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -98,6 +98,14 @@ public function boot(): void return view('auth.two-factor-challenge'); }); + RateLimiter::for('force-password-reset', function (Request $request) { + return Limit::perMinute(15)->by($request->user()->id); + }); + + RateLimiter::for('forgot-password', function (Request $request) { + return Limit::perMinute(5)->by($request->ip()); + }); + RateLimiter::for('login', function (Request $request) { $email = (string)$request->email; diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index b2d279bac..ff0e99679 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -8,6 +8,7 @@ use Nubs\RandomNameGenerator\All; use Poliander\Cron\CronExpression; use Visus\Cuid2\Cuid2; +use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException; function application_configuration_dir(): string { @@ -46,7 +47,9 @@ function general_error_handler(Throwable|null $err = null, $that = null, $isJson } else { throw new Exception($customErrorMessage ?? $err->errorInfo[2]); } - } else { + } elseif($err instanceof TooManyRequestsException){ + throw new Exception($customErrorMessage ?? "Too many requests. Please try again in {$err->secondsUntilAvailable} seconds."); + }else { throw new Exception($customErrorMessage ?? $err->getMessage()); } } catch (Throwable $error) { diff --git a/composer.json b/composer.json index 09539e78a..7fcdd9e26 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "license": "MIT", "require": { "php": "^8.2", + "danharrin/livewire-rate-limiting": "^1.1", "doctrine/dbal": "^3.6", "guzzlehttp/guzzle": "^7.5.0", "laravel/fortify": "^v1.16.0", diff --git a/composer.lock b/composer.lock index a0558a682..45671727a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ba59e457afa0cfb65b82118f7287147b", + "content-hash": "0c023bed552776ee5e4eeda1ff0a5e19", "packages": [ { "name": "aws/aws-crt-php", @@ -330,6 +330,59 @@ ], "time": "2022-02-21T13:15:14+00:00" }, + { + "name": "danharrin/livewire-rate-limiting", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/danharrin/livewire-rate-limiting.git", + "reference": "a55996683cabf2e93893280d602191243b3b80b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/danharrin/livewire-rate-limiting/zipball/a55996683cabf2e93893280d602191243b3b80b8", + "reference": "a55996683cabf2e93893280d602191243b3b80b8", + "shasum": "" + }, + "require": { + "illuminate/support": "^9.0|^10.0", + "php": "^8.0" + }, + "require-dev": { + "livewire/livewire": "^2.3", + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.0|^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "DanHarrin\\LivewireRateLimiting\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Harrin", + "email": "dan@danharrin.com" + } + ], + "description": "Apply rate limiters to Laravel Livewire actions.", + "homepage": "https://github.com/danharrin/livewire-rate-limiting", + "support": { + "issues": "https://github.com/danharrin/livewire-rate-limiting/issues", + "source": "https://github.com/danharrin/livewire-rate-limiting" + }, + "funding": [ + { + "url": "https://github.com/danharrin", + "type": "github" + } + ], + "time": "2023-03-12T12:17:29+00:00" + }, { "name": "dasprid/enum", "version": "1.0.4", diff --git a/config/fortify.php b/config/fortify.php index 510a53912..6b78c1c65 100644 --- a/config/fortify.php +++ b/config/fortify.php @@ -105,6 +105,7 @@ 'limiters' => [ 'login' => 'login', 'two-factor' => 'two-factor', + 'forgot-password' => 'forgot-password', ], /* diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 69204571c..838bc5a11 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -3,7 +3,6 @@
Coolify
-

{{ __('auth.login') }}

diff --git a/resources/views/livewire/force-password-reset.blade.php b/resources/views/livewire/force-password-reset.blade.php index c2ecbf098..28a6f797a 100644 --- a/resources/views/livewire/force-password-reset.blade.php +++ b/resources/views/livewire/force-password-reset.blade.php @@ -9,9 +9,9 @@

Set your initial password

- - - + + + Reset Password
diff --git a/resources/views/livewire/waitlist.blade.php b/resources/views/livewire/waitlist.blade.php index 64eff5def..006d90027 100644 --- a/resources/views/livewire/waitlist.blade.php +++ b/resources/views/livewire/waitlist.blade.php @@ -1,7 +1,12 @@
+
-

Start self-hosting in the +

Self-hosting in the cloud @@ -18,6 +23,6 @@ Join Waitlist - Waiting: {{$waiting_in_line}} + Waiting in the line: {{$waiting_in_line}}

diff --git a/routes/web.php b/routes/web.php index dfe98f0d6..81dd776ec 100644 --- a/routes/web.php +++ b/routes/web.php @@ -93,7 +93,9 @@ Route::middleware(['auth'])->group(function () { Route::get('/', [Controller::class, 'dashboard'])->name('dashboard'); - Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset'); + Route::middleware(['throttle:force-password-reset'])->group(function() { + Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset'); + }); Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription'); Route::get('/settings', [Controller::class, 'settings'])->name('settings.configuration'); Route::get('/settings/license', [Controller::class, 'license'])->name('settings.license');