feat: carry authz stamps on UserDetailsImpl, unify session/JWT/PAT refresh#24496
Closed
netroms wants to merge 2 commits into
Closed
feat: carry authz stamps on UserDetailsImpl, unify session/JWT/PAT refresh#24496netroms wants to merge 2 commits into
netroms wants to merge 2 commits into
Conversation
Replace role/user-driven mass SessionRegistry.expireNow with a JDBC-backed
two-level invalidation scheme:
- authz_version table (Flyway V2_44_17) holds per-user ('user', <uid>) and
per-role ('role', <uid>) generation stamps plus a single global epoch
row; every bump advances the epoch in the same transaction as the entity
change, so readers can never observe a stamp without its data. The store
SQL is PostgreSQL-only like production; H2 test contexts run against an
in-memory store wired in H2TestConfig.
- SoftRefreshSecurityContextRepository decorates the HttpSession delegate
of the standard security context repository pair: when the epoch moved
and the principal's effective generation differs, it rebuilds the
immutable UserDetails and persists the refreshed context. Decorating
the repository makes the refresh session-only by construction (JWT,
PAT, and stateless authentications never load through it), skips
impersonated sessions, and defers the check until a request actually
accesses its authentication. No logout, no cookie change.
- AuthzService memoizes snapshots per username keyed by a pre-read epoch
(stamp-before-build invariant) with striped single-flight rebuilds;
JWT and PAT auth paths share the same epoch-validated cache.
- Role authority/restriction edits and role deletion bump one role key,
O(1) regardless of member count. User role/org-unit changes and group
membership deltas (import hook owning side + service paths) bump only
affected users.
- Password change, disable, lock, and expiry keep hard session
invalidation.
…fresh Moves the soft-refresh freshness stamp (checked epoch + effective gen) from HTTP session attributes onto the immutable UserDetailsImpl snapshot itself. All three auth paths now converge on one three-way check in DefaultAuthzService: epoch fast path, gen re-stamp path, rebuild path. - AuthzService: currentEpoch()/effectiveGen() replaced by refreshIfStale(principal); identity of the result tells callers whether to persist. - DefaultAuthzService: cache stores stamped snapshots directly (CachedUserDetails wrapper removed). Gen stamps are validated by a second epoch read after the build; if the epoch moved mid-build the gen is stamped 0 (unknown), failing safe. JWT/PAT snapshots gain the gen fast path: unrelated bumps re-stamp instead of rebuilding. - SoftRefreshSecurityContextRepository: session attributes and long parsing removed; the repository only extracts, swaps, and persists. AuthzConstants deleted. - UserDetailsImpl: serialVersionUID pinned to the pre-stamp shape, so sessions serialized by earlier releases deserialize with stamps 0 and take one soft refresh instead of a forced re-login. No release boundary session drain is needed. Guarded by UserDetailsImplSerializationTest.
Contributor
Author
|
Folded into #24493 per single-PR policy — identical tree, squashed into the one signed commit. |
netroms
force-pushed
the
feat/userdetails-soft-refresh-v2
branch
from
July 19, 2026 00:23
a1636ab to
cf053d4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #24493 (review that first). Implements the follow-up direction: the soft-refresh freshness stamp moves from HTTP session attributes onto the immutable
UserDetailsImplsnapshot itself, so sessions, JWT, and PAT all converge on one three-way check.Changes
AuthzService:currentEpoch()/effectiveGen()replaced byrefreshIfStale(principal); result identity tells callers whether to persist.DefaultAuthzService: cache stores stamped snapshots directly (CachedUserDetailswrapper removed). Gen stamps are validated by a second epoch read after the build; if the epoch moved mid-build the gen is stamped 0 (unknown), failing safe. JWT/PAT snapshots gain the gen fast path: unrelated bumps re-stamp instead of rebuilding.SoftRefreshSecurityContextRepository: session attributes (DHIS2_AUTHZ_GEN/DHIS2_AUTHZ_EPOCH) and their parsing removed; the repository only extracts, swaps, and persists.AuthzConstantsdeleted.UserDetailsImpl:serialVersionUIDpinned to the pre-stamp shape, so sessions serialized by earlier releases deserialize with stamps 0 and take one soft refresh instead of a forced re-login. No release-boundary session drain is needed. Guarded byUserDetailsImplSerializationTest.Verification
DefaultAuthzServiceTest10/10,SoftRefreshSecurityContextRepositoryTest9/9,UserDetailsImplSerializationTest3/3.UserControllerTest52,MeControllerTest28,ApiTokenAuthenticationTest11,JwtBearerTokenTest6.AuthzSoftRefreshHooksTest4/4,JdbcAuthzVersionStoreTest7/7.JSESSIONIDunchanged, epoch fast path 200, disable still 302.