1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-01-17 13:18:21 +01:00

Replace Laravel UI with Fortify (#172)

This commit is contained in:
Kovah 2020-11-16 22:03:06 +01:00
parent 23592e701e
commit 68b76a60f3
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
28 changed files with 788 additions and 424 deletions

View File

@ -0,0 +1,41 @@
<?php
namespace App\Actions\Fortify;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\CreatesNewUsers;
class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;
/**
* Validate and create a newly registered user.
*
* @param array $input
* @return \App\Models\User
*/
public function create(array $input)
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();
return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Actions\Fortify;
use Laravel\Fortify\Rules\Password;
trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array
*/
protected function passwordRules()
{
return ['required', 'string', new Password, 'confirmed'];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;
class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;
/**
* Validate and reset the user's forgotten password.
*
* @param mixed $user
* @param array $input
* @return void
*/
public function reset($user, array $input)
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;
/**
* Validate and update the user's password.
*
* @param mixed $user
* @param array $input
* @return void
*/
public function update($user, array $input)
{
Validator::make($input, [
'current_password' => ['required', 'string'],
'password' => $this->passwordRules(),
])->after(function ($validator) use ($user, $input) {
if (! Hash::check($input['current_password'], $user->password)) {
$validator->errors()->add('current_password', trans('settings.old_password_invalid'));
}
})->validateWithBag('updatePassword');
$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Actions\Fortify;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param mixed $user
* @param array $input
* @return void
*/
public function update($user, array $input)
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email && $user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}
/**
* Update the given verified user's profile information.
*
* @param mixed $user
* @param array $input
* @return void
*/
protected function updateVerifiedUser($user, array $input)
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();
$user->sendEmailVerificationNotification();
}
}

View File

@ -2,6 +2,8 @@
namespace App\Http\Controllers\App;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Helper\LinkAce;
use App\Http\Controllers\Controller;
use App\Http\Requests\UserAccountUpdateRequest;
@ -11,8 +13,6 @@ use App\Models\Setting;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;
@ -36,15 +36,12 @@ class UserSettingsController extends Controller
/**
* Handles changes of the user account itself.
*
* @param UserAccountUpdateRequest $request
* @param Request $request
* @return RedirectResponse
*/
public function saveAccountSettings(UserAccountUpdateRequest $request): RedirectResponse
public function saveAccountSettings(Request $request): RedirectResponse
{
$request->user()->update($request->only([
'name',
'email',
]));
(new UpdateUserProfileInformation())->update($request->user(), $request->input());
flash(trans('settings.settings_saved'), 'success');
@ -52,7 +49,7 @@ class UserSettingsController extends Controller
}
/**
* Handle changes of generall application settings like share services.
* Handle changes of general application settings like share services.
*
* @param UserSettingsUpdateRequest $request
* @return RedirectResponse
@ -95,26 +92,12 @@ class UserSettingsController extends Controller
/**
* Handles the user password change.
*
* @param UserPasswordUpdateRequest $request
* @param Request $request
* @return RedirectResponse
*/
public function changeUserPassword(UserPasswordUpdateRequest $request): RedirectResponse
public function changeUserPassword(Request $request): RedirectResponse
{
$currentUser = $request->user();
$authorizationSuccessful = Auth::attempt([
'email' => $currentUser->email,
'password' => $request->input('old_password'),
]);
if (!$authorizationSuccessful) {
flash(trans('settings.old_password_invalid'));
return redirect()->back()->withInput();
}
$currentUser->password = Hash::make($request->input('new_password'));
$currentUser->save();
(new UpdateUserPassword())->update($request->user(), $request->input());
flash(trans('settings.password_updated'), 'success');

View File

@ -1,32 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View File

@ -1,50 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo()
{
// Redirect to the bookmarklet form after login from the bookmarklet
if (session()->pull('bookmarklet.login_redirect')) {
return route('bookmarklet-add', [
'u' => session('bookmarklet.new_url'),
't' => session('bookmarklet.new_title'),
]);
}
return '/dashboard';
}
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}

View File

