Skip to content

Commit 8d099da

Browse files
authored
Add "manage_api_key" cluster privilege (elastic#43865)
This adds a new cluster privilege for manage_api_key. Users with this privilege are able to create new API keys (as a child of their own user identity) and may also get and invalidate any/all API keys (including those owned by other users). Backport of: elastic#43728
1 parent b95ee7e commit 8d099da

File tree

3 files changed

+31
-22
lines changed

3 files changed

+31
-22
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public final class ClusterPrivilege extends Privilege {
3939
InvalidateTokenAction.NAME, RefreshTokenAction.NAME);
4040
private static final Automaton MANAGE_OIDC_AUTOMATON = patterns("cluster:admin/xpack/security/oidc/*");
4141
private static final Automaton MANAGE_TOKEN_AUTOMATON = patterns("cluster:admin/xpack/security/token/*");
42+
private static final Automaton MANAGE_API_KEY_AUTOMATON = patterns("cluster:admin/xpack/security/api_key/*");
4243
private static final Automaton MONITOR_AUTOMATON = patterns("cluster:monitor/*");
4344
private static final Automaton MONITOR_ML_AUTOMATON = patterns("cluster:monitor/xpack/ml/*");
4445
private static final Automaton MONITOR_DATA_FRAME_AUTOMATON = patterns("cluster:monitor/data_frame/*");
@@ -84,6 +85,7 @@ public final class ClusterPrivilege extends Privilege {
8485
public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security", MANAGE_SECURITY_AUTOMATON);
8586
public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml", MANAGE_SAML_AUTOMATON);
8687
public static final ClusterPrivilege MANAGE_OIDC = new ClusterPrivilege("manage_oidc", MANAGE_OIDC_AUTOMATON);
88+
public static final ClusterPrivilege MANAGE_API_KEY = new ClusterPrivilege("manage_api_key", MANAGE_API_KEY_AUTOMATON);
8789
public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*");
8890
public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON);
8991
public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON);
@@ -112,6 +114,7 @@ public final class ClusterPrivilege extends Privilege {
112114
.put("manage_security", MANAGE_SECURITY)
113115
.put("manage_saml", MANAGE_SAML)
114116
.put("manage_oidc", MANAGE_OIDC)
117+
.put("manage_api_key", MANAGE_API_KEY)
115118
.put("manage_pipeline", MANAGE_PIPELINE)
116119
.put("manage_rollup", MANAGE_ROLLUP)
117120
.put("manage_ccr", MANAGE_CCR)

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

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -749,27 +749,29 @@ private void findApiKeys(final BoolQueryBuilder boolQuery, boolean filterOutInva
749749
expiredQuery.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("expiration_time")));
750750
boolQuery.filter(expiredQuery);
751751
}
752-
final SearchRequest request = client.prepareSearch(SECURITY_MAIN_ALIAS)
753-
.setScroll(DEFAULT_KEEPALIVE_SETTING.get(settings))
754-
.setQuery(boolQuery)
755-
.setVersion(false)
756-
.setSize(1000)
757-
.setFetchSource(true)
758-
.request();
759-
securityIndex.checkIndexVersionThenExecute(listener::onFailure,
760-
() -> ScrollHelper.fetchAllByEntity(client, request, listener,
761-
(SearchHit hit) -> {
762-
Map<String, Object> source = hit.getSourceAsMap();
763-
String name = (String) source.get("name");
764-
String id = hit.getId();
765-
Long creation = (Long) source.get("creation_time");
766-
Long expiration = (Long) source.get("expiration_time");
767-
Boolean invalidated = (Boolean) source.get("api_key_invalidated");
768-
String username = (String) ((Map<String, Object>) source.get("creator")).get("principal");
769-
String realm = (String) ((Map<String, Object>) source.get("creator")).get("realm");
770-
return new ApiKey(name, id, Instant.ofEpochMilli(creation),
771-
(expiration != null) ? Instant.ofEpochMilli(expiration) : null, invalidated, username, realm);
772-
}));
752+
try (ThreadContext.StoredContext ignore = client.threadPool().getThreadContext().stashWithOrigin(SECURITY_ORIGIN)) {
753+
final SearchRequest request = client.prepareSearch(SECURITY_MAIN_ALIAS)
754+
.setScroll(DEFAULT_KEEPALIVE_SETTING.get(settings))
755+
.setQuery(boolQuery)
756+
.setVersion(false)
757+
.setSize(1000)
758+
.setFetchSource(true)
759+
.request();
760+
securityIndex.checkIndexVersionThenExecute(listener::onFailure,
761+
() -> ScrollHelper.fetchAllByEntity(client, request, listener,
762+
(SearchHit hit) -> {
763+
Map<String, Object> source = hit.getSourceAsMap();
764+
String name = (String) source.get("name");
765+
String id = hit.getId();
766+
Long creation = (Long) source.get("creation_time");
767+
Long expiration = (Long) source.get("expiration_time");
768+
Boolean invalidated = (Boolean) source.get("api_key_invalidated");
769+
String username = (String) ((Map<String, Object>) source.get("creator")).get("principal");
770+
String realm = (String) ((Map<String, Object>) source.get("creator")).get("realm");
771+
return new ApiKey(name, id, Instant.ofEpochMilli(creation),
772+
(expiration != null) ? Instant.ofEpochMilli(expiration) : null, invalidated, username, realm);
773+
}));
774+
}
773775
}
774776

775777
private void findApiKeyForApiKeyName(String apiKeyName, boolean filterOutInvalidatedKeys, boolean filterOutExpiredKeys,

x-pack/plugin/src/test/resources/rest-api-spec/test/api_key/10_basic.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ setup:
1212
name: "admin_role"
1313
body: >
1414
{
15-
"cluster": ["all"],
15+
"cluster": ["manage_api_key"],
1616
"indices": [
1717
{
1818
"names": "*",
@@ -166,6 +166,8 @@ teardown:
166166
- set: { name: api_key_name }
167167

168168
- do:
169+
headers:
170+
Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
169171
security.get_api_key:
170172
id: "$api_key_id"
171173
- match: { "api_keys.0.id": "$api_key_id" }
@@ -198,6 +200,8 @@ teardown:
198200
- transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
199201

200202
- do:
203+
headers:
204+
Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
201205
security.invalidate_api_key:
202206
body: >
203207
{

0 commit comments

Comments
 (0)