8000 Client ID Policy Condition by 1-alex98 · Pull Request #25956 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8000
Original file line numberDiff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ Client Role::
Client Domain Name, Host or IP Address::
Applied for specific domain names of client. Or for the cases when the administrator registers/updates client from particular Host or IP Address.

Client Client-IDs::
Evaluates to true if a client's client-id is configured in the condition.

Any Client::
This condition always evaluates to true. It can be used for example to ensure that all clients in the particular realm are FAPI compliant.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2946,4 +2946,7 @@ referralHelp=Specifies if LDAP referrals should be followed or ignored. Please n
authenticatorRefConfig.value.help=Add a custom reference name for the authenticator. When this authenticator is successfully completed during an authentication flow, the Authentication Method Reference (AMR) protocol mapper will use this value to populate the amr claim of the generated tokens. Note, the AMR protocol must be configured for the given client to populate the AMR claim
authenticatorRefConfig.value.label=Authenticator Reference
authenticatorRefConfig.maxAge.help=The max age in seconds that the authenticator reference value is good for in an SSO session. When the Authentication Method Reference (AMR) protocol mapper is used, the AMR will only be considered valid and populated in the token if the authenticator execution was completed within the specified max age.
authenticatorRefConfig.maxAge.label=Authenticator Reference Max Age
authenticatorRefConfig.maxAge.label=Authenticator Reference Max Age
clientIds=The condition checks whether one of the specified client ids matches the client-id of the client to determine whether the policy is applied.
client-ids.label=Client-IDs
client-ids-condition.tooltip=Client-IDs, which will be checked during this condition evaluation. Condition evaluates to true if the clients client-ID is among the configured client-IDs.
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.keycloak.services.clientpolicy.condition;

import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote;

import java.util.List;

