Configure Extension with UiPageProvider #1
2 changed files with 75 additions and 22 deletions
validate configuration before saving it
commit
88ffb9b9f4
|
|
@ -1,15 +1,23 @@
|
||||||
package de.ccc.hamburg.keycloak.attribute_endpoints;
|
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.Config;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.component.ComponentValidationException;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
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.UiPageProvider;
|
||||||
import org.keycloak.services.ui.extend.UiPageProviderFactory;
|
import org.keycloak.services.ui.extend.UiPageProviderFactory;
|
||||||
|
import org.keycloak.userprofile.UserProfileProvider;
|
||||||
|
|
||||||
|
kritzl marked this conversation as resolved
Outdated
|
|||||||
import java.util.List;
|
import com.google.auto.service.AutoService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements UiPageProvider to show a config page in the admin
|
* Implements UiPageProvider to show a config page in the admin
|
||||||
|
|
@ -35,18 +43,70 @@ public class AdminUiPage implements UiPageProvider, UiPageProviderFactory<Compon
|
||||||
return PROVIDER_ID;
|
return PROVIDER_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHelpText() {
|
public String getHelpText() {
|
||||||
return "Configure endpoints of the Attribute Endpoint Provider.";
|
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()) {
|
||||||
|
june
commented
Currently this gives us an ugly exception in the Keycloak logs for an empty slug, as Currently this gives us an ugly exception in the Keycloak logs for an empty slug, as `configAttributeSlug` is then `null`. Not critical as it still just errors, but we should probably check, if `configAttributeSlug` is `null`. I guess then adding a null check to all the other `config$thing`s is probably a good idea as well.
|
|||||||
|
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
|
@Override
|
||||||
public List<ProviderConfigProperty> getConfigProperties() {
|
public List<ProviderConfigProperty> getConfigProperties() {
|
||||||
return ProviderConfigurationBuilder.create()
|
return ProviderConfigurationBuilder.create()
|
||||||
.property()
|
.property()
|
||||||
.name("slug")
|
.name("slug")
|
||||||
.label("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)
|
.type(ProviderConfigProperty.STRING_TYPE)
|
||||||
.add()
|
.add()
|
||||||
|
|
||||||
|
|
@ -80,4 +140,5 @@ public class AdminUiPage implements UiPageProvider, UiPageProviderFactory<Compon
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package de.ccc.hamburg.keycloak.attribute_endpoints;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
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.services.resource.RealmResourceProvider;
|
||||||
import org.keycloak.userprofile.UserProfileProvider;
|
import org.keycloak.userprofile.UserProfileProvider;
|
||||||
|
|
||||||
import io.quarkus.security.UnauthorizedException;
|
import jakarta.ws.rs.ForbiddenException;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.NotAuthorizedException;
|
import jakarta.ws.rs.NotAuthorizedException;
|
||||||
import jakarta.ws.rs.NotFoundException;
|
import jakarta.ws.rs.NotFoundException;
|
||||||
|
|
@ -102,16 +101,9 @@ public class AttributeEndpointsResourceProvider implements RealmResourceProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
UserModel authUser = auth.getUser();
|
||||||
|
if (!authUser.hasRole(authRole)) {
|
||||||
{
|
throw new ForbiddenException("User does not have required auth role.");
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (componentList.size() > 1) {
|
if (componentList.size() > 1) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue
I think getting rid of the emojis, while they are fun, might be a good idea.