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;
|
||||
|
||||
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
|
||||
|
|
@ -29,55 +37,108 @@ public class AdminUiPage implements UiPageProvider, UiPageProviderFactory<Compon
|
|||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
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()) {
|
||||
|
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
|
||||
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()
|
||||
|
||||
|
||||
.property()
|
||||
.name("attribute-group")
|
||||
.label("Attribute Group")
|
||||
.helpText("The attribute group to export.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
|
||||
|
||||
.property()
|
||||
.name("match-role")
|
||||
.label("Match Role")
|
||||
.helpText("Export only attributes of users with this role.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
|
||||
|
||||
.property()
|
||||
.name("auth-role")
|
||||
.label("Auth Role")
|
||||
.helpText("Role needeed by the authenticated account to be able to use this endpoint.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
|
||||
|
||||
.property()
|
||||
.name("attribute-regex")
|
||||
.label("Attribute RegEx")
|
||||
.helpText("A RegEx Rule used to verify each attribute value. Only matching values are returned.")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.add()
|
||||
|
||||
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
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.