From 122e7074b8cb12e1c3cce848de960cacf32a134d Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 16 Sep 2025 13:34:22 +0530 Subject: [PATCH] Added the GetAllPermissionFroProject API --- .../Controllers/ProjectController.cs | 7 ++ Marco.Pms.Services/Service/ProjectServices.cs | 112 ++++++++++++++++-- .../ServiceInterfaces/IProjectServices.cs | 1 + 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/Marco.Pms.Services/Controllers/ProjectController.cs b/Marco.Pms.Services/Controllers/ProjectController.cs index eb3619c..f5aa8f8 100644 --- a/Marco.Pms.Services/Controllers/ProjectController.cs +++ b/Marco.Pms.Services/Controllers/ProjectController.cs @@ -470,6 +470,13 @@ namespace MarcoBMS.Services.Controllers var response = await _projectServices.GetAssignedProjectLevelPermissionAsync(employeeId, projectId, tenantId, loggedInEmployee); return StatusCode(response.StatusCode, response); } + [HttpGet("get/all/project-level-permission/{projectId}")] + public async Task 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")] public async Task AssignProjectLevelModules() { diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 7856993..4b23f5b 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -16,6 +16,7 @@ using Marco.Pms.Model.ViewModels.Master; using Marco.Pms.Model.ViewModels.Projects; using Marco.Pms.Services.Helpers; using Marco.Pms.Services.Service.ServiceInterfaces; +using MarcoBMS.Services.Helpers; using MarcoBMS.Services.Service; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; @@ -28,26 +29,23 @@ namespace Marco.Pms.Services.Service private readonly IDbContextFactory _dbContextFactory; private readonly ApplicationDbContext _context; // Keeping this for direct scoped context use where appropriate private readonly ILoggingService _logger; - private readonly PermissionServices _permission; private readonly CacheUpdateHelper _cache; private readonly IMapper _mapper; - private readonly GeneralHelper _generalHelper; + private readonly IServiceScopeFactory _serviceScopeFactory; public ProjectServices( IDbContextFactory dbContextFactory, ApplicationDbContext context, ILoggingService logger, - PermissionServices permission, + IServiceScopeFactory serviceScopeFactory, CacheUpdateHelper cache, - IMapper mapper, - GeneralHelper generalHelper) + IMapper mapper) { _dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory)); + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _permission = permission ?? throw new ArgumentNullException(nameof(permission)); _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - _generalHelper = generalHelper ?? throw new ArgumentNullException(nameof(generalHelper)); } #region =================================================================== Project Get APIs =================================================================== @@ -87,7 +85,6 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetAllProjectsAsync(Guid tenantId, Employee loggedInEmployee) { try @@ -146,11 +143,12 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetProjectAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent operations in PARALLEL --- // We can check permissions and fetch data at the same time to reduce latency. var permissionTask = _permission.HasProjectPermission(loggedInEmployee, id); @@ -190,13 +188,15 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred.", null, 500); } } - public async Task> GetProjectDetailsAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { try { _logger.LogInfo("Details requested by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, id); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // Step 1: Check global view project permission var hasViewProjectPermission = await _permission.HasPermission(PermissionsMaster.ViewProject, loggedInEmployee.Id, id); if (!hasViewProjectPermission) @@ -252,7 +252,6 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An internal server error occurred. Please try again later.", null, 500); } } - public async Task> GetProjectDetailsOldAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { var project = await _context.Projects @@ -401,6 +400,8 @@ namespace Marco.Pms.Services.Service { try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); // --- 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. var existingProject = await _context.Projects @@ -489,6 +490,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- CRITICAL: Security Check --- // Before fetching data, you MUST verify the user has permission to see it. // This is a placeholder for your actual permission logic. @@ -559,6 +563,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 2: Security and Existence Checks --- // Before fetching data, you MUST verify the user has permission to see it. // 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); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- (Placeholder) Security Check --- // In a real application, you would check if the loggedInEmployee has permission // to manage allocations for ALL projects involved in this batch. @@ -735,6 +745,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 2: Clarified Security Check --- // 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. @@ -808,6 +821,9 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Starting to manage {AllocationCount} project assignments for Employee {EmployeeId}.", allocationsDto.Count, employeeId); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- (Placeholder) Security Check --- // 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); @@ -977,6 +993,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Run independent permission checks in PARALLEL --- var projectPermissionTask = _permission.HasProjectPermission(loggedInEmployee, projectId); var viewInfraPermissionTask = _permission.HasPermission(PermissionsMaster.ViewProjectInfra, loggedInEmployee.Id, projectId); @@ -1033,6 +1052,9 @@ namespace Marco.Pms.Services.Service try { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var _generalHelper = scope.ServiceProvider.GetRequiredService(); // --- Step 1: Cache-First Strategy --- var cachedWorkItems = await _cache.GetWorkItemDetailsByWorkArea(workAreaId); 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); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // --- Step 1: Input Validation --- if (workItemDtos == null || !workItemDtos.Any()) { @@ -1382,7 +1407,6 @@ namespace Marco.Pms.Services.Service return ApiResponse>.SuccessResponse(responseList, message, 200); } - public async Task DeleteProjectTaskAsync(Guid id, Guid tenantId, Employee loggedInEmployee) { // 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}", model.EmployeeId, model.ProjectId, tenantId); + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var hasTeamPermission = await _permission.HasPermission(PermissionsMaster.ManageTeam, loggedInEmployee.Id, model.ProjectId); if (!hasTeamPermission) { @@ -1768,6 +1795,61 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("An error occurred while retrieving employees with project-level permissions.", 500); } } + public async Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId) + { + using var scope = _serviceScopeFactory.CreateScope(); + var _rolesHelper = scope.ServiceProvider.GetRequiredService(); + // 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.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.SuccessResponse(featurePermissionIds, "Successfully featched the permission ids", 200); + } #endregion #region =================================================================== Helper Functions =================================================================== @@ -1803,6 +1885,9 @@ namespace Marco.Pms.Services.Service public async Task> GetMyProjects(Guid tenantId, Employee LoggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + var projectIds = await _cache.GetProjects(LoggedInEmployee.Id); if (projectIds == null) @@ -1829,6 +1914,9 @@ namespace Marco.Pms.Services.Service public async Task> GetMyProjectIdsAsync(Guid tenantId, Employee loggedInEmployee) { + using var scope = _serviceScopeFactory.CreateScope(); + var _permission = scope.ServiceProvider.GetRequiredService(); + // 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. List? projectIds = await _cache.GetProjects(loggedInEmployee.Id); diff --git a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs index c95559a..4f7eb5e 100644 --- a/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs +++ b/Marco.Pms.Services/Service/ServiceInterfaces/IProjectServices.cs @@ -40,6 +40,7 @@ namespace Marco.Pms.Services.Service.ServiceInterfaces Task> GetAssignedProjectLevelPermissionAsync(Guid employeeId, Guid projectId, Guid tenantId, Employee loggedInEmployee); Task> AssignProjectLevelModulesAsync(Guid tenantId, Employee loggedInEmployee); Task> GetEmployeeToWhomProjectLevelAssignedAsync(Guid projectId, Guid tenantId, Employee loggedInEmployee); + Task> GetAllPermissionFroProjectAsync(Guid projectId, Employee loggedInEmployee, Guid tenantId); } }