mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Validate client policy condition configuration
Closes #40187 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com> (cherry picked from commit b9033ad9c38bacd16e205866c8891b6df6a210d7)
This commit is contained in:
parent
0074fab5c6
commit
1b3541ed15
@ -19,9 +19,13 @@ package org.keycloak.services.clientpolicy.condition;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
||||
@ -32,4 +36,15 @@ public interface ClientPolicyConditionProviderFactory extends ProviderFactory<Cl
|
||||
default boolean isSupported(Config.Scope config) {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before a Client Policy is created or updated. Allows you to validate the configuration
|
||||
*
|
||||
* @param session
|
||||
* @param realm
|
||||
* @param conditionRepresentation
|
||||
* @throws ClientPolicyException
|
||||
*/
|
||||
default void validateConfiguration(KeycloakSession session, RealmModel realm, ClientPolicyConditionRepresentation conditionRepresentation) throws ClientPolicyException {
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,6 +39,8 @@ import org.keycloak.component.JsonConfigComponentModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
|
||||
@ -49,6 +51,7 @@ import org.keycloak.representations.idm.ClientProfileRepresentation;
|
||||
import org.keycloak.representations.idm.ClientProfilesRepresentation;
|
||||
import org.keycloak.securityprofile.SecurityProfileProvider;
|
||||
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
|
||||
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory;
|
||||
import org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
@ -531,6 +534,9 @@ public class ClientPoliciesUtil {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES) && !isValidCondition(session, condition.getConditionProviderId())) {
|
||||
throw new ClientPolicyException("Policy " + proposedPolicyRep.getName() + " contains invalid condition " + condition.getConditionProviderId());
|
||||
}
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.CLIENT_POLICIES)) {
|
||||
validateConditionConfig(session, condition);
|
||||
}
|
||||
policyRep.getConditions().add(condition);
|
||||
}
|
||||
}
|
||||
@ -597,6 +603,30 @@ public class ClientPoliciesUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void validateConditionConfig(KeycloakSession session, ClientPolicyConditionRepresentation conditionRep) throws ClientPolicyException {
|
||||
ClientPolicyConditionProviderFactory factory = getClientPolicyConditionFactory(session, conditionRep.getConditionProviderId());
|
||||
try {
|
||||
factory.validateConfiguration(session, session.getContext().getRealm(), conditionRep);
|
||||
}
|
||||
catch (ClientPolicyException e) {
|
||||
throw new ClientPolicyException("Invalid " + conditionRep.getConditionProviderId() + " configuration - " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static ClientPolicyConditionProviderFactory getClientPolicyConditionFactory(KeycloakSession session, String providerId) {
|
||||
Class<? extends Provider> provider = session.getProviderClass(ClientPolicyConditionProvider.class.getName());
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("Invalid provider type '" + ClientPolicyConditionProvider.class.getName() + "'");
|
||||
}
|
||||
|
||||
ProviderFactory<? extends Provider> f = session.getKeycloakSessionFactory().getProviderFactory(provider, providerId);
|
||||
if (f == null) {
|
||||
throw new IllegalArgumentException("No such provider '" + providerId + "'");
|
||||
}
|
||||
|
||||
return (ClientPolicyConditionProviderFactory) f;
|
||||
}
|
||||
|
||||
static String getClientProfilesJsonString(RealmModel realm) {
|
||||
return realm.getAttribute(Constants.CLIENT_PROFILES);
|
||||
}
|
||||
|
||||
@ -22,8 +22,13 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
|
||||
@ -73,4 +78,13 @@ public class ClientScopesConditionFactory extends AbstractClientPolicyConditionP
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateConfiguration(KeycloakSession session, RealmModel realm, ClientPolicyConditionRepresentation conditionRepresentation) throws ClientPolicyException {
|
||||
ClientScopesCondition.Configuration configuration = JsonSerialization.mapper.convertValue(conditionRepresentation.getConfiguration(), ClientScopesCondition.Configuration.class);
|
||||
|
||||
if (!realm.getClientScopesStream().map(ClientScopeModel::getName).toList().containsAll(configuration.getScopes())) {
|
||||
throw new ClientPolicyException("Client scopes not allowed: " + configuration.getScopes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
@ -53,9 +54,11 @@ import org.keycloak.models.OAuth2DeviceConfig;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ErrorRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||
@ -71,6 +74,7 @@ import org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceRolesCond
|
||||
import org.keycloak.services.clientpolicy.executor.PKCEEnforcerExecutorFactory;
|
||||
import org.keycloak.services.clientpolicy.executor.SecureClientAuthenticatorExecutorFactory;
|
||||
import org.keycloak.services.clientpolicy.executor.SecureSessionEnforceExecutorFactory;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LogoutConfirmPage;
|
||||
@ -84,6 +88,7 @@ import org.keycloak.testsuite.util.ClientPoliciesUtil.ClientProfileBuilder;
|
||||
import org.keycloak.testsuite.util.ClientPoliciesUtil.ClientProfilesBuilder;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* This test class is for testing a condition of client policies.
|
||||
@ -444,6 +449,34 @@ public class ClientPoliciesConditionTest extends AbstractClientPoliciesTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientScopesConditionValidation() throws Exception {
|
||||
// register profiles
|
||||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Het Eerste Profiel")
|
||||
.addExecutor(PKCEEnforcerExecutorFactory.PROVIDER_ID,
|
||||
createPKCEEnforceExecutorConfig(Boolean.TRUE))
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
updateProfiles(json);
|
||||
|
||||
// register policies
|
||||
json = (new ClientPoliciesBuilder()).addPolicy(
|
||||
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "test", Boolean.TRUE)
|
||||
.addCondition(ClientScopesConditionFactory.PROVIDER_ID,
|
||||
createClientScopesConditionConfig(ClientScopesConditionFactory.ANY, List.of("fake-client-scope")))
|
||||
.addProfile(PROFILE_NAME)
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
|
||||
ClientPoliciesRepresentation clientPolicies = json==null ? null : JsonSerialization.readValue(json, ClientPoliciesRepresentation.class);
|
||||
BadRequestException e = Assert.assertThrows(BadRequestException.class,
|
||||
() -> adminClient.realm(REALM_NAME).clientPoliciesPoliciesResource().updatePolicies(clientPolicies));
|
||||
|
||||
ErrorRepresentation error = e.getResponse().readEntity(ErrorRepresentation.class);
|
||||
Assert.assertEquals("Invalid client-scopes configuration - Client scopes not allowed: [fake-client-scope]", error.getErrorMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClientAttributesCondition() throws Exception {
|
||||
// register profiles
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user