Skip to content

Commit 480453a

Browse files
authored
Make role descriptors optional when creating API keys (#43481) (#43614)
This commit changes the `role_descriptors` field from required to optional when creating API key. The default behavior in .NET ES client is to omit properties with `null` value requiring additional workarounds. The behavior for the API does not change. Field names (`id`, `name`) in the invalidate api keys API documentation have been corrected where they were wrong. Closes #42053
1 parent ca43cdf commit 480453a

File tree

6 files changed

+48
-22
lines changed

6 files changed

+48
-22
lines changed

x-pack/docs/en/rest-api/security/create-api-keys.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ The following parameters can be specified in the body of a POST or PUT request:
3737
`name`::
3838
(string) Specifies the name for this API key.
3939

40-
`role_descriptors` (required)::
40+
`role_descriptors` (optional)::
4141
(array-of-role-descriptor) An array of role descriptors for this API key. This
42-
parameter is required but can be an empty array, which applies the permissions
43-
of the authenticated user. If you supply role descriptors, they must be a subset
44-
of the authenticated user's permissions. The structure of role descriptor is the
42+
parameter is optional. When it is not specified or is an empty array, then the API key will have
43+
the permissions of the authenticated user. If you supply role descriptors, they must
44+
be a subset of the authenticated user's permissions. The structure of role descriptor is the
4545
same as the request for create role API. For more details, see
4646
<<security-api-roles,role management APIs>>.
4747

x-pack/docs/en/rest-api/security/invalidate-api-keys.asciidoc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ pertain to invalidating api keys:
3131

3232
`realm_name` (optional)::
3333
(string) The name of an authentication realm. This parameter cannot be used with
34-
either `api_key_id` or `api_key_name`.
34+
either `id` or `name`.
3535

3636
`username` (optional)::
3737
(string) The username of a user. This parameter cannot be used with either
38-
`api_key_id` or `api_key_name`.
38+
`id` or `name`.
3939

4040
NOTE: While all parameters are optional, at least one of them is required.
4141

@@ -47,8 +47,7 @@ If you create an API key as follows:
4747
------------------------------------------------------------
4848
POST /_security/api_key
4949
{
50-
"name": "my-api-key",
51-
"role_descriptors": {}
50+
"name": "my-api-key"
5251
}
5352
------------------------------------------------------------
5453
// CONSOLE

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ public CreateApiKeyRequest() {}
4343
* @param roleDescriptors list of {@link RoleDescriptor}s
4444
* @param expiration to specify expiration for the API key
4545
*/
46-
public CreateApiKeyRequest(String name, List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
46+
public CreateApiKeyRequest(String name, @Nullable List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
4747
if (Strings.hasText(name)) {
4848
this.name = name;
4949
} else {
5050
throw new IllegalArgumentException("name must not be null or empty");
5151
}
52-
this.roleDescriptors = Objects.requireNonNull(roleDescriptors, "role descriptors may not be null");
52+
this.roleDescriptors = (roleDescriptors == null) ? Collections.emptyList() : Collections.unmodifiableList(roleDescriptors);
5353
this.expiration = expiration;
5454
}
5555

@@ -77,16 +77,16 @@ public TimeValue getExpiration() {
7777
return expiration;
7878
}
7979

80-
public void setExpiration(TimeValue expiration) {
80+
public void setExpiration(@Nullable TimeValue expiration) {
8181
this.expiration = expiration;
8282
}
8383

8484
public List<RoleDescriptor> getRoleDescriptors() {
8585
return roleDescriptors;
8686
}
8787

88-
public void setRoleDescriptors(List<RoleDescriptor> roleDescriptors) {
89-
this.roleDescriptors = Collections.unmodifiableList(Objects.requireNonNull(roleDescriptors, "role descriptors may not be null"));
88+
public void setRoleDescriptors(@Nullable List<RoleDescriptor> roleDescriptors) {
89+
this.roleDescriptors = (roleDescriptors == null) ? Collections.emptyList() : Collections.unmodifiableList(roleDescriptors);
9090
}
9191

9292
public WriteRequest.RefreshPolicy getRefreshPolicy() {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public final class CreateApiKeyRequestBuilder extends ActionRequestBuilder<Creat
3939

4040
static {
4141
PARSER.declareString(constructorArg(), new ParseField("name"));
42-
PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> {
42+
PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, n) -> {
4343
p.nextToken();
4444
return RoleDescriptor.parse(n, p, false);
4545
}, new ParseField("role_descriptors"));

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,22 @@ public void testParserAndCreateApiRequestBuilder() throws IOException {
5959
assertThat(request.getExpiration(), equalTo(TimeValue.parseTimeValue("1d", "expiration")));
6060
}
6161
}
62+
63+
public void testParserAndCreateApiRequestBuilderWithNullOrEmptyRoleDescriptors() throws IOException {
64+
boolean withExpiration = randomBoolean();
65+
boolean noRoleDescriptorsField = randomBoolean();
66+
final String json = "{ \"name\" : \"my-api-key\""
67+
+ ((withExpiration) ? ", \"expiration\": \"1d\"" : "")
68+
+ ((noRoleDescriptorsField) ? "" : ", \"role_descriptors\": {}")
69+
+ "}";
70+
final BytesArray source = new BytesArray(json);
71+
final NodeClient mockClient = mock(NodeClient.class);
72+
final CreateApiKeyRequest request = new CreateApiKeyRequestBuilder(mockClient).source(source, XContentType.JSON).request();
73+
final List<RoleDescriptor> actualRoleDescriptors = request.getRoleDescriptors();
74+
assertThat(request.getName(), equalTo("my-api-key"));
75+
assertThat(actualRoleDescriptors.size(), is(0));
76+
if (withExpiration) {
77+
assertThat(request.getExpiration(), equalTo(TimeValue.parseTimeValue("1d", "expiration")));
78+
}
79+
}
6280
}

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.io.IOException;
1818
import java.util.ArrayList;
19+
import java.util.Collections;
1920
import java.util.List;
2021

2122
import static org.hamcrest.Matchers.containsString;
@@ -82,10 +83,16 @@ public void testSerialization() throws IOException {
8283
final TimeValue expiration = randomBoolean() ? null :
8384
TimeValue.parseTimeValue(randomTimeValue(), "test serialization of create api key");
8485
final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values());
85-
final int numDescriptors = randomIntBetween(0, 4);
86-
final List<RoleDescriptor> descriptorList = new ArrayList<>();
87-
for (int i = 0; i < numDescriptors; i++) {
88-
descriptorList.add(new RoleDescriptor("role_" + i, new String[] { "all" }, null, null));
86+
boolean nullOrEmptyRoleDescriptors = randomBoolean();
87+
final List<RoleDescriptor> descriptorList;
88+
if (nullOrEmptyRoleDescriptors) {
89+
descriptorList = randomBoolean() ? null : Collections.emptyList();
90+
} else {
91+
final int numDescriptors = randomIntBetween(1, 4);
92+
descriptorList = new ArrayList<>();
93+
for (int i = 0; i < numDescriptors; i++) {
94+
descriptorList.add(new RoleDescriptor("role_" + i, new String[] { "all" }, null, null));
95+
}
8996
}
9097

9198
final CreateApiKeyRequest request = new CreateApiKeyRequest();
@@ -95,9 +102,7 @@ public void testSerialization() throws IOException {
95102
if (refreshPolicy != request.getRefreshPolicy() || randomBoolean()) {
96103
request.setRefreshPolicy(refreshPolicy);
97104
}
98-
if (descriptorList.isEmpty() == false || randomBoolean()) {
99-
request.setRoleDescriptors(descriptorList);
100-
}
105+
request.setRoleDescriptors(descriptorList);
101106

102107
try (BytesStreamOutput out = new BytesStreamOutput()) {
103108
request.writeTo(out);
@@ -106,7 +111,11 @@ public void testSerialization() throws IOException {
106111
assertEquals(name, serialized.getName());
107112
assertEquals(expiration, serialized.getExpiration());
108113
assertEquals(refreshPolicy, serialized.getRefreshPolicy());
109-
assertEquals(descriptorList, serialized.getRoleDescriptors());
114+
if (nullOrEmptyRoleDescriptors) {
115+
assertThat(serialized.getRoleDescriptors().isEmpty(), is(true));
116+
} else {
117+
assertEquals(descriptorList, serialized.getRoleDescriptors());
118+
}
110119
}
111120
}
112121
}

0 commit comments

Comments
 (0)