Skip to content

Commit 5822aa2

Browse files
authored
Add validation for API key role descriptors (#82049)
Put Role API prevents creation of invalidate role descriptors by validating that the given cluster privileges and index previleges can be resolved. However, the same validation is not performed when creating API keys. As a result, users are able to create invalidate API keys which then fail at use time. The experience is not user friendly and inconsistent. This PR fixes it by adding the same validation logic for API key creation. Resolves: #67311
1 parent 3531172 commit 5822aa2

File tree

4 files changed

+118
-54
lines changed

4 files changed

+118
-54
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.elasticsearch.common.io.stream.StreamOutput;
1818
import org.elasticsearch.core.Nullable;
1919
import org.elasticsearch.core.TimeValue;
20+
import org.elasticsearch.xpack.core.security.action.role.RoleDescriptorRequestValidator;
2021
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
2122
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
2223

@@ -159,10 +160,13 @@ public ActionRequestValidationException validate() {
159160
}
160161
if (metadata != null && MetadataUtils.containsReservedMetadata(metadata)) {
161162
validationException = addValidationError(
162-
"metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]",
163+
"API key metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]",
163164
validationException
164165
);
165166
}
167+
for (RoleDescriptor roleDescriptor : roleDescriptors) {
168+
validationException = RoleDescriptorRequestValidator.validate(roleDescriptor, validationException);
169+
}
166170
return validationException;
167171
}
168172

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/role/PutRoleRequest.java

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,15 @@
1515
import org.elasticsearch.common.io.stream.StreamOutput;
1616
import org.elasticsearch.core.Nullable;
1717
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
18-
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
19-
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
2018
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
2119
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
22-
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
23-
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
2420

2521
import java.io.IOException;
2622
import java.util.ArrayList;
2723
import java.util.Arrays;
2824
import java.util.Collections;
2925
import java.util.List;
3026
import java.util.Map;
31-
import java.util.Set;
32-
33-
import static org.elasticsearch.action.ValidateActions.addValidationError;
3427

