KEYCLOAK-17906 Use auto-configure instead of is-augment. Use default-client-authenticator option in SecureClientAuthenticatorExecutor
This commit is contained in:
parent
23fef24fe1
commit
d3e9e21abd
10 changed files with 210 additions and 132 deletions
|
@ -62,15 +62,15 @@ public class HolderOfKeyEnforcerExecutor implements ClientPolicyExecutorProvider
|
|||
}
|
||||
|
||||
public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
|
||||
@JsonProperty("is-augment")
|
||||
protected Boolean augment;
|
||||
@JsonProperty("auto-configure")
|
||||
protected Boolean autoConfigure;
|
||||
|
||||
public Boolean isAugment() {
|
||||
return augment;
|
||||
public Boolean isAutoConfigure() {
|
||||
return autoConfigure;
|
||||
}
|
||||
|
||||
public void setAugment(Boolean augment) {
|
||||
this.augment = augment;
|
||||
public void setAutoConfigure(Boolean autoConfigure) {
|
||||
this.autoConfigure = autoConfigure;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class HolderOfKeyEnforcerExecutor implements ClientPolicyExecutorProvider
|
|||
case REGISTER:
|
||||
case UPDATE:
|
||||
ClientCRUDContext clientUpdateContext = (ClientCRUDContext)context;
|
||||
augment(clientUpdateContext.getProposedClientRepresentation());
|
||||
autoConfigure(clientUpdateContext.getProposedClientRepresentation());
|
||||
validate(clientUpdateContext.getProposedClientRepresentation());
|
||||
break;
|
||||
case TOKEN_REQUEST:
|
||||
|
@ -112,8 +112,8 @@ public class HolderOfKeyEnforcerExecutor implements ClientPolicyExecutorProvider
|
|||
}
|
||||
}
|
||||
|
||||
private void augment(ClientRepresentation rep) {
|
||||
if (configuration.isAugment()) {
|
||||
private void autoConfigure(ClientRepresentation rep) {
|
||||
if (configuration.isAutoConfigure()) {
|
||||
OIDCAdvancedConfigWrapper.fromClientRepresentation(rep).setUseMtlsHoKToken(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,18 +22,17 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class HolderOfKeyEnforcerExecutorFactory implements ClientPolicyExecutorProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "holder-of-key-enforcer";
|
||||
|
||||
public static final String IS_AUGMENT = "is-augment";
|
||||
public static final String AUTO_CONFIGURE = "auto-configure";
|
||||
|
||||
private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty(
|
||||
IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to use MTLS HoK token", ProviderConfigProperty.BOOLEAN_TYPE, false);
|
||||
private static final ProviderConfigProperty AUTO_CONFIGURE_PROPERTY = new ProviderConfigProperty(
|
||||
AUTO_CONFIGURE, "Auto-configure", "If On, then the during client creation or update, the configuration of the client will be auto-configured to use MTLS HoK token", ProviderConfigProperty.BOOLEAN_TYPE, false);
|
||||
|
||||
@Override
|
||||
public ClientPolicyExecutorProvider create(KeycloakSession session) {
|
||||
|
@ -64,7 +63,7 @@ public class HolderOfKeyEnforcerExecutorFactory implements ClientPolicyExecutorP
|
|||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return new ArrayList<>(Arrays.asList(IS_AUGMENT_PROPERTY));
|
||||
return Collections.singletonList(AUTO_CONFIGURE_PROPERTY);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,15 +71,15 @@ public class PKCEEnforcerExecutor implements ClientPolicyExecutorProvider<PKCEEn
|
|||
}
|
||||
|
||||
public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
|
||||
@JsonProperty("is-augment")
|
||||
protected Boolean augment;
|
||||
@JsonProperty("auto-configure")
|
||||
protected Boolean autoConfigure;
|
||||
|
||||
public Boolean isAugment() {
|
||||
return augment;
|
||||
public Boolean isAutoConfigure() {
|
||||
return autoConfigure;
|
||||
}
|
||||
|
||||
public void setAugment(Boolean augment) {
|
||||
this.augment = augment;
|
||||
public void setAutoConfigure(Boolean autoConfigure) {
|
||||
this.autoConfigure = autoConfigure;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ public class PKCEEnforcerExecutor implements ClientPolicyExecutorProvider<PKCEEn
|
|||
case REGISTER:
|
||||
case UPDATE:
|
||||
ClientCRUDContext clientUpdateContext = (ClientCRUDContext)context;
|
||||
augment(clientUpdateContext.getProposedClientRepresentation());
|
||||
autoConfigure(clientUpdateContext.getProposedClientRepresentation());
|
||||
validate(clientUpdateContext.getProposedClientRepresentation());
|
||||
break;
|
||||
case AUTHORIZATION_REQUEST:
|
||||
|
@ -112,8 +112,8 @@ public class PKCEEnforcerExecutor implements ClientPolicyExecutorProvider<PKCEEn
|
|||
}
|
||||
}
|
||||
|
||||
private void augment(ClientRepresentation rep) {
|
||||
if (configuration.isAugment())
|
||||
private void autoConfigure(ClientRepresentation rep) {
|
||||
if (configuration.isAutoConfigure())
|
||||
OIDCAdvancedConfigWrapper.fromClientRepresentation(rep).setPkceCodeChallengeMethod(OAuth2Constants.PKCE_METHOD_S256);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
package org.keycloak.services.clientpolicy.executor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config.Scope;
|
||||
|
@ -33,10 +32,10 @@ public class PKCEEnforcerExecutorFactory implements ClientPolicyExecutorProvider
|
|||
|
||||
public static final String PROVIDER_ID = "pkce-enforcer";
|
||||
|
||||
public static final String IS_AUGMENT = "is-augment";
|
||||
public static final String AUTO_CONFIGURE = "auto-configure";
|
||||
|
||||
private static final ProviderConfigProperty IS_AUGMENT_PROPERTY = new ProviderConfigProperty(
|
||||
IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to enforce usage of PKCE", ProviderConfigProperty.BOOLEAN_TYPE, false);
|
||||
private static final ProviderConfigProperty AUTO_CONFIGURE_PROPERTY = new ProviderConfigProperty(
|
||||
AUTO_CONFIGURE, "Auto-configure", "If On, then the during client creation or update, the configuration of the client will be auto-configured to enforce usage of PKCE", ProviderConfigProperty.BOOLEAN_TYPE, false);
|
||||
|
||||
@Override
|
||||
public ClientPolicyExecutorProvider create(KeycloakSession session) {
|
||||
|
@ -67,7 +66,7 @@ public class PKCEEnforcerExecutorFactory implements ClientPolicyExecutorProvider
|
|||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return new ArrayList<>(Arrays.asList(IS_AUGMENT_PROPERTY));
|
||||
return Collections.singletonList(AUTO_CONFIGURE_PROPERTY);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ package org.keycloak.services.clientpolicy.executor;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.idm.ClientPolicyExecutorConfigurationRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
@ -34,6 +36,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||
*/
|
||||
public class SecureClientAuthenticatorExecutor implements ClientPolicyExecutorProvider<SecureClientAuthenticatorExecutor.Configuration> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SecureClientAuthenticatorExecutor.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private Configuration configuration;
|
||||
|
||||
|
@ -52,35 +56,25 @@ public class SecureClientAuthenticatorExecutor implements ClientPolicyExecutorPr
|
|||
}
|
||||
|
||||
public static class Configuration extends ClientPolicyExecutorConfigurationRepresentation {
|
||||
@JsonProperty("client-authns")
|
||||
protected List<String> clientAuthns;
|
||||
@JsonProperty("client-authns-augment")
|
||||
protected String clientAuthnsAugment;
|
||||
@JsonProperty("is-augment")
|
||||
protected Boolean augment;
|
||||
@JsonProperty("allowed-client-authenticators")
|
||||
protected List<String> allowedClientAuthenticators;
|
||||
@JsonProperty("default-client-authenticator")
|
||||
protected String defaultClientAuthenticator;
|
||||
|
||||
public List<String> getClientAuthns() {
|
||||
return clientAuthns;
|
||||
public List<String> getAllowedClientAuthenticators() {
|
||||
return allowedClientAuthenticators;
|
||||
}
|
||||
|
||||
public void setClientAuthns(List<String> clientAuthns) {
|
||||
this.clientAuthns = clientAuthns;
|
||||
public void setAllowedClientAuthenticators(List<String> allowedClientAuthenticators) {
|
||||
this.allowedClientAuthenticators = allowedClientAuthenticators;
|
||||
}
|
||||
|
||||
public String getClientAuthnsAugment() {
|
||||
return clientAuthnsAugment;
|
||||
public String getDefaultClientAuthenticator() {
|
||||
return defaultClientAuthenticator;
|
||||
}
|
||||
|
||||
public void setClientAuthnsAugment(String clientAuthnsAugment) {
|
||||
this.clientAuthnsAugment = clientAuthnsAugment;
|
||||
}
|
||||
|
||||
public Boolean isAugment() {
|
||||
return augment;
|
||||
}
|
||||
|
||||
public void setAugment(Boolean augment) {
|
||||
this.augment = augment;
|
||||
public void setDefaultClientAuthenticator(String defaultClientAuthenticator) {
|
||||
this.defaultClientAuthenticator = defaultClientAuthenticator;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,32 +89,56 @@ public class SecureClientAuthenticatorExecutor implements ClientPolicyExecutorPr
|
|||
case REGISTER:
|
||||
case UPDATE:
|
||||
ClientCRUDContext clientUpdateContext = (ClientCRUDContext)context;
|
||||
augment(clientUpdateContext.getProposedClientRepresentation());
|
||||
validate(clientUpdateContext.getProposedClientRepresentation());
|
||||
autoConfigure(clientUpdateContext.getProposedClientRepresentation());
|
||||
validateDuringClientCRUD(clientUpdateContext.getProposedClientRepresentation());
|
||||
break;
|
||||
case TOKEN_REQUEST:
|
||||
case TOKEN_REFRESH:
|
||||
case TOKEN_REVOKE:
|
||||
case TOKEN_INTROSPECT:
|
||||
case LOGOUT_REQUEST:
|
||||
validateDuringClientRequest();
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void augment(ClientRepresentation rep) {
|
||||
if (configuration.isAugment())
|
||||
rep.setClientAuthenticatorType(enforcedClientAuthenticatorType());
|
||||
private void autoConfigure(ClientRepresentation rep) {
|
||||
String defaultClientAuthenticator = configuration.getDefaultClientAuthenticator();
|
||||
if (defaultClientAuthenticator != null) {
|
||||
if (rep.getClientAuthenticatorType() == null) {
|
||||
logger.tracef("Set default client authenticator %s on client %s", defaultClientAuthenticator, rep.getClientId());
|
||||
rep.setClientAuthenticatorType(defaultClientAuthenticator);
|
||||
} else {
|
||||
logger.tracef("Skip setting default client authenticator on client %s. Client authenticator already set to %s", rep.getClientId(), rep.getClientAuthenticatorType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(ClientRepresentation rep) throws ClientPolicyException {
|
||||
verifyClientAuthenticationMethod(rep.getClientAuthenticatorType());
|
||||
}
|
||||
private void validateDuringClientCRUD(ClientRepresentation rep) throws ClientPolicyException {
|
||||
// Allow public clients (There is separate executor to check access type)
|
||||
if (rep.isPublicClient() != null && rep.isPublicClient()) return;
|
||||
|
||||
private String enforcedClientAuthenticatorType() {
|
||||
return configuration.getClientAuthnsAugment();
|
||||
}
|
||||
|
||||
private void verifyClientAuthenticationMethod(String clientAuthenticatorType) throws ClientPolicyException {
|
||||
List<String> acceptableClientAuthn = configuration.getClientAuthns();
|
||||
if (acceptableClientAuthn != null && acceptableClientAuthn.stream().anyMatch(i->i.equals(clientAuthenticatorType))) return;
|
||||
String clientAuthenticatorType = rep.getClientAuthenticatorType();
|
||||
if (isValidClientAuthenticator(clientAuthenticatorType)) return;
|
||||
throw new ClientPolicyException(OAuthErrorException.INVALID_CLIENT_METADATA, "Invalid client metadata: token_endpoint_auth_method");
|
||||
}
|
||||
|
||||
// Validate client authenticator also during client request
|
||||
private void validateDuringClientRequest() throws ClientPolicyException {
|
||||
ClientModel client = session.getContext().getClient();
|
||||
// Allow public clients (There is separate executor to check access type)
|
||||
if (client.isPublicClient()) return;
|
||||
|
||||
if (isValidClientAuthenticator(client.getClientAuthenticatorType())) return;
|
||||
logger.warnf("Client authentication method not allowed for client: %s", client.getClientId());
|
||||
throw new ClientPolicyException(OAuthErrorException.INVALID_REQUEST, "Configured client authentication method not allowed for client");
|
||||
}
|
||||
|
||||
private boolean isValidClientAuthenticator(String clientAuthenticatorType) {
|
||||
List<String> acceptableClientAuthn = configuration.getAllowedClientAuthenticators();
|
||||
return (acceptableClientAuthn != null && acceptableClientAuthn.stream().anyMatch(i->i.equals(clientAuthenticatorType)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -37,9 +37,8 @@ public class SecureClientAuthenticatorExecutorFactory implements ClientPolicyExe
|
|||
|
||||
public static final String PROVIDER_ID = "secure-client-authenticator";
|
||||
|
||||
public static final String IS_AUGMENT = "is-augment";
|
||||
public static final String CLIENT_AUTHNS = "client-authns";
|
||||
public static final String CLIENT_AUTHNS_AUGMENT = "client-authns-augment";
|
||||
public static final String ALLOWED_CLIENT_AUTHENTICATORS = "allowed-client-authenticators";
|
||||
public static final String DEFAULT_CLIENT_AUTHENTICATOR = "default-client-authenticator";
|
||||
|
||||
private List<ProviderConfigProperty> configProperties = new ArrayList<>();
|
||||
|
||||
|
@ -54,25 +53,21 @@ public class SecureClientAuthenticatorExecutorFactory implements ClientPolicyExe
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
ProviderConfigProperty isAugmentProperty = new ProviderConfigProperty(
|
||||
IS_AUGMENT, "Augment Configuration", "If On, then the during client creation or update, the configuration of the client will be augmented to enforce the authentication method to new clients",
|
||||
ProviderConfigProperty.BOOLEAN_TYPE, false);
|
||||
|
||||
List<String> clientAuthProviders = factory.getProviderFactoriesStream(ClientAuthenticator.class)
|
||||
.map(ProviderFactory::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ProviderConfigProperty clientAuthnsProperty = new ProviderConfigProperty(
|
||||
CLIENT_AUTHNS, "Client Authentication Methods", "List of available client authentication methods, which are allowed for clients to use. Other client authentication methods will not be allowed.",
|
||||
ProviderConfigProperty allowedClientAuthenticatorsProperty = new ProviderConfigProperty(
|
||||
ALLOWED_CLIENT_AUTHENTICATORS, "Allowed Client Authenticators", "List of available client authentication methods, which are allowed for clients to use. Other client authentication methods will not be allowed.",
|
||||
ProviderConfigProperty.MULTIVALUED_LIST_TYPE, null);
|
||||
clientAuthnsProperty.setOptions(clientAuthProviders);
|
||||
allowedClientAuthenticatorsProperty.setOptions(clientAuthProviders);
|
||||
|
||||
ProviderConfigProperty clientAuthnsAugment = new ProviderConfigProperty(
|
||||
CLIENT_AUTHNS_AUGMENT, "Augment Client Authentication Method", "If 'Augment Configuration' is ON, then this client authentication method will be set as the authentication method to new clients",
|
||||
ProviderConfigProperty autoConfiguredClientAuthenticator = new ProviderConfigProperty(
|
||||
DEFAULT_CLIENT_AUTHENTICATOR, "Default Client Authenticator", "This client authentication method will be set as the authentication method to new clients during register/update request of the client in case that client does not have explicitly set other client authenticator method. If it is not set, then the client authenticator won't be set on new clients. Regardless the value of this option, client is still always validated to match with any of the allowed client authentication methods",
|
||||
ProviderConfigProperty.LIST_TYPE, JWTClientAuthenticator.PROVIDER_ID);
|
||||
clientAuthnsAugment.setOptions(clientAuthProviders);
|
||||
autoConfiguredClientAuthenticator.setOptions(clientAuthProviders);
|
||||
|
||||
configProperties = Arrays.asList(isAugmentProperty, clientAuthnsProperty, clientAuthnsAugment);
|
||||
configProperties = Arrays.asList(allowedClientAuthenticatorsProperty, autoConfiguredClientAuthenticator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -68,8 +68,8 @@ public class SecureSigningAlgorithmForSignedJwtExecutor implements ClientPolicyE
|
|||
return requireClientAssertion;
|
||||
}
|
||||
|
||||
public void setRequireClientAssertion(Boolean augment) {
|
||||
this.requireClientAssertion = augment;
|
||||
public void setRequireClientAssertion(Boolean requireClientAssertion) {
|
||||
this.requireClientAssertion = requireClientAssertion;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -207,16 +207,14 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
// load profiles
|
||||
ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID),
|
||||
JWTClientAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation();
|
||||
|
||||
ClientProfileRepresentation loadedProfileRepWithoutBuiltinField = (new ClientProfileBuilder()).createProfile("lack-of-builtin-field-test-profile", "Without builtin field that is treated as builtin=false.")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID),
|
||||
JWTClientAuthenticator.PROVIDER_ID))
|
||||
.addExecutor(HolderOfKeyEnforcerExecutorFactory.PROVIDER_ID,
|
||||
|
@ -301,7 +299,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
|
||||
// each executor
|
||||
assertExpectedExecutors(Arrays.asList(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID), actualProfileRep);
|
||||
assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), true, JWTClientAuthenticator.PROVIDER_ID, actualProfileRep);
|
||||
assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), JWTClientAuthenticator.PROVIDER_ID, actualProfileRep);
|
||||
|
||||
// each profile - lack-of-builtin-field-test-profile
|
||||
actualProfileRep = getProfileRepresentation(actualProfilesRep, "lack-of-builtin-field-test-profile", false);
|
||||
|
@ -317,7 +315,7 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
SecureSessionEnforceExecutorFactory.PROVIDER_ID,
|
||||
SecureSigningAlgorithmExecutorFactory.PROVIDER_ID,
|
||||
SecureSigningAlgorithmForSignedJwtExecutorFactory.PROVIDER_ID), actualProfileRep);
|
||||
assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), true, JWTClientAuthenticator.PROVIDER_ID, actualProfileRep);
|
||||
assertExpectedSecureClientAuthEnforceExecutor(Arrays.asList(JWTClientAuthenticator.PROVIDER_ID), JWTClientAuthenticator.PROVIDER_ID, actualProfileRep);
|
||||
assertExpectedHolderOfKeyEnforceExecutor(true, actualProfileRep);
|
||||
assertExpectedSecureRedirectUriEnforceExecutor(actualProfileRep);
|
||||
assertExpectedSecureRequestObjectExecutor(actualProfileRep);
|
||||
|
@ -843,23 +841,22 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
|
||||
// Client Profiles - Executor CRUD Operations
|
||||
|
||||
protected HolderOfKeyEnforcerExecutor.Configuration createHolderOfKeyEnforceExecutorConfig(Boolean isAugment) {
|
||||
protected HolderOfKeyEnforcerExecutor.Configuration createHolderOfKeyEnforceExecutorConfig(Boolean autoConfigure) {
|
||||
HolderOfKeyEnforcerExecutor.Configuration config = new HolderOfKeyEnforcerExecutor.Configuration();
|
||||
config.setAugment(isAugment);
|
||||
config.setAutoConfigure(autoConfigure);
|
||||
return config;
|
||||
}
|
||||
|
||||
protected PKCEEnforcerExecutor.Configuration createPKCEEnforceExecutorConfig(Boolean isAugment) {
|
||||
protected PKCEEnforcerExecutor.Configuration createPKCEEnforceExecutorConfig(Boolean autoConfigure) {
|
||||
PKCEEnforcerExecutor.Configuration config = new PKCEEnforcerExecutor.Configuration();
|
||||
config.setAugment(isAugment);
|
||||
config.setAutoConfigure(autoConfigure);
|
||||
return config;
|
||||
}
|
||||
|
||||
protected SecureClientAuthenticatorExecutor.Configuration createSecureClientAuthEnforceExecutorConfig(Boolean isAugment, List<String> clientAuthns, String clientAuthnsAugment) {
|
||||
protected SecureClientAuthenticatorExecutor.Configuration createSecureClientAuthenticatorExecutorConfig(List<String> allowedClientAuthenticators, String defaultClientAuthenticator) {
|
||||
SecureClientAuthenticatorExecutor.Configuration config = new SecureClientAuthenticatorExecutor.Configuration();
|
||||
config.setAugment(isAugment);
|
||||
config.setClientAuthns(clientAuthns);
|
||||
config.setClientAuthnsAugment(clientAuthnsAugment);
|
||||
config.setAllowedClientAuthenticators(allowedClientAuthenticators);
|
||||
config.setDefaultClientAuthenticator(defaultClientAuthenticator);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -1281,24 +1278,23 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
assertThat(actualExecutorNames, Matchers.containsInAnyOrder(expectedExecutors.toArray()));
|
||||
}
|
||||
|
||||
protected void assertExpectedHolderOfKeyEnforceExecutor(boolean isAugment, ClientProfileRepresentation profileRep) {
|
||||
assertExpectedAugmenedExecutor(isAugment, HolderOfKeyEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
||||
protected void assertExpectedHolderOfKeyEnforceExecutor(boolean autoConfigure, ClientProfileRepresentation profileRep) {
|
||||
assertExpectedAutoConfiguredExecutor(autoConfigure, HolderOfKeyEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
||||
}
|
||||
|
||||
protected void assertExpectedPKCEEnforceExecutor(boolean isAugment, ClientProfileRepresentation profileRep) {
|
||||
assertExpectedAugmenedExecutor(isAugment, PKCEEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
||||
protected void assertExpectedPKCEEnforceExecutor(boolean autoConfigure, ClientProfileRepresentation profileRep) {
|
||||
assertExpectedAutoConfiguredExecutor(autoConfigure, PKCEEnforcerExecutorFactory.PROVIDER_ID, profileRep);
|
||||
}
|
||||
|
||||
protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> clientAuthns, boolean isAugment, String clientAuthnsAugment, ClientProfileRepresentation profileRep) throws Exception {
|
||||
assertExpectedAugmenedExecutor(isAugment, SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
||||
protected void assertExpectedSecureClientAuthEnforceExecutor(List<String> expectedAllowedClientAuthenticators, String expectedAutoConfiguredClientAuthenticator, ClientProfileRepresentation profileRep) throws Exception {
|
||||
assertNotNull(profileRep);
|
||||
JsonNode actualExecutorConfig = getConfigOfExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID, profileRep);
|
||||
assertNotNull(actualExecutorConfig);
|
||||
Set<String> actualClientAuthns = new HashSet<>((Collection<String>) JsonSerialization.readValue(actualExecutorConfig.get("client-authns").toString(), List.class));
|
||||
assertEquals(new HashSet<>(clientAuthns), actualClientAuthns);
|
||||
Set<String> actualClientAuthns = new HashSet<>((Collection<String>) JsonSerialization.readValue(actualExecutorConfig.get(SecureClientAuthenticatorExecutorFactory.ALLOWED_CLIENT_AUTHENTICATORS).toString(), List.class));
|
||||
assertEquals(new HashSet<>(expectedAllowedClientAuthenticators), actualClientAuthns);
|
||||
|
||||
String actualClientAuthnAugment = actualExecutorConfig.get("client-authns-augment").textValue();
|
||||
assertEquals(clientAuthnsAugment, actualClientAuthnAugment);
|
||||
String actualAutoConfiguredClientAuthenticator = actualExecutorConfig.get(SecureClientAuthenticatorExecutorFactory.DEFAULT_CLIENT_AUTHENTICATOR).textValue();
|
||||
assertEquals(expectedAutoConfiguredClientAuthenticator, actualAutoConfiguredClientAuthenticator);
|
||||
}
|
||||
|
||||
protected void assertExpectedSecureRedirectUriEnforceExecutor(ClientProfileRepresentation profileRep) {
|
||||
|
@ -1325,12 +1321,12 @@ public abstract class AbstractClientPoliciesTest extends AbstractKeycloakTest {
|
|||
assertExpectedEmptyConfig(SecureSigningAlgorithmForSignedJwtExecutorFactory.PROVIDER_ID, profileRep);
|
||||
}
|
||||
|
||||
protected void assertExpectedAugmenedExecutor(boolean isAugment, String providerId, ClientProfileRepresentation profileRep) {
|
||||
protected void assertExpectedAutoConfiguredExecutor(boolean expectedAutoConfigure, String providerId, ClientProfileRepresentation profileRep) {
|
||||
assertNotNull(profileRep);
|
||||
JsonNode actualExecutorConfig = getConfigOfExecutor(providerId, profileRep);
|
||||
assertNotNull(actualExecutorConfig);
|
||||
boolean actualIsAugment = actualExecutorConfig.get("is-augment") == null ? false : actualExecutorConfig.get("is-augment").asBoolean();
|
||||
assertEquals(isAugment, actualIsAugment);
|
||||
boolean actualAutoConfigure = actualExecutorConfig.get("auto-configure") == null ? false : actualExecutorConfig.get("auto-configure").asBoolean();
|
||||
assertEquals(expectedAutoConfigure, actualAutoConfigure);
|
||||
}
|
||||
|
||||
private JsonNode getConfigOfExecutor(String providerId, ClientProfileRepresentation profileRep) {
|
||||
|
|
|
@ -161,8 +161,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
|
|||
// load profiles
|
||||
ClientProfileRepresentation duplicatedProfileRep = (new ClientProfileBuilder()).createProfile("builtin-basic-security", "Enforce basic security level")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID),
|
||||
null))
|
||||
.addExecutor(PKCEEnforcerExecutorFactory.PROVIDER_ID,
|
||||
|
@ -173,9 +172,8 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
|
|||
|
||||
ClientProfileRepresentation loadedProfileRep = (new ClientProfileBuilder()).createProfile("ordinal-test-profile", "The profile that can be loaded.")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.TRUE,
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID),
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Collections.singletonList(JWTClientAuthenticator.PROVIDER_ID),
|
||||
JWTClientAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation();
|
||||
|
||||
|
@ -200,7 +198,7 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile("global-default-profile", "Pershyy Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
X509ClientAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation()
|
||||
|
|
|
@ -218,7 +218,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder(getProfilesWithoutGlobals())).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileName, "Profile with SecureClientAuthEnforceExecutorFactory")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
null))
|
||||
.toRepresentation()
|
||||
|
@ -266,12 +266,12 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAdminClientAugmentedAuthType() throws Exception {
|
||||
public void testAdminClientAutoConfiguredClientAuthType() throws Exception {
|
||||
// register profiles
|
||||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
X509ClientAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation()
|
||||
|
@ -288,8 +288,18 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
).toString();
|
||||
updatePolicies(json);
|
||||
|
||||
// Attempt to create client with set authenticator to ClientIdAndSecretAuthenticator. Should fail
|
||||
try {
|
||||
createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {
|
||||
clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID);
|
||||
});
|
||||
fail();
|
||||
} catch (ClientPolicyException e) {
|
||||
assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, e.getMessage());
|
||||
}
|
||||
|
||||
// Attempt to create client without set authenticator. Default authenticator should be set
|
||||
String cId = createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {
|
||||
clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID);
|
||||
});
|
||||
|
||||
assertEquals(X509ClientAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
||||
|
@ -298,17 +308,71 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Pershyy Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
JWTClientAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
updateProfiles(json);
|
||||
|
||||
// It is allowed to update authenticator to one of allowed client authenticators. Default client authenticator is not explicitly set in this case
|
||||
updateClientByAdmin(cId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
|
||||
});
|
||||
assertEquals(JWTClientAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
||||
assertEquals(JWTClientSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cId).getClientAuthenticatorType());
|
||||
}
|
||||
|
||||
// Tests that secured client authenticator is enforced also during client authentication itself (during token request after successful login)
|
||||
@Test
|
||||
public void testSecureClientAuthenticatorDuringLogin() throws Exception {
|
||||
// register profile to NOT allow authentication with ClientIdAndSecret
|
||||
String profileName = "MyProfile";
|
||||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileName, "Primum Profile")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
null))
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
updateProfiles(json);
|
||||
|
||||
// register role policy
|
||||
String roleAlphaName = "sample-client-role-alpha";
|
||||
String roleZetaName = "sample-client-role-zeta";
|
||||
json = (new ClientPoliciesBuilder()).addPolicy(
|
||||
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Den Forste Politikken", Boolean.TRUE)
|
||||
.addCondition(ClientRolesConditionFactory.PROVIDER_ID,
|
||||
createClientRolesConditionConfig(Arrays.asList(roleAlphaName, roleZetaName)))
|
||||
.addProfile(profileName)
|
||||
.toRepresentation()
|
||||
).toString();
|
||||
updatePolicies(json);
|
||||
|
||||
// create a client without client role. It should be successful (policy not applied)
|
||||
String clientId = generateSuffixedName(CLIENT_NAME);
|
||||
String cId = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setSecret("secret");
|
||||
});
|
||||
|
||||
// Login with clientIdAndSecret. It should be successful (policy not applied)
|
||||
successfulLoginAndLogout(clientId, "secret");
|
||||
|
||||
// Add role to the client
|
||||
ClientResource clientResource = ApiUtil.findClientByClientId(adminClient.realm(REALM_NAME), clientId);
|
||||
ClientRepresentation clientRep = clientResource.toRepresentation();
|
||||
Assert.assertEquals(ClientIdAndSecretAuthenticator.PROVIDER_ID, clientRep.getClientAuthenticatorType());
|
||||
clientResource.roles().create(RoleBuilder.create().name(roleAlphaName).build());
|
||||
|
||||
// Not allowed to client authentication with clientIdAndSecret anymore. Client matches policy now
|
||||
oauth.clientId(clientId);
|
||||
oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
OAuthClient.AccessTokenResponse res = oauth.doAccessTokenRequest(code, "secret");
|
||||
assertEquals(400, res.getStatusCode());
|
||||
assertEquals(OAuthErrorException.INVALID_GRANT, res.getError());
|
||||
assertEquals("Configured client authentication method not allowed for client", res.getErrorDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -495,7 +559,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileAlphaName, "Pierwszy Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE, Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID), ClientIdAndSecretAuthenticator.PROVIDER_ID))
|
||||
createSecureClientAuthenticatorExecutorConfig(Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID), ClientIdAndSecretAuthenticator.PROVIDER_ID))
|
||||
.toRepresentation()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileBetaName, "Drugi Profil")
|
||||
.addExecutor(PKCEEnforcerExecutorFactory.PROVIDER_ID,
|
||||
|
@ -525,9 +589,21 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
|
||||
String clientAlphaId = generateSuffixedName("Alpha-App");
|
||||
String clientAlphaSecret = "secretAlpha";
|
||||
|
||||
// Not allowed client authenticator should fail
|
||||
try {
|
||||
createClientByAdmin(generateSuffixedName(CLIENT_NAME), (ClientRepresentation clientRep) -> {
|
||||
clientRep.setSecret(clientAlphaSecret);
|
||||
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
|
||||
});
|
||||
fail();
|
||||
} catch (ClientPolicyException e) {
|
||||
assertEquals(OAuthErrorException.INVALID_CLIENT_METADATA, e.getMessage());
|
||||
}
|
||||
|
||||
String cAlphaId = createClientByAdmin(clientAlphaId, (ClientRepresentation clientRep) -> {
|
||||
clientRep.setSecret(clientAlphaSecret);
|
||||
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
|
||||
clientRep.setClientAuthenticatorType(ClientIdAndSecretAuthenticator.PROVIDER_ID);
|
||||
});
|
||||
RolesResource rolesResourceAlpha = adminClient.realm(REALM_NAME).clients().get(cAlphaId).roles();
|
||||
rolesResourceAlpha.create(RoleBuilder.create().name(roleAlphaName).build());
|
||||
|
@ -659,8 +735,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Prvni Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
null)
|
||||
)
|
||||
|
@ -714,8 +789,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Den Forste Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID),
|
||||
null)
|
||||
)
|
||||
|
@ -754,8 +828,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Il Primo Profilo")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(
|
||||
Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientSecretAuthenticator.PROVIDER_ID),
|
||||
null)
|
||||
)
|
||||
|
@ -2082,7 +2155,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileName, "Primum Profile")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.FALSE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(JWTClientAuthenticator.PROVIDER_ID, JWTClientSecretAuthenticator.PROVIDER_ID, X509ClientAuthenticator.PROVIDER_ID),
|
||||
null))
|
||||
.toRepresentation()
|
||||
|
@ -2106,7 +2179,7 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
|
|||
String json = (new ClientProfilesBuilder()).addProfile(
|
||||
(new ClientProfileBuilder()).createProfile(profileName, "Primul Profil")
|
||||
.addExecutor(SecureClientAuthenticatorExecutorFactory.PROVIDER_ID,
|
||||
createSecureClientAuthEnforceExecutorConfig(Boolean.TRUE,
|
||||
createSecureClientAuthenticatorExecutorConfig(
|
||||
Arrays.asList(ClientIdAndSecretAuthenticator.PROVIDER_ID, JWTClientAuthenticator.PROVIDER_ID),
|
||||
ClientIdAndSecretAuthenticator.PROVIDER_ID))
|
||||
.addExecutor(PKCEEnforcerExecutorFactory.PROVIDER_ID,
|
||||
|
|
Loading…
Reference in a new issue