Added a function to check if have service project permission
This commit is contained in:
parent
e73413c849
commit
bcba454b6e
@ -3,6 +3,7 @@ using Marco.Pms.Model.Employees;
|
|||||||
using Marco.Pms.Model.Entitlements;
|
using Marco.Pms.Model.Entitlements;
|
||||||
using Marco.Pms.Services.Helpers;
|
using Marco.Pms.Services.Helpers;
|
||||||
using MarcoBMS.Services.Helpers;
|
using MarcoBMS.Services.Helpers;
|
||||||
|
using MarcoBMS.Services.Service;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Marco.Pms.Services.Service
|
namespace Marco.Pms.Services.Service
|
||||||
@ -12,49 +13,18 @@ namespace Marco.Pms.Services.Service
|
|||||||
private readonly ApplicationDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
private readonly RolesHelper _rolesHelper;
|
private readonly RolesHelper _rolesHelper;
|
||||||
private readonly CacheUpdateHelper _cache;
|
private readonly CacheUpdateHelper _cache;
|
||||||
|
private readonly ILoggingService _logger;
|
||||||
private readonly Guid tenantId;
|
private readonly Guid tenantId;
|
||||||
|
|
||||||
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, UserHelper userHelper)
|
public PermissionServices(ApplicationDbContext context, RolesHelper rolesHelper, CacheUpdateHelper cache, ILoggingService logger, UserHelper userHelper)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_rolesHelper = rolesHelper;
|
_rolesHelper = rolesHelper;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
|
_logger = logger;
|
||||||
tenantId = userHelper.GetTenantId();
|
tenantId = userHelper.GetTenantId();
|
||||||
}
|
}
|
||||||
|
|
||||||
//public async Task<bool> HasPermission(Guid featurePermissionId, Guid employeeId, Guid? projectId = null)
|
|
||||||
//{
|
|
||||||
// var featurePermissionIds = await _cache.GetPermissions(employeeId);
|
|
||||||
// if (featurePermissionIds == null)
|
|
||||||
// {
|
|
||||||
// List<FeaturePermission> featurePermission = await _rolesHelper.GetFeaturePermissionByEmployeeId(employeeId);
|
|
||||||
// featurePermissionIds = featurePermission.Select(fp => fp.Id).ToList();
|
|
||||||
// }
|
|
||||||
// if (projectId != null)
|
|
||||||
// {
|
|
||||||
// var projectLevelPerissionIds = await _context.ProjectLevelPermissionMappings
|
|
||||||
// .Where(pl => pl.ProjectId == projectId.Value && pl.EmployeeId == employeeId).Select(pl => pl.PermissionId).ToListAsync();
|
|
||||||
|
|
||||||
// var projectLevelModuleIds = new HashSet<Guid>
|
|
||||||
// {
|
|
||||||
// Guid.Parse("53176ebf-c75d-42e5-839f-4508ffac3def"),
|
|
||||||
// Guid.Parse("9d4b5489-2079-40b9-bd77-6e1bf90bc19f"),
|
|
||||||
// Guid.Parse("81ab8a87-8ccd-4015-a917-0627cee6a100"),
|
|
||||||
// Guid.Parse("52c9cf54-1eb2-44d2-81bb-524cf29c0a94"),
|
|
||||||
// Guid.Parse("a8cf4331-8f04-4961-8360-a3f7c3cc7462")
|
|
||||||
// };
|
|
||||||
|
|
||||||
// var allProjectLevelPermissionIds = await _context.FeaturePermissions
|
|
||||||
// .Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) && !projectLevelPerissionIds.Contains(fp.Id)).Select(fp => fp.Id).ToListAsync();
|
|
||||||
// featurePermissionIds.RemoveRange(allProjectLevelPermissionIds);
|
|
||||||
|
|
||||||
// featurePermissionIds.AddRange(projectLevelPerissionIds);
|
|
||||||
// featurePermissionIds = featurePermissionIds.Distinct().ToList();
|
|
||||||
// }
|
|
||||||
// var hasPermission = featurePermissionIds.Contains(featurePermissionId);
|
|
||||||
// return hasPermission;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether an employee has a specific feature permission, optionally within a project context.
|
/// Checks whether an employee has a specific feature permission, optionally within a project context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -156,5 +126,78 @@ namespace Marco.Pms.Services.Service
|
|||||||
}
|
}
|
||||||
return projectIds.Contains(projectId);
|
return projectIds.Contains(projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if an employee has permission to access a specific service project.
|
||||||
|
/// Permission is granted if the user is directly allocated to the project OR
|
||||||
|
/// assigned to any active job ticket within the project.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="loggedInEmployeeId">The ID of the user requesting access.</param>
|
||||||
|
/// <param name="projectId">The ID of the project to access.</param>
|
||||||
|
/// <returns>True if access is allowed, otherwise False.</returns>
|
||||||
|
public async Task<bool> HasServiceProjectPermission(Guid loggedInEmployeeId, Guid projectId)
|
||||||
|
{
|
||||||
|
Guid ReviewDoneStatus = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7");
|
||||||
|
Guid ClosedStatus = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69");
|
||||||
|
// 1. Input Validation
|
||||||
|
if (loggedInEmployeeId == Guid.Empty || projectId == Guid.Empty)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Permission check failed: Invalid input parameters. EmployeeId: {EmployeeId}, ProjectId: {ProjectId}", loggedInEmployeeId, projectId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInfo("Starting permission check for Employee: {EmployeeId} on Project: {ProjectId}", loggedInEmployeeId, projectId);
|
||||||
|
|
||||||
|
// 2. Check Level 1: Is the user a generic Team Member of the project?
|
||||||
|
// This is usually the most common case, so checking this first saves complex query execution.
|
||||||
|
bool isTeamMember = await _context.ServiceProjectAllocations
|
||||||
|
.AsNoTracking() // Optimization: Read-only query does not need tracking
|
||||||
|
.AnyAsync(spa => spa.ProjectId == projectId
|
||||||
|
&& spa.EmployeeId == loggedInEmployeeId
|
||||||
|
&& spa.IsActive
|
||||||
|
&& spa.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (isTeamMember)
|
||||||
|
{
|
||||||
|
_logger.LogInfo("Access Granted: User {EmployeeId} is a team member of Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Check Level 2: Is the user assigned to any ACTIVE specific Job Ticket?
|
||||||
|
// Optimization: Combined the check for JobTicket and Mapping into a single Join query.
|
||||||
|
// This prevents pulling a list of JobIds into memory (fixing memory bloat) and reduces DB roundtrips.
|
||||||
|
bool hasActiveJobAssignment = await _context.JobTickets
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(jt => jt.ProjectId == projectId
|
||||||
|
&& jt.StatusId != ReviewDoneStatus
|
||||||
|
&& jt.StatusId != ClosedStatus
|
||||||
|
&& jt.IsActive)
|
||||||
|
.Join(_context.JobEmployeeMappings,
|
||||||
|
ticket => ticket.Id,
|
||||||
|
mapping => mapping.JobTicketId,
|
||||||
|
(ticket, mapping) => mapping)
|
||||||
|
.AnyAsync(mapping => mapping.AssigneeId == loggedInEmployeeId
|
||||||
|
&& mapping.TenantId == tenantId);
|
||||||
|
|
||||||
|
if (hasActiveJobAssignment)
|
||||||
|
{
|
||||||
|
_logger.LogInfo("Access Granted: User {EmployeeId} is assigned active tickets in Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Default Deny
|
||||||
|
_logger.LogWarning("Access Denied: User {EmployeeId} has no permissions for Project {ProjectId}.", loggedInEmployeeId, projectId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 5. Robust Error Handling
|
||||||
|
// Log the full stack trace for debugging, but return false to maintain security (fail-closed).
|
||||||
|
_logger.LogError(ex, "An error occurred while checking permissions for Employee: {EmployeeId} on Project: {ProjectId}", loggedInEmployeeId, projectId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,8 +36,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
private readonly Guid NewStatus = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918");
|
private readonly Guid NewStatus = Guid.Parse("32d76a02-8f44-4aa0-9b66-c3716c45a918");
|
||||||
private readonly Guid AssignedStatus = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66");
|
private readonly Guid AssignedStatus = Guid.Parse("cfa1886d-055f-4ded-84c6-42a2a8a14a66");
|
||||||
private readonly Guid InProgressStatus = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d");
|
private readonly Guid InProgressStatus = Guid.Parse("5a6873a5-fed7-4745-a52f-8f61bf3bd72d");
|
||||||
private readonly Guid ReviewStatus = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0");
|
private readonly Guid WorkDoneStatus = Guid.Parse("aab71020-2fb8-44d9-9430-c9a7e9bf33b0");
|
||||||
private readonly Guid DoneStatus = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7");
|
private readonly Guid ReviewDoneStatus = Guid.Parse("ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7");
|
||||||
private readonly Guid ClosedStatus = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69");
|
private readonly Guid ClosedStatus = Guid.Parse("3ddeefb5-ae3c-4e10-a922-35e0a452bb69");
|
||||||
private readonly Guid OnHoldStatus = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2");
|
private readonly Guid OnHoldStatus = Guid.Parse("75a0c8b8-9c6a-41af-80bf-b35bab722eb2");
|
||||||
|
|
||||||
@ -130,8 +130,8 @@ namespace Marco.Pms.Services.Service
|
|||||||
.Select(g => new
|
.Select(g => new
|
||||||
{
|
{
|
||||||
ProjectId = g.Key,
|
ProjectId = g.Key,
|
||||||
JobsPassedDueDateCount = g.Count(jt => jt.StatusId != DoneStatus && jt.StatusId != ClosedStatus && jt.DueDate.Date < DateTime.UtcNow.Date),
|
JobsPassedDueDateCount = g.Count(jt => jt.StatusId != ReviewDoneStatus && jt.StatusId != ClosedStatus && jt.DueDate.Date < DateTime.UtcNow.Date),
|
||||||
ActiveJobsCount = g.Count(jt => jt.StatusId != DoneStatus && jt.StatusId != ClosedStatus && jt.StatusId != OnHoldStatus),
|
ActiveJobsCount = g.Count(jt => jt.StatusId != ReviewDoneStatus && jt.StatusId != ClosedStatus && jt.StatusId != OnHoldStatus),
|
||||||
AssignedJobsCount = g.Count(jt => jt.StatusId == AssignedStatus),
|
AssignedJobsCount = g.Count(jt => jt.StatusId == AssignedStatus),
|
||||||
OnHoldJobsCount = g.Count(jt => jt.StatusId == OnHoldStatus)
|
OnHoldJobsCount = g.Count(jt => jt.StatusId == OnHoldStatus)
|
||||||
})
|
})
|
||||||
@ -2164,7 +2164,7 @@ namespace Marco.Pms.Services.Service
|
|||||||
if (jobTicket.IsArchive != model.IsArchive)
|
if (jobTicket.IsArchive != model.IsArchive)
|
||||||
{
|
{
|
||||||
// Validate if job ticket status permits archiving
|
// Validate if job ticket status permits archiving
|
||||||
if (model.IsArchive && jobTicket.StatusId != DoneStatus && jobTicket.StatusId != ClosedStatus)
|
if (model.IsArchive && jobTicket.StatusId != ReviewDoneStatus && jobTicket.StatusId != ClosedStatus)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Archiving failed: Job status not eligible. JobTicketId: {JobTicketId}, StatusId: {StatusId}", jobTicket.Id, jobTicket.StatusId);
|
_logger.LogWarning("Archiving failed: Job status not eligible. JobTicketId: {JobTicketId}, StatusId: {StatusId}", jobTicket.Id, jobTicket.StatusId);
|
||||||
return ApiResponse<object>.ErrorResponse(
|
return ApiResponse<object>.ErrorResponse(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user