Skip to content

fix(session): persist user in reserved _user key and guard reserved keys - #419

Merged
usernane merged 1 commit into
mainfrom
dev
Sep 27, 2026
Merged

usernane merged 1 commit into
mainfrom
dev

Conversation

@usernane

@usernane usernane commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Summary

Persist the session user across resume and protect internal session keys from being modified through the public variable API.

Motivation

The per-key session migration stopped persisting the session user. It was kept only as an in-memory property, so setUser() did not survive a close() + resume round-trip: persistMeta() writes only the _meta key, and start() reset the user to null. This was also untested (the only getUser() assertions were on brand-new sessions, two of them commented out).

Changes

  • Store the SessionUser under a new reserved _user key, serialized and encrypted via encryptValue() so it honors encryption-at-rest when SESSION_KEY is defined.
    • Written on setUser() and on close() (to capture info modified after setUser()); removed when the user is null.
    • Restored in start() via a new restoreUser() helper.
  • Add Session::RESERVED_KEYS = ['_meta', '_user'] and an assertNotReserved() guard.
    • Mutating operations (set(), remove(), pull()) now throw SessionException on a reserved key.
    • Read operations (get(), has()) remain allowed.
  • getVars(), refresh() and start()'s snapshot loop now skip all reserved keys (not just _meta), so _user never leaks into user-visible variables.

UI Changes

Not applicable.

How to Test / Verify

Unit tests in tests/WebFiori/Framework/Tests/Session/SessionTest.php:

  • testUserIsPersistedAcrossResume — sets a user, modifies its email/display name, closes, resumes with the same session ID, and asserts id/username/email/displayName all survive.
  • testReservedKeysRejectMutation — set/remove/pull on _meta/_user throw SessionException.
  • testReservedKeysAllowRead — get/has on reserved keys do not throw.

Session test group: 135 tests / 342 assertions passing (19 pre-existing skips).

Breaking Changes and Migration Steps

None. _meta and _user were never valid application variable names in practice; using them now surfaces an explicit SessionException instead of silently colliding with internal state.

Checklist

  • I reviewed my own diff before requesting review
  • My commits follow Conventional Commits
  • I added/updated tests (or explained why not)
  • I updated docs (if needed) Docs Repo
  • I ran lint/cs-fixer (if applicable) (composer fix-cs)
  • I considered backward compatibility
  • I considered security

Related issues

Closes #418

The per-key session migration stopped persisting the session user: it was
kept only as an in-memory property, so setUser() did not survive a
close()+resume round-trip (persistMeta() writes only _meta, and start()
reset the user to null).

- Store the SessionUser under a new reserved '_user' key, serialized and
  encrypted via encryptValue() (honors encryption-at-rest with SESSION_KEY).
  Written on setUser() and close(); restored in start() via restoreUser().
- Add Session::RESERVED_KEYS = ['_meta','_user'] and assertNotReserved().
  Mutating ops (set/remove/pull) now throw SessionException on a reserved
  key; read ops (get/has) remain allowed.
- getVars(), refresh() and start()'s snapshot loop now skip all reserved
  keys, not just _meta, so _user never leaks into user-visible variables.

Tests: user now survives resume (incl. info modified after setUser); and
reserved keys reject mutation while allowing reads. Session group: 135
tests / 342 assertions passing.
@usernane
usernane merged commit fa9b1e2 into main Sep 27, 2026
4 of 5 checks passed
@codecov

codecov Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.62500% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.73%. Comparing base (d3fd28e) to head (ea4946e).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
WebFiori/Framework/Session/Session.php 90.62% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #419      +/-   ##
============================================
+ Coverage     81.68%   81.73%   +0.05%     
- Complexity     3279     3287       +8     
============================================
  Files           106      106              
  Lines          9685     9714      +29     
============================================
+ Hits           7911     7940      +29     
  Misses         1774     1774              
Flag Coverage Δ
php-8.1 80.57% <89.65%> (+0.06%) ⬆️
php-8.2 80.57% <89.65%> (-0.01%) ⬇️
php-8.3 88.95% <90.62%> (-0.04%) ⬇️
php-8.4 88.95% <90.62%> (-0.04%) ⬇️
php-8.5 ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

bug(session): setUser() not persisted in per-key storage mode (v3.1.0)

1 participant