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 metadata on API keys #70292

Merged
merged 20 commits into from
Mar 28, 2021
Merged
Show file tree
Hide file tree
Changes from 11 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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
Expand All @@ -28,19 +29,28 @@ public final class CreateApiKeyRequest implements Validatable, ToXContentObject
private final TimeValue expiration;
private final List<Role> roles;
private final RefreshPolicy refreshPolicy;
private final Map<String, Object> metadata;

/**
* Create API Key request constructor
* @param name name for the API key
* @param roles list of {@link Role}s
* @param expiration to specify expiration for the API key
* @param metadata Arbitrary metadata for the API key
*/
public CreateApiKeyRequest(String name, List<Role> roles, @Nullable TimeValue expiration,
@Nullable final RefreshPolicy refreshPolicy) {
@Nullable final RefreshPolicy refreshPolicy,
@Nullable Map<String, Object> metadata) {
this.name = name;
this.roles = Objects.requireNonNull(roles, "roles may not be null");
this.expiration = expiration;
this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy;
this.metadata = metadata;
}

public CreateApiKeyRequest(String name, List<Role> roles, @Nullable TimeValue expiration,
@Nullable final RefreshPolicy refreshPolicy) {
this(name, roles, expiration, refreshPolicy, null);
}

public String getName() {
Expand All @@ -59,6 +69,10 @@ public RefreshPolicy getRefreshPolicy() {
return refreshPolicy;
}

public Map<String, Object> getMetadata() {
return metadata;
}

@Override
public int hashCode() {
return Objects.hash(name, refreshPolicy, roles, expiration);
ywangd marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -74,7 +88,7 @@ public boolean equals(Object o) {
}
final CreateApiKeyRequest that = (CreateApiKeyRequest) o;
return Objects.equals(name, that.name) && Objects.equals(refreshPolicy, that.refreshPolicy) && Objects.equals(roles, that.roles)
&& Objects.equals(expiration, that.expiration);
&& Objects.equals(expiration, that.expiration) && Objects.equals(metadata, that.metadata);
}

@Override
Expand Down Expand Up @@ -107,6 +121,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
builder.endObject();
}
builder.endObject();
if (metadata != null) {
builder.field("metadata", metadata);
}
return builder.endObject();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import java.io.IOException;
import java.time.Instant;
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
Expand All @@ -32,8 +33,10 @@ public final class ApiKey {
private final boolean invalidated;
private final String username;
private final String realm;
private final Map<String, Object> metadata;

public ApiKey(String name, String id, Instant creation, Instant expiration, boolean invalidated, String username, String realm) {
public ApiKey(String name, String id, Instant creation, Instant expiration, boolean invalidated, String username, String realm,
Map<String, Object> metadata) {
this.name = name;
this.id = id;
// As we do not yet support the nanosecond precision when we serialize to JSON,
Expand All @@ -44,6 +47,7 @@ public ApiKey(String name, String id, Instant creation, Instant expiration, bool
this.invalidated = invalidated;
this.username = username;
this.realm = realm;
this.metadata = metadata;
}

public String getId() {
Expand Down Expand Up @@ -90,6 +94,10 @@ public String getRealm() {
return realm;
}

public Map<String, Object> getMetadata() {
return metadata;
}

@Override
public int hashCode() {
return Objects.hash(name, id, creation, expiration, invalidated, username, realm);
ywangd marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -113,12 +121,15 @@ public boolean equals(Object obj) {
&& Objects.equals(expiration, other.expiration)
&& Objects.equals(invalidated, other.invalidated)
&& Objects.equals(username, other.username)
&& Objects.equals(realm, other.realm);
&& Objects.equals(realm, other.realm)
&& Objects.equals(metadata, other.metadata);
}

@SuppressWarnings("unchecked")
static final ConstructingObjectParser<ApiKey, Void> PARSER = new ConstructingObjectParser<>("api_key", args -> {
return new ApiKey((String) args[0], (String) args[1], Instant.ofEpochMilli((Long) args[2]),
(args[3] == null) ? null : Instant.ofEpochMilli((Long) args[3]), (Boolean) args[4], (String) args[5], (String) args[6]);
(args[3] == null) ? null : Instant.ofEpochMilli((Long) args[3]), (Boolean) args[4], (String) args[5], (String) args[6],
(Map<String, Object>) args[7]);
});
static {
PARSER.declareField(optionalConstructorArg(), (p, c) -> p.textOrNull(), new ParseField("name"),
Expand All @@ -129,6 +140,7 @@ public boolean equals(Object obj) {
PARSER.declareBoolean(constructorArg(), new ParseField("invalidated"));
PARSER.declareString(constructorArg(), new ParseField("username"));
PARSER.declareString(constructorArg(), new ParseField("realm"));
PARSER.declareObject(optionalConstructorArg(), (p, c) -> p.map(), new ParseField("metadata"));
}

public static ApiKey fromXContent(XContentParser parser) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.apache.http.client.methods.HttpPut;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.CreateApiKeyRequest;
import org.elasticsearch.client.security.CreateApiKeyRequestTests;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.DelegatePkiAuthenticationRequest;
import org.elasticsearch.client.security.DeletePrivilegesRequest;
Expand Down Expand Up @@ -449,7 +450,8 @@ private CreateApiKeyRequest buildCreateApiKeyRequest() {
.indicesPrivileges(IndicesPrivileges.builder().indices("ind-x").privileges(IndexPrivilegeName.ALL).build()).build());
final TimeValue expiration = randomBoolean() ? null : TimeValue.timeValueHours(24);
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
final Map<String, Object> metadata = CreateApiKeyRequestTests.randomMetadata();
final CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy, metadata);
return createApiKeyRequest;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.elasticsearch.client.security.ClearRolesCacheResponse;
import org.elasticsearch.client.security.ClearSecurityCacheResponse;
import org.elasticsearch.client.security.CreateApiKeyRequest;
import org.elasticsearch.client.security.CreateApiKeyRequestTests;
import org.elasticsearch.client.security.CreateApiKeyResponse;
import org.elasticsearch.client.security.CreateTokenRequest;
import org.elasticsearch.client.security.CreateTokenResponse;
Expand Down Expand Up @@ -1957,10 +1958,11 @@ public void testCreateApiKey() throws Exception {
.indicesPrivileges(IndicesPrivileges.builder().indices("ind-x").privileges(IndexPrivilegeName.ALL).build()).build());
final TimeValue expiration = TimeValue.timeValueHours(24);
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, Object> metadata = CreateApiKeyRequestTests.randomMetadata();
{
final String name = randomAlphaOfLength(5);
// tag::create-api-key-request
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy, metadata);
// end::create-api-key-request

// tag::create-api-key-execute
Expand All @@ -1978,7 +1980,7 @@ public void testCreateApiKey() throws Exception {

{
final String name = randomAlphaOfLength(5);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy, metadata);

ActionListener<CreateApiKeyResponse> listener;
// tag::create-api-key-execute-listener
Expand Down Expand Up @@ -2027,6 +2029,7 @@ public void testGrantApiKey() throws Exception {


final Instant start = Instant.now();
final Map<String, Object> metadata = CreateApiKeyRequestTests.randomMetadata();
CheckedConsumer<CreateApiKeyResponse, IOException> apiKeyVerifier = (created) -> {
final GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(created.getId(), false);
final GetApiKeyResponse getApiKeyResponse = client.security().getApiKey(getApiKeyRequest, RequestOptions.DEFAULT);
Expand All @@ -2039,14 +2042,19 @@ public void testGrantApiKey() throws Exception {
assertThat(apiKeyInfo.isInvalidated(), equalTo(false));
assertThat(apiKeyInfo.getCreation(), greaterThanOrEqualTo(start));
assertThat(apiKeyInfo.getCreation(), lessThanOrEqualTo(Instant.now()));
if (metadata == null) {
assertThat(apiKeyInfo.getMetadata(), equalTo(Map.of()));
} else {
assertThat(apiKeyInfo.getMetadata(), equalTo(metadata));
}
};

final TimeValue expiration = TimeValue.timeValueHours(24);
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
{
final String name = randomAlphaOfLength(5);
// tag::grant-api-key-request
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy, metadata);
GrantApiKeyRequest.Grant grant = GrantApiKeyRequest.Grant.passwordGrant(username, password);
GrantApiKeyRequest grantApiKeyRequest = new GrantApiKeyRequest(grant, createApiKeyRequest);
// end::grant-api-key-request
Expand All @@ -2071,7 +2079,7 @@ public void testGrantApiKey() throws Exception {
final CreateTokenRequest tokenRequest = CreateTokenRequest.passwordGrant(username, password);
final CreateTokenResponse token = client.security().createToken(tokenRequest, RequestOptions.DEFAULT);

CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest(name, roles, expiration, refreshPolicy, metadata);
GrantApiKeyRequest.Grant grant = GrantApiKeyRequest.Grant.accessTokenGrant(token.getAccessToken());
GrantApiKeyRequest grantApiKeyRequest = new GrantApiKeyRequest(grant, createApiKeyRequest);

Expand Down Expand Up @@ -2117,14 +2125,15 @@ public void testGetApiKey() throws Exception {
.indicesPrivileges(IndicesPrivileges.builder().indices("ind-x").privileges(IndexPrivilegeName.ALL).build()).build());
final TimeValue expiration = TimeValue.timeValueHours(24);
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, Object> metadata = CreateApiKeyRequestTests.randomMetadata();
// Create API Keys
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest("k1", roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest("k1", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse1 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse1.getName(), equalTo("k1"));
assertNotNull(createApiKeyResponse1.getKey());

final ApiKey expectedApiKeyInfo = new ApiKey(createApiKeyResponse1.getName(), createApiKeyResponse1.getId(), Instant.now(),
Instant.now().plusMillis(expiration.getMillis()), false, "test_user", "default_file");
Instant.now().plusMillis(expiration.getMillis()), false, "test_user", "default_file", metadata);
{
// tag::get-api-key-id-request
GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
Expand Down Expand Up @@ -2258,6 +2267,11 @@ private void verifyApiKey(final ApiKey actual, final ApiKey expected) {
assertThat(actual.getRealm(), is(expected.getRealm()));
assertThat(actual.isInvalidated(), is(expected.isInvalidated()));
assertThat(actual.getExpiration(), is(greaterThan(Instant.now())));
if (expected.getMetadata() == null) {
assertThat(actual.getMetadata(), equalTo(Map.of()));
} else {
assertThat(actual.getMetadata(), equalTo(expected.getMetadata()));
}
}

public void testInvalidateApiKey() throws Exception {
Expand All @@ -2267,8 +2281,9 @@ public void testInvalidateApiKey() throws Exception {
.indicesPrivileges(IndicesPrivileges.builder().indices("ind-x").privileges(IndexPrivilegeName.ALL).build()).build());
final TimeValue expiration = TimeValue.timeValueHours(24);
final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values());
final Map<String, Object> metadata = CreateApiKeyRequestTests.randomMetadata();
// Create API Keys
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest("k1", roles, expiration, refreshPolicy);
CreateApiKeyRequest createApiKeyRequest = new CreateApiKeyRequest("k1", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse1 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse1.getName(), equalTo("k1"));
assertNotNull(createApiKeyResponse1.getKey());
Expand Down Expand Up @@ -2312,7 +2327,7 @@ public void testInvalidateApiKey() throws Exception {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k2", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k2", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse2 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse2.getName(), equalTo("k2"));
assertNotNull(createApiKeyResponse2.getKey());
Expand All @@ -2336,7 +2351,7 @@ public void testInvalidateApiKey() throws Exception {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k3", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k3", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse3 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse3.getName(), equalTo("k3"));
assertNotNull(createApiKeyResponse3.getKey());
Expand All @@ -2359,7 +2374,7 @@ public void testInvalidateApiKey() throws Exception {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k4", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k4", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse4 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse4.getName(), equalTo("k4"));
assertNotNull(createApiKeyResponse4.getKey());
Expand All @@ -2382,7 +2397,7 @@ public void testInvalidateApiKey() throws Exception {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k5", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k5", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse5 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse5.getName(), equalTo("k5"));
assertNotNull(createApiKeyResponse5.getKey());
Expand All @@ -2407,7 +2422,7 @@ public void testInvalidateApiKey() throws Exception {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k6", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k6", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse6 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse6.getName(), equalTo("k6"));
assertNotNull(createApiKeyResponse6.getKey());
Expand Down Expand Up @@ -2450,7 +2465,7 @@ public void onFailure(Exception e) {
}

{
createApiKeyRequest = new CreateApiKeyRequest("k7", roles, expiration, refreshPolicy);
createApiKeyRequest = new CreateApiKeyRequest("k7", roles, expiration, refreshPolicy, metadata);
CreateApiKeyResponse createApiKeyResponse7 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
assertThat(createApiKeyResponse7.getName(), equalTo("k7"));
assertNotNull(createApiKeyResponse7.getKey());
Expand Down
Loading