KEYCLOAK-19374 Create implementation based on annotation processor

Use of boxed types as started in 009d4ca445 is finalized here
to enable storing data in a map. MapClientEntity methods are
reordered for the sake of grouping the collection-based
properties together and understanding the connections between those.
This commit is contained in:
Hynek Mlnarik 2021-09-21 10:16:37 +02:00 committed by Hynek Mlnařík
parent 12d4837fa9
commit 3abf9283a8
17 changed files with 728 additions and 752 deletions

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-model-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>16.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-build-processor</artifactId>
<name>Keycloak Model Java Annotations and Processor</name>
<description/>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -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 "";
}

View file

@ -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;
}

View file

@ -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<String, HashSet<ExecutableElement>> 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<ExecutableElement> 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<String, String> 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<String, HashSet<ExecutableElement>> 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<Field, Object> 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<String, HashSet<ExecutableElement>> me : methodsPerAttribute.entrySet()) {
String enumConstant = toEnumConstant(me.getKey());
HashSet<ExecutableElement> 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<ExecutableElement> 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<ExecutableElement> 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<String> 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());
}
}

View file

@ -11,6 +11,27 @@
<name>Keycloak Model Map</name> <name>Keycloak Model Map</name>
<description/> <description/>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-build-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>org.keycloak.models.map.processor.GenerateEntityImplementationsProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>
@ -42,6 +63,13 @@
<artifactId>hamcrest-all</artifactId> <artifactId>hamcrest-all</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-build-processor</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -23,6 +23,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -79,7 +80,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return entity.isEnabled(); final Boolean enabled = entity.isEnabled();
return enabled == null ? false : enabled;
} }
@Override @Override
@ -89,7 +91,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isAlwaysDisplayInConsole() { public boolean isAlwaysDisplayInConsole() {
return entity.isAlwaysDisplayInConsole(); final Boolean alwaysDisplayInConsole = entity.isAlwaysDisplayInConsole();
return alwaysDisplayInConsole == null ? false : alwaysDisplayInConsole;
} }
@Override @Override
@ -99,7 +102,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isSurrogateAuthRequired() { public boolean isSurrogateAuthRequired() {
return entity.isSurrogateAuthRequired(); final Boolean surrogateAuthRequired = entity.isSurrogateAuthRequired();
return surrogateAuthRequired == null ? false : surrogateAuthRequired;
} }
@Override @Override
@ -109,7 +113,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public Set<String> getWebOrigins() { public Set<String> getWebOrigins() {
return entity.getWebOrigins(); final Set<String> webOrigins = entity.getWebOrigins();
return webOrigins == null ? Collections.emptySet() : webOrigins;
} }
@Override @Override
@ -129,7 +134,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public Set<String> getRedirectUris() { public Set<String> getRedirectUris() {
return entity.getRedirectUris(); final Set<String> redirectUris = entity.getRedirectUris();
return redirectUris == null ? Collections.emptySet() : redirectUris;
} }
@Override @Override
@ -179,7 +185,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isBearerOnly() { public boolean isBearerOnly() {
return entity.isBearerOnly(); final Boolean bearerOnly = entity.isBearerOnly();
return bearerOnly == null ? false : bearerOnly;
} }
@Override @Override
@ -214,7 +221,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public int getNodeReRegistrationTimeout() { public int getNodeReRegistrationTimeout() {
return entity.getNodeReRegistrationTimeout(); final Integer nodeReRegistrationTimeout = entity.getNodeReRegistrationTimeout();
return nodeReRegistrationTimeout == null ? 0 : nodeReRegistrationTimeout;
} }
@Override @Override
@ -265,13 +273,15 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public String getAttribute(String name) { public String getAttribute(String name) {
List<String> attribute = entity.getAttribute(name); List<String> attribute = entity.getAttribute(name);
if (attribute.isEmpty()) return null; if (attribute == null || attribute.isEmpty()) return null;
return attribute.get(0); return attribute.get(0);
} }
@Override @Override
public Map<String, String> getAttributes() { public Map<String, String> getAttributes() {
return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, final Map<String, List<String>> attributes = entity.getAttributes();
final Map<String, List<String>> a = attributes == null ? Collections.emptyMap() : attributes;
return a.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
entry -> { entry -> {
if (entry.getValue().isEmpty()) return null; if (entry.getValue().isEmpty()) return null;
return entry.getValue().get(0); return entry.getValue().get(0);
@ -286,7 +296,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public Map<String, String> getAuthenticationFlowBindingOverrides() { public Map<String, String> getAuthenticationFlowBindingOverrides() {
return entity.getAuthenticationFlowBindingOverrides(); final Map<String, String> authenticationFlowBindingOverrides = entity.getAuthenticationFlowBindingOverrides();
return authenticationFlowBindingOverrides == null ? Collections.emptyMap() : authenticationFlowBindingOverrides;
} }
@Override @Override
@ -301,7 +312,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isFrontchannelLogout() { public boolean isFrontchannelLogout() {
return entity.isFrontchannelLogout(); final Boolean frontchannelLogout = entity.isFrontchannelLogout();
return frontchannelLogout == null ? false : frontchannelLogout;
} }
@Override @Override
@ -311,7 +323,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isFullScopeAllowed() { public boolean isFullScopeAllowed() {
return entity.isFullScopeAllowed(); final Boolean fullScopeAllowed = entity.isFullScopeAllowed();
return fullScopeAllowed == null ? false : fullScopeAllowed;
} }
@Override @Override
@ -321,7 +334,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isPublicClient() { public boolean isPublicClient() {
return entity.isPublicClient(); final Boolean publicClient = entity.isPublicClient();
return publicClient == null ? false : publicClient;
} }
@Override @Override
@ -331,7 +345,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isConsentRequired() { public boolean isConsentRequired() {
return entity.isConsentRequired(); final Boolean consentRequired = entity.isConsentRequired();
return consentRequired == null ? false : consentRequired;
} }
@Override @Override
@ -341,7 +356,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isStandardFlowEnabled() { public boolean isStandardFlowEnabled() {
return entity.isStandardFlowEnabled(); final Boolean standardFlowEnabled = entity.isStandardFlowEnabled();
return standardFlowEnabled == null ? false : standardFlowEnabled;
} }
@Override @Override
@ -351,7 +367,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isImplicitFlowEnabled() { public boolean isImplicitFlowEnabled() {
return entity.isImplicitFlowEnabled(); final Boolean implicitFlowEnabled = entity.isImplicitFlowEnabled();
return implicitFlowEnabled == null ? false : implicitFlowEnabled;
} }
@Override @Override
@ -361,7 +378,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isDirectAccessGrantsEnabled() { public boolean isDirectAccessGrantsEnabled() {
return entity.isDirectAccessGrantsEnabled(); final Boolean directAccessGrantsEnabled = entity.isDirectAccessGrantsEnabled();
return directAccessGrantsEnabled == null ? false : directAccessGrantsEnabled;
} }
@Override @Override
@ -371,7 +389,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public boolean isServiceAccountsEnabled() { public boolean isServiceAccountsEnabled() {
return entity.isServiceAccountsEnabled(); final Boolean serviceAccountsEnabled = entity.isServiceAccountsEnabled();
return serviceAccountsEnabled == null ? false : serviceAccountsEnabled;
} }
@Override @Override
@ -386,7 +405,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public int getNotBefore() { public int getNotBefore() {
return entity.getNotBefore(); final Integer notBefore = entity.getNotBefore();
return notBefore == null ? 0 : notBefore;
} }
@Override @Override
@ -398,7 +418,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public Stream<RoleModel> getScopeMappingsStream() { public Stream<RoleModel> getScopeMappingsStream() {
return this.entity.getScopeMappings().stream() final Collection<String> scopeMappings = this.entity.getScopeMappings();
return scopeMappings == null ? Stream.empty() : scopeMappings.stream()
.map(realm::getRoleById) .map(realm::getRoleById)
.filter(Objects::nonNull); .filter(Objects::nonNull);
} }
@ -415,7 +436,7 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
public void deleteScopeMapping(RoleModel role) { public void deleteScopeMapping(RoleModel role) {
final String id = role == null ? null : role.getId(); final String id = role == null ? null : role.getId();
if (id != null) { if (id != null) {
this.entity.deleteScopeMapping(id); this.entity.removeScopeMapping(id);
} }
} }
@ -434,7 +455,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
if (isFullScopeAllowed()) return true; if (isFullScopeAllowed()) return true;
final String id = role == null ? null : role.getId(); final String id = role == null ? null : role.getId();
if (id != null && this.entity.getScopeMappings().contains(id)) { final Collection<String> scopeMappings = this.entity.getScopeMappings();
if (id != null && scopeMappings != null && scopeMappings.contains(id)) {
return true; return true;
} }
@ -483,7 +505,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override @Override
public Stream<ProtocolMapperModel> getProtocolMappersStream() { public Stream<ProtocolMapperModel> getProtocolMappersStream() {
return entity.getProtocolMappers().stream().distinct(); final Map<String, ProtocolMapperModel> protocolMappers = entity.getProtocolMappers();
return protocolMappers == null ? Stream.empty() : protocolMappers.values().stream().distinct();
} }
@Override @Override
@ -504,7 +527,8 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
pm.setConfig(new HashMap<>()); pm.setConfig(new HashMap<>());
} }
return entity.addProtocolMapper(pm); entity.setProtocolMapper(pm.getId(), pm);
return pm;
} }
@Override @Override
@ -519,18 +543,19 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
public void updateProtocolMapper(ProtocolMapperModel mapping) { public void updateProtocolMapper(ProtocolMapperModel mapping) {
final String id = mapping == null ? null : mapping.getId(); final String id = mapping == null ? null : mapping.getId();
if (id != null) { if (id != null) {
entity.updateProtocolMapper(id, mapping); entity.setProtocolMapper(id, mapping);
} }
} }
@Override @Override
public ProtocolMapperModel getProtocolMapperById(String id) { public ProtocolMapperModel getProtocolMapperById(String id) {
return entity.getProtocolMapperById(id); return entity.getProtocolMapper(id);
} }
@Override @Override
public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) { public ProtocolMapperModel getProtocolMapperByName(String protocol, String name) {
return entity.getProtocolMappers().stream() final Map<String, ProtocolMapperModel> protocolMappers = entity.getProtocolMappers();
return protocolMappers == null ? null : protocolMappers.values().stream()
.filter(pm -> Objects.equals(pm.getProtocol(), protocol) && Objects.equals(pm.getName(), name)) .filter(pm -> Objects.equals(pm.getProtocol(), protocol) && Objects.equals(pm.getName(), name))
.findAny() .findAny()
.orElse(null); .orElse(null);

View file

@ -22,36 +22,89 @@ import org.keycloak.models.map.common.UpdatableEntity;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
/** /**
* *
* @author hmlnarik * @author hmlnarik
*/ */
@GenerateEntityImplementations(inherits="org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity")
public interface MapClientEntity extends AbstractEntity, UpdatableEntity { 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;
ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model); protected AbstractClientEntity() {
this.id = null;
}
public AbstractClientEntity(String id) {
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public boolean isUpdated() {
return this.updated;
}
@Override
public Stream<String> getClientScopes(boolean defaultScope) {
final Map<String, Boolean> clientScopes = getClientScopes();
return clientScopes == null ? Stream.empty() : clientScopes.entrySet().stream()
.filter(me -> Objects.equals(me.getValue(), defaultScope))
.map(Entry::getKey);
}
}
Map<String, Boolean> getClientScopes();
Stream<String> getClientScopes(boolean defaultScope);
void setClientScope(String id, Boolean defaultScope);
void removeClientScope(String id);
ProtocolMapperModel getProtocolMapper(String id);
Map<String, ProtocolMapperModel> getProtocolMappers();
void setProtocolMapper(String id, ProtocolMapperModel mapping);
void removeProtocolMapper(String id);
void addRedirectUri(String redirectUri); void addRedirectUri(String redirectUri);
Set<String> getRedirectUris();
void removeRedirectUri(String redirectUri);
void setRedirectUris(Set<String> redirectUris);
void addScopeMapping(String id); void addScopeMapping(String id);
void removeScopeMapping(String id);
Collection<String> getScopeMappings();
void addWebOrigin(String webOrigin); void addWebOrigin(String webOrigin);
Set<String> getWebOrigins();
void removeWebOrigin(String webOrigin);
void setWebOrigins(Set<String> webOrigins);
void deleteScopeMapping(String id); default List<String> getAttribute(String name) { return getAttributes().get(name); }
List<String> getAttribute(String name);
Map<String, List<String>> getAttributes(); Map<String, List<String>> getAttributes();
void removeAttribute(String name);
void setAttribute(String name, List<String> values);
Map<String, String> getAuthFlowBindings(); Map<String, String> getAuthFlowBindings();
void setAuthFlowBindings(Map<String, String> authFlowBindings);
String getAuthenticationFlowBindingOverride(String binding); String getAuthenticationFlowBindingOverride(String binding);
Map<String, String> getAuthenticationFlowBindingOverrides(); Map<String, String> getAuthenticationFlowBindingOverrides();
void removeAuthenticationFlowBindingOverride(String binding);
void setAuthenticationFlowBindingOverride(String binding, String flowId);
String getBaseUrl(); String getBaseUrl();
@ -59,40 +112,28 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
String getClientId(); String getClientId();
Stream<String> getClientScopes(boolean defaultScope);
String getDescription(); String getDescription();
String getManagementUrl(); String getManagementUrl();
String getName(); String getName();
int getNodeReRegistrationTimeout(); Integer getNodeReRegistrationTimeout();
int getNotBefore(); Integer getNotBefore();
String getProtocol(); String getProtocol();
ProtocolMapperModel getProtocolMapperById(String id);
Collection<ProtocolMapperModel> getProtocolMappers();
String getRealmId(); String getRealmId();
Set<String> getRedirectUris();
String getRegistrationToken(); String getRegistrationToken();
String getRootUrl(); String getRootUrl();
Set<String> getScope(); Set<String> getScope();
Collection<String> getScopeMappings();
String getSecret(); String getSecret();
Set<String> getWebOrigins();
Boolean isAlwaysDisplayInConsole(); Boolean isAlwaysDisplayInConsole();
Boolean isBearerOnly(); Boolean isBearerOnly();
@ -117,26 +158,8 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
Boolean isSurrogateAuthRequired(); 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 setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole);
void setAttribute(String name, List<String> values);
void setAuthFlowBindings(Map<String, String> authFlowBindings);
void setAuthenticationFlowBindingOverride(String binding, String flowId);
void setBaseUrl(String baseUrl); void setBaseUrl(String baseUrl);
void setBearerOnly(Boolean bearerOnly); void setBearerOnly(Boolean bearerOnly);
@ -163,17 +186,15 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
void setName(String name); 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 setProtocol(String protocol);
void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers);
void setPublicClient(Boolean publicClient); void setPublicClient(Boolean publicClient);
void setRedirectUris(Set<String> redirectUris); void setRealmId(String realmId);
void setRegistrationToken(String registrationToken); void setRegistrationToken(String registrationToken);
@ -189,8 +210,4 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
void setSurrogateAuthRequired(Boolean surrogateAuthRequired); void setSurrogateAuthRequired(Boolean surrogateAuthRequired);
void setWebOrigins(Set<String> webOrigins);
void updateProtocolMapper(String id, ProtocolMapperModel mapping);
} }

View file

@ -30,7 +30,7 @@ public class MapClientEntityDelegate extends MapClientEntityLazyDelegate {
} }
@Override @Override
protected MapClientEntity getDelegate() { protected MapClientEntity getWriteDelegate() {
return delegate; return delegate;
} }
} }

View file

@ -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<String> redirectUris = new HashSet<>();
private boolean enabled;
private boolean alwaysDisplayInConsole;
private String clientAuthenticatorType;
private String secret;
private String registrationToken;
private String protocol;
private Map<String, List<String>> attributes = new HashMap<>();
private Map<String, String> authFlowBindings = new HashMap<>();
private boolean publicClient;
private boolean fullScopeAllowed;
private boolean frontchannelLogout;
private int notBefore;
private Set<String> scope = new HashSet<>();
private Set<String> webOrigins = new HashSet<>();
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
private Map<String, Boolean> clientScopes = new HashMap<>();
private Set<String> 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<String> getRedirectUris() {
return redirectUris;
}
@Override
public void setRedirectUris(Set<String> 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<String, List<String>> getAttributes() {
return attributes;
}
@Override
public void setAttribute(String name, List<String> values) {
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
}
@Override
public Map<String, String> getAuthFlowBindings() {
return authFlowBindings;
}
@Override
public void setAuthFlowBindings(Map<String, String> 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<String> getScope() {
return scope;
}
@Override
public void setScope(Set<String> scope) {
this.updated |= ! Objects.equals(this.scope, scope);
this.scope.clear();
this.scope.addAll(scope);
}
@Override
public Set<String> getWebOrigins() {
return webOrigins;
}
@Override
public void setWebOrigins(Set<String> 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<ProtocolMapperModel> 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<ProtocolMapperModel> 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<String> getAttribute(String name) {
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
}
@Override
public String getAuthenticationFlowBindingOverride(String binding) {
return this.authFlowBindings.get(binding);
}
@Override
public Map<String, String> 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<String> 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<String> 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;
}
}

View file

@ -39,8 +39,8 @@ public class MapClientEntityLazyDelegate implements MapClientEntity {
this.delegateSupplier = delegateSupplier; this.delegateSupplier = delegateSupplier;
} }
protected MapClientEntity getDelegate() { protected MapClientEntity getWriteDelegate() {
if (! delegate.isMarked()) { if (! isWriteDelegateInitialized()) {
delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true); delegate.compareAndSet(null, delegateSupplier == null ? null : delegateSupplier.get(), false, true);
} }
MapClientEntity ref = delegate.getReference(); MapClientEntity ref = delegate.getReference();
@ -50,419 +50,427 @@ public class MapClientEntityLazyDelegate implements MapClientEntity {
return ref; return ref;
} }
@Override protected boolean isWriteDelegateInitialized() {
public void addClientScope(String id, Boolean defaultScope) { return delegate.isMarked();
getDelegate().addClientScope(id, defaultScope); }
protected MapClientEntity getReadDelegate() {
return getWriteDelegate();
} }
@Override @Override
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) { public void setClientScope(String id, Boolean defaultScope) {
return getDelegate().addProtocolMapper(model); getWriteDelegate().setClientScope(id, defaultScope);
} }
@Override @Override
public void addRedirectUri(String redirectUri) { public void addRedirectUri(String redirectUri) {
getDelegate().addRedirectUri(redirectUri); getWriteDelegate().addRedirectUri(redirectUri);
} }
@Override @Override
public void addScopeMapping(String id) { public void addScopeMapping(String id) {
getDelegate().addScopeMapping(id); getWriteDelegate().addScopeMapping(id);
} }
@Override @Override
public void addWebOrigin(String webOrigin) { public void addWebOrigin(String webOrigin) {
getDelegate().addWebOrigin(webOrigin); getWriteDelegate().addWebOrigin(webOrigin);
} }
@Override @Override
public void deleteScopeMapping(String id) { public void removeScopeMapping(String id) {
getDelegate().deleteScopeMapping(id); getWriteDelegate().removeScopeMapping(id);
} }
@Override @Override
public List<String> getAttribute(String name) { public List<String> getAttribute(String name) {
return getDelegate().getAttribute(name); return getReadDelegate().getAttribute(name);
} }
@Override @Override
public Map<String, List<String>> getAttributes() { public Map<String, List<String>> getAttributes() {
return getDelegate().getAttributes(); return getReadDelegate().getAttributes();
} }
@Override @Override
public Map<String, String> getAuthFlowBindings() { public Map<String, String> getAuthFlowBindings() {
return getDelegate().getAuthFlowBindings(); return getReadDelegate().getAuthFlowBindings();
} }
@Override @Override
public String getAuthenticationFlowBindingOverride(String binding) { public String getAuthenticationFlowBindingOverride(String binding) {
return getDelegate().getAuthenticationFlowBindingOverride(binding); return getReadDelegate().getAuthenticationFlowBindingOverride(binding);
} }
@Override @Override
public Map<String, String> getAuthenticationFlowBindingOverrides() { public Map<String, String> getAuthenticationFlowBindingOverrides() {
return getDelegate().getAuthenticationFlowBindingOverrides(); return getReadDelegate().getAuthenticationFlowBindingOverrides();
} }
@Override @Override
public String getBaseUrl() { public String getBaseUrl() {
return getDelegate().getBaseUrl(); return getReadDelegate().getBaseUrl();
} }
@Override @Override
public String getClientAuthenticatorType() { public String getClientAuthenticatorType() {
return getDelegate().getClientAuthenticatorType(); return getReadDelegate().getClientAuthenticatorType();
} }
@Override @Override
public String getClientId() { public String getClientId() {
return getDelegate().getClientId(); return getReadDelegate().getClientId();
} }
@Override @Override
public Stream<String> getClientScopes(boolean defaultScope) { public Stream<String> getClientScopes(boolean defaultScope) {
return getDelegate().getClientScopes(defaultScope); return getReadDelegate().getClientScopes(defaultScope);
}
@Override
public Map<String, Boolean> getClientScopes() {
return getReadDelegate().getClientScopes();
} }
@Override @Override
public String getDescription() { public String getDescription() {
return getDelegate().getDescription(); return getReadDelegate().getDescription();
} }
@Override @Override
public String getManagementUrl() { public String getManagementUrl() {
return getDelegate().getManagementUrl(); return getReadDelegate().getManagementUrl();
} }
@Override @Override
public String getName() { public String getName() {
return getDelegate().getName(); return getReadDelegate().getName();
} }
@Override @Override
public int getNodeReRegistrationTimeout() { public Integer getNodeReRegistrationTimeout() {
return getDelegate().getNodeReRegistrationTimeout(); return getReadDelegate().getNodeReRegistrationTimeout();
} }
@Override @Override
public int getNotBefore() { public Integer getNotBefore() {
return getDelegate().getNotBefore(); return getReadDelegate().getNotBefore();
} }
@Override @Override
public String getProtocol() { public String getProtocol() {
return getDelegate().getProtocol(); return getReadDelegate().getProtocol();
} }
@Override @Override
public ProtocolMapperModel getProtocolMapperById(String id) { public ProtocolMapperModel getProtocolMapper(String id) {
return getDelegate().getProtocolMapperById(id); return getReadDelegate().getProtocolMapper(id);
} }
@Override @Override
public Collection<ProtocolMapperModel> getProtocolMappers() { public Map<String,ProtocolMapperModel> getProtocolMappers() {
return getDelegate().getProtocolMappers(); return getReadDelegate().getProtocolMappers();
} }
@Override @Override
public String getRealmId() { public String getRealmId() {
return getDelegate().getRealmId(); return getReadDelegate().getRealmId();
}
@Override
public void setRealmId(String realmId) {
getWriteDelegate().setRealmId(realmId);
} }
@Override @Override
public Set<String> getRedirectUris() { public Set<String> getRedirectUris() {
return getDelegate().getRedirectUris(); return getReadDelegate().getRedirectUris();
} }
@Override @Override
public String getRegistrationToken() { public String getRegistrationToken() {
return getDelegate().getRegistrationToken(); return getReadDelegate().getRegistrationToken();
} }
@Override @Override
public String getRootUrl() { public String getRootUrl() {
return getDelegate().getRootUrl(); return getReadDelegate().getRootUrl();
} }
@Override @Override
public Set<String> getScope() { public Set<String> getScope() {
return getDelegate().getScope(); return getReadDelegate().getScope();
} }
@Override @Override
public Collection<String> getScopeMappings() { public Collection<String> getScopeMappings() {
return getDelegate().getScopeMappings(); return getReadDelegate().getScopeMappings();
} }
@Override @Override
public String getSecret() { public String getSecret() {
return getDelegate().getSecret(); return getReadDelegate().getSecret();
} }
@Override @Override
public Set<String> getWebOrigins() { public Set<String> getWebOrigins() {
return getDelegate().getWebOrigins(); return getReadDelegate().getWebOrigins();
} }
@Override @Override
public Boolean isAlwaysDisplayInConsole() { public Boolean isAlwaysDisplayInConsole() {
return getDelegate().isAlwaysDisplayInConsole(); return getWriteDelegate().isAlwaysDisplayInConsole();
} }
@Override @Override
public Boolean isBearerOnly() { public Boolean isBearerOnly() {
return getDelegate().isBearerOnly(); return getWriteDelegate().isBearerOnly();
} }
@Override @Override
public Boolean isConsentRequired() { public Boolean isConsentRequired() {
return getDelegate().isConsentRequired(); return getWriteDelegate().isConsentRequired();
} }
@Override @Override
public Boolean isDirectAccessGrantsEnabled() { public Boolean isDirectAccessGrantsEnabled() {
return getDelegate().isDirectAccessGrantsEnabled(); return getWriteDelegate().isDirectAccessGrantsEnabled();
} }
@Override @Override
public Boolean isEnabled() { public Boolean isEnabled() {
return getDelegate().isEnabled(); return getWriteDelegate().isEnabled();
} }
@Override @Override
public Boolean isFrontchannelLogout() { public Boolean isFrontchannelLogout() {
return getDelegate().isFrontchannelLogout(); return getWriteDelegate().isFrontchannelLogout();
} }
@Override @Override
public Boolean isFullScopeAllowed() { public Boolean isFullScopeAllowed() {
return getDelegate().isFullScopeAllowed(); return getWriteDelegate().isFullScopeAllowed();
} }
@Override @Override
public Boolean isImplicitFlowEnabled() { public Boolean isImplicitFlowEnabled() {
return getDelegate().isImplicitFlowEnabled(); return getWriteDelegate().isImplicitFlowEnabled();
} }
@Override @Override
public Boolean isPublicClient() { public Boolean isPublicClient() {
return getDelegate().isPublicClient(); return getWriteDelegate().isPublicClient();
} }
@Override @Override
public Boolean isServiceAccountsEnabled() { public Boolean isServiceAccountsEnabled() {
return getDelegate().isServiceAccountsEnabled(); return getWriteDelegate().isServiceAccountsEnabled();
} }
@Override @Override
public Boolean isStandardFlowEnabled() { public Boolean isStandardFlowEnabled() {
return getDelegate().isStandardFlowEnabled(); return getWriteDelegate().isStandardFlowEnabled();
} }
@Override @Override
public Boolean isSurrogateAuthRequired() { public Boolean isSurrogateAuthRequired() {
return getDelegate().isSurrogateAuthRequired(); return getWriteDelegate().isSurrogateAuthRequired();
} }
@Override @Override
public void removeAttribute(String name) { public void removeAttribute(String name) {
getDelegate().removeAttribute(name); getWriteDelegate().removeAttribute(name);
} }
@Override @Override
public void removeAuthenticationFlowBindingOverride(String binding) { public void removeAuthenticationFlowBindingOverride(String binding) {
getDelegate().removeAuthenticationFlowBindingOverride(binding); getWriteDelegate().removeAuthenticationFlowBindingOverride(binding);
} }
@Override @Override
public void removeClientScope(String id) { public void removeClientScope(String id) {
getDelegate().removeClientScope(id); getWriteDelegate().removeClientScope(id);
} }
@Override @Override
public void removeProtocolMapper(String id) { public void removeProtocolMapper(String id) {
getDelegate().removeProtocolMapper(id); getWriteDelegate().removeProtocolMapper(id);
} }
@Override @Override
public void removeRedirectUri(String redirectUri) { public void removeRedirectUri(String redirectUri) {
getDelegate().removeRedirectUri(redirectUri); getWriteDelegate().removeRedirectUri(redirectUri);
} }
@Override @Override
public void removeWebOrigin(String webOrigin) { public void removeWebOrigin(String webOrigin) {
getDelegate().removeWebOrigin(webOrigin); getWriteDelegate().removeWebOrigin(webOrigin);
} }
@Override @Override
public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) { public void setAlwaysDisplayInConsole(Boolean alwaysDisplayInConsole) {
getDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole); getWriteDelegate().setAlwaysDisplayInConsole(alwaysDisplayInConsole);
} }
@Override @Override
public void setAttribute(String name, List<String> values) { public void setAttribute(String name, List<String> values) {
getDelegate().setAttribute(name, values); getWriteDelegate().setAttribute(name, values);
} }
@Override @Override
public void setAuthFlowBindings(Map<String, String> authFlowBindings) { public void setAuthFlowBindings(Map<String, String> authFlowBindings) {
getDelegate().setAuthFlowBindings(authFlowBindings); getWriteDelegate().setAuthFlowBindings(authFlowBindings);
} }
@Override @Override
public void setAuthenticationFlowBindingOverride(String binding, String flowId) { public void setAuthenticationFlowBindingOverride(String binding, String flowId) {
getDelegate().setAuthenticationFlowBindingOverride(binding, flowId); getWriteDelegate().setAuthenticationFlowBindingOverride(binding, flowId);
} }
@Override @Override
public void setBaseUrl(String baseUrl) { public void setBaseUrl(String baseUrl) {
getDelegate().setBaseUrl(baseUrl); getWriteDelegate().setBaseUrl(baseUrl);
} }
@Override @Override
public void setBearerOnly(Boolean bearerOnly) { public void setBearerOnly(Boolean bearerOnly) {
getDelegate().setBearerOnly(bearerOnly); getWriteDelegate().setBearerOnly(bearerOnly);
} }
@Override @Override
public void setClientAuthenticatorType(String clientAuthenticatorType) { public void setClientAuthenticatorType(String clientAuthenticatorType) {
getDelegate().setClientAuthenticatorType(clientAuthenticatorType); getWriteDelegate().setClientAuthenticatorType(clientAuthenticatorType);
} }
@Override @Override
public void setClientId(String clientId) { public void setClientId(String clientId) {
getDelegate().setClientId(clientId); getWriteDelegate().setClientId(clientId);
} }
@Override @Override
public void setConsentRequired(Boolean consentRequired) { public void setConsentRequired(Boolean consentRequired) {
getDelegate().setConsentRequired(consentRequired); getWriteDelegate().setConsentRequired(consentRequired);
} }
@Override @Override
public void setDescription(String description) { public void setDescription(String description) {
getDelegate().setDescription(description); getWriteDelegate().setDescription(description);
} }
@Override @Override
public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) {
getDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled); getWriteDelegate().setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
} }
@Override @Override
public void setEnabled(Boolean enabled) { public void setEnabled(Boolean enabled) {
getDelegate().setEnabled(enabled); getWriteDelegate().setEnabled(enabled);
} }
@Override @Override
public void setFrontchannelLogout(Boolean frontchannelLogout) { public void setFrontchannelLogout(Boolean frontchannelLogout) {
getDelegate().setFrontchannelLogout(frontchannelLogout); getWriteDelegate().setFrontchannelLogout(frontchannelLogout);
} }
@Override @Override
public void setFullScopeAllowed(Boolean fullScopeAllowed) { public void setFullScopeAllowed(Boolean fullScopeAllowed) {
getDelegate().setFullScopeAllowed(fullScopeAllowed); getWriteDelegate().setFullScopeAllowed(fullScopeAllowed);
} }
@Override @Override
public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) {
getDelegate().setImplicitFlowEnabled(implicitFlowEnabled); getWriteDelegate().setImplicitFlowEnabled(implicitFlowEnabled);
} }
@Override @Override
public void setManagementUrl(String managementUrl) { public void setManagementUrl(String managementUrl) {
getDelegate().setManagementUrl(managementUrl); getWriteDelegate().setManagementUrl(managementUrl);
} }
@Override @Override
public void setName(String name) { public void setName(String name) {
getDelegate().setName(name); getWriteDelegate().setName(name);
} }
@Override @Override
public void setNodeReRegistrationTimeout(int nodeReRegistrationTimeout) { public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
getDelegate().setNodeReRegistrationTimeout(nodeReRegistrationTimeout); getWriteDelegate().setNodeReRegistrationTimeout(nodeReRegistrationTimeout);
} }
@Override @Override
public void setNotBefore(int notBefore) { public void setNotBefore(Integer notBefore) {
getDelegate().setNotBefore(notBefore); getWriteDelegate().setNotBefore(notBefore);
} }
@Override @Override
public void setProtocol(String protocol) { public void setProtocol(String protocol) {
getDelegate().setProtocol(protocol); getWriteDelegate().setProtocol(protocol);
}
@Override
public void setProtocolMappers(Collection<ProtocolMapperModel> protocolMappers) {
getDelegate().setProtocolMappers(protocolMappers);
} }
@Override @Override
public void setPublicClient(Boolean publicClient) { public void setPublicClient(Boolean publicClient) {
getDelegate().setPublicClient(publicClient); getWriteDelegate().setPublicClient(publicClient);
} }
@Override @Override
public void setRedirectUris(Set<String> redirectUris) { public void setRedirectUris(Set<String> redirectUris) {
getDelegate().setRedirectUris(redirectUris); getWriteDelegate().setRedirectUris(redirectUris);
} }
@Override @Override
public void setRegistrationToken(String registrationToken) { public void setRegistrationToken(String registrationToken) {
getDelegate().setRegistrationToken(registrationToken); getWriteDelegate().setRegistrationToken(registrationToken);
} }
@Override @Override
public void setRootUrl(String rootUrl) { public void setRootUrl(String rootUrl) {
getDelegate().setRootUrl(rootUrl); getWriteDelegate().setRootUrl(rootUrl);
} }
@Override @Override
public void setScope(Set<String> scope) { public void setScope(Set<String> scope) {
getDelegate().setScope(scope); getWriteDelegate().setScope(scope);
} }
@Override @Override
public void setSecret(String secret) { public void setSecret(String secret) {
getDelegate().setSecret(secret); getWriteDelegate().setSecret(secret);
} }
@Override @Override
public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) { public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) {
getDelegate().setServiceAccountsEnabled(serviceAccountsEnabled); getWriteDelegate().setServiceAccountsEnabled(serviceAccountsEnabled);
} }
@Override @Override
public void setStandardFlowEnabled(Boolean standardFlowEnabled) { public void setStandardFlowEnabled(Boolean standardFlowEnabled) {
getDelegate().setStandardFlowEnabled(standardFlowEnabled); getWriteDelegate().setStandardFlowEnabled(standardFlowEnabled);
} }
@Override @Override
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) { public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
getDelegate().setSurrogateAuthRequired(surrogateAuthRequired); getWriteDelegate().setSurrogateAuthRequired(surrogateAuthRequired);
} }
@Override @Override
public void setWebOrigins(Set<String> webOrigins) { public void setWebOrigins(Set<String> webOrigins) {
getDelegate().setWebOrigins(webOrigins); getWriteDelegate().setWebOrigins(webOrigins);
} }
@Override @Override
public void updateProtocolMapper(String id, ProtocolMapperModel mapping) { public void setProtocolMapper(String id, ProtocolMapperModel mapping) {
getDelegate().updateProtocolMapper(id, mapping); getWriteDelegate().setProtocolMapper(id, mapping);
} }
@Override @Override
public String getId() { public String getId() {
return getDelegate().getId(); return getReadDelegate().getId();
} }
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return getDelegate().isUpdated(); return isWriteDelegateInitialized() && getWriteDelegate().isUpdated();
} }
} }

