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>
AccessTokenContext checks grantType twice, so a null rawTokenId argument
bypasses the intended validation. Replace the second check with rawTokenId
and add tests covering all four constructor arguments.
Closes#54051
Signed-off-by: DDINGJOO <173995233+DDINGJOO@users.noreply.github.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>
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>
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>
* 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>
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>
* Enforce client minimum ACR in Device Authorization Grant
Device verification never set REQUESTED_LEVEL_OF_AUTHENTICATION, so
minimum.acr.value and default.acr.values were ignored.
Closes#53618
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
* Use dedicated ACR user and assert pending token in device grant test
Avoid attaching TOTP to the shared device-login user, and verify the
device token endpoint returns authorization_pending until OTP completes.
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
* Apply client default/minimum ACR for CIBA backchannel requests
BackchannelAuthenticationEndpoint only forwarded explicit acr_values,
so client minimum.acr.value and default.acr.values were ignored. Also
add a CIBATest covering the minimum ACR being passed to the auth channel.
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
* Fix device ACR test compile after TimeOffSet API change
Use timeOffSet.set() instead of the removed setTimeOffset/resetTimeOffset helpers.
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
* Keep device ACR test under deprecated testsuite line limit
The Testsuite Deprecation Check rejects >=100 additions per file;
compact the new minimum-ACR coverage so CI can pass.
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
---------
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
UserManagedPermissionService.create() saved the policy through PolicyService.create(AbstractPolicyRepresentation), which skips the audit call, so no AUTHORIZATION_POLICY CREATE admin event was recorded. The CREATE event now uses the same resource path as the existing UPDATE and DELETE events.
Closes#20008
Signed-off-by: Justin Brueggemann <167224505+JBoogieman@users.noreply.github.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>
Prevent opportunistic STARTTLS downgrade attacks by setting
mail.smtp.starttls.required=true whenever starttls is configured,
so AUTH and message data are never sent in plaintext.
Closes#53674
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
getRealmRoleMappings() and getCompositeRealmRoleMappings() were missing
the per-role auth.roles().canView() filter that getRoleMappings() already
applied. A narrow admin with only view-user permission could enumerate
every realm role assigned to a user or group through the /realm and
/realm/composite siblings, even though the combined /role-mappings endpoint
correctly hid them.
Added the missing filter to both methods and a regression test that verifies
all three endpoints behave consistently.
Closes#52399
Signed-off-by: m3l4n0ff <y.zendour2@gmail.com>