validate configuration before saving it

This commit is contained in:
kritzl 2026-02-21 15:46:41 +01:00
commit 88ffb9b9f4
Signed by: kritzl
SSH key fingerprint: SHA256:5BmINP9VjZWaUk5Z+2CTut1KFhwLtd0ZynMekKbtViM
2 changed files with 75 additions and 22 deletions

View file

@ -1,15 +1,23 @@
package de.ccc.hamburg.keycloak.attribute_endpoints;
import com.google.auto.service.AutoService;
import java.util.List;
import java.util.regex.Pattern;
import org.keycloak.Config;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.services.ui.extend.UiPageProvider;
import org.keycloak.services.ui.extend.UiPageProviderFactory;
import org.keycloak.userprofile.UserProfileProvider;
import java.util.List;
import com.google.auto.service.AutoService;
/**
* Implements UiPageProvider to show a config page in the admin
@ -35,18 +43,70 @@ public class AdminUiPage implements UiPageProvider, UiPageProviderFactory<Compon
return PROVIDER_ID;
}
@Override
public String getHelpText() {
return "Configure endpoints of the Attribute Endpoint Provider.";
}
@Override
public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) {
String errorString = "\n";
Boolean hasError = false;
Pattern slugPattern = Pattern.compile("^[a-zA-Z0-9_-]*$");
String configAttributeSlug = model.getConfig().getFirst("slug");
if (!slugPattern.matcher(configAttributeSlug).matches()) {
hasError = true;
errorString += " • [Slug] can only contain anlphanumeric characters, dash and underscore (a-z A-Z 0-9 _ - )\n";
}
String configAuthRole = model.getConfig().getFirst("auth-role");
RoleModel authRole = realm.getRole(configAuthRole);
if (authRole == null) {
hasError = true;
errorString += " • [Auth Role] does not exist\n";
}
String configMatchRole = model.getConfig().getFirst("match-role");
RoleModel matchRole = realm.getRole(configMatchRole);
if (matchRole == null) {
hasError = true;
errorString += " • [Match Role] does not exist\n";
}
UserProfileProvider profileProvider = session.getProvider(UserProfileProvider.class);
UPConfig upconfig = profileProvider.getConfiguration();
String configAttributeGroup = model.getConfig().getFirst("attribute-group");
if (!upconfig.getGroups().stream().anyMatch(g -> g.getName().equals(configAttributeGroup))) {
hasError = true;
errorString += " • [Attribute Group] does not exist\n";
}
String configAttributeRegex = model.getConfig().getFirst("attribute-regex");
Boolean regexIsBlank = configAttributeRegex == null;
if (!regexIsBlank) {
try {
Pattern.compile(configAttributeRegex);
} catch (Exception e) {
hasError = true;
errorString += " • [Attribute RegEx] is not a valid regex pattern\n";
}
}
if (hasError) {
throw new ComponentValidationException(errorString);
}
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return ProviderConfigurationBuilder.create()
.property()
.name("slug")
.label("Slug")
.helpText("The slug in the path of the API endpoint (e.g. /realms/:realm/attribute-endpoint-provider/export/:slug)")
.helpText(
"The slug in the path of the API endpoint (e.g. /realms/:realm/attribute-endpoint-provider/export/:slug)")
.type(ProviderConfigProperty.STRING_TYPE)
.add()
@ -80,4 +140,5 @@ public class AdminUiPage implements UiPageProvider, UiPageProviderFactory<Compon
.build();
}
}

View file

@ -2,7 +2,6 @@ package de.ccc.hamburg.keycloak.attribute_endpoints;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@ -23,7 +22,7 @@ import org.keycloak.services.managers.AuthenticationManager.AuthResult;
import org.keycloak.services.resource.RealmResourceProvider;
import org.keycloak.userprofile.UserProfileProvider;
import io.quarkus.security.UnauthorizedException;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotAuthorizedException;
import jakarta.ws.rs.NotFoundException;
@ -102,16 +101,9 @@ public class AttributeEndpointsResourceProvider implements RealmResourceProvider
}
}
try
{
UserModel user = auth.getUser();
if (!user.hasRole(authRole)) {
throw new UnauthorizedException("User does not have required auth role.");
}
} catch (Exception e) {
System.err.println(e);
return Response.status(403, e.getMessage()).build();
UserModel authUser = auth.getUser();
if (!authUser.hasRole(authRole)) {
throw new ForbiddenException("User does not have required auth role.");
}
if (componentList.size() > 1) {