KEYCLOAK-15572 make attributes multi-valued for map storage entities
This commit is contained in:
parent
2b9b50d50a
commit
4db2c3f570
7 changed files with 71 additions and 45 deletions
|
@ -23,10 +23,13 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
|
@ -246,7 +249,7 @@ public abstract class MapClientAdapter<K> extends AbstractClientModel<MapClientE
|
|||
return;
|
||||
}
|
||||
|
||||
entity.setAttribute(name, value);
|
||||
entity.setAttribute(name, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -256,12 +259,19 @@ public abstract class MapClientAdapter<K> extends AbstractClientModel<MapClientE
|
|||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return entity.getAttribute(name);
|
||||
List<String> attribute = entity.getAttribute(name);
|
||||
if (attribute.isEmpty()) return null;
|
||||
return attribute.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
return entity.getAttributes();
|
||||
return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
|
||||
entry -> {
|
||||
if (entry.getValue().isEmpty()) return null;
|
||||
return entry.getValue().get(0);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,9 +19,11 @@ package org.keycloak.models.map.client;
|
|||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
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;
|
||||
|
@ -49,7 +51,7 @@ public class MapClientEntity<K> implements AbstractEntity<K> {
|
|||
private String secret;
|
||||
private String registrationToken;
|
||||
private String protocol;
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
private Map<String, List<String>> attributes = new HashMap<>();
|
||||
private Map<String, String> authFlowBindings = new HashMap<>();
|
||||
private boolean publicClient;
|
||||
private boolean fullScopeAllowed;
|
||||
|
@ -190,13 +192,12 @@ public class MapClientEntity<K> implements AbstractEntity<K> {
|
|||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes = attributes;
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
|
||||
}
|
||||
|
||||
public Map<String, String> getAuthFlowBindings() {
|
||||
|
@ -411,17 +412,12 @@ public class MapClientEntity<K> implements AbstractEntity<K> {
|
|||
updated |= this.redirectUris.remove(redirectUri);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public String getAuthenticationFlowBindingOverride(String binding) {
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
*/
|
||||
package org.keycloak.models.map.clientscope;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -66,7 +69,7 @@ public abstract class MapClientScopeAdapter<K> extends AbstractClientScopeModel<
|
|||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) {
|
||||
entity.setAttribute(name, value);
|
||||
entity.setAttribute(name, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,12 +79,19 @@ public abstract class MapClientScopeAdapter<K> extends AbstractClientScopeModel<
|
|||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return entity.getAttribute(name);
|
||||
List<String> attribute = entity.getAttribute(name);
|
||||
if (attribute.isEmpty()) return null;
|
||||
return attribute.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
return entity.getAttributes();
|
||||
return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
|
||||
entry -> {
|
||||
if (entry.getValue().isEmpty()) return null;
|
||||
return entry.getValue().get(0);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
package org.keycloak.models.map.clientscope;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -39,7 +41,7 @@ public class MapClientScopeEntity<K> implements AbstractEntity<K> {
|
|||
|
||||
private final Set<String> scopeMappings = new LinkedHashSet<>();
|
||||
private final Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
private final Map<String, List<String>> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Flag signalizing that any of the setters has been meaningfully used.
|
||||
|
@ -96,14 +98,12 @@ public class MapClientScopeEntity<K> implements AbstractEntity<K> {
|
|||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.updated |= ! Objects.equals(this.attributes, attributes);
|
||||
this.attributes.clear();
|
||||
this.attributes.putAll(attributes);
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
|
||||
}
|
||||
|
||||
public ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model) {
|
||||
|
@ -136,17 +136,12 @@ public class MapClientScopeEntity<K> implements AbstractEntity<K> {
|
|||
return id == null ? null : protocolMappers.get(id);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated = true;
|
||||
this.attributes.put(name, value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return this.attributes.get(name);
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.keycloak.models.map.realm;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import static java.util.Objects.nonNull;
|
||||
|
@ -181,7 +182,7 @@ public abstract class MapRealmAdapter<K> extends AbstractRealmModel<MapRealmEnti
|
|||
|
||||
@Override
|
||||
public void setAttribute(String name, String value) {
|
||||
entity.setAttribute(name, value);
|
||||
entity.setAttribute(name, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -191,12 +192,19 @@ public abstract class MapRealmAdapter<K> extends AbstractRealmModel<MapRealmEnti
|
|||
|
||||
@Override
|
||||
public String getAttribute(String name) {
|
||||
return entity.getAttribute(name);
|
||||
List<String> attribute = entity.getAttribute(name);
|
||||
if (attribute.isEmpty()) return null;
|
||||
return attribute.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getAttributes() {
|
||||
return entity.getAttributes();
|
||||
return entity.getAttributes().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
|
||||
entry -> {
|
||||
if (entry.getValue().isEmpty()) return null;
|
||||
return entry.getValue().get(0);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -435,11 +443,11 @@ public abstract class MapRealmAdapter<K> extends AbstractRealmModel<MapRealmEnti
|
|||
public Map<String, Integer> getUserActionTokenLifespans() {
|
||||
Map<String, Integer> tokenLifespans = entity.getAttributes().entrySet().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(entry -> nonNull(entry.getValue()))
|
||||
.filter(entry -> nonNull(entry.getValue()) && ! entry.getValue().isEmpty())
|
||||
.filter(entry -> entry.getKey().startsWith(ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN + "."))
|
||||
.collect(Collectors.toMap(
|
||||
entry -> entry.getKey().substring(ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN.length() + 1),
|
||||
entry -> Integer.valueOf(entry.getValue())));
|
||||
entry -> Integer.valueOf(entry.getValue().get(0))));
|
||||
|
||||
return Collections.unmodifiableMap(tokenLifespans);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
@ -111,7 +112,7 @@ public class MapRealmEntity<K> implements AbstractEntity<K> {
|
|||
private final Set<String> defaultGroupIds = new HashSet<>();
|
||||
private final Set<String> defaultClientScopes = new HashSet<>();
|
||||
private final Set<String> optionalClientScopes = new HashSet<>();
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
private final Map<String, List<String>> attributes = new HashMap<>();
|
||||
private final Map<String, Map<String, String>> localizationTexts = new HashMap<>();
|
||||
private final Map<String, MapClientInitialAccessEntity> clientInitialAccesses = new HashMap<>();
|
||||
private final Map<String, MapComponentEntity> components = new HashMap<>();
|
||||
|
@ -665,19 +666,19 @@ public class MapRealmEntity<K> implements AbstractEntity<K> {
|
|||
this.webAuthnPolicyPasswordless = webAuthnPolicyPasswordless;
|
||||
}
|
||||
|
||||
public void setAttribute(String name, String value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
this.updated |= ! Objects.equals(this.attributes.put(name, values), values);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.getOrDefault(name, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
|||
import org.keycloak.storage.StorageId;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
@ -299,10 +300,15 @@ public class MapFieldPredicates {
|
|||
throw new CriterionNotSupportedException(ClientModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (String attribute_name), got: " + Arrays.toString(values));
|
||||
}
|
||||
String attrNameS = (String) attrName;
|
||||
Function<MapClientEntity<Object>, ?> getter = ue -> ue.getAttribute(attrNameS);
|
||||
Object[] realValue = {values[1]};
|
||||
Object[] realValues = new Object[values.length - 1];
|
||||
System.arraycopy(values, 1, realValues, 0, values.length - 1);
|
||||
Predicate<Object> valueComparator = CriteriaOperator.predicateFor(op, realValues);
|
||||
Function<MapClientEntity<Object>, ?> getter = ue -> {
|
||||
final List<String> attrs = ue.getAttribute(attrNameS);
|
||||
return attrs != null && attrs.stream().anyMatch(valueComparator);
|
||||
};
|
||||
|
||||
return mcb.fieldCompare(op, getter, realValue);
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> checkGrantedUserRole(MapModelCriteriaBuilder<Object, MapUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
|
|
Loading…
Reference in a new issue