From fc237a8b63b3432dd60f2004ee31de351d769545 Mon Sep 17 00:00:00 2001 From: Michal Hajas Date: Wed, 8 Dec 2021 12:56:55 +0100 Subject: [PATCH] Introduce ancestor interface for entities with attributes --- ...enerateEntityImplementationsProcessor.java | 21 ++++++++++--- .../hotRod/common/HotRodTypesUtils.java | 4 +++ .../adapter/MapResourceAdapter.java | 3 +- .../entity/MapResourceEntity.java | 19 ++++++++---- .../models/map/client/MapClientEntity.java | 9 ++---- .../map/clientscope/MapClientScopeEntity.java | 14 ++++++++- .../map/common/EntityWithAttributes.java | 30 +++++++++++++++++++ .../models/map/group/MapGroupEntity.java | 11 ++----- .../models/map/realm/MapRealmEntity.java | 14 ++++++++- .../models/map/role/MapRoleEntity.java | 9 ++---- .../models/map/user/MapUserEntity.java | 10 +++++-- 11 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 model/map/src/main/java/org/keycloak/models/map/common/EntityWithAttributes.java diff --git a/model/build-processor/src/main/java/org/keycloak/models/map/processor/AbstractGenerateEntityImplementationsProcessor.java b/model/build-processor/src/main/java/org/keycloak/models/map/processor/AbstractGenerateEntityImplementationsProcessor.java index 1dca4f21de..3e5fa013ee 100644 --- a/model/build-processor/src/main/java/org/keycloak/models/map/processor/AbstractGenerateEntityImplementationsProcessor.java +++ b/model/build-processor/src/main/java/org/keycloak/models/map/processor/AbstractGenerateEntityImplementationsProcessor.java @@ -47,6 +47,7 @@ import java.util.stream.Stream; import static org.keycloak.models.map.processor.FieldAccessorType.GETTER; import static org.keycloak.models.map.processor.Util.getGenericsDeclaration; +import static org.keycloak.models.map.processor.Util.isMapType; public abstract class AbstractGenerateEntityImplementationsProcessor extends AbstractProcessor { @@ -157,7 +158,7 @@ public abstract class AbstractGenerateEntityImplementationsProcessor extends Abs protected boolean isKnownCollectionOfImmutableFinalTypes(TypeMirror fieldType) { List res = getGenericsDeclaration(fieldType); - return isCollection(fieldType) && res.stream().allMatch(tm -> isImmutableFinalType(tm) || isKnownCollectionOfImmutableFinalTypes(tm)); + return isCollection(fieldType) && res.stream().allMatch(this::isImmutableFinalType); } protected boolean isCollection(TypeMirror fieldType) { @@ -175,12 +176,24 @@ public abstract class AbstractGenerateEntityImplementationsProcessor extends Abs } protected String deepClone(TypeMirror fieldType, String parameterName) { + TypeElement typeElement = elements.getTypeElement(types.erasure(fieldType).toString()); if (isKnownCollectionOfImmutableFinalTypes(fieldType)) { - TypeElement typeElement = elements.getTypeElement(types.erasure(fieldType).toString()); return parameterName + " == null ? null : " + interfaceToImplementation(typeElement, parameterName); - } else { - return "deepClone(" + parameterName + ")"; + } else if (isMapType(typeElement)) { + List mapTypes = getGenericsDeclaration(fieldType); + boolean isKeyImmutable = isImmutableFinalType(mapTypes.get(0)); + boolean isValueImmutable = isImmutableFinalType(mapTypes.get(1)); + + return parameterName + " == null ? null : " + parameterName + ".entrySet().stream().collect(" + + "java.util.stream.Collectors.toMap(" + + (isKeyImmutable ? "java.util.Map.Entry::getKey" : "entry -> " + deepClone(mapTypes.get(0), "entry.getKey()")) + + ", " + + (isValueImmutable ? "java.util.Map.Entry::getValue" : "entry -> " + deepClone(mapTypes.get(1), "entry.getValue()")) + + ", (o1, o2) -> o1" + + ", java.util.HashMap::new" + + "))"; } + return "deepClone(" + parameterName + ")"; } protected boolean isPrimitiveType(TypeMirror fieldType) { diff --git a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java index 0ff392761f..0a852eaa0b 100644 --- a/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java +++ b/model/map-hot-rod/src/main/java/org/keycloak/models/map/storage/hotRod/common/HotRodTypesUtils.java @@ -44,6 +44,10 @@ public class HotRodTypesUtils { return new HotRodPair<>(entry.getKey(), entry.getValue()); } + public static HotRodAttributeEntity createHotRodAttributeEntityFromMapEntry(Map.Entry> entry) { + return new HotRodAttributeEntity(entry.getKey(), entry.getValue()); + } + public static boolean removeFromSetByMapKey(Set set, KeyType key, Function keyGetter) { if (set == null || set.isEmpty()) { return false; } return set.stream() diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java index f40cb36e7f..73e2c3eed8 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/adapter/MapResourceAdapter.java @@ -136,7 +136,8 @@ public class MapResourceAdapter extends AbstractResourceModel @Override public String getSingleAttribute(String name) { - return entity.getSingleAttribute(name); + List attributeValues = entity.getAttribute(name); + return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java index 2be129707e..6c9d449dd3 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/entity/MapResourceEntity.java @@ -19,6 +19,7 @@ package org.keycloak.models.map.authorization.entity; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; import java.util.HashMap; import java.util.HashSet; @@ -27,7 +28,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -public class MapResourceEntity extends UpdatableEntity.Impl implements AbstractEntity { +public class MapResourceEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes { private String id; private String name; @@ -156,23 +157,29 @@ public class MapResourceEntity extends UpdatableEntity.Impl implements AbstractE return policyIds; } + @Override public Map> getAttributes() { return attributes; } + @Override + public void setAttributes(Map> attributes) { + this.updated |= ! Objects.equals(this.attributes, attributes); + this.attributes.clear(); + this.attributes.putAll(attributes); + } + + @Override public List getAttribute(String name) { return attributes.get(name); } - public String getSingleAttribute(String name) { - List attributeValues = attributes.get(name); - return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0); - } - + @Override public void setAttribute(String name, List value) { this.updated |= !Objects.equals(this.attributes.put(name, value), value); } + @Override public void removeAttribute(String name) { this.updated |= this.attributes.remove(name) != 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 d87243d280..87f7a51225 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 @@ -17,9 +17,9 @@ package org.keycloak.models.map.client; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.EntityWithAttributes; 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; @@ -36,7 +36,7 @@ import org.keycloak.models.map.common.DeepCloner; inherits = "org.keycloak.models.map.client.MapClientEntity.AbstractClientEntity" ) @DeepCloner.Root -public interface MapClientEntity extends AbstractEntity, UpdatableEntity { +public interface MapClientEntity extends AbstractEntity, UpdatableEntity, EntityWithAttributes { public abstract class AbstractClientEntity extends UpdatableEntity.Impl implements MapClientEntity { @@ -87,11 +87,6 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity { void removeWebOrigin(String webOrigin); void setWebOrigins(Set webOrigins); - default List getAttribute(String name) { return getAttributes() == null ? null : getAttributes().get(name); } - Map> getAttributes(); - void removeAttribute(String name); - void setAttribute(String name, List values); - String getAuthenticationFlowBindingOverride(String binding); Map getAuthenticationFlowBindingOverrides(); void removeAuthenticationFlowBindingOverride(String binding); diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java index dd2d800512..2b818f7acb 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeEntity.java @@ -29,9 +29,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; -public class MapClientScopeEntity extends UpdatableEntity.Impl implements AbstractEntity { +public class MapClientScopeEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes { private String id; private String realmId; @@ -94,10 +95,19 @@ public class MapClientScopeEntity extends UpdatableEntity.Impl implements Abstra this.protocol = protocol; } + @Override public Map> getAttributes() { return attributes; } + @Override + public void setAttributes(Map> attributes) { + this.attributes.clear(); + this.attributes.putAll(attributes); + this.updated = true; + } + + @Override public void setAttribute(String name, List values) { this.updated |= ! Objects.equals(this.attributes.put(name, values), values); } @@ -132,10 +142,12 @@ public class MapClientScopeEntity extends UpdatableEntity.Impl implements Abstra return id == null ? null : protocolMappers.get(id); } + @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); } diff --git a/model/map/src/main/java/org/keycloak/models/map/common/EntityWithAttributes.java b/model/map/src/main/java/org/keycloak/models/map/common/EntityWithAttributes.java new file mode 100644 index 0000000000..f549ba1d41 --- /dev/null +++ b/model/map/src/main/java/org/keycloak/models/map/common/EntityWithAttributes.java @@ -0,0 +1,30 @@ +/* + * 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.common; + +import java.util.List; +import java.util.Map; + +public interface EntityWithAttributes { + Map> getAttributes(); + void setAttributes(Map> attributes); + List getAttribute(String name); + void setAttribute(String name, List value); + void removeAttribute(String name); + +} diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java index 5f57c12727..15a649dc52 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupEntity.java @@ -20,17 +20,16 @@ package org.keycloak.models.map.group; import org.keycloak.models.map.annotations.GenerateEntityImplementations; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.DeepCloner; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; -import java.util.List; -import java.util.Map; import java.util.Set; @GenerateEntityImplementations( inherits = "org.keycloak.models.map.group.MapGroupEntity.AbstractGroupEntity" ) @DeepCloner.Root -public interface MapGroupEntity extends UpdatableEntity, AbstractEntity { +public interface MapGroupEntity extends UpdatableEntity, AbstractEntity, EntityWithAttributes { public abstract class AbstractGroupEntity extends UpdatableEntity.Impl implements MapGroupEntity { @@ -50,12 +49,6 @@ public interface MapGroupEntity extends UpdatableEntity, AbstractEntity { } - Map> getAttributes(); - void setAttributes(Map> attributes); - List getAttribute(String name); - void setAttribute(String name, List value); - void removeAttribute(String name); - String getName(); void setName(String name); diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java index 5755f699de..bfa4066d1b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmEntity.java @@ -30,6 +30,7 @@ import org.keycloak.common.util.Time; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.OTPPolicy; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.map.realm.entity.MapAuthenticationExecutionEntity; import org.keycloak.models.map.realm.entity.MapAuthenticationFlowEntity; @@ -43,7 +44,7 @@ import org.keycloak.models.map.realm.entity.MapRequiredActionProviderEntity; import org.keycloak.models.map.realm.entity.MapRequiredCredentialEntity; import org.keycloak.models.map.realm.entity.MapWebAuthnPolicyEntity; -public class MapRealmEntity extends UpdatableEntity.Impl implements AbstractEntity { +public class MapRealmEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes { private String id; private String name; @@ -669,22 +670,33 @@ public class MapRealmEntity extends UpdatableEntity.Impl implements AbstractEnti this.webAuthnPolicyPasswordless = webAuthnPolicyPasswordless; } + @Override public void setAttribute(String name, List values) { this.updated |= ! Objects.equals(this.attributes.put(name, values), values); } + @Override public void removeAttribute(String name) { this.updated |= attributes.remove(name) != null; } + @Override public List getAttribute(String name) { return attributes.getOrDefault(name, Collections.EMPTY_LIST); } + @Override public Map> getAttributes() { return attributes; } + @Override + public void setAttributes(Map> attributes) { + this.attributes.clear(); + this.attributes.putAll(attributes); + this.updated = true; + } + public void addDefaultClientScope(String scopeId) { this.updated |= this.defaultClientScopes.add(scopeId); } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java index ba5f7be4be..5c96aafce6 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleEntity.java @@ -22,13 +22,14 @@ import java.util.Set; import org.keycloak.models.map.annotations.GenerateEntityImplementations; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.DeepCloner; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; @GenerateEntityImplementations( inherits = "org.keycloak.models.map.role.MapRoleEntity.AbstractRoleEntity" ) @DeepCloner.Root -public interface MapRoleEntity extends AbstractEntity, UpdatableEntity { +public interface MapRoleEntity extends AbstractEntity, UpdatableEntity, EntityWithAttributes { public abstract class AbstractRoleEntity extends UpdatableEntity.Impl implements MapRoleEntity { @@ -76,10 +77,4 @@ public interface MapRoleEntity extends AbstractEntity, UpdatableEntity { void setCompositeRoles(Set compositeRoles); void addCompositeRole(String roleId); void removeCompositeRole(String roleId); - - Map> getAttributes(); - void setAttributes(Map> attributes); - void setAttribute(String name, List values); - void removeAttribute(String name); - } diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java index 2eab99a6aa..3d82273545 100644 --- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserEntity.java @@ -19,12 +19,12 @@ package org.keycloak.models.map.user; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.EntityWithAttributes; import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.utils.KeycloakModelUtils; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -40,7 +40,7 @@ import java.util.stream.Stream; * * @author mhajas */ -public class MapUserEntity extends UpdatableEntity.Impl implements AbstractEntity { +public class MapUserEntity extends UpdatableEntity.Impl implements AbstractEntity, EntityWithAttributes { private String id; private String realmId; @@ -183,19 +183,23 @@ public class MapUserEntity extends UpdatableEntity.Impl implements AbstractEntit return attributes; } + @Override public List getAttribute(String name) { return attributes.getOrDefault(name, Collections.emptyList()); } + @Override public void setAttributes(Map> attributes) { this.updated |= !Objects.equals(this.attributes, attributes); this.attributes = attributes; } + @Override public void setAttribute(String name, List value) { this.updated |= !Objects.equals(this.attributes.put(name, value), value); } - + + @Override public void removeAttribute(String name) { this.updated |= this.attributes.remove(name) != null; }