Fix: resolve tenants by identifier, not only internal tenant_id
Found via a real cross-service check while implementing key-cape's KEY-WP-0005-T02: key-cape's Go adapter called GET /tenants/tenant:coulomb/roles and got a genuine 404 for a tenant that existed. External callers (key-cape, flex-auth) only ever have a tenant's profile identifier, never tenant-engine's internal tenant_id (caller-chosen at creation, otherwise opaque). Every existing test happened to use identical strings for both fields, so this was invisible until a real, independent second caller exercised the documented contract. InMemoryTenantStore gained a _by_identifier index and a _resolve() helper every method calls first; create_tenant now also rejects a duplicate identifier under a different internal id (an oversight the same fix surfaced). 5 new tests, including the exact HTTP-level scenario with colon characters in the URL path. 65 total, all 60 pre-existing tests unaffected. Re-verified end-to-end for real: fresh flex-auth + tenant-engine + key-cape's actual adapter code, over real HTTP -- roles=[IAM] ok=true resolving by identifier. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
18f510070f
commit
31e237cfa6
4 changed files with 193 additions and 17 deletions
71
workplans/ADHOC-2026-07-24.md
Normal file
71
workplans/ADHOC-2026-07-24.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
id: ADHOC-2026-07-24
|
||||
type: workplan
|
||||
title: "Resolve tenants by identifier, not only internal tenant_id"
|
||||
domain: infotech
|
||||
repo: tenant-engine
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-24"
|
||||
updated: "2026-07-24"
|
||||
---
|
||||
|
||||
# Resolve tenants by identifier, not only internal tenant_id
|
||||
|
||||
Discovered via a real cross-service check while implementing `key-cape`'s
|
||||
`KEY-WP-0005-T02` (`tenant_roles` cache-read integration): `key-cape`'s Go
|
||||
adapter called `GET /tenants/tenant:coulomb/roles` and got a genuine `404
|
||||
tenant_not_found` for a tenant that actually existed. External callers
|
||||
(`key-cape`, `flex-auth`) only ever have a tenant's **profile identifier**
|
||||
(the IAM Profile `tenant` claim value, e.g. `tenant:coulomb`) — never
|
||||
tenant-engine's internal `tenant_id`, which is caller-chosen at creation
|
||||
(`POST /tenants`) and otherwise opaque. Every existing test happened to
|
||||
pass an identical string for both fields, so this gap was invisible to
|
||||
unit/API tests until a genuine second, independent caller (`key-cape`,
|
||||
written against the documented contract, not against tenant-engine's
|
||||
internal test conventions) exercised it for real.
|
||||
|
||||
## Task: Fix
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-07-24-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
`InMemoryTenantStore` gained a `_by_identifier` index (identifier →
|
||||
internal `tenant_id`) and a private `_resolve()` helper every method now
|
||||
calls first, so `get_tenant`, `grant_role`, `revoke_role`, `active_roles`,
|
||||
and `assign_plan` all transparently accept either the internal id or the
|
||||
identifier. `create_tenant` also now rejects a duplicate identifier under a
|
||||
different internal id (previously unenforced — an oversight the same fix
|
||||
surfaced).
|
||||
|
||||
Done when: a real cross-process check (real `flex-auth`, real
|
||||
`tenant-engine`, real `key-cape` adapter code, over actual HTTP) resolves a
|
||||
tenant by identifier correctly, and all existing tests still pass
|
||||
unmodified in intent.
|
||||
|
||||
**Done 2026-07-24:** Fixed in `store.py`. Added
|
||||
`test_get_tenant_resolves_by_identifier_not_only_internal_id`,
|
||||
`test_active_roles_resolves_by_identifier`,
|
||||
`test_revoke_role_resolves_by_identifier`, and
|
||||
`test_create_tenant_rejects_duplicate_identifier_with_different_internal_id`
|
||||
in `tests/test_store.py`; added
|
||||
`test_cache_read_roles_resolves_by_identifier_not_only_internal_id` in
|
||||
`tests/test_api_reads.py` (the exact HTTP-level scenario, with the colon
|
||||
characters a real `tenant:friendly:binky`-shaped identifier puts in the URL
|
||||
path). `pytest` → `65 passed` (all 60 pre-existing tests unaffected).
|
||||
|
||||
Re-ran the exact real end-to-end check that found the bug, on fresh ports
|
||||
to avoid colliding with another session's own `flex-auth serve` process on
|
||||
this shared workstation (a collision that led to accidentally killing that
|
||||
other process — a low-harm, trivially-restartable local dev server, but
|
||||
noted here as a mistake to avoid repeating: check `pgrep` output carefully
|
||||
and prefer distinctive ports before killing anything matching a broad
|
||||
pattern): created a tenant with a genuinely different internal id and
|
||||
identifier, granted it a role through the real `flex-auth`-gated write
|
||||
path, then called `GET /tenants/tenant:coulomb/roles` from `key-cape`'s
|
||||
actual `internal/adapters/tenantengine.Client` (not a stand-in) — correctly
|
||||
returned `roles=[IAM] ok=true`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue