diff --git a/model/build-processor/pom.xml b/model/build-processor/pom.xml
new file mode 100644
index 0000000000..0d36e17e87
--- /dev/null
+++ b/model/build-processor/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ keycloak-model-pom
+ org.keycloak
+ 16.0.0-SNAPSHOT
+
+ 4.0.0
+
+ keycloak-model-build-processor
+ Keycloak Model Java Annotations and Processor
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+
+
\ No newline at end of file
diff --git a/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEntityImplementations.java b/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEntityImplementations.java
new file mode 100644
index 0000000000..f36cf901fb
--- /dev/null
+++ b/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEntityImplementations.java
@@ -0,0 +1,32 @@
+/*
+ * 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.models.map.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author hmlnarik
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface GenerateEntityImplementations {
+ String inherits() default "";
+}
diff --git a/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEnumMapFieldType.java b/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEnumMapFieldType.java
new file mode 100644
index 0000000000..80a3f9beaf
--- /dev/null
+++ b/model/build-processor/src/main/java/org/keycloak/models/map/annotations/GenerateEnumMapFieldType.java
@@ -0,0 +1,32 @@
+/*
+ * 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.models.map.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author hmlnarik
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.METHOD)
+public @interface GenerateEnumMapFieldType {
+ Class> value() default Void.class;
+}
diff --git a/model/build-processor/src/main/java/org/keycloak/models/map/processor/GenerateEntityImplementationsProcessor.java b/model/build-processor/src/main/java/org/keycloak/models/map/processor/GenerateEntityImplementationsProcessor.java
new file mode 100644
index 0000000000..aaed0f2ce2
--- /dev/null
+++ b/model/build-processor/src/main/java/org/keycloak/models/map/processor/GenerateEntityImplementationsProcessor.java
@@ -0,0 +1,328 @@
+/*
+ * 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.models.map.processor;
+
+import org.keycloak.models.map.annotations.GenerateEntityImplementations;
+import org.keycloak.models.map.annotations.GenerateEnumMapFieldType;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.JavaFileObject;
+
+/**
+ *
+ * @author hmlnarik
+ */
+@SupportedAnnotationTypes("org.keycloak.models.map.annotations.GenerateEntityImplementations")
+@SupportedSourceVersion(SourceVersion.RELEASE_8)
+public class GenerateEntityImplementationsProcessor extends AbstractProcessor {
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ for (TypeElement annotation : annotations) {
+ Set extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);
+ annotatedElements.stream()
+ .map(TypeElement.class::cast)
+ .forEach(this::processTypeElement);
+ }
+
+ return true;
+ }
+
+ private void processTypeElement(TypeElement e) {
+ if (e.getKind() != ElementKind.INTERFACE) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation @GenerateEntityImplementations is only applicable to interface", e);
+ return;
+ }
+
+ // Find all properties
+ Map> methodsPerAttribute = e.getEnclosedElements().stream()
+ .filter(ExecutableElement.class::isInstance)
+ .map(ExecutableElement.class::cast)
+ .filter(ee -> ! (ee.getReceiverType() instanceof NoType))
+ .collect(Collectors.toMap(this::determineAttributeFromMethodName, v -> new HashSet(Arrays.asList(v)), (a,b) -> { a.addAll(b); return a; }));
+
+ // Merge plurals with singulars
+ methodsPerAttribute.keySet().stream()
+ .filter(key -> methodsPerAttribute.containsKey(key + "s"))
+ .collect(Collectors.toSet())
+ .forEach(key -> {
+ HashSet removed = methodsPerAttribute.remove(key);
+ methodsPerAttribute.get(key + "s").addAll(removed);
+ });
+
+ try {
+ generateImpl(e, methodsPerAttribute);
+ } catch (IOException ex) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Could not generate implementation for class", e);
+ }
+
+// methodsPerAttribute.entrySet().stream()
+// .sorted(Comparator.comparing(Map.Entry::getKey))
+// .forEach(me -> processingEnv.getMessager().printMessage(
+// Diagnostic.Kind.NOTE,
+// "** " + me.getKey() + ": " + me.getValue().stream().map(ExecutableElement::getSimpleName).sorted(Comparator.comparing(Object::toString)).collect(Collectors.joining(", ")))
+// );
+ }
+
+ private static final Pattern BEAN_NAME = Pattern.compile("(get|set|is|delete|remove|add|update)([A-Z]\\S+)");
+ private static final Map FORBIDDEN_PREFIXES = new HashMap<>();
+ static {
+ FORBIDDEN_PREFIXES.put("delete", "remove");
+ }
+
+ private String determineAttributeFromMethodName(ExecutableElement e) {
+ Name name = e.getSimpleName();
+ Matcher m = BEAN_NAME.matcher(name.toString());
+ if (m.matches()) {
+ String prefix = m.group(1);
+ if (FORBIDDEN_PREFIXES.containsKey(prefix)) {
+ processingEnv.getMessager().printMessage(
+ Kind.ERROR,
+ "Forbidden prefix " + prefix + "... detected, use " + FORBIDDEN_PREFIXES.get(prefix) + "... instead", e
+ );
+ }
+ return m.group(2);
+ }
+ return null;
+ }
+
+ private void generateImpl(TypeElement e, Map> methodsPerAttribute) throws IOException {
+ GenerateEntityImplementations an = e.getAnnotation(GenerateEntityImplementations.class);
+ Elements elements = processingEnv.getElementUtils();
+ TypeElement parentTypeElement = elements.getTypeElement(an.inherits().isEmpty() ? "void" : an.inherits());
+ final List extends Element> allMembers = elements.getAllMembers(parentTypeElement);
+ String className = e.getQualifiedName().toString();
+ String packageName = null;
+ int lastDot = className.lastIndexOf('.');
+ if (lastDot > 0) {
+ packageName = className.substring(0, lastDot);
+ }
+
+ String simpleClassName = className.substring(lastDot + 1);
+ String mapImplClassName = className + "Impl";
+ String mapSimpleClassName = simpleClassName + "Impl";
+
+ JavaFileObject enumFile = processingEnv.getFiler().createSourceFile(mapImplClassName);
+ try (PrintWriter pw = new PrintWriter(enumFile.openWriter()) {
+ @Override
+ public void println(String x) {
+ super.println(x == null ? x : x.replaceAll("java.lang.", ""));
+ }
+ }) {
+ if (packageName != null) {
+ pw.println("package " + packageName + ";");
+ }
+
+ pw.println("import java.util.EnumMap;");
+ pw.println("import java.util.Objects;");
+ pw.println("// DO NOT CHANGE THIS CLASS, IT IS GENERATED AUTOMATICALLY BY " + GenerateEntityImplementationsProcessor.class.getSimpleName());
+ pw.println("public class " + mapSimpleClassName + (an.inherits().isEmpty() ? "" : " extends " + an.inherits()) + " implements " + className + " {");
+ pw.println(" public enum Field {");
+ methodsPerAttribute.keySet().stream()
+ .sorted()
+ .map(GenerateEntityImplementationsProcessor::toEnumConstant)
+ .forEach(key -> pw.println(" " + key + ","));
+ pw.println(" }");
+ pw.println(" private final EnumMap values = new EnumMap<>(Field.class);");
+ pw.println(" protected Object get(Field field) { return values.get(field); }");
+ pw.println(" protected Object set(Field field, Object p0) { return values.put(field, p0); }");
+
+ // Constructors
+ allMembers.stream()
+ .filter(ExecutableElement.class::isInstance)
+ .map(ExecutableElement.class::cast)
+ .filter((ExecutableElement ee) -> ee.getKind() == ElementKind.CONSTRUCTOR)
+ .forEach((ExecutableElement ee) -> pw.println(" public " + mapSimpleClassName + "(" + methodParameters(ee.getParameters()) + ") { super(" + ee.getParameters() + "); }"));
+
+ for (Entry> me : methodsPerAttribute.entrySet()) {
+ String enumConstant = toEnumConstant(me.getKey());
+ HashSet methods = me.getValue();
+ TypeMirror fieldType = determineFieldType(me.getKey(), methods);
+ if (fieldType == null) {
+ continue;
+ }
+
+ for (ExecutableElement method : methods) {
+ if (! printMethodBody(pw, method, me.getKey(), enumConstant, fieldType)) {
+ List parentMethods = allMembers.stream()
+ .filter(ExecutableElement.class::isInstance)
+ .map(ExecutableElement.class::cast)
+ .filter(ee -> Objects.equals(ee.toString(), method.toString()))
+ .filter((ExecutableElement ee) -> ! ee.getModifiers().contains(Modifier.ABSTRACT))
+ .collect(Collectors.toList());
+ if (! parentMethods.isEmpty()) {
+ processingEnv.getMessager().printMessage(Kind.OTHER, "Method " + method + " is declared in a parent class.");
+ } else {
+ processingEnv.getMessager().printMessage(Kind.WARNING, "Could not determine desired semantics of method from its signature", method);
+ }
+ }
+ }
+ }
+ pw.println("}");
+
+ }
+ }
+
+ protected static String toEnumConstant(String key) {
+ return key.replaceAll("([a-z])([A-Z])", "$1_$2").toUpperCase();
+ }
+
+ private TypeMirror determineFieldType(String fieldName, HashSet methods) {
+ Pattern getter = Pattern.compile("(get|is)" + Pattern.quote(fieldName));
+ TypeMirror res = null;
+ for (ExecutableElement method : methods) {
+ if (getter.matcher(method.getSimpleName()).matches() && method.getParameters().isEmpty()) {
+ return method.getReturnType();
+ }
+ }
+ if (res == null) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Could not determine return type for field " + fieldName, methods.iterator().next());
+ }
+ return res;
+ }
+
+ private boolean printMethodBody(PrintWriter pw, ExecutableElement method, String fieldName, String enumConstant, TypeMirror fieldType) {
+ Pattern getter = Pattern.compile("(get|is)" + Pattern.quote(fieldName));
+ Types types = processingEnv.getTypeUtils();
+ final String methodName = method.getSimpleName().toString();
+ String setter = "set" + fieldName;
+ TypeMirror firstParameterType = method.getParameters().isEmpty()
+ ? types.getNullType()
+ : method.getParameters().get(0).asType();
+ String fieldNameSingular = fieldName.endsWith("s") ? fieldName.substring(0, fieldName.length() - 1) : fieldName;
+ String getFromMap = "get" + fieldNameSingular;
+ String addToCollection = "add" + fieldNameSingular;
+ String updateMap = "set" + fieldNameSingular;
+ String removeFromCollection = "remove" + fieldNameSingular;
+ Elements elements = processingEnv.getElementUtils();
+ TypeElement typeElement = elements.getTypeElement(types.erasure(fieldType).toString());
+
+ if (getter.matcher(methodName).matches() && method.getParameters().isEmpty() && types.isSameType(fieldType, method.getReturnType())) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method + " {");
+ pw.println(" return (" + fieldType + ") get(Field." + enumConstant + ");");
+ pw.println(" }");
+ return true;
+ } else if (setter.equals(methodName) && types.isSameType(firstParameterType, fieldType)) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
+ pw.println(" Object o = set(Field." + enumConstant + ", p0);");
+ pw.println(" updated |= ! Objects.equals(o, p0);");
+ pw.println(" }");
+ return true;
+ } else if (addToCollection.equals(methodName) && method.getParameters().size() == 1) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
+ pw.println(" " + fieldType + " o = (" + fieldType + ") get(Field." + enumConstant + ");");
+ pw.println(" if (o == null) { o = " + interfaceToImplementation(typeElement) + "; set(Field." + enumConstant + ", o); }");
+ if (isSetType(typeElement)) {
+ pw.println(" updated |= o.add(p0);");
+ } else {
+ pw.println(" o.add(p0);");
+ pw.println(" updated = true;");
+ }
+ pw.println(" }");
+ return true;
+ } else if (removeFromCollection.equals(methodName) && method.getParameters().size() == 1) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
+ pw.println(" " + fieldType + " o = (" + fieldType + ") get(Field." + enumConstant + ");");
+ pw.println(" if (o == null) { return; }");
+ pw.println(" boolean removed = o.remove(p0)" + ("java.util.Map".equals(typeElement.getQualifiedName().toString()) ? " != null" : "") + ";");
+ pw.println(" updated |= removed;");
+ pw.println(" }");
+ return true;
+ } else if (updateMap.equals(methodName) && method.getParameters().size() == 2) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0, " + method.getParameters().get(1).asType() + " p1) {");
+ pw.println(" " + fieldType + " o = (" + fieldType + ") get(Field." + enumConstant + ");");
+ pw.println(" if (o == null) { o = " + interfaceToImplementation(typeElement) + "; set(Field." + enumConstant + ", o); }");
+ pw.println(" Object v = o.put(p0, p1);");
+ pw.println(" updated |= ! Objects.equals(v, p1);");
+ pw.println(" }");
+ return true;
+ } else if (getFromMap.equals(methodName) && method.getParameters().size() == 1) {
+ pw.println(" @Override public " + method.getReturnType() + " " + method.getSimpleName() + "(" + firstParameterType + " p0) {");
+ pw.println(" " + fieldType + " o = (" + fieldType + ") get(Field." + enumConstant + ");");
+ pw.println(" return o == null ? null : o.get(p0);");
+ pw.println(" }");
+ return true;
+ }
+
+ return false;
+ }
+
+ private String interfaceToImplementation(TypeElement typeElement) {
+ GenerateEnumMapFieldType an = typeElement.getAnnotation(GenerateEnumMapFieldType.class);
+ if (an != null) {
+ return "new " + an.value().getCanonicalName() + "<>()";
+ }
+
+ Name parameterTypeQN = typeElement.getQualifiedName();
+ switch (parameterTypeQN.toString()) {
+ case "java.util.List":
+ return "new java.util.LinkedList<>()";
+ case "java.util.Map":
+ return "new java.util.HashMap<>()";
+ case "java.util.Set":
+ return "new java.util.HashSet<>()";
+ case "java.util.Collection":
+ return "new java.util.LinkedList<>()";
+ default:
+ processingEnv.getMessager().printMessage(Kind.ERROR, "Could not determine implementation for type " + typeElement, typeElement);
+ return "TODO()";
+ }
+ }
+
+ private String methodParameters(List extends VariableElement> parameters) {
+ return parameters.stream()
+ .map(p -> p.asType() + " " + p.getSimpleName())
+ .collect(Collectors.joining(", "));
+ }
+
+ private static final HashSet SET_TYPES = new HashSet<>(Arrays.asList(Set.class.getCanonicalName(), TreeSet.class.getCanonicalName(), HashSet.class.getCanonicalName(), LinkedHashSet.class.getCanonicalName()));
+
+ private boolean isSetType(TypeElement typeElement) {
+ Name name = typeElement.getQualifiedName();
+ return SET_TYPES.contains(name.toString());
+ }
+}
diff --git a/model/map/pom.xml b/model/map/pom.xml
index 29548ad219..fce044df6a 100644
--- a/model/map/pom.xml
+++ b/model/map/pom.xml
@@ -11,6 +11,27 @@
Keycloak Model Map
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.keycloak
+ keycloak-model-build-processor
+ ${project.version}
+
+
+
+ org.keycloak.models.map.processor.GenerateEntityImplementationsProcessor
+
+
+
+
+
+
org.bouncycastle
@@ -42,6 +63,13 @@
hamcrest-all
test
+
+ org.keycloak
+ keycloak-model-build-processor
+ ${project.version}
+ true
+
+
\ No newline at end of file
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java
index a6f7bf09f1..e09db4c1f3 100644
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java
+++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientAdapter.java
@@ -23,6 +23,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.MessageDigest;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -79,7 +80,8 @@ public abstract class MapClientAdapter extends AbstractClientModel getWebOrigins() {
- return entity.getWebOrigins();
+ final Set webOrigins = entity.getWebOrigins();
+ return webOrigins == null ? Collections.emptySet() : webOrigins;
}
@Override
@@ -129,7 +134,8 @@ public abstract class MapClientAdapter extends AbstractClientModel getRedirectUris() {
- return entity.getRedirectUris();
+ final Set redirectUris = entity.getRedirectUris();
+ return redirectUris == null ? Collections.emptySet() : redirectUris;
}
@Override
@@ -179,7 +185,8 @@ public abstract class MapClientAdapter extends AbstractClientModel attribute = entity.getAttribute(name);
- if (attribute.isEmpty()) return null;
+ if (attribute == null || attribute.isEmpty()) return null;
return attribute.get(0);
}
@Override
public Map getAttributes() {
- return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
+ final Map> attributes = entity.getAttributes();
+ final Map> a = attributes == null ? Collections.emptyMap() : attributes;
+ return a.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
entry -> {
if (entry.getValue().isEmpty()) return null;
return entry.getValue().get(0);
@@ -286,7 +296,8 @@ public abstract class MapClientAdapter extends AbstractClientModel getAuthenticationFlowBindingOverrides() {
- return entity.getAuthenticationFlowBindingOverrides();
+ final Map authenticationFlowBindingOverrides = entity.getAuthenticationFlowBindingOverrides();
+ return authenticationFlowBindingOverrides == null ? Collections.emptyMap() : authenticationFlowBindingOverrides;
}
@Override
@@ -301,7 +312,8 @@ public abstract class MapClientAdapter extends AbstractClientModel getScopeMappingsStream() {
- return this.entity.getScopeMappings().stream()
+ final Collection scopeMappings = this.entity.getScopeMappings();
+ return scopeMappings == null ? Stream.empty() : scopeMappings.stream()
.map(realm::getRoleById)
.filter(Objects::nonNull);
}
@@ -415,7 +436,7 @@ public abstract class MapClientAdapter extends AbstractClientModel scopeMappings = this.entity.getScopeMappings();
+ if (id != null && scopeMappings != null && scopeMappings.contains(id)) {
return true;
}
@@ -483,7 +505,8 @@ public abstract class MapClientAdapter extends AbstractClientModel getProtocolMappersStream() {
- return entity.getProtocolMappers().stream().distinct();
+ final Map protocolMappers = entity.getProtocolMappers();
+ return protocolMappers == null ? Stream.empty() : protocolMappers.values().stream().distinct();
}
@Override
@@ -504,7 +527,8 @@ public abstract class MapClientAdapter extends AbstractClientModel());
}
- return entity.addProtocolMapper(pm);
+ entity.setProtocolMapper(pm.getId(), pm);
+ return pm;
}
@Override
@@ -519,18 +543,19 @@ public abstract class MapClientAdapter extends AbstractClientModel protocolMappers = entity.getProtocolMappers();
+ return protocolMappers == null ? null : protocolMappers.values().stream()
.filter(pm -> Objects.equals(pm.getProtocol(), protocol) && Objects.equals(pm.getName(), name))
.findAny()
.orElse(null);
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java
index af5ae9ad0f..ae80c98baf 100644
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java
+++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntity.java
@@ -22,36 +22,89 @@ import org.keycloak.models.map.common.UpdatableEntity;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
+import org.keycloak.models.map.annotations.GenerateEntityImplementations;
/**
*
* @author hmlnarik
*/
+@GenerateEntityImplementations(inherits="org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity")
public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
- void addClientScope(String id, Boolean defaultScope);
+ static abstract class AbstractClientEntity implements MapClientEntity {
+ /**
+ * Flag signalizing that any of the setters has been meaningfully used.
+ */
+ protected boolean updated;
+ private String id;
+
+ protected AbstractClientEntity() {
+ this.id = null;
+ }
- ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model);
+ public AbstractClientEntity(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public boolean isUpdated() {
+ return this.updated;
+ }
+
+ @Override
+ public Stream getClientScopes(boolean defaultScope) {
+ final Map clientScopes = getClientScopes();
+ return clientScopes == null ? Stream.empty() : clientScopes.entrySet().stream()
+ .filter(me -> Objects.equals(me.getValue(), defaultScope))
+ .map(Entry::getKey);
+ }
+ }
+
+ Map getClientScopes();
+ Stream getClientScopes(boolean defaultScope);
+ void setClientScope(String id, Boolean defaultScope);
+ void removeClientScope(String id);
+
+ ProtocolMapperModel getProtocolMapper(String id);
+ Map getProtocolMappers();
+ void setProtocolMapper(String id, ProtocolMapperModel mapping);
+ void removeProtocolMapper(String id);
void addRedirectUri(String redirectUri);
+ Set getRedirectUris();
+ void removeRedirectUri(String redirectUri);
+ void setRedirectUris(Set redirectUris);
void addScopeMapping(String id);
+ void removeScopeMapping(String id);
+ Collection getScopeMappings();
void addWebOrigin(String webOrigin);
+ Set getWebOrigins();
+ void removeWebOrigin(String webOrigin);
+ void setWebOrigins(Set webOrigins);
- void deleteScopeMapping(String id);
-
- List getAttribute(String name);
-
+ default List getAttribute(String name) { return getAttributes().get(name); }
Map> getAttributes();
+ void removeAttribute(String name);
+ void setAttribute(String name, List values);
Map getAuthFlowBindings();
+ void setAuthFlowBindings(Map authFlowBindings);
String getAuthenticationFlowBindingOverride(String binding);
-
Map getAuthenticationFlowBindingOverrides();
+ void removeAuthenticationFlowBindingOverride(String binding);
+ void setAuthenticationFlowBindingOverride(String binding, String flowId);
String getBaseUrl();
@@ -59,40 +112,28 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
String getClientId();
- Stream getClientScopes(boolean defaultScope);
-
String getDescription();
String getManagementUrl();
String getName();
- int getNodeReRegistrationTimeout();
+ Integer getNodeReRegistrationTimeout();
- int getNotBefore();
+ Integer getNotBefore();
String getProtocol();
- ProtocolMapperModel getProtocolMapperById(String id);
-
- Collection getProtocolMappers();
-
String getRealmId();
- Set getRedirectUris();
-
String getRegistrationToken();
String getRootUrl();
Set getScope();
- Collection getScopeMappings();
-
String getSecret();
- Set getWebOrigins();
-
Boolean isAlwaysDisplayInConsole();
Boolean isBearerOnly();
@@ -117,26 +158,8 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
Boolean isSurrogateAuthRequired();
- void removeAttribute(String name);
-
- void removeAuthenticationFlowBindingOverride(String binding);
-
- void removeClientScope(String id);
-
- void removeProtocolMapper(String id);
-
- void removeRedirectUri(String redirectUri);
-
- void removeWebOrigin(String webOrigin);
-
void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole);
- void setAttribute(String name, List values);
-
- void setAuthFlowBindings(Map authFlowBindings);
-
- void setAuthenticationFlowBindingOverride(String binding, String flowId);
-
void setBaseUrl(String baseUrl);
void setBearerOnly(Boolean bearerOnly);
@@ -163,17 +186,15 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
void setName(String name);
- void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout);
+ void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout);
- void setNotBefore(int notBefore);
+ void setNotBefore(Integer notBefore);
void setProtocol(String protocol);
- void setProtocolMappers(Collection protocolMappers);
-
void setPublicClient(Boolean publicClient);
- void setRedirectUris(Set redirectUris);
+ void setRealmId(String realmId);
void setRegistrationToken(String registrationToken);
@@ -189,8 +210,4 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
void setSurrogateAuthRequired(Boolean surrogateAuthRequired);
- void setWebOrigins(Set webOrigins);
-
- void updateProtocolMapper(String id, ProtocolMapperModel mapping);
-
}
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java
index 9a2aabcb21..fad13b6414 100644
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java
+++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityDelegate.java
@@ -30,7 +30,7 @@ public class MapClientEntityDelegate extends MapClientEntityLazyDelegate {
}
@Override
- protected MapClientEntity getDelegate() {
+ protected MapClientEntity getWriteDelegate() {
return delegate;
}
}
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java
deleted file mode 100644
index 1131b47bc1..0000000000
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityImpl.java
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright 2020 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.models.map.client;
-
-import org.keycloak.models.ProtocolMapperModel;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- *
- * @author hmlnarik
- */
-public class MapClientEntityImpl implements MapClientEntity {
-
- private String id;
- private String realmId;
-
- private String clientId;
- private String name;
- private String description;
- private Set redirectUris = new HashSet<>();
- private boolean enabled;
- private boolean alwaysDisplayInConsole;
- private String clientAuthenticatorType;
- private String secret;
- private String registrationToken;
- private String protocol;
- private Map> attributes = new HashMap<>();
- private Map authFlowBindings = new HashMap<>();
- private boolean publicClient;
- private boolean fullScopeAllowed;
- private boolean frontchannelLogout;
- private int notBefore;
- private Set scope = new HashSet<>();
- private Set webOrigins = new HashSet<>();
- private Map protocolMappers = new HashMap<>();
- private Map clientScopes = new HashMap<>();
- private Set scopeMappings = new LinkedHashSet<>();
- private boolean surrogateAuthRequired;
- private String managementUrl;
- private String rootUrl;
- private String baseUrl;
- private boolean bearerOnly;
- private boolean consentRequired;
- private boolean standardFlowEnabled;
- private boolean implicitFlowEnabled;
- private boolean directAccessGrantsEnabled;
- private boolean serviceAccountsEnabled;
- private int nodeReRegistrationTimeout;
-
- /**
- * Flag signalizing that any of the setters has been meaningfully used.
- */
- protected boolean updated;
-
- protected MapClientEntityImpl() {
- this.id = null;
- this.realmId = null;
- }
-
- public MapClientEntityImpl(String id, String realmId) {
- Objects.requireNonNull(realmId, "realmId");
-
- this.id = id;
- this.realmId = realmId;
- }
-
- @Override
- public String getId() {
- return this.id;
- }
-
- @Override
- public boolean isUpdated() {
- return this.updated;
- }
-
- @Override
- public String getClientId() {
- return clientId;
- }
-
- @Override
- public void setClientId(String clientId) {
- this.updated |= ! Objects.equals(this.clientId, clientId);
- this.clientId = clientId;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public void setName(String name) {
- this.updated |= ! Objects.equals(this.name, name);
- this.name = name;
- }
-
- @Override
- public String getDescription() {
- return description;
- }
-
- @Override
- public void setDescription(String description) {
- this.updated |= ! Objects.equals(this.description, description);
- this.description = description;
- }
-
- @Override
- public Set getRedirectUris() {
- return redirectUris;
- }
-
- @Override
- public void setRedirectUris(Set redirectUris) {
- this.updated |= ! Objects.equals(this.redirectUris, redirectUris);
- this.redirectUris = redirectUris;
- }
-
- @Override
- public Boolean isEnabled() {
- return enabled;
- }
-
- @Override
- public void setEnabled(Boolean enabled) {
- this.updated |= ! Objects.equals(this.enabled, enabled);
- this.enabled = enabled;
- }
-
- @Override
- public Boolean isAlwaysDisplayInConsole() {
- return alwaysDisplayInConsole;
- }
-
- @Override
- public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) {
- this.updated |= ! Objects.equals(this.alwaysDisplayInConsole, alwaysDisplayInConsole);
- this.alwaysDisplayInConsole = alwaysDisplayInConsole;
- }
-
- @Override
- public String getClientAuthenticatorType() {
- return clientAuthenticatorType;
- }
-
- @Override
- public void setClientAuthenticatorType(String clientAuthenticatorType) {
- this.updated |= ! Objects.equals(this.clientAuthenticatorType, clientAuthenticatorType);
- this.clientAuthenticatorType = clientAuthenticatorType;
- }
-
- @Override
- public String getSecret() {
- return secret;
- }
-
- @Override
- public void setSecret(String secret) {
- this.updated |= ! Objects.equals(this.secret, secret);
- this.secret = secret;
- }
-
- @Override
- public String getRegistrationToken() {
- return registrationToken;
- }
-
- @Override
- public void setRegistrationToken(String registrationToken) {
- this.updated |= ! Objects.equals(this.registrationToken, registrationToken);
- this.registrationToken = registrationToken;
- }
-
- @Override
- public String getProtocol() {
- return protocol;
- }
-
- @Override
- public void setProtocol(String protocol) {
- this.updated |= ! Objects.equals(this.protocol, protocol);
- this.protocol = protocol;
- }
-
- @Override
- public Map> getAttributes() {
- return attributes;
- }
-
- @Override
- public void setAttribute(String name, List values) {
- this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
- }
-
- @Override
- public Map getAuthFlowBindings() {
- return authFlowBindings;
- }
-
- @Override
- public void setAuthFlowBindings(Map authFlowBindings) {
- this.updated |= ! Objects.equals(this.authFlowBindings, authFlowBindings);
- this.authFlowBindings = authFlowBindings;
- }
-
- @Override
- public Boolean isPublicClient() {
- return publicClient;
- }
-
- @Override
- public void setPublicClient(Boolean publicClient) {
- this.updated |= ! Objects.equals(this.publicClient, publicClient);
- this.publicClient = publicClient;
- }
-
- @Override
- public Boolean isFullScopeAllowed() {
- return fullScopeAllowed;
- }
-
- @Override
- public void setFullScopeAllowed(Boolean fullScopeAllowed) {
- this.updated |= ! Objects.equals(this.fullScopeAllowed, fullScopeAllowed);
- this.fullScopeAllowed = fullScopeAllowed;
- }
-
- @Override
- public Boolean isFrontchannelLogout() {
- return frontchannelLogout;
- }
-
- @Override
- public void setFrontchannelLogout(Boolean frontchannelLogout) {
- this.updated |= ! Objects.equals(this.frontchannelLogout, frontchannelLogout);
- this.frontchannelLogout = frontchannelLogout;
- }
-
- @Override
- public int getNotBefore() {
- return notBefore;
- }
-
- @Override
- public void setNotBefore(int notBefore) {
- this.updated |= ! Objects.equals(this.notBefore, notBefore);
- this.notBefore = notBefore;
- }
-
- @Override
- public Set getScope() {
- return scope;
- }
-
- @Override
- public void setScope(Set scope) {
- this.updated |= ! Objects.equals(this.scope, scope);
- this.scope.clear();
- this.scope.addAll(scope);
- }
-
- @Override
- public Set getWebOrigins() {
- return webOrigins;
- }
-
- @Override
- public void setWebOrigins(Set webOrigins) {
- this.updated |= ! Objects.equals(this.webOrigins, webOrigins);
- this.webOrigins.clear();
- this.webOrigins.addAll(webOrigins);
- }
-
- @Override
- public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
- Objects.requireNonNull(model.getId(), "protocolMapper.id");
- updated = true;
- this.protocolMappers.put(model.getId(), model);
- return model;
- }
-
- @Override
- public Collection getProtocolMappers() {
- return protocolMappers.values();
- }
-
- @Override
- public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
- updated = true;
- protocolMappers.put(id, mapping);
- }
-
- @Override
- public void removeProtocolMapper(String id) {
- updated |= protocolMappers.remove(id) != null;
- }
-
- @Override
- public void setProtocolMappers(Collection protocolMappers) {
- this.updated |= ! Objects.equals(this.protocolMappers, protocolMappers);
- this.protocolMappers.clear();
- this.protocolMappers.putAll(protocolMappers.stream().collect(Collectors.toMap(ProtocolMapperModel::getId, Function.identity())));
- }
-
- @Override
- public ProtocolMapperModel getProtocolMapperById(String id) {
- return id == null ? null : protocolMappers.get(id);
- }
-
- @Override
- public Boolean isSurrogateAuthRequired() {
- return surrogateAuthRequired;
- }
-
- @Override
- public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
- this.updated |= ! Objects.equals(this.surrogateAuthRequired, surrogateAuthRequired);
- this.surrogateAuthRequired = surrogateAuthRequired;
- }
-
- @Override
- public String getManagementUrl() {
- return managementUrl;
- }
-
- @Override
- public void setManagementUrl(String managementUrl) {
- this.updated |= ! Objects.equals(this.managementUrl, managementUrl);
- this.managementUrl = managementUrl;
- }
-
- @Override
- public String getRootUrl() {
- return rootUrl;
- }
-
- @Override
- public void setRootUrl(String rootUrl) {
- this.updated |= ! Objects.equals(this.rootUrl, rootUrl);
- this.rootUrl = rootUrl;
- }
-
- @Override
- public String getBaseUrl() {
- return baseUrl;
- }
-
- @Override
- public void setBaseUrl(String baseUrl) {
- this.updated |= ! Objects.equals(this.baseUrl, baseUrl);
- this.baseUrl = baseUrl;
- }
-
- @Override
- public Boolean isBearerOnly() {
- return bearerOnly;
- }
-
- @Override
- public void setBearerOnly(Boolean bearerOnly) {
- this.updated |= ! Objects.equals(this.bearerOnly, bearerOnly);
- this.bearerOnly = bearerOnly;
- }
-
- @Override
- public Boolean isConsentRequired() {
- return consentRequired;
- }
-
- @Override
- public void setConsentRequired(Boolean consentRequired) {
- this.updated |= ! Objects.equals(this.consentRequired, consentRequired);
- this.consentRequired = consentRequired;
- }
-
- @Override
- public Boolean isStandardFlowEnabled() {
- return standardFlowEnabled;
- }
-
- @Override
- public void setStandardFlowEnabled(Boolean standardFlowEnabled) {
- this.updated |= ! Objects.equals(this.standardFlowEnabled, standardFlowEnabled);
- this.standardFlowEnabled = standardFlowEnabled;
- }
-
- @Override
- public Boolean isImplicitFlowEnabled() {
- return implicitFlowEnabled;
- }
-
- @Override
- public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) {
- this.updated |= ! Objects.equals(this.implicitFlowEnabled, implicitFlowEnabled);
- this.implicitFlowEnabled = implicitFlowEnabled;
- }
-
- @Override
- public Boolean isDirectAccessGrantsEnabled() {
- return directAccessGrantsEnabled;
- }
-
- @Override
- public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) {
- this.updated |= ! Objects.equals(this.directAccessGrantsEnabled, directAccessGrantsEnabled);
- this.directAccessGrantsEnabled = directAccessGrantsEnabled;
- }
-
- @Override
- public Boolean isServiceAccountsEnabled() {
- return serviceAccountsEnabled;
- }
-
- @Override
- public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
- this.updated |= ! Objects.equals(this.serviceAccountsEnabled, serviceAccountsEnabled);
- this.serviceAccountsEnabled = serviceAccountsEnabled;
- }
-
- @Override
- public int getNodeReRegistrationTimeout() {
- return nodeReRegistrationTimeout;
- }
-
- @Override
- public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) {
- this.updated |= ! Objects.equals(this.nodeReRegistrationTimeout, nodeReRegistrationTimeout);
- this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
- }
-
- @Override
- public void addWebOrigin(String webOrigin) {
- updated = true;
- this.webOrigins.add(webOrigin);
- }
-
- @Override
- public void removeWebOrigin(String webOrigin) {
- updated |= this.webOrigins.remove(webOrigin);
- }
-
- @Override
- public void addRedirectUri(String redirectUri) {
- this.updated |= ! this.redirectUris.contains(redirectUri);
- this.redirectUris.add(redirectUri);
- }
-
- @Override
- public void removeRedirectUri(String redirectUri) {
- updated |= this.redirectUris.remove(redirectUri);
- }
-
- @Override
- public void removeAttribute(String name) {
- this.updated |= this.attributes.remove(name) != null;
- }
-
- @Override
- public List getAttribute(String name) {
- return attributes.getOrDefault(name, Collections.EMPTY_LIST);
- }
-
- @Override
- public String getAuthenticationFlowBindingOverride(String binding) {
- return this.authFlowBindings.get(binding);
- }
-
- @Override
- public Map getAuthenticationFlowBindingOverrides() {
- return this.authFlowBindings;
- }
-
- @Override
- public void removeAuthenticationFlowBindingOverride(String binding) {
- updated |= this.authFlowBindings.remove(binding) != null;
- }
-
- @Override
- public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
- this.updated = true;
- this.authFlowBindings.put(binding, flowId);
- }
-
- @Override
- public Collection getScopeMappings() {
- return scopeMappings;
- }
-
- @Override
- public void addScopeMapping(String id) {
- if (id != null) {
- updated = true;
- scopeMappings.add(id);
- }
- }
-
- @Override
- public void deleteScopeMapping(String id) {
- updated |= scopeMappings.remove(id);
- }
-
- @Override
- public void addClientScope(String id, Boolean defaultScope) {
- if (id != null) {
- updated = true;
- this.clientScopes.put(id, defaultScope);
- }
- }
-
- @Override
- public void removeClientScope(String id) {
- if (id != null) {
- updated |= clientScopes.remove(id) != null;
- }
- }
-
- @Override
- public Stream getClientScopes(boolean defaultScope) {
- return this.clientScopes.entrySet().stream()
- .filter(me -> Objects.equals(me.getValue(), defaultScope))
- .map(Entry::getKey);
- }
-
- @Override
- public String getRealmId() {
- return this.realmId;
- }
-
-}
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java
index bcfb065c01..95d66c72af 100644
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java
+++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientEntityLazyDelegate.java
@@ -39,8 +39,8 @@ public class MapClientEntityLazyDelegate implements MapClientEntity {
this.delegateSupplier = delegateSupplier;
}
- protected MapClientEntity getDelegate() {
- if (! delegate.isMarked()) {
+ protected MapClientEntity getWriteDelegate() {
+ if (! isWriteDelegateInitialized()) {
delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true);
}
MapClientEntity ref = delegate.getReference();
@@ -50,419 +50,427 @@ public class MapClientEntityLazyDelegate implements MapClientEntity {
return ref;
}
- @Override
- public void addClientScope(String id, Boolean defaultScope) {
- getDelegate().addClientScope(id, defaultScope);
+ protected boolean isWriteDelegateInitialized() {
+ return delegate.isMarked();
+ }
+
+ protected MapClientEntity getReadDelegate() {
+ return getWriteDelegate();
}
@Override
- public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
- return getDelegate().addProtocolMapper(model);
+ public void setClientScope(String id, Boolean defaultScope) {
+ getWriteDelegate().setClientScope(id, defaultScope);
}
@Override
public void addRedirectUri(String redirectUri) {
- getDelegate().addRedirectUri(redirectUri);
+ getWriteDelegate().addRedirectUri(redirectUri);
}
@Override
public void addScopeMapping(String id) {
- getDelegate().addScopeMapping(id);
+ getWriteDelegate().addScopeMapping(id);
}
@Override
public void addWebOrigin(String webOrigin) {
- getDelegate().addWebOrigin(webOrigin);
+ getWriteDelegate().addWebOrigin(webOrigin);
}
@Override
- public void deleteScopeMapping(String id) {
- getDelegate().deleteScopeMapping(id);
+ public void removeScopeMapping(String id) {
+ getWriteDelegate().removeScopeMapping(id);
}
@Override
public List getAttribute(String name) {
- return getDelegate().getAttribute(name);
+ return getReadDelegate().getAttribute(name);
}
@Override
public Map> getAttributes() {
- return getDelegate().getAttributes();
+ return getReadDelegate().getAttributes();
}
@Override
public Map getAuthFlowBindings() {
- return getDelegate().getAuthFlowBindings();
+ return getReadDelegate().getAuthFlowBindings();
}
@Override
public String getAuthenticationFlowBindingOverride(String binding) {
- return getDelegate().getAuthenticationFlowBindingOverride(binding);
+ return getReadDelegate().getAuthenticationFlowBindingOverride(binding);
}
@Override
public Map getAuthenticationFlowBindingOverrides() {
- return getDelegate().getAuthenticationFlowBindingOverrides();
+ return getReadDelegate().getAuthenticationFlowBindingOverrides();
}
@Override
public String getBaseUrl() {
- return getDelegate().getBaseUrl();
+ return getReadDelegate().getBaseUrl();
}
@Override
public String getClientAuthenticatorType() {
- return getDelegate().getClientAuthenticatorType();
+ return getReadDelegate().getClientAuthenticatorType();
}
@Override
public String getClientId() {
- return getDelegate().getClientId();
+ return getReadDelegate().getClientId();
}
@Override
public Stream getClientScopes(boolean defaultScope) {
- return getDelegate().getClientScopes(defaultScope);
+ return getReadDelegate().getClientScopes(defaultScope);
+ }
+
+ @Override
+ public Map getClientScopes() {
+ return getReadDelegate().getClientScopes();
}
@Override
public String getDescription() {
- return getDelegate().getDescription();
+ return getReadDelegate().getDescription();
}
@Override
public String getManagementUrl() {
- return getDelegate().getManagementUrl();
+ return getReadDelegate().getManagementUrl();
}
@Override
public String getName() {
- return getDelegate().getName();
+ return getReadDelegate().getName();
}
@Override
- public int getNodeReRegistrationTimeout() {
- return getDelegate().getNodeReRegistrationTimeout();
+ public Integer getNodeReRegistrationTimeout() {
+ return getReadDelegate().getNodeReRegistrationTimeout();
}
@Override
- public int getNotBefore() {
- return getDelegate().getNotBefore();
+ public Integer getNotBefore() {
+ return getReadDelegate().getNotBefore();
}
@Override
public String getProtocol() {
- return getDelegate().getProtocol();
+ return getReadDelegate().getProtocol();
}
@Override
- public ProtocolMapperModel getProtocolMapperById(String id) {
- return getDelegate().getProtocolMapperById(id);
+ public ProtocolMapperModel getProtocolMapper(String id) {
+ return getReadDelegate().getProtocolMapper(id);
}
@Override
- public Collection getProtocolMappers() {
- return getDelegate().getProtocolMappers();
+ public Map getProtocolMappers() {
+ return getReadDelegate().getProtocolMappers();
}
@Override
public String getRealmId() {
- return getDelegate().getRealmId();
+ return getReadDelegate().getRealmId();
+ }
+
+ @Override
+ public void setRealmId(String realmId) {
+ getWriteDelegate().setRealmId(realmId);
}
@Override
public Set getRedirectUris() {
- return getDelegate().getRedirectUris();
+ return getReadDelegate().getRedirectUris();
}
@Override
public String getRegistrationToken() {
- return getDelegate().getRegistrationToken();
+ return getReadDelegate().getRegistrationToken();
}
@Override
public String getRootUrl() {
- return getDelegate().getRootUrl();
+ return getReadDelegate().getRootUrl();
}
@Override
public Set getScope() {
- return getDelegate().getScope();
+ return getReadDelegate().getScope();
}
@Override
public Collection getScopeMappings() {
- return getDelegate().getScopeMappings();
+ return getReadDelegate().getScopeMappings();
}
@Override
public String getSecret() {
- return getDelegate().getSecret();
+ return getReadDelegate().getSecret();
}
@Override
public Set getWebOrigins() {
- return getDelegate().getWebOrigins();
+ return getReadDelegate().getWebOrigins();
}
@Override
public Boolean isAlwaysDisplayInConsole() {
- return getDelegate().isAlwaysDisplayInConsole();
+ return getWriteDelegate().isAlwaysDisplayInConsole();
}
@Override
public Boolean isBearerOnly() {
- return getDelegate().isBearerOnly();
+ return getWriteDelegate().isBearerOnly();
}
@Override
public Boolean isConsentRequired() {
- return getDelegate().isConsentRequired();
+ return getWriteDelegate().isConsentRequired();
}
@Override
public Boolean isDirectAccessGrantsEnabled() {
- return getDelegate().isDirectAccessGrantsEnabled();
+ return getWriteDelegate().isDirectAccessGrantsEnabled();
}
@Override
public Boolean isEnabled() {
- return getDelegate().isEnabled();
+ return getWriteDelegate().isEnabled();
}
@Override
public Boolean isFrontchannelLogout() {
- return getDelegate().isFrontchannelLogout();
+ return getWriteDelegate().isFrontchannelLogout();
}
@Override
public Boolean isFullScopeAllowed() {
- return getDelegate().isFullScopeAllowed();
+ return getWriteDelegate().isFullScopeAllowed();
}
@Override
public Boolean isImplicitFlowEnabled() {
- return getDelegate().isImplicitFlowEnabled();
+ return getWriteDelegate().isImplicitFlowEnabled();
}
@Override
public Boolean isPublicClient() {
- return getDelegate().isPublicClient();
+ return getWriteDelegate().isPublicClient();
}
@Override
public Boolean isServiceAccountsEnabled() {
- return getDelegate().isServiceAccountsEnabled();
+ return getWriteDelegate().isServiceAccountsEnabled();
}
@Override
public Boolean isStandardFlowEnabled() {
- return getDelegate().isStandardFlowEnabled();
+ return getWriteDelegate().isStandardFlowEnabled();
}
@Override
public Boolean isSurrogateAuthRequired() {
- return getDelegate().isSurrogateAuthRequired();
+ return getWriteDelegate().isSurrogateAuthRequired();
}
@Override
public void removeAttribute(String name) {
- getDelegate().removeAttribute(name);
+ getWriteDelegate().removeAttribute(name);
}
@Override
public void removeAuthenticationFlowBindingOverride(String binding) {
- getDelegate().removeAuthenticationFlowBindingOverride(binding);
+ getWriteDelegate().removeAuthenticationFlowBindingOverride(binding);
}
@Override
public void removeClientScope(String id) {
- getDelegate().removeClientScope(id);
+ getWriteDelegate().removeClientScope(id);
}
@Override
public void removeProtocolMapper(String id) {
- getDelegate().removeProtocolMapper(id);
+ getWriteDelegate().removeProtocolMapper(id);
}
@Override
public void removeRedirectUri(String redirectUri) {
- getDelegate().removeRedirectUri(redirectUri);
+ getWriteDelegate().removeRedirectUri(redirectUri);
}
@Override
public void removeWebOrigin(String webOrigin) {
- getDelegate().removeWebOrigin(webOrigin);
+ getWriteDelegate().removeWebOrigin(webOrigin);
}
@Override
public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) {
- getDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole);
+ getWriteDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole);
}
@Override
public void setAttribute(String name, List values) {
- getDelegate().setAttribute(name, values);
+ getWriteDelegate().setAttribute(name, values);
}
@Override
public void setAuthFlowBindings(Map authFlowBindings) {
- getDelegate().setAuthFlowBindings(authFlowBindings);
+ getWriteDelegate().setAuthFlowBindings(authFlowBindings);
}
@Override
public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
- getDelegate().setAuthenticationFlowBindingOverride(binding, flowId);
+ getWriteDelegate().setAuthenticationFlowBindingOverride(binding, flowId);
}
@Override
public void setBaseUrl(String baseUrl) {
- getDelegate().setBaseUrl(baseUrl);
+ getWriteDelegate().setBaseUrl(baseUrl);
}
@Override
public void setBearerOnly(Boolean bearerOnly) {
- getDelegate().setBearerOnly(bearerOnly);
+ getWriteDelegate().setBearerOnly(bearerOnly);
}
@Override
public void setClientAuthenticatorType(String clientAuthenticatorType) {
- getDelegate().setClientAuthenticatorType(clientAuthenticatorType);
+ getWriteDelegate().setClientAuthenticatorType(clientAuthenticatorType);
}
@Override
public void setClientId(String clientId) {
- getDelegate().setClientId(clientId);
+ getWriteDelegate().setClientId(clientId);
}
@Override
public void setConsentRequired(Boolean consentRequired) {
- getDelegate().setConsentRequired(consentRequired);
+ getWriteDelegate().setConsentRequired(consentRequired);
}
@Override
public void setDescription(String description) {
- getDelegate().setDescription(description);
+ getWriteDelegate().setDescription(description);
}
@Override
public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) {
- getDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
+ getWriteDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
}
@Override
public void setEnabled(Boolean enabled) {
- getDelegate().setEnabled(enabled);
+ getWriteDelegate().setEnabled(enabled);
}
@Override
public void setFrontchannelLogout(Boolean frontchannelLogout) {
- getDelegate().setFrontchannelLogout(frontchannelLogout);
+ getWriteDelegate().setFrontchannelLogout(frontchannelLogout);
}
@Override
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
- getDelegate().setFullScopeAllowed(fullScopeAllowed);
+ getWriteDelegate().setFullScopeAllowed(fullScopeAllowed);
}
@Override
public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) {
- getDelegate().setImplicitFlowEnabled(implicitFlowEnabled);
+ getWriteDelegate().setImplicitFlowEnabled(implicitFlowEnabled);
}
@Override
public void setManagementUrl(String managementUrl) {
- getDelegate().setManagementUrl(managementUrl);
+ getWriteDelegate().setManagementUrl(managementUrl);
}
@Override
public void setName(String name) {
- getDelegate().setName(name);
+ getWriteDelegate().setName(name);
}
@Override
- public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) {
- getDelegate().setNodeReRegistrationTimeout(nodeReRegistrationTimeout);
+ public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
+ getWriteDelegate().setNodeReRegistrationTimeout(nodeReRegistrationTimeout);
}
@Override
- public void setNotBefore(int notBefore) {
- getDelegate().setNotBefore(notBefore);
+ public void setNotBefore(Integer notBefore) {
+ getWriteDelegate().setNotBefore(notBefore);
}
@Override
public void setProtocol(String protocol) {
- getDelegate().setProtocol(protocol);
- }
-
- @Override
- public void setProtocolMappers(Collection protocolMappers) {
- getDelegate().setProtocolMappers(protocolMappers);
+ getWriteDelegate().setProtocol(protocol);
}
@Override
public void setPublicClient(Boolean publicClient) {
- getDelegate().setPublicClient(publicClient);
+ getWriteDelegate().setPublicClient(publicClient);
}
@Override
public void setRedirectUris(Set redirectUris) {
- getDelegate().setRedirectUris(redirectUris);
+ getWriteDelegate().setRedirectUris(redirectUris);
}
@Override
public void setRegistrationToken(String registrationToken) {
- getDelegate().setRegistrationToken(registrationToken);
+ getWriteDelegate().setRegistrationToken(registrationToken);
}
@Override
public void setRootUrl(String rootUrl) {
- getDelegate().setRootUrl(rootUrl);
+ getWriteDelegate().setRootUrl(rootUrl);
}
@Override
public void setScope(Set scope) {
- getDelegate().setScope(scope);
+ getWriteDelegate().setScope(scope);
}
@Override
public void setSecret(String secret) {
- getDelegate().setSecret(secret);
+ getWriteDelegate().setSecret(secret);
}
@Override
public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
- getDelegate().setServiceAccountsEnabled(serviceAccountsEnabled);
+ getWriteDelegate().setServiceAccountsEnabled(serviceAccountsEnabled);
}
@Override
public void setStandardFlowEnabled(Boolean standardFlowEnabled) {
- getDelegate().setStandardFlowEnabled(standardFlowEnabled);
+ getWriteDelegate().setStandardFlowEnabled(standardFlowEnabled);
}
@Override
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
- getDelegate().setSurrogateAuthRequired(surrogateAuthRequired);
+ getWriteDelegate().setSurrogateAuthRequired(surrogateAuthRequired);
}
@Override
public void setWebOrigins(Set webOrigins) {
- getDelegate().setWebOrigins(webOrigins);
+ getWriteDelegate().setWebOrigins(webOrigins);
}
@Override
- public void updateProtocolMapper(String id, ProtocolMapperModel mapping) {
- getDelegate().updateProtocolMapper(id, mapping);
+ public void setProtocolMapper(String id, ProtocolMapperModel mapping) {
+ getWriteDelegate().setProtocolMapper(id, mapping);
}
@Override
public String getId() {
- return getDelegate().getId();
+ return getReadDelegate().getId();
}
@Override
public boolean isUpdated() {
- return getDelegate().isUpdated();
+ return isWriteDelegateInitialized() && getWriteDelegate().isUpdated();
}
}
diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java
index ed2a06cbf3..576ec6584a 100644
--- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java
+++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java
@@ -140,7 +140,8 @@ public class MapClientProvider implements ClientProvider {
public ClientModel addClient(RealmModel realm, String id, String clientId) {
LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace());
- MapClientEntity entity = new MapClientEntityImpl(id, realm.getId());
+ MapClientEntity entity = new MapClientEntityImpl(id);
+ entity.setRealmId(realm.getId());
entity.setClientId(clientId);
entity.setEnabled(true);
entity.setStandardFlowEnabled(true);
@@ -294,7 +295,7 @@ public class MapClientProvider implements ClientProvider {
clientScopes.stream()
.filter(clientScope -> ! existingClientScopes.containsKey(clientScope.getName()))
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
- .forEach(clientScope -> entity.addClientScope(clientScope.getId(), defaultScope));
+ .forEach(clientScope -> entity.setClientScope(clientScope.getId(), defaultScope));
}
@Override
diff --git a/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java b/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java
index e0c483c9ae..1db0d2f650 100644
--- a/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java
+++ b/model/map/src/main/java/org/keycloak/models/map/common/Serialization.java
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jdk8.StreamSerializer;
@@ -51,6 +52,7 @@ public class Serialization {
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
+ .activateDefaultTyping(new LaissezFaireSubTypeValidator() /* TODO - see javadoc */, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY)
.addMixIn(UpdatableEntity.class, IgnoreUpdatedMixIn.class)
.addMixIn(AbstractEntity.class, AbstractEntityMixIn.class)
;
diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
index a1a95a5a4b..e28cac4265 100644
--- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
+++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java
@@ -62,14 +62,15 @@ public class ConcurrentHashMapKeycloakTransaction checkScopeMappingRole(MapModelCriteriaBuilder