Overview
CachingService derives its counter namespace from the cache:{type}:... key convention via deriveCacheType, but nothing in the service or in CacheKeyBuilder incorporates a tenant identifier into the cache key itself. Any cached value keyed only on an entity id — a course, a search result, a dashboard aggregate — is shared across every tenant in the deployment. A tenant that requests a resource first populates the entry, and every other tenant subsequently reads it, which turns the cache into a cross-tenant disclosure channel that bypasses whatever tenant scoping the underlying query applied.
Specifications
Features:
- Every cache key is namespaced by tenant.
- A missing tenant context is a hard failure rather than a shared key.
Tasks:
- Extend
CacheKeyBuilder to require a tenant identifier and emit keys of the form cache:{tenant}:{type}:{id}.
- Resolve the tenant from the request-scoped tenancy context and thread it through the caching call sites.
- Throw rather than falling back to a global namespace when no tenant context is available in a tenant-scoped path.
- Update
deriveCacheType so counter namespacing still works with the extra segment.
- Add an integration test that populates the cache as tenant A and asserts tenant B receives a miss.
Impacted Files:
src/caching/cache-key.builder.ts
src/caching/caching.service.ts
src/tenancy/
Acceptance Criteria
- No cache key omits the tenant segment on a tenant-scoped path.
- Tenant B never reads a value populated by tenant A.
- Cache hit/miss counters remain correctly bucketed by type.
- An integration test covers the cross-tenant isolation.
Overview
CachingServicederives its counter namespace from thecache:{type}:...key convention viaderiveCacheType, but nothing in the service or inCacheKeyBuilderincorporates a tenant identifier into the cache key itself. Any cached value keyed only on an entity id — a course, a search result, a dashboard aggregate — is shared across every tenant in the deployment. A tenant that requests a resource first populates the entry, and every other tenant subsequently reads it, which turns the cache into a cross-tenant disclosure channel that bypasses whatever tenant scoping the underlying query applied.Specifications
Features:
Tasks:
CacheKeyBuilderto require a tenant identifier and emit keys of the formcache:{tenant}:{type}:{id}.deriveCacheTypeso counter namespacing still works with the extra segment.Impacted Files:
src/caching/cache-key.builder.tssrc/caching/caching.service.tssrc/tenancy/Acceptance Criteria