Files
pos-system/services/iam-service-net/src/IamService.API/Application/Commands/Roles/UpdateRolePermissionsCommand.cs
Ho Ngoc Hai 4849b7b6fc feat(permissions): implement full-stack role permission management
Backend (IAM Service):
- New GetRolePermissionsQuery + Handler: reads permissions from role_claims
- New UpdateRolePermissionsCommand + Handler: validates permission names
  against StaffPermissions enum, replaces role_claims, blocks system roles
- New endpoints: GET/PUT /api/v1/roles/{id}/permissions
- GetRolesQuery: batch-fetch permissions per role via role_claims join
- RoleResponse: add Permissions field to API response
- Seeded role_claims for Admin (7), Merchant (7), MerchantAdmin (6),
  MerchantStaff (2), SuperAdmin (All), Support (2)

Frontend (Blazor WASM):
- IamApiService: add Permissions to RoleDto, UpdateRolePermissionsAsync()
- RolePermissions.razor: replace hardcoded GetPermissionsForRole() with
  API-driven permission toggles from role_claims data
- Editable toggles for non-system roles, disabled for system roles
- Save/Cancel buttons appear when permissions modified
- 7 permission types matching StaffPermissions enum

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 19:50:06 +07:00

91 lines
3.4 KiB
C#

using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using IamService.Domain.AggregatesModel.RoleAggregate;
using IamService.Domain.Exceptions;
using IamService.Infrastructure;
namespace IamService.API.Application.Commands.Roles;
/// <summary>
/// EN: Command to update permissions for a role (stored in role_claims table).
/// VI: Command để cập nhật permissions cho một role (lưu trong bảng role_claims).
/// </summary>
public record UpdateRolePermissionsCommand(
Guid RoleId,
List<string> Permissions) : IRequest<bool>;
/// <summary>
/// EN: Valid permission names matching MerchantService StaffPermissions enum.
/// VI: Tên permission hợp lệ khớp với StaffPermissions enum của MerchantService.
/// </summary>
public static class ValidPermissions
{
public static readonly HashSet<string> Names = new(StringComparer.OrdinalIgnoreCase)
{
"ViewSales", "ProcessPayment", "RefundOrder",
"ManageInventory", "ViewReports", "ManageStaff", "ManageSettings", "All"
};
}
/// <summary>
/// EN: Handler for UpdateRolePermissionsCommand.
/// VI: Handler cho UpdateRolePermissionsCommand.
/// </summary>
public class UpdateRolePermissionsCommandHandler : IRequestHandler<UpdateRolePermissionsCommand, bool>
{
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly IamServiceContext _dbContext;
private readonly ILogger<UpdateRolePermissionsCommandHandler> _logger;
public UpdateRolePermissionsCommandHandler(
RoleManager<ApplicationRole> roleManager,
IamServiceContext dbContext,
ILogger<UpdateRolePermissionsCommandHandler> logger)
{
_roleManager = roleManager;
_dbContext = dbContext;
_logger = logger;
}
public async Task<bool> Handle(UpdateRolePermissionsCommand request, CancellationToken cancellationToken)
{
var role = await _roleManager.FindByIdAsync(request.RoleId.ToString())
?? throw new DomainException($"Role with ID {request.RoleId} not found");
if (role.IsSystemRole)
throw new DomainException("Cannot modify permissions of system roles");
// EN: Validate all permission names
// VI: Kiểm tra tất cả tên permission
var invalid = request.Permissions.Where(p => !ValidPermissions.Names.Contains(p)).ToList();
if (invalid.Any())
throw new DomainException($"Invalid permissions: {string.Join(", ", invalid)}");
// EN: Remove existing permission claims
// VI: Xóa permission claims hiện tại
var existing = await _dbContext.RoleClaims
.Where(rc => rc.RoleId == request.RoleId && rc.ClaimType == "permission")
.ToListAsync(cancellationToken);
_dbContext.RoleClaims.RemoveRange(existing);
// EN: Add new permission claims
// VI: Thêm permission claims mới
foreach (var perm in request.Permissions.Distinct())
{
_dbContext.RoleClaims.Add(new IdentityRoleClaim<Guid>
{
RoleId = request.RoleId,
ClaimType = "permission",
ClaimValue = perm
});
}
await _dbContext.SaveChangesAsync(cancellationToken);
_logger.LogInformation("Updated permissions for role {RoleName}: [{Permissions}]",
role.Name, string.Join(", ", request.Permissions));
return true;
}
}