KEYCLOAK-18108 Refactoring retrieve of condition/executor providers. Make sure correct configuration of executor/condition is used for particular provider
This commit is contained in:
parent
c2e2cbe180
commit
71dcbec642
14 changed files with 329 additions and 136 deletions
|
@ -19,6 +19,7 @@
|
||||||
package org.keycloak.representations.idm;
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -28,15 +29,8 @@ public class ClientPolicyConditionRepresentation {
|
||||||
@JsonProperty("condition")
|
@JsonProperty("condition")
|
||||||
private String conditionProviderId;
|
private String conditionProviderId;
|
||||||
|
|
||||||
private ClientPolicyConditionConfigurationRepresentation configuration;
|
@JsonProperty("configuration")
|
||||||
|
private JsonNode configuration;
|
||||||
public ClientPolicyConditionRepresentation() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientPolicyConditionRepresentation(String conditionProviderId, ClientPolicyConditionConfigurationRepresentation configuration) {
|
|
||||||
this.conditionProviderId = conditionProviderId;
|
|
||||||
this.configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getConditionProviderId() {
|
public String getConditionProviderId() {
|
||||||
return conditionProviderId;
|
return conditionProviderId;
|
||||||
|
@ -46,11 +40,11 @@ public class ClientPolicyConditionRepresentation {
|
||||||
this.conditionProviderId = conditionProviderId;
|
this.conditionProviderId = conditionProviderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientPolicyConditionConfigurationRepresentation getConfiguration() {
|
public JsonNode getConfiguration() {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfiguration(ClientPolicyConditionConfigurationRepresentation configuration) {
|
public void setConfiguration(JsonNode configuration) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.keycloak.representations.idm;
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -28,15 +29,8 @@ public class ClientPolicyExecutorRepresentation {
|
||||||
@JsonProperty("executor")
|
@JsonProperty("executor")
|
||||||
private String executorProviderId;
|
private String executorProviderId;
|
||||||
|
|
||||||
private ClientPolicyExecutorConfigurationRepresentation configuration;
|
@JsonProperty("configuration")
|
||||||
|
private JsonNode configuration;
|
||||||
public ClientPolicyExecutorRepresentation() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientPolicyExecutorRepresentation(String executorProviderId, ClientPolicyExecutorConfigurationRepresentation configuration) {
|
|
||||||
this.executorProviderId = executorProviderId;
|
|
||||||
this.configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExecutorProviderId() {
|
public String getExecutorProviderId() {
|
||||||
return executorProviderId;
|
return executorProviderId;
|
||||||
|
@ -46,11 +40,11 @@ public class ClientPolicyExecutorRepresentation {
|
||||||
this.executorProviderId = providerId;
|
this.executorProviderId = providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientPolicyExecutorConfigurationRepresentation getConfiguration() {
|
public JsonNode getConfiguration() {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfiguration(ClientPolicyExecutorConfigurationRepresentation configuration) {
|
public void setConfiguration(JsonNode configuration) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ import org.junit.Test;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.representations.JsonWebToken;
|
import org.keycloak.representations.JsonWebToken;
|
||||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
|
||||||
|
import org.keycloak.representations.idm.ClientPolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -30,6 +34,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -185,5 +190,26 @@ public class JsonParserTest {
|
||||||
return JsonSerialization.readValue(repp, Map.class);
|
return JsonSerialization.readValue(repp, Map.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadClientPolicy() throws Exception {
|
||||||
|
InputStream is = getClass().getClassLoader().getResourceAsStream("sample-client-policy.json");
|
||||||
|
ClientPoliciesRepresentation clientPolicies = JsonSerialization.readValue(is, ClientPoliciesRepresentation.class);
|
||||||
|
|
||||||
|
Assert.assertEquals(clientPolicies.getPolicies().size(), 1);
|
||||||
|
ClientPolicyRepresentation clientPolicy = clientPolicies.getPolicies().get(0);
|
||||||
|
Assert.assertEquals("some-policy", clientPolicy.getName());
|
||||||
|
List<ClientPolicyConditionRepresentation> conditions = clientPolicy.getConditions();
|
||||||
|
Assert.assertEquals(conditions.size(), 1);
|
||||||
|
ClientPolicyConditionRepresentation condition = conditions.get(0);
|
||||||
|
Assert.assertEquals("some-condition", condition.getConditionProviderId());
|
||||||
|
|
||||||
|
ClientPolicyConditionConfigurationRepresentation configRep = JsonSerialization.mapper.convertValue(condition.getConfiguration(), ClientPolicyConditionConfigurationRepresentation.class);
|
||||||
|
Assert.assertEquals(true, configRep.isNegativeLogic());
|
||||||
|
Assert.assertEquals("val1", configRep.getConfigAsMap().get("string-option"));
|
||||||
|
Assert.assertEquals(14, configRep.getConfigAsMap().get("int-option"));
|
||||||
|
Assert.assertEquals(true, configRep.getConfigAsMap().get("bool-option"));
|
||||||
|
Assert.assertNull(configRep.getConfigAsMap().get("not-existing-option"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
20
core/src/test/resources/sample-client-policy.json
Normal file
20
core/src/test/resources/sample-client-policy.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"policies": [
|
||||||
|
{
|
||||||
|
"name": "some-policy",
|
||||||
|
"description": "This is some client policy.",
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "some-condition",
|
||||||
|
"configuration": {
|
||||||
|
"is-negative-logic": true,
|
||||||
|
"string-option": "val1",
|
||||||
|
"int-option": 14,
|
||||||
|
"bool-option": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component model backed by JSON configuration. Useful for providers, which rely on JSON configuration rather than on ComponentModel, which is directly
|
||||||
|
* persisted as entity in the DB (store).
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class JsonConfigComponentModel extends ComponentModel {
|
||||||
|
|
||||||
|
private final String providerType;
|
||||||
|
private final String providerId;
|
||||||
|
private final String componentId;
|
||||||
|
private final JsonNode configNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param providerType
|
||||||
|
* @param realmId
|
||||||
|
* @param providerId
|
||||||
|
* @param configNode JSON configuration of this provider. For example if node corresponds to JSON like "{\"foo\":\"bar\"}", then
|
||||||
|
* component configuration is supposed to have one configuration option "foo" with value "bar"
|
||||||
|
*/
|
||||||
|
public JsonConfigComponentModel(Class<? extends Provider> providerType, String realmId, String providerId, JsonNode configNode) {
|
||||||
|
checkNotNull(providerType, "providerType must be not null");
|
||||||
|
checkNotNull(realmId, "realmId must be not null");
|
||||||
|
checkNotNull(providerId, "providerId must be not null");
|
||||||
|
checkNotNull(configNode, "configNode must be not null for provider " + providerId);
|
||||||
|
this.providerType = providerType.getName();
|
||||||
|
this.providerId = providerId;
|
||||||
|
this.configNode = configNode;
|
||||||
|
|
||||||
|
// We don't have realm model ID of the component, so componentId based on the realmId, providerType, providerId and hashCode of configurations.
|
||||||
|
this.componentId = realmId + "::" + providerType + "::" + this.providerId + "::" + configNode.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkNotNull(Object value, String message) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProviderType() {
|
||||||
|
return providerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProviderId() {
|
||||||
|
return providerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return componentId + "-config";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return componentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean get(String key, boolean defaultValue) {
|
||||||
|
JsonNode sub = configNode.get(key);
|
||||||
|
return sub == null ? defaultValue : sub.asBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long get(String key, long defaultValue) {
|
||||||
|
JsonNode sub = configNode.get(key);
|
||||||
|
return sub == null ? defaultValue : sub.asLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int get(String key, int defaultValue) {
|
||||||
|
JsonNode sub = configNode.get(key);
|
||||||
|
return sub == null ? defaultValue : sub.asInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key, String defaultValue) {
|
||||||
|
JsonNode sub = configNode.get(key);
|
||||||
|
return sub == null ? defaultValue : sub.asText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(String key) {
|
||||||
|
return get(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.vault.VaultTranscriber;
|
import org.keycloak.vault.VaultTranscriber;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -74,6 +75,18 @@ public interface KeycloakSession extends InvalidationHandler {
|
||||||
*/
|
*/
|
||||||
<T extends Provider> T getComponentProvider(Class<T> clazz, String componentId);
|
<T extends Provider> T getComponentProvider(Class<T> clazz, String componentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a component provider for a component from the realm that is relevant to this session.
|
||||||
|
* The relevant realm must be set prior to calling this method in the context, see {@link KeycloakContext#getRealm()}.
|
||||||
|
* @param <T>
|
||||||
|
* @param clazz
|
||||||
|
* @param componentId Component configuration
|
||||||
|
* @param modelGetter Getter to retrieve componentModel
|
||||||
|
* @throws IllegalArgumentException If the realm is not set in the context.
|
||||||
|
* @return Provider configured according to the {@link componentId}, {@code null} if it cannot be instantiated.
|
||||||
|
*/
|
||||||
|
<T extends Provider> T getComponentProvider(Class<T> clazz, String componentId, Function<KeycloakSessionFactory, ComponentModel> modelGetter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param <T>
|
* @param <T>
|
||||||
|
|
|
@ -65,6 +65,7 @@ import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,8 +338,19 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T extends Provider> T getComponentProvider(Class<T> clazz, String componentId) {
|
public <T extends Provider> T getComponentProvider(Class<T> clazz, String componentId) {
|
||||||
|
final RealmModel realm = getContext().getRealm();
|
||||||
|
if (realm == null) {
|
||||||
|
throw new IllegalArgumentException("Realm not set in the context.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads componentModel from the realm
|
||||||
|
return this.getComponentProvider(clazz, componentId, KeycloakModelUtils.componentModelGetter(realm.getId(), componentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Provider> T getComponentProvider(Class<T> clazz, String componentId, Function<KeycloakSessionFactory, ComponentModel> modelGetter) {
|
||||||
Integer hash = clazz.hashCode() + componentId.hashCode();
|
Integer hash = clazz.hashCode() + componentId.hashCode();
|
||||||
T provider = (T) providers.get(hash);
|
T provider = (T) providers.get(hash);
|
||||||
final RealmModel realm = getContext().getRealm();
|
final RealmModel realm = getContext().getRealm();
|
||||||
|
@ -351,7 +363,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
// allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+
|
// allowed on JDK 1.8, attempt of such a modification throws ConcurrentModificationException with JDK 9+
|
||||||
if (provider == null) {
|
if (provider == null) {
|
||||||
final String realmId = realm.getId();
|
final String realmId = realm.getId();
|
||||||
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz, realmId, componentId, KeycloakModelUtils.componentModelGetter(realmId, componentId));
|
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz, realmId, componentId, modelGetter);
|
||||||
if (providerFactory != null) {
|
if (providerFactory != null) {
|
||||||
provider = providerFactory.create(this);
|
provider = providerFactory.create(this);
|
||||||
providers.put(hash, provider);
|
providers.put(hash, provider);
|
||||||
|
|
|
@ -22,16 +22,17 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
|
import org.keycloak.component.JsonConfigComponentModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -71,75 +72,57 @@ public class ClientPoliciesUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets existing client profiles in a realm as model.
|
* Gets existing client profile of given name with resolved executor providers. It can be profile from realm or from global client profiles.
|
||||||
* not return null.
|
|
||||||
*/
|
*/
|
||||||
static Map<String, ClientProfileModel> getClientProfilesModel(KeycloakSession session, RealmModel realm, List<ClientProfileRepresentation> globalClientProfiles) {
|
static ClientProfile getClientProfileModel(KeycloakSession session, RealmModel realm, ClientProfilesRepresentation profilesRep, List<ClientProfileRepresentation> globalClientProfiles, String profileName) throws ClientPolicyException {
|
||||||
// get existing profiles as json
|
// Obtain profiles from realm
|
||||||
String profilesJson = getClientProfilesJsonString(realm);
|
|
||||||
if (profilesJson == null) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// deserialize existing profiles (json -> representation)
|
|
||||||
ClientProfilesRepresentation profilesRep = null;
|
|
||||||
try {
|
|
||||||
profilesRep = convertClientProfilesJsonToRepresentation(profilesJson);
|
|
||||||
} catch (ClientPolicyException e) {
|
|
||||||
logger.warnv("Failed to serialize client profiles json string. err={0}, errDetail={1}", e.getError(), e.getErrorDetail());
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
if (profilesRep == null || profilesRep.getProfiles() == null) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
List<ClientProfileRepresentation> profiles = profilesRep.getProfiles();
|
List<ClientProfileRepresentation> profiles = profilesRep.getProfiles();
|
||||||
|
if (profiles == null) {
|
||||||
|
profiles = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
// Add global profiles as well
|
// Add global profiles as well
|
||||||
profiles.addAll(globalClientProfiles);
|
profiles.addAll(globalClientProfiles);
|
||||||
|
|
||||||
// constructing existing profiles (representation -> model)
|
ClientProfileRepresentation profileRep = profiles.stream()
|
||||||
Map<String, ClientProfileModel> profileMap = new HashMap<>();
|
.filter(clientProfile -> profileName.equals(clientProfile.getName()))
|
||||||
for (ClientProfileRepresentation profileRep : profilesRep.getProfiles()) {
|
.findFirst().orElse(null);
|
||||||
// ignore profile without name
|
if (profileRep == null) {
|
||||||
if (profileRep.getName() == null) {
|
return null;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientProfileModel profileModel = new ClientProfileModel();
|
ClientProfile profileModel = new ClientProfile();
|
||||||
profileModel.setName(profileRep.getName());
|
profileModel.setName(profileRep.getName());
|
||||||
profileModel.setDescription(profileRep.getDescription());
|
profileModel.setDescription(profileRep.getDescription());
|
||||||
|
|
||||||
if (profileRep.getExecutors() == null) {
|
if (profileRep.getExecutors() == null) {
|
||||||
profileModel.setExecutors(new ArrayList<>());
|
profileModel.setExecutors(new ArrayList<>());
|
||||||
profileMap.put(profileRep.getName(), profileModel);
|
return profileModel;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ClientPolicyExecutorProvider> executors = new ArrayList<>();
|
List<ClientPolicyExecutorProvider> executors = new ArrayList<>();
|
||||||
if (profileRep.getExecutors() != null) {
|
if (profileRep.getExecutors() != null) {
|
||||||
for (ClientPolicyExecutorRepresentation executorRep : profileRep.getExecutors()) {
|
for (ClientPolicyExecutorRepresentation executorRep : profileRep.getExecutors()) {
|
||||||
ClientPolicyExecutorProvider provider = session.getProvider(ClientPolicyExecutorProvider.class, executorRep.getExecutorProviderId());
|
ClientPolicyExecutorProvider provider = getExecutorProvider(session, realm, executorRep.getExecutorProviderId(), executorRep.getConfiguration());
|
||||||
if (provider == null) {
|
|
||||||
// executor's provider not found. just skip it.
|
|
||||||
logger.warnf("Executor with provider ID %s not found", executorRep.getExecutorProviderId());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ClientPolicyExecutorConfigurationRepresentation configuration = (ClientPolicyExecutorConfigurationRepresentation) JsonSerialization.mapper.convertValue(executorRep.getConfiguration(), provider.getExecutorConfigurationClass());
|
|
||||||
provider.setupConfiguration(configuration);
|
|
||||||
executors.add(provider);
|
executors.add(provider);
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
logger.warnv("failed for Configuration Setup during setup provider {0} :: error = {1}", executorRep.getExecutorProviderId(), iae.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
profileModel.setExecutors(executors);
|
profileModel.setExecutors(executors);
|
||||||
|
|
||||||
profileMap.put(profileRep.getName(), profileModel);
|
return profileModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return profileMap;
|
private static ClientPolicyExecutorProvider getExecutorProvider(KeycloakSession session, RealmModel realm, String providerId, JsonNode config) {
|
||||||
|
ComponentModel componentModel = new JsonConfigComponentModel(ClientPolicyExecutorProvider.class, realm.getId(), providerId, config);
|
||||||
|
ClientPolicyExecutorProvider executorProvider = session.getComponentProvider(ClientPolicyExecutorProvider.class, componentModel.getId(), sessionFactory -> componentModel);
|
||||||
|
if (executorProvider == null) {
|
||||||
|
// condition's provider not found. just skip it.
|
||||||
|
throw new IllegalStateException("Executor with provider ID " + providerId + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientPolicyExecutorConfigurationRepresentation configuration = (ClientPolicyExecutorConfigurationRepresentation) JsonSerialization.mapper.convertValue(config, executorProvider.getExecutorConfigurationClass());
|
||||||
|
executorProvider.setupConfiguration(configuration);
|
||||||
|
return executorProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -306,10 +289,10 @@ public class ClientPoliciesUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get existing enabled client policies in a realm as model.
|
* Gets existing enabled client policies in a realm.
|
||||||
* not return null.
|
* not return null.
|
||||||
*/
|
*/
|
||||||
static List<ClientPolicyModel> getEnabledClientPoliciesModel(KeycloakSession session, RealmModel realm) {
|
static List<ClientPolicy> getEnabledClientPolicies(KeycloakSession session, RealmModel realm) {
|
||||||
// get existing profiles as json
|
// get existing profiles as json
|
||||||
String policiesJson = getClientPoliciesJsonString(realm);
|
String policiesJson = getClientPoliciesJsonString(realm);
|
||||||
if (policiesJson == null) {
|
if (policiesJson == null) {
|
||||||
|
@ -329,7 +312,7 @@ public class ClientPoliciesUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructing existing policies (representation -> model)
|
// constructing existing policies (representation -> model)
|
||||||
List<ClientPolicyModel> policyList = new ArrayList<>();
|
List<ClientPolicy> policyList = new ArrayList<>();
|
||||||
for (ClientPolicyRepresentation policyRep: policiesRep.getPolicies()) {
|
for (ClientPolicyRepresentation policyRep: policiesRep.getPolicies()) {
|
||||||
// ignore policy without name
|
// ignore policy without name
|
||||||
if (policyRep.getName() == null) {
|
if (policyRep.getName() == null) {
|
||||||
|
@ -341,7 +324,7 @@ public class ClientPoliciesUtil {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientPolicyModel policyModel = new ClientPolicyModel();
|
ClientPolicy policyModel = new ClientPolicy();
|
||||||
policyModel.setName(policyRep.getName());
|
policyModel.setName(policyRep.getName());
|
||||||
policyModel.setDescription(policyRep.getDescription());
|
policyModel.setDescription(policyRep.getDescription());
|
||||||
policyModel.setEnable(true);
|
policyModel.setEnable(true);
|
||||||
|
@ -349,20 +332,8 @@ public class ClientPoliciesUtil {
|
||||||
List<ClientPolicyConditionProvider> conditions = new ArrayList<>();
|
List<ClientPolicyConditionProvider> conditions = new ArrayList<>();
|
||||||
if (policyRep.getConditions() != null) {
|
if (policyRep.getConditions() != null) {
|
||||||
for (ClientPolicyConditionRepresentation conditionRep : policyRep.getConditions()) {
|
for (ClientPolicyConditionRepresentation conditionRep : policyRep.getConditions()) {
|
||||||
ClientPolicyConditionProvider provider = session.getProvider(ClientPolicyConditionProvider.class, conditionRep.getConditionProviderId());
|
ClientPolicyConditionProvider provider = getConditionProvider(session, realm, conditionRep.getConditionProviderId(), conditionRep.getConfiguration());
|
||||||
if (provider == null) {
|
|
||||||
// condition's provider not found. just skip it.
|
|
||||||
logger.warnf("Condition with provider ID %s not found", conditionRep.getConditionProviderId());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ClientPolicyConditionConfigurationRepresentation configuration = (ClientPolicyConditionConfigurationRepresentation) JsonSerialization.mapper.convertValue(conditionRep.getConfiguration(), provider.getConditionConfigurationClass());
|
|
||||||
provider.setupConfiguration(configuration);
|
|
||||||
conditions.add(provider);
|
conditions.add(provider);
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
logger.warnv("failed for Configuration Setup :: error = {0}", iae.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
policyModel.setConditions(conditions);
|
policyModel.setConditions(conditions);
|
||||||
|
@ -377,6 +348,19 @@ public class ClientPoliciesUtil {
|
||||||
return policyList;
|
return policyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClientPolicyConditionProvider getConditionProvider(KeycloakSession session, RealmModel realm, String providerId, JsonNode config) {
|
||||||
|
ComponentModel componentModel = new JsonConfigComponentModel(ClientPolicyConditionProvider.class, realm.getId(), providerId, config);
|
||||||
|
ClientPolicyConditionProvider conditionProvider = session.getComponentProvider(ClientPolicyConditionProvider.class, componentModel.getId(), sessionFactory -> componentModel);
|
||||||
|
if (conditionProvider == null) {
|
||||||
|
// condition's provider not found. just skip it.
|
||||||
|
throw new IllegalStateException("Condition with provider ID " + providerId + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientPolicyConditionConfigurationRepresentation configuration = (ClientPolicyConditionConfigurationRepresentation) JsonSerialization.mapper.convertValue(config, conditionProvider.getConditionConfigurationClass());
|
||||||
|
conditionProvider.setupConfiguration(configuration);
|
||||||
|
return conditionProvider;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert client policies as representation to json.
|
* convert client policies as representation to json.
|
||||||
* can return null.
|
* can return null.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.services.clientpolicy;
|
package org.keycloak.services.clientpolicy;
|
||||||
|
@ -25,7 +26,7 @@ import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvide
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
||||||
*/
|
*/
|
||||||
public class ClientPolicyModel implements Serializable {
|
class ClientPolicy implements Serializable {
|
||||||
|
|
||||||
protected String name;
|
protected String name;
|
||||||
protected String description;
|
protected String description;
|
|
@ -26,7 +26,7 @@ import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
||||||
*/
|
*/
|
||||||
public class ClientProfileModel implements Serializable {
|
class ClientProfile implements Serializable {
|
||||||
|
|
||||||
protected String name;
|
protected String name;
|
||||||
protected String description;
|
protected String description;
|
|
@ -20,9 +20,7 @@ package org.keycloak.services.clientpolicy;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
@ -68,15 +66,14 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doPolicyOperation(ClientConditionOperation condition, ClientExecutorOperation executor, RealmModel realm) throws ClientPolicyException {
|
private void doPolicyOperation(ClientConditionOperation condition, ClientExecutorOperation executor, RealmModel realm) throws ClientPolicyException {
|
||||||
Map<String, ClientProfileModel> map = ClientPoliciesUtil.getClientProfilesModel(session, realm, globalClientProfilesSupplier.get());
|
List<ClientPolicy> list = ClientPoliciesUtil.getEnabledClientPolicies(session, realm);
|
||||||
List<ClientPolicyModel> list = ClientPoliciesUtil.getEnabledClientPoliciesModel(session, realm).stream().collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
logger.trace("POLICY OPERATION :: No enabled policy.");
|
logger.trace("POLICY OPERATION :: No enabled policy.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ClientPolicyModel policy: list) {
|
for (ClientPolicy policy: list) {
|
||||||
logger.tracev("POLICY OPERATION :: policy name = {0}", policy.getName());
|
logger.tracev("POLICY OPERATION :: policy name = {0}", policy.getName());
|
||||||
if (!isSatisfied(policy, condition)) {
|
if (!isSatisfied(policy, condition)) {
|
||||||
logger.tracev("POLICY UNSATISFIED :: policy name = {0}", policy.getName());
|
logger.tracev("POLICY UNSATISFIED :: policy name = {0}", policy.getName());
|
||||||
|
@ -84,12 +81,12 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.tracev("POLICY APPLIED :: policy name = {0}", policy.getName());
|
logger.tracev("POLICY APPLIED :: policy name = {0}", policy.getName());
|
||||||
execute(policy, executor, map);
|
execute(policy, executor, realm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSatisfied(
|
private boolean isSatisfied(
|
||||||
ClientPolicyModel policy,
|
ClientPolicy policy,
|
||||||
ClientConditionOperation op) throws ClientPolicyException {
|
ClientConditionOperation op) throws ClientPolicyException {
|
||||||
|
|
||||||
if (policy.getConditions() == null || policy.getConditions().isEmpty()) {
|
if (policy.getConditions() == null || policy.getConditions().isEmpty()) {
|
||||||
|
@ -133,16 +130,20 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void execute(
|
private void execute(
|
||||||
ClientPolicyModel policy,
|
ClientPolicy policy,
|
||||||
ClientExecutorOperation op,
|
ClientExecutorOperation op,
|
||||||
Map<String, ClientProfileModel> map) throws ClientPolicyException {
|
RealmModel realm) throws ClientPolicyException {
|
||||||
|
|
||||||
if (policy.getProfiles() == null || policy.getProfiles().isEmpty()) {
|
if (policy.getProfiles() == null || policy.getProfiles().isEmpty()) {
|
||||||
logger.tracev("NO PROFILE :: policy name = {0}", policy.getName());
|
logger.tracev("NO PROFILE :: policy name = {0}", policy.getName());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get profiles from realm
|
||||||
|
ClientProfilesRepresentation clientProfiles = ClientPoliciesUtil.getClientProfilesRepresentation(session, realm);
|
||||||
|
|
||||||
for (String profileName : policy.getProfiles()) {
|
for (String profileName : policy.getProfiles()) {
|
||||||
ClientProfileModel profile = map.get(profileName);
|
ClientProfile profile = ClientPoliciesUtil.getClientProfileModel(session, realm, clientProfiles, globalClientProfilesSupplier.get(), profileName);
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
logger.tracev("PROFILE NOT FOUND :: policy name = {0}, profile name = {1}", policy.getName(), profileName);
|
logger.tracev("PROFILE NOT FOUND :: policy name = {0}, profile name = {1}", policy.getName(), profileName);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -279,7 +279,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void assertExpectedLoadedProfiles(Consumer<ClientProfilesRepresentation> modifiedAssertion) {
|
protected void assertExpectedLoadedProfiles(Consumer<ClientProfilesRepresentation> modifiedAssertion) throws Exception {
|
||||||
|
|
||||||
// retrieve loaded builtin profiles
|
// retrieve loaded builtin profiles
|
||||||
ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
|
ClientProfilesRepresentation actualProfilesRep = getProfilesWithGlobals();
|
||||||
|
@ -768,6 +768,11 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
profilesRep.setProfiles(new ArrayList<>());
|
profilesRep.setProfiles(new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create client profile from existing representation
|
||||||
|
public ClientProfilesBuilder(ClientProfilesRepresentation existingRep) {
|
||||||
|
this.profilesRep = existingRep;
|
||||||
|
}
|
||||||
|
|
||||||
public ClientProfilesBuilder addProfile(ClientProfileRepresentation rep) {
|
public ClientProfilesBuilder addProfile(ClientProfileRepresentation rep) {
|
||||||
profilesRep.getProfiles().add(rep);
|
profilesRep.getProfiles().add(rep);
|
||||||
return this;
|
return this;
|
||||||
|
@ -809,11 +814,14 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientProfileBuilder addExecutor(String providerId, ClientPolicyExecutorConfigurationRepresentation config) {
|
public ClientProfileBuilder addExecutor(String providerId, ClientPolicyExecutorConfigurationRepresentation config) throws Exception {
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
config = new ClientPolicyExecutorConfigurationRepresentation();
|
config = new ClientPolicyExecutorConfigurationRepresentation();
|
||||||
}
|
}
|
||||||
profileRep.getExecutors().add(new ClientPolicyExecutorRepresentation(providerId, config));
|
ClientPolicyExecutorRepresentation executor = new ClientPolicyExecutorRepresentation();
|
||||||
|
executor.setExecutorProviderId(providerId);
|
||||||
|
executor.setConfiguration(JsonSerialization.mapper.readValue(JsonSerialization.mapper.writeValueAsBytes(config), JsonNode.class));
|
||||||
|
profileRep.getExecutors().add(executor);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -930,8 +938,11 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientPolicyBuilder addCondition(String providerId, ClientPolicyConditionConfigurationRepresentation config) {
|
public ClientPolicyBuilder addCondition(String providerId, ClientPolicyConditionConfigurationRepresentation config) throws Exception {
|
||||||
policyRep.getConditions().add(new ClientPolicyConditionRepresentation(providerId, config));
|
ClientPolicyConditionRepresentation condition = new ClientPolicyConditionRepresentation();
|
||||||
|
condition.setConditionProviderId(providerId);
|
||||||
|
condition.setConfiguration(JsonSerialization.mapper.readValue(JsonSerialization.mapper.writeValueAsBytes(config), JsonNode.class));
|
||||||
|
policyRep.getConditions().add(condition);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1278,16 +1289,15 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
assertExpectedAugmenedExecutor(isAugment, PKCEEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
assertExpectedAugmenedExecutor(isAugment, PKCEEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> clientAuthns, boolean isAugment, String clientAuthnsAugment, ClientProfileRepresentation profileRep) {
|
protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> clientAuthns, boolean isAugment, String clientAuthnsAugment, ClientProfileRepresentation profileRep) throws Exception {
|
||||||
assertExpectedAugmenedExecutor(isAugment, SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
assertExpectedAugmenedExecutor(isAugment, SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
||||||
assertNotNull(profileRep);
|
assertNotNull(profileRep);
|
||||||
Map<String, Object> actualExecutorConfig = getConfigOfExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
JsonNode actualExecutorConfig = getConfigOfExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
||||||
assertNotNull(actualExecutorConfig);
|
assertNotNull(actualExecutorConfig);
|
||||||
|
Set<String> actualClientAuthns = new HashSet<>((Collection<String>) JsonSerialization.readValue(actualExecutorConfig.get("client-authns").toString(), List.class));
|
||||||
Set<String> actualClientAuthns = new HashSet<>((Collection<String>) actualExecutorConfig.get("client-authns"));
|
|
||||||
assertEquals(new HashSet<>(clientAuthns), actualClientAuthns);
|
assertEquals(new HashSet<>(clientAuthns), actualClientAuthns);
|
||||||
|
|
||||||
String actualClientAuthnAugment = actualExecutorConfig.get("client-authns-augment").toString();
|
String actualClientAuthnAugment = actualExecutorConfig.get("client-authns-augment").textValue();
|
||||||
assertEquals(clientAuthnsAugment, actualClientAuthnAugment);
|
assertEquals(clientAuthnsAugment, actualClientAuthnAugment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1317,17 +1327,17 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
protected void assertExpectedAugmenedExecutor(boolean isAugment, String providerId, ClientProfileRepresentation profileRep) {
|
protected void assertExpectedAugmenedExecutor(boolean isAugment, String providerId, ClientProfileRepresentation profileRep) {
|
||||||
assertNotNull(profileRep);
|
assertNotNull(profileRep);
|
||||||
Map<String, Object> actualExecutorConfig = getConfigOfExecutor(providerId, profileRep);
|
JsonNode actualExecutorConfig = getConfigOfExecutor(providerId, profileRep);
|
||||||
assertNotNull(actualExecutorConfig);
|
assertNotNull(actualExecutorConfig);
|
||||||
boolean actualIsAugment = actualExecutorConfig.get("is-augment") == null ? false : (Boolean) actualExecutorConfig.get("is-augment");
|
boolean actualIsAugment = actualExecutorConfig.get("is-augment") == null ? false : actualExecutorConfig.get("is-augment").asBoolean();
|
||||||
assertEquals(isAugment, actualIsAugment);
|
assertEquals(isAugment, actualIsAugment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getConfigOfExecutor(String providerId, ClientProfileRepresentation profileRep) {
|
private JsonNode getConfigOfExecutor(String providerId, ClientProfileRepresentation profileRep) {
|
||||||
ClientPolicyExecutorRepresentation executorRep = profileRep.getExecutors().stream()
|
ClientPolicyExecutorRepresentation executorRep = profileRep.getExecutors().stream()
|
||||||
.filter(profileRepp -> providerId.equals(profileRepp.getExecutorProviderId()))
|
.filter(profileRepp -> providerId.equals(profileRepp.getExecutorProviderId()))
|
||||||
.findFirst().orElse(null);
|
.findFirst().orElse(null);
|
||||||
return executorRep == null ? null : executorRep.getConfiguration().getConfigAsMap();
|
return executorRep == null ? null : executorRep.getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assertions about policies
|
// Assertions about policies
|
||||||
|
@ -1455,7 +1465,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertExpectedEmptyConfig(String executorProviderId, ClientProfileRepresentation profileRep) {
|
private void assertExpectedEmptyConfig(String executorProviderId, ClientProfileRepresentation profileRep) {
|
||||||
Map<String, Object> config = getConfigOfExecutor(executorProviderId, profileRep);
|
JsonNode config = getConfigOfExecutor(executorProviderId, profileRep);
|
||||||
Assert.assertTrue("Expected empty configuration for provider " + executorProviderId, config.isEmpty());
|
Assert.assertTrue("Expected empty configuration for provider " + executorProviderId, config.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class ClientPoliciesImportExportTest extends AbstractClientPoliciesTest {
|
||||||
testRealmExportImport();
|
testRealmExportImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testRealmExportImport() throws LifecycleException {
|
private void testRealmExportImport() throws Exception {
|
||||||
testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_EXPORT);
|
testingClient.testing().exportImport().setAction(ExportImportConfig.ACTION_EXPORT);
|
||||||
testingClient.testing().exportImport().setRealmName("test");
|
testingClient.testing().exportImport().setRealmName("test");
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,31 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
||||||
assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-18108
|
||||||
|
@Test
|
||||||
|
public void testTwoProfilesWithDifferentConfigurationOfSameExecutorType() throws Exception {
|
||||||
|
setupPolicyClientIdAndSecretNotAcceptableAuthType(POLICY_NAME);
|
||||||
|
|
||||||
|
// register another profile with "SecureClientAuthEnforceExecutorFactory", but use different configuration of client authenticator.
|
||||||
|
// This profile won't allow JWTClientSecretAuthenticator.PROVIDER_ID
|
||||||
|
String profileName = "UnusedProfile";
|
||||||
|
String json = (new ClientProfilesBuilder(getProfilesWithoutGlobals())).addProfile(
|
||||||
|
(new ClientProfileBuilder()).createProfile(profileName, "Profile with SecureClientAuthEnforceExecutorFactory")
|
||||||
|
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||||
|
createSecureClientAuthEnforceExecutorConfig(Boolean.FALSE,
|
||||||
|
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||||
|
null))
|
||||||
|
.toRepresentation()
|
||||||
|
).toString();
|
||||||
|
updateProfiles(json);
|
||||||
|
|
||||||
|
// Make sure it is still possible to create client with JWTClientSecretAuthenticator. The "UnusedProfile" should not be used as it is not referenced from any client policy
|
||||||
|
String cId = createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {
|
||||||
|
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
|
||||||
|
});
|
||||||
|
assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAdminClientUpdateAcceptableAuthType() throws Exception {
|
public void testAdminClientUpdateAcceptableAuthType() throws Exception {
|
||||||
setupPolicyClientIdAndSecretNotAcceptableAuthType(POLICY_NAME);
|
setupPolicyClientIdAndSecretNotAcceptableAuthType(POLICY_NAME);
|
||||||
|
@ -2051,7 +2076,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPolicyClientIdAndSecretNotAcceptableAuthType(String policyName) throws ClientPolicyException {
|
private void setupPolicyClientIdAndSecretNotAcceptableAuthType(String policyName) throws Exception {
|
||||||
// register profiles
|
// register profiles
|
||||||
String profileName = "MyProfile";
|
String profileName = "MyProfile";
|
||||||
String json = (new ClientProfilesBuilder()).addProfile(
|
String json = (new ClientProfilesBuilder()).addProfile(
|
||||||
|
@ -2075,7 +2100,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
||||||
updatePolicies(json);
|
updatePolicies(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPolicyAuthzCodeFlowUnderMultiPhasePolicy(String policyName) throws ClientPolicyException {
|
private void setupPolicyAuthzCodeFlowUnderMultiPhasePolicy(String policyName) throws Exception {
|
||||||
// register profiles
|
// register profiles
|
||||||
String profileName = "MyProfile";
|
String profileName = "MyProfile";
|
||||||
String json = (new ClientProfilesBuilder()).addProfile(
|
String json = (new ClientProfilesBuilder()).addProfile(
|
||||||
|
|
Loading…
Reference in a new issue