Skip to content
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

Support getting active API keys #98259

Merged
merged 31 commits into from Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1177f01
Support getting active-only API keys
n1v0lg Aug 7, 2023
6b3df09
Tweaks
n1v0lg Aug 7, 2023
c18ed69
Update docs/changelog/98259.yaml
n1v0lg Aug 7, 2023
c66e265
Changelog
n1v0lg Aug 7, 2023
a5c017f
Merge branch 'main' into get-active-only-api-keys
n1v0lg Aug 7, 2023
e6a896c
Fix version
n1v0lg Aug 7, 2023
bda405a
Serialization tests
n1v0lg Aug 7, 2023
99bfcaa
Rest test
n1v0lg Aug 7, 2023
5226afa
Moar tests
n1v0lg Aug 7, 2023
369e3af
Version bump sadness
n1v0lg Aug 7, 2023
aa65d03
Yet more tests
n1v0lg Aug 7, 2023
7bc488f
No querying by id or name
n1v0lg Aug 7, 2023
d081b7c
Fix imports
n1v0lg Aug 7, 2023
6b29031
Test with multiple users
n1v0lg Aug 7, 2023
3c9c519
Dedicated test suite
n1v0lg Aug 8, 2023
ed37b3c
Cross cluster keys
n1v0lg Aug 8, 2023
03a5b61
Test 403
n1v0lg Aug 8, 2023
8b31881
Nit
n1v0lg Aug 8, 2023
0bba4b5
Update docs/changelog/98259.yaml
n1v0lg Aug 8, 2023
4b99009
Clearer constant
n1v0lg Aug 8, 2023
3ffedab
Unit test
n1v0lg Aug 8, 2023
75a2e87
Log message
n1v0lg Aug 8, 2023
22acfcb
WIP
n1v0lg Aug 8, 2023
f2522d6
Spotless
n1v0lg Aug 8, 2023
5177e1b
Merge branch 'main' into get-active-only-api-keys
elasticmachine Aug 8, 2023
772ac56
Merge branch 'main' into get-active-only-api-keys
n1v0lg Aug 8, 2023
c534c19
Merge branch 'get-active-only-api-keys' of github.com:n1v0lg/elastics…
n1v0lg Aug 8, 2023
a325cd7
Realm filter
n1v0lg Aug 8, 2023
21aae8a
Upper case
n1v0lg Aug 9, 2023
a03bbc3
Merge branch 'main' into get-active-only-api-keys
elasticmachine Aug 9, 2023
3af56c7
Merge branch 'main' into get-active-only-api-keys
elasticmachine Aug 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/changelog/98259.yaml
@@ -0,0 +1,6 @@
pr: 98259
summary: Support getting active-only API keys via Get API keys API
area: Security
type: enhancement
issues:
- 97995
3 changes: 2 additions & 1 deletion server/src/main/java/org/elasticsearch/TransportVersion.java
Expand Up @@ -176,9 +176,10 @@ private static TransportVersion registerTransportVersion(int id, String uniqueId
public static final TransportVersion V_8_500_051 = registerTransportVersion(8_500_051, "a28b43bc-bb5f-4406-afcf-26900aa98a71");
public static final TransportVersion V_8_500_052 = registerTransportVersion(8_500_052, "2d382b3d-9838-4cce-84c8-4142113e5c2b");
public static final TransportVersion V_8_500_053 = registerTransportVersion(8_500_053, "aa603bae-01e2-380a-8950-6604468e8c6d");
public static final TransportVersion V_8_500_054 = registerTransportVersion(8_500_054, "b76ef950-af03-4dda-85c2-6400ec442e7e");

private static class CurrentHolder {
private static final TransportVersion CURRENT = findCurrent(V_8_500_053);
private static final TransportVersion CURRENT = findCurrent(V_8_500_054);

// finds the pluggable current version, or uses the given fallback
private static TransportVersion findCurrent(TransportVersion fallback) {
Expand Down
Expand Up @@ -25,12 +25,15 @@
*/
public final class GetApiKeyRequest extends ActionRequest {

static TransportVersion API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION = TransportVersion.V_8_500_054;

private final String realmName;
private final String userName;
private final String apiKeyId;
private final String apiKeyName;
private final boolean ownedByAuthenticatedUser;
private final boolean withLimitedBy;
private final boolean activeOnly;

public GetApiKeyRequest(StreamInput in) throws IOException {
super(in);
Expand All @@ -48,6 +51,11 @@ public GetApiKeyRequest(StreamInput in) throws IOException {
} else {
withLimitedBy = false;
}
if (in.getTransportVersion().onOrAfter(API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION)) {
activeOnly = in.readBoolean();
} else {
activeOnly = false;
}
}

private GetApiKeyRequest(
Expand All @@ -56,14 +64,16 @@ private GetApiKeyRequest(
@Nullable String apiKeyId,
@Nullable String apiKeyName,
boolean ownedByAuthenticatedUser,
boolean withLimitedBy
boolean withLimitedBy,
boolean activeOnly
) {
this.realmName = textOrNull(realmName);
this.userName = textOrNull(userName);
this.apiKeyId = textOrNull(apiKeyId);
this.apiKeyName = textOrNull(apiKeyName);
this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
this.withLimitedBy = withLimitedBy;
this.activeOnly = activeOnly;
}

private static String textOrNull(@Nullable String arg) {
Expand Down Expand Up @@ -94,6 +104,10 @@ public boolean withLimitedBy() {
return withLimitedBy;
}

public boolean activeOnly() {
return activeOnly;
}

@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
Expand Down Expand Up @@ -132,6 +146,9 @@ public void writeTo(StreamOutput out) throws IOException {
if (out.getTransportVersion().onOrAfter(TransportVersion.V_8_5_0)) {
out.writeBoolean(withLimitedBy);
}
if (out.getTransportVersion().onOrAfter(API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION)) {
out.writeBoolean(activeOnly);
}
}

@Override
Expand All @@ -148,12 +165,13 @@ public boolean equals(Object o) {
&& Objects.equals(userName, that.userName)
&& Objects.equals(apiKeyId, that.apiKeyId)
&& Objects.equals(apiKeyName, that.apiKeyName)
&& withLimitedBy == that.withLimitedBy;
&& withLimitedBy == that.withLimitedBy
&& activeOnly == that.activeOnly;
}

@Override
public int hashCode() {
return Objects.hash(realmName, userName, apiKeyId, apiKeyName, ownedByAuthenticatedUser, withLimitedBy);
return Objects.hash(realmName, userName, apiKeyId, apiKeyName, ownedByAuthenticatedUser, withLimitedBy, activeOnly);
}

public static Builder builder() {
Expand All @@ -167,6 +185,7 @@ public static class Builder {
private String apiKeyName = null;
private boolean ownedByAuthenticatedUser = false;
private boolean withLimitedBy = false;
private boolean activeOnly = false;

public Builder realmName(String realmName) {
this.realmName = realmName;
Expand Down Expand Up @@ -206,8 +225,13 @@ public Builder withLimitedBy(boolean withLimitedBy) {
return this;
}

public Builder activeOnly(boolean activeOnly) {
this.activeOnly = activeOnly;
return this;
}

public GetApiKeyRequest build() {
return new GetApiKeyRequest(realmName, userName, apiKeyId, apiKeyName, ownedByAuthenticatedUser, withLimitedBy);
return new GetApiKeyRequest(realmName, userName, apiKeyId, apiKeyName, ownedByAuthenticatedUser, withLimitedBy, activeOnly);
}
}
}
Expand Up @@ -14,13 +14,15 @@
import org.elasticsearch.common.io.stream.OutputStreamStreamOutput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.TransportVersionUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.function.Supplier;

import static org.elasticsearch.test.TransportVersionUtils.randomVersionBetween;
import static org.elasticsearch.xpack.core.security.action.apikey.GetApiKeyRequest.API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
Expand All @@ -38,13 +40,17 @@ public void testRequestValidation() {
request = GetApiKeyRequest.builder().apiKeyName(randomAlphaOfLength(5)).ownedByAuthenticatedUser(randomBoolean()).build();
ve = request.validate();
assertNull(ve);
request = GetApiKeyRequest.builder().realmName(randomAlphaOfLength(5)).build();
request = GetApiKeyRequest.builder().realmName(randomAlphaOfLength(5)).activeOnly(randomBoolean()).build();
ve = request.validate();
assertNull(ve);
request = GetApiKeyRequest.builder().userName(randomAlphaOfLength(5)).build();
request = GetApiKeyRequest.builder().userName(randomAlphaOfLength(5)).activeOnly(randomBoolean()).build();
ve = request.validate();
assertNull(ve);
request = GetApiKeyRequest.builder().realmName(randomAlphaOfLength(5)).userName(randomAlphaOfLength(7)).build();
request = GetApiKeyRequest.builder()
.realmName(randomAlphaOfLength(5))
.userName(randomAlphaOfLength(7))
.activeOnly(randomBoolean())
.build();
ve = request.validate();
assertNull(ve);
}
Expand Down Expand Up @@ -79,6 +85,7 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalString(apiKeyName);
out.writeOptionalBoolean(ownedByAuthenticatedUser);
out.writeBoolean(randomBoolean());
out.writeBoolean(randomBoolean());
}
}

Expand Down Expand Up @@ -143,6 +150,7 @@ public void testSerialization() throws IOException {
.apiKeyId(apiKeyId)
.ownedByAuthenticatedUser(true)
.withLimitedBy(randomBoolean())
.activeOnly(randomBoolean())
.build();
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
Expand All @@ -157,17 +165,48 @@ public void testSerialization() throws IOException {
assertThat(requestFromInputStream.ownedByAuthenticatedUser(), is(true));
// old version so the default for `withLimitedBy` is false
assertThat(requestFromInputStream.withLimitedBy(), is(false));
// old version so the default for `activeOnly` is false
assertThat(requestFromInputStream.activeOnly(), is(false));
}
{
final GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.builder().apiKeyId(apiKeyId).withLimitedBy(randomBoolean()).build();
final GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.builder()
.apiKeyId(apiKeyId)
.ownedByAuthenticatedUser(randomBoolean())
.withLimitedBy(randomBoolean())
.activeOnly(randomBoolean())
.build();
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
out.setTransportVersion(randomVersionBetween(random(), TransportVersion.V_8_5_0, TransportVersion.current()));
TransportVersion beforeActiveOnly = TransportVersionUtils.getPreviousVersion(API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION);
out.setTransportVersion(randomVersionBetween(random(), TransportVersion.V_8_5_0, beforeActiveOnly));
getApiKeyRequest.writeTo(out);

InputStreamStreamInput inputStreamStreamInput = new InputStreamStreamInput(new ByteArrayInputStream(outBuffer.toByteArray()));
inputStreamStreamInput.setTransportVersion(randomVersionBetween(random(), TransportVersion.V_8_5_0, beforeActiveOnly));
GetApiKeyRequest requestFromInputStream = new GetApiKeyRequest(inputStreamStreamInput);

assertThat(requestFromInputStream.getApiKeyId(), equalTo(getApiKeyRequest.getApiKeyId()));
assertThat(requestFromInputStream.ownedByAuthenticatedUser(), is(getApiKeyRequest.ownedByAuthenticatedUser()));
assertThat(requestFromInputStream.withLimitedBy(), is(getApiKeyRequest.withLimitedBy()));
// old version so the default for `activeOnly` is false
assertThat(requestFromInputStream.activeOnly(), is(false));
}
{
final GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.builder()
.apiKeyId(apiKeyId)
.withLimitedBy(randomBoolean())
.activeOnly(randomBoolean())
.build();
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
OutputStreamStreamOutput out = new OutputStreamStreamOutput(outBuffer);
out.setTransportVersion(
randomVersionBetween(random(), API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION, TransportVersion.current())
);
getApiKeyRequest.writeTo(out);

InputStreamStreamInput inputStreamStreamInput = new InputStreamStreamInput(new ByteArrayInputStream(outBuffer.toByteArray()));
inputStreamStreamInput.setTransportVersion(
randomVersionBetween(random(), TransportVersion.V_8_5_0, TransportVersion.current())
randomVersionBetween(random(), API_KEY_ACTIVE_ONLY_PARAM_TRANSPORT_VERSION, TransportVersion.current())
);
GetApiKeyRequest requestFromInputStream = new GetApiKeyRequest(inputStreamStreamInput);

Expand Down