148 lines
6.1 KiB
PHP
148 lines
6.1 KiB
PHP
<?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 App\Models\User;
|
|
use Illuminate\Cache\RateLimiting\Limit;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\RateLimiter;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Illuminate\Support\ServiceProvider;
|
|
use Illuminate\Validation\ValidationException;
|
|
use Laravel\Fortify\Fortify;
|
|
use Laravel\Fortify\Http\Controllers\VerifyEmailController;
|
|
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
|
|
|
class FortifyServiceProvider extends ServiceProvider
|
|
{
|
|
/**
|
|
* Register any application services.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register()
|
|
{
|
|
//
|
|
}
|
|
|
|
/**
|
|
* Bootstrap any application services.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function boot()
|
|
{
|
|
// Disable Fortify route registration, a selection of the initial fortify routes are moved ot /routes/fortify.php.
|
|
// Custom routes with the same name however are registered in web.php. This is done because Fortify does not allow to register routes with the same name.
|
|
Fortify::ignoreRoutes();
|
|
|
|
Fortify::createUsersUsing(CreateNewUser::class);
|
|
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
|
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
|
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
|
|
|
Fortify::verifyEmailView(fn () => view('auth.verify-email'));
|
|
|
|
RateLimiter::for('login', function (Request $request) {
|
|
$loginIdentifier = (string) $request->input('name'); // Changed from $request->email to $request->input('name')
|
|
return Limit::perMinute(10)->by($loginIdentifier.$request->ip());
|
|
});
|
|
|
|
RateLimiter::for('two-factor', function (Request $request) {
|
|
return Limit::perMinute(5)->by($request->session()->get('login.id'));
|
|
});
|
|
|
|
|
|
|
|
// Rehash Cyclos salted sha256 passwords on first login
|
|
Fortify::authenticateUsing(function (Request $request) {
|
|
// Attempt to find the user by email or name.
|
|
$user = User::where('email', $request->name)
|
|
->orWhere('name', $request->name)
|
|
->first();
|
|
|
|
if (!$user) {
|
|
info('Auth failed: User not identified with login identifier: ' . $request->auth);
|
|
return null;
|
|
}
|
|
if ($user->isRemoved()) {
|
|
info('Auth failed: User ' . $user->name . ' is marked as removed (soft-deleted).');
|
|
return null;
|
|
}
|
|
|
|
// If maintenance mode is enabled, check if user has admin relationship
|
|
if (isMaintenanceMode()) {
|
|
$hasAdminRelation = $user->admins()->exists();
|
|
if (!$hasAdminRelation) {
|
|
info('Auth failed: Maintenance mode is active and user ' . $user->name . ' does not have admin relationship.');
|
|
session()->flash('maintenance_mode_active', true);
|
|
throw ValidationException::withMessages([
|
|
'name' => __('Login is currently disabled due to site maintenance. Only administrator accounts can access the site at this time. Please try again later.'),
|
|
]);
|
|
}
|
|
info('Auth allowed during maintenance: User ' . $user->name . ' has admin relationship.');
|
|
}
|
|
|
|
info('User identified: ' . $user->name . ' (ID: ' . $user->id . ') attempting login with identifier: ' . $request->auth);
|
|
$passwordVerified = false;
|
|
|
|
// Check for Cyclos password first if salt exists
|
|
if (!empty($user->cyclos_salt)) {
|
|
info('Attempting Cyclos password verification for user: ' . $user->name);
|
|
$concatenated = $user->cyclos_salt . $request->password;
|
|
$hashedInputPassword = hash("sha256", $concatenated);
|
|
|
|
if (strtolower($hashedInputPassword) === strtolower($user->password)) {
|
|
info('Cyclos password verified for user: ' . $user->name);
|
|
// Prepare user model for update: rehash password and clear salt
|
|
$user->password = Hash::make($request->password);
|
|
$user->cyclos_salt = null;
|
|
$passwordVerified = true;
|
|
info('Cyclos password prepared for rehashing for user: ' . $user->name);
|
|
} else {
|
|
info('Cyclos password verification failed for user: ' . $user->name . '. Will attempt standard Laravel hash check.');
|
|
}
|
|
}
|
|
|
|
// If not verified by Cyclos, or if cyclos_salt was empty, try Laravel's default hash check
|
|
if (!$passwordVerified && Hash::check($request->password, $user->password)) {
|
|
info('Laravel password verified for user: ' . $user->name);
|
|
$passwordVerified = true;
|
|
}
|
|
|
|
if ($passwordVerified) {
|
|
info('Auth success for user: ' . $user->name);
|
|
// Update last login details
|
|
$now = Carbon::now()->toDateTimeString();
|
|
$user->last_login_at = $now;
|
|
$user->last_login_ip = $request->getClientIp();
|
|
if (timebank_config('profile_inactive.re-activate_at_login')) {
|
|
if (!$user->isActive()) {
|
|
$user->inactive_at = null;
|
|
$activated = true;
|
|
}
|
|
}
|
|
// Save all changes (rehashed password, cleared salt, login details)
|
|
$user->save();
|
|
|
|
if (isset($activated) && $activated) {
|
|
info('User re-activated: ' . $user->name);
|
|
}
|
|
|
|
return $user;
|
|
}
|
|
|
|
info('Auth failed: Password mismatch for user: ' . $user->name . ' with identifier: ' . $request->auth);
|
|
return null;
|
|
});
|
|
|
|
|
|
}
|
|
}
|