View file

@ -140,7 +140,8 @@ public class MapClientProvider implements ClientProvider {
public ClientModel addClient(RealmModel realm, String id, String clientId) { public ClientModel addClient(RealmModel realm, String id, String clientId) {
LOG.tracef("addClient(%s, %s, %s)%s", realm, id, clientId, getShortStackTrace()); 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.setClientId(clientId);
entity.setEnabled(true); entity.setEnabled(true);
entity.setStandardFlowEnabled(true); entity.setStandardFlowEnabled(true);
@ -294,7 +295,7 @@ public class MapClientProvider implements ClientProvider {
clientScopes.stream() clientScopes.stream()
.filter(clientScope -> ! existingClientScopes.containsKey(clientScope.getName())) .filter(clientScope -> ! existingClientScopes.containsKey(clientScope.getName()))
.filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol)) .filter(clientScope -> Objects.equals(clientScope.getProtocol(), clientProtocol))
.forEach(clientScope -> entity.addClientScope(clientScope.getId(), defaultScope)); .forEach(clientScope -> entity.setClientScope(clientScope.getId(), defaultScope));
} }
@Override @Override

View file

@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature; 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.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory; import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jdk8.StreamSerializer; import com.fasterxml.jackson.datatype.jdk8.StreamSerializer;
@ -51,6 +52,7 @@ public class Serialization {
.setSerializationInclusion(JsonInclude.Include.NON_NULL) .setSerializationInclusion(JsonInclude.Include.NON_NULL)
.setVisibility(PropertyAccessor.ALL, Visibility.NONE) .setVisibility(PropertyAccessor.ALL, Visibility.NONE)
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY) .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(UpdatableEntity.class, IgnoreUpdatedMixIn.class)
.addMixIn(AbstractEntity.class, AbstractEntityMixIn.class) .addMixIn(AbstractEntity.class, AbstractEntityMixIn.class)
; ;