public class ClientIdsCondition extends AbstractClientPolicyConditionProvider<ClientIdsCondition.Configuration> {

public ClientIdsCondition(KeycloakSession session) {
super(session);
}

@Override public Class<ClientIdsCondition.Configuration> getConditionConfigurationClass() {
return ClientIdsCondition.Configuration.class;
}

@Override public String getProviderId() {
return ClientIdsConditionFactory.PROVIDER_ID;
}

@Override public ClientPolicyVote applyPolicy(ClientPolicyContext context) throws ClientPolicyException {
if (clientIdMatched(session.getContext().getClient()))
return ClientPolicyVote.YES;
return ClientPolicyVote.NO;
}

private boolean clientIdMatched(ClientModel client) {
if (client == null || client.getClientId() == null)
return false;

List<String> configuredClientIds = configuration.getClientIds();

if (configuredClientIds == null)
return false;

String clientId = client.getClientId();
return configuredClientIds.contains(clientId); // contains results in strict string comparison
}

public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {

protected List<String> clientIds;

public List<String> getClientIds() {
return clientIds;
}

public void setClientIds(List<String> clientIds) {
this.clientIds = clientIds;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.keycloak.services.clientpolicy.condition;

import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;

import java.util.ArrayList;
import java.util.List;

import static org.keycloak.services.clientpolicy.condition.AbstractClientPolicyConditionProviderFactory.addCommonConfigProperties;

public class ClientIdsConditionFactory implements ClientPolicyConditionProviderFactory {

public static final String PROVIDER_ID = "client-ids";
public static final String CLIENT_IDS = "clientIds";

private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();

static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(CLIENT_IDS, PROVIDER_ID + ".label", PROVIDER_ID + "-condition.tooltip",
ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null);
configProperties.add(property);
addCommonConfigProperties(configProperties);
}

@Override public ClientPolicyConditionProvider<ClientIdsCondition.Configuration> create(KeycloakSession session) {
return new ClientIdsCondition(session);
}

@Override public void init(Config.Scope config) {
}

@Override public void postInit(KeycloakSessionFactory factory) {
}

@Override public void close() {
}

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

@Override public String getHelpText() {
return "The condition checks whether one of the specified client ids matches the client-id of the client to determine whether the policy is applied.";
}

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory
org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceHostsConditionFactory
org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceGroupsConditionFactory
org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceRolesConditionFactory
org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory
org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory
org.keycloak.services.clientpolicy.condition.ClientIdsConditionFactory
FE98
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createAnyClientConditionConfig;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientAccessTypeConditionConfig;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientIdConditionConfig;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientScopesConditionConfig;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientUpdateSourceGroupsConditionConfig;
import static org.keycloak.testsuite.util.ClientPoliciesUtil.createClientUpdateSourceHostsConditionConfig;
Expand Down Expand Up @@ -60,6 +61,7 @@
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientIdsConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientScopesConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceGroupsConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientUpdaterSourceHostsConditionFactory;
Expand Down Expand Up @@ -382,6 +384,51 @@ public void testClientScopesCondition() throws Exception {
}
}

/**
* Tests the client id condition. It uses the PKCEEnforcerExecutor to test if the condition works correctly.
* It registers two clients. One has a policy that enforces PKCE on it via it's id. The other client is not PKCE enforced.
* At the end we test if everything behaves as expected.
*/
@Test
public void testClientIdsCondition() throws Exception {
// register profiles
String json = (new ClientProfilesBuilder()).addProfile(
(new ClientProfileBuilder()).createProfile(PROFILE_NAME, "Mein erstes Profil")
.addExecutor(PKCEEnforcerExecutorFactory.PROVIDER_ID,
createPKCEEnforceExecutorConfig(Boolean.TRUE))
.toRepresentation()
).toString();
updateProfiles(json);
String clientIdMatchingCondition = generateSuffixedName(CLIENT_NAME);

// register policies
json = (new ClientPoliciesBuilder()).addPolicy(
(new ClientPolicyBuilder()).createPolicy(POLICY_NAME, "Mein erstes Beleid", Boolean.TRUE)
.addCondition(ClientIdsConditionFactory.PROVIDER_ID,
createClientIdConditionConfig(Arrays.asList(clientIdMatchingCondition, "other-id")))
.addProfile(PROFILE_NAME)
.toRepresentation()
).toString();
updatePolicies(json);

String clientIDNotMatchingCondition = generateSuffixedName("unrelated-client-id");
String clientSecret = "secret";
createClientByAdmin(clientIdMatchingCondition,
(ClientRepresentation clientRep) -> clientRep.setSecret(clientSecret));
createClientByAdmin(clientIDNotMatchingCondition,
(ClientRepresentation clientRep) -> clientRep.setSecret(clientSecret));

try {
successfulLoginAndLogout(clientIDNotMatchingCondition, clientSecret);

failLoginByNotFollowingPKCE(clientIdMatchingCondition);

successfulLoginAndLogoutWithPKCE(clientIdMatchingCondition, clientSecret, TEST_USER_NAME, TEST_USER_PASSWORD);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Test
public void testClientAccessTypeCondition() throws Exception {
// register profiles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyEvent;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeCondition;
import org.keycloak.services.clientpolicy.condition.ClientIdsCondition;
import org.keycloak.services.clientpolicy.condition.ClientRolesCondition;
import org.keycloak.services.clientpolicy.condition.ClientScopesCondition;
import org.keycloak.services.clientpolicy.condition.ClientUpdaterContextCondition;
Expand Down Expand Up @@ -394,6 +395,13 @@ public static ClientScopesCondition.Configuration createClientScopesConditionCon
return config;
}


public static ClientIdsCondition.Configuration createClientIdConditionConfig(List<String> ids) {
final var config = new ClientIdsCondition.Configuration();
config.setClientIds(ids);
return config;
}

public static ClientUpdaterContextCondition.Configuration createClientUpdateContextConditionConfig(List<String> updateClientSource) {
ClientUpdaterContextCondition.Configuration config = new ClientUpdaterContextCondition.Configuration();
config.setUpdateClientSource(updateClientSource);
Expand Down
0