normogen/web/normogen-web
goose bf5aeedbc2
Some checks failed
Lint and Build / format (pull_request) Successful in 40s
Lint and Build / clippy (pull_request) Successful in 1m56s
Lint and Build / build (pull_request) Successful in 3m47s
Lint and Build / test (pull_request) Failing after 3m51s
feat: hard revoke / re-key (Phase C, #3)
An owner can now rotate a profile's DEK — generating a fresh key,
re-encrypting all the profile's data under it (client-side), and
re-wrapping to the owner share + kept recipients. A revoked recipient's
cached old DEK stops working, closing the soft-revoke window the ADR
requires for graduation / suspected compromise.

The server stays a blind store: it sees opaque old→new ciphertext blobs
flow through, never the DEK or plaintext. No backend crypto.

Backend:
- ProfileRepository::update_wrapped_dek — rotate the owner share
  (account-wrapped profile DEK), owner-scoped.
- ProfileShareRepository::find_active_for_profile +
  delete_for_profile_excluding — list current recipients and hard-delete
  omitted ones.
- POST /api/profiles/:id/rekey: validates each submitted envelope
  against existing active shares (no smuggling new recipients in via
  rekey), rotates the owner share, upserts recipient envelopes with
  fresh ephemeral ECDH keys, hard-deletes omitted recipients.
- rekey_tests.rs: rotation, hard-revoke-from-recipient-view, no-share
  rejection, non-owner rejection, revoke-all.

Frontend:
- useProfileStore.rekeyProfile: fetches all profile data, re-encrypts
  each row under a new DEK (resume-safe — rows already on the new DEK
  are skipped), builds fresh ECDH envelopes per kept recipient, commits
  via POST /rekey, swaps the in-memory DEK.
- ProfileSharing: 'Rotate encryption key (hard revoke)' action with a
  strong confirmation dialog explaining the cost and the resume-safe
  retry.

Best-effort + resumable retry (per design decision); no server-side
write lock.

Verification: backend cargo build/clippy (-D warnings)/fmt clean, tests
compile (integration tests run in CI — Mongo is fixed there). Frontend
tsc clean, 31/31 tests pass.

⚠️ Backend integration tests not run locally (no Mongo in this sandbox);
CI will run them — I'll fix any failures.

Admin-initiated rekey (ADR §5c reserves the permission) is out of scope
— handler is owner-only until admin shares are creatable in Phase D.
Refs #3.
2026-07-19 15:15:39 -03:00
..
public feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
src feat: hard revoke / re-key (Phase C, #3) 2026-07-19 15:15:39 -03:00
.env.example feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
.gitignore feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
Dockerfile fix: rust:slim (not rust:latest-slim which doesn't exist) 2026-07-14 12:43:05 -03:00
index.html feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
package-lock.json feat: Phase 3c — dose logging + adherence, profile management, tests 2026-06-28 10:32:28 -03:00
package.json feat: Phase 3c — dose logging + adherence, profile management, tests 2026-06-28 10:32:28 -03:00
tsconfig.json feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
tsconfig.node.json feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00
vite.config.ts feat(web): Phase 3a — migrate CRA to Vite + make the app runnable 2026-06-28 00:47:19 -03:00