| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- using System.Security.Claims;
- using Admin.NET.Core;
- using Admin.NET.Core.Service;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Mvc.Abstractions;
- using Microsoft.AspNetCore.Mvc.Filters;
- using Microsoft.AspNetCore.Mvc.ModelBinding;
- using Microsoft.AspNetCore.Routing;
- using Microsoft.Extensions.DependencyInjection;
- using Xunit;
- namespace Admin.NET.Test.Security;
- public class CoreRbacContractTests
- {
- [Theory]
- [InlineData("/aidop/s8/dashboard", "/aidop/s8/dashboard")]
- [InlineData(" /aidop/s8/dashboard/ ", "/aidop/s8/dashboard")]
- [InlineData("/", "/")]
- public void NormalizeMenuPath_AcceptsOnlyCanonicalPcPaths(string input, string expected)
- {
- Assert.Equal(expected, SysMenuService.NormalizeMenuPath(input));
- }
- [Theory]
- [InlineData(null)]
- [InlineData("")]
- [InlineData("aidop/s8/dashboard")]
- [InlineData("//external.example/path")]
- [InlineData("/aidop/s8/dashboard?tenant=other")]
- [InlineData("/aidop/s8/dashboard#fragment")]
- public void NormalizeMenuPath_RejectsAmbiguousOrExternalPaths(string input)
- {
- Assert.Null(SysMenuService.NormalizeMenuPath(input));
- }
- [Fact]
- public async Task Attribute_AllowsAuthenticatedUser_WhenServiceGrantsPath()
- {
- var context = CreateContext(new StubMenuAccessService(true), authenticated: true);
- await new RequirePcMenuPathAttribute("/aidop/s8/dashboard").OnAuthorizationAsync(context);
- Assert.Null(context.Result);
- }
- [Fact]
- public async Task Attribute_AllowsWhenAnyConfiguredPathIsGranted()
- {
- var context = CreateContext(
- new StubMenuAccessService(grantedPath: "/aidop/s7/fqc/result-list"),
- authenticated: true);
- await new RequirePcMenuPathAttribute(
- "/aidop/s7/fqc/task-list",
- "/aidop/s7/fqc/result-list").OnAuthorizationAsync(context);
- Assert.Null(context.Result);
- }
- [Fact]
- public async Task Attribute_Returns401_WhenAuthenticationIsMissing()
- {
- var context = CreateContext(new StubMenuAccessService(true), authenticated: false);
- await new RequirePcMenuPathAttribute("/aidop/s8/dashboard").OnAuthorizationAsync(context);
- Assert.IsType<UnauthorizedResult>(context.Result);
- }
- [Fact]
- public async Task Attribute_FailsClosed_WhenServiceDeniesIsMissingOrThrows()
- {
- foreach (var accessService in new ICurrentUserMenuAccessService[]
- {
- new StubMenuAccessService(false),
- new StubMenuAccessService(exception: new InvalidOperationException("db unavailable")),
- null
- })
- {
- var context = CreateContext(accessService, authenticated: true);
- await new RequirePcMenuPathAttribute("/aidop/s8/dashboard").OnAuthorizationAsync(context);
- var result = Assert.IsType<ObjectResult>(context.Result);
- Assert.Equal(StatusCodes.Status403Forbidden, result.StatusCode);
- }
- }
- [Fact]
- public void Attribute_RejectsEmptyContract()
- {
- Assert.Throws<ArgumentException>(() => new RequirePcMenuPathAttribute(" "));
- }
- private static AuthorizationFilterContext CreateContext(
- ICurrentUserMenuAccessService accessService,
- bool authenticated)
- {
- var services = new ServiceCollection();
- if (accessService != null)
- services.AddSingleton(accessService);
- var httpContext = new DefaultHttpContext
- {
- RequestServices = services.BuildServiceProvider(),
- User = new ClaimsPrincipal(new ClaimsIdentity(
- new[] { new Claim(ClaimTypes.NameIdentifier, "1") },
- authenticated ? "test" : null))
- };
- var actionContext = new ActionContext(
- httpContext,
- new RouteData(),
- new ActionDescriptor(),
- new ModelStateDictionary());
- return new AuthorizationFilterContext(actionContext, new List<IFilterMetadata>());
- }
- private sealed class StubMenuAccessService : ICurrentUserMenuAccessService
- {
- private readonly bool _result;
- private readonly Exception _exception;
- private readonly string _grantedPath;
- public StubMenuAccessService(
- bool result = false,
- Exception exception = null,
- string grantedPath = null)
- {
- _result = result;
- _exception = exception;
- _grantedPath = grantedPath;
- }
- public Task<bool> HasMenuPathAsync(string menuPath) =>
- _exception == null
- ? Task.FromResult(_result || string.Equals(_grantedPath, menuPath, StringComparison.Ordinal))
- : Task.FromException<bool>(_exception);
- }
- }
|