forked from gongxuegit/tiku-backend.net
feat: enforce tenant isolation and shared question bank
This commit is contained in:
@@ -13,6 +13,60 @@ namespace Tiku.IntegrationTests.Api;
|
||||
|
||||
public sealed class AuthEndpointTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Custom_host_rejects_jwt_from_another_tenant_and_ignores_spoofed_tenant_header()
|
||||
{
|
||||
await using var factory = new ApiTestFactory();
|
||||
var tenantB = await SeedLoginUserAsync(factory);
|
||||
var tenantAId = Guid.NewGuid();
|
||||
await factory.SeedAsync(
|
||||
new Tenant
|
||||
{
|
||||
Id = tenantAId,
|
||||
Slug = "tenant-a",
|
||||
Name = "Tenant A",
|
||||
Status = TenantStatus.Active,
|
||||
Mode = TenantMode.Saas
|
||||
},
|
||||
new TenantDomain
|
||||
{
|
||||
TenantId = tenantAId,
|
||||
Host = "a.example.test",
|
||||
DomainType = TenantDomainType.Custom,
|
||||
Status = TenantDomainStatus.Active,
|
||||
IsPrimary = true
|
||||
});
|
||||
using var client = factory.CreateClient();
|
||||
var loginResponse = await client.PostAsJsonAsync(
|
||||
"/api/auth/login/password",
|
||||
new PasswordLoginDto
|
||||
{
|
||||
TenantCode = tenantB.TenantId.ToString("N"),
|
||||
Phone = tenantB.Phone,
|
||||
Password = "passw0rd!"
|
||||
});
|
||||
var loginJson = await ReadJsonAsync(loginResponse);
|
||||
var accessToken = loginJson.RootElement.GetProperty("tokens").GetProperty("accessToken").GetString();
|
||||
|
||||
using var jwtRequest = new HttpRequestMessage(HttpMethod.Get, "/api/me");
|
||||
jwtRequest.Headers.Host = "a.example.test";
|
||||
jwtRequest.Headers.Authorization = new("Bearer", accessToken);
|
||||
var jwtResponse = await client.SendAsync(jwtRequest);
|
||||
|
||||
using var spoofRequest = new HttpRequestMessage(HttpMethod.Post, "/api/auth/login/password");
|
||||
spoofRequest.Headers.Host = "a.example.test";
|
||||
spoofRequest.Headers.Add("x-tenant-code", tenantB.TenantId.ToString("N"));
|
||||
spoofRequest.Content = JsonContent.Create(new PasswordLoginDto
|
||||
{
|
||||
Phone = tenantB.Phone,
|
||||
Password = "passw0rd!"
|
||||
});
|
||||
var spoofResponse = await client.SendAsync(spoofRequest);
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, jwtResponse.StatusCode);
|
||||
Assert.NotEqual(HttpStatusCode.OK, spoofResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Password_login_can_access_current_user_and_tenant()
|
||||
{
|
||||
@@ -24,7 +78,7 @@ public sealed class AuthEndpointTests
|
||||
"/api/auth/login/password",
|
||||
new PasswordLoginDto
|
||||
{
|
||||
TenantId = seed.TenantId,
|
||||
TenantCode = seed.TenantId.ToString("N"),
|
||||
Phone = seed.Phone,
|
||||
Password = "passw0rd!"
|
||||
});
|
||||
@@ -57,7 +111,7 @@ public sealed class AuthEndpointTests
|
||||
"/api/auth/login/sms",
|
||||
new SmsLoginDto
|
||||
{
|
||||
TenantId = seed.TenantId,
|
||||
TenantCode = seed.TenantId.ToString("N"),
|
||||
Phone = seed.Phone,
|
||||
Code = "123456"
|
||||
});
|
||||
@@ -84,7 +138,7 @@ public sealed class AuthEndpointTests
|
||||
"/api/auth/login/password",
|
||||
new PasswordLoginDto
|
||||
{
|
||||
TenantId = seed.TenantId,
|
||||
TenantCode = seed.TenantId.ToString("N"),
|
||||
Phone = seed.Phone,
|
||||
Password = "passw0rd!"
|
||||
});
|
||||
@@ -136,7 +190,7 @@ public sealed class AuthEndpointTests
|
||||
"/api/auth/oauth/wechat-miniapp",
|
||||
new OAuthCodeDto
|
||||
{
|
||||
TenantId = tenantId,
|
||||
TenantCode = tenantId.ToString("N"),
|
||||
Code = "wx-code"
|
||||
});
|
||||
var loginJson = await ReadJsonAsync(loginResponse);
|
||||
@@ -147,7 +201,7 @@ public sealed class AuthEndpointTests
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new("Bearer", accessToken);
|
||||
var meResponse = await client.GetAsync("/api/me");
|
||||
using var scope = factory.Services.CreateScope();
|
||||
using var scope = factory.CreateSystemScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, loginResponse.StatusCode);
|
||||
@@ -204,7 +258,7 @@ public sealed class AuthEndpointTests
|
||||
string phone,
|
||||
string code)
|
||||
{
|
||||
using var scope = factory.Services.CreateScope();
|
||||
using var scope = factory.CreateSystemScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
dbContext.SmsVerificationCodes.Add(new SmsVerificationCode
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user