mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-05-03 08:50:22 -05:00
update: two_factor_auth
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author Roardom <roardom@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TwoFactorAuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* Edit user two-factor auth settings.
|
||||
*/
|
||||
public function edit(Request $request, User $user): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
abort_unless($request->user()->is($user), 403);
|
||||
|
||||
return view('user.two_factor_auth.edit', ['user' => $user]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author HDVinnie <hdinnovations@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Laravel\Fortify\Actions\ConfirmTwoFactorAuthentication;
|
||||
use Laravel\Fortify\Actions\DisableTwoFactorAuthentication;
|
||||
use Laravel\Fortify\Actions\EnableTwoFactorAuthentication;
|
||||
use Laravel\Fortify\Actions\GenerateNewRecoveryCodes;
|
||||
use Laravel\Fortify\Features;
|
||||
use Livewire\Component;
|
||||
|
||||
class TwoFactorAuthForm extends Component
|
||||
{
|
||||
/**
|
||||
* Indicates if two-factor authentication QR code is being displayed.
|
||||
*/
|
||||
public bool $showingQrCode = false;
|
||||
|
||||
/**
|
||||
* Indicates if the two-factor authentication confirmation input and button are being displayed.
|
||||
*/
|
||||
public bool $showingConfirmation = false;
|
||||
|
||||
/**
|
||||
* Indicates if two-factor authentication recovery codes are being displayed.
|
||||
*/
|
||||
public bool $showingRecoveryCodes = false;
|
||||
|
||||
/**
|
||||
* The OTP code for confirming two-factor authentication.
|
||||
*/
|
||||
public ?string $code;
|
||||
|
||||
/**
|
||||
* Mount the component.
|
||||
*/
|
||||
final public function mount(): void
|
||||
{
|
||||
if (Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm') &&
|
||||
is_null(\auth()->user()->two_factor_confirmed_at)) {
|
||||
app(DisableTwoFactorAuthentication::class)(auth()->user());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable two-factor authentication for the user.
|
||||
*/
|
||||
final public function enableTwoFactorAuthentication(EnableTwoFactorAuthentication $enable): void
|
||||
{
|
||||
$enable(auth()->user());
|
||||
|
||||
$this->showingQrCode = true;
|
||||
|
||||
if (Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm')) {
|
||||
$this->showingConfirmation = true;
|
||||
} else {
|
||||
$this->showingRecoveryCodes = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm two-factor authentication for the user.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
final public function confirmTwoFactorAuthentication(ConfirmTwoFactorAuthentication $confirm): void
|
||||
{
|
||||
$confirm(auth()->user(), $this->code);
|
||||
|
||||
$this->showingQrCode = false;
|
||||
$this->showingConfirmation = false;
|
||||
$this->showingRecoveryCodes = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the user's recovery codes.
|
||||
*/
|
||||
final public function showRecoveryCodes(): void
|
||||
{
|
||||
$this->showingRecoveryCodes = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new recovery codes for the user.
|
||||
*/
|
||||
final public function regenerateRecoveryCodes(GenerateNewRecoveryCodes $generate): void
|
||||
{
|
||||
$generate(auth()->user());
|
||||
|
||||
$this->showingRecoveryCodes = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable two-factor authentication for the user.
|
||||
*/
|
||||
final public function disableTwoFactorAuthentication(DisableTwoFactorAuthentication $disable): void
|
||||
{
|
||||
$disable(auth()->user());
|
||||
|
||||
$this->showingQrCode = false;
|
||||
$this->showingConfirmation = false;
|
||||
$this->showingRecoveryCodes = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user of the application.
|
||||
*/
|
||||
final public function getUserProperty(): ?\Illuminate\Contracts\Auth\Authenticatable
|
||||
{
|
||||
return auth()->user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if two-factor authentication is enabled.
|
||||
*/
|
||||
final public function getEnabledProperty(): bool
|
||||
{
|
||||
return ! empty($this->user->two_factor_secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the component.
|
||||
*/
|
||||
final public function render(): \Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\Foundation\Application
|
||||
{
|
||||
return view('livewire.two-factor-auth-form');
|
||||
}
|
||||
}
|
||||
Generated
+48
-48
@@ -4459,16 +4459,16 @@
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "4.7.4",
|
||||
"version": "4.7.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ramsey/uuid.git",
|
||||
"reference": "60a4c63ab724854332900504274f6150ff26d286"
|
||||
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286",
|
||||
"reference": "60a4c63ab724854332900504274f6150ff26d286",
|
||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
|
||||
"reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4535,7 +4535,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ramsey/uuid/issues",
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.4"
|
||||
"source": "https://github.com/ramsey/uuid/tree/4.7.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4547,7 +4547,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-15T23:01:58+00:00"
|
||||
"time": "2023-11-08T05:53:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/db-dumper",
|
||||
@@ -5227,16 +5227,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v6.3.4",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6"
|
||||
"reference": "0d14a9f6d04d4ac38a8cea1171f4554e325dae92"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6",
|
||||
"reference": "eca495f2ee845130855ddf1cf18460c38966c8b6",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/0d14a9f6d04d4ac38a8cea1171f4554e325dae92",
|
||||
"reference": "0d14a9f6d04d4ac38a8cea1171f4554e325dae92",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5297,7 +5297,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v6.3.4"
|
||||
"source": "https://github.com/symfony/console/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5313,7 +5313,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-16T10:10:12+00:00"
|
||||
"time": "2023-10-31T08:09:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@@ -5810,16 +5810,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v6.3.7",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "59d1837d5d992d16c2628cd0d6b76acf8d69b33e"
|
||||
"reference": "ce332676de1912c4389222987193c3ef38033df6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/59d1837d5d992d16c2628cd0d6b76acf8d69b33e",
|
||||
"reference": "59d1837d5d992d16c2628cd0d6b76acf8d69b33e",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/ce332676de1912c4389222987193c3ef38033df6",
|
||||
"reference": "ce332676de1912c4389222987193c3ef38033df6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5867,7 +5867,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v6.3.7"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5883,20 +5883,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-28T23:55:27+00:00"
|
||||
"time": "2023-11-07T10:17:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v6.3.7",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "6d4098095f93279d9536a0e9124439560cc764d0"
|
||||
"reference": "929202375ccf44a309c34aeca8305408442ebcc1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/6d4098095f93279d9536a0e9124439560cc764d0",
|
||||
"reference": "6d4098095f93279d9536a0e9124439560cc764d0",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/929202375ccf44a309c34aeca8305408442ebcc1",
|
||||
"reference": "929202375ccf44a309c34aeca8305408442ebcc1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5980,7 +5980,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v6.3.7"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -5996,7 +5996,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-29T14:31:45+00:00"
|
||||
"time": "2023-11-10T13:47:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
@@ -7211,16 +7211,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v6.3.5",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "13d76d0fb049051ed12a04bef4f9de8715bea339"
|
||||
"reference": "13880a87790c76ef994c91e87efb96134522577a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339",
|
||||
"reference": "13d76d0fb049051ed12a04bef4f9de8715bea339",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/13880a87790c76ef994c91e87efb96134522577a",
|
||||
"reference": "13880a87790c76ef994c91e87efb96134522577a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7277,7 +7277,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v6.3.5"
|
||||
"source": "https://github.com/symfony/string/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7293,7 +7293,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-18T10:38:32+00:00"
|
||||
"time": "2023-11-09T08:28:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
@@ -7470,16 +7470,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/uid",
|
||||
"version": "v6.3.0",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/uid.git",
|
||||
"reference": "01b0f20b1351d997711c56f1638f7a8c3061e384"
|
||||
"reference": "819fa5ac210fb7ddda4752b91a82f50be7493dd9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384",
|
||||
"reference": "01b0f20b1351d997711c56f1638f7a8c3061e384",
|
||||
"url": "https://api.github.com/repos/symfony/uid/zipball/819fa5ac210fb7ddda4752b91a82f50be7493dd9",
|
||||
"reference": "819fa5ac210fb7ddda4752b91a82f50be7493dd9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7524,7 +7524,7 @@
|
||||
"uuid"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/uid/tree/v6.3.0"
|
||||
"source": "https://github.com/symfony/uid/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7540,20 +7540,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-08T07:25:02+00:00"
|
||||
"time": "2023-10-31T08:07:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v6.3.6",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "999ede244507c32b8e43aebaa10e9fce20de7c97"
|
||||
"reference": "81acabba9046550e89634876ca64bfcd3c06aa0a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97",
|
||||
"reference": "999ede244507c32b8e43aebaa10e9fce20de7c97",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/81acabba9046550e89634876ca64bfcd3c06aa0a",
|
||||
"reference": "81acabba9046550e89634876ca64bfcd3c06aa0a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7608,7 +7608,7 @@
|
||||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v6.3.6"
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7624,7 +7624,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-12T18:45:56+00:00"
|
||||
"time": "2023-11-08T10:42:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theodorejb/polycast",
|
||||
@@ -11675,16 +11675,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v6.3.7",
|
||||
"version": "v6.3.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "9758b6c69d179936435d0ffb577c3708d57e38a8"
|
||||
"reference": "3493af8a8dad7fa91c77fa473ba23ecd95334a92"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/9758b6c69d179936435d0ffb577c3708d57e38a8",
|
||||
"reference": "9758b6c69d179936435d0ffb577c3708d57e38a8",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/3493af8a8dad7fa91c77fa473ba23ecd95334a92",
|
||||
"reference": "3493af8a8dad7fa91c77fa473ba23ecd95334a92",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11727,7 +11727,7 @@
|
||||
"description": "Loads and dumps YAML files",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/yaml/tree/v6.3.7"
|
||||
"source": "https://github.com/symfony/yaml/tree/v6.3.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11743,7 +11743,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-10-28T23:31:00+00:00"
|
||||
"time": "2023-11-06T10:58:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ta-tikoma/phpunit-architecture-test",
|
||||
|
||||
+20
-30
@@ -1,5 +1,20 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: "#^Method App\\\\Actions\\\\Fortify\\\\CreateNewUser\\:\\:passwordRules\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: app/Actions/Fortify/CreateNewUser.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Actions\\\\Fortify\\\\ResetUserPassword\\:\\:passwordRules\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: app/Actions/Fortify/ResetUserPassword.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Actions\\\\Fortify\\\\UpdateUserPassword\\:\\:passwordRules\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
path: app/Actions/Fortify/UpdateUserPassword.php
|
||||
|
||||
-
|
||||
message: "#^Called 'pluck' on Laravel collection, but could have been retrieved as a query\\.$#"
|
||||
count: 1
|
||||
@@ -2010,11 +2025,6 @@ parameters:
|
||||
count: 1
|
||||
path: app/Http/Controllers/AnnounceController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\ActivationController\\:\\:activate\\(\\) has parameter \\$token with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/ActivationController.php
|
||||
|
||||
-
|
||||
message: "#^Unable to resolve the template type TKey in call to function collect$#"
|
||||
count: 2
|
||||
@@ -2025,31 +2035,6 @@ parameters:
|
||||
count: 2
|
||||
path: app/Http/Controllers/Auth/ApplicationController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\LoginController\\:\\:authenticated\\(\\) has parameter \\$user with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/LoginController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\RegisterController\\:\\:register\\(\\) has parameter \\$code with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/RegisterController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\RegisterController\\:\\:registrationForm\\(\\) has parameter \\$code with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/RegisterController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\ResetPasswordController\\:\\:resetPassword\\(\\) has parameter \\$password with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/ResetPasswordController.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Controllers\\\\Auth\\\\ResetPasswordController\\:\\:resetPassword\\(\\) has parameter \\$user with no type specified\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Controllers/Auth/ResetPasswordController.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property App\\\\Models\\\\Torrent\\:\\:\\$meta\\.$#"
|
||||
count: 10
|
||||
@@ -3200,6 +3185,11 @@ parameters:
|
||||
count: 1
|
||||
path: app/Http/Livewire/TvSearch.php
|
||||
|
||||
-
|
||||
message: "#^Access to an undefined property App\\\\Models\\\\User\\:\\:\\$two_factor_confirmed_at\\.$#"
|
||||
count: 1
|
||||
path: app/Http/Livewire/TwoFactorAuthForm.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Http\\\\Livewire\\\\UserActive\\:\\:getActivesProperty\\(\\) return type with generic interface Illuminate\\\\Contracts\\\\Pagination\\\\LengthAwarePaginator does not specify its types\\: TItem$#"
|
||||
count: 1
|
||||
|
||||
@@ -3,112 +3,66 @@
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ __('auth.login') }} - {{ config('other.title') }}</title>
|
||||
@section('meta')
|
||||
<meta name="description"
|
||||
content="{{ __('auth.login-now-on') }} {{ config('other.title') }} . {{ __('auth.not-a-member') }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta property="og:title" content="{{ __('auth.login') }}">
|
||||
<meta property="og:site_name" content="{{ config('other.title') }}">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:image" content="{{ url('/img/og.png') }}">
|
||||
<meta property="og:description" content="{{ config('unit3d.powered-by') }}">
|
||||
<meta property="og:url" content="{{ url('/') }}">
|
||||
<meta property="og:locale" content="{{ config('app.locale') }}">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@show
|
||||
<title>{{ __('Two Factor Authentication') }} - {{ config('other.title') }}</title>
|
||||
<link rel="shortcut icon" href="{{ url('/favicon.ico') }}" type="image/x-icon">
|
||||
<link rel="icon" href="{{ url('/favicon.ico') }}" type="image/x-icon">
|
||||
<link rel="stylesheet" href="{{ mix('css/main/login.css') }}" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="{{ mix('css/app.css') }}" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Dont Not Change! For Jackett Support -->
|
||||
<div class="Jackett" style="display:none;">{{ config('unit3d.powered-by') }}</div>
|
||||
<!-- Dont Not Change! For Jackett Support -->
|
||||
|
||||
@if ($errors->any())
|
||||
<div id="ERROR_COPY" style="display: none;">
|
||||
@foreach ($errors->all() as $error)
|
||||
{{ $error }}<br>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<div class="wrapper fadeInDown">
|
||||
<svg viewBox="0 0 800 100" class="sitebanner">
|
||||
<symbol id="s-text">
|
||||
<text text-anchor="middle" x="50%" y="50%" dy=".35em">
|
||||
{{ config('other.title') }}
|
||||
</text>
|
||||
</symbol>
|
||||
<use xlink:href="#s-text" class="text"></use>
|
||||
<use xlink:href="#s-text" class="text"></use>
|
||||
<use xlink:href="#s-text" class="text"></use>
|
||||
<use xlink:href="#s-text" class="text"></use>
|
||||
<use xlink:href="#s-text" class="text"></use>
|
||||
</svg>
|
||||
|
||||
<div id="formContent">
|
||||
<div>
|
||||
<h2 class="active">{{ __('auth.totp.title') }} </h2>
|
||||
</div>
|
||||
|
||||
<div class="fadeIn first">
|
||||
<img src="{{ url('/img/icon.svg') }}" id="icon" alt="{{ __('auth.user-icon') }}"/>
|
||||
</div>
|
||||
|
||||
<form role="form" method="POST" action="{{ route('two-factor.login') }}">
|
||||
@csrf
|
||||
<div>
|
||||
<label for="username" class="col-md-4 control-label">{{ __('auth.totp.input') }}</label>
|
||||
<div class="col-md-6">
|
||||
<input id="code" type="text" class="form-control" name="code"
|
||||
required autofocus>
|
||||
</div>
|
||||
<div class="block">
|
||||
<div>
|
||||
<div x-data="{ recovery: false }">
|
||||
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400" x-show="! recovery">
|
||||
{{ __('Please confirm access to your account by entering the authentication code provided by your authenticator application.') }}
|
||||
</div>
|
||||
<button type="submit" class="fadeIn fourth" id="login-button">{{ __('auth.login') }}</button>
|
||||
</form>
|
||||
|
||||
<div id="formFooter">
|
||||
<a href="{{ route('password.request') }}">
|
||||
<h2 class="inactive underlineHover">{{ __('auth.lost-password') }} </h2>
|
||||
</a>
|
||||
<div class="mb-4 text-sm text-gray-600 dark:text-gray-400" x-cloak x-show="recovery">
|
||||
{{ __('Please confirm access to your account by entering one of your emergency recovery codes.') }}
|
||||
</div>
|
||||
|
||||
|
||||
<form method="POST" action="{{ route('two-factor.login') }}">
|
||||
@csrf
|
||||
|
||||
<div class="mt-4" x-show="! recovery">
|
||||
<label for="code" value="{{ __('Code') }}"></label>
|
||||
<input id="code" class="block mt-1 w-full" type="text" inputmode="numeric" name="code" autofocus x-ref="code" autocomplete="one-time-code" />
|
||||
</div>
|
||||
|
||||
<div class="mt-4" x-cloak x-show="recovery">
|
||||
<label for="recovery_code" value="{{ __('Recovery Code') }}"></label>
|
||||
<input id="recovery_code" class="block mt-1 w-full" type="text" name="recovery_code" x-ref="recovery_code" autocomplete="one-time-code" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-4">
|
||||
<button type="button" class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 underline cursor-pointer"
|
||||
x-show="! recovery"
|
||||
x-on:click="
|
||||
recovery = true;
|
||||
$nextTick(() => { $refs.recovery_code.focus() })
|
||||
">
|
||||
{{ __('Use a recovery code') }}
|
||||
</button>
|
||||
|
||||
<button type="button" class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 underline cursor-pointer"
|
||||
x-cloak
|
||||
x-show="recovery"
|
||||
x-on:click="
|
||||
recovery = false;
|
||||
$nextTick(() => { $refs.code.focus() })
|
||||
">
|
||||
{{ __('Use an authentication code') }}
|
||||
</button>
|
||||
|
||||
<button class="ms-4">
|
||||
{{ __('Log in') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ mix('js/app.js') }}" crossorigin="anonymous"></script>
|
||||
@foreach (['warning', 'success', 'info'] as $key)
|
||||
@if (Session::has($key))
|
||||
<script nonce="{{ HDVinnie\SecureHeaders\SecureHeaders::nonce('script') }}">
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 3000
|
||||
})
|
||||
|
||||
Toast.fire({
|
||||
icon: '{{ $key }}',
|
||||
title: '{{ Session::get($key) }}'
|
||||
})
|
||||
|
||||
</script>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
@if (Session::has('errors'))
|
||||
<script nonce="{{ HDVinnie\SecureHeaders\SecureHeaders::nonce('script') }}">
|
||||
Swal.fire({
|
||||
title: '<strong style=" color: rgb(17,17,17);">Error</strong>',
|
||||
icon: 'error',
|
||||
html: document.getElementById('ERROR_COPY').innerHTML,
|
||||
showCloseButton: true,
|
||||
})
|
||||
|
||||
</script>
|
||||
@endif
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -0,0 +1,110 @@
|
||||
<section class="panelV2">
|
||||
<header class="panel__header">
|
||||
<h2 class="panel__heading">{{ __('Two Factor Authentication') }}</h2>
|
||||
</header>
|
||||
<div class="panel__body">
|
||||
@if ($this->enabled)
|
||||
@if ($showingConfirmation)
|
||||
<span class="text-warning">{{ __('Finish enabling two factor authentication.') }}</span>
|
||||
@else
|
||||
<span class="text-success">{{ __('You have enabled two factor authentication.') }}</span>
|
||||
@endif
|
||||
@else
|
||||
<span class="text-danger">{{ __('You have not enabled two factor authentication.') }}</span>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
<span class="text-muted">
|
||||
{{ __('When two factor authentication is enabled, you will be prompted for a secure, random token during authentication. You may retrieve this token from your phone\'s Google Authenticator application.') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if ($this->enabled)
|
||||
@if ($showingQrCode)
|
||||
<div>
|
||||
<p class="text-info">
|
||||
@if ($showingConfirmation)
|
||||
{{ __('To finish enabling two factor authentication, scan the following QR code using your phone\'s authenticator application or enter the setup key and provide the generated OTP code.') }}
|
||||
@else
|
||||
{{ __('Two factor authentication is now enabled. Scan the following QR code using your phone\'s authenticator application or enter the setup key.') }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{!! $this->user->twoFactorQrCodeSvg() !!}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
{{ __('Setup Key') }}: {{ decrypt($this->user->two_factor_secret) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@if ($showingConfirmation)
|
||||
<div>
|
||||
<label for="code" value="{{ __('Code') }}"></label>
|
||||
|
||||
<input id="code"
|
||||
name="code"
|
||||
class="form__text"
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
autofocus
|
||||
autocomplete="one-time-code"
|
||||
wire:model="code"
|
||||
wire:keydown.enter="confirmTwoFactorAuthentication" />
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if ($showingRecoveryCodes)
|
||||
<div class="panel__body">
|
||||
<span class="text-danger">
|
||||
{{ __('Store these recovery codes in a secure password manager. They can be used to recover access to your account if your two factor authentication device is lost.') }}
|
||||
</span>
|
||||
|
||||
|
||||
<pre>
|
||||
@foreach (json_decode(decrypt($this->user->two_factor_recovery_codes), true) as $code)
|
||||
<div>{{ $code }}</div>
|
||||
@endforeach
|
||||
</pre>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div>
|
||||
@if (! $this->enabled)
|
||||
<button class="form__button form__button--filled" wire:click="enableTwoFactorAuthentication" wire:loading.attr="disabled">
|
||||
{{ __('Enable') }}
|
||||
</button>
|
||||
@else
|
||||
@if ($showingRecoveryCodes)
|
||||
<button class="form__button form__button--filled" wire:click="regenerateRecoveryCodes">
|
||||
{{ __('Regenerate Recovery Codes') }}
|
||||
</button>
|
||||
@elseif ($showingConfirmation)
|
||||
<button class="form__button form__button--filled" type="button" wire:click="confirmTwoFactorAuthentication" wire:loading.attr="disabled">
|
||||
{{ __('Confirm') }}
|
||||
</button>
|
||||
@else
|
||||
<button class="form__button form__button--filled" wire:click="showRecoveryCodes">
|
||||
{{ __('Show Recovery Codes') }}
|
||||
</button>
|
||||
@endif
|
||||
|
||||
@if ($showingConfirmation)
|
||||
<button class="form__button form__button--filled" wire:click="disableTwoFactorAuthentication" wire:loading.attr="disabled">
|
||||
{{ __('Cancel') }}
|
||||
</button>
|
||||
@else
|
||||
<button class="form__button form__button--filled" wire:click="disableTwoFactorAuthentication" wire:loading.attr="disabled">
|
||||
{{ __('Disable') }}
|
||||
</button>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
@@ -122,10 +122,10 @@
|
||||
API Key
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{ Route::is('users.two_step.edit') ? 'nav-tab--active' : 'nav-tavV2' }}">
|
||||
<li class="{{ Route::is('users.two_factor_auth.edit') ? 'nav-tab--active' : 'nav-tavV2' }}">
|
||||
<a
|
||||
class="{{ Route::is('users.two_step.edit') ? 'nav-tab--active__link' : 'nav-tab__link' }}"
|
||||
href="{{ route('users.two_step.edit', ['user' => $user]) }}"
|
||||
class="{{ Route::is('users.two_factor_auth.edit') ? 'nav-tab--active__link' : 'nav-tab__link' }}"
|
||||
href="{{ route('users.two_factor_auth.edit', ['user' => $user]) }}"
|
||||
>
|
||||
{{ __('user.two-step-auth.title') }}
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title')
|
||||
<title>{{ $user->username }} - Security - {{ __('common.members') }} - {{ config('other.title') }}</title>
|
||||
@endsection
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('users.show', ['user' => $user]) }}" class="breadcrumb__link">
|
||||
{{ $user->username }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('users.general_settings.edit', ['user' => $user]) }}" class="breadcrumb__link">
|
||||
{{ __('user.settings') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb--active">
|
||||
{{ __('Two Factor Authentication') }}
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('nav-tabs')
|
||||
@include('user.buttons.user')
|
||||
@endsection
|
||||
|
||||
@section('main')
|
||||
@livewire('two-factor-auth-form')
|
||||
@endsection
|
||||
@@ -1,164 +0,0 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title')
|
||||
<title>{{ $user->username }} - Security - {{ __('common.members') }} - {{ config('other.title') }}</title>
|
||||
@endsection
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('users.show', ['user' => $user]) }}" class="breadcrumb__link">
|
||||
{{ $user->username }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumbV2">
|
||||
<a href="{{ route('users.general_settings.edit', ['user' => $user]) }}" class="breadcrumb__link">
|
||||
{{ __('user.settings') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="breadcrumb--active">
|
||||
{{ __('user.two-step-auth.title') }}
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('nav-tabs')
|
||||
@include('user.buttons.user')
|
||||
@endsection
|
||||
|
||||
@section('main')
|
||||
@if (session('status') == 'two-factor-authentication-confirmed' || request()->user()->hasEnabledTwoFactorAuthentication())
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('user.two-step-auth.totp') }}</h2>
|
||||
<div class="panel__body">
|
||||
<p>{{ __('user.two-step-auth.totp-is-enabled') }}</p>
|
||||
<div>
|
||||
<div>
|
||||
<form
|
||||
class="form"
|
||||
action="{{ route('two-factor.disable') }}"
|
||||
method="POST"
|
||||
>
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<p>{{ __('user.two-step-auth.password-confirm') }}</p>
|
||||
<p class="form__group">
|
||||
<button class="form__button form__button--filled">
|
||||
{{ __('common.disable') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<section class="panelV2" x-data="{show: false}">
|
||||
<h2 class="panel__heading">{{ __('user.two-step-auth.recovery-code') }}</h2>
|
||||
<div class="panel__body">
|
||||
<p>{{ __('user.two-step-auth.recovery-code-description') }}</p>
|
||||
<div>
|
||||
<div>
|
||||
<form method="POST" action="{{route('two-factor.recovery-codes')}}" class="form__group">
|
||||
@csrf
|
||||
<button type="submit" class="form__button form__button--filled">
|
||||
{{ __('user.two-step-auth.recovery-code-reset') }}
|
||||
</button>
|
||||
<div class="form__button form__button--filled" @click="show = !show">
|
||||
{{ __('user.two-step-auth.recovery-code-reveal') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="twoStep__recoveryCodes" x-cloak x-show="show" >
|
||||
<p>Recovery Codes:</p>
|
||||
<p style="font-family: monospace;">
|
||||
@foreach(auth()->user()->recoveryCodes() as $code)
|
||||
{{ $code }}<br/>
|
||||
@endforeach
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@elseif (session('status') == 'two-factor-authentication-enabled')
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('user.two-step-auth.totp') }}</h2>
|
||||
<div class="panel__body">
|
||||
<p>{{ __('user.two-step-auth.complete-setup') }}</p>
|
||||
<form
|
||||
class="form"
|
||||
action="{{ route('two-factor.confirm') }}"
|
||||
method="POST"
|
||||
>
|
||||
@csrf
|
||||
<div class="twoStep__qrCode">
|
||||
<img src="data:image/svg+xml;base64,{{base64_encode(request()->user()->twoFactorQrCodeSvg())}}" alt="2FA QR Code">
|
||||
</div>
|
||||
<div class="form__group">
|
||||
<input type="text" class="form__text" name="code" id="code" value="" required>
|
||||
<label class="form__label form__label--floating" for="code">{{ __('user.two-step-auth.confirm-code') }}</label>
|
||||
</div>
|
||||
<div class="form__group">
|
||||
<button class="form__button form__button--filled">
|
||||
{{ __('common.enable') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@else
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('user.two-step-auth.totp') }}</h2>
|
||||
<div class="panel__body">
|
||||
<p>{{ __('user.two-step-auth.totp-is-disabled') }}</p>
|
||||
<form
|
||||
class="form"
|
||||
action="{{ route('two-factor.enable') }}"
|
||||
method="POST"
|
||||
>
|
||||
@csrf
|
||||
<p>{{ __('user.two-step-auth.password-confirm') }}</p>
|
||||
<p>{{ __('user.two-step-auth.upon-enabling') }}</p>
|
||||
<p class="form__group">
|
||||
<button class="form__button form__button--filled">
|
||||
{{ __('common.enable') }}
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panelV2">
|
||||
<h2 class="panel__heading">{{ __('user.two-step-auth.email') }}</h2>
|
||||
<div class="panel__body">
|
||||
<form
|
||||
class="form"
|
||||
action="{{ route('users.two_step.update', ['user' => $user]) }}"
|
||||
method="POST"
|
||||
>
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<p>Upon enabling, you will receive an email including a code to your registered email address.</p>
|
||||
<p>Token-based two factor authentication is planned for a future update.</p>
|
||||
<div class="form__group">
|
||||
<input type="hidden" name="twostep" value="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form__checkbox"
|
||||
id="twostep"
|
||||
name="twostep"
|
||||
value="1"
|
||||
@checked($user->twostep)
|
||||
>
|
||||
<label class="form__label" for="internal">Enable two-step authentication</label>
|
||||
</div>
|
||||
<div class="form__group">
|
||||
<button class="form__button form__button--filled">
|
||||
{{ __('common.save') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
|
||||
+9
-1
@@ -2,6 +2,9 @@
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Laravel\Fortify\Features;
|
||||
use Laravel\Fortify\Http\Controllers\TwoFactorAuthenticatedSessionController;
|
||||
use Laravel\Fortify\RoutePath;
|
||||
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
@@ -38,7 +41,7 @@ Route::middleware('language')->group(function (): void {
|
||||
| Website (Not Authorized) (Alpha Ordered)
|
||||
|---------------------------------------------------------------------------------
|
||||
*/
|
||||
Route::group(['before' => 'auth', 'middleware' => 'guest'], function (): void {
|
||||
Route::middleware('guest')->group(function (): void {
|
||||
// Application Signup
|
||||
Route::get('/application', [App\Http\Controllers\Auth\ApplicationController::class, 'create'])->name('application.create');
|
||||
Route::post('/application', [App\Http\Controllers\Auth\ApplicationController::class, 'store'])->name('application.store');
|
||||
@@ -496,6 +499,11 @@ Route::middleware('language')->group(function (): void {
|
||||
Route::delete('/{seedbox}', [App\Http\Controllers\User\SeedboxController::class, 'destroy'])->name('destroy');
|
||||
});
|
||||
|
||||
// Two-Factor Authentication
|
||||
Route::prefix('two-factor-auth')->name('two_factor_auth.')->group(function (): void {
|
||||
Route::get('/edit', [App\Http\Controllers\User\TwoFactorAuthController::class, 'edit'])->name('edit');
|
||||
});
|
||||
|
||||
// Email
|
||||
Route::prefix('email')->name('email.')->group(function (): void {
|
||||
Route::get('/edit', [App\Http\Controllers\User\EmailController::class, 'edit'])->name('edit');
|
||||
|
||||
Reference in New Issue
Block a user