aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrankie B <git@diskfloppy.me>2023-07-16 01:49:09 +0100
committerGitHub <noreply@github.com>2023-07-16 01:49:09 +0100
commit8482a98ca6a767062917747a0b016a9ab4d35f01 (patch)
tree0ab0e8d3baf5b421ab909bc731334e2deff82fe0
parent1b267f61029e7d4b824979709434ffb437c347b8 (diff)
feat: add guestbook with rate limiting (#6)
* Re-add guestbook w/ rate limiting * Add guestbook to navbar
-rw-r--r--app/Http/Controllers/GuestbookController.php29
-rw-r--r--app/Http/Kernel.php5
-rw-r--r--app/Http/Middleware/RateLimiter.php32
-rw-r--r--composer.json3
-rw-r--r--composer.lock138
-rw-r--r--config/database.php81
-rw-r--r--public/css/master.css40
-rw-r--r--public/css/minimal.css11
-rw-r--r--resources/views/errors/ratelimit-guestbook.blade.php16
-rw-r--r--resources/views/includes/header.blade.php3
-rw-r--r--resources/views/pages/guestbook.blade.php53
-rw-r--r--routes/web.php9
12 files changed, 329 insertions, 91 deletions
diff --git a/app/Http/Controllers/GuestbookController.php b/app/Http/Controllers/GuestbookController.php
new file mode 100644
index 0000000..aff30ed
--- /dev/null
+++ b/app/Http/Controllers/GuestbookController.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\Request;
+use DB;
+
+class GuestbookController extends Controller {
+ public function guestbook() {
+ return view('pages.guestbook');
+ }
+
+ public function guestbookPost(Request $request) {
+ $this->validate($request, [
+ 'name' => 'required',
+ 'message' => 'required'
+ ]);
+
+ DB::insert('INSERT INTO guestbook_entries (name, timestamp, ip_address, agent, message) values (?, ?, ?, ?, ?)', array(
+ htmlspecialchars($request->get('name')),
+ time(),
+ $request->ip(),
+ $request->userAgent(),
+ htmlspecialchars($request->get('message'))
+ ));
+
+ return back()->with('success', 'Entry submitted successfully!');
+ }
+}
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 1fb53dc..4eab7b8 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -45,6 +45,11 @@ class Kernel extends HttpKernel
],
];
+ protected $routeMiddleware = [
+ 'rate_limit' => \App\Http\Middleware\RateLimiter::class,
+ ];
+
+
/**
* The application's middleware aliases.
*
diff --git a/app/Http/Middleware/RateLimiter.php b/app/Http/Middleware/RateLimiter.php
new file mode 100644
index 0000000..c81da43
--- /dev/null
+++ b/app/Http/Middleware/RateLimiter.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Cache;
+use Symfony\Component\HttpFoundation\Response;
+
+class RateLimiter
+{
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
+ */
+ public function handle(Request $request, Closure $next): Response
+ {
+ $ipAddress = $request->ip();
+ $cacheKey = 'rate_limit_' . $ipAddress;
+
+ if (Cache::has($cacheKey)) {
+ // If the cache key exists, the IP has submitted an entry within the last hour
+ return response()->view('errors.ratelimit-guestbook', [], 429);
+ }
+
+ // Add the IP address to the cache and set the expiration time to one hour
+ Cache::put($cacheKey, true, 60);
+
+ return $next($request);
+ }
+}
diff --git a/composer.json b/composer.json
index e4c5e7d..451f135 100644
--- a/composer.json
+++ b/composer.json
@@ -9,7 +9,8 @@
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.2",
- "laravel/tinker": "^2.8"
+ "laravel/tinker": "^2.8",
+ "spatie/laravel-honeypot": "^4.3"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
diff --git a/composer.lock b/composer.lock
index 4664da4..3018272 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": "aa322c53454393ed775cfe4807d54a50",
+ "content-hash": "505f8d503188864625fc855900ea2202",
"packages": [
{
"name": "brick/math",
@@ -2995,6 +2995,142 @@
"time": "2023-04-15T23:01:58+00:00"
},
{
+ "name": "spatie/laravel-honeypot",
+ "version": "4.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/laravel-honeypot.git",
+ "reference": "eab92dd2096f1cdb83c28ced4f4632d3cfde2872"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/laravel-honeypot/zipball/eab92dd2096f1cdb83c28ced4f4632d3cfde2872",
+ "reference": "eab92dd2096f1cdb83c28ced4f4632d3cfde2872",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^8.0|^9.0|^10.0",
+ "illuminate/encryption": "^8.0|^9.0|^10.0",
+ "illuminate/http": "^8.0|^9.0|^10.0",
+ "illuminate/support": "^8.0|^9.0|^10.0",
+ "illuminate/validation": "^8.0|^9.0|^10.0",
+ "nesbot/carbon": "^2.0",
+ "php": "^8.0",
+ "spatie/laravel-package-tools": "^1.9",
+ "symfony/http-foundation": "^5.1.2|^6.0"
+ },
+ "require-dev": {
+ "livewire/livewire": "^2.10",
+ "orchestra/testbench": "^6.23|^7.0|^8.0",
+ "pestphp/pest-plugin-livewire": "^1.0",
+ "phpunit/phpunit": "^9.4",
+ "spatie/pest-plugin-snapshots": "^1.1",
+ "spatie/phpunit-snapshot-assertions": "^4.2",
+ "spatie/test-time": "^1.2.1"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Spatie\\Honeypot\\HoneypotServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Honeypot\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Preventing spam submitted through forms",
+ "homepage": "https://github.com/spatie/laravel-honeypot",
+ "keywords": [
+ "laravel-honeypot",
+ "spatie"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/laravel-honeypot/tree/4.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ }
+ ],
+ "time": "2023-01-17T07:09:34+00:00"
+ },
+ {
+ "name": "spatie/laravel-package-tools",
+ "version": "1.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/laravel-package-tools.git",
+ "reference": "efab1844b8826443135201c4443690f032c3d533"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/efab1844b8826443135201c4443690f032c3d533",
+ "reference": "efab1844b8826443135201c4443690f032c3d533",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/contracts": "^9.28|^10.0",
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.5",
+ "orchestra/testbench": "^7.7|^8.0",
+ "pestphp/pest": "^1.22",
+ "phpunit/phpunit": "^9.5.24",
+ "spatie/pest-plugin-test-time": "^1.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\LaravelPackageTools\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Tools for creating Laravel packages",
+ "homepage": "https://github.com/spatie/laravel-package-tools",
+ "keywords": [
+ "laravel-package-tools",
+ "spatie"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/laravel-package-tools/issues",
+ "source": "https://github.com/spatie/laravel-package-tools/tree/1.15.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2023-04-27T08:09:01+00:00"
+ },
+ {
"name": "symfony/console",
"version": "v6.3.0",
"source": {
diff --git a/config/database.php b/config/database.php
index 137ad18..67ae798 100644
--- a/config/database.php
+++ b/config/database.php
@@ -34,15 +34,6 @@ return [
*/
'connections' => [
-
- 'sqlite' => [
- 'driver' => 'sqlite',
- 'url' => env('DATABASE_URL'),
- 'database' => env('DB_DATABASE', database_path('database.sqlite')),
- 'prefix' => '',
- 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
- ],
-
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
@@ -62,37 +53,6 @@ return [
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
-
- 'pgsql' => [
- 'driver' => 'pgsql',
- 'url' => env('DATABASE_URL'),
- 'host' => env('DB_HOST', '127.0.0.1'),
- 'port' => env('DB_PORT', '5432'),
- 'database' => env('DB_DATABASE', 'forge'),
- 'username' => env('DB_USERNAME', 'forge'),
- 'password' => env('DB_PASSWORD', ''),
- 'charset' => 'utf8',
- 'prefix' => '',
- 'prefix_indexes' => true,
- 'search_path' => 'public',
- 'sslmode' => 'prefer',
- ],
-
- 'sqlsrv' => [
- 'driver' => 'sqlsrv',
- 'url' => env('DATABASE_URL'),
- 'host' => env('DB_HOST', 'localhost'),
- 'port' => env('DB_PORT', '1433'),
- 'database' => env('DB_DATABASE', 'forge'),
- 'username' => env('DB_USERNAME', 'forge'),
- 'password' => env('DB_PASSWORD', ''),
- 'charset' => 'utf8',
- 'prefix' => '',
- 'prefix_indexes' => true,
- // 'encrypt' => env('DB_ENCRYPT', 'yes'),
- // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
- ],
-
],
/*
@@ -107,45 +67,4 @@ return [
*/
'migrations' => 'migrations',
-
- /*
- |--------------------------------------------------------------------------
- | Redis Databases
- |--------------------------------------------------------------------------
- |
- | Redis is an open source, fast, and advanced key-value store that also
- | provides a richer body of commands than a typical key-value system
- | such as APC or Memcached. Laravel makes it easy to dig right in.
- |
- */
-
- 'redis' => [
-
- 'client' => env('REDIS_CLIENT', 'phpredis'),
-
- 'options' => [
- 'cluster' => env('REDIS_CLUSTER', 'redis'),
- 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
- ],
-
- 'default' => [
- 'url' => env('REDIS_URL'),
- 'host' => env('REDIS_HOST', '127.0.0.1'),
- 'username' => env('REDIS_USERNAME'),
- 'password' => env('REDIS_PASSWORD'),
- 'port' => env('REDIS_PORT', '6379'),
- 'database' => env('REDIS_DB', '0'),
- ],
-
- 'cache' => [
- 'url' => env('REDIS_URL'),
- 'host' => env('REDIS_HOST', '127.0.0.1'),
- 'username' => env('REDIS_USERNAME'),
- 'password' => env('REDIS_PASSWORD'),
- 'port' => env('REDIS_PORT', '6379'),
- 'database' => env('REDIS_CACHE_DB', '1'),
- ],
-
- ],
-
];
diff --git a/public/css/master.css b/public/css/master.css
index 323c2ec..7cadfe2 100644
--- a/public/css/master.css
+++ b/public/css/master.css
@@ -115,6 +115,10 @@ div.preview pre.small, div.project pre.small {
div.preview pre, div.project pre {
background-color: #222;
color: #ccc;
+ display: inline-block;
+ text-align: left;
+ padding: 0.2em;
+ max-width: 90%
}
div.project pre {
@@ -283,13 +287,6 @@ table.skami {
color: #FFFFFF;
}
-div.preview pre, div.project pre {
- display: inline-block;
- text-align: left;
- padding: 0.2em;
- max-width: 90%
-}
-
h1 { font-size: 150% }
h2 { font-size: 130% }
h3 { font-size: 115% }
@@ -324,3 +321,32 @@ a {
color: #99f;
text-decoration: none
}
+
+table.gb_entryform tr td {
+ border: none;
+}
+
+table.gb_entryform tr td label {
+ padding-right: 5px;
+}
+
+table.gb_entryform tr td span.text-danger {
+ padding-left: 5px;
+ color: rgb(255, 114, 114);
+}
+
+table.gb_entryform tr td textarea,
+table.gb_entryform tr td input {
+ margin-bottom: 5px;
+}
+
+table.gb_entry tr td {
+ border: solid #FFFFFF;
+ width: 500px;
+ vertical-align: top;
+ padding: 5px;
+}
+
+table.gb_entry {
+ margin-bottom: 5px;
+}
diff --git a/public/css/minimal.css b/public/css/minimal.css
new file mode 100644
index 0000000..3362057
--- /dev/null
+++ b/public/css/minimal.css
@@ -0,0 +1,11 @@
+html {
+ color-scheme: dark;
+}
+
+body {
+ font-family: sans-serif;
+ margin: 0px;
+ margin-left: 10px;
+ color: #ddd;
+ background-color: #333;
+}
diff --git a/resources/views/errors/ratelimit-guestbook.blade.php b/resources/views/errors/ratelimit-guestbook.blade.php
new file mode 100644
index 0000000..fa29855
--- /dev/null
+++ b/resources/views/errors/ratelimit-guestbook.blade.php
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+ <title>Error 429: Overclocking Detected!</title>
+ <link rel="stylesheet" href="{{ URL::asset ('css/minimal.css') }}"/>
+</head>
+
+<body>
+ <h1>Error 429: Overclocking Detected!</h1>
+ <hr/>
+ <p>Whoa there! Your submissions are going at warp speed.</p>
+ <p>Remember you can only submit an entry <u>once every hour</u>!</p>
+ <br/>
+ Click <a href="/guestbook">here</a> to go back to the guestbook.
+</body>
+</html>
diff --git a/resources/views/includes/header.blade.php b/resources/views/includes/header.blade.php
index 2951003..53d44e2 100644
--- a/resources/views/includes/header.blade.php
+++ b/resources/views/includes/header.blade.php
@@ -6,6 +6,7 @@
<a href="/projects/">projects</a> |
<a href="/calculators/">calculators</a> |
<a href="/computers/">computers</a> |
- <a href="/bookmarks/">bookmarks</a>
+ <a href="/bookmarks/">bookmarks</a> |
+ <a href="/guestbook/">guestbook</a>
</div>
</nav>
diff --git a/resources/views/pages/guestbook.blade.php b/resources/views/pages/guestbook.blade.php
new file mode 100644
index 0000000..f6b758a
--- /dev/null
+++ b/resources/views/pages/guestbook.blade.php
@@ -0,0 +1,53 @@
+@extends('layouts.default')
+@section('title', 'guestbook')
+@section('content')
+ <br/>
+ <form method="POST" action="/guestbook">
+ @csrf
+ <x-honeypot />
+ <table class="gb_entryform">
+ <tr>
+ <td>
+ <label for="name">Name:</label>
+ </td>
+ <td>
+ <input name="name" type="text" id="name" placeholder="John Doe">
+ </td>
+ <td>
+ <span class="text-danger">{{ $errors->first('name') }}</span>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <label for="message">Message:</label>
+ </td>
+ <td>
+ <textarea name="message" id="message" rows="3"></textarea>
+ </td>
+ <td>
+ <span class="text-danger">{{ $errors->first('message') }}</span>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <button type="submit">Submit</button>
+ </td>
+ </tr>
+ </table>
+ </form>
+ <p>You can submit an entry <u>once every hour</u>.</p>
+ <p>Your IP address <u>will</u> be logged but <u>will not</u> be publically displayed.</p>
+ <hr/>
+ @php
+ $entries = DB::select('SELECT name, timestamp, message FROM guestbook_entries ORDER BY id DESC');
+ @endphp
+ <h1>Entries <small>({{ count($entries) }} total)</small></h1>
+ @foreach ($entries as $entry)
+ <table class="gb_entry"><tr><td>
+ Name:&nbsp;{{ $entry->name }}<br/>
+ Date:&nbsp;{{ gmdate("H:i:s - Y-m-d", $entry->timestamp) }}<br/><br/>
+ {{ htmlspecialchars($entry->message) }}
+ </td></tr></table>
+ @endforeach
+@stop
+
diff --git a/routes/web.php b/routes/web.php
index 055b4e5..6185449 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -32,3 +32,12 @@ Route::get('/calculators', function () {
Route::get('/computers', function () {
return View::make('pages.computers');
});
+
+Route::get('/guestbook', 'App\Http\Controllers\GuestbookController@guestbook')
+ ->name('guestbook');
+
+Route::post('/guestbook', 'App\Http\Controllers\GuestbookController@guestbookpost')
+ ->name('guestbookPost')
+ ->middleware('rate_limit');
+
+