Skip to content

session: add Principal, readable authorization state outside a dispatch - #34

Merged
Yaraslaut merged 2 commits into
masterfrom
feature/24-readable-principal
Aug 5, 2026
Merged

session: add Principal, readable authorization state outside a dispatch#34
Yaraslaut merged 2 commits into
masterfrom
feature/24-readable-principal

Conversation

@Yaraslaut

Copy link
Copy Markdown
Member

Summary

  • session::current() (Context::principal) only exists during a dispatch, so UI code had no way to ask "who is signed in and what may they do?" without attempting the action and catching the refusal — issue No readable authorization state outside a dispatch (UI cannot gate on roles) #24's exact complaint.
  • The issue itself flagged the scoping decision (process-wide global vs. per-session object) as needing agreement first. Went with per-Bridge instance, not a global (confirmed with the repo owner before implementing) — the same object that already holds the default session::Context via setDefaultSession/defaultSession.
  • Adds morph::session::Principal (id, roles, claims, hasRole()) and Bridge::setPrincipal(principal) / Bridge::currentPrincipal(), guarded by their own mutex (separate from the session mutex, since UI code is expected to read it far more often).
  • Purely a client-side convenience: no wire representation, doesn't touch Context or dispatch. Every dispatch is still authorized server-side via IAuthorizer regardless of what currentPrincipal() says — this only shapes what the UI offers.
  • Design docs updated: docs/spec/session/session.md (new Principal section, API reference, design-decision row, and a note on the current() limitation this closes) and docs/spec/core/bridge.md (new setPrincipal/currentPrincipal API rows).

Test plan

  • New tests/test_principal.cpp: Principal/hasRole unit tests; Bridge::setPrincipal/currentPrincipal round-trip (id/roles/claims); readable with no dispatch in flight at all (the exact gap reported); clearing via a default-constructed Principal{} (sign-out); independence across two separate Bridge instances.
  • Full suite: ./build/tests/morph_tests — all 818 test cases / 8304 assertions pass.

Closes #24

🤖 Generated with Claude Code

@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

session::current() (Context::principal) only exists during a dispatch, so
UI code -- a button's enabled state, a menu item's visibility -- had no way
to ask "who is signed in and what may they do?" without attempting the
action and catching the refusal. Applications ended up maintaining a second,
parallel notion of the signed-in user's permissions for the UI, with no
guarantee it agreed with the one morph verifies server-side.

Add morph::session::Principal (id, roles, claims, hasRole()) and
Bridge::setPrincipal/currentPrincipal: an application installs it once,
typically right after a successful login dispatch from data the server
actually returned, and UI code reads it back outside any dispatch. Scoped to
the specific Bridge instance -- not a process-wide global -- alongside the
existing setDefaultSession/defaultSession, each guarded by its own mutex.
Purely a client-side convenience: no wire representation, and it never
substitutes for server-side authorization, which still runs on every
dispatch via IAuthorizer regardless of what currentPrincipal() says.

Closes #24

Signed-off-by: Yaraslau Tamashevich <yaraslau.tamashevich@gmail.com>
Principal{.id = ..., .roles = ...} (and similar partial forms) tripped
-Wmissing-designated-field-initializers (clang) / -Wmissing-field-initializers
(gcc) under this project's -Werror build, since neither warning is in the
opt-out list in apply_warnings(). This broke every real build/test CI job
(15 of them) plus clang-tidy-diff, which all share the same compile step.

Name every field (id/roles/claims) in each partial designated
initializer; Principal{} (all-default) is unaffected since it names
zero fields, which both compilers accept.

Signed-off-by: Yaraslau Tamashevich <yaraslau.tamashevich@gmail.com>
@Yaraslaut
Yaraslaut force-pushed the feature/24-readable-principal branch from f53e9c6 to 899c275 Compare August 4, 2026 16:35
@Yaraslaut
Yaraslaut merged commit ad4f7e1 into master Aug 5, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No readable authorization state outside a dispatch (UI cannot gate on roles)

1 participant