using System; using System.Threading.Tasks; using AccountsDAL.CustomCode.AccountReports; using AccountsDAL.DTO.AccountsReports; using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; namespace AccountsBLL.AccountReports { // New backend enforcement for the "avoid a mistaken open-filter report run" gap identified during // migration planning: today (both in legacy and in GB5) MROLEVSMENU's day/record limits and // MUSERACCESSRIGHTS.NOOFREADPERIOD are only enforced by the frontend — a REST caller hitting these // endpoints directly bypasses them entirely. This closes that gap for the AccountReports family. // // Menu IDs for these reports are not yet minted in MMENU (no gb5-meta/uscdevsys access this // session) — callers pass in a menuId; if no MROLEVSMENU row exists for (RoleId, menuId) this // enforces nothing for the day/record-limit check (fail-open) rather than blocking the report // outright on an unresolved placeholder id. Replace the placeholder menuId constants in // AccountRegisterReportBLL/AccountRegisterSummaryBLL once the real menu entries exist. // // NOOFREADPERIOD's exact semantics are inferred, not independently verified: the only concrete // precedent found (UserAccessRightsDAL.GetUserAccessRightsOnUser, for the sibling NOOFPERIOD // column) treats the value 1 as "restrict to the current/latest period only" rather than a literal // "N periods back" count. This enforces the same interpretation for report reads. If this ever // needs to be a genuine multi-period lookback count, revisit here. public class AccountReportAccessControlBLL : IAccountReportAccessControlBLL { private readonly IAccountReportAccessControlDAL _dal; public AccountReportAccessControlBLL(IAccountReportAccessControlDAL dal) { _dal = dal; } public async Task EnforceAsync(AccountReportCriteria criteria, int maxResult, int menuId, LoginDTO loginDTO) { if (criteria.PeriodFromDate == default || criteria.PeriodToDate == default) throw new InvalidOperationException(ErrorResponse.PeriodRangeRequiredMessage); if (criteria.PeriodFromDate > criteria.PeriodToDate) throw new InvalidOperationException(ErrorResponse.PeriodRangeInvertedMessage); var limits = await _dal.GetReportAccessLimits(loginDTO.RoleId, menuId, loginDTO).ConfigureAwait(false); if (limits != null) { var requestedDays = (criteria.PeriodToDate.Date - criteria.PeriodFromDate.Date).TotalDays + 1; if (limits.DaysLimit > 0 && requestedDays > limits.DaysLimit) throw new InvalidOperationException(ErrorResponse.DaysLimitExceededMessage); if (limits.RecordsLimit > 0 && maxResult > limits.RecordsLimit) throw new InvalidOperationException(ErrorResponse.RecordsLimitExceededMessage); } var noOfReadPeriod = await _dal.GetNoOfReadPeriodForUser(loginDTO.UserId, loginDTO).ConfigureAwait(false); if (noOfReadPeriod == 1) { var workPeriod = await _dal.GetPeriodDateRange(loginDTO.WorkPeriodId, loginDTO).ConfigureAwait(false); if (workPeriod != null && criteria.PeriodFromDate.Date < workPeriod.FromDate.Date) throw new InvalidOperationException(ErrorResponse.PeriodTooEarlyMessage); } } public async Task EnforceForAsOnDateAsync(AccountReportCriteria criteria, int maxResult, int menuId, LoginDTO loginDTO) { var limits = await _dal.GetReportAccessLimits(loginDTO.RoleId, menuId, loginDTO).ConfigureAwait(false); if (limits != null && limits.RecordsLimit > 0 && maxResult > limits.RecordsLimit) throw new InvalidOperationException(ErrorResponse.RecordsLimitExceededMessage); var noOfReadPeriod = await _dal.GetNoOfReadPeriodForUser(loginDTO.UserId, loginDTO).ConfigureAwait(false); if (noOfReadPeriod == 1) { var workPeriod = await _dal.GetPeriodDateRange(loginDTO.WorkPeriodId, loginDTO).ConfigureAwait(false); if (workPeriod != null && criteria.AsOnDate.Date < workPeriod.FromDate.Date) throw new InvalidOperationException(ErrorResponse.PeriodTooEarlyMessage); } } } }