212 lines
8.0 KiB
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;
|
|
}
|
|
}
|