A receipt lookup keyed only by receipt ID can select another tenant's row when IDs are reused across tenants.
Spring data exercise: repair a receipt query missing its tenant predicate
Repair the query and prove it
Start with a conceptual lookup that selects by receipt_id alone. The fixture stores R-41 twice: tenant-east has accepted and tenant-west has held. Add tenant_id to the SQL predicate and bind the trusted tenant from authentication. Then assert that east returns accepted, west returns held only for an authorized west identity, and a third tenant returns no row. Do not rely on an ORDER BY to hide the duplicate ID.
The source-kit tenant predicate lesson contains the repaired lookup. Its web test also denies an east token requesting the west route. Those are separate checks: a correct query does not make an untrusted tenant parameter trustworthy, and a correct service rule does not excuse an unscoped repository method.
Add a write trace
Change the read to update state. Include both receipt_id and tenant_id in the WHERE clause, and check that the affected row count is exactly one. If zero, distinguish not-found, stale version and unauthorized access according to the API's disclosure policy. The current fixture checks a read predicate, not a database write or row-level security. Run the named JUnit test locally; this exercise has no hosted judge.
Checked source
String sql = "select state from tenant_receipt "
+ "where receipt_id = ? and tenant_id = ?";
List<String> states = jdbc.queryForList(sql, String.class, "R-41", trustedTenant);Verification boundary
JwtTenantBoundaryTest.scopedQueryExcludesRowsForOtherTenantValues runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.
Costs and limits
The test queries two H2 rows and an absent third tenant value. It does not exercise a production data set, index plan, write transaction or a user-to-tenant membership service. For a large table, inspect a composite index plan on the target engine.
Common Mistakes
- Do not fetch by receipt ID alone and filter after the query.
- Do not bind an unverified URL tenant into the repaired SQL.
- Do not call a zero-row update success.
Read next
Spring JdbcTemplate tenant predicates: put ownership in the SQL query, Spring Security JWT tenant principal: map only a validated claim, Spring security exercise: trace four JWT request outcomes.
