From 6047d6626ec8adfb5d770d3955be5ffbcb0c907b Mon Sep 17 00:00:00 2001 From: Peter Nied Date: Wed, 17 Jan 2024 11:42:41 -0600 Subject: [PATCH] Protect config object from concurrent modification issues (#3945) Signed-off-by: Peter Nied --- .../impl/SecurityDynamicConfiguration.java | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java index 938ee23c1e..bba44b5e28 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/SecurityDynamicConfiguration.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -56,6 +57,8 @@ public class SecurityDynamicConfiguration implements ToXContent { @JsonIgnore private final Map centries = new HashMap<>(); + @JsonIgnore + private final Object modificationLock = new Object(); private long seqNo = -1; private long primaryTerm = -1; private CType ctype; @@ -158,23 +161,33 @@ void setCEntries(String key, T value) { @JsonAnyGetter public Map getCEntries() { - return centries; + synchronized (modificationLock) { + return new HashMap<>(centries); + } } @JsonIgnore public void removeHidden() { - for (Entry entry : new HashMap(centries).entrySet()) { - if (entry.getValue() instanceof Hideable && ((Hideable) entry.getValue()).isHidden()) { - centries.remove(entry.getKey()); + synchronized (modificationLock) { + final Iterator> iterator = centries.entrySet().iterator(); + while (iterator.hasNext()) { + final var entry = iterator.next(); + if (entry.getValue() instanceof Hideable && ((Hideable) entry.getValue()).isHidden()) { + iterator.remove(); + } } } } @JsonIgnore public void removeStatic() { - for (Entry entry : new HashMap(centries).entrySet()) { - if (entry.getValue() instanceof StaticDefinable && ((StaticDefinable) entry.getValue()).isStatic()) { - centries.remove(entry.getKey()); + synchronized (modificationLock) { + final Iterator> iterator = centries.entrySet().iterator(); + while (iterator.hasNext()) { + final var entry = iterator.next(); + if (entry.getValue() instanceof StaticDefinable && ((StaticDefinable) entry.getValue()).isStatic()) { + iterator.remove(); + } } } } @@ -189,20 +202,26 @@ public void clearHashes() { } public void removeOthers(String key) { - T tmp = this.centries.get(key); - this.centries.clear(); - this.centries.put(key, tmp); + synchronized (modificationLock) { + T tmp = this.centries.get(key); + this.centries.clear(); + this.centries.put(key, tmp); + } } @JsonIgnore public T putCEntry(String key, T value) { - return centries.put(key, value); + synchronized (modificationLock) { + return centries.put(key, value); + } } @JsonIgnore @SuppressWarnings("unchecked") public void putCObject(String key, Object value) { - centries.put(key, (T) value); + synchronized (modificationLock) { + centries.put(key, (T) value); + } } @JsonIgnore @@ -286,36 +305,42 @@ public SecurityDynamicConfiguration deepClone() { @JsonIgnore public void remove(String key) { - centries.remove(key); + synchronized (modificationLock) { + centries.remove(key); + } } @JsonIgnore public void remove(List keySet) { - keySet.stream().forEach(this::remove); + synchronized (modificationLock) { + keySet.stream().forEach(centries::remove); + } } @SuppressWarnings({ "rawtypes", "unchecked" }) public boolean add(SecurityDynamicConfiguration other) { - if (other.ctype == null || !other.ctype.equals(this.ctype)) { - return false; - } + synchronized (modificationLock) { + if (other.ctype == null || !other.ctype.equals(this.ctype)) { + return false; + } - if (other.getImplementingClass() == null || !other.getImplementingClass().equals(this.getImplementingClass())) { - return false; - } + if (other.getImplementingClass() == null || !other.getImplementingClass().equals(this.getImplementingClass())) { + return false; + } - if (other.version != this.version) { - return false; - } + if (other.version != this.version) { + return false; + } - this.centries.putAll(other.centries); - return true; + this.centries.putAll(other.centries); + return true; + } } @JsonIgnore @SuppressWarnings({ "rawtypes" }) public boolean containsAny(SecurityDynamicConfiguration other) { - return !Collections.disjoint(this.centries.keySet(), other.centries.keySet()); + return !Collections.disjoint(this.getCEntries().keySet(), other.getCEntries().keySet()); } public boolean isHidden(String resourceName) {