keycloak/keycloak

Can't see the config of custom PolicyProvider

Open

#41.833 aperta il 13 ago 2025

Vedi su GitHub
 (6 commenti) (6 reazioni) (0 assegnatari)Java (8346 fork)batch import
area/admin/fine-grained-permissionsarea/admin/uihelp wantedkind/enhancementpriority/normalstatus/auto-bumpstatus/auto-expireteam/core-iamteam/core-shared

Metriche repository

Star
 (34.398 star)
Metriche merge PR
 (Merge medio 6g 19h) (384 PR mergiate in 30 g)

Descrizione

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/fine-grained-permissions

Describe the bug

Hi, I want to use Fine Grained Admin Permissions. I need a policy that will allow access to the clients whose clientId starts with pre-defined prefix. For that case, I implemented my custom PolicyProvider, PolicyProviderFactory, AbstractPolicyRepresentation.

As a result, I can see my policy in the New policy dialogue but when I select it, the config is not present. There is some Code input field instead of my config prefix field of type string.

Create new policy:

Policy config window

public class ClientPrefixPolicyRepresentation extends AbstractPolicyRepresentation {

  private String clientPrefix;

  @Override
  public String getType() {
    return "client-prefix";
  }

  public String getClientPrefix() {
    return clientPrefix;
  }

  public void setClientPrefix(String clientPrefix) {
    this.clientPrefix = clientPrefix;
  }

  @Override
  public String getDescription() {
    return "Only allow access to clients whose ID starts with this prefix.";
  }
}
public class ClientPrefixPolicyProvider implements PolicyProvider {

  private final Logger logger = Logger.getLogger(ClientPrefixPolicyProvider.class);
  private final BiFunction<Policy, AuthorizationProvider, ClientPrefixPolicyRepresentation> representationFunction;

  public ClientPrefixPolicyProvider(
      BiFunction<Policy, AuthorizationProvider, ClientPrefixPolicyRepresentation> representationFunction) {
    this.representationFunction = representationFunction;
  }

  @Override
  public void evaluate(Evaluation evaluation) {
    ClientPrefixPolicyRepresentation representation = representationFunction.apply(
        evaluation.getPolicy(), evaluation.getAuthorizationProvider());
    ResourcePermission permission = evaluation.getPermission();
    String clientUUID = permission.getResource().getName();
    RealmModel realm = evaluation.getAuthorizationProvider().getRealm();
    ClientModel clientModel = realm.getClientById(clientUUID);
    if (clientModel != null) {
      String clientId = clientModel.getClientId();
      if (clientId.startsWith(representation.getClientPrefix())) {
        evaluation.grant();
      } else {
        evaluation.deny();
      }
    }
  }

  @Override
  public void close() {

  }
}
@AutoService(PolicyProviderFactory.class)
public class ClientPrefixPolicyProviderFactory implements PolicyProviderFactory<ClientPrefixPolicyRepresentation> {

  private final ClientPrefixPolicyProvider provider = new ClientPrefixPolicyProvider(this::toRepresentation);

  @Override
  public String getName() {
    return "Client prefix policy";
  }

  @Override
  public String getGroup() {
    return "Identity based";
  }

  @Override
  public PolicyProvider create(AuthorizationProvider authorizationProvider) {
    return provider;
  }

  @Override
  public ClientPrefixPolicyRepresentation toRepresentation(Policy policy,
      AuthorizationProvider authorizationProvider) {
    ClientPrefixPolicyRepresentation representation = new ClientPrefixPolicyRepresentation();
    representation.setClientPrefix(getClientPrefix(policy));
    return representation;
  }

  @Override
  public Class<ClientPrefixPolicyRepresentation> getRepresentationType() {
    return ClientPrefixPolicyRepresentation.class;
  }

  @Override
  public PolicyProvider create(KeycloakSession keycloakSession) {
    return provider;
  }

  @Override
  public void init(Scope scope) {
  }

  @Override
  public void postInit(KeycloakSessionFactory keycloakSessionFactory) {

  }

  @Override
  public void close() {

  }

  @Override
  public String getId() {
    return "client-prefix";
  }

  @Override
  public List<ProviderConfigProperty> getConfigMetadata() {
    return ProviderConfigurationBuilder.create()
        .property()
        .name("prefix")
        .label("Client prefix")
        .type("string")
        .helpText("Only allow access to clients whose ID starts with this prefix.")
        .required(true)
        .add()
        .build();
  }

  private String getClientPrefix(Policy policy) {
    String clientPrefix = policy.getConfig().get("prefix");

    if (clientPrefix != null) {
      try {
        return JsonSerialization.readValue(clientPrefix, String.class);
      } catch (IOException e) {
        throw new RuntimeException(
            "Could not parse client prefix [" + clientPrefix + "] from policy config ["
                + policy.getName() + "].", e);
      }
    }

    return "";
  }
}

Can you please advice what is wrong with the implementation and how to make it work?

Version

26.3.2

Regression

  • The issue is a regression

Expected behavior

Custom policy is configurable in the UI.

Actual behavior

Custom policy is not configurable in the UI.

How to Reproduce?

Implement a custom policy and go to Create new policy in the Configure -> Permissions menu

Anything else?

No response

Guida contributor