Compare commits

..

1 commit

Author SHA1 Message Date
Alexandre Brandizzi
c6f7224604
Merge 9156107e72 into b36b69df83 2026-09-22 23:10:04 -03:00
6 changed files with 12 additions and 124 deletions

View file

@ -78,53 +78,6 @@ public sealed class WorkOrderUpliftControllerTests
Assert.Equal(12, created.Id);
}
[Fact]
public async Task CreateUplift_RequestPermissionDenied_ReturnsExact403Message()
{
var service = new Mock<IWorkOrderUpliftService>();
service.Setup(x => x.CreateAsync(
7,
It.IsAny<CreateWorkOrderUpliftRequestDto>(),
It.IsAny<ClaimsPrincipal>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new UpliftForbiddenException(UpliftForbiddenException.RequestUpliftsDeniedMessage));
var controller = NewController(service, "Dispatcher");
var result = await controller.CreateUplift(
7,
new CreateWorkOrderUpliftRequestDto { Amount = 750m },
CancellationToken.None);
var forbidden = result.Should().BeOfType<ObjectResult>().Subject;
forbidden.StatusCode.Should().Be(StatusCodes.Status403Forbidden);
var response = forbidden.Value.Should().BeOfType<Response>().Subject;
response.Message.Should().Be(UpliftForbiddenException.RequestUpliftsDeniedMessage);
}
[Fact]
public async Task CreateUplift_UnrelatedForbiddenException_ReturnsSanitized403()
{
var service = new Mock<IWorkOrderUpliftService>();
service.Setup(x => x.CreateAsync(
7,
It.IsAny<CreateWorkOrderUpliftRequestDto>(),
It.IsAny<ClaimsPrincipal>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new UpliftForbiddenException("SECRET-internal-tier-detail"));
var controller = NewController(service, "Dispatcher");
var result = await controller.CreateUplift(
7,
new CreateWorkOrderUpliftRequestDto { Amount = 750m },
CancellationToken.None);
var forbidden = result.Should().BeOfType<ObjectResult>().Subject;
forbidden.StatusCode.Should().Be(StatusCodes.Status403Forbidden);
var response = forbidden.Value.Should().BeOfType<Response>().Subject;
response.Message.Should().NotContain("SECRET-internal-tier-detail");
response.Message.Should().Contain("reference");
}
[Fact]
public async Task CancelUplift_NotFound_Returns404()
{

View file

@ -170,10 +170,7 @@ namespace Api.SeaHavenIndustries.Controllers
}
catch (UpliftForbiddenException ex)
{
var message = ex.Message == UpliftForbiddenException.RequestUpliftsDeniedMessage
? UpliftForbiddenException.RequestUpliftsDeniedMessage
: _logger.Sanitize(ex, "You are not authorized to perform this action");
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = message });
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action") });
}
catch (InvalidOperationException ex)
{

View file

@ -104,9 +104,6 @@ namespace SeaHaven.Services.DTOs
public class UpliftForbiddenException : Exception
{
public const string RequestUpliftsDeniedMessage =
"Your role can't request uplifts on this work order.";
public UpliftForbiddenException(string message) : base(message) { }
}

View file

@ -99,20 +99,17 @@ namespace SeaHaven.Services.Implementation
var permissionUser = string.IsNullOrWhiteSpace(userId)
? null
: await _permissionOverrideData.GetUserAsync(userId, cancellationToken);
if (permissionUser is null)
{
throw new UpliftForbiddenException(
UpliftForbiddenException.RequestUpliftsDeniedMessage);
}
var roleName = user.IsInRole("Admin")
? "Admin"
: user.FindFirstValue(ClaimTypes.Role);
if (!_permissionPolicy.IsAllowed(
permissionUser.RoleName,
roleName,
TeamPermissionKeys.RequestUplifts,
permissionUser.Overrides))
permissionUser?.Overrides))
{
throw new UpliftForbiddenException(
UpliftForbiddenException.RequestUpliftsDeniedMessage);
"Your role can't request uplifts on this work order.");
}
}

View file

