Files
PowderCoatingLogix/src/PowderCoating.Infrastructure/Services/ApplicationUserClaimsPrincipalFactory.cs
T
spouliot feff0fa73d Add Accountant role and CanManageBills/CanManageAccounting permissions
- AppConstants: add Accountant to CompanyRoles; add CanManageBills and
  CanManageAccounting to Policies
- ApplicationUser: add CanManageBills and CanManageAccounting bool fields
- UserManagementDtos: expose new fields in all three DTOs
- ClaimsPrincipalFactory: emit ManageBills and ManageAccounting claims
- Program.cs: add CanManageBills and CanManageAccounting policies;
  update CanManageInvoices, CanViewReports, CanManagePurchaseOrders,
  and CanManageVendors to auto-pass for Accountant role
- BillsController: replace CanManageInventory with CanManageBills on
  all write actions (correct policy — bills are not inventory)
- BankReconciliationsController: replace CanManageJobs with
  CanManageAccounting on write actions
- CompanyUsersController: add Accountant to validCompanyRoles (both
  Create/Edit), legacyRole switch, and all permission assignment blocks
- Create/Edit views: add Accountant option to role dropdown; add
  CanManageBills and CanManageAccounting checkboxes; JS auto-checks
  financial permissions when Accountant role is selected
- Migration AddAccountantRolePermissions: adds columns + backfills
  CanManageBills=1 and CanManageAccounting=1 for all CompanyAdmin users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 19:42:53 -04:00

153 lines
5.0 KiB
C#

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using PowderCoating.Core.Entities;
using PowderCoating.Infrastructure.Data;
using System.Security.Claims;
namespace PowderCoating.Infrastructure.Services;
/// <summary>
/// Custom claims principal factory that adds company-specific claims to the user's identity
/// </summary>
public class ApplicationUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly ApplicationDbContext _context;
public ApplicationUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> options,
ApplicationDbContext context)
: base(userManager, roleManager, options)
{
_context = context;
}
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
{
var identity = await base.GenerateClaimsAsync(user);
// Add CompanyId claim if user has a company
if (user.CompanyId > 0)
{
identity.AddClaim(new Claim("CompanyId", user.CompanyId.ToString()));
// Add subscription plan display name from DB (for display in nav)
var company = await _context.Companies.IgnoreQueryFilters()
.Where(c => c.Id == user.CompanyId && !c.IsDeleted)
.Select(c => new { c.SubscriptionPlan })
.FirstOrDefaultAsync();
if (company != null)
{
// Look up the DisplayName from SubscriptionPlanConfig so it reflects DB values
var planConfig = await _context.SubscriptionPlanConfigs.IgnoreQueryFilters()
.Where(p => p.Plan == company.SubscriptionPlan && p.IsActive && !p.IsDeleted)
.Select(p => new { p.DisplayName })
.FirstOrDefaultAsync();
var planName = planConfig?.DisplayName ?? company.SubscriptionPlan.ToString();
identity.AddClaim(new Claim("SubscriptionPlan", planName));
}
}
// Add CompanyRole claim if user has a company role
if (!string.IsNullOrEmpty(user.CompanyRole))
{
identity.AddClaim(new Claim("CompanyRole", user.CompanyRole));
}
// Add user's full name for display purposes
identity.AddClaim(new Claim("FullName", user.FullName));
// Add appearance claims
identity.AddClaim(new Claim("Theme", user.Theme ?? "light"));
identity.AddClaim(new Claim("SidebarColor", user.SidebarColor ?? "ocean"));
identity.AddClaim(new Claim("HasProfilePicture",
(!string.IsNullOrEmpty(user.ProfilePictureFilePath)).ToString().ToLower()));
identity.AddClaim(new Claim("ProfilePictureVersion", (user.UpdatedAt ?? DateTime.UtcNow).Ticks.ToString()));
// Add permission claims for easier authorization
if (user.CanManageJobs)
{
identity.AddClaim(new Claim("Permission", "ManageJobs"));
}
if (user.CanManageInventory)
{
identity.AddClaim(new Claim("Permission", "ManageInventory"));
}
if (user.CanManageCustomers)
{
identity.AddClaim(new Claim("Permission", "ManageCustomers"));
}
if (user.CanCreateQuotes)
{
identity.AddClaim(new Claim("Permission", "CreateQuotes"));
}
if (user.CanApproveQuotes)
{
identity.AddClaim(new Claim("Permission", "ApproveQuotes"));
}
if (user.CanManageCalendar)
{
identity.AddClaim(new Claim("Permission", "ManageCalendar"));
}
if (user.CanViewCalendar)
{
identity.AddClaim(new Claim("Permission", "ViewCalendar"));
}
if (user.CanManageProducts)
{
identity.AddClaim(new Claim("Permission", "ManageProducts"));
}
if (user.CanViewProducts)
{
identity.AddClaim(new Claim("Permission", "ViewProducts"));
}
if (user.CanManageEquipment)
{
identity.AddClaim(new Claim("Permission", "ManageEquipment"));
}
if (user.CanManageVendors)
{
identity.AddClaim(new Claim("Permission", "ManageVendors"));
}
if (user.CanManageMaintenance)
{
identity.AddClaim(new Claim("Permission", "ManageMaintenance"));
}
if (user.CanManageInvoices)
{
identity.AddClaim(new Claim("Permission", "ManageInvoices"));
}
if (user.CanViewReports)
{
identity.AddClaim(new Claim("Permission", "ViewReports"));
}
if (user.CanManageBills)
{
identity.AddClaim(new Claim("Permission", "ManageBills"));
}
if (user.CanManageAccounting)
{
identity.AddClaim(new Claim("Permission", "ManageAccounting"));
}
return identity;
}
}