Files
tiku-backend.net/Tiku.IntegrationTests/Api/RbacAuthorizationTests.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

238 lines
8.9 KiB
C#

using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Api.Security;
using Tiku.Application.Security;
namespace Tiku.IntegrationTests.Api;
public sealed class RbacAuthorizationTests
{
[Fact]
public async Task TenantPolicy_requires_current_membership_and_permission()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var snapshot = Snapshot(
userId,
tenantId,
[BackendPermissions.TenantRoleManage]);
await using var provider = Services(snapshot);
var authorization = provider.GetRequiredService<IAuthorizationService>();
var allowed = await authorization.AuthorizeAsync(
Principal(userId, "tenant", tenantId),
null,
BackendPermissions.TenantRoleManage);
var wrongTenant = await authorization.AuthorizeAsync(
Principal(userId, "tenant", Guid.NewGuid()),
null,
BackendPermissions.TenantRoleManage);
Assert.True(allowed.Succeeded);
Assert.False(wrongTenant.Succeeded);
}
[Fact]
public async Task PlatformPolicy_RejectsTenantRealmEvenWhenUserHasPlatformPermission()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var snapshot = Snapshot(
userId,
tenantId,
platformPermissions: [BackendPermissions.PlatformRoleManage]);
await using var provider = Services(snapshot);
var authorization = provider.GetRequiredService<IAuthorizationService>();
var tenantRealm = await authorization.AuthorizeAsync(
Principal(userId, "tenant", tenantId),
null,
BackendPermissions.PlatformRoleManage);
var platformRealm = await authorization.AuthorizeAsync(
Principal(userId, "platform", null),
null,
BackendPermissions.PlatformRoleManage);
Assert.False(tenantRealm.Succeeded);
Assert.True(platformRealm.Succeeded);
}
[Fact]
public async Task PermissionPolicy_DoesNotUseJwtRoleClaims()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var snapshot = Snapshot(userId, tenantId);
await using var provider = Services(snapshot);
var authorization = provider.GetRequiredService<IAuthorizationService>();
var principal = Principal(userId, "tenant", tenantId);
((ClaimsIdentity)principal.Identity!).AddClaim(new Claim(ClaimTypes.Role, "TenantOwner"));
var result = await authorization.AuthorizeAsync(
principal,
null,
BackendPermissions.TenantRoleManage);
Assert.False(result.Succeeded);
}
[Fact]
public async Task AllScopePolicy_RejectsRestrictedOrSelfDataScope()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var principal = Principal(userId, "tenant", tenantId);
var selfSnapshot = Snapshot(
userId,
tenantId,
[BackendPermissions.TenantContentManage]);
await using var selfProvider = Services(selfSnapshot);
var denied = await selfProvider.GetRequiredService<IAuthorizationService>().AuthorizeAsync(
principal,
null,
TikuPolicies.TenantContentManageAllScope);
var allScope = new CurrentDataScope(DataScopeMode.All, new HashSet<Guid>(), new HashSet<Guid>(), true);
var allSnapshot = Snapshot(
userId,
tenantId,
[BackendPermissions.TenantContentManage],
dataScope: allScope);
await using var allProvider = Services(allSnapshot);
var allowed = await allProvider.GetRequiredService<IAuthorizationService>().AuthorizeAsync(
principal,
null,
TikuPolicies.TenantContentManageAllScope);
Assert.False(denied.Succeeded);
Assert.True(allowed.Succeeded);
}
[Fact]
public async Task ResourceRequirement_UsesOwnerRegionClassAndTenantBoundary()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var regionId = Guid.NewGuid();
var classId = Guid.NewGuid();
var scope = new CurrentDataScope(
DataScopeMode.Restricted,
new HashSet<Guid> { regionId },
new HashSet<Guid> { classId },
true);
await using var provider = Services(Snapshot(userId, tenantId, dataScope: scope));
var authorization = provider.GetRequiredService<IAuthorizationService>();
var principal = Principal(userId, "tenant", tenantId);
var requirement = new TenantResourceAccessRequirement();
var own = await authorization.AuthorizeAsync(
principal,
new TenantResourceAuthorizationResource(tenantId, userId),
requirement);
var region = await authorization.AuthorizeAsync(
principal,
new TenantResourceAuthorizationResource(tenantId, RegionId: regionId),
requirement);
var @class = await authorization.AuthorizeAsync(
principal,
new TenantResourceAuthorizationResource(tenantId, ClassId: classId),
requirement);
var outside = await authorization.AuthorizeAsync(
principal,
new TenantResourceAuthorizationResource(tenantId, RegionId: Guid.NewGuid()),
requirement);
var otherTenant = await authorization.AuthorizeAsync(
principal,
new TenantResourceAuthorizationResource(Guid.NewGuid(), userId),
requirement);
Assert.True(own.Succeeded);
Assert.True(region.Succeeded);
Assert.True(@class.Succeeded);
Assert.False(outside.Succeeded);
Assert.False(otherTenant.Succeeded);
}
[Fact]
public async Task BackofficeBootstrapPolicies_RequireCurrentRealmAndEffectiveAccess()
{
var tenantId = Guid.NewGuid();
var userId = Guid.NewGuid();
var tenantSnapshot = Snapshot(userId, tenantId);
await using var tenantProvider = Services(tenantSnapshot);
var tenantAuthorization = tenantProvider.GetRequiredService<IAuthorizationService>();
var tenantAllowed = await tenantAuthorization.AuthorizeAsync(
Principal(userId, "tenant", tenantId),
null,
TikuPolicies.TenantBackofficeBootstrap);
var platformSnapshot = Snapshot(
userId,
null,
platformPermissions: [BackendPermissions.PlatformDashboardView]);
await using var platformProvider = Services(platformSnapshot);
var platformAuthorization = platformProvider.GetRequiredService<IAuthorizationService>();
var platformAllowed = await platformAuthorization.AuthorizeAsync(
Principal(userId, "platform", null),
null,
TikuPolicies.PlatformBackofficeBootstrap);
var tenantRealmDenied = await platformAuthorization.AuthorizeAsync(
Principal(userId, "tenant", tenantId),
null,
TikuPolicies.PlatformBackofficeBootstrap);
Assert.True(tenantAllowed.Succeeded);
Assert.True(platformAllowed.Succeeded);
Assert.False(tenantRealmDenied.Succeeded);
}
private static ServiceProvider Services(CurrentAccessSnapshot snapshot)
{
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton<ICurrentAccessContext>(new StubCurrentAccessContext(snapshot));
services.AddTikuRbacAuthorization();
return services.BuildServiceProvider();
}
private static ClaimsPrincipal Principal(
Guid userId,
string realm,
Guid? tenantId)
{
var claims = new List<Claim>
{
new(TikuClaimTypes.UserId, userId.ToString()),
new(TikuClaimTypes.Realm, realm)
};
if (tenantId.HasValue) claims.Add(new Claim(TikuClaimTypes.TenantId, tenantId.Value.ToString()));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "test"));
}
private static CurrentAccessSnapshot Snapshot(
Guid userId,
Guid? tenantId,
IEnumerable<string>? tenantPermissions = null,
IEnumerable<string>? platformPermissions = null,
CurrentDataScope? dataScope = null)
{
return new CurrentAccessSnapshot(
userId,
tenantId,
true,
tenantId.HasValue,
(tenantPermissions ?? []).ToHashSet(StringComparer.Ordinal),
(platformPermissions ?? []).ToHashSet(StringComparer.Ordinal),
dataScope ?? CurrentDataScope.Self);
}
private sealed class StubCurrentAccessContext(CurrentAccessSnapshot snapshot) : ICurrentAccessContext
{
public Task<CurrentAccessSnapshot> GetAsync(CancellationToken cancellationToken = default)
{
return Task.FromResult(snapshot);
}
}
}