diff --git a/app/app/Http/Middleware/SetTenantContext.php b/app/app/Http/Middleware/SetTenantContext.php index 62c0a286..466b6e00 100644 --- a/app/app/Http/Middleware/SetTenantContext.php +++ b/app/app/Http/Middleware/SetTenantContext.php @@ -51,7 +51,9 @@ class SetTenantContext private function resolveTenantId(Request $request): ?int { $user = $request->user(); - if ($user !== null && property_exists($user, 'tenant_id') && $user->tenant_id !== null) { + // Eloquent-атрибуты через __get не видны property_exists() — нужен isset() + // (вызывает __isset, который проверяет наличие атрибута через getAttribute). + if ($user !== null && isset($user->tenant_id) && $user->tenant_id !== null) { return (int) $user->tenant_id; } diff --git a/app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php b/app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php new file mode 100644 index 00000000..b335a251 --- /dev/null +++ b/app/app/Http/Requests/Auth/Concerns/HasPasswordRules.php @@ -0,0 +1,32 @@ + + */ + protected function passwordRules(): array + { + return ['required', 'string', 'min:8', 'max:255']; + } + + /** + * Сообщения об ошибках для password. + * + * @return array + */ + protected function passwordMessages(): array + { + return [ + 'password.required' => 'Укажите пароль.', + 'password.min' => 'Пароль должен быть не короче 8 символов.', + ]; + } +} diff --git a/app/app/Http/Requests/Auth/LoginRequest.php b/app/app/Http/Requests/Auth/LoginRequest.php index f3301035..127e7b9d 100644 --- a/app/app/Http/Requests/Auth/LoginRequest.php +++ b/app/app/Http/Requests/Auth/LoginRequest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Http\Requests\Auth; +use App\Http\Requests\Auth\Concerns\HasPasswordRules; use Illuminate\Foundation\Http\FormRequest; /** @@ -14,12 +15,14 @@ use Illuminate\Foundation\Http\FormRequest; */ class LoginRequest extends FormRequest { + use HasPasswordRules; + /** @return array */ public function rules(): array { return [ 'email' => ['required', 'string', 'email', 'max:255'], - 'password' => ['required', 'string', 'min:8', 'max:255'], + 'password' => $this->passwordRules(), 'remember' => ['sometimes', 'boolean'], ]; } @@ -27,11 +30,9 @@ class LoginRequest extends FormRequest /** @return array */ public function messages(): array { - return [ + return array_merge($this->passwordMessages(), [ 'email.required' => 'Укажите email.', 'email.email' => 'Email указан некорректно.', - 'password.required' => 'Укажите пароль.', - 'password.min' => 'Пароль должен быть не короче 8 символов.', - ]; + ]); } } diff --git a/app/app/Http/Requests/Auth/RegisterRequest.php b/app/app/Http/Requests/Auth/RegisterRequest.php index 0951244f..75f85b04 100644 --- a/app/app/Http/Requests/Auth/RegisterRequest.php +++ b/app/app/Http/Requests/Auth/RegisterRequest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Http\Requests\Auth; +use App\Http\Requests\Auth\Concerns\HasPasswordRules; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -15,12 +16,14 @@ use Illuminate\Validation\Rule; */ class RegisterRequest extends FormRequest { + use HasPasswordRules; + /** @return array */ public function rules(): array { return [ 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')], - 'password' => ['required', 'string', 'min:8', 'max:255'], + 'password' => $this->passwordRules(), 'accept_offer' => ['required', 'accepted'], 'accept_pdn' => ['required', 'accepted'], ]; @@ -29,13 +32,12 @@ class RegisterRequest extends FormRequest /** @return array */ public function messages(): array { - return [ + return array_merge($this->passwordMessages(), [ 'email.required' => 'Укажите email.', 'email.email' => 'Email указан некорректно.', 'email.unique' => 'Аккаунт с таким email уже существует.', - 'password.min' => 'Пароль должен быть не короче 8 символов.', 'accept_offer.accepted' => 'Необходимо принять оферту.', 'accept_pdn.accepted' => 'Необходимо согласие на обработку персональных данных.', - ]; + ]); } } diff --git a/app/routes/web.php b/app/routes/web.php index 7d434413..ec97a9aa 100644 --- a/app/routes/web.php +++ b/app/routes/web.php @@ -41,7 +41,7 @@ Route::prefix('/api/auth')->group(function () { // In-app уведомления (P0 этап 2b). Все endpoint'ы под Sanctum SPA auth — // уведомления USER-personal, читать/писать может только сам user. -Route::middleware('auth:sanctum')->prefix('/api/notifications')->group(function () { +Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/notifications')->group(function () { Route::get('/', [InAppNotificationController::class, 'index']); Route::patch('/{id}/read', [InAppNotificationController::class, 'markRead'])->where('id', '[0-9]+'); Route::post('/mark-all-read', [InAppNotificationController::class, 'markAllRead']); @@ -49,7 +49,7 @@ Route::middleware('auth:sanctum')->prefix('/api/notifications')->group(function }); // Reminders (P0 этап 4) — schema v8.10 §17.5. Auth обязательный. -Route::middleware('auth:sanctum')->prefix('/api/reminders')->group(function () { +Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/reminders')->group(function () { Route::get('/', [ReminderController::class, 'index']); Route::post('/', [ReminderController::class, 'store']); Route::patch('/{id}', [ReminderController::class, 'update'])->where('id', '[0-9]+'); @@ -60,7 +60,7 @@ Route::middleware('auth:sanctum')->prefix('/api/reminders')->group(function () { // Reports backend. Schema §13.5 report_jobs. Auth обязательный. // Этапы 1+2 (CRUD + provider/formatter) + этап 3 (retry/cancel/delete + // retention cron `reports:cleanup-expired`). -Route::middleware('auth:sanctum')->prefix('/api/reports/jobs')->group(function () { +Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/reports/jobs')->group(function () { Route::get('/', [ReportJobController::class, 'index']); Route::post('/', [ReportJobController::class, 'store']); Route::get('/{id}', [ReportJobController::class, 'show'])->where('id', '[0-9]+'); diff --git a/app/tests/Feature/AdminIncidentsIndexTest.php b/app/tests/Feature/AdminIncidentsIndexTest.php index 2aff6816..5b3a813f 100644 --- a/app/tests/Feature/AdminIncidentsIndexTest.php +++ b/app/tests/Feature/AdminIncidentsIndexTest.php @@ -13,7 +13,7 @@ beforeEach(function () { $this->adminId = (int) DB::table('saas_admin_users')->insertGetId([ 'email' => 'admin-'.bin2hex(random_bytes(3)).'@test', 'full_name' => 'Admin Test', - 'password_hash' => bcrypt('test'), + 'password_hash' => bcrypt('test1234'), 'is_active' => true, 'role' => 'support', 'created_at' => now(),