3528
/**
3629
* Request object for adding a role to the security index
@@ -66,51 +59,7 @@ public PutRoleRequest() {}
6659

6760
@Override
6861
public ActionRequestValidationException validate() {
69-
ActionRequestValidationException validationException = null;
70-
if (name == null) {
71-
validationException = addValidationError("role name is missing", validationException);
72-
}
73-
if (clusterPrivileges != null) {
74-
for (String cp : clusterPrivileges) {
75-
try {
76-
ClusterPrivilegeResolver.resolve(cp);
77-
} catch (IllegalArgumentException ile) {
78-
validationException = addValidationError(ile.getMessage(), validationException);
79-
}
80-
}
81-
}
82-
if (indicesPrivileges != null) {
83-
for (RoleDescriptor.IndicesPrivileges idp : indicesPrivileges) {
84-
try {
85-
IndexPrivilege.get(Set.of(idp.getPrivileges()));
86-
} catch (IllegalArgumentException ile) {
87-
validationException = addValidationError(ile.getMessage(), validationException);
88-
}
89-
}
90-
}
91-
if (applicationPrivileges != null) {
92-
for (RoleDescriptor.ApplicationResourcePrivileges privilege : applicationPrivileges) {
93-
try {
94-
ApplicationPrivilege.validateApplicationNameOrWildcard(privilege.getApplication());
95-
} catch (IllegalArgumentException e) {
96-
validationException = addValidationError(e.getMessage(), validationException);
97-
}
98-
for (String privilegeName : privilege.getPrivileges()) {
99-
try {
100-
ApplicationPrivilege.validatePrivilegeOrActionName(privilegeName);
101-
} catch (IllegalArgumentException e) {
102-
validationException = addValidationError(e.getMessage(), validationException);
103-
}
104-
}
105-
}
106-
}
107-
if (metadata != null && MetadataUtils.containsReservedMetadata(metadata)) {
108-
validationException = addValidationError(
109-
"metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]",
110-
validationException
111-
);
112-
}
113-
return validationException;
62+
return RoleDescriptorRequestValidator.validate(roleDescriptor());
11463
}
11564

11665
public void name(String name) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.core.security.action.role;
9+
10+
import org.elasticsearch.action.ActionRequestValidationException;
11+
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
12+
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
13+
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
14+
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
15+
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
16+
17+
import java.util.Set;
18+
19+
import static org.elasticsearch.action.ValidateActions.addValidationError;
20+
21+
public class RoleDescriptorRequestValidator {
22+
23+
private RoleDescriptorRequestValidator() {}
24+
25+
public static ActionRequestValidationException validate(RoleDescriptor roleDescriptor) {
26+
return validate(roleDescriptor, null);
27+
}
28+
29+
public static ActionRequestValidationException validate(
30+
RoleDescriptor roleDescriptor,
31+
ActionRequestValidationException validationException
32+
) {
33+
if (roleDescriptor.getName() == null) {
34+
validationException = addValidationError("role name is missing", validationException);
35+
}
36+
if (roleDescriptor.getClusterPrivileges() != null) {
37+
for (String cp : roleDescriptor.getClusterPrivileges()) {
38+
try {
39+
ClusterPrivilegeResolver.resolve(cp);
40+
} catch (IllegalArgumentException ile) {
41+
validationException = addValidationError(ile.getMessage(), validationException);
42+
}
43+
}
44+
}
45+
if (roleDescriptor.getIndicesPrivileges() != null) {
46+
for (RoleDescriptor.IndicesPrivileges idp : roleDescriptor.getIndicesPrivileges()) {
47+
try {
48+
IndexPrivilege.get(Set.of(idp.getPrivileges()));
49+
} catch (IllegalArgumentException ile) {
50+
validationException = addValidationError(ile.getMessage(), validationException);
51+
}
52+
}
53+
}
54+
if (roleDescriptor.getApplicationPrivileges() != null) {
55+
for (RoleDescriptor.ApplicationResourcePrivileges privilege : roleDescriptor.getApplicationPrivileges()) {
56+
try {
57+
ApplicationPrivilege.validateApplicationNameOrWildcard(privilege.getApplication());
58+
} catch (IllegalArgumentException e) {
59+
validationException = addValidationError(e.getMessage(), validationException);
60+
}
61+
for (String privilegeName : privilege.getPrivileges()) {
62+
try {
63+
ApplicationPrivilege.validatePrivilegeOrActionName(privilegeName);
64+
} catch (IllegalArgumentException e) {
65+
validationException = addValidationError(e.getMessage(), validationException);
66+
}
67+
}
68+
}
69+
}
70+
if (roleDescriptor.getMetadata() != null && MetadataUtils.containsReservedMetadata(roleDescriptor.getMetadata())) {
71+
validationException = addValidationError(
72+
"role descriptor metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]",
73+
validationException
74+
);
75+
}
76+
return validationException;
77+
}
78+
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/CreateApiKeyRequestTests.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323

2424
import static org.hamcrest.Matchers.containsString;
25+
import static org.hamcrest.Matchers.containsStringIgnoringCase;
2526
import static org.hamcrest.Matchers.equalTo;
2627
import static org.hamcrest.Matchers.is;
2728

@@ -82,7 +83,39 @@ public void testMetadataKeyValidation() {
8283
final ActionRequestValidationException ve = request.validate();
8384
assertNotNull(ve);
8485
assertThat(ve.validationErrors().size(), equalTo(1));
85-
assertThat(ve.validationErrors().get(0), containsString("metadata keys may not start with [_]"));
86+
assertThat(ve.validationErrors().get(0), containsString("API key metadata keys may not start with [_]"));
87+
}
88+
89+
public void testRoleDescriptorValidation() {
90+
final CreateApiKeyRequest request1 = new CreateApiKeyRequest(
91+
randomAlphaOfLength(5),
92+
List.of(
93+
new RoleDescriptor(
94+
randomAlphaOfLength(5),
95+
new String[] { "manage_index_template" },
96+
new RoleDescriptor.IndicesPrivileges[] {
97+
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("rad").build() },
98+
new RoleDescriptor.ApplicationResourcePrivileges[] {
99+
RoleDescriptor.ApplicationResourcePrivileges.builder()
100+
.application(randomFrom("app*tab", "app 1"))
101+
.privileges(randomFrom(" ", "\n"))
102+
.resources("resource")
103+
.build() },
104+
null,
105+
null,
106+
Map.of("_key", "value"),
107+
null
108+
)
109+
),
110+
null
111+
);
112+
final ActionRequestValidationException ve1 = request1.validate();
113+
assertNotNull(ve1);
114+
assertThat(ve1.validationErrors().get(0), containsString("unknown cluster privilege"));
115+
assertThat(ve1.validationErrors().get(1), containsString("unknown index privilege"));
116+
assertThat(ve1.validationErrors().get(2), containsStringIgnoringCase("application name"));
117+
assertThat(ve1.validationErrors().get(3), containsStringIgnoringCase("Application privilege names"));
118+
assertThat(ve1.validationErrors().get(4), containsStringIgnoringCase("role descriptor metadata keys may not start with "));
86119
}
87120

88121
public void testSerialization() throws IOException {

0 commit comments

Comments
 (0)