Files
timebank-cc-public/app/Http/Requests/ProfileEmailVerificationRequest.php
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

194 lines
7.6 KiB
PHP

<?php
namespace App\Http\Requests;
use App\Events\ProfileVerified;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
class ProfileEmailVerificationRequest extends FormRequest
{
public $profileModel;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$type = $this->route('type');
$profileId = $this->route('id');
$modelClass = 'App\\Models\\' . \Illuminate\Support\Str::studly($type);
if (!class_exists($modelClass)) {
Log::warning("Authorization failed: Model class does not exist - {$modelClass}");
return false;
}
$profileModel = $modelClass::find($profileId);
if (!$profileModel) {
Log::warning("Authorization failed: Profile model not found - {$modelClass} with ID {$profileId}");
return false;
}
// Check all guards to find authenticated user
$loggedInUser = $this->user(); // Try default web guard first
// If not on web guard, check other guards
if (!$loggedInUser) {
$guards = ['organization', 'bank', 'admin'];
foreach ($guards as $guard) {
$loggedInUser = auth()->guard($guard)->user();
if ($loggedInUser) {
Log::info("Found authenticated user on '{$guard}' guard", [
'user_id' => $loggedInUser->id,
'user_type' => get_class($loggedInUser),
]);
break;
}
}
}
if (!$loggedInUser) {
Log::warning("Authorization failed: No authenticated user on any guard.");
return false;
}
$isAssociated = false; // Flag to track if user is associated with the profile
$emailToVerify = null;
// --- Check Association and Get Email ---
// First check: Is the logged-in entity the same as the profile being verified?
if (get_class($loggedInUser) === get_class($profileModel) && $loggedInUser->id === $profileModel->id) {
// User is verifying their own profile (e.g., Organization verifying Organization email)
$isAssociated = true;
$emailToVerify = method_exists($profileModel, 'getEmailForVerification')
? $profileModel->getEmailForVerification()
: $profileModel->email;
Log::info("Authorization: Logged-in entity is verifying own email", [
'entity_type' => get_class($profileModel),
'entity_id' => $profileModel->id,
]);
} elseif ($profileModel instanceof \App\Models\User) {
// Case 1: Profile is a User model (and logged-in user is different)
if ($loggedInUser instanceof \App\Models\User && $profileModel->id === $loggedInUser->id) {
$isAssociated = true;
$emailToVerify = $loggedInUser->getEmailForVerification();
} else {
Log::warning("Authorization failed: User ID mismatch. Profile ID: {$profileModel->id}, Logged-in User ID: {$loggedInUser->id}");
}
} else {
// Case 2: Profile is Organization, Bank, Admin, etc. and logged-in user is a User
if ($loggedInUser instanceof \App\Models\User) {
// Check 'users' relationship first
if (method_exists($profileModel, 'users')) {
if ($profileModel->users()->whereKey($loggedInUser->id)->exists()) {
$isAssociated = true;
}
}
// If not associated via 'users', check 'managers' relationship
elseif (method_exists($profileModel, 'managers')) {
if ($profileModel->managers()->whereKey($loggedInUser->id)->exists()) {
$isAssociated = true;
}
}
if ($isAssociated) {
// Use the profileModel's email for verification
$emailToVerify = method_exists($profileModel, 'getEmailForVerification')
? $profileModel->getEmailForVerification()
: $profileModel->email;
} else {
Log::warning("Authorization failed: User ID {$loggedInUser->id} not associated with " . class_basename($profileModel) . " ID {$profileModel->id} via users() or managers().");
}
} else {
Log::warning("Authorization failed: Logged-in entity is not a User and doesn't match profile", [
'logged_in_type' => get_class($loggedInUser),
'logged_in_id' => $loggedInUser->id,
'profile_type' => get_class($profileModel),
'profile_id' => $profileModel->id,
]);
}
}
// If user is not associated, deny authorization
if (!$isAssociated) {
return false;
}
// --- Compare Hash ---
if ($emailToVerify === null) {
Log::error("Authorization failed: Could not determine email to verify for " . class_basename($profileModel) . " ID {$profileModel->id}");
return false; // Should not happen if $isAssociated is true, but safety check
}
$routeHash = (string) $this->route('hash');
if (!hash_equals(sha1($emailToVerify), $routeHash)) {
Log::error("Email hash mismatch for " . class_basename($profileModel) . " ID {$profileModel->id}. Email: {$emailToVerify}, Route Hash: {$routeHash}");
return false;
}
// --- Authorization Successful ---
$this->profileModel = $profileModel;
Log::info("Authorization successful for email verification of " . class_basename($profileModel) . " id: " . $profileModel->id . " by user id: " . $loggedInUser->id );
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
/**
* Fulfill the email verification request.
*
* @return void
*/
public function fulfill()
{
Log::info('ProfileEmailVerificationRequest::fulfill() called', [
'profileModel_class' => $this->profileModel ? get_class($this->profileModel) : 'null',
'profileModel_id' => $this->profileModel?->id,
'hasVerifiedEmail' => $this->profileModel?->hasVerifiedEmail(),
'email_verified_at_before' => $this->profileModel?->email_verified_at,
]);
if (!$this->profileModel->hasVerifiedEmail()) {
$result = $this->profileModel->markEmailAsVerified();
Log::info('ProfileEmailVerificationRequest::fulfill() markEmailAsVerified result', [
'result' => $result,
'email_verified_at_after' => $this->profileModel->fresh()->email_verified_at,
]);
event(new ProfileVerified($this->profileModel));
Log::info('ProfileEmailVerificationRequest::fulfill() ProfileVerified event dispatched');
} else {
Log::info('ProfileEmailVerificationRequest::fulfill() email already verified, skipping');
}
}
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return \Illuminate\Validation\Validator
*/
public function withValidator(Validator $validator)
{
return $validator;
}
}