mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 12:10:41 -05:00
User Bans
This commit is contained in:
parent
be0eecf1e9
commit
a3b3971d2f
@ -18,6 +18,7 @@ LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Due to the hub import script, only MySQL is supported at this time.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_PORT=3306
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ yarn-error.log
|
||||
/.fleet
|
||||
/.idea
|
||||
/.vscode
|
||||
.DS_Store
|
||||
|
@ -20,8 +20,8 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
use Stevebauman\Purify\Facades\Purify;
|
||||
@ -33,7 +33,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
public function handle(): void
|
||||
{
|
||||
// Stream some data locally so that we don't have to keep accessing the Hub's database. Use MySQL temporary
|
||||
// tables to store the data to save on memory; we don't want this to be a memory hog.
|
||||
// tables to store the data to save on memory; we don't want this to be a hog.
|
||||
$this->bringFileOptionsLocal();
|
||||
$this->bringFileContentLocal();
|
||||
$this->bringFileVersionLabelsLocal();
|
||||
@ -60,9 +60,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
protected function bringFileOptionsLocal(): void
|
||||
{
|
||||
if (Schema::hasTable('temp_file_option_values')) {
|
||||
DB::statement('DROP TABLE temp_file_option_values');
|
||||
}
|
||||
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
|
||||
DB::statement('CREATE TEMPORARY TABLE temp_file_option_values (
|
||||
fileID INT,
|
||||
optionID INT,
|
||||
@ -88,9 +86,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
protected function bringFileContentLocal(): void
|
||||
{
|
||||
if (Schema::hasTable('temp_file_content')) {
|
||||
DB::statement('DROP TABLE temp_file_content');
|
||||
}
|
||||
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_file_content');
|
||||
DB::statement('CREATE TEMPORARY TABLE temp_file_content (
|
||||
fileID INT,
|
||||
subject VARCHAR(255),
|
||||
@ -118,9 +114,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
protected function bringFileVersionLabelsLocal(): void
|
||||
{
|
||||
if (Schema::hasTable('temp_file_version_labels')) {
|
||||
DB::statement('DROP TABLE temp_file_version_labels');
|
||||
}
|
||||
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_file_version_labels');
|
||||
DB::statement('CREATE TEMPORARY TABLE temp_file_version_labels (
|
||||
labelID INT,
|
||||
objectID INT
|
||||
@ -145,9 +139,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
protected function bringFileVersionContentLocal(): void
|
||||
{
|
||||
if (Schema::hasTable('temp_file_version_content')) {
|
||||
DB::statement('DROP TABLE temp_file_version_content');
|
||||
}
|
||||
DB::statement('DROP TEMPORARY TABLE IF EXISTS temp_file_version_content');
|
||||
DB::statement('CREATE TEMPORARY TABLE temp_file_version_content (
|
||||
versionID INT,
|
||||
description TEXT
|
||||
@ -173,10 +165,12 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
{
|
||||
DB::connection('mysql_hub')
|
||||
->table('wcf1_user')
|
||||
->select('userID', 'username', 'email', 'password', 'registrationDate')
|
||||
->select('userID', 'username', 'email', 'password', 'registrationDate', 'banned', 'banReason', 'banExpires')
|
||||
->chunkById(250, function (Collection $users) {
|
||||
|
||||
$insertData = [];
|
||||
$bannedUsers = [];
|
||||
|
||||
foreach ($users as $user) {
|
||||
$insertData[] = [
|
||||
'hub_id' => (int) $user->userID,
|
||||
@ -186,14 +180,36 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
'created_at' => $this->cleanRegistrationDate($user->registrationDate),
|
||||
'updated_at' => now('UTC')->toDateTimeString(),
|
||||
];
|
||||
|
||||
// If the user is banned, add them to an array of banned users.
|
||||
if ($user->banned) {
|
||||
$bannedUsers[] = [
|
||||
'hub_id' => (int) $user->userID,
|
||||
'comment' => $user->banReason ?? '',
|
||||
'expired_at' => $this->cleanUnbannedAtDate($user->banExpires),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($insertData)) {
|
||||
DB::table('users')->upsert(
|
||||
$insertData,
|
||||
['hub_id'],
|
||||
['name', 'email', 'password', 'created_at', 'updated_at']
|
||||
);
|
||||
DB::table('users')->upsert($insertData, ['hub_id'], [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]);
|
||||
}
|
||||
|
||||
// Ban the users in the new local database.
|
||||
if (! empty($bannedUsers)) {
|
||||
foreach ($bannedUsers as $bannedUser) {
|
||||
$user = User::whereHubId($bannedUser['hub_id'])->first();
|
||||
$user->ban([
|
||||
'comment' => $bannedUser['comment'],
|
||||
'expired_at' => $bannedUser['expired_at'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}, 'userID');
|
||||
}
|
||||
@ -207,7 +223,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
// If it's not Bcrypt, they'll have to reset their password. Tough luck.
|
||||
$clean = str_ireplace(['invalid:', 'bcrypt:', 'bcrypt::', 'cryptmd5:', 'cryptmd5::'], '', $password);
|
||||
|
||||
// If the password hash starts with $2, it's a valid Bcrypt hash. Otherwise, it's invalid.
|
||||
// At this point, if the password hash starts with $2, it's a valid Bcrypt hash. Otherwise, it's invalid.
|
||||
return str_starts_with($clean, '$2') ? $clean : '';
|
||||
}
|
||||
|
||||
@ -226,6 +242,46 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
return $date->toDateTimeString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the banned_at date from the Hub database.
|
||||
*/
|
||||
protected function cleanUnbannedAtDate(?string $unbannedAt): ?string
|
||||
{
|
||||
// If the input is null, return null
|
||||
if ($unbannedAt === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Explicit check for the Unix epoch start date
|
||||
if (Str::contains($unbannedAt, '1970-01-01')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use validator to check for a valid date format
|
||||
$validator = Validator::make(['date' => $unbannedAt], [
|
||||
'date' => 'date_format:Y-m-d H:i:s',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
// If the date format is invalid, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate the date using Carbon
|
||||
try {
|
||||
$date = Carbon::parse($unbannedAt);
|
||||
|
||||
// Additional check to ensure the date is not a default or zero date
|
||||
if ($date->year == 0 || $date->month == 0 || $date->day == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $date->toDateTimeString();
|
||||
} catch (\Exception $e) {
|
||||
// If the date is not valid, return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the licenses from the Hub database to the local database.
|
||||
*/
|
||||
@ -512,7 +568,14 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
|
||||
*/
|
||||
public function failed(Exception $exception): void
|
||||
{
|
||||
// Disconnect from the 'mysql_hub' database connection
|
||||
// Drop the temporary tables.
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_option_values');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_content');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_labels');
|
||||
DB::unprepared('DROP TEMPORARY TABLE IF EXISTS temp_file_version_content');
|
||||
|
||||
// This *should* drop the temporary tables as well, but I like to be sure.
|
||||
DB::connection('mysql_hub')->disconnect();
|
||||
DB::disconnect();
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,11 @@ use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Jetstream\HasProfilePhoto;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Mchev\Banhammer\Traits\Bannable;
|
||||
|
||||
class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
use Bannable;
|
||||
use HasApiTokens;
|
||||
use HasFactory;
|
||||
use HasProfilePhoto;
|
||||
@ -38,14 +40,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
'profile_photo_url',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
public function mods(): HasMany
|
||||
{
|
||||
return $this->hasMany(Mod::class);
|
||||
@ -63,4 +57,12 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
return ! is_null($this->email_verified_at);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Mchev\Banhammer\Middleware\IPBanned;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
@ -12,7 +13,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
//
|
||||
$middleware->append(IPBanned::class);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
|
@ -22,6 +22,7 @@
|
||||
"league/flysystem-aws-s3-v3": "3.0",
|
||||
"league/html-to-markdown": "^5.1",
|
||||
"livewire/livewire": "^3.0",
|
||||
"mchev/banhammer": "^2.3",
|
||||
"meilisearch/meilisearch-php": "^1.8",
|
||||
"stevebauman/purify": "^6.2"
|
||||
},
|
||||
|
70
composer.lock
generated
70
composer.lock
generated
@ -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": "35e09d2f7ca6d5c8de5ed54e14d13e58",
|
||||
"content-hash": "f9f3d2b898b0a2e762d3c042c4d38f3a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -3447,6 +3447,74 @@
|
||||
],
|
||||
"time": "2024-05-21T13:39:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mchev/banhammer",
|
||||
"version": "v2.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mchev/banhammer.git",
|
||||
"reference": "9821abe9a2be60742b43b880091481c2a113d938"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mchev/banhammer/zipball/9821abe9a2be60742b43b880091481c2a113d938",
|
||||
"reference": "9821abe9a2be60742b43b880091481c2a113d938",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^7.0|^8.0|^9.0",
|
||||
"phpunit/phpunit": "^9.5|^10.5|^11.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Mchev\\Banhammer\\BanhammerServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mchev\\Banhammer\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "mchev",
|
||||
"email": "martin@pegase.io",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Banhammer for Laravel allows you to ban any Model by key and by IP.",
|
||||
"homepage": "https://github.com/mchev/banhammer",
|
||||
"keywords": [
|
||||
"IP",
|
||||
"ban",
|
||||
"bannable",
|
||||
"bans-for-laravel",
|
||||
"country",
|
||||
"laravel",
|
||||
"mchev"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mchev/banhammer/issues",
|
||||
"source": "https://github.com/mchev/banhammer/tree/v2.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/mchev",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-05-24T10:09:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "meilisearch/meilisearch-php",
|
||||
"version": "v1.8.0",
|
||||
|
95
config/ban.php
Normal file
95
config/ban.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Table Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the name of the table created during migration for the ban system.
|
||||
| This table will store information about banned users.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => 'bans',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Model Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the model which you want to use as your Ban model.
|
||||
|
|
||||
*/
|
||||
|
||||
'model' => \Mchev\Banhammer\Models\Ban::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Where to Redirect Banned Users
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define the URL to which users will be redirected when attempting to log in
|
||||
| after being banned. If not defined, the banned user will be redirected to
|
||||
| the previous page they tried to access.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_url' => null, // Examples: null (default), "/oops", "/login"
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 403 Message
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The message that will be displayed if no fallback URL is defined for banned users.
|
||||
|
|
||||
*/
|
||||
|
||||
'messages' => [
|
||||
'user' => 'Your account has been banned.',
|
||||
'ip' => 'Access from your IP address is restricted.',
|
||||
'country' => 'Access from your country is restricted.',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Block by Country
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determine whether to block users based on their country. This setting uses
|
||||
| the value of BANHAMMER_BLOCK_BY_COUNTRY from the environment. Enabling this
|
||||
| feature may result in up to 45 HTTP requests per minute with the free version
|
||||
| of https://ip-api.com/.
|
||||
|
|
||||
*/
|
||||
|
||||
'block_by_country' => env('BANHAMMER_BLOCK_BY_COUNTRY', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| List of Blocked Countries
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the countries where users will be blocked if 'block_by_country' is true.
|
||||
| Add country codes to the array to restrict access from those countries.
|
||||
|
|
||||
*/
|
||||
|
||||
'blocked_countries' => [], // Examples: ['US', 'CA', 'GB', 'FR', 'ES', 'DE']
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Duration for IP Geolocation
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This configuration option determines the duration, in minutes, for which
|
||||
| the IP geolocation data will be stored in the cache. This helps prevent
|
||||
| excessive requests and enables the middleware to efficiently determine
|
||||
| whether to block a request based on the user's country.
|
||||
|
|
||||
*/
|
||||
'cache_duration' => 120, // Duration in minutes
|
||||
|
||||
];
|
35
database/migrations/2024_06_15_000000_create_bans_table.php
Normal file
35
database/migrations/2024_06_15_000000_create_bans_table.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create(config('ban.table'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->nullableMorphs('bannable');
|
||||
$table->nullableMorphs('created_by');
|
||||
$table->text('comment')->nullable();
|
||||
$table->string('ip', 45)->nullable();
|
||||
$table->timestamp('expired_at')->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
$table->index('ip');
|
||||
$table->index('expired_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists(config('ban.table'));
|
||||
}
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasColumn(config('ban.table'), 'metas')) {
|
||||
Schema::table(config('ban.table'), function (Blueprint $table) {
|
||||
$table->json('metas')->nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasColumn(config('ban.table'), 'metas')) {
|
||||
Schema::table(config('ban.table'), function (Blueprint $table) {
|
||||
$table->dropColumn('metas');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -3,17 +3,21 @@
|
||||
use App\Http\Controllers\ModController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('home');
|
||||
})->name('home');
|
||||
Route::middleware(['auth.banned'])->group(function () {
|
||||
|
||||
Route::controller(ModController::class)->group(function () {
|
||||
Route::get('/mods', 'index')->name('mods');
|
||||
Route::get('/mod/{mod}/{slug}', 'show')->where(['id' => '[0-9]+'])->name('mod.show');
|
||||
});
|
||||
Route::get('/', function () {
|
||||
return view('home');
|
||||
})->name('home');
|
||||
|
||||
Route::controller(ModController::class)->group(function () {
|
||||
Route::get('/mods', 'index')->name('mods');
|
||||
Route::get('/mod/{mod}/{slug}', 'show')->where(['id' => '[0-9]+'])->name('mod.show');
|
||||
});
|
||||
|
||||
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
|
||||
Route::get('/dashboard', function () {
|
||||
return view('dashboard');
|
||||
})->name('dashboard');
|
||||
});
|
||||
|
||||
Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])->group(function () {
|
||||
Route::get('/dashboard', function () {
|
||||
return view('dashboard');
|
||||
})->name('dashboard');
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user