Add a new ids field to the API of invalidating API keys so that it supports bulk invalidation with a list of IDs. Note the existing id field is kept as is and it is an error if both id and ids are specified.
This commit is contained in:
parent
bbfa2f1303
commit
abf9b885b4
|
@ -15,7 +15,9 @@ import org.elasticsearch.common.io.stream.StreamInput;
|
|||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.elasticsearch.action.ValidateActions.addValidationError;
|
||||
|
||||
|
@ -26,19 +28,24 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
|
||||
private final String realmName;
|
||||
private final String userName;
|
||||
private final String id;
|
||||
private final String[] ids;
|
||||
private final String name;
|
||||
private final boolean ownedByAuthenticatedUser;
|
||||
|
||||
public InvalidateApiKeyRequest() {
|
||||
this(null, null, null, null, false);
|
||||
this(null, null, null, null, false, null);
|
||||
}
|
||||
|
||||
public InvalidateApiKeyRequest(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
realmName = in.readOptionalString();
|
||||
userName = in.readOptionalString();
|
||||
id = in.readOptionalString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
ids = in.readOptionalStringArray();
|
||||
} else {
|
||||
final String id = in.readOptionalString();
|
||||
ids = Strings.hasText(id) == false ? null : new String[] { id };
|
||||
}
|
||||
name = in.readOptionalString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_4_0)) {
|
||||
ownedByAuthenticatedUser = in.readOptionalBoolean();
|
||||
|
@ -49,9 +56,22 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
|
||||
public InvalidateApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String id,
|
||||
@Nullable String name, boolean ownedByAuthenticatedUser) {
|
||||
this(realmName, userName, id, name, ownedByAuthenticatedUser, null);
|
||||
}
|
||||
|
||||
public InvalidateApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String id,
|
||||
@Nullable String name, boolean ownedByAuthenticatedUser, @Nullable String[] ids) {
|
||||
if (id != null && ids != null) {
|
||||
throw new IllegalArgumentException("Must use either [id] or [ids], not both at the same time");
|
||||
}
|
||||
|
||||
this.realmName = realmName;
|
||||
this.userName = userName;
|
||||
this.id = id;
|
||||
if (id != null) {
|
||||
this.ids = new String[] {id};
|
||||
} else {
|
||||
this.ids = ids;
|
||||
}
|
||||
this.name = name;
|
||||
this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
|
||||
}
|
||||
|
@ -64,8 +84,8 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
return userName;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
public String[] getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -141,15 +161,28 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && Strings.hasText(id) == false
|
||||
if (ids != null) {
|
||||
if (ids.length == 0) {
|
||||
validationException = addValidationError("Field [ids] cannot be an empty array", validationException);
|
||||
} else {
|
||||
final int[] idxOfBlankIds = IntStream.range(0, ids.length).filter(i -> Strings.hasText(ids[i]) == false).toArray();
|
||||
if (idxOfBlankIds.length > 0) {
|
||||
validationException = addValidationError("Field [ids] must not contain blank id, but got blank "
|
||||
+ (idxOfBlankIds.length == 1 ? "id" : "ids") + " at index "
|
||||
+ (idxOfBlankIds.length == 1 ? "position" : "positions") + ": "
|
||||
+ Arrays.toString(idxOfBlankIds), validationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && ids == null
|
||||
&& Strings.hasText(name) == false && ownedByAuthenticatedUser == false) {
|
||||
validationException = addValidationError("One of [api key id, api key name, username, realm name] must be specified if " +
|
||||
validationException = addValidationError("One of [api key id(s), api key name, username, realm name] must be specified if " +
|
||||
"[owner] flag is false", validationException);
|
||||
}
|
||||
if (Strings.hasText(id) || Strings.hasText(name)) {
|
||||
if (ids != null || Strings.hasText(name)) {
|
||||
if (Strings.hasText(realmName) || Strings.hasText(userName)) {
|
||||
validationException = addValidationError(
|
||||
"username or realm name must not be specified when the api key id or api key name is specified",
|
||||
"username or realm name must not be specified when the api key id(s) or api key name are specified",
|
||||
validationException);
|
||||
}
|
||||
}
|
||||
|
@ -160,8 +193,8 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
validationException);
|
||||
}
|
||||
}
|
||||
if (Strings.hasText(id) && Strings.hasText(name)) {
|
||||
validationException = addValidationError("only one of [api key id, api key name] can be specified", validationException);
|
||||
if (ids != null && Strings.hasText(name)) {
|
||||
validationException = addValidationError("only one of [api key id(s), api key name] can be specified", validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
@ -171,7 +204,19 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
super.writeTo(out);
|
||||
out.writeOptionalString(realmName);
|
||||
out.writeOptionalString(userName);
|
||||
out.writeOptionalString(id);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
out.writeOptionalStringArray(ids);
|
||||
} else {
|
||||
if (ids != null) {
|
||||
if (ids.length == 1) {
|
||||
out.writeOptionalString(ids[0]);
|
||||
} else {
|
||||
throw new IllegalArgumentException("a request with multi-valued field [ids] cannot be sent to an older version");
|
||||
}
|
||||
} else {
|
||||
out.writeOptionalString(null);
|
||||
}
|
||||
}
|
||||
out.writeOptionalString(name);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_4_0)) {
|
||||
out.writeOptionalBoolean(ownedByAuthenticatedUser);
|
||||
|
@ -190,12 +235,12 @@ public final class InvalidateApiKeyRequest extends ActionRequest {
|
|||
return ownedByAuthenticatedUser == that.ownedByAuthenticatedUser &&
|
||||
Objects.equals(realmName, that.realmName) &&
|
||||
Objects.equals(userName, that.userName) &&
|
||||
Objects.equals(id, that.id) &&
|
||||
Arrays.equals(ids, that.ids) &&
|
||||
Objects.equals(name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(realmName, userName, id, name, ownedByAuthenticatedUser);
|
||||
return Objects.hash(realmName, userName, ids, name, ownedByAuthenticatedUser);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import org.elasticsearch.xpack.core.security.authc.Authentication.Authentication
|
|||
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
|
||||
import org.elasticsearch.xpack.core.security.support.Automatons;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Named cluster privilege for managing API keys owned by the current authenticated user.
|
||||
*/
|
||||
|
@ -56,9 +58,16 @@ public class ManageOwnApiKeyClusterPrivilege implements NamedClusterPrivilege {
|
|||
getApiKeyRequest.getRealmName(), getApiKeyRequest.ownedByAuthenticatedUser());
|
||||
} else if (request instanceof InvalidateApiKeyRequest) {
|
||||
final InvalidateApiKeyRequest invalidateApiKeyRequest = (InvalidateApiKeyRequest) request;
|
||||
return checkIfUserIsOwnerOfApiKeys(authentication, invalidateApiKeyRequest.getId(),
|
||||
invalidateApiKeyRequest.getUserName(), invalidateApiKeyRequest.getRealmName(),
|
||||
invalidateApiKeyRequest.ownedByAuthenticatedUser());
|
||||
final String[] apiKeyIds = invalidateApiKeyRequest.getIds();
|
||||
if (apiKeyIds == null) {
|
||||
return checkIfUserIsOwnerOfApiKeys(authentication, null,
|
||||
invalidateApiKeyRequest.getUserName(), invalidateApiKeyRequest.getRealmName(),
|
||||
invalidateApiKeyRequest.ownedByAuthenticatedUser());
|
||||
} else {
|
||||
return Arrays.stream(apiKeyIds).allMatch(id -> checkIfUserIsOwnerOfApiKeys(authentication, id,
|
||||
invalidateApiKeyRequest.getUserName(), invalidateApiKeyRequest.getRealmName(),
|
||||
invalidateApiKeyRequest.ownedByAuthenticatedUser()));
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"manage own api key privilege only supports API key requests (not " + request.getClass().getName() + ")");
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.elasticsearch.xpack.core.security.action;
|
|||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
|
||||
import org.elasticsearch.common.io.stream.OutputStreamStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
@ -18,13 +19,53 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.test.VersionUtils.getPreviousVersion;
|
||||
import static org.elasticsearch.test.VersionUtils.randomVersionBetween;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class InvalidateApiKeyRequestTests extends ESTestCase {
|
||||
|
||||
public void testCannotSpecifyBothIdAndIds() {
|
||||
final IllegalArgumentException e =
|
||||
expectThrows(IllegalArgumentException.class, () -> new InvalidateApiKeyRequest(
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
randomAlphaOfLength(12),
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
false,
|
||||
new String[] { randomAlphaOfLength(12) }));
|
||||
assertThat(e.getMessage(), containsString("Must use either [id] or [ids], not both at the same time"));
|
||||
}
|
||||
|
||||
public void testNonNullIdsCannotBeEmptyNorContainBlankId() {
|
||||
InvalidateApiKeyRequest invalidateApiKeyRequest = new InvalidateApiKeyRequest(
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
null,
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
false,
|
||||
new String[] {});
|
||||
ActionRequestValidationException validationException = invalidateApiKeyRequest.validate();
|
||||
assertNotNull(validationException);
|
||||
assertThat(validationException.getMessage(), containsString("Field [ids] cannot be an empty array"));
|
||||
|
||||
invalidateApiKeyRequest = new InvalidateApiKeyRequest(
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
null,
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
false,
|
||||
new String[] {randomAlphaOfLength(12), null});
|
||||
validationException = invalidateApiKeyRequest.validate();
|
||||
assertNotNull(validationException);
|
||||
|
||||
assertThat(validationException.getMessage(), containsString("Field [ids] must not contain blank id, "
|
||||
+ "but got blank id at index position: [1]"));
|
||||
}
|
||||
|
||||
public void testRequestValidation() {
|
||||
InvalidateApiKeyRequest request = InvalidateApiKeyRequest.usingApiKeyId(randomAlphaOfLength(5), randomBoolean());
|
||||
ActionRequestValidationException ve = request.validate();
|
||||
|
@ -69,7 +110,15 @@ public class InvalidateApiKeyRequestTests extends ESTestCase {
|
|||
super.writeTo(out);
|
||||
out.writeOptionalString(realm);
|
||||
out.writeOptionalString(user);
|
||||
out.writeOptionalString(apiKeyId);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
if (Strings.hasText(apiKeyId)) {
|
||||
out.writeOptionalStringArray(new String[] { apiKeyId });
|
||||
} else {
|
||||
out.writeOptionalStringArray(null);
|
||||
}
|
||||
} else {
|
||||
out.writeOptionalString(apiKeyId);
|
||||
}
|
||||
out.writeOptionalString(apiKeyName);
|
||||
out.writeOptionalBoolean(ownedByAuthenticatedUser);
|
||||
}
|
||||
|
@ -86,13 +135,13 @@ public class InvalidateApiKeyRequestTests extends ESTestCase {
|
|||
{randomNullOrEmptyString(), "user", randomNullOrEmptyString(), randomNullOrEmptyString(), "true"},
|
||||
};
|
||||
String[][] expectedErrorMessages = new String[][]{
|
||||
{"One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false"},
|
||||
{"username or realm name must not be specified when the api key id or api key name is specified",
|
||||
"only one of [api key id, api key name] can be specified"},
|
||||
{"username or realm name must not be specified when the api key id or api key name is specified",
|
||||
"only one of [api key id, api key name] can be specified"},
|
||||
{"username or realm name must not be specified when the api key id or api key name is specified"},
|
||||
{"only one of [api key id, api key name] can be specified"},
|
||||
{"One of [api key id(s), api key name, username, realm name] must be specified if [owner] flag is false"},
|
||||
{"username or realm name must not be specified when the api key id(s) or api key name are specified",
|
||||
"only one of [api key id(s), api key name] can be specified"},
|
||||
{"username or realm name must not be specified when the api key id(s) or api key name are specified",
|
||||
"only one of [api key id(s), api key name] can be specified"},
|
||||
{"username or realm name must not be specified when the api key id(s) or api key name are specified"},
|
||||
{"only one of [api key id(s), api key name] can be specified"},
|
||||
{"neither username nor realm-name may be specified when invalidating owned API keys"},
|
||||
{"neither username nor realm-name may be specified when invalidating owned API keys"}
|
||||
};
|
||||
|
@ -100,15 +149,18 @@ public class InvalidateApiKeyRequestTests extends ESTestCase {
|
|||
for (int caseNo = 0; caseNo < inputs.length; caseNo++) {
|
||||
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
OutputStreamStreamOutput osso = new OutputStreamStreamOutput(bos)) {
|
||||
final Version streamVersion = randomVersionBetween(random(), Version.V_7_4_0, getPreviousVersion(Version.V_7_10_0));
|
||||
Dummy d = new Dummy(inputs[caseNo]);
|
||||
osso.setVersion(streamVersion);
|
||||
d.writeTo(osso);
|
||||
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
|
||||
InputStreamStreamInput issi = new InputStreamStreamInput(bis);
|
||||
issi.setVersion(streamVersion);
|
||||
|
||||
InvalidateApiKeyRequest request = new InvalidateApiKeyRequest(issi);
|
||||
ActionRequestValidationException ve = request.validate();
|
||||
assertNotNull(ve);
|
||||
assertNotNull(ve.getMessage(), ve);
|
||||
assertEquals(expectedErrorMessages[caseNo].length, ve.validationErrors().size());
|
||||
assertThat(ve.validationErrors(), containsInAnyOrder(expectedErrorMessages[caseNo]));
|
||||
}
|
||||
|
@ -129,22 +181,49 @@ public class InvalidateApiKeyRequestTests extends ESTestCase {
|
|||
inputStreamStreamInput.setVersion(randomVersionBetween(random(), Version.V_7_0_0, Version.V_7_3_0));
|
||||
InvalidateApiKeyRequest requestFromInputStream = new InvalidateApiKeyRequest(inputStreamStreamInput);
|
||||
|
||||
assertThat(requestFromInputStream.getId(), equalTo(invalidateApiKeyRequest.getId()));
|
||||
assertThat(requestFromInputStream.getIds(), equalTo(invalidateApiKeyRequest.getIds()));
|
||||
// old version so the default for `ownedByAuthenticatedUser` is false
|
||||
assertThat(requestFromInputStream.ownedByAuthenticatedUser(), is(false));
|
||||
}
|
||||
{
|
||||
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
|
||||
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
|
||||
out.setVersion(randomVersionBetween(random(), Version.V_7_4_0, Version.CURRENT));
|
||||
out.setVersion(randomVersionBetween(random(), Version.V_7_4_0, Version.V_7_9_0));
|
||||
invalidateApiKeyRequest.writeTo(out);
|
||||
|
||||
InputStreamStreamInput inputStreamStreamInput = new InputStreamStreamInput(new ByteArrayInputStream(outBuffer.toByteArray()));
|
||||
inputStreamStreamInput.setVersion(randomVersionBetween(random(), Version.V_7_4_0, Version.CURRENT));
|
||||
inputStreamStreamInput.setVersion(randomVersionBetween(random(), Version.V_7_4_0, Version.V_7_9_0));
|
||||
InvalidateApiKeyRequest requestFromInputStream = new InvalidateApiKeyRequest(inputStreamStreamInput);
|
||||
|
||||
assertThat(requestFromInputStream, equalTo(invalidateApiKeyRequest));
|
||||
}
|
||||
{
|
||||
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
|
||||
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
|
||||
out.setVersion(randomVersionBetween(random(), Version.V_7_10_0, Version.CURRENT));
|
||||
invalidateApiKeyRequest.writeTo(out);
|
||||
|
||||
InputStreamStreamInput inputStreamStreamInput = new InputStreamStreamInput(new ByteArrayInputStream(outBuffer.toByteArray()));
|
||||
inputStreamStreamInput.setVersion(randomVersionBetween(random(), Version.V_7_10_0, Version.CURRENT));
|
||||
InvalidateApiKeyRequest requestFromInputStream = new InvalidateApiKeyRequest(inputStreamStreamInput);
|
||||
|
||||
assertThat(requestFromInputStream, equalTo(invalidateApiKeyRequest));
|
||||
}
|
||||
}
|
||||
|
||||
public void testSerializationWillThrowWhenMultipleIdsAndOldVersionStream() {
|
||||
final InvalidateApiKeyRequest invalidateApiKeyRequest = new InvalidateApiKeyRequest(
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
null,
|
||||
randomFrom(randomNullOrEmptyString(), randomAlphaOfLength(8)),
|
||||
false,
|
||||
new String[] { randomAlphaOfLength(12), randomAlphaOfLength(12) });
|
||||
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
|
||||
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
|
||||
out.setVersion(randomVersionBetween(random(), Version.V_7_4_0, getPreviousVersion(Version.V_7_10_0)));
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> invalidateApiKeyRequest.writeTo(out));
|
||||
assertThat(e.getMessage(), containsString("a request with multi-valued field [ids] cannot be sent to an older version"));
|
||||
}
|
||||
|
||||
private static String randomNullOrEmptyString() {
|
||||
|
|
|
@ -36,7 +36,7 @@ public final class TransportInvalidateApiKeyAction extends HandledTransportActio
|
|||
|
||||
@Override
|
||||
protected void doExecute(Task task, InvalidateApiKeyRequest request, ActionListener<InvalidateApiKeyResponse> listener) {
|
||||
String apiKeyId = request.getId();
|
||||
String[] apiKeyIds = request.getIds();
|
||||
String apiKeyName = request.getName();
|
||||
String username = request.getUserName();
|
||||
String realm = request.getRealmName();
|
||||
|
@ -53,7 +53,7 @@ public final class TransportInvalidateApiKeyAction extends HandledTransportActio
|
|||
realm = ApiKeyService.getCreatorRealmName(authentication);
|
||||
}
|
||||
|
||||
apiKeyService.invalidateApiKeys(realm, username, apiKeyName, apiKeyId, listener);
|
||||
apiKeyService.invalidateApiKeys(realm, username, apiKeyName, apiKeyIds, listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -808,24 +808,24 @@ public class ApiKeyService {
|
|||
* @param realmName realm name
|
||||
* @param username user name
|
||||
* @param apiKeyName API key name
|
||||
* @param apiKeyId API key id
|
||||
* @param apiKeyIds API key id
|
||||
* @param invalidateListener listener for {@link InvalidateApiKeyResponse}
|
||||
*/
|
||||
public void invalidateApiKeys(String realmName, String username, String apiKeyName, String apiKeyId,
|
||||
public void invalidateApiKeys(String realmName, String username, String apiKeyName, String[] apiKeyIds,
|
||||
ActionListener<InvalidateApiKeyResponse> invalidateListener) {
|
||||
ensureEnabled();
|
||||
if (Strings.hasText(realmName) == false && Strings.hasText(username) == false && Strings.hasText(apiKeyName) == false
|
||||
&& Strings.hasText(apiKeyId) == false) {
|
||||
&& (apiKeyIds == null || apiKeyIds.length == 0)) {
|
||||
logger.trace("none of the parameters [api key id, api key name, username, realm name] were specified for invalidation");
|
||||
invalidateListener
|
||||
.onFailure(new IllegalArgumentException("One of [api key id, api key name, username, realm name] must be specified"));
|
||||
} else {
|
||||
findApiKeysForUserRealmApiKeyIdAndNameCombination(realmName, username, apiKeyName, apiKeyId, true, false,
|
||||
findApiKeysForUserRealmApiKeyIdAndNameCombination(realmName, username, apiKeyName, apiKeyIds, true, false,
|
||||
ActionListener.wrap(apiKeys -> {
|
||||
if (apiKeys.isEmpty()) {
|
||||
logger.debug(
|
||||
"No active api keys to invalidate for realm [{}], username [{}], api key name [{}] and api key id [{}]",
|
||||
realmName, username, apiKeyName, apiKeyId);
|
||||
realmName, username, apiKeyName, Arrays.toString(apiKeyIds));
|
||||
invalidateListener.onResponse(InvalidateApiKeyResponse.emptyResponse());
|
||||
} else {
|
||||
invalidateAllApiKeys(apiKeys.stream().map(apiKey -> apiKey.getId()).collect(Collectors.toSet()),
|
||||
|
@ -876,7 +876,7 @@ public class ApiKeyService {
|
|||
}
|
||||
}
|
||||
|
||||
private void findApiKeysForUserRealmApiKeyIdAndNameCombination(String realmName, String userName, String apiKeyName, String apiKeyId,
|
||||
private void findApiKeysForUserRealmApiKeyIdAndNameCombination(String realmName, String userName, String apiKeyName, String[] apiKeyIds,
|
||||
boolean filterOutInvalidatedKeys, boolean filterOutExpiredKeys,
|
||||
ActionListener<Collection<ApiKey>> listener) {
|
||||
final SecurityIndexManager frozenSecurityIndex = securityIndex.freeze();
|
||||
|
@ -899,8 +899,8 @@ public class ApiKeyService {
|
|||
boolQuery.filter(QueryBuilders.termQuery("name", apiKeyName));
|
||||
}
|
||||
}
|
||||
if (Strings.hasText(apiKeyId)) {
|
||||
boolQuery.filter(QueryBuilders.termQuery("_id", apiKeyId));
|
||||
if (apiKeyIds != null && apiKeyIds.length > 0) {
|
||||
boolQuery.filter(QueryBuilders.idsQuery().addIds(apiKeyIds));
|
||||
}
|
||||
|
||||
findApiKeys(boolQuery, filterOutInvalidatedKeys, filterOutExpiredKeys, listener);
|
||||
|
@ -1059,7 +1059,8 @@ public class ApiKeyService {
|
|||
public void getApiKeys(String realmName, String username, String apiKeyName, String apiKeyId,
|
||||
ActionListener<GetApiKeyResponse> listener) {
|
||||
ensureEnabled();
|
||||
findApiKeysForUserRealmApiKeyIdAndNameCombination(realmName, username, apiKeyName, apiKeyId, false, false,
|
||||
final String[] apiKeyIds = Strings.hasText(apiKeyId) == false ? null : new String[] { apiKeyId };
|
||||
findApiKeysForUserRealmApiKeyIdAndNameCombination(realmName, username, apiKeyName, apiKeyIds, false, false,
|
||||
ActionListener.wrap(apiKeyInfos -> {
|
||||
if (apiKeyInfos.isEmpty()) {
|
||||
logger.debug("No active api keys found for realm [{}], user [{}], api key name [{}] and api key id [{}]",
|
||||
|
|
|
@ -34,8 +34,9 @@ import static org.elasticsearch.rest.RestRequest.Method.DELETE;
|
|||
public final class RestInvalidateApiKeyAction extends ApiKeyBaseRestHandler {
|
||||
static final ConstructingObjectParser<InvalidateApiKeyRequest, Void> PARSER = new ConstructingObjectParser<>("invalidate_api_key",
|
||||
a -> {
|
||||
return new InvalidateApiKeyRequest((String) a[0], (String) a[1], (String) a[2], (String) a[3], (a[4] == null) ? false :
|
||||
(Boolean) a[4]);
|
||||
return new InvalidateApiKeyRequest((String) a[0], (String) a[1], (String) a[2], (String) a[3],
|
||||
(a[4] == null) ? false : (Boolean) a[4],
|
||||
(a[5] == null) ? null : ((List<String>) a[5]).toArray(new String[0]));
|
||||
});
|
||||
|
||||
static {
|
||||
|
@ -44,6 +45,7 @@ public final class RestInvalidateApiKeyAction extends ApiKeyBaseRestHandler {
|
|||
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("id"));
|
||||
PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), new ParseField("name"));
|
||||
PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), new ParseField("owner"));
|
||||
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("ids"));
|
||||
}
|
||||
|
||||
public RestInvalidateApiKeyAction(Settings settings, XPackLicenseState licenseState) {
|
||||
|
|
|
@ -97,7 +97,8 @@ public class RestInvalidateApiKeyActionTests extends ESTestCase {
|
|||
return;
|
||||
}
|
||||
if (invalidateApiKeyRequest.getName() != null && invalidateApiKeyRequest.getName().equals("api-key-name-1")
|
||||
|| invalidateApiKeyRequest.getId() != null && invalidateApiKeyRequest.getId().equals("api-key-id-1")
|
||||
|| invalidateApiKeyRequest.getIds() != null && Arrays.equals(
|
||||
invalidateApiKeyRequest.getIds(), new String[] {"api-key-id-1"})
|
||||
|| invalidateApiKeyRequest.getRealmName() != null && invalidateApiKeyRequest.getRealmName().equals("realm-1")
|
||||
|| invalidateApiKeyRequest.getUserName() != null && invalidateApiKeyRequest.getUserName().equals("user-x")) {
|
||||
listener.onResponse((Response) invalidateApiKeyResponseExpected);
|
||||
|
|
|
@ -201,7 +201,7 @@ teardown:
|
|||
|
||||
|
||||
---
|
||||
"Test invalidate api key":
|
||||
"Test invalidate api keys":
|
||||
- skip:
|
||||
features: transform_and_set
|
||||
|
||||
|
@ -220,7 +220,7 @@ teardown:
|
|||
- is_true: id
|
||||
- is_true: api_key
|
||||
- is_true: expiration
|
||||
- set: { id: api_key_id }
|
||||
- set: { id: api_key_id_1 }
|
||||
- transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
|
||||
|
||||
- do:
|
||||
|
@ -229,10 +229,58 @@ teardown:
|
|||
security.invalidate_api_key:
|
||||
body: >
|
||||
{
|
||||
"id": "${api_key_id}"
|
||||
"id": "${api_key_id_1}"
|
||||
}
|
||||
- length: { "invalidated_api_keys" : 1 }
|
||||
- match: { "invalidated_api_keys.0" : "${api_key_id}" }
|
||||
- match: { "invalidated_api_keys.0" : "${api_key_id_1}" }
|
||||
- length: { "previously_invalidated_api_keys" : 0 }
|
||||
- match: { "error_count" : 0 }
|
||||
|
||||
- do:
|
||||
headers:
|
||||
Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
|
||||
security.create_api_key:
|
||||
body: >
|
||||
{
|
||||
"name": "my-api-key-2",
|
||||
"expiration": "1d",
|
||||
"role_descriptors": {
|
||||
}
|
||||
}
|
||||
- match: { name: "my-api-key-2" }
|
||||
- is_true: id
|
||||
- is_true: api_key
|
||||
- is_true: expiration
|
||||
- set: { id: api_key_id_2 }
|
||||
|
||||
- do:
|
||||
headers:
|
||||
Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
|
||||
security.create_api_key:
|
||||
body: >
|
||||
{
|
||||
"name": "my-api-key-3",
|
||||
"expiration": "1d",
|
||||
"role_descriptors": {
|
||||
}
|
||||
}
|
||||
- match: { name: "my-api-key-3" }
|
||||
- is_true: id
|
||||
- is_true: api_key
|
||||
- is_true: expiration
|
||||
- set: { id: api_key_id_3 }
|
||||
|
||||
- do:
|
||||
headers:
|
||||
Authorization: "Basic YXBpX2tleV91c2VyOngtcGFjay10ZXN0LXBhc3N3b3Jk" # api_key_user
|
||||
security.invalidate_api_key:
|
||||
body: >
|
||||
{
|
||||
"ids": [ "${api_key_id_1}", "${api_key_id_2}", "${api_key_id_3}" ]
|
||||
}
|
||||
- length: { "invalidated_api_keys" : 2 }
|
||||
- match: { "invalidated_api_keys.0" : "/^(${api_key_id_2}|${api_key_id_3})$/" }
|
||||
- match: { "invalidated_api_keys.1" : "/^(${api_key_id_2}|${api_key_id_3})$/" }
|
||||
- length: { "previously_invalidated_api_keys" : 0 }
|
||||
- match: { "error_count" : 0 }
|
||||
|
||||
|
|
Loading…
Reference in New Issue