@ -1,6 +1,5 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using SeaHaven.DataServices.Implementation;
@ -305,13 +304,6 @@ public class WorkOrderBoardCancelServiceTests
FirstName = "Alex",
LastName = "Dispatcher",
});
var adminRole = new IdentityRole("Admin");
seed.Roles.Add(adminRole);
seed.UserRoles.Add(new IdentityUserRole<string>
{
UserId = "dispatcher-1",
RoleId = adminRole.Id,
});
seed.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" });
seed.Dispatches.Add(new Dispatch
{

View file

@ -1,6 +1,5 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using SeaHaven.DataServices.Implementation;
@ -50,8 +49,7 @@ public sealed class WorkOrderUpliftServiceTests
private static async Task<(WorkOrder WorkOrder, Dispatch Dispatch)> SeedWorkOrderAsync(
ApplicationDbContext context,
WorkOrderType type = WorkOrderType.PM,
string role = "Admin")
WorkOrderType type = WorkOrderType.PM)
{
await WorkOrderAccountTestHelpers.EnsureAccountAsync(context);
context.Users.Add(new ApplicationUser
@ -61,13 +59,6 @@ public sealed class WorkOrderUpliftServiceTests
FirstName = "Alex",
LastName = "Dispatcher",
});
var identityRole = new IdentityRole(role);
context.Roles.Add(identityRole);
context.UserRoles.Add(new IdentityUserRole<string>
{
UserId = "dispatcher-1",
RoleId = identityRole.Id,
});
context.Vendors.Add(new Vendor { Id = 1, CompanyName = "Acme HVAC" });
context.Dispatches.Add(new Dispatch
{
@ -136,7 +127,7 @@ public sealed class WorkOrderUpliftServiceTests
}
[Fact]
public async Task CreateAsync_CurrentDatabaseAdmin_RemainsUnrestricted()
public async Task CreateAsync_Admin_RemainsUnrestricted()
{
await using var context = CreateContext();
var (workOrder, _) = await SeedWorkOrderAsync(context);
@ -145,58 +136,19 @@ public sealed class WorkOrderUpliftServiceTests
var created = await service.CreateAsync(
workOrder.Id,
new CreateWorkOrderUpliftRequestDto { Amount = 400m },
WorkOrderAccountTestHelpers.AccountUser("dispatcher-1", 1, "Scheduler"),
Dispatcher(),
CancellationToken.None);
Assert.NotNull(created);
}
[Fact]
public async Task CreateAsync_StaleAdminClaim_DeniesWhenDatabaseRoleIsNotAdmin()
{
await using var context = CreateContext();
var (workOrder, dispatch) = await SeedWorkOrderAsync(context, role: "Scheduler");
var service = NewService(context);
var exception = await Assert.ThrowsAsync<UpliftForbiddenException>(() => service.CreateAsync(
workOrder.Id,
new CreateWorkOrderUpliftRequestDto { Amount = 400m },
Dispatcher(),
CancellationToken.None));
Assert.Equal(UpliftForbiddenException.RequestUpliftsDeniedMessage, exception.Message);
Assert.Empty(context.DispatchUpliftRequests);
Assert.Equal(1000m, dispatch.NTEAmount);
}
[Fact]
public async Task CreateAsync_MissingDatabaseUser_DeniesEvenWithAdminClaim()
{
await using var context = CreateContext();
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
context.UserRoles.RemoveRange(context.UserRoles.Where(userRole => userRole.UserId == "dispatcher-1"));
context.Users.Remove(context.Users.Single(user => user.Id == "dispatcher-1"));
await context.SaveChangesAsync();
var service = NewService(context);
var exception = await Assert.ThrowsAsync<UpliftForbiddenException>(() => service.CreateAsync(
workOrder.Id,
new CreateWorkOrderUpliftRequestDto { Amount = 400m },
Dispatcher("dispatcher-1"),
CancellationToken.None));
Assert.Equal(UpliftForbiddenException.RequestUpliftsDeniedMessage, exception.Message);
Assert.Empty(context.DispatchUpliftRequests);
Assert.Equal(1000m, dispatch.NTEAmount);
}
[Theory]
[InlineData("Dispatcher", true)]
[InlineData("Scheduler", false)]
public async Task CreateAsync_UsesRoleDefaultForRequestPermission(string role, bool allowed)
{
await using var context = CreateContext();
var (workOrder, dispatch) = await SeedWorkOrderAsync(context, role: role);
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
var service = NewService(context);
var act = () => service.CreateAsync(
@ -226,7 +178,7 @@ public sealed class WorkOrderUpliftServiceTests
bool allowed)
{
await using var context = CreateContext();
var (workOrder, dispatch) = await SeedWorkOrderAsync(context, role: role);
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
context.UserPermissionOverrides.Add(new UserPermissionOverride
{
UserId = "dispatcher-1",