Skip to content

Horde_Registry constructor overwrites $GLOBALS['injector'] with a fresh legacy container, breaking session state on the modern Rampage stack #218

Description

@jcdelepine

Small context: the OIDC code that surfaces this is an upcoming revision, not published yet — so this isn't blocking anything currently live, just flagging it while it's fresh.

Found something related in spirit to #190 (legacy/modern coexistence) but a distinct root cause — opening separately rather than piling onto that thread since the mechanism is different.

Symptom: on the Rampage stack, hitting a route whose middleware constructs Horde_Registry (e.g. AuthHordeSession, which depends on it) after an OIDC backchannel logout has revoked the user's IMP token — services/ajax.php/imp/poll is what surfaces it — produces:

PHP Warning: session_set_cookie_params(): Session cookie parameters cannot be changed when a session is active
PHP Warning: session_cache_limiter(): ...
PHP Warning: session_name(): ...
PHP Warning: session_set_save_handler(): ...

and, when the failing check is IMP's own transparent-auth validation, an unwanted forced logout.

Root cause, traced with logging on SessionLifecycle::setup()/SessionLifecycleFactory::create(): within a single Rampage request, SessionLifecycle gets constructed multiple times with different object identities — confirmed the modern container itself caches correctly (added the missing SessionLifecycle::class => SessionLifecycleFactory::class binding to DefaultInjectorBindings, which didn't change the symptom). Tracing the call sites down to Horde_Registry.php:

// line 525
$GLOBALS['injector'] = $injector = new Horde_Injector(new Horde_Injector_TopLevel());

Horde_Registry's own bootstrap builds a fresh legacy Horde_Injector and overwrites $GLOBALS['injector'] with it, every time it's constructed — including when a Horde_Registry instance is built as a middleware dependency mid-request on the modern Rampage stack, which already has its own Horde\Injector\Injector container. From that point on, Horde_Session.php:294 and Registry.php:2321 (both call sites in clearAuth()) resolve SessionLifecycle/HordeSession through this fresh legacy container instead of the Rampage one — so anything already cached in the Rampage container (session already configured via session_set_cookie_params() etc.) gets a second, independent SessionLifecycle instance that redoes setup() on a PHP session that's already active.

Tried pre-seeding $GLOBALS['injector'] with the Rampage container at the top of RampageBootstrap::run() — no effect, since Horde_Registry overwrites it again on construction regardless.

Not something I want to guess a fix for — this is really "two DI containers with different lifetimes coexist in one request whenever legacy Horde_Registry gets constructed on the modern stack," which seems like it needs a decision on your end (should Horde_Registry accept/reuse an injector rather than always minting its own?) rather than a local patch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions