From cdc610a635891019dfed2506f85715a6c5b3b969 Mon Sep 17 00:00:00 2001 From: "mustafa.ahmed" Date: Tue, 7 Apr 2026 17:32:51 +0500 Subject: [PATCH] Enforce employee-specific menu authorization via employee_menu_item_tag Add IsMenuItemAuthorizedForRfidAsync to IEmployeeLookupService / EmployeeLookupService to validate menu access by joining employee_rfid_tag and employee_menu_item_tag. Update RfidService.ProcessScanDetailed to filter HRMS menu items against this authorization, block scans when no authorized item remains, and return a clear "No menu item selected." message while logging failures. This ensures only menu items explicitly tagged for a given RFID can be ordered, and prevents unauthorized or unmapped items from creating transactions. --- Services/EmployeeLookupService.cs | 29 +++++++++++++++++++++++++++++ Services/IEmployeeLookupService.cs | 5 +++++ Services/RfidService.cs | 27 ++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Services/EmployeeLookupService.cs b/Services/EmployeeLookupService.cs index 5551a9d..9dc914f 100644 --- a/Services/EmployeeLookupService.cs +++ b/Services/EmployeeLookupService.cs @@ -107,6 +107,35 @@ public class EmployeeLookupService : IEmployeeLookupService return string.IsNullOrEmpty(raw) ? null : raw; } + /// + public async Task IsMenuItemAuthorizedForRfidAsync(string rfid, int menuItemId, CancellationToken cancellationToken = default) + { + var card = rfid?.Trim() ?? string.Empty; + if (string.IsNullOrEmpty(card) || menuItemId <= 0) + return false; + + var connectionString = _configService.GetHrmsLookupConnectionString(); + if (string.IsNullOrWhiteSpace(connectionString)) + return false; + + const string sql = @" + SELECT 1 + FROM employee_rfid_tag r + JOIN employee_menu_item_tag em ON em.employee_rfid_tag_id = r.id + WHERE r.manufacturer_serial = @rfid + AND em.item_id = @itemId + LIMIT 1"; + + await using var conn = new MySqlConnection(connectionString); + await conn.OpenAsync(cancellationToken).ConfigureAwait(false); + await using var cmd = new MySqlCommand(sql, conn); + cmd.Parameters.AddWithValue("@rfid", card); + cmd.Parameters.AddWithValue("@itemId", menuItemId); + + var exists = await cmd.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false); + return exists != null && exists != DBNull.Value; + } + private static string GetString(MySqlDataReader reader, int ordinal) { if (reader.IsDBNull(ordinal)) return string.Empty; diff --git a/Services/IEmployeeLookupService.cs b/Services/IEmployeeLookupService.cs index b7190c7..cd76b61 100644 --- a/Services/IEmployeeLookupService.cs +++ b/Services/IEmployeeLookupService.cs @@ -17,4 +17,9 @@ public interface IEmployeeLookupService /// Reads employee.location_site_id where employee.serial_number matches the login employee id. Null if missing or not configured. /// Task GetLocationSiteIdByEmployeeSerialAsync(string employeeSerial, CancellationToken cancellationToken = default); + + /// + /// Validates that a menu item is authorized for the employee RFID via employee_menu_item_tag. + /// + Task IsMenuItemAuthorizedForRfidAsync(string rfid, int menuItemId, CancellationToken cancellationToken = default); } diff --git a/Services/RfidService.cs b/Services/RfidService.cs index 24aea08..6172563 100644 --- a/Services/RfidService.cs +++ b/Services/RfidService.cs @@ -333,7 +333,32 @@ public class RfidService : IRfidService .GetAwaiter() .GetResult(); - var matching = menuItems.ToList(); + var matching = menuItems + .Where(i => i.MenuItemId > 0) + .Where(i => + { + try + { + return _employeeLookup + .IsMenuItemAuthorizedForRfidAsync(cardId, i.MenuItemId) + .GetAwaiter() + .GetResult(); + } + catch (Exception ex) + { + Logger.Log(ex, $"RfidService.EmployeeMenuValidation card={cardId}, menuItemId={i.MenuItemId}"); + return false; + } + }) + .ToList(); + + if (matching.Count == 0) + { + Logger.Log( + new Exception($"No authorized menu item found for card={cardId}, site={siteNumeric}, date={nowLocal:yyyy-MM-dd}, meal={mealLabel}, grade={employee.GradeType}"), + "RfidService.ProcessScanDetailed authorization"); + return new ScanResult(false, "No menu item selected.", 0, employee, session); + } var names = matching .Select(i => i.ItemName)