'Acme Corporation', 'contact_name' => 'Acme Admin', 'email' => $inquiryEmail, 'desired_subdomain' => 'acme-test-domain', 'employee_count' => 25, 'requested_features' => ['payroll', 'geofence'], 'email_verified_at' => now(), 'status' => 'approved', ]); $job = new ProvisionTenantJob($inquiry); $job->handle(); $tenant = Tenant::find('acme-test-domain'); $this->assertNotNull($tenant); // Verify that inside the tenant database context: // 1. The admin user exists ONLY under $inquiryEmail (the email entered in the inquiry wizard). // 2. The admin user can authenticate with password '12345678'. $tenant->run(function () use ($inquiryEmail) { $user = \App\Models\User::where('email', $inquiryEmail)->first(); $this->assertNotNull($user, "Tenant admin user with email {$inquiryEmail} was not found in tenant database."); // Assert password verifies against default password '12345678' $this->assertTrue(\Illuminate\Support\Facades\Hash::check('12345678', $user->password)); // Assert non-registered email does not exist in this tenant's isolated database $unregisteredUser = \App\Models\User::where('email', 'unregistered.other@domain.com')->first(); $this->assertNull($unregisteredUser); }); } public function test_subdomains_have_isolated_databases_and_credentials(): void { // Tenant A $inquiryA = Inquiry::create([ 'company_name' => 'Company A', 'contact_name' => 'Admin A', 'email' => 'admin@companya.com', 'desired_subdomain' => 'companya-tenant', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryA))->handle(); // Tenant B $inquiryB = Inquiry::create([ 'company_name' => 'Company B', 'contact_name' => 'Admin B', 'email' => 'admin@companyb.com', 'desired_subdomain' => 'companyb-tenant', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryB))->handle(); // Verify Tenant A DB contains Admin A $tenantA = Tenant::find('companya-tenant'); $this->assertNotNull($tenantA); $tenantA->run(function () { $this->assertNotNull(\App\Models\User::where('email', 'admin@companya.com')->first()); }); // Verify Tenant B DB contains Admin B $tenantB = Tenant::find('companyb-tenant'); $this->assertNotNull($tenantB); $tenantB->run(function () { $this->assertNotNull(\App\Models\User::where('email', 'admin@companyb.com')->first()); }); } public function test_real_http_domain_cross_tenant_login_rejection(): void { // 1. Provision Subdomain A (admin@companya.com / 12345678) $inquiryA = Inquiry::create([ 'company_name' => 'Company A', 'contact_name' => 'Admin A', 'email' => 'admin@companya.com', 'desired_subdomain' => 'subdomain-a', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryA))->handle(); // 2. Provision Subdomain B (admin@companyb.com / 12345678) $inquiryB = Inquiry::create([ 'company_name' => 'Company B', 'contact_name' => 'Admin B', 'email' => 'admin@companyb.com', 'desired_subdomain' => 'subdomain-b', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryB))->handle(); if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $domainHost = config('app.url_domain', 'localhost'); // 3. Real HTTP POST to Subdomain A (/login) with Subdomain B credentials -> Fails validation / 401 / redirect $tenantA = \App\Models\Tenant::find('subdomain-a'); tenancy()->initialize($tenantA); $responseAToB = $this->withServerVariables(['HTTP_HOST' => "subdomain-a.{$domainHost}"]) ->post("http://subdomain-a.{$domainHost}/login", [ 'email' => 'admin@companyb.com', 'password' => '12345678', ]); $this->assertTrue( $responseAToB->isRedirect() || $responseAToB->status() === 422 || $responseAToB->status() === 401 ); $this->assertGuest(); // 4. Real HTTP POST to Subdomain B (/login) with Subdomain A credentials -> Fails validation / 401 / redirect if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $tenantB = \App\Models\Tenant::find('subdomain-b'); tenancy()->initialize($tenantB); $responseBToA = $this->withServerVariables(['HTTP_HOST' => "subdomain-b.{$domainHost}"]) ->post("http://subdomain-b.{$domainHost}/login", [ 'email' => 'admin@companya.com', 'password' => '12345678', ]); $this->assertTrue( $responseBToA->isRedirect() || $responseBToA->status() === 422 || $responseBToA->status() === 401 ); $this->assertGuest(); } public function test_subdomain_a_rejects_subdomain_b_credentials_without_manual_tenant_initialization(): void { $inquiryA = Inquiry::create([ 'company_name' => 'Company A', 'contact_name' => 'Admin A', 'email' => 'admin@subdomain-a.test', 'desired_subdomain' => 'strict-subdomain-a', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryA))->handle(); $inquiryB = Inquiry::create([ 'company_name' => 'Company B', 'contact_name' => 'Admin B', 'email' => 'admin@subdomain-b.test', 'desired_subdomain' => 'strict-subdomain-b', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryB))->handle(); if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $domainHost = config('app.url_domain', 'localhost'); $response = $this->withServerVariables([ 'HTTP_HOST' => "strict-subdomain-a.{$domainHost}", ])->post("http://strict-subdomain-a.{$domainHost}/login", [ 'email' => 'admin@subdomain-b.test', 'password' => '12345678', ]); $this->assertGuest(); $this->assertNotSame( '/dashboard', parse_url($response->headers->get('Location', ''), PHP_URL_PATH), 'Cross-tenant credentials must not redirect to the authenticated dashboard.' ); } public function test_real_subdomain_login_accepts_only_the_subdomain_credentials(): void { $inquiryA = Inquiry::create([ 'company_name' => 'Company A', 'contact_name' => 'Admin A', 'email' => 'admin@real-a.test', 'desired_subdomain' => 'real-subdomain-a', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryA))->handle(); $inquiryB = Inquiry::create([ 'company_name' => 'Company B', 'contact_name' => 'Admin B', 'email' => 'admin@real-b.test', 'desired_subdomain' => 'real-subdomain-b', 'email_verified_at' => now(), 'status' => 'approved', ]); (new ProvisionTenantJob($inquiryB))->handle(); if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $domainHost = config('app.url_domain', 'localhost'); $request = fn (string $subdomain, string $email) => $this ->withServerVariables(['HTTP_HOST' => "{$subdomain}.{$domainHost}"]) ->post("http://{$subdomain}.{$domainHost}/login", [ 'email' => $email, 'password' => '12345678', ]); $aWithA = $request('real-subdomain-a', 'admin@real-a.test'); $this->assertSame( '/dashboard', parse_url($aWithA->headers->get('Location', ''), PHP_URL_PATH), 'Subdomain A credentials must authenticate on Subdomain A.' ); $this->app['auth']->logout(); if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $aWithB = $request('real-subdomain-a', 'admin@real-b.test'); $this->assertGuest(); $this->assertNotSame('/dashboard', parse_url($aWithB->headers->get('Location', ''), PHP_URL_PATH)); if (function_exists('tenancy') && tenancy()->initialized) { tenancy()->end(); } $bWithA = $request('real-subdomain-b', 'admin@real-a.test'); $this->assertGuest(); $this->assertNotSame('/dashboard', parse_url($bWithA->headers->get('Location', ''), PHP_URL_PATH)); } }