Files
timebank-cc-public/app/Http/Controllers/ResetNonUserPasswordController.php

212 lines
8.0 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Validator;
use App\Models\Admin;
use App\Models\Bank;
use App\Models\Organization;
use Illuminate\Support\Facades\Auth;
class ResetNonUserPasswordController extends Controller
{
/**
* Display the form to request a password reset link.
*/
public function showLinkRequestForm(Request $request, $profileType)
{
$profileId = $request->query('profileId');
if ($profileId) {
$profile = $this->resolveProfileById($profileType, $profileId);
if ($profile && !empty($profile->password_change_disabled)) {
return redirect()->back()->withErrors(['email' => __('Password changes are disabled for this account.')]);
}
// Block if the user's role for this profile does not allow password changes
$user = Auth::guard('web')->user();
if ($user && !$this->userRoleAllowsPasswordChange($user, $profileType, $profileId)) {
return redirect()->back()->withErrors(['email' => __('Password changes are disabled for this account.')]);
}
}
return view('auth.forgot-non-user-password', [
'profileType' => $profileType,
'profileId' => $profileId,
]);
}
/**
* Handle sending the password reset link.
*/
public function sendResetLinkEmail(Request $request, $profileType)
{
$validator = Validator::make($request->all(), ['email' => 'required|email']);
if ($validator->fails()) {
return back()->withErrors($validator);
}
// Resolve profile and check password_change_disabled + role permission
$profileId = $request->input('profileId');
$profile = $profileId
? $this->resolveProfileById($profileType, $profileId)
: $this->resolveProfileByEmail($profileType, $request->input('email'));
if ($profile && !empty($profile->password_change_disabled)) {
return back()->withErrors(['email' => __('Password changes are disabled for this account.')]);
}
$user = Auth::guard('web')->user();
$resolvedId = $profileId ?? ($profile ? $profile->id : null);
if ($user && $resolvedId && !$this->userRoleAllowsPasswordChange($user, $profileType, $resolvedId)) {
return back()->withErrors(['email' => __('Password changes are disabled for this account.')]);
}
$broker = $this->getPasswordBroker($profileType);
$status = Password::broker($broker)->sendResetLink(
$request->only('email')
);
return $status === Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withErrors(['email' => __($status)]);
}
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*/
public function showResetForm(Request $request, $profileType, $token = null)
{
if (is_null($token)) {
return $this->showLinkRequestForm($profileType);
}
$email = $request->query('email');
return view('auth.reset-non-user-password', [
'token' => $token,
'email' => $email,
'profileType' => $profileType
]);
}
/**
* Reset the given profile's password.
*/
public function reset(Request $request, $profileType)
{
// Dynamically get the password validation rules from the config
$passwordRules = timebank_config('rules.profile_' . strtolower($profileType) . '.password', ['required', 'string', 'min:8', 'confirmed']);
$validator = Validator::make($request->all(), [
'token' => 'required',
'email' => 'required|email',
'password' => $passwordRules,
]);
if ($validator->fails()) {
// Store errors in a persistent (non-flash) session key so Livewire
// background requests don't consume the flash before the browser arrives.
session(['password_reset_errors' => $validator->errors()->toArray()]);
return redirect(url()->previous())->withInput($request->only('email'));
}
$broker = $this->getPasswordBroker($profileType);
$passwordChangeDisabled = false;
$status = Password::broker($broker)->reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($profile, $password) use (&$passwordChangeDisabled) {
if (!empty($profile->password_change_disabled)) {
$passwordChangeDisabled = true;
return;
}
$profile->forceFill([
'password' => Hash::make($password),
])->save();
}
);
if ($passwordChangeDisabled) {
session(['password_reset_errors' => ['password' => [__('Password changes are disabled for this account.')]]]);
return redirect(url()->previous())->withInput($request->only('email'));
}
if ($status === Password::PASSWORD_RESET) {
session()->forget('password_reset_errors');
$loginPath = parse_url(route('login'), PHP_URL_PATH);
return redirect($loginPath)->with('status', __('passwords.reset'));
}
session(['password_reset_errors' => ['email' => [__($status)]]]);
return redirect(url()->previous())->withInput($request->only('email'));
}
/**
* Get the password broker for the given profile type.
*/
private function getPasswordBroker($profileType)
{
// Ensure this maps to the keys in config/auth.php 'passwords'
$brokers = [
'admin' => 'admins',
'bank' => 'banks',
// 'organization' => 'organizations', // etc.
];
// Fallback to 'users' broker if profileType doesn't match,
// or handle as an error if only specific profile types are allowed here.
return $brokers[strtolower($profileType)] ?? 'users';
}
private function resolveProfileById($profileType, $id)
{
$modelMap = [
'admin' => Admin::class,
'bank' => Bank::class,
'organization' => Organization::class,
];
$model = $modelMap[strtolower($profileType)] ?? null;
return $model ? $model::find((int) $id) : null;
}
private function resolveProfileByEmail($profileType, $email)
{
$modelMap = [
'admin' => Admin::class,
'bank' => Bank::class,
'organization' => Organization::class,
];
$model = $modelMap[strtolower($profileType)] ?? null;
return $model ? $model::where('email', $email)->first() : null;
}
private function userRoleAllowsPasswordChange($user, string $profileType, $profileId): bool
{
$typeMap = [
'admin' => ['prefix' => 'Admin', 'model' => \App\Models\AdminRole::class, 'permission' => 'admin change-password'],
'bank' => ['prefix' => 'Bank', 'model' => \App\Models\OrganizationRole::class, 'permission' => 'org change-password'],
'organization' => ['prefix' => 'Organization', 'model' => \App\Models\OrganizationRole::class, 'permission' => 'org change-password'],
];
$config = $typeMap[strtolower($profileType)] ?? null;
if (!$config) {
return true;
}
$prefix = "{$config['prefix']}\\{$profileId}\\";
$scopedRoles = $user->roles->filter(fn($r) => str_starts_with($r->name, $prefix));
foreach ($scopedRoles as $spatieRole) {
$roleName = str_replace($prefix, '', $spatieRole->name);
$role = $config['model']::where('name', $roleName)->first();
if ($role && in_array($config['permission'], $role->permissions ?? [])) {
return true;
}
}
return false;
}
}