Skip to content

refactor(session): reshape Horde_Session as shim over modern PSR-4 stack#135

Merged
ralflang merged 1 commit into
FRAMEWORK_6_0from
feat/session-shim-cutover
Jun 9, 2026
Merged

refactor(session): reshape Horde_Session as shim over modern PSR-4 stack#135
ralflang merged 1 commit into
FRAMEWORK_6_0from
feat/session-shim-cutover

Conversation

@ralflang

@ralflang ralflang commented Jun 9, 2026

Copy link
Copy Markdown
Member

Summary

Horde_Session now delegates to Horde\Core\Session\HordeSession for data storage and to Horde\Token\Token for CSRF tokens. setup() installs the modern Horde\SessionHandler\SessionHandler via session_set_save_handler.

The legacy public API and $GLOBALS['session'] global are preserved. Horde_Pack wire format is kept for ENCRYPT/TYPE_ARRAY/TYPE_OBJECT masks so existing on-disk sessions continue to decode. A _mirrorToSession() helper syncs the modern payload into $_SESSION before shutdown / destroy so PHP's save handler writes the up-to-date data.

Token service resolution is lazy to avoid a circular DI dependency at construction time (TokenServiceFactory::create() reads $GLOBALS['session'], which is the shim itself). checkToken rethrows as Horde_Exception('Invalid token!') so existing call sites that catch Horde_Exception keep working.

Constructor args are all optional with a no-injector fallback so legacy tests that do new Horde_Session() (e.g. Horde_Test_Factory_Session, NlsconfigTest) continue to work.

setup() drops the dead use_only_cookies = 0 branch (URL-based sessions are not used) and keeps the FQDN check with an expanded message that names localhost as the typical pitfall.

Token semantics change

Legacy $session->getToken() returned a stable per-session random id stored in $_SESSION['horde']['session_token']. After this change, tokens are HMAC-signed via Horde\Token\Token and are NOT stable across calls within a session. All known framework + app call sites round-trip the token (emit, POST, verify), so the change is safe. Two cache-key consumers in Core (Horde_Core_Cache_Session, Horde_Core_HashTable_PersistentSession) already moved to sha1(session_id()) in anticipation of this cutover.

Standalone

This PR is self-sufficient. No accompanying changes in horde/base or apps are required. The shim preserves every legacy entry point (get/set/exists/remove/getToken/checkToken/getNonce/setup/start/close/destroy/clean/isActive).

Follow-ups (separate PRs)

  • Simplify Horde_Registry session bootstrap now that the shim does the heavy lifting.
  • Migrate bin/horde-sessions-gc and bin/horde-active-sessions to Horde\SessionHandler\SessionAdministrator; remove the legacy Horde_Core_Factory_SessionHandler.
  • Migrate the four direct $_SESSION access sites (base/src/Service/AuthenticationService.php, base/src/Auth/ResponsiveLoginController.php, base/src/Portal/ResponsivePortalController.php, turba/lib/Turba.php) to inject HordeSession.
  • Add characterization tests pinning the legacy contract (token round-trip, encryption symmetry, mirror, nonce single-use, regeneration rekeying).

Refs #131

Horde_Session now delegates to Horde\Core\Session\HordeSession for data
storage and to Horde\Token\Token for CSRF tokens. setup() installs the
modern Horde\SessionHandler\SessionHandler via session_set_save_handler.

Legacy public API and $GLOBALS['session'] are preserved. Horde_Pack wire
format kept for ENCRYPT/TYPE_ARRAY/TYPE_OBJECT masks so existing on-disk
sessions continue to decode. _mirrorToSession() syncs the modern payload
into $_SESSION before shutdown.

Token service resolved lazily to avoid a circular DI dependency. checkToken
rethrows as Horde_Exception for legacy callers. Constructor args are all
optional with a no-injector fallback for legacy tests.

setup() drops the dead use_only_cookies=0 branch and expands the FQDN
mismatch message to name localhost.

Refs #131
@ralflang ralflang merged commit 8e8a3f4 into FRAMEWORK_6_0 Jun 9, 2026
0 of 4 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.

1 participant