Skip to content

Commit 54aaada

Browse files
authored
API key name should always be required for creation (#59836) (#60636)
The name is now required when creating or granting API keys.
1 parent c58e32b commit 54aaada

File tree

5 files changed

+48
-13
lines changed

5 files changed

+48
-13
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/security/CreateApiKeyRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public final class CreateApiKeyRequest implements Validatable, ToXContentObject
4646
* @param roles list of {@link Role}s
4747
* @param expiration to specify expiration for the API key
4848
*/
49-
public CreateApiKeyRequest(@Nullable String name, List<Role> roles, @Nullable TimeValue expiration,
49+
public CreateApiKeyRequest(String name, List<Role> roles, @Nullable TimeValue expiration,
5050
@Nullable final RefreshPolicy refreshPolicy) {
5151
this.name = name;
5252
this.roles = Objects.requireNonNull(roles, "roles may not be null");

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.action.ActionRequestValidationException;
1212
import org.elasticsearch.action.support.WriteRequest;
1313
import org.elasticsearch.common.Nullable;
14+
import org.elasticsearch.common.Strings;
1415
import org.elasticsearch.common.io.stream.StreamInput;
1516
import org.elasticsearch.common.io.stream.StreamOutput;
1617
import org.elasticsearch.common.unit.TimeValue;
@@ -43,7 +44,7 @@ public CreateApiKeyRequest() {}
4344
* @param roleDescriptors list of {@link RoleDescriptor}s
4445
* @param expiration to specify expiration for the API key
4546
*/
46-
public CreateApiKeyRequest(@Nullable String name, @Nullable List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
47+
public CreateApiKeyRequest(String name, @Nullable List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
4748
this.name = name;
4849
this.roleDescriptors = (roleDescriptors == null) ? Collections.emptyList() : Collections.unmodifiableList(roleDescriptors);
4950
this.expiration = expiration;
@@ -65,7 +66,7 @@ public String getName() {
6566
return name;
6667
}
6768

68-
public void setName(@Nullable String name) {
69+
public void setName(String name) {
6970
this.name = name;
7071
}
7172

@@ -96,15 +97,17 @@ public void setRefreshPolicy(WriteRequest.RefreshPolicy refreshPolicy) {
9697
@Override
9798
public ActionRequestValidationException validate() {
9899
ActionRequestValidationException validationException = null;
99-
if (name != null) {
100+
if (Strings.isNullOrEmpty(name)) {
101+
validationException = addValidationError("api key name is required", validationException);
102+
} else {
100103
if (name.length() > 256) {
101-
validationException = addValidationError("name may not be more than 256 characters long", validationException);
104+
validationException = addValidationError("api key name may not be more than 256 characters long", validationException);
102105
}
103106
if (name.equals(name.trim()) == false) {
104-
validationException = addValidationError("name may not begin or end with whitespace", validationException);
107+
validationException = addValidationError("api key name may not begin or end with whitespace", validationException);
105108
}
106109
if (name.startsWith("_")) {
107-
validationException = addValidationError("name may not begin with an underscore", validationException);
110+
validationException = addValidationError("api key name may not begin with an underscore", validationException);
108111
}
109112
}
110113
return validationException;

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public void testNameValidation() {
2929
CreateApiKeyRequest request = new CreateApiKeyRequest();
3030

3131
ActionRequestValidationException ve = request.validate();
32-
assertNull(ve);
32+
assertThat(ve.validationErrors().size(), is(1));
33+
assertThat(ve.validationErrors().get(0), containsString("api key name is required"));
3334

3435
request.setName(name);
3536
ve = request.validate();
@@ -39,25 +40,25 @@ public void testNameValidation() {
3940
ve = request.validate();
4041
assertNotNull(ve);
4142
assertThat(ve.validationErrors().size(), is(1));
42-
assertThat(ve.validationErrors().get(0), containsString("name may not be more than 256 characters long"));
43+
assertThat(ve.validationErrors().get(0), containsString("api key name may not be more than 256 characters long"));
4344

4445
request.setName(" leading space");
4546
ve = request.validate();
4647
assertNotNull(ve);
4748
assertThat(ve.validationErrors().size(), is(1));
48-
assertThat(ve.validationErrors().get(0), containsString("name may not begin or end with whitespace"));
49+
assertThat(ve.validationErrors().get(0), containsString("api key name may not begin or end with whitespace"));
4950

5051
request.setName("trailing space ");
5152
ve = request.validate();
5253
assertNotNull(ve);
5354
assertThat(ve.validationErrors().size(), is(1));
54-
assertThat(ve.validationErrors().get(0), containsString("name may not begin or end with whitespace"));
55+
assertThat(ve.validationErrors().get(0), containsString("api key name may not begin or end with whitespace"));
5556

5657
request.setName(" leading and trailing space ");
5758
ve = request.validate();
5859
assertNotNull(ve);
5960
assertThat(ve.validationErrors().size(), is(1));
60-
assertThat(ve.validationErrors().get(0), containsString("name may not begin or end with whitespace"));
61+
assertThat(ve.validationErrors().get(0), containsString("api key name may not begin or end with whitespace"));
6162

6263
request.setName("inner space");
6364
ve = request.validate();
@@ -67,7 +68,7 @@ public void testNameValidation() {
6768
ve = request.validate();
6869
assertNotNull(ve);
6970
assertThat(ve.validationErrors().size(), is(1));
70-
assertThat(ve.validationErrors().get(0), containsString("name may not begin with an underscore"));
71+
assertThat(ve.validationErrors().get(0), containsString("api key name may not begin with an underscore"));
7172
}
7273

7374
public void testSerialization() throws IOException {

x-pack/plugin/security/qa/security-trial/src/test/java/org/elasticsearch/xpack/security/apikey/ApiKeyRestIT.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.elasticsearch.client.Request;
1010
import org.elasticsearch.client.RequestOptions;
1111
import org.elasticsearch.client.Response;
12+
import org.elasticsearch.client.ResponseException;
1213
import org.elasticsearch.client.security.support.ApiKey;
1314
import org.elasticsearch.common.collect.MapBuilder;
1415
import org.elasticsearch.common.collect.Tuple;
@@ -27,6 +28,7 @@
2728
import java.util.HashMap;
2829
import java.util.Map;
2930

31+
import static org.hamcrest.Matchers.containsString;
3032
import static org.hamcrest.Matchers.equalTo;
3133
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
3234
import static org.hamcrest.Matchers.instanceOf;
@@ -114,5 +116,23 @@ public void testGrantApiKeyForOtherUserWithAccessToken() throws IOException {
114116
assertThat(apiKey.getExpiration(), greaterThanOrEqualTo(minExpiry));
115117
assertThat(apiKey.getExpiration(), lessThanOrEqualTo(maxExpiry));
116118
}
119+
120+
public void testGrantApiKeyWithoutApiKeyNameWillFail() throws IOException {
121+
Request request = new Request("POST", "_security/api_key/grant");
122+
request.setOptions(RequestOptions.DEFAULT.toBuilder().addHeader("Authorization",
123+
UsernamePasswordToken.basicAuthHeaderValue(SYSTEM_USER, SYSTEM_USER_PASSWORD)));
124+
final Map<String, Object> requestBody = org.elasticsearch.common.collect.Map.ofEntries(
125+
org.elasticsearch.common.collect.Map.entry("grant_type", "password"),
126+
org.elasticsearch.common.collect.Map.entry("username", END_USER),
127+
org.elasticsearch.common.collect.Map.entry("password", END_USER_PASSWORD.toString())
128+
);
129+
request.setJsonEntity(XContentTestUtils.convertToXContent(requestBody, XContentType.JSON).utf8ToString());
130+
131+
final ResponseException e =
132+
expectThrows(ResponseException.class, () -> client().performRequest(request));
133+
134+
assertEquals(400, e.getResponse().getStatusLine().getStatusCode());
135+
assertThat(e.getMessage(), containsString("api key name is required"));
136+
}
117137
}
118138

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.xpack.security.authc;
88

99
import org.elasticsearch.ElasticsearchSecurityException;
10+
import org.elasticsearch.action.ActionRequestValidationException;
1011
import org.elasticsearch.action.DocWriteResponse;
1112
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
1213
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
@@ -223,6 +224,16 @@ public void testMultipleApiKeysCanHaveSameName() {
223224
}
224225
}
225226

227+
public void testCreateApiKeyWithoutNameWillFail() {
228+
Client client = client().filterWithHeader(Collections.singletonMap("Authorization",
229+
UsernamePasswordToken.basicAuthHeaderValue(SecuritySettingsSource.TEST_SUPERUSER,
230+
SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING)));
231+
SecurityClient securityClient = new SecurityClient(client);
232+
final ActionRequestValidationException e =
233+
expectThrows(ActionRequestValidationException.class, () -> securityClient.prepareCreateApiKey().get());
234+
assertThat(e.getMessage(), containsString("api key name is required"));
235+
}
236+
226237
public void testInvalidateApiKeysForRealm() throws InterruptedException, ExecutionException {
227238
int noOfApiKeys = randomIntBetween(3, 5);
228239
List<CreateApiKeyResponse> responses = createApiKeys(noOfApiKeys, null);

0 commit comments

Comments
 (0)