* Authorize receiver-driven SSF subject adds per subject
Any SSF receiver with ssf.manage could subscribe to security events for
every user in the realm, since subjects/add only verified stream
ownership. Receiver adds are now gated by ssf.receiverSubjectAddPolicy
(AUTHENTICATED by default, ANY, NONE), and can no longer override a
subject an administrator explicitly ignored.
Fixes#53695
Signed-off-by: Stan Silvert <ssilvert@redhat.com>
Assisted-by: Claude Opus 5.5
* Ignore expired sessions in the SSF AUTHENTICATED add check
Session streams can return user and client sessions past their idle or
max lifespan, so an expired relationship still let a receiver add the
user. Require the same session validity checks as token refresh.
Also restore the default add policy in test cleanup by blanking the
attribute: a client update keeps attributes omitted from the
representation, so ANY/NONE leaked into later tests.
Fixes#53695
Signed-off-by: Stan Silvert <ssilvert@redhat.com>
Assisted-by: Claude Opus 5.5
---------
Signed-off-by: Stan Silvert <ssilvert@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>
The invitation docs only described the registration form. Explain that the
invitee can return to the login page and authenticate through an identity
provider instead, that the resulting membership is unmanaged, and that the
email of the account has to match the address the invitation was sent to.
Note that the invitation is accepted through any identity provider in the
realm, and that a user already linked to the provider skips first broker
login, so the invitation stays pending until it expires.
Signed-off-by: Alexis Rico <sferadev@gmail.com>
With catchNotFound the agent returns null for a missing resource, but most
of these methods declared T | undefined, and several declared no missing
case at all: findProtocolMapperById, findProtocolMappersByProtocol,
roles.findUsersWithRole, workflows.scheduled, getRequiredActionForAlias
(any) and the two policy lookups (void). The admin console now converts
the null where it stores the result as T | undefined, filters it out of
the user, role and policy selections, and shows an empty list where a
missing role or user made the Users in role and Workflows tabs fail.
Closes#43371
Signed-off-by: Yang Bai <271969568+ybai08@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>
The fix from #53502 is backported to 26.8.1, so the upgrade guide note belongs under "Migrating to 26.8.1" rather than 27.0.0.
Signed-off-by: Michal Vavřík <dev@michalvavrik.net>
* Emit credential_id on UPDATE_CREDENTIAL events from built-in required actions
UpdatePassword, UpdateTotp and WebAuthnRegister now set Details.CREDENTIAL_ID on the generic
UPDATE_CREDENTIAL event, matching REMOVE_CREDENTIAL which already carries the credential id. This
lets event consumers identify which credential row was created or updated when a user has multiple
credentials of the same type.
The OTP credential id is surfaced by hydrating it in OTPCredentialProvider.createCredential; the
WebAuthn id is read from the createCredential return value. A password update, however, may be
handled entirely by a federated CredentialInputUpdater (e.g. LDAP in WRITABLE edit mode), in which
case nothing is written to the Keycloak credential store and there is no id to report.
SubjectCredentialManager therefore gains updateCredentialAndGet, which returns the credential
written to the Keycloak credential store, or an empty Optional when a user-storage provider handled
the write. UpdatePassword emits the detail only when a credential comes back.
CredentialInputUpdater gains a default updateCredentialAndGet returning a CredentialUpdate: whether
the updater handled the input, and the credential it wrote to the Keycloak credential store, if
any. The default delegates to updateCredential and reports no credential, so an updater writing to
its own store, or one without data about what it wrote, leaves the event without an id.
PasswordCredentialProvider overrides it and hands over the model produced by the write itself:
nothing is read back afterwards, so no concurrent password write can make the event report another
row, and the required action needs neither a pre-update snapshot nor a hash or timestamp comparison
to find out whether the local row changed. UserCredentialManager asks each updater once and keeps
the first outcome that was handled, so updateCredential keeps reporting what it reported before.
SubjectCredentialManagerCacheAdapter invalidates the cached user for updateCredentialAndGet as it
does for every other mutating method, so the write path does not read credentials through the user
cache.
RecoveryAuthnCodesAction emits the id as well. CredentialHelper.createRecoveryCodesCredential now
returns the credential it created in the local storage, or null when the userStorage handled the
write, so the action applies the same emit-only-when-locally-stored rule as the other required
actions.
The tests compare the emitted value with the credential id actually persisted for the user, rather
than only asserting that some id is present. RequiredActionUpdatePasswordFederatedTest covers the
federated branch: a user whose password is held by a user-storage provider, with a stale password
row left in the Keycloak DB, must not get that untouched row's id on the event.
WebAuthnRegisterAndLoginTest asserts that the deprecated cloned event still carries the details it
had before, as the id is added to the generic event only after that clone is emitted.
The release notes mention the new event detail. The migration guide needs no entry, as the touched
SPI interfaces only gain default methods.
Closes#50247
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Nuno Faria <nuno.faria@getflip.com>
* Fix release notes, avoid executing other updater
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
---------
Signed-off-by: Nuno Faria <nuno.faria@getflip.com>
Signed-off-by: Martin Bartoš <mabartos@redhat.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Martin Bartoš <mabartos@redhat.com>
* Do not chek critical status for key usage validations.
Provide perovider options for backwards compatibility.
Closes#52982
Signed-off-by: rmartinc <rmartinc@redhat.com>
* Move X509BrowserLoginTest and X509DirectGrantTest to the new CI
Closes#52982
Signed-off-by: rmartinc <rmartinc@redhat.com>
* Migrate tests to the new CI
Closes#52982
Signed-off-by: rmartinc <rmartinc@redhat.com>
* Do not initialize stores is tls is not enabled in ManagedCertificates
Closes#52982
Signed-off-by: rmartinc <rmartinc@redhat.com>
---------
Signed-off-by: rmartinc <rmartinc@redhat.com>