@ -1,63 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/dashboard';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return Validator
*/
protected function validator(array $data): Validator
{
return User::validateRegistration($data);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return User
*/
protected function create(array $data): User
{
return User::createUser($data);
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/dashboard';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View File

@ -2,16 +2,15 @@
namespace App\Http\Controllers\Setup;
use App\Actions\Fortify\CreateNewUser;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class AccountController extends Controller
{
use RegistersUsers;
protected function redirectTo(): string
{
return route('setup.complete');
@ -28,20 +27,17 @@ class AccountController extends Controller
}
/**
* @param array $data
* @return Validator
* Validate and create the new user, then login him, and redirect him to the dashboard
*
* @param Request $request
* @return RedirectResponse
*/
protected function validator(array $data): Validator
protected function register(Request $request): RedirectResponse
{
return User::validateRegistration($data);
}
$user = (new CreateNewUser())->create($request->input());
/**
* @param array $data
* @return User
*/
protected function create(array $data): User
{
return User::createUser($data);
Auth::login($user, true);
return redirect()->route('dashboard');
}
}

View File

@ -4,6 +4,7 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class RedirectIfAuthenticated
{
@ -18,6 +19,13 @@ class RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if (Session::pull('bookmarklet.login_redirect')) {
return redirect()->route('bookmarklet-add', [
'u' => session('bookmarklet.new_url'),
't' => session('bookmarklet.new_title'),
]);
}
return redirect('/dashboard');
}

View File

@ -1,58 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
/**
* Class UserAccountUpdateRequest
*
* @package App\Http\Requests
*/
class UserAccountUpdateRequest extends FormRequest
{
/** @var bool */
private $validate_username = false;
/** @var bool */
private $validate_email = false;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(Request $request)
{
// Validate the username if it was changed
if ($request->input('name') !== auth()->user()->name) {
$this->validate_username = true;
}
// Validate the email address if it was changed
if ($request->input('email') !== auth()->user()->email) {
$this->validate_email = true;
}
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
if ($this->validate_username) {
$rules['name'] = 'unique:users,name';
}
if ($this->validate_email) {
$rules['email'] = 'unique:users,email';
}
return $rules ?? [];
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class UserAccountUpdateRequest
*
* @package App\Http\Requests
*/
class UserPasswordUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'old_password' => 'required',
'new_password' => 'required|min:10|confirmed',
];
}
}

View File

@ -41,24 +41,6 @@ class User extends Authenticatable
'remember_token',
];
public static function validateRegistration(array $data): ValidatorContract
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:10|confirmed',
]);
}
public static function createUser(array $data): self
{
return self::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
/*
| ========================================================================
| RELATIONSHIPS

View File

@ -0,0 +1,56 @@
<?php
namespace App\Providers;
use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;
class FortifyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
Fortify::loginView(function () {
return view('auth.login');
});
Fortify::requestPasswordResetLinkView(function () {
return view('auth.passwords.email');
});
Fortify::resetPasswordView(function () {
return view('auth.passwords.reset');
});
Fortify::confirmPasswordView(function () {
return view('auth.confirm-password');
});
Fortify::twoFactorChallengeView(function () {
return view(); // @TODO
});
}
}

View File

@ -4,6 +4,7 @@ namespace App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Session;
class RouteServiceProvider extends ServiceProvider
{
@ -16,6 +17,8 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $namespace = 'App\Http\Controllers';
public const HOME = '/dashboard';
/**
* Define your route model bindings, pattern filters, etc.
*

View File

@ -10,8 +10,8 @@
"fideloper/proxy": "^4.0",
"guzzlehttp/guzzle": "^7.0.1",
"laracasts/flash": "^3.1",
"laravel/fortify": "^1.7",
"laravel/framework": "^8.0",
"laravel/ui": "^3.0",
"league/csv": "^9.6",
"predis/predis": "^1.1",
"shaarli/netscape-bookmark-parser": "^2.1",

317
composer.lock generated
View File

@ -4,8 +4,57 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "885bd4615d6394c87ffa558cc49c0b1a",
"content-hash": "ca1d0154e63b5d72e092e495ed8413d0",
"packages": [
{
"name": "bacon/bacon-qr-code",
"version": "2.0.3",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "3e9d791b67d0a2912922b7b7c7312f4b37af41e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/3e9d791b67d0a2912922b7b7c7312f4b37af41e4",
"reference": "3e9d791b67d0a2912922b7b7c7312f4b37af41e4",
"shasum": ""
},
"require": {
"dasprid/enum": "^1.0.3",
"ext-iconv": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phly/keep-a-changelog": "^1.4",
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4"
},
"suggest": {
"ext-imagick": "to generate QR code images"
},
"type": "library",
"autoload": {
"psr-4": {
"BaconQrCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "BaconQrCode is a QR code generator for PHP.",
"homepage": "https://github.com/Bacon/BaconQrCode",
"time": "2020-10-30T02:02:47+00:00"
},
{
"name": "brick/math",
"version": "0.9.1",
@ -133,6 +182,49 @@
],
"time": "2020-09-27T13:13:07+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2",
"reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^7 | ^8 | ^9",
"squizlabs/php_codesniffer": "^3.4"
},
"type": "library",
"autoload": {
"psr-4": {
"DASPRiD\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "https://dasprids.de/",
"role": "Developer"
}
],
"description": "PHP 7.1 enum implementation",
"keywords": [
"enum",
"map"
],
"time": "2020-10-02T16:03:48+00:00"
},
{
"name": "doctrine/cache",
"version": "1.10.2",
@ -1145,6 +1237,65 @@
"description": "Easy flash notifications",
"time": "2020-09-07T13:25:35+00:00"
},
{
"name": "laravel/fortify",
"version": "v1.7.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/fortify.git",
"reference": "b2430958fa93883ab0e5f0caf486ef3688711608"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/fortify/zipball/b2430958fa93883ab0e5f0caf486ef3688711608",
"reference": "b2430958fa93883ab0e5f0caf486ef3688711608",
"shasum": ""
},
"require": {
"bacon/bacon-qr-code": "^2.0",
"ext-json": "*",
"illuminate/support": "^8.0",
"php": "^7.3|^8.0",
"pragmarx/google2fa": "^7.0|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^6.0",
"phpunit/phpunit": "^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Fortify\\FortifyServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Fortify\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Backend controllers and scaffolding for Laravel authentication.",
"keywords": [
"auth",
"laravel"
],
"time": "2020-11-13T13:55:54+00:00"
},
{
"name": "laravel/framework",
"version": "v8.13.0",
@ -1308,60 +1459,6 @@
],
"time": "2020-11-03T14:13:19+00:00"
},
{
"name": "laravel/ui",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/ui.git",
"reference": "444072cb2f8baaa15172c5cde2bd30d188c3b7e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/ui/zipball/444072cb2f8baaa15172c5cde2bd30d188c3b7e7",
"reference": "444072cb2f8baaa15172c5cde2bd30d188c3b7e7",
"shasum": ""
},
"require": {
"illuminate/console": "^8.0",
"illuminate/filesystem": "^8.0",
"illuminate/support": "^8.0",
"php": "^7.3|^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Ui\\UiServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Ui\\": "src/",
"Illuminate\\Foundation\\Auth\\": "auth-backend/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel UI utilities and presets.",
"keywords": [
"laravel",
"ui"
],
"time": "2020-11-03T19:51:21+00:00"
},
{
"name": "league/commonmark",
"version": "1.5.7",
@ -1918,6 +2015,68 @@
],
"time": "2020-11-07T02:01:34+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.3.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2",
"reference": "47a1cedd2e4d52688eb8c96469c05ebc8fd28fa2",
"shasum": ""
},
"require": {
"php": "^7|^8"
},
"require-dev": {
"phpunit/phpunit": "^6|^7",
"vimeo/psalm": "^1|^2|^3"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"time": "2019-11-06T19:20:29+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.7.5",
@ -1983,6 +2142,54 @@
],
"time": "2020-07-20T17:29:33+00:00"
},
{
"name": "pragmarx/google2fa",
"version": "8.0.0",
"source": {
"type": "git",
"url": "https://github.com/antonioribeiro/google2fa.git",
"reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/26c4c5cf30a2844ba121760fd7301f8ad240100b",
"reference": "26c4c5cf30a2844ba121760fd7301f8ad240100b",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1.0|^2.0",
"php": "^7.1|^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.18",
"phpunit/phpunit": "^7.5.15|^8.5|^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"PragmaRX\\Google2FA\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Antonio Carlos Ribeiro",
"email": "acr@antoniocarlosribeiro.com",
"role": "Creator & Designer"
}
],
"description": "A One Time Password Authentication package, compatible with Google Authenticator.",
"keywords": [
"2fa",
"Authentication",
"Two Factor Authentication",
"google2fa"
],
"time": "2020-04-05T10:47:18+00:00"
},
{
"name": "predis/predis",
"version": "v1.1.6",

View File

@ -206,6 +206,7 @@ return [
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\FortifyServiceProvider::class,
],

144
config/fortify.php Normal file
View File

@ -0,0 +1,144 @@
<?php
use App\Providers\RouteServiceProvider;
use Laravel\Fortify\Features;
return [
/*
|--------------------------------------------------------------------------
| Fortify Guard
|--------------------------------------------------------------------------
|
| Here you may specify which authentication guard Fortify will use while
| authenticating users. This value should correspond with one of your
| guards that is already present in your "auth" configuration file.
|
*/
'guard' => 'web',
/*
|--------------------------------------------------------------------------
| Fortify Password Broker
|--------------------------------------------------------------------------
|
| Here you may specify which password broker Fortify can use when a user
| is resetting their password. This configured value should match one
| of your password brokers setup in your "auth" configuration file.
|
*/
'passwords' => 'users',
/*
|--------------------------------------------------------------------------
| Username / Email
|--------------------------------------------------------------------------
|
| This value defines which model attribute should be considered as your
| application's "username" field. Typically, this might be the email
| address of the users but you are free to change this value here.
|
| Out of the box, Fortify expects forgot password and reset password
| requests to have a field named 'email'. If the application uses
| another name for the field you may define it below as needed.
|
*/
'username' => 'email',
'email' => 'email',
/*
|--------------------------------------------------------------------------
| Home Path
|--------------------------------------------------------------------------
|
| Here you may configure the path where users will get redirected during
| authentication or password reset when the operations are successful
| and the user is authenticated. You are free to change this value.
|
*/
'home' => RouteServiceProvider::HOME,
/*
|--------------------------------------------------------------------------
| Fortify Routes Prefix / Subdomain
|--------------------------------------------------------------------------
|
| Here you may specify which prefix Fortify will assign to the all routes
| that it registers with the application. If necessary, you may change
| subdomain under which all of the Fortify routes will be available.
|
*/
'prefix' => '',
'domain' => null,
/*
|--------------------------------------------------------------------------
| Fortify Routes Middleware
|--------------------------------------------------------------------------
|
| Here you may specify which middleware Fortify will assign to the routes
| that it registers with the application. If necessary, you may change
| these middleware but typically this provided default is preferred.
|
*/
'middleware' => ['web'],
/*
|--------------------------------------------------------------------------
| Rate Limiting
|--------------------------------------------------------------------------
|
| By default, Fortify will throttle logins to five requests per minute for
| every email and IP address combination. However, if you would like to
| specify a custom rate limiter to call then you may specify it here.
|
*/
'limiters' => [
'login' => null,
],
/*
|--------------------------------------------------------------------------
| Register View Routes
|--------------------------------------------------------------------------
|
| Here you may specify if the routes returning views should be disabled as
| you may not need them when building your own application. This may be
| especially true if you're writing a custom single-page application.
|
*/
'views' => true,
/*
|--------------------------------------------------------------------------
| Features
|--------------------------------------------------------------------------
|
| Some of the Fortify features are optional. You may disable the features
| by removing them from this array. You're free to only remove some of
| these features or you can even remove all of these if you need to.
|
*/
'features' => [
Features::registration(),
Features::resetPasswords(),
// Features::emailVerification(),
Features::updateProfileInformation(),
Features::updatePasswords(),
Features::twoFactorAuthentication([
'confirmPassword' => true,
]),
],
];

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTwoFactorColumnsToUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->text('two_factor_secret')
->after('password')
->nullable();
$table->text('two_factor_recovery_codes')
->after('two_factor_secret')
->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('two_factor_secret', 'two_factor_recovery_codes');
});
}
}

