mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
Compare commits
5 commits
c6f7224604
...
9fea78dfde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fea78dfde | ||
|
|
50a2cbab5f | ||
|
|
e1632bc3bf | ||
|
|
f4284badf2 | ||
|
|
ff6a5945f1 |
6 changed files with 126 additions and 14 deletions
|
|
@ -78,6 +78,53 @@ 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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -170,7 +170,10 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
catch (UpliftForbiddenException ex)
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action") });
|
||||
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 });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ 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) { }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,17 +99,20 @@ namespace SeaHaven.Services.Implementation
|
|||
var permissionUser = string.IsNullOrWhiteSpace(userId)
|
||||
? null
|
||||
: await _permissionOverrideData.GetUserAsync(userId, cancellationToken);
|
||||
var roleName = user.IsInRole("Admin")
|
||||
? "Admin"
|
||||
: user.FindFirstValue(ClaimTypes.Role);
|
||||
|
||||
if (!_permissionPolicy.IsAllowed(
|
||||
roleName,
|
||||
TeamPermissionKeys.RequestUplifts,
|
||||
permissionUser?.Overrides))
|
||||
if (permissionUser is null)
|
||||
{
|
||||
throw new UpliftForbiddenException(
|
||||
"Your role can't request uplifts on this work order.");
|
||||
UpliftForbiddenException.RequestUpliftsDeniedMessage);
|
||||
}
|
||||
|
||||
if (!_permissionPolicy.IsAllowed(
|
||||
permissionUser.RoleName,
|
||||
TeamPermissionKeys.RequestUplifts,
|
||||
permissionUser.Overrides))
|
||||
{
|
||||
throw new UpliftForbiddenException(
|
||||
UpliftForbiddenException.RequestUpliftsDeniedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
|
|
@ -304,6 +305,13 @@ 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Data.SeaHavenIndustries.Enums;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
|
|
@ -49,7 +50,8 @@ public sealed class WorkOrderUpliftServiceTests
|
|||
|
||||
private static async Task<(WorkOrder WorkOrder, Dispatch Dispatch)> SeedWorkOrderAsync(
|
||||
ApplicationDbContext context,
|
||||
WorkOrderType type = WorkOrderType.PM)
|
||||
WorkOrderType type = WorkOrderType.PM,
|
||||
string role = "Admin")
|
||||
{
|
||||
await WorkOrderAccountTestHelpers.EnsureAccountAsync(context);
|
||||
context.Users.Add(new ApplicationUser
|
||||
|
|
@ -59,6 +61,13 @@ 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
|
||||
{
|
||||
|
|
@ -127,7 +136,7 @@ public sealed class WorkOrderUpliftServiceTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_Admin_RemainsUnrestricted()
|
||||
public async Task CreateAsync_CurrentDatabaseAdmin_RemainsUnrestricted()
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
var (workOrder, _) = await SeedWorkOrderAsync(context);
|
||||
|
|
@ -136,19 +145,58 @@ public sealed class WorkOrderUpliftServiceTests
|
|||
var created = await service.CreateAsync(
|
||||
workOrder.Id,
|
||||
new CreateWorkOrderUpliftRequestDto { Amount = 400m },
|
||||
Dispatcher(),
|
||||
WorkOrderAccountTestHelpers.AccountUser("dispatcher-1", 1, "Scheduler"),
|
||||
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);
|
||||
var (workOrder, dispatch) = await SeedWorkOrderAsync(context, role: role);
|
||||
var service = NewService(context);
|
||||
|
||||
var act = () => service.CreateAsync(
|
||||
|
|
@ -178,7 +226,7 @@ public sealed class WorkOrderUpliftServiceTests
|
|||
bool allowed)
|
||||
{
|
||||
await using var context = CreateContext();
|
||||
var (workOrder, dispatch) = await SeedWorkOrderAsync(context);
|
||||
var (workOrder, dispatch) = await SeedWorkOrderAsync(context, role: role);
|
||||
context.UserPermissionOverrides.Add(new UserPermissionOverride
|
||||
{
|
||||
UserId = "dispatcher-1",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue