Skip to content

Commit a28ce1e

Browse files
authored
Improve role cache efficiency for API key roles (#58156)
This PR ensure that same roles are cached only once even when they are from different API keys. API key role descriptors and limited role descriptors are now saved in Authentication#metadata as raw bytes instead of deserialised Map<String, Object>. Hashes of these bytes are used as keys for API key roles. Only when the required role is not found in the cache, they will be deserialised to build the RoleDescriptors. The deserialisation is directly from raw bytes to RoleDescriptors without going through the current detour of "bytes -> Map -> bytes -> RoleDescriptors".
1 parent 5273681 commit a28ce1e

File tree

12 files changed

+606
-114
lines changed

12 files changed

+606
-114
lines changed

libs/x-content/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ public void declareLong(BiConsumer<Value, Long> consumer, ParseField field) {
210210
declareField(consumer, p -> p.longValue(), field, ValueType.LONG);
211211
}
212212

213+
public void declareLongOrNull(BiConsumer<Value, Long> consumer, long nullValue, ParseField field) {
214+
// Using a method reference here angers some compilers
215+
declareField(consumer, p -> p.currentToken() == XContentParser.Token.VALUE_NULL ? nullValue : p.longValue(),
216+
field, ValueType.LONG_OR_NULL);
217+
}
218+
213219
public void declareInt(BiConsumer<Value, Integer> consumer, ParseField field) {
214220
// Using a method reference here angers some compilers
215221
declareField(consumer, p -> p.intValue(), field, ValueType.INT);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityContext.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import org.elasticsearch.ElasticsearchSecurityException;
1111
import org.elasticsearch.Version;
1212
import org.elasticsearch.common.Nullable;
13+
import org.elasticsearch.common.bytes.BytesReference;
1314
import org.elasticsearch.common.settings.Settings;
1415
import org.elasticsearch.common.util.concurrent.ThreadContext;
1516
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
17+
import org.elasticsearch.common.xcontent.XContentHelper;
18+
import org.elasticsearch.common.xcontent.XContentType;
1619
import org.elasticsearch.node.Node;
1720
import org.elasticsearch.xpack.core.security.authc.Authentication;
1821
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
@@ -23,14 +26,21 @@
2326
import java.io.IOException;
2427
import java.io.UncheckedIOException;
2528
import java.util.Collections;
29+
import java.util.HashMap;
30+
import java.util.Map;
2631
import java.util.Objects;
2732
import java.util.function.Consumer;
2833
import java.util.function.Function;
2934

35+
import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES;
36+
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY;
37+
import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY;
38+
3039
/**
3140
* A lightweight utility that can find the current user and authentication information for the local thread.
3241
*/
3342
public class SecurityContext {
43+
3444
private final Logger logger = LogManager.getLogger(SecurityContext.class);
3545

3646
private final ThreadContext threadContext;
@@ -149,8 +159,27 @@ public void executeAfterRewritingAuthentication(Consumer<StoredContext> consumer
149159
final Authentication authentication = getAuthentication();
150160
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
151161
setAuthentication(new Authentication(authentication.getUser(), authentication.getAuthenticatedBy(),
152-
authentication.getLookedUpBy(), version, authentication.getAuthenticationType(), authentication.getMetadata()));
162+
authentication.getLookedUpBy(), version, authentication.getAuthenticationType(),
163+
rewriteMetadataForApiKeyRoleDescriptors(version, authentication)));
153164
consumer.accept(original);
154165
}
155166
}
167+
168+
private Map<String, Object> rewriteMetadataForApiKeyRoleDescriptors(Version streamVersion, Authentication authentication) {
169+
Map<String, Object> metadata = authentication.getMetadata();
170+
if (authentication.getAuthenticationType() == AuthenticationType.API_KEY
171+
&& authentication.getVersion().onOrAfter(VERSION_API_KEY_ROLES_AS_BYTES)
172+
&& streamVersion.before(VERSION_API_KEY_ROLES_AS_BYTES)) {
173+
metadata = new HashMap<>(metadata);
174+
metadata.put(
175+
API_KEY_ROLE_DESCRIPTORS_KEY,
176+
XContentHelper.convertToMap(
177+
(BytesReference) metadata.get(API_KEY_ROLE_DESCRIPTORS_KEY), false, XContentType.JSON).v2());
178+
metadata.put(
179+
API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY,
180+
XContentHelper.convertToMap(
181+
(BytesReference) metadata.get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY), false, XContentType.JSON).v2());
182+
}
183+
return metadata;
184+
}
156185
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
// That interface can be removed
2828
public class Authentication implements ToXContentObject {
2929

30+
public static final Version VERSION_API_KEY_ROLES_AS_BYTES = Version.V_7_9_0;
31+
3032
private final User user;
3133
private final RealmRef authenticatedBy;
3234
private final RealmRef lookedUpBy;

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
public final class AuthenticationField {
99

1010
public static final String AUTHENTICATION_KEY = "_xpack_security_authentication";
11+
public static final String API_KEY_ROLE_DESCRIPTORS_KEY = "_security_api_key_role_descriptors";
12+
public static final String API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY = "_security_api_key_limited_by_role_descriptors";
1113

1214
private AuthenticationField() {}
1315
}

0 commit comments

Comments
 (0)