diff --git a/README.md b/README.md index 2ef2a3d..f095ae5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,11 @@ administrator. That account can sign in at `/manager/sign-in`, create businesses, edit their primary domains, and share the one-time owner setup link generated during provisioning. +For an existing installation created before this platform/tenant split, the +recorded `initialAdminUserId` account is allowed to sign in once and is +promoted to the platform administrator. No other business owner can use that +upgrade path. + Business provisioning creates the tenant domain, settings, booking rules, opening hours, system roles, owner staff profile, and seven-day owner invitation in one transaction. The owner chooses a password through that diff --git a/app/Http/Controllers/PlatformAuthController.php b/app/Http/Controllers/PlatformAuthController.php index 2a8ad89..205a6fa 100644 --- a/app/Http/Controllers/PlatformAuthController.php +++ b/app/Http/Controllers/PlatformAuthController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers; use App\Enums\InstallationStatus; use App\Models\ApplicationInstallation; +use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -31,12 +32,34 @@ class PlatformAuthController extends Controller 'password' => ['required', 'string'], ]); - if (! Auth::attempt($credentials, $request->boolean('remember')) || ! $request->user()?->isSuperAdmin) { + if (! Auth::attempt($credentials, $request->boolean('remember'))) { Auth::logout(); return back()->withErrors(['email' => 'Those platform administrator details do not match our records.'])->onlyInput('email'); } + $user = $request->user(); + if (! $user?->isSuperAdmin) { + $installation = ApplicationInstallation::query() + ->where('status', InstallationStatus::Installed) + ->first(); + + $canUpgradeLegacyAdmin = ! User::query()->where('isSuperAdmin', true)->exists() + && $installation?->initialAdminUserId === $user?->id; + + if (! $canUpgradeLegacyAdmin) { + Auth::logout(); + + return back()->withErrors(['email' => 'Those platform administrator details do not match our records.'])->onlyInput('email'); + } + + // Existing installations created before platform administration + // was separated may have only an initial business owner. Promote + // that exact recorded account once so the migration is usable. + $user->forceFill(['isSuperAdmin' => true])->save(); + Auth::setUser($user->fresh()); + } + $request->session()->regenerate(); return redirect()->intended(route('manager.dashboard')); diff --git a/tests/Feature/InstallationTest.php b/tests/Feature/InstallationTest.php index 6d1b8a3..ed8075c 100644 --- a/tests/Feature/InstallationTest.php +++ b/tests/Feature/InstallationTest.php @@ -49,4 +49,22 @@ class InstallationTest extends TestCase $this->assertDatabaseMissing('User', ['email' => 'another@example.test']); } + + public function test_an_existing_installation_can_upgrade_its_recorded_initial_admin_once(): void + { + $legacyOwner = User::factory()->create(['isSuperAdmin' => false, 'passwordHash' => password_hash('password123', PASSWORD_BCRYPT)]); + ApplicationInstallation::create([ + 'id' => 'singleton', + 'status' => 'INSTALLED', + 'installedAt' => now(), + 'initialAdminUserId' => $legacyOwner->id, + ]); + + $this->post('/manager/sign-in', [ + 'email' => $legacyOwner->email, + 'password' => 'password123', + ])->assertRedirect(route('manager.dashboard')); + + $this->assertDatabaseHas('User', ['id' => $legacyOwner->id, 'isSuperAdmin' => 1]); + } }