View File

@ -16,4 +16,8 @@ return [
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'confirm_title' => 'Confirmation required',
'confirm' => 'Please confirm this action using your current password.',
'confirm_action' => 'Confirm action',
];

View File

@ -12,24 +12,24 @@
<label for="old_password">
@lang('settings.old_password')
</label>
<input type="password" name="old_password" id="old_password" required
class="form-control{{ $errors->has('old_password') ? ' is-invalid' : '' }}">
<input type="password" name="current_password" id="current_password" required
class="form-control{{ $errors->has('current_password') ? ' is-invalid' : '' }}">
</div>
<div class="form-group">
<label for="new_password">
@lang('settings.new_password')
</label>
<input type="password" name="new_password" id="new_password" required
class="form-control{{ $errors->has('new_password') ? ' is-invalid' : '' }}">
<input type="password" name="password" id="password" required
class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}">
</div>
<div class="form-group">
<label for="new_password_confirmation">
@lang('settings.new_password2')
</label>
<input type="password" name="new_password_confirmation" id="new_password_confirmation" required
class="form-control{{ $errors->has('new_password_confirmation') ? ' is-invalid' : '' }}">
<input type="password" name="password_confirmation" id="password_confirmation" required
class="form-control{{ $errors->has('password_confirmation') ? ' is-invalid' : '' }}">
</div>
<button type="submit" class="btn btn-primary">

