KEYCLOAK-19481 Make Id and RealmId mutable fields

This commit is contained in:
Michal Hajas 2021-10-08 15:28:10 +02:00 committed by Hynek Mlnařík
parent 26a2bbac60
commit 6e591305f9
21 changed files with 156 additions and 112 deletions

View file

@ -38,14 +38,9 @@ public class MapRootAuthenticationSessionEntity implements AbstractEntity, Updat
private int timestamp; private int timestamp;
private Map<String, MapAuthenticationSessionEntity> authenticationSessions = new ConcurrentHashMap<>(); private Map<String, MapAuthenticationSessionEntity> authenticationSessions = new ConcurrentHashMap<>();
protected MapRootAuthenticationSessionEntity() { protected MapRootAuthenticationSessionEntity() {}
this.id = null;
this.realmId = null;
}
public MapRootAuthenticationSessionEntity(String id, String realmId) { public MapRootAuthenticationSessionEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -55,6 +50,13 @@ public class MapRootAuthenticationSessionEntity implements AbstractEntity, Updat
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;

View file

@ -109,7 +109,7 @@ public class MapPermissionTicketStore implements PermissionTicketStore {
+ ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists."); + ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists.");
} }
MapPermissionTicketEntity entity = new MapPermissionTicketEntity(null); MapPermissionTicketEntity entity = new MapPermissionTicketEntity();
entity.setResourceId(resourceId); entity.setResourceId(resourceId);
entity.setRequester(requester); entity.setRequester(requester);
entity.setCreatedTimestamp(System.currentTimeMillis()); entity.setCreatedTimestamp(System.currentTimeMillis());

View file

