Trace validation of users to see contribution of external timing (#36060)

Closes #36059

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2025-01-07 12:35:48 +01:00 committed by GitHub
parent 9416e9afdd
commit c651323b7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 2 deletions

View File

@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.opentelemetry.api.trace.StatusCode;
import org.jboss.logging.Logger;
import org.keycloak.common.Profile;
import org.keycloak.common.constants.ServiceAccountConstants;
@ -75,6 +76,7 @@ import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryMethodsProvider;
import org.keycloak.storage.user.UserQueryProvider;
import org.keycloak.storage.user.UserRegistrationProvider;
import org.keycloak.tracing.TracingProvider;
import org.keycloak.userprofile.AttributeMetadata;
import org.keycloak.userprofile.UserProfileDecorator;
import org.keycloak.userprofile.UserProfileMetadata;
@ -171,7 +173,21 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
for (CredentialAuthentication credentialAuthentication : credentialAuthenticationStream
.filter(credentialAuthentication -> credentialAuthentication.supportsCredentialAuthenticationFor(input.getType()))
.collect(Collectors.toList())) {
CredentialValidationOutput validationOutput = credentialAuthentication.authenticate(realm, input);
CredentialValidationOutput validationOutput = session.getProvider(TracingProvider.class).trace(credentialAuthentication.getClass(), "authenticate",
span -> {
CredentialValidationOutput output = credentialAuthentication.authenticate(realm, input);
if (span.isRecording()) {
if (output != null) {
CredentialValidationOutput.Status status = output.getAuthStatus();
span.setAttribute("kc.validationStatus", status.name());
if (status == CredentialValidationOutput.Status.FAILED) {
span.setStatus(StatusCode.ERROR);
}
}
}
return output;
}
);
if (Objects.nonNull(validationOutput)) {
CredentialValidationOutput.Status status = validationOutput.getAuthStatus();
if (status == CredentialValidationOutput.Status.AUTHENTICATED || status == CredentialValidationOutput.Status.CONTINUE || status == CredentialValidationOutput.Status.FAILED) {

View File

@ -17,6 +17,7 @@
package org.keycloak.credential;
import io.opentelemetry.api.trace.StatusCode;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -29,6 +30,7 @@ import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderFactory;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.tracing.TracingProvider;
import java.util.LinkedList;
import java.util.List;
@ -252,7 +254,18 @@ public class UserCredentialManager extends AbstractStorageManager<UserStoragePro
}
private void validate(RealmModel realm, UserModel user, List<CredentialInput> toValidate, CredentialInputValidator validator) {
toValidate.removeIf(input -> validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input));
toValidate.removeIf(input -> {
if(validator.supportsCredentialType(input.getType())) {
return session.getProvider(TracingProvider.class).trace(validator.getClass(), "isValid", span -> {
boolean valid = validator.isValid(realm, user, input);
if (!valid) {
span.setStatus(StatusCode.ERROR);
}
return valid;
});
}
return false;
});
}
private static <T> Stream<T> getCredentialProviders(KeycloakSession session, Class<T> type) {