From 9adfff48a7d2625127886113329ae2f892069eb0 Mon Sep 17 00:00:00 2001 From: "Kalana B. Thilakarathna" Date: Fri, 9 Oct 2026 09:37:05 +0530 Subject: [PATCH] auth layer implemented --- .claude/worktrees/agent-a203fc082069b1faf | 1 - .../Data/Seed/RoleAndPermissionSeed.cs | 173 +++++++++++++++++- Distribution/Features/Auth/AuthController.cs | 2 + .../Features/Brand/BrandController.cs | 8 +- .../Features/Category/CategoryController.cs | 11 +- .../Features/Claims/ClaimController.cs | 8 +- .../DeliveryRide/DeliveryTaskController.cs | 9 +- Distribution/Features/Grn/GrnController.cs | 8 +- .../Features/Invoice/InvoiceController.cs | 4 +- Distribution/Features/Item/ItemController.cs | 9 +- .../Features/Payment/PaymentController.cs | 5 +- Distribution/Features/Ride/RideController.cs | 9 +- Distribution/Features/Role/RoleController.cs | 4 +- .../Features/Scheme/SchemeController.cs | 16 +- .../Features/ShopPO/ShopPOController.cs | 4 +- .../Features/Supplier/SupplierController.cs | 9 +- .../SupplierClaims/SupplierClaimController.cs | 10 +- Distribution/Features/Sync/SyncController.cs | 4 +- Distribution/Features/Uom/UomController.cs | 8 +- Distribution/Features/User/UserController.cs | 10 +- .../Features/Vehicle/VehicleController.cs | 8 +- .../Features/Warehouse/WarehouseController.cs | 8 +- .../PermissionAuthorizationHandler.cs | 39 ++-- .../Authorization/PermissionPolicyProvider.cs | 49 ++--- .../Authorization/PermissionRequirement.cs | 21 ++- Distribution/Program.cs | 15 +- 26 files changed, 367 insertions(+), 85 deletions(-) delete mode 160000 .claude/worktrees/agent-a203fc082069b1faf diff --git a/.claude/worktrees/agent-a203fc082069b1faf b/.claude/worktrees/agent-a203fc082069b1faf deleted file mode 160000 index ade41b4..0000000 --- a/.claude/worktrees/agent-a203fc082069b1faf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ade41b4a60b1381b8f3027f457139f5b9879a869 diff --git a/Distribution/Data/Seed/RoleAndPermissionSeed.cs b/Distribution/Data/Seed/RoleAndPermissionSeed.cs index a984e4d..0cd8f37 100644 --- a/Distribution/Data/Seed/RoleAndPermissionSeed.cs +++ b/Distribution/Data/Seed/RoleAndPermissionSeed.cs @@ -7,22 +7,175 @@ public static class RoleAndPermissionSeed { private static readonly DateTime SeedDate = new(2026, 8, 18); - private static readonly Guid SalesRepId = new("10000000-0000-0000-0000-000000000001"); - private static readonly Guid SupervisorId = new("10000000-0000-0000-0000-000000000002"); - private static readonly Guid AreaManagerId = new("10000000-0000-0000-0000-000000000003"); - private static readonly Guid AreaSupervisorId = new("10000000-0000-0000-0000-000000000004"); - private static readonly Guid RegionalManagerId = new("10000000-0000-0000-0000-000000000005"); - private static readonly Guid RegionalAgencyId = new("10000000-0000-0000-0000-000000000006"); + private static readonly Guid SalesRepId = new("10000000-0000-0000-0000-000000000001"); + private static readonly Guid SupervisorId = new("10000000-0000-0000-0000-000000000002"); + private static readonly Guid AreaManagerId = new("10000000-0000-0000-0000-000000000003"); + private static readonly Guid AreaSupervisorId = new("10000000-0000-0000-0000-000000000004"); + private static readonly Guid RegionalManagerId = new("10000000-0000-0000-0000-000000000005"); + private static readonly Guid RegionalAgencyId = new("10000000-0000-0000-0000-000000000006"); + + // Permission IDs — stable, deterministic + private static readonly Guid P01 = new("20000000-0000-0000-0000-000000000001"); // loading-sheet.view + private static readonly Guid P02 = new("20000000-0000-0000-0000-000000000002"); // loading-sheet.manage + private static readonly Guid P03 = new("20000000-0000-0000-0000-000000000003"); // ride.view + private static readonly Guid P04 = new("20000000-0000-0000-0000-000000000004"); // ride.manage + private static readonly Guid P05 = new("20000000-0000-0000-0000-000000000005"); // delivery-task.view + private static readonly Guid P06 = new("20000000-0000-0000-0000-000000000006"); // delivery-task.manage + private static readonly Guid P07 = new("20000000-0000-0000-0000-000000000007"); // store.view + private static readonly Guid P08 = new("20000000-0000-0000-0000-000000000008"); // store.manage + private static readonly Guid P09 = new("20000000-0000-0000-0000-000000000009"); // invoice.view + private static readonly Guid P10 = new("20000000-0000-0000-0000-000000000010"); // payment.manage + private static readonly Guid P11 = new("20000000-0000-0000-0000-000000000011"); // return.view + private static readonly Guid P12 = new("20000000-0000-0000-0000-000000000012"); // return.manage + private static readonly Guid P13 = new("20000000-0000-0000-0000-000000000013"); // shop-po.view + private static readonly Guid P14 = new("20000000-0000-0000-0000-000000000014"); // stock.view + private static readonly Guid P15 = new("20000000-0000-0000-0000-000000000015"); // stock.manage + private static readonly Guid P16 = new("20000000-0000-0000-0000-000000000016"); // claim.view + private static readonly Guid P17 = new("20000000-0000-0000-0000-000000000017"); // claim.manage + private static readonly Guid P18 = new("20000000-0000-0000-0000-000000000018"); // user.view + private static readonly Guid P19 = new("20000000-0000-0000-0000-000000000019"); // route.view + private static readonly Guid P20 = new("20000000-0000-0000-0000-000000000020"); // scheme.view + private static readonly Guid P21 = new("20000000-0000-0000-0000-000000000021"); // scheme.manage + private static readonly Guid P22 = new("20000000-0000-0000-0000-000000000022"); // route.manage + private static readonly Guid P23 = new("20000000-0000-0000-0000-000000000023"); // price-list.view + private static readonly Guid P24 = new("20000000-0000-0000-0000-000000000024"); // region.view + private static readonly Guid P25 = new("20000000-0000-0000-0000-000000000025"); // price-list.manage + private static readonly Guid P26 = new("20000000-0000-0000-0000-000000000026"); // grn.view + private static readonly Guid P27 = new("20000000-0000-0000-0000-000000000027"); // grn.manage + private static readonly Guid P28 = new("20000000-0000-0000-0000-000000000028"); // supplier.view + private static readonly Guid P29 = new("20000000-0000-0000-0000-000000000029"); // region.manage + private static readonly Guid P30 = new("20000000-0000-0000-0000-000000000030"); // warehouse.view + private static readonly Guid P31 = new("20000000-0000-0000-0000-000000000031"); // warehouse.manage + private static readonly Guid P32 = new("20000000-0000-0000-0000-000000000032"); // purchase-order.view + private static readonly Guid P33 = new("20000000-0000-0000-0000-000000000033"); // purchase-order.manage + private static readonly Guid P34 = new("20000000-0000-0000-0000-000000000034"); // purchase-return.manage + private static readonly Guid P35 = new("20000000-0000-0000-0000-000000000035"); // supplier-claim.view + private static readonly Guid P36 = new("20000000-0000-0000-0000-000000000036"); // supplier-claim.manage + private static readonly Guid P37 = new("20000000-0000-0000-0000-000000000037"); // brand.view + private static readonly Guid P38 = new("20000000-0000-0000-0000-000000000038"); // brand.manage + private static readonly Guid P39 = new("20000000-0000-0000-0000-000000000039"); // category.view + private static readonly Guid P40 = new("20000000-0000-0000-0000-000000000040"); // category.manage + private static readonly Guid P41 = new("20000000-0000-0000-0000-000000000041"); // item.view + private static readonly Guid P42 = new("20000000-0000-0000-0000-000000000042"); // item.manage + private static readonly Guid P43 = new("20000000-0000-0000-0000-000000000043"); // uom.view + private static readonly Guid P44 = new("20000000-0000-0000-0000-000000000044"); // uom.manage + private static readonly Guid P45 = new("20000000-0000-0000-0000-000000000045"); // vehicle.view + private static readonly Guid P46 = new("20000000-0000-0000-0000-000000000046"); // vehicle.manage + private static readonly Guid P47 = new("20000000-0000-0000-0000-000000000047"); // driver.view + private static readonly Guid P48 = new("20000000-0000-0000-0000-000000000048"); // driver.manage + private static readonly Guid P49 = new("20000000-0000-0000-0000-000000000049"); // role.view + private static readonly Guid P50 = new("20000000-0000-0000-0000-000000000050"); // user.manage + private static readonly Guid P51 = new("20000000-0000-0000-0000-000000000051"); // sync.manage + private static readonly Guid P52 = new("20000000-0000-0000-0000-000000000052"); // sys-config.view + private static readonly Guid P53 = new("20000000-0000-0000-0000-000000000053"); // sys-config.manage + private static readonly Guid P54 = new("20000000-0000-0000-0000-000000000054"); // supplier.manage public static void Apply(ModelBuilder modelBuilder) { modelBuilder.Entity().HasData( - new Role { Id = SalesRepId, Name = "SalesRep", Level = 10, CreatedAt = SeedDate, IsActive = true }, - new Role { Id = SupervisorId, Name = "Supervisor", Level = 20, CreatedAt = SeedDate, IsActive = true }, - new Role { Id = AreaManagerId, Name = "AreaManager", Level = 30, CreatedAt = SeedDate, IsActive = true }, + new Role { Id = SalesRepId, Name = "SalesRep", Level = 10, CreatedAt = SeedDate, IsActive = true }, + new Role { Id = SupervisorId, Name = "Supervisor", Level = 20, CreatedAt = SeedDate, IsActive = true }, + new Role { Id = AreaManagerId, Name = "AreaManager", Level = 30, CreatedAt = SeedDate, IsActive = true }, new Role { Id = AreaSupervisorId, Name = "AreaSupervisor", Level = 40, CreatedAt = SeedDate, IsActive = true }, - new Role { Id = RegionalManagerId, Name = "RegionalManager", Level = 50, CreatedAt = SeedDate, IsActive = true }, + new Role { Id = RegionalManagerId,Name = "RegionalManager",Level = 50, CreatedAt = SeedDate, IsActive = true }, new Role { Id = RegionalAgencyId, Name = "RegionalAgency", Level = 60, CreatedAt = SeedDate, IsActive = true } ); + + modelBuilder.Entity().HasData( + new Permission { Id = P01, Code = "loading-sheet.view", Description = "View loading sheets", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P02, Code = "loading-sheet.manage", Description = "Create/edit loading sheets", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P03, Code = "ride.view", Description = "View rides", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P04, Code = "ride.manage", Description = "Create/manage rides", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P05, Code = "delivery-task.view", Description = "View delivery tasks", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P06, Code = "delivery-task.manage", Description = "Manage delivery tasks", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P07, Code = "store.view", Description = "View stores", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P08, Code = "store.manage", Description = "Create/edit stores", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P09, Code = "invoice.view", Description = "View invoices", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P10, Code = "payment.manage", Description = "Record payments", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P11, Code = "return.view", Description = "View returns", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P12, Code = "return.manage", Description = "Create/manage returns", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P13, Code = "shop-po.view", Description = "View shop purchase orders", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P14, Code = "stock.view", Description = "View stock", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P15, Code = "stock.manage", Description = "Manage stock movements", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P16, Code = "claim.view", Description = "View claims", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P17, Code = "claim.manage", Description = "Create/settle claims", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P18, Code = "user.view", Description = "View users", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P19, Code = "route.view", Description = "View routes", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P20, Code = "scheme.view", Description = "View schemes", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P21, Code = "scheme.manage", Description = "Create/edit schemes", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P22, Code = "route.manage", Description = "Create/edit routes", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P23, Code = "price-list.view", Description = "View price lists", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P24, Code = "region.view", Description = "View regions", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P25, Code = "price-list.manage", Description = "Create/edit price lists", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P26, Code = "grn.view", Description = "View goods receipts", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P27, Code = "grn.manage", Description = "Create goods receipts", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P28, Code = "supplier.view", Description = "View suppliers", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P29, Code = "region.manage", Description = "Create/edit regions", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P30, Code = "warehouse.view", Description = "View warehouses", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P31, Code = "warehouse.manage", Description = "Create/edit warehouses", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P32, Code = "purchase-order.view", Description = "View purchase orders", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P33, Code = "purchase-order.manage", Description = "Create/manage purchase orders",CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P34, Code = "purchase-return.manage", Description = "Create purchase returns", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P35, Code = "supplier-claim.view", Description = "View supplier claims", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P36, Code = "supplier-claim.manage", Description = "Manage supplier claims", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P37, Code = "brand.view", Description = "View brands", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P38, Code = "brand.manage", Description = "Create/edit brands", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P39, Code = "category.view", Description = "View categories", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P40, Code = "category.manage", Description = "Create/edit categories", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P41, Code = "item.view", Description = "View items", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P42, Code = "item.manage", Description = "Create/edit items", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P43, Code = "uom.view", Description = "View units of measure", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P44, Code = "uom.manage", Description = "Create/edit units of measure", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P45, Code = "vehicle.view", Description = "View vehicles", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P46, Code = "vehicle.manage", Description = "Create/edit vehicles", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P47, Code = "driver.view", Description = "View driver profiles", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P48, Code = "driver.manage", Description = "Manage driver profiles", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P49, Code = "role.view", Description = "View roles", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P50, Code = "user.manage", Description = "Create/edit/deactivate users", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P51, Code = "sync.manage", Description = "Trigger data sync", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P52, Code = "sys-config.view", Description = "View system config", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P53, Code = "sys-config.manage", Description = "Edit system config", CreatedAt = SeedDate, IsActive = true }, + new Permission { Id = P54, Code = "supplier.manage", Description = "Create/edit suppliers", CreatedAt = SeedDate, IsActive = true } + ); + + // RolePermission seed — cumulative by role level + // Each row needs a stable Id because RolePermission inherits Template + // Using 30000000-00RR-0000-0000-00000000PPPP (RR = role index, PPPP = permission index) + var rp = new List(); + + // SalesRep (10): P01-P15 + var salesRepPermissions = new[] { P01, P02, P03, P04, P05, P06, P07, P09, P10, P11, P12, P13, P14, P15 }; + foreach (var (p, i) in salesRepPermissions.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0001-0000-0000-{i:D12}"), SalesRepId, p)); + + // Supervisor (20): + P16-P20 + var supervisorExtra = new[] { P16, P17, P18, P19, P20 }; + foreach (var (p, i) in supervisorExtra.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0002-0000-0000-{i:D12}"), SupervisorId, p)); + + // AreaManager (30): + P08, P21, P22, P23 + var areaManagerExtra = new[] { P08, P21, P22, P23 }; + foreach (var (p, i) in areaManagerExtra.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0003-0000-0000-{i:D12}"), AreaManagerId, p)); + + // AreaSupervisor (40): + P24, P25, P26, P27, P28 + var areaSupervisorExtra = new[] { P24, P25, P26, P27, P28 }; + foreach (var (p, i) in areaSupervisorExtra.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0004-0000-0000-{i:D12}"), AreaSupervisorId, p)); + + // RegionalManager (50): + P29-P36 + var regionalManagerExtra = new[] { P29, P30, P31, P32, P33, P34, P35, P36 }; + foreach (var (p, i) in regionalManagerExtra.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0005-0000-0000-{i:D12}"), RegionalManagerId, p)); + + // RegionalAgency (60): + P37-P54 + var regionalAgencyExtra = new[] { P37, P38, P39, P40, P41, P42, P43, P44, P45, P46, P47, P48, P49, P50, P51, P52, P53, P54 }; + foreach (var (p, i) in regionalAgencyExtra.Select((p, i) => (p, i + 1))) + rp.Add(Rp(new Guid($"30000000-0006-0000-0000-{i:D12}"), RegionalAgencyId, p)); + + modelBuilder.Entity().HasData(rp); } + + private static RolePermission Rp(Guid id, Guid roleId, Guid permissionId) => + new() { Id = id, FK_RoleId = roleId, FK_PermissionId = permissionId, CreatedAt = SeedDate, IsActive = true }; } diff --git a/Distribution/Features/Auth/AuthController.cs b/Distribution/Features/Auth/AuthController.cs index 385c9b7..8521dd0 100644 --- a/Distribution/Features/Auth/AuthController.cs +++ b/Distribution/Features/Auth/AuthController.cs @@ -12,10 +12,12 @@ public class AuthController(AuthService service, ICurrentUserService currentUser result.Success ? new OkObjectResult(result) : new ObjectResult(result) { StatusCode = result.StatusCode is >= 400 and < 600 ? result.StatusCode : 401 }; [HttpPost("login")] + [AllowAnonymous] public async Task Login(LoginRequest request, CancellationToken ct) => ToActionResult(await service.LoginAsync(request, ct)); [HttpPost("register")] + [AllowAnonymous] public async Task Register(RegisterRequest request, CancellationToken ct) => ToActionResult(await service.RegisterAsync(request, ct)); diff --git a/Distribution/Features/Brand/BrandController.cs b/Distribution/Features/Brand/BrandController.cs index 7bf17c2..780c11b 100644 --- a/Distribution/Features/Brand/BrandController.cs +++ b/Distribution/Features/Brand/BrandController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Brand; @@ -7,10 +8,12 @@ namespace Distribution.Features.Brand; public class BrandController(BrandService service) : ControllerBase { [HttpGet] + [Authorize("brand.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("brand.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class BrandController(BrandService service) : ControllerBase } [HttpPost] + [Authorize("brand.manage")] public async Task Create(CreateBrandRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class BrandController(BrandService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("brand.manage")] public async Task Update(Guid id, UpdateBrandRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class BrandController(BrandService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("brand.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); diff --git a/Distribution/Features/Category/CategoryController.cs b/Distribution/Features/Category/CategoryController.cs index c3e4635..c69297f 100644 --- a/Distribution/Features/Category/CategoryController.cs +++ b/Distribution/Features/Category/CategoryController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Category; @@ -6,10 +7,12 @@ namespace Distribution.Features.Category; public class CategoryController(CategoryService service) : ControllerBase { [HttpGet("api/categories")] + [Authorize("category.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("api/categories/{id:guid}")] + [Authorize("category.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -17,6 +20,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpPost("api/categories")] + [Authorize("category.manage")] public async Task Create(CreateCategoryRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -24,6 +28,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpPut("api/categories/{id:guid}")] + [Authorize("category.manage")] public async Task Update(Guid id, UpdateCategoryRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -31,6 +36,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpDelete("api/categories/{id:guid}")] + [Authorize("category.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); @@ -38,6 +44,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpPost("api/categories/{categoryId:guid}/subcategories")] + [Authorize("category.manage")] public async Task CreateSubCategory(Guid categoryId, CreateSubCategoryRequest request, CancellationToken ct) { var result = await service.CreateSubCategoryAsync(categoryId, request, ct); @@ -45,6 +52,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpPut("api/subcategories/{id:guid}")] + [Authorize("category.manage")] public async Task UpdateSubCategory(Guid id, UpdateSubCategoryRequest request, CancellationToken ct) { var result = await service.UpdateSubCategoryAsync(id, request, ct); @@ -52,6 +60,7 @@ public class CategoryController(CategoryService service) : ControllerBase } [HttpDelete("api/subcategories/{id:guid}")] + [Authorize("category.manage")] public async Task DeleteSubCategory(Guid id, CancellationToken ct) { var deleted = await service.DeleteSubCategoryAsync(id, ct); diff --git a/Distribution/Features/Claims/ClaimController.cs b/Distribution/Features/Claims/ClaimController.cs index 922835f..d108b9a 100644 --- a/Distribution/Features/Claims/ClaimController.cs +++ b/Distribution/Features/Claims/ClaimController.cs @@ -1,4 +1,5 @@ -using Distribution.Infra.Authorization; +using Distribution.Infra.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Claims; @@ -7,10 +8,12 @@ namespace Distribution.Features.Claims; public class ClaimController(ClaimService service, ICurrentUserService currentUser) : ControllerBase { [HttpGet("api/claims")] + [Authorize("claim.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("api/claims/{id:guid}")] + [Authorize("claim.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs } [HttpPost("api/claims")] + [Authorize("claim.manage")] public async Task Create(CreateClaimRequest request, CancellationToken ct) { try @@ -29,6 +33,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs } [HttpPut("api/claims/{id:guid}")] + [Authorize("claim.manage")] public async Task Update(Guid id, UpdateClaimRequest request, CancellationToken ct) { try @@ -40,6 +45,7 @@ public class ClaimController(ClaimService service, ICurrentUserService currentUs } [HttpPost("api/claims/{id:guid}/settle")] + [Authorize("claim.manage")] public async Task Settle(Guid id, SettleClaimRequest request, CancellationToken ct) { var userId = currentUser.UserId ?? request.SettledByUserId; diff --git a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs index 919155e..83b883e 100644 --- a/Distribution/Features/DeliveryRide/DeliveryTaskController.cs +++ b/Distribution/Features/DeliveryRide/DeliveryTaskController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.DeliveryRide; @@ -7,6 +8,7 @@ namespace Distribution.Features.DeliveryRide; public class DeliveryTaskController(DeliveryTaskService service) : ControllerBase { [HttpPost("rides/{rideId:guid}/tasks/generate")] + [Authorize("delivery-task.manage")] public async Task Generate(Guid rideId, CancellationToken ct) { try @@ -20,6 +22,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas [HttpGet("rides/{rideId:guid}/tasks")] [HttpGet("rides/{rideId:guid}/delivery-tasks")] + [Authorize("delivery-task.view")] public async Task GetByRide(Guid rideId) { var result = await service.GetByRideIdAsync(rideId); @@ -27,6 +30,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas } [HttpGet("delivery-tasks/{id:guid}")] + [Authorize("delivery-task.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -34,6 +38,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas } [HttpPut("delivery-tasks/{id:guid}/quantities")] + [Authorize("delivery-task.manage")] public async Task SetQuantities(Guid id, SetQuantitiesRequest request, CancellationToken ct) { try @@ -46,6 +51,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas } [HttpPost("delivery-tasks/{id:guid}/confirm")] + [Authorize("delivery-task.manage")] public async Task Confirm(Guid id, CancellationToken ct) { try @@ -58,6 +64,7 @@ public class DeliveryTaskController(DeliveryTaskService service) : ControllerBas } [HttpPost("delivery-tasks/{id:guid}/fail")] + [Authorize("delivery-task.manage")] public async Task Fail(Guid id, FailTaskRequest request, CancellationToken ct) { try diff --git a/Distribution/Features/Grn/GrnController.cs b/Distribution/Features/Grn/GrnController.cs index f87f56a..cf5aa31 100644 --- a/Distribution/Features/Grn/GrnController.cs +++ b/Distribution/Features/Grn/GrnController.cs @@ -1,4 +1,5 @@ -using Distribution.Infra.Authorization; +using Distribution.Infra.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Grn; @@ -7,6 +8,7 @@ namespace Distribution.Features.Grn; public class GrnController(GrnService service, ICurrentUserService currentUser) : ControllerBase { [HttpGet("api/goods-receipts")] + [Authorize("grn.view")] public async Task GetAll( [FromQuery] Guid? purchaseOrderId, [FromQuery] int page = 1, @@ -17,6 +19,7 @@ public class GrnController(GrnService service, ICurrentUserService currentUser) } [HttpGet("api/goods-receipts/{id:guid}")] + [Authorize("grn.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -24,6 +27,7 @@ public class GrnController(GrnService service, ICurrentUserService currentUser) } [HttpPost("api/goods-receipts")] + [Authorize("grn.manage")] public async Task CreateFlat(CreateFlatGrnRequest request, CancellationToken ct) { // TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware @@ -40,10 +44,12 @@ public class GrnController(GrnService service, ICurrentUserService currentUser) } [HttpGet("api/purchase-orders/{id:guid}/goods-receipts")] + [Authorize("grn.view")] public async Task GetByPurchaseOrder(Guid id) => Ok(await service.GetByPurchaseOrderIdAsync(id)); [HttpPost("api/purchase-orders/{id:guid}/goods-receipts")] + [Authorize("grn.manage")] public async Task Create(Guid id, CreateGrnRequest request, CancellationToken ct) { var userId = currentUser.UserId ?? request.ReceivedByUserId; diff --git a/Distribution/Features/Invoice/InvoiceController.cs b/Distribution/Features/Invoice/InvoiceController.cs index 5060dcf..7ca7be8 100644 --- a/Distribution/Features/Invoice/InvoiceController.cs +++ b/Distribution/Features/Invoice/InvoiceController.cs @@ -1,10 +1,12 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Distribution.Models; namespace Distribution.Features.Invoice; [ApiController] [Route("api/invoices")] +[Authorize("invoice.view")] public class InvoiceController(InvoiceService service) : ControllerBase { [HttpGet] diff --git a/Distribution/Features/Item/ItemController.cs b/Distribution/Features/Item/ItemController.cs index d497152..215ead8 100644 --- a/Distribution/Features/Item/ItemController.cs +++ b/Distribution/Features/Item/ItemController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Item; @@ -7,6 +8,7 @@ namespace Distribution.Features.Item; public class ItemController(ItemService service) : ControllerBase { [HttpGet] + [Authorize("item.view")] public async Task GetAll( [FromQuery] Guid? categoryId, [FromQuery] Guid? subCategoryId, @@ -14,6 +16,7 @@ public class ItemController(ItemService service) : ControllerBase Ok(await service.GetAllAsync(categoryId, subCategoryId, brandId)); [HttpGet("{id:guid}")] + [Authorize("item.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -21,6 +24,7 @@ public class ItemController(ItemService service) : ControllerBase } [HttpPost] + [Authorize("item.manage")] public async Task Create(CreateItemRequest request, CancellationToken ct) { try @@ -35,6 +39,7 @@ public class ItemController(ItemService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("item.manage")] public async Task Update(Guid id, UpdateItemRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -42,6 +47,7 @@ public class ItemController(ItemService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("item.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); @@ -49,6 +55,7 @@ public class ItemController(ItemService service) : ControllerBase } [HttpPut("{id:guid}/uom-conversions")] + [Authorize("item.manage")] public async Task ReplaceConversions(Guid id, ReplaceConversionsRequest request, CancellationToken ct) { var result = await service.ReplaceConversionsAsync(id, request, ct); diff --git a/Distribution/Features/Payment/PaymentController.cs b/Distribution/Features/Payment/PaymentController.cs index d3e6e91..d93f63d 100644 --- a/Distribution/Features/Payment/PaymentController.cs +++ b/Distribution/Features/Payment/PaymentController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Payment; @@ -7,6 +8,7 @@ namespace Distribution.Features.Payment; public class PaymentController(PaymentService service) : ControllerBase { [HttpGet] + [Authorize("invoice.view")] public async Task GetByInvoice(Guid invoiceId) { var result = await service.GetByInvoiceIdAsync(invoiceId); @@ -14,6 +16,7 @@ public class PaymentController(PaymentService service) : ControllerBase } [HttpPost] + [Authorize("payment.manage")] public async Task Create(Guid invoiceId, CreatePaymentRequest request, CancellationToken ct) { try diff --git a/Distribution/Features/Ride/RideController.cs b/Distribution/Features/Ride/RideController.cs index ec0086d..4f51156 100644 --- a/Distribution/Features/Ride/RideController.cs +++ b/Distribution/Features/Ride/RideController.cs @@ -1,5 +1,6 @@ -using Distribution.Models; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Distribution.Models; namespace Distribution.Features.Ride; @@ -8,6 +9,7 @@ namespace Distribution.Features.Ride; public class RideController(RideService service) : ControllerBase { [HttpGet] + [Authorize("ride.view")] public async Task GetAll( [FromQuery] RideStatus? status, [FromQuery] Guid? driverProfileId, @@ -20,6 +22,7 @@ public class RideController(RideService service) : ControllerBase } [HttpGet("{id:guid}")] + [Authorize("ride.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -27,6 +30,7 @@ public class RideController(RideService service) : ControllerBase } [HttpPost] + [Authorize("ride.manage")] public async Task Create(CreateRideRequest request, CancellationToken ct) { try @@ -38,6 +42,7 @@ public class RideController(RideService service) : ControllerBase } [HttpPut("{id:guid}/driver")] + [Authorize("ride.manage")] public async Task AssignDriver(Guid id, AssignRideDriverRequest request, CancellationToken ct) { try @@ -50,6 +55,7 @@ public class RideController(RideService service) : ControllerBase } [HttpPost("{id:guid}/close")] + [Authorize("ride.manage")] public async Task Close(Guid id, CloseRideRequest request, CancellationToken ct) { try @@ -62,6 +68,7 @@ public class RideController(RideService service) : ControllerBase } [HttpPost("{id:guid}/cancel")] + [Authorize("ride.manage")] public async Task Cancel(Guid id, CancelRideRequest request, CancellationToken ct) { try diff --git a/Distribution/Features/Role/RoleController.cs b/Distribution/Features/Role/RoleController.cs index 5c872f9..2af2155 100644 --- a/Distribution/Features/Role/RoleController.cs +++ b/Distribution/Features/Role/RoleController.cs @@ -1,9 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Role; [ApiController] [Route("api/roles")] +[Authorize("role.view")] public class RoleController(RoleService service) : ControllerBase { [HttpGet] diff --git a/Distribution/Features/Scheme/SchemeController.cs b/Distribution/Features/Scheme/SchemeController.cs index 7cb79b3..6858999 100644 --- a/Distribution/Features/Scheme/SchemeController.cs +++ b/Distribution/Features/Scheme/SchemeController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Scheme; @@ -7,10 +8,12 @@ namespace Distribution.Features.Scheme; public class SchemeController(SchemeService service) : ControllerBase { [HttpGet] + [Authorize("scheme.view")] public async Task GetAll([FromQuery] Guid? regionId) => Ok(await service.GetAllAsync(regionId)); [HttpGet("{id:guid}")] + [Authorize("scheme.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpPost] + [Authorize("scheme.manage")] public async Task Create(CreateSchemeRequest request, CancellationToken ct) { try @@ -32,6 +36,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("scheme.manage")] public async Task Update(Guid id, UpdateSchemeRequest request, CancellationToken ct) { try @@ -46,6 +51,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("scheme.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); @@ -53,6 +59,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpPut("{id:guid}/free-goods-rule")] + [Authorize("scheme.manage")] public async Task SetFreeGoodsRule(Guid id, SetFreeGoodsRuleRequest request, CancellationToken ct) { try @@ -67,6 +74,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpDelete("{id:guid}/free-goods-rule")] + [Authorize("scheme.manage")] public async Task ClearFreeGoodsRule(Guid id, CancellationToken ct) { var result = await service.ClearFreeGoodsRuleAsync(id, ct); @@ -74,6 +82,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpPut("{id:guid}/bundle-components/{itemId:guid}")] + [Authorize("scheme.manage")] public async Task UpsertBundleComponent(Guid id, Guid itemId, UpsertBundleComponentRequest request, CancellationToken ct) { try @@ -88,6 +97,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpDelete("{id:guid}/bundle-components/{itemId:guid}")] + [Authorize("scheme.manage")] public async Task RemoveBundleComponent(Guid id, Guid itemId, CancellationToken ct) { var result = await service.RemoveBundleComponentAsync(id, itemId, ct); @@ -95,6 +105,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpGet("eligible")] + [Authorize("scheme.view")] public async Task GetEligible([FromQuery] Guid storeId, [FromQuery] Guid orderId) { var result = await service.EvaluateEligibilityAsync(storeId, orderId); @@ -102,6 +113,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpGet("{id:guid}/spend")] + [Authorize("scheme.view")] public async Task GetSpend(Guid id) { var result = await service.GetSpendAsync(id); @@ -109,6 +121,7 @@ public class SchemeController(SchemeService service) : ControllerBase } [HttpGet("{id:guid}/roi")] + [Authorize("scheme.view")] public async Task GetROI(Guid id) { var result = await service.GetROIAsync(id); @@ -121,6 +134,7 @@ public class SchemeController(SchemeService service) : ControllerBase public class SchemeOrderController(SchemeService service) : ControllerBase { [HttpPost("{id:guid}/apply-scheme")] + [Authorize("scheme.manage")] public async Task ApplyScheme(Guid id, ApplySchemeRequest request, CancellationToken ct) { try diff --git a/Distribution/Features/ShopPO/ShopPOController.cs b/Distribution/Features/ShopPO/ShopPOController.cs index a7fb4c7..e759e9a 100644 --- a/Distribution/Features/ShopPO/ShopPOController.cs +++ b/Distribution/Features/ShopPO/ShopPOController.cs @@ -1,9 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.ShopPO; [ApiController] [Route("api/shop-pos")] +[Authorize("shop-po.view")] public class ShopPOController(ShopPOService service) : ControllerBase { [HttpGet] diff --git a/Distribution/Features/Supplier/SupplierController.cs b/Distribution/Features/Supplier/SupplierController.cs index 65210d7..e323000 100644 --- a/Distribution/Features/Supplier/SupplierController.cs +++ b/Distribution/Features/Supplier/SupplierController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Supplier; @@ -7,10 +8,12 @@ namespace Distribution.Features.Supplier; public class SupplierController(SupplierService service) : ControllerBase { [HttpGet] + [Authorize("supplier.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("supplier.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class SupplierController(SupplierService service) : ControllerBase } [HttpPost] + [Authorize("supplier.manage")] public async Task Create(CreateSupplierRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class SupplierController(SupplierService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("supplier.manage")] public async Task Update(Guid id, UpdateSupplierRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class SupplierController(SupplierService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("supplier.manage")] public async Task Delete(Guid id, CancellationToken ct) { try @@ -43,6 +49,7 @@ public class SupplierController(SupplierService service) : ControllerBase } [HttpPost("{id:guid}/evaluate")] + [Authorize("supplier.view")] public async Task Evaluate(Guid id, CancellationToken ct) { var result = await service.EvaluateAsync(id, ct); diff --git a/Distribution/Features/SupplierClaims/SupplierClaimController.cs b/Distribution/Features/SupplierClaims/SupplierClaimController.cs index d678f42..f43ba1f 100644 --- a/Distribution/Features/SupplierClaims/SupplierClaimController.cs +++ b/Distribution/Features/SupplierClaims/SupplierClaimController.cs @@ -1,4 +1,5 @@ -using Distribution.Infra.Authorization; +using Distribution.Infra.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.SupplierClaims; @@ -7,10 +8,12 @@ namespace Distribution.Features.SupplierClaims; public class SupplierClaimController(SupplierClaimService service, ICurrentUserService currentUser) : ControllerBase { [HttpGet("api/supplier-claims")] + [Authorize("supplier-claim.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("api/supplier-claims/{id:guid}")] + [Authorize("supplier-claim.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,10 +21,12 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS } [HttpGet("api/purchase-orders/{id:guid}/claims")] + [Authorize("supplier-claim.view")] public async Task GetByPurchaseOrder(Guid id) => Ok(await service.GetByPurchaseOrderIdAsync(id)); [HttpPost("api/purchase-orders/{id:guid}/claims")] + [Authorize("supplier-claim.manage")] public async Task Create(Guid id, CreateSupplierClaimRequest request, CancellationToken ct) { // TEMPORARY: currentUser.UserId is always unset due to the broken auth middleware @@ -37,6 +42,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS } [HttpPut("api/supplier-claims/{id:guid}")] + [Authorize("supplier-claim.manage")] public async Task Update(Guid id, UpdateSupplierClaimRequest request, CancellationToken ct) { try @@ -48,6 +54,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS } [HttpPost("api/supplier-claims/{id:guid}/approve")] + [Authorize("supplier-claim.manage")] public async Task Approve(Guid id, ApproveSupplierClaimRequest request, CancellationToken ct) { var userId = currentUser.UserId ?? request.ApprovedByUserId; @@ -62,6 +69,7 @@ public class SupplierClaimController(SupplierClaimService service, ICurrentUserS } [HttpPost("api/supplier-claims/{id:guid}/reject")] + [Authorize("supplier-claim.manage")] public async Task Reject(Guid id, RejectSupplierClaimRequest request, CancellationToken ct) { var userId = currentUser.UserId ?? request.RejectedByUserId; diff --git a/Distribution/Features/Sync/SyncController.cs b/Distribution/Features/Sync/SyncController.cs index 2708196..ff40520 100644 --- a/Distribution/Features/Sync/SyncController.cs +++ b/Distribution/Features/Sync/SyncController.cs @@ -1,9 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Sync; [ApiController] [Route("api/sync")] +[Authorize("sync.manage")] public class SyncController(SyncService service) : ControllerBase { [HttpPost("uoms")] diff --git a/Distribution/Features/Uom/UomController.cs b/Distribution/Features/Uom/UomController.cs index da1ea22..d7351d5 100644 --- a/Distribution/Features/Uom/UomController.cs +++ b/Distribution/Features/Uom/UomController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Uom; @@ -7,10 +8,12 @@ namespace Distribution.Features.Uom; public class UomController(UomService service) : ControllerBase { [HttpGet] + [Authorize("uom.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("uom.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class UomController(UomService service) : ControllerBase } [HttpPost] + [Authorize("uom.manage")] public async Task Create(CreateUomRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class UomController(UomService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("uom.manage")] public async Task Update(Guid id, UpdateUomRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class UomController(UomService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("uom.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); diff --git a/Distribution/Features/User/UserController.cs b/Distribution/Features/User/UserController.cs index 09db811..03ffbd1 100644 --- a/Distribution/Features/User/UserController.cs +++ b/Distribution/Features/User/UserController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.User; @@ -7,10 +8,12 @@ namespace Distribution.Features.User; public class UserController(UserService service) : ControllerBase { [HttpGet] + [Authorize("user.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("user.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class UserController(UserService service) : ControllerBase } [HttpPost] + [Authorize("user.manage")] public async Task Create(CreateUserRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class UserController(UserService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("user.manage")] public async Task Update(Guid id, UpdateUserRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class UserController(UserService service) : ControllerBase } [HttpPut("{id:guid}/role")] + [Authorize("user.manage")] public async Task AssignRole(Guid id, AssignUserRoleRequest request, CancellationToken ct) { var result = await service.AssignRoleAsync(id, request, ct); @@ -39,6 +45,7 @@ public class UserController(UserService service) : ControllerBase } [HttpPut("{id:guid}/scope")] + [Authorize("user.manage")] public async Task AssignScope(Guid id, AssignUserScopeRequest request, CancellationToken ct) { var updated = await service.AssignScopeAsync(id, request, ct); @@ -46,6 +53,7 @@ public class UserController(UserService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("user.manage")] public async Task Deactivate(Guid id, CancellationToken ct) { var deactivated = await service.DeactivateAsync(id, ct); diff --git a/Distribution/Features/Vehicle/VehicleController.cs b/Distribution/Features/Vehicle/VehicleController.cs index d19dd1a..9ff2797 100644 --- a/Distribution/Features/Vehicle/VehicleController.cs +++ b/Distribution/Features/Vehicle/VehicleController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Vehicle; @@ -7,10 +8,12 @@ namespace Distribution.Features.Vehicle; public class VehicleController(VehicleService service) : ControllerBase { [HttpGet] + [Authorize("vehicle.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("vehicle.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class VehicleController(VehicleService service) : ControllerBase } [HttpPost] + [Authorize("vehicle.manage")] public async Task Create(CreateVehicleRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class VehicleController(VehicleService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("vehicle.manage")] public async Task Update(Guid id, UpdateVehicleRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class VehicleController(VehicleService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("vehicle.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); diff --git a/Distribution/Features/Warehouse/WarehouseController.cs b/Distribution/Features/Warehouse/WarehouseController.cs index 043dfbd..b9afc59 100644 --- a/Distribution/Features/Warehouse/WarehouseController.cs +++ b/Distribution/Features/Warehouse/WarehouseController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.Warehouse; @@ -7,10 +8,12 @@ namespace Distribution.Features.Warehouse; public class WarehouseController(WarehouseService service) : ControllerBase { [HttpGet] + [Authorize("warehouse.view")] public async Task GetAll() => Ok(await service.GetAllAsync()); [HttpGet("{id:guid}")] + [Authorize("warehouse.view")] public async Task GetById(Guid id) { var result = await service.GetByIdAsync(id); @@ -18,6 +21,7 @@ public class WarehouseController(WarehouseService service) : ControllerBase } [HttpPost] + [Authorize("warehouse.manage")] public async Task Create(CreateWarehouseRequest request, CancellationToken ct) { var result = await service.CreateAsync(request, ct); @@ -25,6 +29,7 @@ public class WarehouseController(WarehouseService service) : ControllerBase } [HttpPut("{id:guid}")] + [Authorize("warehouse.manage")] public async Task Update(Guid id, UpdateWarehouseRequest request, CancellationToken ct) { var result = await service.UpdateAsync(id, request, ct); @@ -32,6 +37,7 @@ public class WarehouseController(WarehouseService service) : ControllerBase } [HttpDelete("{id:guid}")] + [Authorize("warehouse.manage")] public async Task Delete(Guid id, CancellationToken ct) { var deleted = await service.DeleteAsync(id, ct); diff --git a/Distribution/Infra/Authorization/PermissionAuthorizationHandler.cs b/Distribution/Infra/Authorization/PermissionAuthorizationHandler.cs index 77b0e72..edcc4b3 100644 --- a/Distribution/Infra/Authorization/PermissionAuthorizationHandler.cs +++ b/Distribution/Infra/Authorization/PermissionAuthorizationHandler.cs @@ -1,23 +1,24 @@ - -//namespace Distribution.Infra.Authorization; +using Microsoft.AspNetCore.Authorization; -//public class PermissionAuthorizationHandler : AuthorizationHandler -//{ -// private readonly IPermissionService _permissionService; +namespace Distribution.Infra.Authorization; -// public PermissionAuthorizationHandler(IPermissionService permissionService) -// { -// _permissionService = permissionService; -// } +public class PermissionAuthorizationHandler : AuthorizationHandler +{ + private readonly IPermissionService _permissionService; -// protected override async Task HandleRequirementAsync( -// AuthorizationHandlerContext context, PermissionRequirement requirement) -// { -// var roleIdClaim = context.User.FindFirst("role_id")?.Value; -// if (!Guid.TryParse(roleIdClaim, out var roleId)) -// return; + public PermissionAuthorizationHandler(IPermissionService permissionService) + { + _permissionService = permissionService; + } -// if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode)) -// context.Succeed(requirement); -// } -//} + protected override async Task HandleRequirementAsync( + AuthorizationHandlerContext context, PermissionRequirement requirement) + { + var roleIdClaim = context.User.FindFirst("role_id")?.Value; + if (!Guid.TryParse(roleIdClaim, out var roleId)) + return; + + if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode)) + context.Succeed(requirement); + } +} diff --git a/Distribution/Infra/Authorization/PermissionPolicyProvider.cs b/Distribution/Infra/Authorization/PermissionPolicyProvider.cs index a27cb7d..9ee8ef9 100644 --- a/Distribution/Infra/Authorization/PermissionPolicyProvider.cs +++ b/Distribution/Infra/Authorization/PermissionPolicyProvider.cs @@ -1,31 +1,32 @@ -//using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; -//namespace Distribution.Infra.Authorization; +namespace Distribution.Infra.Authorization; -//public class PermissionPolicyProvider : IAuthorizationPolicyProvider -//{ -// private readonly DefaultAuthorizationPolicyProvider _fallback; +public class PermissionPolicyProvider : IAuthorizationPolicyProvider +{ + private readonly DefaultAuthorizationPolicyProvider _fallback; -// public PermissionPolicyProvider(IOptions options) -// { -// _fallback = new DefaultAuthorizationPolicyProvider(options); -// } + public PermissionPolicyProvider(IOptions options) + { + _fallback = new DefaultAuthorizationPolicyProvider(options); + } -// public Task GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync(); + public Task GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync(); -// public Task GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync(); + public Task GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync(); -// public Task GetPolicyAsync(string policyName) -// { -// if (policyName.Contains('.')) -// { -// var policy = new AuthorizationPolicyBuilder() -// .RequireAuthenticatedUser() -// .AddRequirements(new PermissionRequirement(policyName)) -// .Build(); -// return Task.FromResult(policy); -// } + public Task GetPolicyAsync(string policyName) + { + if (policyName.Contains('.')) + { + var policy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .AddRequirements(new PermissionRequirement(policyName)) + .Build(); + return Task.FromResult(policy); + } -// return _fallback.GetPolicyAsync(policyName); -// } -//} + return _fallback.GetPolicyAsync(policyName); + } +} diff --git a/Distribution/Infra/Authorization/PermissionRequirement.cs b/Distribution/Infra/Authorization/PermissionRequirement.cs index dacd23b..a25e8f6 100644 --- a/Distribution/Infra/Authorization/PermissionRequirement.cs +++ b/Distribution/Infra/Authorization/PermissionRequirement.cs @@ -1,12 +1,13 @@ - -//namespace Distribution.Infra.Authorization; +using Microsoft.AspNetCore.Authorization; -//public class PermissionRequirement : IAuthorizationRequirement -//{ -// public string PermissionCode { get; } +namespace Distribution.Infra.Authorization; -// public PermissionRequirement(string permissionCode) -// { -// PermissionCode = permissionCode; -// } -//} +public class PermissionRequirement : IAuthorizationRequirement +{ + public string PermissionCode { get; } + + public PermissionRequirement(string permissionCode) + { + PermissionCode = permissionCode; + } +} diff --git a/Distribution/Program.cs b/Distribution/Program.cs index 7308d1a..f3dde1e 100644 --- a/Distribution/Program.cs +++ b/Distribution/Program.cs @@ -1,6 +1,8 @@ using System.Text.Json.Serialization; using Distribution.Data; using Distribution.Features.Auth; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.Authorization; using Distribution.Middleware; using Distribution.Features.Brand; using Distribution.Features.Category; @@ -124,6 +126,9 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddAuthorization(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); // ── Auth Service connector ─────────────────────────────────────────────────── builder.Services.Configure( builder.Configuration.GetSection(AuthServiceOptions.SectionName)); @@ -145,8 +150,12 @@ builder.Services.AddCors(options => .AllowAnyHeader() .AllowAnyMethod())); -builder.Services.AddControllers() - .AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); +builder.Services.AddControllers(options => +{ + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + options.Filters.Add(new AuthorizeFilter(policy)); +}) +.AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApi(); builder.Services.AddSwaggerGen(); @@ -162,6 +171,8 @@ if (app.Environment.IsDevelopment()) //app.UseHttpsRedirection(); app.UseCors(); +app.UseAuthentication(); +app.UseAuthorization(); app.UseMiddleware(); app.MapControllers();