fix(security,pos): close payment/push/PII gaps from app review
CI/CD / CI · API (dotnet build + test) (push) Successful in 59s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Successful in 2m13s
CI/CD / CI · API (dotnet build + test) (push) Successful in 59s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Successful in 2m13s
- Payments: reject RecordPaymentsAsync when the order is already Delivered/
Cancelled (ORDER_ALREADY_CLOSED) — prevents duplicate payments, double loyalty
earn, and overstated cash drawer from a double-tap or paying a reopened order.
- Push broadcast: POST /api/push/broadcast was [Authorize]-only (any user → any
topic, platform-wide). Now requires SendSms + café context and is forced to the
caller's own topic (cafe-{slug}); arbitrary/cross-café topics rejected.
- HR reads: GetEmployees/GetAttendance/GetShifts now require ViewStaff/
ViewAttendance/ViewSchedules (were café-access-only, leaking roster PII the UI
already hid). Expenses list now requires ViewExpenses.
- Receipt: removed the auto-print on full payment so the POS success sheet is the
single print path (no more double receipt).
Local build blocked by NU1301 (NuGet network unreachable); CI builds via mirror.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,7 @@ public class HrController : CafeApiControllerBase
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||
if (EnsurePermission(tenant, Permission.ViewStaff) is { } forbidden) return forbidden;
|
||||
var data = await _hr.GetEmployeesAsync(cafeId, branchId, ct);
|
||||
return Ok(new ApiResponse<IReadOnlyList<EmployeeSummaryDto>>(true, data));
|
||||
}
|
||||
@@ -184,6 +185,7 @@ public class HrController : CafeApiControllerBase
|
||||
CancellationToken ct)
|
||||
{
|
||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||
if (EnsurePermission(tenant, Permission.ViewAttendance) is { } forbidden) return forbidden;
|
||||
var data = await _hr.GetAttendanceAsync(cafeId, employeeId, from, to, ct);
|
||||
return Ok(new ApiResponse<IReadOnlyList<AttendanceDto>>(true, data));
|
||||
}
|
||||
@@ -192,6 +194,7 @@ public class HrController : CafeApiControllerBase
|
||||
public async Task<IActionResult> GetShifts(string cafeId, string employeeId, ITenantContext tenant, CancellationToken ct)
|
||||
{
|
||||
if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
|
||||
if (EnsurePermission(tenant, Permission.ViewSchedules) is { } forbidden) return forbidden;
|
||||
var data = await _hr.GetShiftsAsync(cafeId, employeeId, ct);
|
||||
return Ok(new ApiResponse<IReadOnlyList<ShiftDto>>(true, data));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user