Added the GetAllPermissionFroProject API

This commit is contained in:
ashutosh.nehete 2025-09-16 13:34:22 +05:30
parent e1b045f852
commit 122e7074b8
3 changed files with 108 additions and 12 deletions

View File

@ -470,6 +470,13 @@ namespace MarcoBMS.Services.Controllers
var response = await _projectServices.GetAssignedProjectLevelPermissionAsync(employeeId, projectId, tenantId, loggedInEmployee); var response = await _projectServices.GetAssignedProjectLevelPermissionAsync(employeeId, projectId, tenantId, loggedInEmployee);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("get/all/project-level-permission/{projectId}")]
public async Task<IActionResult> GetAllPermissionFroProject(Guid projectId)
{
Employee loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
var response = await _projectServices.GetAllPermissionFroProjectAsync(projectId, loggedInEmployee, tenantId);
return StatusCode(response.StatusCode, response);
}
[HttpGet("get/proejct-level/modules")] [HttpGet("get/proejct-level/modules")]
public async Task<IActionResult> AssignProjectLevelModules() public async Task<IActionResult> AssignProjectLevelModules()
{ {

View File

@ -16,6 +16,7 @@ using Marco.Pms.Model.ViewModels.Master;
using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Model.ViewModels.Projects;
using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Helpers;
using Marco.Pms.Services.Service.ServiceInterfaces; using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service; using MarcoBMS.Services.Service;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -28,26 +29,23 @@ namespace Marco.Pms.Services.Service
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory; private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate
private readonly ILoggingService _logger; private readonly ILoggingService _logger;
private readonly PermissionServices _permission;
private readonly CacheUpdateHelper _cache; private readonly CacheUpdateHelper _cache;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly GeneralHelper _generalHelper; private readonly IServiceScopeFactory _serviceScopeFactory;
public ProjectServices( public ProjectServices(
IDbContextFactory<ApplicationDbContext> dbContextFactory, IDbContextFactory<ApplicationDbContext> dbContextFactory,
ApplicationDbContext context, ApplicationDbContext context,
ILoggingService logger, ILoggingService logger,
PermissionServices permission, IServiceScopeFactory serviceScopeFactory,
CacheUpdateHelper cache, CacheUpdateHelper cache,
IMapper mapper, IMapper mapper)
GeneralHelper generalHelper)
{ {
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
_context = context ?? throw new ArgumentNullException(nameof(context)); _context = context ?? throw new ArgumentNullException(nameof(context));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_permission = permission ?? throw new ArgumentNullException(nameof(permission));
_cache = cache ?? throw new ArgumentNullException(nameof(cache)); _cache = cache ?? throw new ArgumentNullException(nameof(cache));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_generalHelper = generalHelper ?? throw new ArgumentNullException(nameof(generalHelper));
} }
#region =================================================================== Project Get APIs =================================================================== #region =================================================================== Project Get APIs ===================================================================
@ -87,7 +85,6 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
public async Task<ApiResponse<object>> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee)
{ {
try try
@ -146,11 +143,12 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
public async Task<ApiResponse<object>> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee)
{ {
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- Step 1: Run independent operations in PARALLEL --- // --- Step 1: Run independent operations in PARALLEL ---
// We can check permissions and fetch data at the same time to reduce latency. // We can check permissions and fetch data at the same time to reduce latency.
var permissionTask = _permission.HasProjectPermission(loggedInEmployee, id); var permissionTask = _permission.HasProjectPermission(loggedInEmployee, id);
@ -190,13 +188,15 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred.", null, 500);
} }
} }
public async Task<ApiResponse<object>> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee)
{ {
try try
{ {
_logger.LogInfo("Details requested by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, id); _logger.LogInfo("Details requested by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, id);
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// Step 1: Check global view project permission // Step 1: Check global view project permission
var hasViewProjectPermission = await _permission.HasPermission(PermissionsMaster.ViewProject, loggedInEmployee.Id, id); var hasViewProjectPermission = await _permission.HasPermission(PermissionsMaster.ViewProject, loggedInEmployee.Id, id);
if (!hasViewProjectPermission) if (!hasViewProjectPermission)
@ -252,7 +252,6 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); return ApiResponse<object>.ErrorResponse("An internal server error occurred. Please try again later.", null, 500);
} }
} }
public async Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee) public async Task<ApiResponse<object>> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee)
{ {
var project = await _context.Projects var project = await _context.Projects
@ -401,6 +400,8 @@ namespace Marco.Pms.Services.Service
{ {
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- Step 1: Fetch the Existing Entity from the Database --- // --- Step 1: Fetch the Existing Entity from the Database ---
// This is crucial to avoid the data loss bug. We only want to modify an existing record. // This is crucial to avoid the data loss bug. We only want to modify an existing record.
var existingProject = await _context.Projects var existingProject = await _context.Projects
@ -489,6 +490,9 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- CRITICAL: Security Check --- // --- CRITICAL: Security Check ---
// Before fetching data, you MUST verify the user has permission to see it. // Before fetching data, you MUST verify the user has permission to see it.
// This is a placeholder for your actual permission logic. // This is a placeholder for your actual permission logic.
@ -559,6 +563,9 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- Step 2: Security and Existence Checks --- // --- Step 2: Security and Existence Checks ---
// Before fetching data, you MUST verify the user has permission to see it. // Before fetching data, you MUST verify the user has permission to see it.
// This is a placeholder for your actual permission logic. // This is a placeholder for your actual permission logic.
@ -627,6 +634,9 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("Starting to manage {AllocationCount} allocations for user {UserId}.", allocationsDto.Count, loggedInEmployee.Id); _logger.LogInfo("Starting to manage {AllocationCount} allocations for user {UserId}.", allocationsDto.Count, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- (Placeholder) Security Check --- // --- (Placeholder) Security Check ---
// In a real application, you would check if the loggedInEmployee has permission // In a real application, you would check if the loggedInEmployee has permission
// to manage allocations for ALL projects involved in this batch. // to manage allocations for ALL projects involved in this batch.
@ -735,6 +745,9 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- Step 2: Clarified Security Check --- // --- Step 2: Clarified Security Check ---
// The permission should be about viewing another employee's assignments, not a generic "Manage Team". // The permission should be about viewing another employee's assignments, not a generic "Manage Team".
// This is a placeholder for your actual, more specific permission logic. // This is a placeholder for your actual, more specific permission logic.
@ -808,6 +821,9 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("Starting to manage {AllocationCount} project assignments for Employee {EmployeeId}.", allocationsDto.Count, employeeId); _logger.LogInfo("Starting to manage {AllocationCount} project assignments for Employee {EmployeeId}.", allocationsDto.Count, employeeId);
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- (Placeholder) Security Check --- // --- (Placeholder) Security Check ---
// You MUST verify that the loggedInEmployee has permission to modify the assignments for the target employeeId. // You MUST verify that the loggedInEmployee has permission to modify the assignments for the target employeeId.
var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id); var hasPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id);
@ -977,6 +993,9 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var _generalHelper = scope.ServiceProvider.GetRequiredService<GeneralHelper>();
// --- Step 1: Run independent permission checks in PARALLEL --- // --- Step 1: Run independent permission checks in PARALLEL ---
var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId);
var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId);
@ -1033,6 +1052,9 @@ namespace Marco.Pms.Services.Service
try try
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var _generalHelper = scope.ServiceProvider.GetRequiredService<GeneralHelper>();
// --- Step 1: Cache-First Strategy --- // --- Step 1: Cache-First Strategy ---
var cachedWorkItems = await _cache.GetWorkItemDetailsByWorkArea(workAreaId); var cachedWorkItems = await _cache.GetWorkItemDetailsByWorkArea(workAreaId);
if (cachedWorkItems != null) if (cachedWorkItems != null)
@ -1268,6 +1290,9 @@ namespace Marco.Pms.Services.Service
{ {
_logger.LogInfo("CreateProjectTask called with {Count} items by user {UserId}", workItemDtos?.Count ?? 0, loggedInEmployee.Id); _logger.LogInfo("CreateProjectTask called with {Count} items by user {UserId}", workItemDtos?.Count ?? 0, loggedInEmployee.Id);
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// --- Step 1: Input Validation --- // --- Step 1: Input Validation ---
if (workItemDtos == null || !workItemDtos.Any()) if (workItemDtos == null || !workItemDtos.Any())
{ {
@ -1382,7 +1407,6 @@ namespace Marco.Pms.Services.Service
return ApiResponse<List<WorkItemVM>>.SuccessResponse(responseList, message, 200); return ApiResponse<List<WorkItemVM>>.SuccessResponse(responseList, message, 200);
} }
public async Task<ServiceResponse> DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee) public async Task<ServiceResponse> DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee)
{ {
// 1. Fetch the task and its parent data in a single query. // 1. Fetch the task and its parent data in a single query.
@ -1483,6 +1507,9 @@ namespace Marco.Pms.Services.Service
_logger.LogInfo("ManageProjectLevelPermissionAsync started for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}", _logger.LogInfo("ManageProjectLevelPermissionAsync started for EmployeeId: {EmployeeId}, ProjectId: {ProjectId}, TenantId: {TenantId}",
model.EmployeeId, model.ProjectId, tenantId); model.EmployeeId, model.ProjectId, tenantId);
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var hasTeamPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, model.ProjectId); var hasTeamPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, model.ProjectId);
if (!hasTeamPermission) if (!hasTeamPermission)
{ {
@ -1768,6 +1795,61 @@ namespace Marco.Pms.Services.Service
return ApiResponse<object>.ErrorResponse("An error occurred while retrieving employees with project-level permissions.", 500); return ApiResponse<object>.ErrorResponse("An error occurred while retrieving employees with project-level permissions.", 500);
} }
} }
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();
}
}
return ApiResponse<object>.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200);
}
#endregion #endregion
#region =================================================================== Helper Functions =================================================================== #region =================================================================== Helper Functions ===================================================================
@ -1803,6 +1885,9 @@ namespace Marco.Pms.Services.Service
public async Task<List<Guid>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) public async Task<List<Guid>> GetMyProjects(Guid tenantId, Employee LoggedInEmployee)
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
var projectIds = await _cache.GetProjects(LoggedInEmployee.Id); var projectIds = await _cache.GetProjects(LoggedInEmployee.Id);
if (projectIds == null) if (projectIds == null)
@ -1829,6 +1914,9 @@ namespace Marco.Pms.Services.Service
public async Task<List<Guid>> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee) public async Task<List<Guid>> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee)
{ {
using var scope = _serviceScopeFactory.CreateScope();
var _permission = scope.ServiceProvider.GetRequiredService<PermissionServices>();
// 1. Attempt to retrieve the list of project IDs from the cache first. // 1. Attempt to retrieve the list of project IDs from the cache first.
// This is the "happy path" and should be as fast as possible. // This is the "happy path" and should be as fast as possible.
List<Guid>? projectIds = await _cache.GetProjects(loggedInEmployee.Id); List<Guid>? projectIds = await _cache.GetProjects(loggedInEmployee.Id);

View File

@ -40,6 +40,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces
Task<ApiResponse<object>> GetAssignedProjectLevelPermissionAsync(Guid employeeId, Guid projectId, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetAssignedProjectLevelPermissionAsync(Guid employeeId, Guid projectId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> AssignProjectLevelModulesAsync(Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> AssignProjectLevelModulesAsync(Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); Task<ApiResponse<object>> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee);
Task<ApiResponse<object>> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId);
} }
} }