View file

@ -62,16 +62,17 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity &
@Override @Override
public void commit() { public void commit() {
log.tracef("Commit - %s", map);
if (rollback) { if (rollback) {
throw new RuntimeException("Rollback only!"); throw new RuntimeException("Rollback only!");
} }
if (! tasks.isEmpty()) {
log.tracef("Commit - %s", map);
for (MapTaskWithValue value : tasks.values()) { for (MapTaskWithValue value : tasks.values()) {
value.execute(); value.execute();
} }
} }
}
@Override @Override
public void rollback() { public void rollback() {

View file

@ -66,7 +66,9 @@ import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.keycloak.models.map.storage.CriterionNotSupportedException; import org.keycloak.models.map.storage.CriterionNotSupportedException;
import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Optional;
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID; import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
/** /**
@ -261,14 +263,14 @@ public class MapFieldPredicates {
private static MapModelCriteriaBuilder<Object, MapClientEntity, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder<Object, MapClientEntity, ClientModel> mcb, Operator op, Object[] values) { private static MapModelCriteriaBuilder<Object, MapClientEntity, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder<Object, MapClientEntity, ClientModel> mcb, Operator op, Object[] values) {
String roleIdS = ensureEqSingleValue(ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, "role_id", op, values); String roleIdS = ensureEqSingleValue(ClientModel.SearchableFields.SCOPE_MAPPING_ROLE, "role_id", op, values);
Function<MapClientEntity, ?> getter; Function<MapClientEntity, ?> getter;
getter = ce -> ce.getScopeMappings().contains(roleIdS); getter = ce -> Optional.ofNullable(ce.getScopeMappings()).orElse(Collections.emptyList()).contains(roleIdS);
return mcb.fieldCompare(Boolean.TRUE::equals, getter); return mcb.fieldCompare(Boolean.TRUE::equals, getter);
} }
private static MapModelCriteriaBuilder<Object, MapGroupEntity, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder<Object, MapGroupEntity, GroupModel> mcb, Operator op, Object[] values) { private static MapModelCriteriaBuilder<Object, MapGroupEntity, GroupModel> checkGrantedGroupRole(MapModelCriteriaBuilder<Object, MapGroupEntity, GroupModel> mcb, Operator op, Object[] values) {
String roleIdS = ensureEqSingleValue(GroupModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values); String roleIdS = ensureEqSingleValue(GroupModel.SearchableFields.ASSIGNED_ROLE, "role_id", op, values);
Function<MapGroupEntity, ?> getter; Function<MapGroupEntity, ?> getter;
getter = ge -> ge.getGrantedRoles().contains(roleIdS); getter = ge -> Optional.ofNullable(ge.getGrantedRoles()).orElse(Collections.emptySet()).contains(roleIdS);
return mcb.fieldCompare(Boolean.TRUE::equals, getter); return mcb.fieldCompare(Boolean.TRUE::equals, getter);
} }

View file

@ -34,5 +34,6 @@
<module>jpa</module> <module>jpa</module>
<module>infinispan</module> <module>infinispan</module>
<module>map</module> <module>map</module>
<module>build-processor</module>
</modules> </modules>
</project> </project>

View file

@ -19,6 +19,7 @@ package org.keycloak.models;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Specifies a mapping from user data to a protocol claim assertion. * Specifies a mapping from user data to a protocol claim assertion.
@ -78,16 +79,41 @@ public class ProtocolMapperModel implements Serializable {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object obj) {
if (this == o) return true; if (this == obj) {
if (o == null || getClass() != o.getClass()) return false;
ProtocolMapperModel that = (ProtocolMapperModel) o;
if (!id.equals(that.id)) return false;
return true; return true;
} }
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ProtocolMapperModel other = (ProtocolMapperModel) obj;
if (this.consentRequired != other.consentRequired) {
return false;
}
if ( ! Objects.equals(this.id, other.id)) {
return false;
}
if ( ! Objects.equals(this.name, other.name)) {
return false;
}
if ( ! Objects.equals(this.protocol, other.protocol)) {
return false;
}
if ( ! Objects.equals(this.protocolMapper, other.protocolMapper)) {
return false;
}
if ( ! Objects.equals(this.consentText, other.consentText)) {
return false;
}
if ( ! Objects.equals(this.config, other.config)) {
return false;
}
return true;
}
@Override @Override
public int hashCode() { public int hashCode() {

View file

@ -117,9 +117,12 @@ public class MapStorageTest extends KeycloakModelTest {
assertClientDoesNotExist(storage2, idMain, kcMain, kc2); assertClientDoesNotExist(storage2, idMain, kcMain, kc2);
assertClientDoesNotExist(storage2, id1, kc1, kc2); assertClientDoesNotExist(storage2, id1, kc1, kc2);
MapClientEntity clientMain = new MapClientEntityImpl(idMain, realmId); MapClientEntity clientMain = new MapClientEntityImpl(idMain);
MapClientEntity client1 = new MapClientEntityImpl(id1, realmId); clientMain.setRealmId(realmId);
MapClientEntity client2 = new MapClientEntityImpl(id2, realmId); MapClientEntity client1 = new MapClientEntityImpl(id1);
client1.setRealmId(realmId);
MapClientEntity client2 = new MapClientEntityImpl(id2);
client2.setRealmId(realmId);
clientMain = storageMain.create(clientMain); clientMain = storageMain.create(clientMain);
client1 = storage1.create(client1); client1 = storage1.create(client1);