forked from gongxuegit/tiku-backend.net
feat(security): add distributed authorization foundation
This commit is contained in:
@@ -305,6 +305,123 @@ public sealed class AuthEndpointTests
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Wechat_first_login_is_rejected_without_self_registration_and_leaves_no_identity()
|
||||
{
|
||||
await using var factory = new ApiTestFactory(new FakeWechatOAuthClient());
|
||||
var tenantId = Guid.NewGuid();
|
||||
await SeedWechatProviderAsync(factory, tenantId,
|
||||
new TenantAuthPolicy
|
||||
{
|
||||
TenantId = tenantId,
|
||||
AllowExternalStudentSelfRegistration = false
|
||||
});
|
||||
using var client = factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("x-tenant-code", tenantId.ToString("N"));
|
||||
|
||||
var response = await client.PostAsJsonAsync(
|
||||
"/api/auth/oauth/wechat-miniapp",
|
||||
new OAuthCodeDto
|
||||
{
|
||||
Realm = AuthRealm.Tenant,
|
||||
TenantCode = tenantId.ToString("N"),
|
||||
Code = "wx-code"
|
||||
});
|
||||
using var scope = factory.CreateSystemScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
Assert.DoesNotContain(dbContext.UserIdentities, item => item.Provider == "wechat_miniapp");
|
||||
Assert.DoesNotContain(dbContext.TenantMemberships, item => item.TenantId == tenantId);
|
||||
Assert.DoesNotContain(dbContext.AuthSessions, item => item.TenantId == tenantId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(MembershipStatus.Invited)]
|
||||
[InlineData(MembershipStatus.Disabled)]
|
||||
public async Task Wechat_login_does_not_reactivate_non_active_membership(MembershipStatus status)
|
||||
{
|
||||
await using var factory = new ApiTestFactory(new FakeWechatOAuthClient());
|
||||
var tenantId = Guid.NewGuid();
|
||||
var user = new User { Id = Guid.NewGuid(), Name = "Existing Wechat User" };
|
||||
await SeedWechatProviderAsync(factory, tenantId,
|
||||
user,
|
||||
new UserIdentity
|
||||
{
|
||||
UserId = user.Id,
|
||||
Provider = "wechat_miniapp",
|
||||
ProviderSubject = "wx-app-id:mini-open-id",
|
||||
OpenId = "mini-open-id",
|
||||
UnionId = "union-id"
|
||||
},
|
||||
new TenantMembership
|
||||
{
|
||||
TenantId = tenantId,
|
||||
UserId = user.Id,
|
||||
Role = TenantRole.Student,
|
||||
Status = status
|
||||
});
|
||||
using var client = factory.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("x-tenant-code", tenantId.ToString("N"));
|
||||
|
||||
var response = await client.PostAsJsonAsync(
|
||||
"/api/auth/oauth/wechat-miniapp",
|
||||
new OAuthCodeDto
|
||||
{
|
||||
Realm = AuthRealm.Tenant,
|
||||
TenantCode = tenantId.ToString("N"),
|
||||
Code = "wx-code"
|
||||
});
|
||||
using var scope = factory.CreateSystemScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
|
||||
Assert.Equal(status, dbContext.TenantMemberships.Single(item =>
|
||||
item.TenantId == tenantId && item.UserId == user.Id).Status);
|
||||
Assert.DoesNotContain(dbContext.AuthSessions, item =>
|
||||
item.TenantId == tenantId && item.UserId == user.Id);
|
||||
}
|
||||
|
||||
private static async Task SeedWechatProviderAsync(
|
||||
ApiTestFactory factory,
|
||||
Guid tenantId,
|
||||
params object[] additionalEntities)
|
||||
{
|
||||
const string secretRef = "tenant_secrets:identity:wechat_miniapp:default";
|
||||
var protectedSecret = ProtectTenantSecret(
|
||||
tenantId,
|
||||
secretRef,
|
||||
JsonSerializer.SerializeToElement(new { appSecret = "wx-app-secret" }));
|
||||
var entities = new List<object>
|
||||
{
|
||||
new Tenant { Id = tenantId, Slug = tenantId.ToString("N"), Name = "Wechat Tenant" },
|
||||
new TenantExternalProvider
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Provider = "wechat_miniapp",
|
||||
Capability = TenantExternalProviderCapability.Identity,
|
||||
Status = TenantExternalProviderStatus.Active,
|
||||
SecretRef = secretRef,
|
||||
ConfigPublic = JsonSerializer.SerializeToElement(new { appId = "wx-app-id" })
|
||||
},
|
||||
new TenantSecret
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Purpose = "identity",
|
||||
Provider = "wechat_miniapp",
|
||||
SecretKey = "default",
|
||||
SecretRef = secretRef,
|
||||
Status = TenantSecretStatus.Active,
|
||||
EncryptionKeyId = protectedSecret.KeyId,
|
||||
EncryptedPayload = protectedSecret.Ciphertext,
|
||||
EncryptionNonce = protectedSecret.Nonce,
|
||||
EncryptionTag = protectedSecret.Tag
|
||||
}
|
||||
};
|
||||
entities.AddRange(additionalEntities);
|
||||
await factory.SeedAsync(entities.ToArray());
|
||||
}
|
||||
|
||||
private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedLoginUserAsync(
|
||||
ApiTestFactory factory)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user