diff --git a/Marco.Pms.Model/Dtos/Projects/CreateProjectDto.cs b/Marco.Pms.Model/Dtos/Projects/CreateProjectDto.cs index 32846dc..44f6a90 100644 --- a/Marco.Pms.Model/Dtos/Projects/CreateProjectDto.cs +++ b/Marco.Pms.Model/Dtos/Projects/CreateProjectDto.cs @@ -27,7 +27,7 @@ namespace Marco.Pms.Model.Dtos.Project [DisplayName("Project Status")] [Required(ErrorMessage = "Project Status is required!")] public required Guid ProjectStatusId { get; set; } - public required Guid PromoterId { get; set; } - public required Guid PMCId { get; set; } + public Guid? PromoterId { get; set; } + public Guid? PMCId { get; set; } } } diff --git a/Marco.Pms.Services/Controllers/AttendanceController.cs b/Marco.Pms.Services/Controllers/AttendanceController.cs index 7c1b31f..963b8c4 100644 --- a/Marco.Pms.Services/Controllers/AttendanceController.cs +++ b/Marco.Pms.Services/Controllers/AttendanceController.cs @@ -160,13 +160,6 @@ namespace MarcoBMS.Services.Controllers var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id); var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id); - var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId); - - if (!hasProjectPermission) - { - _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId); - return Unauthorized(ApiResponse.ErrorResponse("Unauthorized access", "Unauthorized access", 404)); - } DateTime fromDate = new DateTime(); DateTime toDate = new DateTime(); @@ -307,12 +300,6 @@ namespace MarcoBMS.Services.Controllers return NotFound(ApiResponse.ErrorResponse("Project not found.")); } - if (!await _permission.HasProjectPermission(loggedInEmployee, projectId)) - { - _logger.LogWarning("Unauthorized access attempt by EmployeeId: {EmployeeId} for ProjectId: {ProjectId}", loggedInEmployee.Id, projectId); - return Unauthorized(ApiResponse.ErrorResponse("You do not have permission to access this project.")); - } - // --- 2. Delegate to Specific Logic Based on Permissions --- try { @@ -356,13 +343,6 @@ namespace MarcoBMS.Services.Controllers Guid TenantId = GetTenantId(); Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync(); var result = new List(); - var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId); - - if (!hasProjectPermission) - { - _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId); - return Unauthorized(ApiResponse.ErrorResponse("Unauthorized access", "Unauthorized access", 404)); - } List lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync(); diff --git a/Marco.Pms.Services/Controllers/AuthController.cs b/Marco.Pms.Services/Controllers/AuthController.cs index d65bd35..caa92d7 100644 --- a/Marco.Pms.Services/Controllers/AuthController.cs +++ b/Marco.Pms.Services/Controllers/AuthController.cs @@ -59,7 +59,7 @@ namespace MarcoBMS.Services.Controllers { var user = await _context.ApplicationUsers - .FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.PhoneNumber == loginDto.Username); + .FirstOrDefaultAsync(u => u.Email == loginDto.Username || u.UserName == loginDto.Username); if (user == null) { @@ -103,9 +103,11 @@ namespace MarcoBMS.Services.Controllers return NotFound(ApiResponse.ErrorResponse("Username not found", "Username not found", 404)); } + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.OrganizationId == emp.OrganizationId); + // Generate tokens - var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, emp.OrganizationId, _jwtSettings); - var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), emp.OrganizationId, _jwtSettings); + var token = _refreshTokenService.GenerateJwtToken(user.UserName, tenant?.Id ?? Guid.Empty, emp.OrganizationId, _jwtSettings); + var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, tenant?.Id.ToString(), emp.OrganizationId, _jwtSettings); _logger.LogInfo("User login successful - UserId: {UserId}", user.Id); return Ok(ApiResponse.SuccessResponse(new @@ -201,12 +203,15 @@ namespace MarcoBMS.Services.Controllers } _logger.LogInfo("Successfully found employee details for tenant ID: {TenantId}", emp.TenantId ?? Guid.Empty); + + var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.OrganizationId == emp.OrganizationId); + // Generate JWT token - var token = _refreshTokenService.GenerateJwtToken(user.UserName, emp.TenantId ?? Guid.Empty, emp.OrganizationId, _jwtSettings); + var token = _refreshTokenService.GenerateJwtToken(user.UserName, tenant?.Id ?? Guid.Empty, emp.OrganizationId, _jwtSettings); // Generate a new refresh token and store it in the database. _logger.LogInfo("Generating and storing Refresh Token for user: {Username}", user.UserName); - var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, emp.TenantId.ToString(), emp.OrganizationId, _jwtSettings); + var refreshToken = await _refreshTokenService.CreateRefreshToken(user.Id, tenant?.Id.ToString(), emp.OrganizationId, _jwtSettings); // Fetch MPIN Token var mpinToken = await _context.MPINDetails.FirstOrDefaultAsync(p => p.UserId == Guid.Parse(user.Id)); @@ -264,22 +269,19 @@ namespace MarcoBMS.Services.Controllers } string? tokenType = claimsPrincipal.FindFirst("token_type")?.Value; - string? tokenTenantId = claimsPrincipal.FindFirst("TenantId")?.Value; string? tokenUserId = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier)?.Value; // Validate essential claims - if (string.IsNullOrWhiteSpace(tokenType) || string.IsNullOrWhiteSpace(tokenTenantId) || string.IsNullOrWhiteSpace(tokenUserId)) + if (string.IsNullOrWhiteSpace(tokenType) || string.IsNullOrWhiteSpace(tokenUserId)) { _logger.LogWarning("MPIN token claims are incomplete"); return Unauthorized(ApiResponse.ErrorResponse("Invalid token claims", "MPIN token does not match your identity", 401)); } - Guid tenantId = Guid.Parse(tokenTenantId); - // Fetch employee by ID and tenant var requestEmployee = await _context.Employees .Include(e => e.ApplicationUser) - .FirstOrDefaultAsync(e => e.Id == verifyMPIN.EmployeeId && e.TenantId == tenantId && e.ApplicationUserId == tokenUserId && e.IsActive); + .FirstOrDefaultAsync(e => e.Id == verifyMPIN.EmployeeId && e.HasApplicationAccess && e.ApplicationUserId == tokenUserId && e.IsActive); if (requestEmployee == null || string.IsNullOrWhiteSpace(requestEmployee.ApplicationUserId)) { @@ -287,8 +289,10 @@ namespace MarcoBMS.Services.Controllers return BadRequest(ApiResponse.ErrorResponse("Invalid request", "Provided invalid employee information", 400)); } + Guid tenantId = await _context.Tenants.Where(t => t.OrganizationId == requestEmployee.OrganizationId).Select(t => t.Id).FirstOrDefaultAsync(); + // Validate that the token belongs to the same employee making the request - if (requestEmployee.ApplicationUserId != tokenUserId || tokenType != "mpin") + if (requestEmployee.ApplicationUserId != tokenUserId || tokenType != "mpin" || tenantId == Guid.Empty) { _logger.LogWarning("Token identity does not match employee info - EmployeeId: {EmployeeId}", requestEmployee.Id); return Unauthorized(ApiResponse.ErrorResponse("Unauthorized", "MPIN token does not match your identity", 401)); diff --git a/Marco.Pms.Services/Service/ProjectServices.cs b/Marco.Pms.Services/Service/ProjectServices.cs index 3cbd794..8e75670 100644 --- a/Marco.Pms.Services/Service/ProjectServices.cs +++ b/Marco.Pms.Services/Service/ProjectServices.cs @@ -67,7 +67,7 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Basic project list requested by EmployeeId {EmployeeId}", loggedInEmployee.Id); // Step 2: Get the list of project IDs the user has access to - List accessibleProjectIds = await GetMyProjects(tenantId, loggedInEmployee); + List accessibleProjectIds = await _context.Projects.Where(p => p.TenantId == tenantId).Select(p => p.Id).ToListAsync(); if (accessibleProjectIds == null || !accessibleProjectIds.Any()) { @@ -96,7 +96,7 @@ namespace Marco.Pms.Services.Service _logger.LogInfo("Starting GetAllProjects for TenantId: {TenantId}, User: {UserId}", tenantId, loggedInEmployee.Id); // --- Step 1: Get a list of project IDs the user can access --- - List projectIds = await GetMyProjects(tenantId, loggedInEmployee); + List projectIds = await _context.Projects.Where(p => p.TenantId == tenantId).Select(p => p.Id).ToListAsync(); if (!projectIds.Any()) { _logger.LogInfo("User has no assigned projects. Returning empty list."); @@ -368,17 +368,20 @@ namespace Marco.Pms.Services.Service return ApiResponse.ErrorResponse("Access Denied", "You do not have permission to create a project for this tenant.", 403); } + var promoterId = model.PromoterId ?? loggedInEmployee.OrganizationId; + var pmcId = model.PMCId ?? loggedInEmployee.OrganizationId; + // Step 2: Concurrent validation for Promoter and PMC organization existence. // Run database queries in parallel for better performance. var promoterTask = Task.Run(async () => { await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.Organizations.FirstOrDefaultAsync(o => o.Id == model.PromoterId); + return await context.Organizations.FirstOrDefaultAsync(o => o.Id == promoterId); }); var pmcTask = Task.Run(async () => { await using var context = await _dbContextFactory.CreateDbContextAsync(); - return await context.Organizations.FirstOrDefaultAsync(o => o.Id == model.PMCId); + return await context.Organizations.FirstOrDefaultAsync(o => o.Id == pmcId); }); await Task.WhenAll(promoterTask, pmcTask); @@ -388,18 +391,20 @@ namespace Marco.Pms.Services.Service if (promoter == null) { - _logger.LogWarning("Promoter check failed. PromoterId={PromoterId} not found.", model.PromoterId); + _logger.LogWarning("Promoter check failed. PromoterId={PromoterId} not found.", promoterId); return ApiResponse.ErrorResponse("Promoter not found", "Promoter not found in database.", 404); } if (pmc == null) { - _logger.LogWarning("PMC check failed. PMCId={PMCId} not found.", model.PMCId); + _logger.LogWarning("PMC check failed. PMCId={PMCId} not found.", pmcId); return ApiResponse.ErrorResponse("PMC not found", "PMC not found in database.", 404); } // Step 3: Prepare the project entity. var loggedInUserId = loggedInEmployee.Id; var project = _mapper.Map(model); + project.PromoterId = promoterId; + project.PMCId = pmcId; project.TenantId = tenantId; // Step 4: Save the new project to the database.