View File

@ -0,0 +1,52 @@
@extends('layouts.app')
@section('content')
<div class="row justify-content-center">
<div class="col-12 col-md-8">
@if(env('APP_DEMO', false))
<div class="alert alert-info small">@lang('linkace.demo_login_hint')</div>
@endif
<div class="card">
<div class="card-header">
@lang('auth.confirm_title')
</div>
<div class="card-body">
<p>@lang('auth.confirm')</p>
<form method="POST" action="{{ url('/user/confirm-password') }}"
aria-label="@lang('auth.confirm_title')">
@csrf
<div class="form-group">
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<x-icon.lock/>
</div>
</div>
<input type="password" name="password" id="password" class="form-control"
placeholder="@lang('placeholder.password')" aria-label="@lang('linkace.password')">
</div>
@if ($errors->has('password'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('password') }}
</p>
@endif
</div>
<button type="submit" class="btn btn-primary">
<x-icon.save class="mr-2"/> @lang('auth.confirm_action')
</button>
</form>
</div>
</div>
</div>
</div>
@endsection

View File

@ -11,11 +11,13 @@
</div>
<div class="card-body">
<form method="POST" action="{{ route('password.request') }}"
<form method="POST" action="{{ route('password.update') }}"
aria-label="@lang('linkace.reset_password')">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
{{ implode('|',$errors->all()) }}
<input type="hidden" name="token" value="{{ request()->route('token') }}">
<div class="form-group">
<label for="email">@lang('linkace.email')</label>

