keycloak-aplcache
Changes
core/src/main/java/org/keycloak/representations/idm/ClientRegistrationTrustedHostRepresentation.java 60(+0 -60)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientRegistrationTrustedHostResource.java 63(+0 -63)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 66(+0 -66)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientRegistrationTrustedHostPredicate.java 58(+0 -58)
services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java 5(+5 -0)
services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java 5(+5 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationHostUtils.java 67(+0 -67)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java 2(+2 -0)
services/src/main/java/org/keycloak/services/clientregistration/policy/ClientRegistrationPolicyManager.java 25(+15 -10)
services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java 8(+7 -1)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientDisabledClientRegistrationPolicy.java 72(+72 -0)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientDisabledClientRegistrationPolicyFactory.java 46(+24 -22)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicy.java 2(+0 -2)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicy.java 64(+27 -37)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicyFactory.java 79(+79 -0)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java 12(+4 -8)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java 17(+8 -9)
services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationTrustedHostResource.java 180(+0 -180)
services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory 4(+3 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractCliTest.java 3(+0 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTester.java 36(+27 -9)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java 8(+4 -4)
Details
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index f6d1640..44419cd 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -23,11 +23,9 @@ import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientInitialAccessModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientRegistrationTrustedHostModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
-import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserLoginFailureModel;
import org.keycloak.models.UserModel;
@@ -35,14 +33,12 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
-import org.keycloak.models.sessions.infinispan.entities.ClientRegistrationTrustedHostEntity;
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import org.keycloak.models.sessions.infinispan.stream.ClientInitialAccessPredicate;
-import org.keycloak.models.sessions.infinispan.stream.ClientRegistrationTrustedHostPredicate;
import org.keycloak.models.sessions.infinispan.stream.ClientSessionPredicate;
import org.keycloak.models.sessions.infinispan.stream.Comparators;
import org.keycloak.models.sessions.infinispan.stream.Mappers;
@@ -540,12 +536,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return entity != null ? new ClientInitialAccessAdapter(session, this, cache, realm, entity) : null;
}
- ClientRegistrationTrustedHostAdapter wrap(RealmModel realm, ClientRegistrationTrustedHostEntity entity) {
- Cache<String, SessionEntity> cache = getCache(false);
- return entity != null ? new ClientRegistrationTrustedHostAdapter(session, this, cache, realm, entity) : null;
- }
-
-
UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
}
@@ -737,62 +727,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return list;
}
- @Override
- public ClientRegistrationTrustedHostModel createClientRegistrationTrustedHostModel(RealmModel realm, String hostName, int count) {
- if (getClientRegistrationTrustedHostModel(realm, hostName) != null) {
- throw new ModelDuplicateException("Client registration already exists for this realm and hostName");
- }
-
- String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
-
- ClientRegistrationTrustedHostEntity entity = new ClientRegistrationTrustedHostEntity();
- entity.setId(id);
- entity.setHostName(hostName);
- entity.setRealm(realm.getId());
- entity.setCount(count);
- entity.setRemainingCount(count);
-
- tx.put(sessionCache, id, entity);
-
- return wrap(realm, entity);
- }
-
- @Override
- public ClientRegistrationTrustedHostModel getClientRegistrationTrustedHostModel(RealmModel realm, String hostName) {
- String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
-
- Cache<String, SessionEntity> cache = getCache(false);
- ClientRegistrationTrustedHostEntity entity = (ClientRegistrationTrustedHostEntity) cache.get(id);
-
- // If created in this transaction
- if (entity == null) {
- entity = (ClientRegistrationTrustedHostEntity) tx.get(cache, id);
- }
-
- return wrap(realm, entity);
- }
-
- @Override
- public void removeClientRegistrationTrustedHostModel(RealmModel realm, String hostName) {
- String id = computeClientRegistrationTrustedHostEntityId(realm, hostName);
- tx.remove(getCache(false), id);
- }
-
- @Override
- public List<ClientRegistrationTrustedHostModel> listClientRegistrationTrustedHosts(RealmModel realm) {
- Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientRegistrationTrustedHostPredicate.create(realm.getId())).iterator();
- List<ClientRegistrationTrustedHostModel> list = new LinkedList<>();
- while (itr.hasNext()) {
- list.add(wrap(realm, (ClientRegistrationTrustedHostEntity) itr.next().getValue()));
- }
- return list;
- }
-
- private static final String CLIENT_REG_TRUSTED_HOST_ID_PREFIX = "reg:::";
-
- private String computeClientRegistrationTrustedHostEntityId(RealmModel realm, String hostName) {
- return CLIENT_REG_TRUSTED_HOST_ID_PREFIX + realm.getId() + ":::" + hostName;
- }
class InfinispanKeycloakTransaction implements KeycloakTransaction {
diff --git a/server-spi/src/main/java/org/keycloak/component/ComponentValidationException.java b/server-spi/src/main/java/org/keycloak/component/ComponentValidationException.java
index d1d707c..09bbc53 100644
--- a/server-spi/src/main/java/org/keycloak/component/ComponentValidationException.java
+++ b/server-spi/src/main/java/org/keycloak/component/ComponentValidationException.java
@@ -21,11 +21,15 @@ package org.keycloak.component;
* @version $Revision: 1 $
*/
public class ComponentValidationException extends RuntimeException {
+
+ private Object[] parameters;
+
public ComponentValidationException() {
}
- public ComponentValidationException(String message) {
+ public ComponentValidationException(String message, Object... parameters) {
super(message);
+ this.parameters = parameters;
}
public ComponentValidationException(String message, Throwable cause) {
@@ -39,4 +43,12 @@ public class ComponentValidationException extends RuntimeException {
public ComponentValidationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Object[] parameters) {
+ this.parameters = parameters;
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java b/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
index 8b9c27c..045258c 100644
--- a/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
+++ b/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java
@@ -131,11 +131,6 @@ public enum ResourceType {
/**
*
*/
- , CLIENT_REGISTRATION_TRUSTED_HOST_MODEL
-
- /**
- *
- */
, CLIENT_ROLE
/**
diff --git a/server-spi/src/main/java/org/keycloak/events/Details.java b/server-spi/src/main/java/org/keycloak/events/Details.java
index 772eaa7..0ef227d 100755
--- a/server-spi/src/main/java/org/keycloak/events/Details.java
+++ b/server-spi/src/main/java/org/keycloak/events/Details.java
@@ -61,4 +61,6 @@ public interface Details {
String SIGNATURE_REQUIRED = "signature_required";
String SIGNATURE_ALGORITHM = "signature_algorithm";
+ String CLIENT_REGISTRATION_POLICY = "client_registration_policy";
+
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserModel.java b/server-spi/src/main/java/org/keycloak/models/UserModel.java
index 6bc80c3..233c8a8 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserModel.java
@@ -57,7 +57,7 @@ public interface UserModel extends RoleMapperModel {
void setEnabled(boolean enabled);
/**
- * Set single value of specified attribute. Remove all other existing values
+ * Set single value of specified attribute. Remove all other existing values of this attribute
*
* @param name
* @param value
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index 27ce108..585558c 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -82,11 +82,6 @@ public interface UserSessionProvider extends Provider {
void removeClientInitialAccessModel(RealmModel realm, String id);
List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm);
- ClientRegistrationTrustedHostModel createClientRegistrationTrustedHostModel(RealmModel realm, String hostName, int count);
- ClientRegistrationTrustedHostModel getClientRegistrationTrustedHostModel(RealmModel realm, String hostName);
- void removeClientRegistrationTrustedHostModel(RealmModel realm, String hostName);
- List<ClientRegistrationTrustedHostModel> listClientRegistrationTrustedHosts(RealmModel realm);
-
void close();
}
diff --git a/server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java b/server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java
index dd9561f..047c1bd 100644
--- a/server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java
+++ b/server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java
@@ -49,7 +49,7 @@ public class ConfigurationValidationHelper {
try {
Integer.parseInt(val);
} catch (NumberFormatException e) {
- throw new ComponentValidationException(label + " should be a number");
+ throw new ComponentValidationException("''{0}'' should be a number", label);
}
}
@@ -68,7 +68,7 @@ public class ConfigurationValidationHelper {
try {
Long.parseLong(val);
} catch (NumberFormatException e) {
- throw new ComponentValidationException(label + " should be a number");
+ throw new ComponentValidationException("''{0}'' should be a number", label);
}
}
@@ -81,7 +81,7 @@ public class ConfigurationValidationHelper {
public ConfigurationValidationHelper checkSingle(String key, String label, boolean required) throws ComponentValidationException {
if (model.getConfig().containsKey(key) && model.getConfig().get(key).size() > 1) {
- throw new ComponentValidationException(label + " should be a single entry");
+ throw new ComponentValidationException("''{0}'' should be a single entry", label);
}
if (required) {
@@ -98,7 +98,7 @@ public class ConfigurationValidationHelper {
public ConfigurationValidationHelper checkRequired(String key, String label) throws ComponentValidationException {
List<String> values = model.getConfig().get(key);
if (values == null) {
- throw new ComponentValidationException(label + " is required");
+ throw new ComponentValidationException("''{0}'' is required", label);
}
return this;
@@ -113,7 +113,7 @@ public class ConfigurationValidationHelper {
String val = model.getConfig().getFirst(key);
if (val != null && !(val.equals("true") || val.equals("false"))) {
- throw new ComponentValidationException(label + " should be 'true' or 'false'");
+ throw new ComponentValidationException("''{0}'' should be ''true'' or ''false''", label);
}
return this;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
index 45f0430..2974b6c 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -171,6 +171,11 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
}
@Override
+ public EventBuilder getEvent() {
+ return event;
+ }
+
+ @Override
public void close() {
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
index e06ad8b..54af568 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AdapterInstallationClientRegistrationProvider.java
@@ -76,6 +76,11 @@ public class AdapterInstallationClientRegistrationProvider implements ClientRegi
}
@Override
+ public EventBuilder getEvent() {
+ return event;
+ }
+
+ @Override
public void close() {
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
index f4660fa..a235934 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
@@ -31,4 +31,6 @@ public interface ClientRegistrationProvider extends Provider {
void setEvent(EventBuilder event);
+ EventBuilder getEvent();
+
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/ClientRegistrationPolicyManager.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/ClientRegistrationPolicyManager.java
index c2e6092..2679fa1 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/ClientRegistrationPolicyManager.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/ClientRegistrationPolicyManager.java
@@ -18,13 +18,16 @@
package org.keycloak.services.clientregistration.policy;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
+import org.keycloak.events.Details;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.services.ServicesLogger;
import org.keycloak.services.clientregistration.ClientRegistrationContext;
import org.keycloak.services.clientregistration.ClientRegistrationProvider;
@@ -36,7 +39,7 @@ public class ClientRegistrationPolicyManager {
private static final Logger logger = Logger.getLogger(ClientRegistrationPolicyManager.class);
public static void triggerBeforeRegister(ClientRegistrationContext context, RegistrationAuth authType) throws ClientRegistrationPolicyException {
- triggerPolicies(context.getSession(), authType, "before register client", (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(context.getSession(), context.getProvider(), authType, "before register client", (ClientRegistrationPolicy policy) -> {
policy.beforeRegister(context);
@@ -46,7 +49,7 @@ public class ClientRegistrationPolicyManager {
public static void triggerAfterRegister(ClientRegistrationContext context, RegistrationAuth authType, ClientModel client) {
try {
- triggerPolicies(context.getSession(), authType, "after register client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(context.getSession(), context.getProvider(), authType, "after register client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
policy.afterRegister(context, client);
@@ -58,7 +61,7 @@ public class ClientRegistrationPolicyManager {
public static void triggerBeforeUpdate(ClientRegistrationContext context, RegistrationAuth authType, ClientModel client) throws ClientRegistrationPolicyException {
- triggerPolicies(context.getSession(), authType, "before update client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(context.getSession(), context.getProvider(), authType, "before update client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
policy.beforeUpdate(context, client);
@@ -67,7 +70,7 @@ public class ClientRegistrationPolicyManager {
public static void triggerAfterUpdate(ClientRegistrationContext context, RegistrationAuth authType, ClientModel client) {
try {
- triggerPolicies(context.getSession(), authType, "after update client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(context.getSession(), context.getProvider(), authType, "after update client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
policy.afterUpdate(context, client);
@@ -78,7 +81,7 @@ public class ClientRegistrationPolicyManager {
}
public static void triggerBeforeView(KeycloakSession session, ClientRegistrationProvider provider, RegistrationAuth authType, ClientModel client) throws ClientRegistrationPolicyException {
- triggerPolicies(session, authType, "before view client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(session, provider, authType, "before view client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
policy.beforeView(provider, client);
@@ -86,7 +89,7 @@ public class ClientRegistrationPolicyManager {
}
public static void triggerBeforeRemove(KeycloakSession session, ClientRegistrationProvider provider, RegistrationAuth authType, ClientModel client) throws ClientRegistrationPolicyException {
- triggerPolicies(session, authType, "before delete client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
+ triggerPolicies(session, provider, authType, "before delete client " + client.getClientId(), (ClientRegistrationPolicy policy) -> {
policy.beforeDelete(provider, client);
@@ -95,7 +98,7 @@ public class ClientRegistrationPolicyManager {
- private static void triggerPolicies(KeycloakSession session, RegistrationAuth authType, String opDescription, ClientRegOperation op) throws ClientRegistrationPolicyException {
+ private static void triggerPolicies(KeycloakSession session, ClientRegistrationProvider provider, RegistrationAuth authType, String opDescription, ClientRegOperation op) throws ClientRegistrationPolicyException {
RealmModel realm = session.getContext().getRealm();
String policyTypeKey = getComponentTypeKey(authType);
@@ -113,14 +116,16 @@ public class ClientRegistrationPolicyManager {
throw new ClientRegistrationPolicyException("Policy of type '" + policyModel.getProviderId() + "' not found");
}
- // TODO: trace
- logger.infof("Running policy '%s' %s", policyModel.getName(), opDescription);
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Running policy '%s' %s", policyModel.getName(), opDescription);
+ }
try {
op.run(policy);
} catch (ClientRegistrationPolicyException crpe) {
+ provider.getEvent().detail(Details.CLIENT_REGISTRATION_POLICY, policyModel.getName());
crpe.setPolicyModel(policyModel);
- logger.warnf("Operation '%s' rejected. %s", opDescription, crpe.getMessage());
+ ServicesLogger.LOGGER.clientRegistrationRequestRejected(opDescription, crpe.getMessage());
throw crpe;
}
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
index a3d074f..6fefa93 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
@@ -33,6 +33,8 @@ import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper;
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
import org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ConsentRequiredClientRegistrationPolicyFactory;
+import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicy;
+import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ScopeClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrationPolicyFactory;
@@ -87,9 +89,13 @@ public class DefaultClientRegistrationPolicies {
ComponentModel consentRequiredModel = createModelInstance("Consent Required", realm, ConsentRequiredClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
realm.addComponentModel(consentRequiredModel);
- ComponentModel scopeModel =createModelInstance("Full Scope Disabled", realm, ScopeClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
+ ComponentModel scopeModel = createModelInstance("Full Scope Disabled", realm, ScopeClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
realm.addComponentModel(scopeModel);
+ ComponentModel maxClientsModel = createModelInstance("Max Clients Limit", realm, MaxClientsClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
+ maxClientsModel.put(MaxClientsClientRegistrationPolicyFactory.MAX_CLIENTS, MaxClientsClientRegistrationPolicyFactory.DEFAULT_MAX_CLIENTS);
+ realm.addComponentModel(maxClientsModel);
+
addGenericPolicies(realm, policyTypeKey);
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientDisabledClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientDisabledClientRegistrationPolicy.java
new file mode 100644
index 0000000..0fd53a3
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientDisabledClientRegistrationPolicy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 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.clientregistration.policy.impl;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.services.clientregistration.ClientRegistrationContext;
+import org.keycloak.services.clientregistration.ClientRegistrationProvider;
+import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
+import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyException;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientDisabledClientRegistrationPolicy implements ClientRegistrationPolicy {
+
+ @Override
+ public void beforeRegister(ClientRegistrationContext context) throws ClientRegistrationPolicyException {
+
+ }
+
+ @Override
+ public void afterRegister(ClientRegistrationContext context, ClientModel clientModel) {
+ clientModel.setEnabled(false);
+ }
+
+ @Override
+ public void beforeUpdate(ClientRegistrationContext context, ClientModel clientModel) throws ClientRegistrationPolicyException {
+ if (context.getClient().isEnabled() == null) {
+ return;
+ }
+ if (clientModel == null) {
+ return;
+ }
+
+ boolean isEnabled = clientModel.isEnabled();
+ boolean newEnabled = context.getClient().isEnabled();
+
+ if (!isEnabled && newEnabled) {
+ throw new ClientRegistrationPolicyException("Not permitted to enable client");
+ }
+ }
+
+ @Override
+ public void afterUpdate(ClientRegistrationContext context, ClientModel clientModel) {
+
+ }
+
+ @Override
+ public void beforeView(ClientRegistrationProvider provider, ClientModel clientModel) throws ClientRegistrationPolicyException {
+
+ }
+
+ @Override
+ public void beforeDelete(ClientRegistrationProvider provider, ClientModel clientModel) throws ClientRegistrationPolicyException {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicy.java
index 41d7ba7..508dad4 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicy.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientTemplatesClientRegistrationPolicy.java
@@ -35,8 +35,6 @@ import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyE
*/
public class ClientTemplatesClientRegistrationPolicy implements ClientRegistrationPolicy {
- private static final Logger logger = Logger.getLogger(ClientTemplatesClientRegistrationPolicy.class);
-
private final KeycloakSession session;
private final ComponentModel componentModel;
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicyFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicyFactory.java
new file mode 100644
index 0000000..00ebe05
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/MaxClientsClientRegistrationPolicyFactory.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2016 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.clientregistration.policy.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.keycloak.component.ComponentModel;
+import org.keycloak.component.ComponentValidationException;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.provider.ConfigurationValidationHelper;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.services.clientregistration.policy.AbstractClientRegistrationPolicyFactory;
+import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MaxClientsClientRegistrationPolicyFactory extends AbstractClientRegistrationPolicyFactory {
+
+ public static final String MAX_CLIENTS = "max-clients";
+ public static final ProviderConfigProperty MAX_CLIENTS_PROPERTY = new ProviderConfigProperty();
+
+ public static final int DEFAULT_MAX_CLIENTS = 200;
+
+ private static List<ProviderConfigProperty> configProperties = new LinkedList<>();
+
+ static {
+ MAX_CLIENTS_PROPERTY.setName(MAX_CLIENTS);
+ MAX_CLIENTS_PROPERTY.setLabel("max-clients.label");
+ MAX_CLIENTS_PROPERTY.setHelpText("max-clients.tooltip");
+ MAX_CLIENTS_PROPERTY.setType(ProviderConfigProperty.STRING_TYPE);
+ MAX_CLIENTS_PROPERTY.setDefaultValue(String.valueOf(DEFAULT_MAX_CLIENTS));
+ configProperties.add(MAX_CLIENTS_PROPERTY);
+ }
+
+ public static final String PROVIDER_ID = "max-clients";
+
+ @Override
+ public ClientRegistrationPolicy create(KeycloakSession session, ComponentModel model) {
+ return new MaxClientsClientRegistrationPolicy(session, model);
+ }
+
+ @Override
+ public String getHelpText() {
+ return "When present, then it won't be allowed to register new client if count of existing clients in realm is same or bigger than configured limit";
+ }
+
+ @Override
+ public List<ProviderConfigProperty> getConfigProperties() {
+ return configProperties;
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+
+ @Override
+ public void validateConfiguration(KeycloakSession session, ComponentModel config) throws ComponentValidationException {
+ ConfigurationValidationHelper.check(config)
+ .checkInt(MAX_CLIENTS_PROPERTY, true);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
index 4bfdf32..f273def 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
@@ -18,17 +18,15 @@
package org.keycloak.services.clientregistration.policy.impl;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.services.ServicesLogger;
import org.keycloak.services.clientregistration.ClientRegistrationContext;
import org.keycloak.services.clientregistration.ClientRegistrationProvider;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
@@ -66,7 +64,7 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
String mapperType = mapper.getProtocolMapper();
if (!allowedMapperProviders.contains(mapperType)) {
- logger.warnf("ProtocolMapper '%s' of type '%s' not allowed", mapper.getName(), mapperType);
+ ServicesLogger.LOGGER.clientRegistrationMapperNotAllowed(mapper.getName(), mapperType);
throw new ClientRegistrationPolicyException("ProtocolMapper type not allowed");
}
}
@@ -75,8 +73,7 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
protected void enableConsentRequiredForAll(ClientModel clientModel) {
if (isConsentRequiredForMappers()) {
- // TODO: Debug
- logger.infof("Enable consentRequired for all protocol mappers of client %s", clientModel.getClientId());
+ logger.debugf("Enable consentRequired for all protocol mappers of client %s", clientModel.getClientId());
Set<ProtocolMapperModel> mappers = clientModel.getProtocolMappers();
@@ -105,8 +102,7 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
}).forEach((ProtocolMapperModel mapperToRemove) -> {
- // TODO: debug
- logger.infof("Removing builtin mapper '%s' of type '%s' as type is not permitted", mapperToRemove.getName(), mapperToRemove.getProtocolMapper());
+ logger.debugf("Removing builtin mapper '%s' of type '%s' as type is not permitted", mapperToRemove.getName(), mapperToRemove.getProtocolMapper());
clientModel.removeProtocolMapper(mapperToRemove);
});
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java
index d5359d8..84f2662 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/TrustedHostClientRegistrationPolicy.java
@@ -32,6 +32,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.oidc.utils.PairwiseSubMapperUtils;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ServicesLogger;
import org.keycloak.services.clientregistration.ClientRegistrationContext;
import org.keycloak.services.clientregistration.ClientRegistrationProvider;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
@@ -95,8 +96,7 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
String hostAddress = session.getContext().getConnection().getRemoteAddr();
- // TODO: debug
- logger.infof("Verifying remote host : %s", hostAddress);
+ logger.debugf("Verifying remote host : %s", hostAddress);
List<String> trustedHosts = getTrustedHosts();
List<String> trustedDomains = getTrustedDomains();
@@ -113,7 +113,7 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
return;
}
- logger.warnf("Failed to verify remote host : %s", hostAddress);
+ ServicesLogger.LOGGER.failedToVerifyRemoteHost(hostAddress);
throw new ClientRegistrationPolicyException("Host not trusted.");
}
@@ -154,7 +154,7 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
return confHostName;
}
} catch (UnknownHostException uhe) {
- logger.warnf("Unknown host from realm configuration: %s", confHostName);
+ logger.debugf(uhe, "Unknown host from realm configuration: %s", confHostName);
}
}
@@ -167,8 +167,7 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
try {
String hostname = InetAddress.getByName(hostAddress).getHostName();
- // TODO: Debug
- logger.infof("Trying verify request from address '%s' of host '%s' by domains", hostAddress, hostname);
+ logger.debugf("Trying verify request from address '%s' of host '%s' by domains", hostAddress, hostname);
for (String confDomain : trustedDomains) {
if (hostname.endsWith(confDomain)) {
@@ -177,7 +176,7 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
}
}
} catch (UnknownHostException uhe) {
- logger.warnf("Request of address '%s' came from unknown host. Skip verification by domains", hostAddress);
+ logger.debugf(uhe, "Request of address '%s' came from unknown host. Skip verification by domains", hostAddress);
}
}
@@ -237,11 +236,11 @@ public class TrustedHostClientRegistrationPolicy implements ClientRegistrationPo
}
}
} catch (MalformedURLException mfe) {
- logger.warnf("URL '%s' is malformed", url);
+ logger.debugf(mfe, "URL '%s' is malformed", url);
throw new ClientRegistrationPolicyException("URL is malformed");
}
- logger.warnf("URL '%s' doesn't match any trustedHost or trustedDomain", url);
+ ServicesLogger.LOGGER.urlDoesntMatch(url);
throw new ClientRegistrationPolicyException("URL doesn't match any trusted host or trusted domain");
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
index 39f152c..5a0e817 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
@@ -28,6 +28,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorResponseException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -43,9 +44,14 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+
+import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -116,7 +122,7 @@ public class ComponentResource {
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId()).representation(rep).success();
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
} catch (ComponentValidationException e) {
- return ErrorResponse.error(e.getMessage(), Response.Status.BAD_REQUEST);
+ return localizedErrorResponse(e);
}
}
@@ -147,7 +153,7 @@ public class ComponentResource {
realm.updateComponent(model);
return Response.noContent().build();
} catch (ComponentValidationException e) {
- return ErrorResponse.error(e.getMessage(), Response.Status.BAD_REQUEST);
+ return localizedErrorResponse(e);
}
}
@@ -164,6 +170,22 @@ public class ComponentResource {
}
+ private Response localizedErrorResponse(ComponentValidationException cve) {
+ Properties messages = AdminRoot.getMessages(session, realm, "admin-messages", auth.getAuth().getToken().getLocale());
+
+ Object[] localizedParameters = cve.getParameters()==null ? null : Arrays.asList(cve.getParameters()).stream().map((Object parameter) -> {
+
+ if (parameter instanceof String) {
+ String paramStr = (String) parameter;
+ return messages.getProperty(paramStr, paramStr);
+ } else {
+ return parameter;
+ }
+
+ }).toArray();
+ String message = MessageFormat.format(messages.getProperty(cve.getMessage(), cve.getMessage()), localizedParameters);
+ return ErrorResponse.error(message, Response.Status.BAD_REQUEST);
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/ServicesLogger.java b/services/src/main/java/org/keycloak/services/ServicesLogger.java
index b0e0cca..bd239a6 100644
--- a/services/src/main/java/org/keycloak/services/ServicesLogger.java
+++ b/services/src/main/java/org/keycloak/services/ServicesLogger.java
@@ -434,8 +434,21 @@ public interface ServicesLogger extends BasicLogger {
@Message(id=97, value="Invalid request")
void invalidRequest(@Cause Throwable t);
- @LogMessage(level = ERROR)
- @Message(id=98, value="Failed to get redirect uris from sector identifier URI: %s")
- void failedToGetRedirectUrisFromSectorIdentifierUri(@Cause Throwable t, String sectorIdentifierUri);
+
+ @LogMessage(level = WARN)
+ @Message(id=99, value="Operation '%s' rejected. %s")
+ void clientRegistrationRequestRejected(String opDescription, String detailedMessage);
+
+ @LogMessage(level = WARN)
+ @Message(id=100, value= "ProtocolMapper '%s' of type '%s' not allowed")
+ void clientRegistrationMapperNotAllowed(String mapperName, String mapperType);
+
+ @LogMessage(level = WARN)
+ @Message(id=101, value= "Failed to verify remote host : %s")
+ void failedToVerifyRemoteHost(String hostname);
+
+ @LogMessage(level = WARN)
+ @Message(id=102, value= "URL '%s' doesn't match any trustedHost or trustedDomain")
+ void urlDoesntMatch(String url);
}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
index 652501a..78a8cc9 100644
--- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
@@ -19,4 +19,6 @@ org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrati
org.keycloak.services.clientregistration.policy.impl.ConsentRequiredClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory
-org.keycloak.services.clientregistration.policy.impl.ScopeClientRegistrationPolicyFactory
\ No newline at end of file
+org.keycloak.services.clientregistration.policy.impl.ScopeClientRegistrationPolicyFactory
+org.keycloak.services.clientregistration.policy.impl.ClientDisabledClientRegistrationPolicyFactory
+org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
index ab934fe..dba19dd 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ComponentsTest.java
@@ -65,7 +65,7 @@ public class ComponentsTest extends AbstractAdminTest {
try {
createComponent(rep);
} catch (WebApplicationException e) {
- assertErrror(e.getResponse(), "Required is required");
+ assertErrror(e.getResponse(), "'Required' is required");
}
rep.getConfig().putSingle("required", "Required");
@@ -75,7 +75,7 @@ public class ComponentsTest extends AbstractAdminTest {
try {
createComponent(rep);
} catch (WebApplicationException e) {
- assertErrror(e.getResponse(), "Number should be a number");
+ assertErrror(e.getResponse(), "'Number' should be a number");
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractCliTest.java
index 01ef404..e44a348 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractCliTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractCliTest.java
@@ -3,7 +3,6 @@ package org.keycloak.testsuite.cli.registration;
import org.junit.Assert;
import org.junit.Before;
import org.keycloak.admin.client.resource.ClientInitialAccessResource;
-import org.keycloak.admin.client.resource.ClientRegistrationTrustedHostResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator;
import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
@@ -12,7 +11,6 @@ import org.keycloak.client.registration.cli.config.FileConfigHandler;
import org.keycloak.client.registration.cli.config.RealmConfigData;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
-import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -27,7 +25,6 @@ import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.util.JsonSerialization;
-import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
index 2a4ef8b..d5055fa 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
@@ -51,7 +51,9 @@ import org.keycloak.services.clientregistration.RegistrationAccessToken;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager;
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
+import org.keycloak.services.clientregistration.policy.impl.ClientDisabledClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory;
+import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrationPolicyFactory;
import org.keycloak.testsuite.Assert;
@@ -270,6 +272,61 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
@Test
+ public void testClientDisabledPolicy() throws Exception {
+ setTrustedHost("localhost", getPolicyAnon());
+
+ // Assert new client is enabled
+ OIDCClientRepresentation client = create();
+ String clientId = client.getClientId();
+ ClientRepresentation clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation();
+ Assert.assertTrue(clientRep.isEnabled());
+
+ // Add client-disabled policy
+ ComponentRepresentation rep = new ComponentRepresentation();
+ rep.setName("Clients disabled");
+ rep.setParentId(REALM_NAME);
+ rep.setProviderId(ClientDisabledClientRegistrationPolicyFactory.PROVIDER_ID);
+ rep.setProviderType(ClientRegistrationPolicy.class.getName());
+ rep.setSubType(getPolicyAnon());
+ realmResource().components().add(rep);
+
+ // Assert new client is disabled
+ client = create();
+ clientId = client.getClientId();
+ clientRep = ApiUtil.findClientByClientId(realmResource(), clientId).toRepresentation();
+ Assert.assertFalse(clientRep.isEnabled());
+
+ // Try enable client. Should fail
+ clientRep.setEnabled(true);
+ assertFail(ClientRegOp.UPDATE, clientRep, 403, "Not permitted to enable client");
+
+ // Try update disabled client. Should pass
+ clientRep.setEnabled(false);
+ reg.update(clientRep);
+ }
+
+
+ @Test
+ public void testMaxClientsPolicy() throws Exception {
+ setTrustedHost("localhost", getPolicyAnon());
+
+ int clientsCount = realmResource().clients().findAll().size();
+ int newClientsLimit = clientsCount + 1;
+
+ // Allow to create one more client to current limit
+ ComponentRepresentation maxClientsPolicyRep = findPolicyByProviderAndAuth(MaxClientsClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon());
+ maxClientsPolicyRep.getConfig().putSingle(MaxClientsClientRegistrationPolicyFactory.MAX_CLIENTS, String.valueOf(newClientsLimit));
+ realmResource().components().component(maxClientsPolicyRep.getId()).update(maxClientsPolicyRep);
+
+ // I can register one new client
+ OIDCClientRepresentation client = create();
+
+ // I can't register more clients
+ assertOidcFail(ClientRegOp.CREATE, createRepOidc(), 403, "It's allowed to have max " + newClientsLimit + " clients per realm");
+ }
+
+
+ @Test
public void testProviders() throws Exception {
List<ComponentTypeRepresentation> reps = realmResource().clientRegistrationPolicy().getProviders();
Map<String, ComponentTypeRepresentation> providersMap = reps.stream().collect(Collectors.toMap((ComponentTypeRepresentation rep) -> {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
index f927851..2912c3c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
@@ -31,7 +31,6 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
-import org.keycloak.representations.idm.ClientRegistrationTrustedHostRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.oidc.OIDCClientRepresentation;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
index 65cda1a..2dc3506 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java
@@ -144,7 +144,7 @@ public class RsaKeyProviderTest extends AbstractKeycloakTest {
rep.getConfig().putSingle(Attributes.PRIORITY_KEY, "invalid");
Response response = adminClient.realm("test").components().add(rep);
- assertErrror(response, "Priority should be a number");
+ assertErrror(response, "'Priority' should be a number");
}
@Test
@@ -156,7 +156,7 @@ public class RsaKeyProviderTest extends AbstractKeycloakTest {
rep.getConfig().putSingle(Attributes.ENABLED_KEY, "invalid");
Response response = adminClient.realm("test").components().add(rep);
- assertErrror(response, "Enabled should be 'true' or 'false'");
+ assertErrror(response, "'Enabled' should be 'true' or 'false'");
}
@Test
@@ -168,7 +168,7 @@ public class RsaKeyProviderTest extends AbstractKeycloakTest {
rep.getConfig().putSingle(Attributes.ACTIVE_KEY, "invalid");
Response response = adminClient.realm("test").components().add(rep);
- assertErrror(response, "Active should be 'true' or 'false'");
+ assertErrror(response, "'Active' should be 'true' or 'false'");
}
@Test
@@ -178,7 +178,7 @@ public class RsaKeyProviderTest extends AbstractKeycloakTest {
ComponentRepresentation rep = createRep("invalid", RsaKeyProviderFactory.ID);
Response response = adminClient.realm("test").components().add(rep);
- assertErrror(response, "Private RSA Key is required");
+ assertErrror(response, "'Private RSA Key' is required");
rep.getConfig().putSingle(Attributes.PRIVATE_KEY_KEY, "nonsense");
response = adminClient.realm("test").components().add(rep);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
index 5e5547f..74e0046 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
@@ -316,15 +316,6 @@ public class AdminEventPaths {
return uri.toString();
}
- // CLIENT REGISTRATION TRUSTED HOSTS
-
- public static String clientRegistrationTrustedHostPath(String hostName) {
- URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientRegistrationTrustedHost")
- .path(ClientInitialAccessResource.class, "delete")
- .build(hostName);
- return uri.toString();
- }
-
// GROUPS
public static String groupsPath() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
index 91aa2a6..5605c9d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
@@ -67,4 +67,4 @@ log4j.logger.org.apache.directory.server.core=warn
# log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace
# log4j.logger.org.keycloak.keys.infinispan=trace
-log4j.logger.org.keycloak.services.clientregistration.policy=trace
\ No newline at end of file
+log4j.logger.org.keycloak.services.clientregistration.policy=debug
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index ef8ef74..37055f2 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -543,6 +543,7 @@ initial-access-token.confirm.text=Please copy and paste the initial access token
no-initial-access-available=No Initial Access Tokens available
client-reg-policies=Client Registration Policies
+client-reg-policy.name.tooltip=Display Name of the policy
anonymous-policies=Anonymous Access Policies
anonymous-policies.tooltip=Those Policies are used when Client Registration Service is invoked by unauthenticated request. This means request doesn't contain Initial Access Token nor Bearer Token.
auth-policies=Authenticated Access Policies
@@ -561,6 +562,8 @@ consent-required-for-all-mappers.label=Consent Required For Mappers
consent-required-for-all-mappers.tooltip=If on, then all newly registered protocol mappers will automatically have consentRequired switch on. This means that user will need to approve consent screen. NOTE: Consent screen is shown just if client has consentRequired switch on. So it's usually good to use this switch together with consent-required policy.
allowed-client-templates.label=Allowed Client Templates
allowed-client-templates.tooltip=Whitelist of the client templates, which can be used on newly registered client. Attempt to register client with some client template, which is not whitelisted, will be rejected. By default, the whitelist is empty, so there are not any client templates are allowed.
+max-clients.label=Max Clients Per Realm
+max-clients.tooltip=It won't be allowed to register new client if count of existing clients in realm is same or bigger than configured limit.
client-templates=Client Templates
client-templates.tooltip=Client templates allow you to define common configuration that is shared between multiple clients