@ -24,7 +24,7 @@ import java.util.Objects;
public class MapPermissionTicketEntity implements AbstractEntity, UpdatableEntity { public class MapPermissionTicketEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private String owner; private String owner;
private String requester; private String requester;
private Long createdTimestamp; private Long createdTimestamp;
@ -39,15 +39,20 @@ public class MapPermissionTicketEntity implements AbstractEntity, UpdatableEntit
this.id = id; this.id = id;
} }
public MapPermissionTicketEntity() { public MapPermissionTicketEntity() {}
this.id = null;
}
@Override @Override
public String getId() { public String getId() {
return id; return id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
public String getOwner() { public String getOwner() {
return owner; return owner;
} }

View file

@ -30,7 +30,7 @@ import java.util.Objects;
public class MapPolicyEntity implements AbstractEntity, UpdatableEntity { public class MapPolicyEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private String name; private String name;
private String description; private String description;
private String type; private String type;
@ -48,9 +48,7 @@ public class MapPolicyEntity implements AbstractEntity, UpdatableEntity {
this.id = id; this.id = id;
} }
public MapPolicyEntity() { public MapPolicyEntity() {}
this.id = null;
}
public String getName() { public String getName() {
return name; return name;
@ -182,6 +180,13 @@ public class MapPolicyEntity implements AbstractEntity, UpdatableEntity {
return id; return id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return updated; return updated;

View file

@ -29,7 +29,7 @@ import java.util.Set;
public class MapResourceEntity implements AbstractEntity, UpdatableEntity { public class MapResourceEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private String name; private String name;
private String displayName; private String displayName;
private final Set<String> uris = new HashSet<>(); private final Set<String> uris = new HashSet<>();
@ -47,15 +47,20 @@ public class MapResourceEntity implements AbstractEntity, UpdatableEntity {
this.id = id; this.id = id;
} }
public MapResourceEntity() { public MapResourceEntity() {}
this.id = null;
}
@Override @Override
public String getId() { public String getId() {
return id; return id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
public String getName() { public String getName() {
return name; return name;
} }

View file

@ -26,7 +26,7 @@ import java.util.Objects;
public class MapResourceServerEntity implements AbstractEntity, UpdatableEntity { public class MapResourceServerEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private boolean updated = false; private boolean updated = false;
private boolean allowRemoteResourceManagement; private boolean allowRemoteResourceManagement;
@ -37,15 +37,20 @@ public class MapResourceServerEntity implements AbstractEntity, UpdatableEntity
this.id = id; this.id = id;
} }
public MapResourceServerEntity() { public MapResourceServerEntity() {}
this.id = null;
}
@Override @Override
public String getId() { public String getId() {
return id; return id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
public boolean isAllowRemoteResourceManagement() { public boolean isAllowRemoteResourceManagement() {
return allowRemoteResourceManagement; return allowRemoteResourceManagement;
} }

View file

@ -24,7 +24,7 @@ import java.util.Objects;
public class MapScopeEntity implements AbstractEntity, UpdatableEntity { public class MapScopeEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private String name; private String name;
private String displayName; private String displayName;
private String iconUri; private String iconUri;
@ -35,15 +35,20 @@ public class MapScopeEntity implements AbstractEntity, UpdatableEntity {
this.id = id; this.id = id;
} }
public MapScopeEntity() { public MapScopeEntity() {}
this.id = null;
}
@Override @Override
public String getId() { public String getId() {
return id; return id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
public String getName() { public String getName() {
return name; return name;
} }

View file

@ -42,9 +42,7 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
protected boolean updated; protected boolean updated;
private String id; private String id;
protected AbstractClientEntity() { protected AbstractClientEntity() {}
this.id = null;
}
public AbstractClientEntity(String id) { public AbstractClientEntity(String id) {
this.id = id; this.id = id;
@ -55,6 +53,13 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;

View file

@ -468,6 +468,11 @@ public class MapClientEntityLazyDelegate implements MapClientEntity {
return getReadDelegate().getId(); return getReadDelegate().getId();
} }
@Override
public void setId(String id) {
getWriteDelegate().setId(id);
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return isWriteDelegateInitialized() && getWriteDelegate().isUpdated(); return isWriteDelegateInitialized() && getWriteDelegate().isUpdated();

View file

@ -33,8 +33,8 @@ import org.keycloak.models.map.common.UpdatableEntity;
public class MapClientScopeEntity implements AbstractEntity, UpdatableEntity { public class MapClientScopeEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private final String realmId; private String realmId;
private String name; private String name;
private String protocol; private String protocol;
@ -49,14 +49,9 @@ public class MapClientScopeEntity implements AbstractEntity, UpdatableEntity {
*/ */
protected boolean updated; protected boolean updated;
protected MapClientScopeEntity() { protected MapClientScopeEntity() {}
this.id = null;
this.realmId = null;
}
public MapClientScopeEntity(String id, String realmId) { public MapClientScopeEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -66,6 +61,13 @@ public class MapClientScopeEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;
@ -148,6 +150,11 @@ public class MapClientScopeEntity implements AbstractEntity, UpdatableEntity {
return this.realmId; return this.realmId;
} }
public void setRealmId(String realmId) {
this.updated |= !Objects.equals(this.realmId, realmId);
this.realmId = realmId;
}
public Stream<String> getScopeMappings() { public Stream<String> getScopeMappings() {
return scopeMappings.stream(); return scopeMappings.stream();
} }

View file

@ -23,5 +23,5 @@ package org.keycloak.models.map.common;
public interface AbstractEntity { public interface AbstractEntity {
String getId(); String getId();
void setId(String id);
} }

View file

@ -54,7 +54,6 @@ public class Serialization {
.setVisibility(PropertyAccessor.FIELD, Visibility.ANY) .setVisibility(PropertyAccessor.FIELD, Visibility.ANY)
.activateDefaultTyping(new LaissezFaireSubTypeValidator() /* TODO - see javadoc */, ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY) .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)
; ;
public static final ConcurrentHashMap<Class<?>, ObjectReader> READERS = new ConcurrentHashMap<>(); public static final ConcurrentHashMap<Class<?>, ObjectReader> READERS = new ConcurrentHashMap<>();
@ -64,11 +63,6 @@ public class Serialization {
@JsonIgnore public abstract boolean isUpdated(); @JsonIgnore public abstract boolean isUpdated();
} }
abstract class AbstractEntityMixIn {
@JsonTypeInfo(property="id", use=Id.CLASS, include=As.WRAPPER_ARRAY)
abstract Object getId();
}
static { static {
JavaType type = TypeFactory.unknownType(); JavaType type = TypeFactory.unknownType();
JavaType streamType = MAPPER.getTypeFactory().constructParametricType(Stream.class, type); JavaType streamType = MAPPER.getTypeFactory().constructParametricType(Stream.class, type);
@ -78,10 +72,6 @@ public class Serialization {
public static <T extends AbstractEntity> T from(T orig) { public static <T extends AbstractEntity> T from(T orig) {
return from(orig, null);
}
public static <T extends AbstractEntity> T from(T orig, String newId) {
if (orig == null) { if (orig == null) {
return null; return null;
} }
@ -94,26 +84,10 @@ public class Serialization {
ObjectWriter writer = WRITERS.computeIfAbsent(origClass, MAPPER::writerFor); ObjectWriter writer = WRITERS.computeIfAbsent(origClass, MAPPER::writerFor);
final T res; final T res;
res = reader.readValue(writer.writeValueAsBytes(orig)); res = reader.readValue(writer.writeValueAsBytes(orig));
if (newId != null) {
updateId(origClass, res, newId);
}
return res; return res;
} catch (IOException ex) { } catch (IOException ex) {
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
} }
private static <K> void updateId(Class<?> origClass, AbstractEntity res, K newId) {
Field field = Reflections.findDeclaredField(origClass, "id");
if (field == null) {
throw new IllegalArgumentException("Cannot find id for " + origClass + " class");
}
try {
Reflections.setAccessible(field).set(res, newId);
} catch (IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(Serialization.class.getName()).log(Level.SEVERE, null, ex);
throw new IllegalArgumentException("Cannot set id for " + origClass + " class");
}
}
} }

View file

@ -34,7 +34,7 @@ import java.util.Set;
public class MapGroupEntity implements AbstractEntity, UpdatableEntity { public class MapGroupEntity implements AbstractEntity, UpdatableEntity {
private String id; private String id;
private final String realmId; private String realmId;
private String name; private String name;
private String parentId; private String parentId;
@ -46,14 +46,9 @@ public class MapGroupEntity implements AbstractEntity, UpdatableEntity {
*/ */
protected boolean updated; protected boolean updated;
protected MapGroupEntity() { protected MapGroupEntity() {}
this.id = null;
this.realmId = null;
}
public MapGroupEntity(String id, String realmId) { public MapGroupEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -63,6 +58,13 @@ public class MapGroupEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;
@ -113,6 +115,11 @@ public class MapGroupEntity implements AbstractEntity, UpdatableEntity {
return this.realmId; return this.realmId;
} }
public void setRealmId(String realmId) {
this.updated |= !Objects.equals(this.realmId, realmId);
this.realmId = realmId;
}
public Set<String> getGrantedRoles() { public Set<String> getGrantedRoles() {
return grantedRoles; return grantedRoles;
} }

View file

@ -39,11 +39,7 @@ public class MapUserLoginFailureEntity implements AbstractEntity, UpdatableEntit
private long lastFailure; private long lastFailure;
private String lastIPFailure; private String lastIPFailure;
public MapUserLoginFailureEntity() { public MapUserLoginFailureEntity() {}
this.id = null;
this.realmId = null;
this.userId = null;
}
public MapUserLoginFailureEntity(String id, String realmId, String userId) { public MapUserLoginFailureEntity(String id, String realmId, String userId) {
this.id = id; this.id = id;
@ -56,6 +52,13 @@ public class MapUserLoginFailureEntity implements AbstractEntity, UpdatableEntit
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;

View file

@ -130,9 +130,7 @@ public class MapRealmEntity implements AbstractEntity, UpdatableEntity {
*/ */
protected boolean updated; protected boolean updated;
protected MapRealmEntity() { protected MapRealmEntity() {}
this.id = null;
}
public MapRealmEntity(String id) { public MapRealmEntity(String id) {
this.id = id; this.id = id;
@ -143,6 +141,13 @@ public class MapRealmEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated return this.updated

View file

@ -42,14 +42,9 @@ public class MapRoleEntity implements AbstractEntity, UpdatableEntity {
*/ */
protected boolean updated; protected boolean updated;
protected MapRoleEntity() { protected MapRoleEntity() {}
this.id = null;
this.realmId = null;
}
public MapRoleEntity(String id, String realmId) { public MapRoleEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -59,6 +54,13 @@ public class MapRoleEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;

View file

@ -214,7 +214,7 @@ public class ConcurrentHashMapKeycloakTransaction<K, V extends AbstractEntity &
if (key == null) { if (key == null) {
K newKey = keyConvertor.yieldNewUniqueKey(); K newKey = keyConvertor.yieldNewUniqueKey();
key = keyConvertor.keyToString(newKey); key = keyConvertor.keyToString(newKey);
value = Serialization.from(value, key); value.setId(key);
} }
addTask(key, new CreateOperation(value)); addTask(key, new CreateOperation(value));
return value; return value;

View file

@ -62,7 +62,7 @@ public class ConcurrentHashMapStorage<K, V extends AbstractEntity & UpdatableEnt
K key = keyConvertor.fromStringSafe(value.getId()); K key = keyConvertor.fromStringSafe(value.getId());
if (key == null) { if (key == null) {
key = keyConvertor.yieldNewUniqueKey(); key = keyConvertor.yieldNewUniqueKey();
value = Serialization.from(value, keyConvertor.keyToString(key)); value.setId(keyConvertor.keyToString(key));
} }
store.putIfAbsent(key, value); store.putIfAbsent(key, value);
return value; return value;

View file

@ -42,8 +42,8 @@ import java.util.stream.Stream;
*/ */
public class MapUserEntity implements AbstractEntity, UpdatableEntity { public class MapUserEntity implements AbstractEntity, UpdatableEntity {
private final String id; private String id;
private final String realmId; private String realmId;
private String username; private String username;
private String firstName; private String firstName;
@ -71,14 +71,9 @@ public class MapUserEntity implements AbstractEntity, UpdatableEntity {
*/ */
protected boolean updated; protected boolean updated;
protected MapUserEntity() { protected MapUserEntity() {}
this.id = null;
this.realmId = null;
}
public MapUserEntity(String id, String realmId) { public MapUserEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -88,6 +83,13 @@ public class MapUserEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated return this.updated
@ -100,6 +102,11 @@ public class MapUserEntity implements AbstractEntity, UpdatableEntity {
return realmId; return realmId;
} }
public void setRealmId(String realmId) {
this.updated |= !Objects.equals(this.realmId, realmId);
this.realmId = realmId;
}
public String getUsername() { public String getUsername() {
return username; return username;
} }

View file

@ -52,16 +52,9 @@ public class MapAuthenticatedClientSessionEntity implements AbstractEntity, Upda
private boolean offline; private boolean offline;
public MapAuthenticatedClientSessionEntity() { public MapAuthenticatedClientSessionEntity() {}
this.id = null;
this.realmId = null;
}
public MapAuthenticatedClientSessionEntity(String id, String userSessionId, String realmId, String clientId, boolean offline) { public MapAuthenticatedClientSessionEntity(String id, String userSessionId, String realmId, String clientId, boolean offline) {
Objects.requireNonNull(userSessionId, "userSessionId");
Objects.requireNonNull(realmId, "realmId");
Objects.requireNonNull(clientId, "clientId");
this.id = id; this.id = id;
this.userSessionId = userSessionId; this.userSessionId = userSessionId;
this.realmId = realmId; this.realmId = realmId;
@ -75,6 +68,13 @@ public class MapAuthenticatedClientSessionEntity implements AbstractEntity, Upda
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;

View file

@ -69,14 +69,9 @@ public class MapUserSessionEntity implements AbstractEntity, UpdatableEntity {
private boolean offline; private boolean offline;
public MapUserSessionEntity() { public MapUserSessionEntity() {}
this.id = null;
this.realmId = null;
}
public MapUserSessionEntity(String id, String realmId) { public MapUserSessionEntity(String id, String realmId) {
Objects.requireNonNull(realmId, "realmId");
this.id = id; this.id = id;
this.realmId = realmId; this.realmId = realmId;
} }
@ -103,6 +98,13 @@ public class MapUserSessionEntity implements AbstractEntity, UpdatableEntity {
return this.id; return this.id;
} }
@Override
public void setId(String id) {
if (this.id != null) throw new IllegalStateException("Id cannot be changed");
this.id = id;
this.updated |= id != null;
}
@Override @Override
public boolean isUpdated() { public boolean isUpdated() {
return this.updated; return this.updated;