feat(security): add distributed authorization foundation

This commit is contained in:
2026-07-29 10:40:10 +08:00
parent c7f9a4e3c9
commit df88fa19cb
76 changed files with 22020 additions and 88 deletions

View File

@@ -15,6 +15,7 @@ using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;
using Tiku.Infrastructure.Security;
using Tiku.Infrastructure.Messaging;
using CommerceRefundStatus = Tiku.Domain.Commerce.CommerceRefundStatus;
using OrderStatus = Tiku.Domain.Commerce.OrderStatus;
using PointActivityClaimStatus = Tiku.Domain.Commerce.PointActivityClaimStatus;
@@ -28,7 +29,8 @@ public sealed class TenantAdminDirectService(
ITenantExternalProviderConfigService providerConfigService,
INotificationProvider notificationProvider,
ICurrentAccessContext currentAccessContext,
IAuthSessionStore sessionStore) : ITenantAdminDirectService
IAuthSessionStore sessionStore,
ISecurityEventPublisher securityEventPublisher) : ITenantAdminDirectService
{
public async Task<TenantAdminOverviewItem> GetOverviewAsync(
TenantAdminActor actor,
@@ -1154,6 +1156,7 @@ public sealed class TenantAdminDirectService(
}
var isNew = membership is null;
var previousStatus = membership?.Status.ToString() ?? "none";
if (membership?.Role == TenantRole.TenantOwner && role != TenantRole.TenantOwner)
{
throw new TenantAdminDirectException("Tenant owner membership cannot be downgraded.", "tenant_owner_required");
@@ -1182,6 +1185,9 @@ public sealed class TenantAdminDirectService(
}
await AddAuditAsync(actor, "tenant.member.upserted", "tenant_memberships", membership.Id, cancellationToken);
await securityEventPublisher.MembershipChangedAsync(
actor.TenantId, user.Id, previousStatus, status.ToString(),
$"tenant-member-{membership.Id:N}", cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken);
return new ContentManagementResult<TenantAdminMemberItem>(ToMemberItem(membership, user));
}
@@ -1206,9 +1212,13 @@ public sealed class TenantAdminDirectService(
}
await AssertGrantableAsync(actor, membership.Role, cancellationToken);
var previousStatus = membership.Status;
membership.Status = MembershipStatus.Disabled;
await RevokeSessionsAsync(actor.TenantId, membership.UserId, cancellationToken);
await AddAuditAsync(actor, "tenant.member.disabled", "tenant_memberships", membership.Id, cancellationToken);
await securityEventPublisher.MembershipChangedAsync(
actor.TenantId, membership.UserId, previousStatus.ToString(), MembershipStatus.Disabled.ToString(),
$"tenant-member-{membership.Id:N}", cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken);
var user = await dbContext.Users.AsNoTracking().SingleAsync(item => item.Id == membership.UserId, cancellationToken);
return new ContentManagementResult<TenantAdminMemberItem>(ToMemberItem(membership, user));