Skip to content

Make role descriptors optional when creating API keys #43481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions x-pack/docs/en/rest-api/security/create-api-keys.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ The following parameters can be specified in the body of a POST or PUT request:
`name`::
(string) Specifies the name for this API key.

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

Expand Down
7 changes: 3 additions & 4 deletions x-pack/docs/en/rest-api/security/invalidate-api-keys.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ pertain to invalidating api keys:

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

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

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

Expand All @@ -47,8 +47,7 @@ If you create an API key as follows:
------------------------------------------------------------
POST /_security/api_key
{
"name": "my-api-key",
"role_descriptors": {}
"name": "my-api-key"
}
------------------------------------------------------------
// CONSOLE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,21 @@ public CreateApiKeyRequest() {}
* @param roleDescriptors list of {@link RoleDescriptor}s
* @param expiration to specify expiration for the API key
*/
public CreateApiKeyRequest(String name, List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
public CreateApiKeyRequest(String name, @Nullable List<RoleDescriptor> roleDescriptors, @Nullable TimeValue expiration) {
if (Strings.hasText(name)) {
this.name = name;
} else {
throw new IllegalArgumentException("name must not be null or empty");
}
this.roleDescriptors = Objects.requireNonNull(roleDescriptors, "role descriptors may not be null");
this.roleDescriptors = (roleDescriptors == null) ? List.of() : List.copyOf(roleDescriptors);
this.expiration = expiration;
}

public CreateApiKeyRequest(StreamInput in) throws IOException {
super(in);
this.name = in.readString();
this.expiration = in.readOptionalTimeValue();
this.roleDescriptors = Collections.unmodifiableList(in.readList(RoleDescriptor::new));
this.roleDescriptors = List.copyOf(in.readList(RoleDescriptor::new));
this.refreshPolicy = WriteRequest.RefreshPolicy.readFrom(in);
}

Expand All @@ -77,16 +77,16 @@ public TimeValue getExpiration() {
return expiration;
}

public void setExpiration(TimeValue expiration) {
public void setExpiration(@Nullable TimeValue expiration) {
this.expiration = expiration;
}

public List<RoleDescriptor> getRoleDescriptors() {
return roleDescriptors;
}

public void setRoleDescriptors(List<RoleDescriptor> roleDescriptors) {
this.roleDescriptors = Collections.unmodifiableList(Objects.requireNonNull(roleDescriptors, "role descriptors may not be null"));
public void setRoleDescriptors(@Nullable List<RoleDescriptor> roleDescriptors) {
this.roleDescriptors = (roleDescriptors == null) ? List.of() : List.copyOf(roleDescriptors);
}

public WriteRequest.RefreshPolicy getRefreshPolicy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public final class CreateApiKeyRequestBuilder extends ActionRequestBuilder<Creat

static {
PARSER.declareString(constructorArg(), new ParseField("name"));
PARSER.declareNamedObjects(constructorArg(), (p, c, n) -> {
PARSER.declareNamedObjects(optionalConstructorArg(), (p, c, n) -> {
p.nextToken();
return RoleDescriptor.parse(n, p, false);
}, new ParseField("role_descriptors"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,22 @@ public void testParserAndCreateApiRequestBuilder() throws IOException {
assertThat(request.getExpiration(), equalTo(TimeValue.parseTimeValue("1d", "expiration")));
}
}

public void testParserAndCreateApiRequestBuilderWithNullOrEmptyRoleDescriptors() throws IOException {
boolean withExpiration = randomBoolean();
boolean noRoleDescriptorsField = randomBoolean();
final String json = "{ \"name\" : \"my-api-key\""
+ ((withExpiration) ? ", \"expiration\": \"1d\"" : "")
+ ((noRoleDescriptorsField) ? "" : ", \"role_descriptors\": {}")
+ "}";
final BytesArray source = new BytesArray(json);
final NodeClient mockClient = mock(NodeClient.class);
final CreateApiKeyRequest request = new CreateApiKeyRequestBuilder(mockClient).source(source, XContentType.JSON).request();
final List<RoleDescriptor> actualRoleDescriptors = request.getRoleDescriptors();
assertThat(request.getName(), equalTo("my-api-key"));
assertThat(actualRoleDescriptors.size(), is(0));
if (withExpiration) {
assertThat(request.getExpiration(), equalTo(TimeValue.parseTimeValue("1d", "expiration")));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,16 @@ public void testSerialization() throws IOException {
final TimeValue expiration = randomBoolean() ? null :
TimeValue.parseTimeValue(randomTimeValue(), "test serialization of create api key");
final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values());
final int numDescriptors = randomIntBetween(0, 4);
final List<RoleDescriptor> descriptorList = new ArrayList<>();
for (int i = 0; i < numDescriptors; i++) {
descriptorList.add(new RoleDescriptor("role_" + i, new String[] { "all" }, null, null));
boolean nullOrEmptyRoleDescriptors = randomBoolean();
final List<RoleDescriptor> descriptorList;
if (nullOrEmptyRoleDescriptors) {
descriptorList = randomBoolean() ? null : List.of();
} else {
final int numDescriptors = randomIntBetween(1, 4);
descriptorList = new ArrayList<>();
for (int i = 0; i < numDescriptors; i++) {
descriptorList.add(new RoleDescriptor("role_" + i, new String[] { "all" }, null, null));
}
}

final CreateApiKeyRequest request = new CreateApiKeyRequest();
Expand All @@ -95,9 +101,7 @@ public void testSerialization() throws IOException {
if (refreshPolicy != request.getRefreshPolicy() || randomBoolean()) {
request.setRefreshPolicy(refreshPolicy);
}
if (descriptorList.isEmpty() == false || randomBoolean()) {
request.setRoleDescriptors(descriptorList);
}
request.setRoleDescriptors(descriptorList);

try (BytesStreamOutput out = new BytesStreamOutput()) {
request.writeTo(out);
Expand All @@ -106,7 +110,11 @@ public void testSerialization() throws IOException {
assertEquals(name, serialized.getName());
assertEquals(expiration, serialized.getExpiration());
assertEquals(refreshPolicy, serialized.getRefreshPolicy());
assertEquals(descriptorList, serialized.getRoleDescriptors());
if (nullOrEmptyRoleDescriptors) {
assertThat(serialized.getRoleDescriptors().isEmpty(), is(true));
} else {
assertEquals(descriptorList, serialized.getRoleDescriptors());
}
}
}
}
Expand Down