Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Project: tenant-safe case export

Last updated: 4 Oct 20268 min read
project
IntermediateBy AITrove Editorial

Build a case-export workflow for organizations 6 and 9, both of which contain a case numbered 47. Reviewer 29 belongs to organization 6 and can access only assigned cases; reviewer 62 belongs to organization 9. A list endpoint constrains authorized rows before counting, sorting, and pagination. A case-detail endpoint checks the current actor-record relationship for every direct ID request. Cache entries include tenant and permission scope, but cache keys never replace server authorization. An export request carries a server-authored job envelope with requester, tenant, normalized filter, and a short-lived authorization decision. The worker restricts its query, and the status and artifact download each enforce their own check. Revoke reviewer 29 before a pending job runs and follow the documented cancellation policy. A private response must not enter a shared cache.

Build contract

  • Test direct detail, mutation, count, cursor, and mixed-ID bulk requests under both tenants and after a reassignment.
  • Run alternating cache requests for the same numeric ID in two tenants; verify protected values never cross and measure query cost.
  • Forge a tenant identifier in the export payload and verify the worker uses only server-approved scope.
  • Test job status, download, range resume, expiry, and revocation from a second account and from a suspended old tab.

Implementation checkpoint

javascript
function exportScope(actor, request) {
  if (actor.tenantId !== request.tenantId) return null;
  return { requesterId: actor.userId, tenantId: actor.tenantId, filter: request.filter };
}
console.log(exportScope({ userId: 29, tenantId: 6 }, { tenantId: 9, filter: "open" }));
// Output: null

Cost and boundaries

A scoped lookup can be O(1) with a suitable tenant and record index, while a careless per-item authorization query makes a fifty-row list generate fifty extra reads. Exporting n permitted rows costs O(n) serialization and storage or transfer; a broad worker query may be faster to write but breaks the tenant boundary. More specific cache keys reduce hit rate and create extra variants, yet prevent cross-tenant disclosure. Rechecking permission at the worker and download boundaries adds small work relative to a full export. Measure request latency, unauthorized denials, cache collisions, job size, artifact cleanup lag, and maximum revocation delay.

Failure drill

Change case 47 to case 62 in a direct request and verify that the server denies both read and write without returning the other case title. Mix authorized and unauthorized IDs in a bulk export. Make the API correctly constrain data but leave the cache keyed only by case number; the second tenant must not see the first response. Forge tenantId 9 in the job payload and inspect the exact worker query. Revoke access after queueing but before generation. Request status and bytes from reviewer 62. Send the artifact through a shared cache and inspect whether one account can receive another account’s response.

Acceptance checks

  • Each detail, list, mutation, bulk, and search count applies current server-side object scope.
  • Tenant and permission scope survive cache, queue, worker, and artifact boundaries.
  • Revocation and expiry block status and download under the chosen policy.
  • No private body or metadata is served from a public cache.

Common Mistakes

  • Checking only that a caller has a reviewer role.
  • Putting tenant ID in a cache key while trusting it from client input.
  • Assuming an opaque job ID grants download permission.
  • Rechecking permission only at the first API request.

Related lessons

Object-Level Authorization for Reads and Writes; Tenant Scope in Cache and Background Work; Permission Revocation and Active Sessions; Private Export Authorization and Artifact Scope; Search Permissions and Private Results.

web-tech
web-development
Storage details