refactor(auth): убрать одношаговый register (заменён start/verify)
Удалён старый POST /api/auth/register (метод + роут + RegisterRequest + 3 теста); регистрация теперь только через register/start → register/verify. SPA-роут страницы /register сохранён. Larastan baseline перегенерирован (counts AuthControllerTest 9→8 / 14→10 после удаления тестов). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,6 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Http\Requests\Auth\RegisterRequest;
|
||||
use App\Http\Requests\Auth\RegisterStartRequest;
|
||||
use App\Http\Requests\Auth\RegisterVerifyRequest;
|
||||
use App\Mail\RegisterEmailVerificationCode;
|
||||
@@ -147,36 +146,6 @@ class AuthController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request): JsonResponse
|
||||
{
|
||||
// На MVP — attach нового user'а к первому tenant'у (для UI-разводки).
|
||||
// Production: wizard с tenant_name + ИНН + создание Tenant + первый user owner-роли.
|
||||
$tenant = Tenant::first();
|
||||
if (! $tenant) {
|
||||
return response()->json([
|
||||
'message' => 'Tenants не настроены. Обратитесь к администратору.',
|
||||
], 503);
|
||||
}
|
||||
|
||||
$user = User::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'email' => $request->string('email')->toString(),
|
||||
'password_hash' => Hash::make($request->string('password')->toString()),
|
||||
'first_name' => 'Новый',
|
||||
'last_name' => 'Пользователь',
|
||||
'is_active' => true,
|
||||
'totp_enabled' => false,
|
||||
]);
|
||||
|
||||
Auth::login($user);
|
||||
$request->session()->regenerate();
|
||||
|
||||
return response()->json([
|
||||
'user' => $this->userResource($user),
|
||||
'requires_2fa' => false,
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Шаг 1 регистрации: валидирует форму, генерирует 6-значный код,
|
||||
* кладёт pending-данные в session, шлёт код письмом. Аккаунт НЕ создаётся.
|
||||
|
||||
@@ -8,7 +8,7 @@ trait HasPasswordRules
|
||||
{
|
||||
/**
|
||||
* Правила валидации поля password.
|
||||
* Используется в LoginRequest и RegisterRequest для DRY.
|
||||
* Используется в LoginRequest и RegisterStartRequest для DRY.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*/
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Валидация POST /api/auth/register.
|
||||
*
|
||||
* По ТЗ §1.5/§4.1: 2 обязательных click-wrap'а — оферта + согласие на ПДн
|
||||
* (3-й «маркетинговый» из handoff НЕ требуется — расхождение #2 реестра v1.13).
|
||||
*/
|
||||
class RegisterRequest extends FormRequest
|
||||
{
|
||||
use HasPasswordRules;
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')],
|
||||
'password' => $this->passwordRules(),
|
||||
'accept_offer' => ['required', 'accepted'],
|
||||
'accept_pdn' => ['required', 'accepted'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
public function messages(): array
|
||||
{
|
||||
return array_merge($this->passwordMessages(), [
|
||||
'email.required' => 'Укажите email.',
|
||||
'email.email' => 'Email указан некорректно.',
|
||||
'email.unique' => 'Аккаунт с таким email уже существует.',
|
||||
'accept_offer.accepted' => 'Необходимо принять оферту.',
|
||||
'accept_pdn.accepted' => 'Необходимо согласие на обработку персональных данных.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -627,7 +627,7 @@ parameters:
|
||||
-
|
||||
message: '#^Access to an undefined property Pest\\PendingCalls\\TestCall\:\:\$tenant\.$#'
|
||||
identifier: property.notFound
|
||||
count: 9
|
||||
count: 8
|
||||
path: tests/Feature/Auth/AuthControllerTest.php
|
||||
|
||||
-
|
||||
@@ -639,7 +639,7 @@ parameters:
|
||||
-
|
||||
message: '#^Call to an undefined method Pest\\PendingCalls\\TestCall\:\:postJson\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 14
|
||||
count: 10
|
||||
path: tests/Feature/Auth/AuthControllerTest.php
|
||||
|
||||
-
|
||||
|
||||
@@ -19,7 +19,6 @@ use Illuminate\Support\Facades\Route;
|
||||
// добавляется только к web-группе. См. laravel.com/docs/sanctum#spa-authentication.
|
||||
Route::prefix('/api/auth')->group(function () {
|
||||
Route::post('/login', 'App\Http\Controllers\Api\AuthController@login');
|
||||
Route::post('/register', 'App\Http\Controllers\Api\AuthController@register');
|
||||
Route::post('/register/start', 'App\Http\Controllers\Api\AuthController@registerStart');
|
||||
Route::post('/register/verify', 'App\Http\Controllers\Api\AuthController@registerVerify');
|
||||
Route::post('/register/resend', 'App\Http\Controllers\Api\AuthController@registerResend');
|
||||
|
||||
@@ -119,57 +119,6 @@ test('POST /api/auth/login обновляет last_login_at у user', function (
|
||||
expect($user->fresh()->last_login_at)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('POST /api/auth/register создаёт user + возвращает 201', function () {
|
||||
$response = $this->postJson('/api/auth/register', [
|
||||
'email' => 'new-signup@example.ru',
|
||||
'password' => 'fresh-pass-123',
|
||||
'accept_offer' => true,
|
||||
'accept_pdn' => true,
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
$response->assertJsonPath('user.email', 'new-signup@example.ru');
|
||||
$response->assertJsonPath('requires_2fa', false);
|
||||
|
||||
$user = User::where('email', 'new-signup@example.ru')->first();
|
||||
expect($user)->not->toBeNull();
|
||||
expect(Hash::check('fresh-pass-123', $user->password_hash))->toBeTrue();
|
||||
});
|
||||
|
||||
test('POST /api/auth/register отвергает существующий email (unique)', function () {
|
||||
User::factory()->create([
|
||||
'tenant_id' => $this->tenant->id,
|
||||
'email' => 'duplicate@example.ru',
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/api/auth/register', [
|
||||
'email' => 'duplicate@example.ru',
|
||||
'password' => 'any-password-123',
|
||||
'accept_offer' => true,
|
||||
'accept_pdn' => true,
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonValidationErrors(['email']);
|
||||
});
|
||||
|
||||
test('POST /api/auth/register требует accept_offer=true И accept_pdn=true (ТЗ §1.5/§4.1)', function () {
|
||||
$base = [
|
||||
'email' => 'no-consent@example.ru',
|
||||
'password' => 'fresh-pass-123',
|
||||
];
|
||||
|
||||
// Без оферты.
|
||||
$this->postJson('/api/auth/register', array_merge($base, ['accept_pdn' => true]))
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors(['accept_offer']);
|
||||
|
||||
// Без ПДн.
|
||||
$this->postJson('/api/auth/register', array_merge($base, ['accept_offer' => true]))
|
||||
->assertStatus(422)
|
||||
->assertJsonValidationErrors(['accept_pdn']);
|
||||
});
|
||||
|
||||
test('GET /api/auth/me возвращает 401 без авторизации', function () {
|
||||
$this->getJson('/api/auth/me')->assertStatus(401);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user