diff --git a/app/Http/Controllers/User/InviteController.php b/app/Http/Controllers/User/InviteController.php index 8427a4750..41018f8e3 100644 --- a/app/Http/Controllers/User/InviteController.php +++ b/app/Http/Controllers/User/InviteController.php @@ -67,9 +67,11 @@ class InviteController extends Controller ->withErrors(trans('user.invites-disabled-group')); } - if ($user->two_factor_confirmed_at === null) { + $minHours = config('other.hours-until-invite-after-2fa'); + + if ($user->two_factor_confirmed_at === null || $user->two_factor_confirmed_at->addHours($minHours)->isFuture()) { return to_route('home.index') - ->withErrors('Two-factor authentication must be enabled to send invites'); + ->withErrors("Two-factor authentication must be enabled for {$minHours} hours to send invites"); } return view('user.invite.create', ['user' => $user]); @@ -94,9 +96,11 @@ class InviteController extends Controller ->withErrors(trans('user.not-enough-invites')); } - if ($user->two_factor_confirmed_at === null) { + $minHours = config('other.hours-until-invite-after-2fa'); + + if ($user->two_factor_confirmed_at === null || $user->two_factor_confirmed_at->addHours($minHours)->isFuture()) { return to_route('home.index') - ->withErrors('Two-factor authentication must be enabled to send invites'); + ->withErrors("Two-factor authentication must be enabled for {$minHours} hours to send invites"); } $request->validate([ diff --git a/app/Models/User.php b/app/Models/User.php index 045c88bf8..eb9fba3d4 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -36,7 +36,7 @@ use Laravel\Fortify\TwoFactorAuthenticatable; * @property string $password * @property string|null $two_factor_secret * @property string|null $two_factor_recovery_codes - * @property string|null $two_factor_confirmed_at + * @property \Illuminate\Support\Carbon|null $two_factor_confirmed_at * @property string $passkey * @property int $group_id * @property int $uploaded @@ -129,18 +129,19 @@ class User extends Authenticatable implements MustVerifyEmail protected function casts(): array { return [ - 'seedbonus' => 'decimal:2', - 'last_login' => 'datetime', - 'last_action' => 'datetime', - 'disabled_at' => 'datetime', - 'can_comment' => 'bool', - 'can_download' => 'bool', - 'can_request' => 'bool', - 'can_invite' => 'bool', - 'can_upload' => 'bool', - 'can_chat' => 'bool', - 'is_donor' => 'bool', - 'is_lifetime' => 'bool', + 'seedbonus' => 'decimal:2', + 'last_login' => 'datetime', + 'last_action' => 'datetime', + 'disabled_at' => 'datetime', + 'two_factor_confirmed_at' => 'datetime', + 'can_comment' => 'bool', + 'can_download' => 'bool', + 'can_request' => 'bool', + 'can_invite' => 'bool', + 'can_upload' => 'bool', + 'can_chat' => 'bool', + 'is_donor' => 'bool', + 'is_lifetime' => 'bool', ]; } diff --git a/config/other.php b/config/other.php index 47abab994..ada59c4d3 100644 --- a/config/other.php +++ b/config/other.php @@ -132,6 +132,8 @@ return [ ], 'max_unused_user_invites' => 1, + 'hours-until-invite-after-2fa' => 24, + /* |-------------------------------------------------------------------------- | Default Users Stats diff --git a/tests/Feature/Http/Controllers/User/InviteControllerTest.php b/tests/Feature/Http/Controllers/User/InviteControllerTest.php index 7de3b9fe7..1e5cc2e18 100644 --- a/tests/Feature/Http/Controllers/User/InviteControllerTest.php +++ b/tests/Feature/Http/Controllers/User/InviteControllerTest.php @@ -161,6 +161,7 @@ test('store returns an ok response', function (): void { config(['other.invites_restriced' => true]); config(['other.invite_groups' => [$group->name]]); config(['other.invite_groups' => [$group->name]]); + config(['other.hours-until-invite-after-2fa' => 0]); config(['email-blacklist.enabled' => false]); Mail::fake(); @@ -193,8 +194,9 @@ test('store with internal note as staff user', function (): void { $inviteEmail = 'test@unit3d.dev'; config(['other' => [ - 'invites_restriced' => true, - 'invite_groups' => [$group->name], + 'invites_restriced' => true, + 'invite_groups' => [$group->name], + 'hours-until-invite-after-2fa' => 0, ], 'email-blacklist.enabled' => false, ]);