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; } }