View File

@ -46,24 +46,6 @@ Route::post('setup/account', [AccountController::class, 'register'])
Route::get('setup/complete', [MetaController::class, 'complete'])
->name('setup.complete');
// Authentication Routes
Route::get('login', [LoginController::class, 'showLoginForm'])->name('login');
Route::post('login', [LoginController::class, 'login']);
Route::post('logout', [LoginController::class, 'logout'])->name('logout');
// Registration Routes (disabled, use the `artisan registeruser` command)
//Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
//Route::post('register', 'Auth\RegisterController@register');
// Password Reset Routes
Route::get('password/reset', [ForgotPasswordController::class, 'showLinkRequestForm'])
->name('password.request');
Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])
->name('password.email');
Route::get('password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])
->name('password.reset');
Route::post('password/reset', [ResetPasswordController::class, 'reset']);
// Bookmarklet routes
Route::prefix('bookmarklet')->group(function () {
Route::get('add', [BookmarkletController::class, 'getLinkAddForm'])->name('bookmarklet-add');

View File

@ -85,9 +85,9 @@ class UserSettingsControllerTest extends TestCase
public function testValidUpdatePasswordResponse(): void
{
$response = $this->post('settings/change-password', [
'old_password' => 'secretpassword',
'new_password' => 'newuserpassword',
'new_password_confirmation' => 'newuserpassword',
'current_password' => 'secretpassword',
'password' => 'newuserpassword',
'password_confirmation' => 'newuserpassword',
]);
$response->assertRedirect('/');