updated the get project-level permissions

This commit is contained in:
ashutosh.nehete 2025-09-16 15:05:32 +05:30
parent 122e7074b8
commit d9a1832e2e

View File

@ -1648,31 +1648,30 @@ namespace Marco.Pms.Services.Service
employeeId, projectId, tenantId, loggedInEmployee.Id);
// Query the database for relevant project-level permission mappings
var permissionMappings = await _context.ProjectLevelPermissionMappings
.Include(p => p.Employee)
.ThenInclude(e => e!.JobRole)
.Include(p => p.Project)
.Include(p => p.Permission)
.ThenInclude(fp => fp!.Feature)
.AsNoTracking()
.Where(p => p.EmployeeId == employeeId
&& p.ProjectId == projectId
&& p.TenantId == tenantId)
.ToListAsync();
if (permissionMappings == null || !permissionMappings.Any())
var employeeTask = Task.Run(async () =>
{
_logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}",
employeeId, projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404);
}
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Employees.FirstOrDefaultAsync(e => e.Id == employeeId);
});
var projectTask = Task.Run(async () =>
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
return await context.Projects.FirstOrDefaultAsync(p => p.Id == projectId && p.TenantId == tenantId);
});
var permissionIdsTask = Task.Run(async () =>
{
return await GetPermissionIdsByProject(projectId, employeeId, tenantId);
});
// Map the employee, project, and permissions.
var employee = _mapper.Map<BasicEmployeeVM>(permissionMappings.First().Employee);
var project = _mapper.Map<BasicProjectVM>(permissionMappings.First().Project);
var permissions = permissionMappings
.Select(p => _mapper.Map<FeaturePermissionVM>(p.Permission))
.ToList();
await Task.WhenAll(employeeTask, projectTask, permissionIdsTask);
var employee = employeeTask.Result;
var project = projectTask.Result;
var permissionIds = permissionIdsTask.Result;
var permissions = await _context.FeaturePermissions
.Include(fp => fp.Feature)
.Where(fp => permissionIds.Contains(fp.Id)).ToListAsync();
if (employee == null)
{
@ -1685,12 +1684,26 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("Project not found", "Project not found in database", 404);
}
if (permissions == null || !permissions.Any())
{
_logger.LogWarning("No project-level permissions found for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}",
employeeId, projectId, tenantId);
return ApiResponse<object>.ErrorResponse("Project-Level Permissions not found", "Project-Level Permissions not found in database", 404);
}
// Map the employee, project, and permissions.
var employeeVm = _mapper.Map<BasicEmployeeVM>(employee);
var projectVm = _mapper.Map<BasicProjectVM>(project);
var permissionVms = permissions
.Select(p => _mapper.Map<FeaturePermissionVM>(p))
.ToList();
// Prepare the result object.
var result = new
{
Employee = employee,
Project = project,
Permissions = permissions
Employee = employeeVm,
Project = projectVm,
Permissions = permissionVms
};
_logger.LogInfo("Project-level permissions fetched successfully for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}",
@ -1797,57 +1810,7 @@ namespace Marco.Pms.Services.Service
}
public async Task<ApiResponse<object>> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId)
{
using var scope = _serviceScopeFactory.CreateScope();
var _rolesHelper = scope.ServiceProvider.GetRequiredService<RolesHelper>();
// 1. Try fetching permissions from cache (fast-path lookup).
var featurePermissionIds = await _cache.GetPermissions(loggedInEmployee.Id);
// If not found in cache, fallback to database (slower).
if (featurePermissionIds == null)
{
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(loggedInEmployee.Id);
featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList();
}
// 2. Handle project-level permission overrides if a project is specified.
if (projectId != Guid.Empty)
{
// Fetch permissions explicitly assigned to this employee in the project.
var projectLevelPermissionIds = await _context.ProjectLevelPermissionMappings
.Where(pl => pl.ProjectId == projectId && pl.EmployeeId == loggedInEmployee.Id)
.Select(pl => pl.PermissionId)
.ToListAsync();
if (projectLevelPermissionIds?.Any() ?? false)
{
// Define modules where project-level overrides apply.
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")
};
// Get all feature permissions under those modules where the user didn't have explicit project-level grants.
var allOverriddenPermissions = await _context.FeaturePermissions
.Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) &&
!projectLevelPermissionIds.Contains(fp.Id))
.Select(fp => fp.Id)
.ToListAsync();
// Apply overrides:
// - Remove global permissions overridden by project-level rules.
// - Add explicit project-level permissions.
featurePermissionIds = featurePermissionIds
.Except(allOverriddenPermissions) // Remove overridden
.Concat(projectLevelPermissionIds) // Add project-level
.Distinct() // Ensure no duplicates
.ToList();
}
}
var featurePermissionIds = await GetPermissionIdsByProject(projectId, loggedInEmployee.Id, tenantId);
return ApiResponse<object>.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200);
}
#endregion
@ -1859,13 +1822,11 @@ namespace Marco.Pms.Services.Service
List<Project> alloc = await _context.Projects.Where(c => c.TenantId == tanentId).ToListAsync();
return alloc;
}
public async Task<List<ProjectAllocation>> GetProjectByEmployeeID(Guid employeeId)
{
List<ProjectAllocation> alloc = await _context.ProjectAllocations.Where(c => c.EmployeeId == employeeId && c.IsActive == true).Include(c => c.Project).ToListAsync();
return alloc;
}
public async Task<List<ProjectAllocation>> GetTeamByProject(Guid TenantId, Guid ProjectId, bool IncludeInactive)
{
if (IncludeInactive)
@ -1882,7 +1843,6 @@ namespace Marco.Pms.Services.Service
return employees;
}
}
public async Task<List<Guid>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee)
{
using var scope = _serviceScopeFactory.CreateScope();
@ -1911,7 +1871,6 @@ namespace Marco.Pms.Services.Service
}
return projectIds;
}
public async Task<List<Guid>> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee)
{
using var scope = _serviceScopeFactory.CreateScope();
@ -1963,7 +1922,6 @@ namespace Marco.Pms.Services.Service
return newProjectIds;
}
/// <summary>
/// Retrieves a list of ProjectInfoVMs by their IDs, using an efficient partial cache-hit strategy.
/// It fetches what it can from the cache (as ProjectMongoDB), gets the rest from the
@ -2014,7 +1972,6 @@ namespace Marco.Pms.Services.Service
return finalViewModels;
}
private async Task<ProjectDetailsVM> GetProjectViewModel(Guid? id, Project project)
{
ProjectDetailsVM vm = new ProjectDetailsVM();
@ -2164,7 +2121,6 @@ namespace Marco.Pms.Services.Service
// Map from the database entity to the response ViewModel.
return dbProject;
}
private async Task UpdateCacheInBackground(Project project)
{
try
@ -2182,7 +2138,6 @@ namespace Marco.Pms.Services.Service
_logger.LogError(ex, "Background cache update failed for project {ProjectId} ", project.Id);
}
}
private async Task UpdateCacheAndNotify(Dictionary<Guid, (double Planned, double Completed)> workDelta, List<WorkItem> affectedItems)
{
try
@ -2204,7 +2159,6 @@ namespace Marco.Pms.Services.Service
_logger.LogError(ex, "An error occurred during background cache update/notification.");
}
}
private void ProcessBuilding(BuildingDto dto, Guid tenantId, InfraVM responseData, List<string> messages, ISet<Guid> projectIds, List<Task> cacheTasks)
{
Building building = _mapper.Map<Building>(dto);
@ -2227,7 +2181,6 @@ namespace Marco.Pms.Services.Service
responseData.building = building;
projectIds.Add(building.ProjectId);
}
private void ProcessFloor(FloorDto dto, Guid tenantId, InfraVM responseData, List<string> messages, ISet<Guid> projectIds, List<Task> cacheTasks, IDictionary<Guid, Building> buildings)
{
Floor floor = _mapper.Map<Floor>(dto);
@ -2253,7 +2206,6 @@ namespace Marco.Pms.Services.Service
responseData.floor = floor;
if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId);
}
private void ProcessWorkArea(WorkAreaDto dto, Guid tenantId, InfraVM responseData, List<string> messages, ISet<Guid> projectIds, List<Task> cacheTasks, IDictionary<Guid, Floor> floors)
{
WorkArea workArea = _mapper.Map<WorkArea>(dto);
@ -2280,6 +2232,63 @@ namespace Marco.Pms.Services.Service
responseData.workArea = workArea;
if (parentBuilding != null) projectIds.Add(parentBuilding.ProjectId);
}
private async Task<List<Guid>> GetPermissionIdsByProject(Guid projectId, Guid EmployeeId, Guid tenantId)
{
await using var context = await _dbContextFactory.CreateDbContextAsync();
using var scope = _serviceScopeFactory.CreateScope();
var _rolesHelper = scope.ServiceProvider.GetRequiredService<RolesHelper>();
// 1. Try fetching permissions from cache (fast-path lookup).
var featurePermissionIds = await _cache.GetPermissions(EmployeeId);
// If not found in cache, fallback to database (slower).
if (featurePermissionIds == null)
{
var featurePermissions = await _rolesHelper.GetFeaturePermissionByEmployeeId(EmployeeId);
featurePermissionIds = featurePermissions.Select(fp => fp.Id).ToList();
}
// 2. Handle project-level permission overrides if a project is specified.
if (projectId != Guid.Empty)
{
// Fetch permissions explicitly assigned to this employee in the project.
var projectLevelPermissionIds = await context.ProjectLevelPermissionMappings
.Where(pl => pl.ProjectId == projectId && pl.EmployeeId == EmployeeId && pl.TenantId == tenantId)
.Select(pl => pl.PermissionId)
.ToListAsync();
if (projectLevelPermissionIds?.Any() ?? false)
{
// Define modules where project-level overrides apply.
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")
};
// Get all feature permissions under those modules where the user didn't have explicit project-level grants.
var allOverriddenPermissions = await context.FeaturePermissions
.Where(fp => projectLevelModuleIds.Contains(fp.FeatureId) &&
!projectLevelPermissionIds.Contains(fp.Id))
.Select(fp => fp.Id)
.ToListAsync();
// Apply overrides:
// - Remove global permissions overridden by project-level rules.
// - Add explicit project-level permissions.
featurePermissionIds = featurePermissionIds
.Except(allOverriddenPermissions) // Remove overridden
.Concat(projectLevelPermissionIds) // Add project-level
.Distinct() // Ensure no duplicates
.ToList();
}
}
return featurePermissionIds;
}
#endregion
}