Skip to content

Commit

Permalink
Cache role mappings on last successful load for stateless
Browse files Browse the repository at this point in the history
  • Loading branch information
n1v0lg committed Nov 3, 2023
1 parent 8c57de7 commit e546aba
Showing 1 changed file with 42 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.ContextPreservingActionListener;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
Expand Down Expand Up @@ -52,6 +53,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -92,12 +94,25 @@ public class NativeRoleMappingStore implements UserRoleMapper {
private final SecurityIndexManager securityIndex;
private final ScriptService scriptService;
private final List<String> realmsToRefresh = new CopyOnWriteArrayList<>();
private final boolean shouldCacheSuccessfulLoad;
private final AtomicReference<List<ExpressionRoleMapping>> lastSuccessfulLoadRef = new AtomicReference<>(null);

public NativeRoleMappingStore(Settings settings, Client client, SecurityIndexManager securityIndex, ScriptService scriptService) {
this(settings, client, securityIndex, scriptService, DiscoveryNode.isStateless(settings));
}

NativeRoleMappingStore(
Settings settings,
Client client,
SecurityIndexManager securityIndex,
ScriptService scriptService,
boolean shouldCacheSuccessfulLoad
) {
this.settings = settings;
this.client = client;
this.securityIndex = securityIndex;
this.scriptService = scriptService;
this.shouldCacheSuccessfulLoad = shouldCacheSuccessfulLoad;
}

private static String getNameFromId(String id) {
Expand Down Expand Up @@ -139,6 +154,10 @@ protected void loadMappings(ActionListener<List<ExpressionRoleMapping>> listener
new ContextPreservingActionListener<>(supplier, ActionListener.wrap((Collection<ExpressionRoleMapping> mappings) -> {
final List<ExpressionRoleMapping> mappingList = mappings.stream().filter(Objects::nonNull).toList();
logger.debug("successfully loaded [{}] role-mapping(s) from [{}]", mappingList.size(), securityIndex.aliasName());
if (shouldCacheSuccessfulLoad) {
logger.debug("caching loaded role-mapping(s)");
lastSuccessfulLoadRef.set(mappingList);
}
listener.onResponse(mappingList);
}, ex -> {
logger.error(
Expand Down Expand Up @@ -294,14 +313,29 @@ public void getRoleMappings(Set<String> names, ActionListener<List<ExpressionRol
private void getMappings(ActionListener<List<ExpressionRoleMapping>> listener) {
final SecurityIndexManager frozenSecurityIndex = securityIndex.defensiveCopy();
if (frozenSecurityIndex.indexExists() == false) {
logger.debug("The security does not index exist - no role mappings can be loaded");
listener.onResponse(Collections.emptyList());
} else if (frozenSecurityIndex.indexIsClosed()) {
logger.debug("The security index exists but is closed - no role mappings can be loaded");
logger.debug("The security index does not exist - no role mappings can be loaded");
listener.onResponse(Collections.emptyList());
return;
}
final List<ExpressionRoleMapping> lastSuccessfulLoad = lastSuccessfulLoadRef.get();
if (frozenSecurityIndex.indexIsClosed()) {
if (lastSuccessfulLoad != null) {
assert shouldCacheSuccessfulLoad;
logger.debug("The security index exists but is closed - returning previously cached role mappings");
listener.onResponse(lastSuccessfulLoad);
} else {
logger.debug("The security index exists but is closed - no role mappings can be loaded");
listener.onResponse(Collections.emptyList());
}
} else if (frozenSecurityIndex.isAvailable(SEARCH_SHARDS) == false) {
logger.debug("The security index exists but is not available - no role mappings can be loaded");
listener.onFailure(frozenSecurityIndex.getUnavailableReason(SEARCH_SHARDS));
if (lastSuccessfulLoad != null) {
assert shouldCacheSuccessfulLoad;
logger.debug("The security index exists but is not available - returning previously cached role mappings");
listener.onResponse(lastSuccessfulLoad);
} else {
logger.debug("The security index exists but is not available - no role mappings can be loaded");
listener.onFailure(frozenSecurityIndex.getUnavailableReason(SEARCH_SHARDS));
}
} else {
loadMappings(listener);
}
Expand All @@ -317,7 +351,7 @@ private void getMappings(ActionListener<List<ExpressionRoleMapping>> listener) {
* </ul>
*/
public void usageStats(ActionListener<Map<String, Object>> listener) {
if (securityIndex.isAvailable(SEARCH_SHARDS) == false) {
if (securityIndex.indexIsClosed() || securityIndex.isAvailable(SEARCH_SHARDS) == false) {
reportStats(listener, Collections.emptyList());
} else {
getMappings(ActionListener.wrap(mappings -> reportStats(listener, mappings), listener::onFailure));
Expand All @@ -337,6 +371,7 @@ public void onSecurityIndexStateChange(SecurityIndexManager.State previousState,
|| Objects.equals(previousState.indexUUID, currentState.indexUUID) == false
|| previousState.isIndexUpToDate != currentState.isIndexUpToDate) {
refreshRealms(ActionListener.noop(), null);
lastSuccessfulLoadRef.set(null);
}
}

Expand Down

0 comments on commit e546aba

Please sign in to comment.