Look up top-level groups by exact name when the import payload has no path.
Cover plain and slash-containing names under SKIP, OVERWRITE and FAIL.
Closes#53934
Signed-off-by: Chethann <chethankumar0544@gmail.com>
* Lock the MERGE target of the emulated "on conflict" upsert on SQL Server
Without a lock hint, two concurrent upserts of the same new key could
both take "when not matched", and one failed with a primary key
violation (2627). Add HOLDLOCK to the MERGE target.
* Make the emulated "on conflict" upserts race-free on Oracle
MERGE reads the target with a consistent read, so a concurrent insert of
the same key made it fail with ORA-00001. Render "do nothing" as an
INSERT with the IGNORE_ROW_ON_DUPKEY_INDEX hint, and replace the
conditional "do update ... where expired" upserts of single-use objects
and revoked tokens with "delete if expired, then insert do nothing".
Signed-off-by: Semih702 <semih702do@gmail.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Co-authored-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Both admin REST endpoints that re-parent an existing group accepted a
request to move a group into its own subtree, which created a cycle in
the group hierarchy and made the request fail with an internal error:
JpaRealmProvider.moveGroup re-parented the group and
KeycloakModelUtils.buildGroupPath then recursed up the now-cyclic
parent chain until the stack overflowed, so the Admin API answered
HTTP 500 while the group was not moved (#53936).
GroupResource.addChild and OrganizationGroupResource.addChild now
reject such moves up front with 400 and the message 'Cannot move group
<name> under itself or its descendants', including the degenerate
self-move that previously answered 204 after a silent storage-level
no-op. JpaRealmProvider.moveGroup additionally enforces the same
invariant at the storage level with a ModelValidationException, so
programmatic callers of RealmModel.moveGroup can no longer corrupt
the hierarchy either; the silent no-op for a direct self-move is kept
for backwards compatibility. Moving a group under a parent outside its
own subtree is unchanged.
GroupTest and OrganizationGroupHierarchyTest gain regression tests
covering self-move, direct-child, deeper-descendant rejections, the
unchanged hierarchy afterwards, and a legal move as positive control.
Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
Since the user-storage lastSync timestamp was split into the per-mode
keys lastSync_FULL / lastSync_CHANGED (#44275), StripSecretsUtils
removed both keys from the component representation and the realm
partial export, because undeclared config keys are stripped and the
per-mode keys were never added to UserStorageProviderSpi.commonConfig()
(the legacy lastSync key stayed declared but is no longer written).
Monitoring and admin tooling had no supported way left to check when a
provider last synced.
Declare lastSync_FULL and lastSync_CHANGED in commonConfig(), keeping
the legacy lastSync declared for installations upgraded in place that
still carry the legacy key. The values were already persisted by the
sync; only the exposure was lost.
Adds LastSyncRestTest covering the admin REST flow (component
representation + partial export) end to end; fails on unmodified main
with the exact reported symptom.
Fixes#53989
Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
Closes#51066
clearCache() only cleared the theme cache, not the FreeMarker template cache or the gzip resource cache, so edited templates/resources kept serving stale content. Now it clears all three.
Also clears the cache from #52802 as suggested on the issue.
Signed-off-by: Faseela K <faseela.k@est.tech>
Signed-off-by: Alexander Schwartz <alexander.schwartz@gmx.net>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Co-authored-by: Alexander Schwartz <alexander.schwartz@gmx.net>
Viewing a user's sessions, offline sessions and consents only required
permission to view the user, so an admin without view permission on a
client could still read its UUID, clientId and the user's grants for it.
The same leak existed in the Admin UI sessions list and in the other
clients listed in a client's session endpoints.
Fixes#50981
Signed-off-by: Stan Silvert <ssilvert@redhat.com>
Signed-off-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
Co-authored-by: Tomas Kyjovsky <tkyjovsk@ibm.com>
#53912 checks sector_identifier_uri against the executor's trusted
domains and covers the rejected case. Add the accepted case: a public
CIMD client with subject_type pairwise and a sector_identifier_uri on
a trusted domain that lists the redirect URI completes the code flow,
and the persisted client gets a pairwise mapper with that URI.
Closes#50529
Signed-off-by: Oleksii Udovychenko <oleksii-udovychenko@users.noreply.github.com>
When a client sends prompt=none and the identity provider accepts passive
login forwards from the client, the broker forwards the passive login to the
IDP. The prompt parameter of the resulting authorization request was however
built from the identity provider configuration alone, so an IDP configured
with its own prompt (e.g. login) silently suppressed the forwarded
prompt=none: instead of being logged in silently, the user was sent through
an interactive login, and the expected passive-login error semantics
(login_required / interaction_required) were lost.
Give the forwarded passive login precedence over the IDP-configured prompt,
so the authorization request sent to the IDP always carries prompt=none when
the passive login was forwarded.
Also remove the @Ignore from KcOidcBrokerPromptNoneRedirectTest now that the
tests pass again: the assertions still expected the 2019 account console URL
shape (/account/?error=...) while the account console now lives at
/account?error=..., and add a regression test proving the forwarded
prompt=none wins over an IDP-configured prompt=login.
Closes#53865Closes#20642
Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
FGAPEvaluation.isDenied() checks deniedScopes.contains(deniedScope)
before denying on a permission for a specific resource, but not on a
permission for all resources of a type. An all-users permission for
manage-group-membership whose policy did not match an admin therefore
denied that admin map-roles, even with an all-users map-roles
permission. Apply the same scope check to the resource-type branch.
Closes#50740
Signed-off-by: Santosh Kumar Puppala <puppalas97@gmail.com>
* Replace cached gzip theme resources atomically
Concurrent first requests for a theme resource each gzip it to a temp
file and move it over the cached copy. Without ATOMIC_MOVE the JDK
replaces an existing target by deleting it and then renaming, so a
request opening the file in between gets FileNotFoundException (and on
JDK 17 a second mover's own delete can fail with NoSuchFileException).
getEncodedStream then returns null and the resource is answered with
404.
Publish the cache file only by an ATOMIC_MOVE of the completed temp
file, which is in the target's directory, so a cached file that exists
is always complete. On POSIX this is a rename(2) that replaces the
target in one step; on Windows it is a MoveFileEx replace, which fails
while another request has the target open. Racing requests all write
the same content, so if the move fails and the target exists, serve
that target. If the move fails and there is no target, serve this
request's gzipped bytes from memory without caching them. The temp
file is deleted whenever the move fails, and a failed delete does not
fail the request.
GzipResourceEncodingProviderTest has 16 threads request the same
uncached resource at once, over 100 rounds, and checks that every one
gets a stream that decompresses to the original content.
Closes#53238
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Cornelius Roemer <cornelius.roemer@gmail.com>
* Falls back to original resource when encoding fails
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
---------
Signed-off-by: Cornelius Roemer <cornelius.roemer@gmail.com>
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Bartoš <mabartos@redhat.com>
The periodic sync gate (isSyncPeriod) skipped the next timer tick whenever
a sync run took longer than about a second, so the effective sync period
doubled. The timer schedules each run 'period' seconds after the start of
the previous run, but lastSync recorded the time the previous run
finished, so the tick landed less than period - 1 seconds after the
recorded time and was ignored.
Capture the start of the sync run before executing it and stamp that
into lastSync when the run succeeds, mirroring the timer's start-anchored
scheduling. A run that throws still leaves lastSync untouched, so the
next tick retries as before. Incremental sync also no longer misses
entries changed while the previous run was in progress, as syncSince now
receives the start of the previous run instead of its finish.
Fixes keycloak#53930
Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
Batch importing of users would detach the session's realm at the end,
so JSON migrations (MigrateTo24_0_0) failed with LazyInitializationException
when a users file was present.
Closes#52870
Signed-off-by: Haeata <haeata.ash@pm.me>
Fix V2 broker token endpoint to refresh expired external IDP tokens
When retrieving external OAuth2 access tokens via POST /broker/{alias}/token,
expired tokens are now automatically refreshed when a refresh_token is available.
The response includes expiresIn so callers can determine token lifetime.
Supports all token storage configurations (DB-only, session-only, both, neither).
Closes keycloak#49317
* Emit CaepSessionRevoked when an admin deletes a single user session (#53627)
DELETE /admin/realms/{realm}/sessions/{id} (the per-session "Sign out" in the
admin console) fired only a ResourceType.USER_SESSION admin event, which the
SSF transmitter ignored, and backchannelLogout emits no LOGOUT user event.
RealmAdminResource.deleteSession now attaches the session's user as the
Details.USER_ID admin event detail (the path only names the session, which
is already removed when listeners run). The SSF mapper maps that event to a
synthetic LOGOUT carrying the concrete session id, so the resulting
CaepSessionRevoked SET names exactly the revoked session.
Also pass the admin event through for "log out all user sessions" so its
SET reports initiating_entity admin instead of user.
Fixes#53627
Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
* SSF: Document why admin session deletion skips the liveness check
Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
---------
Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
Previously, a RealmConfigInterceptor applied to every realm created within a test class, and it couldn't distinguish which realm it is decorating.
AbstractInterceptorHelper contains now an applies(..) filter that defaults to true, and Interception exposes the interceptor annotation, which is available whether or not the interceptor has been deployed. An interception that does not apply is skipped before it is applied, logged, or registered as a dependent of the intercepted instance. RealmConfigInterceptor exposes this as appliesTo(..).
Existing interceptors inherit the default and keep applying to every realm.
Closes#51751Closes#51752
Signed-off-by: Bernhard Strähle <bernhard.straehle@gmail.com>
The Oid4VciNonHaipTestSuite conformance job was intermittently failing on
the oid4vci-1_0-issuer-happy-flow-multiple-clients module, whose
VCIEnsureCredentialTimeClaimsNotLinkable check (RFC 9901 10.1) occasionally
flagged the iat claim as linkable. The realm used the RANDOMIZE time-claim
strategy, which is probabilistic: the two independent random offsets applied
to the same-dataset credentials sometimes coincide, leaving an iat delta that
matches the real inter-issuance gap and tripping the check.
Switch the conformance realm to the deterministic ROUND strategy with a
MINUTE unit. Rounding quantizes iat to unit boundaries so the delta between
two credentials issued seconds apart is either 0 or a full unit, never
approximately the real gap. MINUTE is the coarsest usable unit here: exp is
computed from the already-normalized iat and the scopes use a 300s (5 min)
expiry, so MINUTE keeps exp exactly 300s after iat, while the coarser HOUR and
DAY units would truncate exp back onto iat. MINUTE backdates iat by at most
60s, within the envelope the rest of the suite already tolerates.
Closes#53787
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
Closes#53312
The subject of the notify-user step was passed to the email provider
verbatim, where it ends up as a java.text.MessageFormat pattern when it
does not match a key from the email theme message bundle. A subject such
as "${user.firstName}, log in to keep your account active" therefore
failed with:
IllegalArgumentException: can't parse argument number: user.firstName
The email was never sent, the step was still marked as completed, and
the following steps of the workflow ran as usual.
Resolve properties in the subject with the same resolver already used
for the custom message, and escape the resulting text so that it is not
interpreted as a MessageFormat pattern. Escaping is a no-op for a text
without MessageFormat meta characters, so a subject referencing a key
from the message bundle keeps being resolved and localized as before.
It also makes a subject with a single quote, like "your account's
review", keep its quote instead of silently losing it.
Signed-off-by: Luca Tronchin <ltronky@gmail.com>