keycloak / keycloak

Open Source Identity and Access Management For Modern Applications and Services

Home Page:https://www.keycloak.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

MAP_TYPE property doesn't activate the "Save" button when updating own Jpa impl

Arnaud-FELTZ opened this issue · comments

Before reporting an issue

  • I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

Area

admin/ui

Describe the bug

I am currently developing a custom UserStorageProviderFactory for Keycloak 24.0.4, which includes an admin configuration page.

This page is designed to collect several pieces of information from the administrator:

Property Type Required
API URI STRING_TYPE Yes
Trust Certificates BOOLEAN_TYPE Yes
Headers MAP_TYPE No

The issue arises when attempting to modify the custom headers on the admin page. Despite making changes to the headers, the "Save" button remains disabled (greyed out), preventing the submission of the modified configuration.
This behavior persists even after ensuring that both keys and values are provided for each entry in the map of headers. The expected behavior is for the "Save" button to become active (enabled) whenever any configuration changes are made, allowing the administrator to save the updated settings.

My implementation:

package com.contoso.keycloak.authenticator.jpa;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.storage.UserStorageProviderFactory;
import org.keycloak.validate.validators.UriValidator;

public class JpaStorageProviderFactory implements UserStorageProviderFactory<JpaStorageProvider> {

    public static final String PROVIDER_ID = "CONTOSO-JPA";
    private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();

    static {
        // API URL
        ProviderConfigProperty apiUrl = new ProviderConfigProperty();
        apiUrl.setName("apiUrl");
        apiUrl.setLabel("API URL");
        apiUrl.setType(ProviderConfigProperty.STRING_TYPE);
        apiUrl.setHelpText("The URL of the external API used to authenticate suppliers.");
        apiUrl.setRequired(true);
        configProperties.add(apiUrl);

        // Trust Certificate
        ProviderConfigProperty trustCertificate = new ProviderConfigProperty();
        trustCertificate.setName("trustCertificate");
        trustCertificate.setLabel("Trust Certificate");
        trustCertificate.setType(ProviderConfigProperty.BOOLEAN_TYPE);
        trustCertificate.setHelpText("Whether to trust HTTPS certificate.");
        trustCertificate.setDefaultValue(true);
        configProperties.add(trustCertificate);

        // Headers
        ProviderConfigProperty headers = new ProviderConfigProperty();
        headers.setName("headers");
        headers.setLabel("Headers");
        headers.setType(ProviderConfigProperty.MAP_TYPE);
        headers.setHelpText("Custom headers to add to the request when calling the API.");
        configProperties.add(headers);
    }

    @Override
    public JpaStorageProvider create(KeycloakSession session, ComponentModel model) {
        return new JpaStorageProvider(session, model);
    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }

    @Override
    public String getHelpText() {
        return "Contoso - JPA";
    }

    @Override
    public void close() {
    }

    @Override
    public List<ProviderConfigProperty> getConfigProperties() {
        return configProperties;
    }

    @Override
    public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config)
        throws ComponentValidationException {

        // Validate the apiUrl configuration
        String apiUrl = config.getConfig().getFirst("apiUrl");
        if (apiUrl == null || apiUrl.isEmpty()) {
            throw new ComponentValidationException("API URL is required.");
        }

        // Use Keycloak's built-in URL validator
        UriValidator uriValidator = new UriValidator();
        try {
            URI apiUri = URI.create(apiUrl);

            if (!uriValidator.validateUri(apiUri, Set.copyOf(UriValidator.DEFAULT_ALLOWED_SCHEMES), false, true)) {
                throw new ComponentValidationException("Invalid API URL.");
            }
        } catch (Exception e) {
            throw new ComponentValidationException("Invalid API URL.");
        }
    }
}

Capture below, once created, I return on the JPA to modify it. Adding the headers b/c (red squared) doesn't activate the Save button.

image

Version

24.0.4

Regression

  • The issue is a regression

Expected behavior

When adding/updating/deleting a value in a MAP_TYPE property, the Save button should be enabled.

Actual behavior

When adding/updating/deleting a value in a MAP_TYPE entry, the Save button remains disabled.

How to Reproduce?

  • Create a custom JPA using provided code for Factory
  • Compile and add resulting provider to keycloak
  • Build and start keycloak
  • Create your custom JPA and validate
  • Now return on your custom JPA and try to modify the "Headers", Save remains disabled while you added/updated/removed entries

Anything else?

No response