KEYCLOAK-16880 Client Policy - Condition : Negative Logic Support

This commit is contained in:
Takashi Norimatsu 2021-01-21 14:08:20 +09:00 committed by Marek Posolda
parent 41dc94fead
commit c4bf8ecdf0
22 changed files with 223 additions and 332 deletions

View file

@ -48,6 +48,18 @@ public interface ClientPolicyConditionProvider extends Provider {
return ClientPolicyVote.ABSTAIN;
}
/**
* tells whether the result of applyPolicy method is inverted or not as follows.
* ClientPolicyVote.YES is inverted to ClientPolicyVote.NO
* ClientPolicyVote.NO is inverted to ClientPolicyVote.YES
* ClientPolicyVote.ABSTAIN remains unchanged
*
* @return true if the result of applyPolicy method is inverted.
*/
default boolean isNegativeLogic() {
return false;
}
String getName();
String getProviderId();

View file

@ -96,6 +96,13 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
for (ClientPolicyConditionProvider condition : conditions) {
try {
ClientPolicyVote vote = op.run(condition);
if (condition.isNegativeLogic()) {
if (vote == ClientPolicyVote.YES) {
vote = ClientPolicyVote.NO;
} else if (vote == ClientPolicyVote.NO) {
vote = ClientPolicyVote.YES;
}
}
if (vote == ClientPolicyVote.ABSTAIN) {
ClientPolicyLogger.logv(logger, "SKIP : This condition is not evaluated due to its nature. name = {0}, provider id = {1}", condition.getName(), condition.getProviderId());
continue;

View file

@ -88,7 +88,7 @@ public class DefaultClientPolicyProvider implements ClientPolicyProvider {
ClientPolicyConditionProvider provider = session.getProvider(ClientPolicyConditionProvider.class, cm);
providers.add(provider);
session.enlistForClose(provider);
ClientPolicyLogger.logv(logger, "Loaded Condition id = {0}, name = {1}, provider id = {2}", conditionId, cm.getName(), cm.getProviderId());
ClientPolicyLogger.logv(logger, "Loaded Condition id = {0}, name = {1}, provider id = {2}, is negative logic = {3}", conditionId, cm.getName(), cm.getProviderId(), provider.isNegativeLogic());
} catch (Throwable t) {
logger.errorv(t, "Failed to load condition {0}", cm.getId());
}

View file

@ -0,0 +1,47 @@
/*
* 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.services.clientpolicy.condition;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
public abstract class AbstractClientPolicyConditionProvider implements ClientPolicyConditionProvider {
protected final KeycloakSession session;
protected final ComponentModel componentModel;
public AbstractClientPolicyConditionProvider(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
}
@Override
public boolean isNegativeLogic() {
return Boolean.valueOf(componentModel.getConfig().getFirst(AbstractClientPolicyConditionProviderFactory.IS_NEGATIVE_LOGIC)).booleanValue();
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
}
}

View file

@ -0,0 +1,51 @@
/*
* 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.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public abstract class AbstractClientPolicyConditionProviderFactory implements ClientPolicyConditionProviderFactory {
public static final String IS_NEGATIVE_LOGIC = "is-negative-logic";
private static final ProviderConfigProperty IS_NEGATIVE_LOGIC_PROPERTY = new ProviderConfigProperty(IS_NEGATIVE_LOGIC, "clientpolicycondition-is-negative-logic.label", "clientpolicycondition-is-negative-logic.tooltip", ProviderConfigProperty.BOOLEAN_TYPE, false);
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return new ArrayList<>(Arrays.asList(IS_NEGATIVE_LOGIC_PROPERTY));
}
}

View file

@ -24,15 +24,12 @@ import org.keycloak.services.clientpolicy.ClientPolicyContext;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
public class AnyClientCondition implements ClientPolicyConditionProvider {
public class AnyClientCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(AnyClientCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public AnyClientCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
super(session, componentModel);
}
@Override
@ -40,14 +37,4 @@ public class AnyClientCondition implements ClientPolicyConditionProvider {
return ClientPolicyVote.YES;
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
}
}

View file

@ -17,16 +17,10 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.Collections;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class AnyClientConditionFactory implements ClientPolicyConditionProviderFactory {
public class AnyClientConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "anyclient-condition";
@ -35,18 +29,6 @@ public class AnyClientConditionFactory implements ClientPolicyConditionProviderF
return new AnyClientCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -56,10 +38,4 @@ public class AnyClientConditionFactory implements ClientPolicyConditionProviderF
public String getHelpText() {
return "The condition is satisfied by any client on any event.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return Collections.emptyList();
}
}

View file

@ -29,26 +29,12 @@ import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyLogger;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
public class ClientAccessTypeCondition implements ClientPolicyConditionProvider {
public class ClientAccessTypeCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientAccessTypeCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientAccessTypeCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
super(session, componentModel);
}
@Override

View file

@ -17,17 +17,14 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientAccessTypeConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientAccessTypeConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "client-accesstype-condition";
public static final String TYPE = "type";
@ -35,14 +32,10 @@ public class ClientAccessTypeConditionFactory implements ClientPolicyConditionPr
public static final String TYPE_PUBLIC = "public";
public static final String TYPE_BEARERONLY = "bearer-only";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private static final ProviderConfigProperty CLIENTACCESSTYPE_PROPERTY;
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(TYPE, "client-accesstype.label", "client-accesstype.tooltip", ProviderConfigProperty.MULTIVALUED_LIST_TYPE, TYPE_CONFIDENTIAL);
List<String> updateProfileValues = Arrays.asList(TYPE_CONFIDENTIAL, TYPE_PUBLIC, TYPE_BEARERONLY);
property.setOptions(updateProfileValues);
configProperties.add(property);
CLIENTACCESSTYPE_PROPERTY = new ProviderConfigProperty(TYPE, "client-accesstype.label", "client-accesstype.tooltip", ProviderConfigProperty.MULTIVALUED_LIST_TYPE, TYPE_CONFIDENTIAL);
CLIENTACCESSTYPE_PROPERTY.setOptions(Arrays.asList(TYPE_CONFIDENTIAL, TYPE_PUBLIC, TYPE_BEARERONLY));
}
@Override
@ -50,18 +43,6 @@ public class ClientAccessTypeConditionFactory implements ClientPolicyConditionPr
return new ClientAccessTypeCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -74,7 +55,8 @@ public class ClientAccessTypeConditionFactory implements ClientPolicyConditionPr
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTACCESSTYPE_PROPERTY);
return l;
}
}

View file

@ -32,15 +32,12 @@ import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyLogger;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
public class ClientRolesCondition implements ClientPolicyConditionProvider {
public class ClientRolesCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientRolesCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientRolesCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
super(session, componentModel);
}
@Override
@ -91,14 +88,4 @@ public class ClientRolesCondition implements ClientPolicyConditionProvider {
return new HashSet<>(roles);
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
}
}

View file

@ -17,44 +17,23 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientRolesConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientRolesConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientroles-condition";
public static final String ROLES = "roles";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(ROLES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null);
configProperties.add(property);
}
private static final ProviderConfigProperty CLIENTROLES_PROPERTY = new ProviderConfigProperty(
ROLES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, null);
@Override
public ClientPolicyConditionProvider create(KeycloakSession session, ComponentModel model) {
return new ClientRolesCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
@ -69,7 +48,8 @@ public class ClientRolesConditionFactory implements ClientPolicyConditionProvide
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTROLES_PROPERTY);
return l;
}
}

View file

@ -37,16 +37,12 @@ import org.keycloak.services.clientpolicy.ClientPolicyLogger;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
import org.keycloak.services.clientpolicy.TokenRequestContext;
public class ClientScopesCondition implements ClientPolicyConditionProvider {
public class ClientScopesCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientScopesCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientScopesCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
super(session, componentModel);
}
@Override
@ -63,16 +59,6 @@ public class ClientScopesCondition implements ClientPolicyConditionProvider {
}
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
}
private boolean isScopeMatched(AuthenticatedClientSessionModel clientSession) {
if (clientSession == null) return false;
return isScopeMatched(clientSession.getNote(OAuth2Constants.SCOPE), clientSession.getClient());

View file

@ -17,16 +17,13 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientScopesConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientScopesConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientscopes-condition";
public static final String SCOPES = "scopes";
@ -34,33 +31,16 @@ public class ClientScopesConditionFactory implements ClientPolicyConditionProvid
public static final String DEFAULT = "Default";
public static final String OPTIONAL = "Optional";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(SCOPES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "offline_access");
configProperties.add(property);
property = new ProviderConfigProperty(TYPE, "Scope Type", "Default or Optional", ProviderConfigProperty.LIST_TYPE, OPTIONAL);
configProperties.add(property);
}
private static final ProviderConfigProperty CLIENTSCOPES_PROPERTY = new ProviderConfigProperty(
SCOPES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "offline_access");
private static final ProviderConfigProperty CLIENTSCOPETYPE_PROPERTY = new ProviderConfigProperty(
TYPE, "Scope Type", "Default or Optional", ProviderConfigProperty.LIST_TYPE, OPTIONAL);
@Override
public ClientPolicyConditionProvider create(KeycloakSession session, ComponentModel model) {
return new ClientScopesCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -73,7 +53,9 @@ public class ClientScopesConditionFactory implements ClientPolicyConditionProvid
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTSCOPES_PROPERTY);
l.add(CLIENTSCOPETYPE_PROPERTY);
return l;
}
}

View file

@ -29,20 +29,15 @@ import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyLogger;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
import org.keycloak.services.clientpolicy.ClientUpdateContext;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils;
import org.keycloak.util.TokenUtil;
public class ClientUpdateContextCondition implements ClientPolicyConditionProvider {
public class ClientUpdateContextCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientUpdateContextCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientUpdateContextCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
super(session, componentModel);
}
@Override
@ -108,14 +103,4 @@ public class ClientUpdateContextCondition implements ClientPolicyConditionProvid
private boolean isBearerToken(JsonWebToken jwt) {
return jwt != null && TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType());
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
}
}

View file

@ -17,19 +17,15 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProvider;
import org.keycloak.services.clientpolicy.condition.ClientPolicyConditionProviderFactory;
public class ClientUpdateContextConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientUpdateContextConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientupdatecontext-condition";
@ -40,14 +36,12 @@ public class ClientUpdateContextConditionFactory implements ClientPolicyConditio
public static final String BY_INITIAL_ACCESS_TOKEN = "ByInitialAccessToken";
public static final String BY_REGISTRATION_ACCESS_TOKEN = "ByRegistrationAccessToken";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
private static final ProviderConfigProperty CLIENTUPDATESOURCE_PROPERTY;
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(UPDATE_CLIENT_SOURCE, null, null, ProviderConfigProperty.MULTIVALUED_LIST_TYPE, BY_AUTHENTICATED_USER);
List<String> updateProfileValues = Arrays.asList(BY_AUTHENTICATED_USER, BY_ANONYMOUS, BY_INITIAL_ACCESS_TOKEN, BY_REGISTRATION_ACCESS_TOKEN);
property.setOptions(updateProfileValues);
configProperties.add(property);
CLIENTUPDATESOURCE_PROPERTY = new ProviderConfigProperty(
UPDATE_CLIENT_SOURCE, null, null, ProviderConfigProperty.MULTIVALUED_LIST_TYPE, BY_AUTHENTICATED_USER);
CLIENTUPDATESOURCE_PROPERTY.setOptions(
Arrays.asList(BY_AUTHENTICATED_USER, BY_ANONYMOUS, BY_INITIAL_ACCESS_TOKEN, BY_REGISTRATION_ACCESS_TOKEN));
}
@Override
@ -55,18 +49,6 @@ public class ClientUpdateContextConditionFactory implements ClientPolicyConditio
return new ClientUpdateContextCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -79,7 +61,8 @@ public class ClientUpdateContextConditionFactory implements ClientPolicyConditio
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTUPDATESOURCE_PROPERTY);
return l;
}
}

View file

@ -17,7 +17,6 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -25,7 +24,6 @@ import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.OAuthErrorException;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@ -41,26 +39,12 @@ import org.keycloak.services.clientpolicy.ClientUpdateContext;
import org.keycloak.services.clientpolicy.DynamicClientRegisterContext;
import org.keycloak.services.clientpolicy.DynamicClientUpdateContext;
public class ClientUpdateSourceGroupsCondition implements ClientPolicyConditionProvider {
public class ClientUpdateSourceGroupsCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceGroupsCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientUpdateSourceGroupsCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
super(session, componentModel);
}
@Override

View file

@ -17,46 +17,26 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientUpdateSourceGroupsConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientUpdateSourceGroupsConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientupdatesourcegroups-condition";
public static final String GROUPS = "groups";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(GROUPS, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "topGroup");
configProperties.add(property);
}
private static final ProviderConfigProperty CLIENTUPDATEGROUP_PROPERTY = new ProviderConfigProperty(
GROUPS, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "topGroup");
@Override
public ClientPolicyConditionProvider create(KeycloakSession session, ComponentModel model) {
return new ClientUpdateSourceGroupsCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -69,7 +49,8 @@ public class ClientUpdateSourceGroupsConditionFactory implements ClientPolicyCon
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTUPDATEGROUP_PROPERTY);
return l;
}
}

View file

@ -31,26 +31,12 @@ import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.ClientPolicyLogger;
import org.keycloak.services.clientpolicy.ClientPolicyVote;
public class ClientUpdateSourceHostsCondition implements ClientPolicyConditionProvider {
public class ClientUpdateSourceHostsCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceHostsCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientUpdateSourceHostsCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
super(session, componentModel);
}
@Override

View file

@ -17,19 +17,16 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.Arrays;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ConfigurationValidationHelper;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientUpdateSourceHostsConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientUpdateSourceHostsConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientupdatesourcehost-condition";
@ -47,18 +44,6 @@ public class ClientUpdateSourceHostsConditionFactory implements ClientPolicyCond
return new ClientUpdateSourceHostsCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -71,7 +56,10 @@ public class ClientUpdateSourceHostsConditionFactory implements ClientPolicyCond
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return Arrays.asList(TRUSTED_HOSTS_PROPERTY, HOST_SENDING_REGISTRATION_REQUEST_MUST_MATCH_PROPERTY);
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(TRUSTED_HOSTS_PROPERTY);
l.add(HOST_SENDING_REGISTRATION_REQUEST_MUST_MATCH_PROPERTY);
return l;
}
@Override

View file

@ -40,26 +40,12 @@ import org.keycloak.services.clientpolicy.ClientUpdateContext;
import org.keycloak.services.clientpolicy.DynamicClientRegisterContext;
import org.keycloak.services.clientpolicy.DynamicClientUpdateContext;
public class ClientUpdateSourceRolesCondition implements ClientPolicyConditionProvider {
public class ClientUpdateSourceRolesCondition extends AbstractClientPolicyConditionProvider {
private static final Logger logger = Logger.getLogger(ClientUpdateSourceRolesCondition.class);
private final KeycloakSession session;
private final ComponentModel componentModel;
public ClientUpdateSourceRolesCondition(KeycloakSession session, ComponentModel componentModel) {
this.session = session;
this.componentModel = componentModel;
}
@Override
public String getName() {
return componentModel.getName();
}
@Override
public String getProviderId() {
return componentModel.getProviderId();
super(session, componentModel);
}
@Override

View file

@ -17,48 +17,25 @@
package org.keycloak.services.clientpolicy.condition;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.Config.Scope;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
public class ClientUpdateSourceRolesConditionFactory implements ClientPolicyConditionProviderFactory {
public class ClientUpdateSourceRolesConditionFactory extends AbstractClientPolicyConditionProviderFactory {
public static final String PROVIDER_ID = "clientupdatesourceroles-condition";
public static final String ROLES = "roles";
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty(ROLES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "admin");
configProperties.add(property);
}
private static final ProviderConfigProperty CLIENTUPDATEROLE_PROPERTY = new ProviderConfigProperty(
ROLES, PROVIDER_ID + ".label", PROVIDER_ID + ".tooltip", ProviderConfigProperty.MULTIVALUED_STRING_TYPE, "admin");
@Override
public ClientPolicyConditionProvider create(KeycloakSession session, ComponentModel model) {
return new ClientUpdateSourceRolesCondition(session, model);
}
@Override
public void init(Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
@ -67,12 +44,12 @@ public class ClientUpdateSourceRolesConditionFactory implements ClientPolicyCond
@Override
public String getHelpText() {
return "The condition checks the role of the entity who tries to create/update the client to determine whether the policy is applied.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return configProperties;
List<ProviderConfigProperty> l = super.getConfigProperties();
l.add(CLIENTUPDATEROLE_PROPERTY);
return l;
}
}

View file

@ -74,6 +74,7 @@ import org.keycloak.representations.oidc.OIDCClientRepresentation;
import org.keycloak.representations.oidc.TokenMetadataRepresentation;
import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.DefaultClientPolicyProviderFactory;
import org.keycloak.services.clientpolicy.condition.AbstractClientPolicyConditionProviderFactory;
import org.keycloak.services.clientpolicy.condition.AnyClientConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientAccessTypeConditionFactory;
import org.keycloak.services.clientpolicy.condition.ClientUpdateContextConditionFactory;
@ -1279,6 +1280,43 @@ public class ClientPoliciesTest extends AbstractClientPoliciesTest {
}
}
@Test
public void testNegativeLogicCondition() throws ClientRegistrationException, ClientPolicyException {
String policyName = POLICY_NAME;
createPolicy(policyName, DefaultClientPolicyProviderFactory.PROVIDER_ID, null, null, null);
logger.info("... Created Policy : " + policyName);
String conditionName = AnyClientCondition_NAME;
createCondition(conditionName, AnyClientConditionFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> {});
registerCondition(conditionName, policyName);
logger.info("... Registered Condition : " + conditionName);
String executorName = SecureSessionEnforceExecutor_NAME;
createExecutor(executorName, SecureSessionEnforceExecutorFactory.PROVIDER_ID, null, (ComponentRepresentation provider) -> {});
registerExecutor(executorName, policyName);
logger.info("... Registered Executor : " + executorName);
String clientId = generateSuffixedName(CLIENT_NAME);
String clientSecret = "secretBeta";
createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
clientRep.setSecret(clientSecret);
});
try {
failLoginWithoutSecureSessionParameter(clientId, ERR_MSG_MISSING_NONCE);
updateCondition("AnyClientCondition", (ComponentRepresentation provider) -> {
provider.getConfig().putSingle(AbstractClientPolicyConditionProviderFactory.IS_NEGATIVE_LOGIC, Boolean.TRUE.toString());
});
successfulLoginAndLogout(clientId, clientSecret);
updateCondition("AnyClientCondition", (ComponentRepresentation provider) -> {
provider.getConfig().putSingle(AbstractClientPolicyConditionProviderFactory.IS_NEGATIVE_LOGIC, Boolean.FALSE.toString());
});
failLoginWithoutSecureSessionParameter(clientId, ERR_MSG_MISSING_NONCE);
} catch (Exception e) {
fail();
}
}
private void checkMtlsFlow() throws IOException {
// Check login.
OAuthClient.AuthorizationEndpointResponse loginResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);