mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
feat: open the anamnesis record surface to managers
POST /api/allergies and POST /api/family-history have resolved
requireRecordAuth("manage", "profile") since the level shipped, while the only
form in the product that posts to either sat behind a Settings destination
classified unavailable. The write was admitted, audited and owner-scoped, and
nobody could reach it — the one-ended change this repository keeps
rediscovering, with the permission shipped and the surface left as a follow-up.
The destination now carries a classification that means what the routes mean:
reachable by everybody they already admit, which is a guardian inside the
profile they administer and an adult delegate holding manage. The four places
that decided visibility on the guardian-only literal were each updated
deliberately, and the shell that refused every settings path in an adult share
now admits exactly the destinations classified as record content. Record
configuration — modules, thresholds, notification routing — stays with the
owner at every level.
The page inside a record is its own component holding the two managers whose
routes answer for a delegate. Mounting the personal section would have put
three more forms on screen that refuse on submit. The family-history card says
whose statement the row is, which the route comment could not.
Both routes carried a long paragraph arguing the write was deliberately
unreachable. It is gone rather than left to send the next reader back to close
what this opens.
The integration coverage moves from the undeclared-mode refusal to a manage
contract: a manage delegate and a guardian each write and the row lands under
the owner with the delegate named as actor, while read and write delegates get
the insufficient-access refusal. Nothing exercised either route at manage
before.
This commit is contained in:
1 parent
0cdfb97a45
commit
ef242f33bb
17 files changed
+550
-119
No files matched your search
@@ -67,39 +67,23 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
export const POST = apiHandler(withIdempotency<[NextRequest]>(postAllergy));
|
||||
|
||||
async function postAllergy(request: NextRequest): Promise<Response> {
|
||||
// v1.36.x — the GET above is delegable and this is not, which is the
|
||||
// opposite of where the classification landed and worth the paragraph.
|
||||
// v1.37.0 — MANAGE, and reachable.
|
||||
//
|
||||
// Nothing about the row changed: it is still a plain statement about the
|
||||
// record's own body, still the single most useful thing a caregiver could
|
||||
// contribute, and the argument for admitting it still holds. What it does
|
||||
// not have is a caller. The only place in the product that posts here is the
|
||||
// allergy manager in Settings → Anamnese, and `/settings/*` is not a
|
||||
// shared-record destination — the shell shows the "not part of what was
|
||||
// shared" panel there, so no delegate can reach the form at any level.
|
||||
// Recording an allergy for somebody whose record you manage is the case the
|
||||
// level exists for; on a managed profile it is the guardian's ordinary act.
|
||||
// Additive, audited, and the row belongs to the record owner.
|
||||
//
|
||||
// An admitted write with no reachable surface is the one-ended change this
|
||||
// repository keeps rediscovering (CLAUDE.md, "A two-ended change carries
|
||||
// both ends"): the permission ships, the consumer is the follow-up, and
|
||||
// nothing in the gate notices because every other check proves the other
|
||||
// end. The frozen list is built the other way round on purpose — its own
|
||||
// actor-surface note says the rest "arrive as their own diffs; naming them
|
||||
// before they exist would freeze a guess."
|
||||
// The surface is Settings → Anamnese inside the record
|
||||
// (`src/components/settings/record-anamnesis-section.tsx`, reached through
|
||||
// `record-settings-section-gate.tsx` for any destination classified
|
||||
// `manage-writable`). This arm carried a long paragraph through v1.36.x
|
||||
// arguing that the write was deliberately unreachable and waiting for that
|
||||
// surface; the surface exists now, so the paragraph is gone rather than left
|
||||
// to send the next reader back to close what it opens.
|
||||
//
|
||||
// So this arm waits for the surface that would exercise it, and the two
|
||||
// land together. Choosing that surface is design work, not a fix: allergies
|
||||
// and family history have exactly one home today, that home is a personal
|
||||
// account surface a switch rightly closes, and bolting a second copy onto a
|
||||
// shared page would split one concept across two places. Re-admitting is
|
||||
// one line here plus one entry in `sharing-surface-guard.test.ts` plus the
|
||||
// paragraph that argues it — which is exactly the reviewed diff that guard
|
||||
// exists to force.
|
||||
//
|
||||
// The half that was always the point is untouched: a caregiver can still
|
||||
// READ the allergy list inside the record.
|
||||
// v1.37.0 — MANAGE. Recording an allergy for somebody whose record you
|
||||
// manage is the case the level exists for; on a managed profile it is the
|
||||
// guardian's ordinary act. Additive, audited, and the row is the record's.
|
||||
// A READ or WRITE delegate reaches neither: the destination is not on their
|
||||
// Settings list, and this line refuses them with `sharing.access.denied`
|
||||
// regardless.
|
||||
const { user } = await requireRecordAuth("manage", "profile");
|
||||
|
||||
const { data: rawBody, error: jsonError } = await safeJson(request, {
|
||||
|
||||
@@ -62,23 +62,18 @@ export const POST = apiHandler(
|
||||
);
|
||||
|
||||
async function postFamilyHistory(request: NextRequest): Promise<Response> {
|
||||
// v1.36.x — not a delegated write, though the GET above is delegable. The
|
||||
// reasoning is written out at the sibling arm in `api/allergies/route.ts`
|
||||
// and is the same here: the only surface that posts to either route is the
|
||||
// manager in Settings → Anamnese, and `/settings/*` is closed inside a
|
||||
// shared record, so no delegate can reach the form at any level. An
|
||||
// admitted write with no reachable caller is a permission frozen ahead of
|
||||
// the surface that would exercise it.
|
||||
// v1.37.0 — MANAGE, and reachable. Third-party data by design, admitted on
|
||||
// the same reasoning the read arm was: it is the record's own health
|
||||
// background, and the surface is Settings → Anamnese inside the record
|
||||
// (`src/components/settings/record-anamnesis-section.tsx`).
|
||||
//
|
||||
// This arm carried an extra reason to wait. The row describes the record's
|
||||
// RELATIVES, so it is the one admitted write where third-party health
|
||||
// information is present by design — and frequently the delegate is that
|
||||
// relative, asserting a condition about themselves into somebody else's
|
||||
// record. The classification admitted it and named the discomfort. Landing
|
||||
// it together with the surface that offers it means the copy on that surface
|
||||
// can say whose statement the row is, which no route comment can.
|
||||
// v1.37.0 — MANAGE. Third-party data by design, admitted on the same
|
||||
// reasoning the read arm was: it is the record's own health background.
|
||||
// This arm carried an extra reason to wait, and that reason is now answered
|
||||
// where it can be. The row describes the record subject's RELATIVES, so it
|
||||
// is the one admitted write where third-party health information is present
|
||||
// by design — and frequently the delegate IS that relative, asserting a
|
||||
// condition about themselves into somebody else's record. No route comment
|
||||
// can say that to the person filling the form; the card above the form says
|
||||
// it (`recordSharing.anamnesis.relativeNote`).
|
||||
const { user } = await requireRecordAuth("manage", "profile");
|
||||
|
||||
const { data: rawBody, error: jsonError } = await safeJson(request, {
|
||||
|
||||
Reference in new issue
Block a user