-
Notifications
You must be signed in to change notification settings - Fork 25k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Service Accounts - Get service account API (#71315)
This PR adds a new API endpoint to retrieve service accounts. Depends on the request parameters, it returns either all accounts, accounts belong to a namespace, a specific account, or an empty map if nothing is found.
- Loading branch information
Showing
13 changed files
with
626 additions
and
1 deletion.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
...in/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountAction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.action.ActionType; | ||
|
||
public class GetServiceAccountAction extends ActionType<GetServiceAccountResponse> { | ||
|
||
public static final String NAME = "cluster:admin/xpack/security/service_account/get"; | ||
public static final GetServiceAccountAction INSTANCE = new GetServiceAccountAction(); | ||
|
||
public GetServiceAccountAction() { | ||
super(NAME, GetServiceAccountResponse::new); | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
...n/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.action.ActionRequest; | ||
import org.elasticsearch.action.ActionRequestValidationException; | ||
import org.elasticsearch.common.Nullable; | ||
import org.elasticsearch.common.io.stream.StreamInput; | ||
import org.elasticsearch.common.io.stream.StreamOutput; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
public class GetServiceAccountRequest extends ActionRequest { | ||
|
||
@Nullable | ||
private final String namespace; | ||
@Nullable | ||
private final String serviceName; | ||
|
||
public GetServiceAccountRequest(@Nullable String namespace, @Nullable String serviceName) { | ||
this.namespace = namespace; | ||
this.serviceName = serviceName; | ||
} | ||
|
||
public GetServiceAccountRequest(StreamInput in) throws IOException { | ||
super(in); | ||
this.namespace = in.readOptionalString(); | ||
this.serviceName = in.readOptionalString(); | ||
} | ||
|
||
public String getNamespace() { | ||
return namespace; | ||
} | ||
|
||
public String getServiceName() { | ||
return serviceName; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
GetServiceAccountRequest that = (GetServiceAccountRequest) o; | ||
return Objects.equals(namespace, that.namespace) && Objects.equals(serviceName, that.serviceName); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(namespace, serviceName); | ||
} | ||
|
||
@Override | ||
public void writeTo(StreamOutput out) throws IOException { | ||
super.writeTo(out); | ||
out.writeOptionalString(namespace); | ||
out.writeOptionalString(serviceName); | ||
} | ||
|
||
@Override | ||
public ActionRequestValidationException validate() { | ||
return null; | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
.../java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.action.ActionResponse; | ||
import org.elasticsearch.common.io.stream.StreamInput; | ||
import org.elasticsearch.common.io.stream.StreamOutput; | ||
import org.elasticsearch.common.xcontent.ToXContentObject; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.Objects; | ||
|
||
public class GetServiceAccountResponse extends ActionResponse implements ToXContentObject { | ||
|
||
private final ServiceAccountInfo[] serviceAccountInfos; | ||
|
||
public GetServiceAccountResponse(ServiceAccountInfo[] serviceAccountInfos) { | ||
this.serviceAccountInfos = Objects.requireNonNull(serviceAccountInfos); | ||
} | ||
|
||
public GetServiceAccountResponse(StreamInput in) throws IOException { | ||
super(in); | ||
this.serviceAccountInfos = in.readArray(ServiceAccountInfo::new, ServiceAccountInfo[]::new); | ||
} | ||
|
||
public ServiceAccountInfo[] getServiceAccountInfos() { | ||
return serviceAccountInfos; | ||
} | ||
|
||
@Override | ||
public void writeTo(StreamOutput out) throws IOException { | ||
out.writeArray(serviceAccountInfos); | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject(); | ||
for (ServiceAccountInfo info : serviceAccountInfos) { | ||
info.toXContent(builder, params); | ||
} | ||
builder.endObject(); | ||
return builder; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "GetServiceAccountResponse{" + "serviceAccountInfos=" + Arrays.toString(serviceAccountInfos) + '}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
GetServiceAccountResponse that = (GetServiceAccountResponse) o; | ||
return Arrays.equals(serviceAccountInfos, that.serviceAccountInfos); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Arrays.hashCode(serviceAccountInfos); | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
...rc/main/java/org/elasticsearch/xpack/core/security/action/service/ServiceAccountInfo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.common.io.stream.StreamInput; | ||
import org.elasticsearch.common.io.stream.StreamOutput; | ||
import org.elasticsearch.common.io.stream.Writeable; | ||
import org.elasticsearch.common.xcontent.ToXContent; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; | ||
|
||
import java.io.IOException; | ||
import java.util.Objects; | ||
|
||
public class ServiceAccountInfo implements Writeable, ToXContent { | ||
|
||
private final String principal; | ||
private final RoleDescriptor roleDescriptor; | ||
|
||
public ServiceAccountInfo(String principal, RoleDescriptor roleDescriptor) { | ||
this.principal = Objects.requireNonNull(principal, "service account principal cannot be null"); | ||
this.roleDescriptor = Objects.requireNonNull(roleDescriptor, "service account descriptor cannot be null"); | ||
} | ||
|
||
public ServiceAccountInfo(StreamInput in) throws IOException { | ||
this.principal = in.readString(); | ||
this.roleDescriptor = new RoleDescriptor(in); | ||
} | ||
|
||
public String getPrincipal() { | ||
return principal; | ||
} | ||
|
||
public RoleDescriptor getRoleDescriptor() { | ||
return roleDescriptor; | ||
} | ||
|
||
@Override | ||
public void writeTo(StreamOutput out) throws IOException { | ||
out.writeString(principal); | ||
roleDescriptor.writeTo(out); | ||
} | ||
|
||
@Override | ||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
builder.startObject(principal); | ||
builder.field("role_descriptor"); | ||
roleDescriptor.toXContent(builder, params); | ||
builder.endObject(); | ||
return builder; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "ServiceAccountInfo{" + "principal='" + principal + '\'' + ", roleDescriptor=" + roleDescriptor + '}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) | ||
return true; | ||
if (o == null || getClass() != o.getClass()) | ||
return false; | ||
ServiceAccountInfo that = (ServiceAccountInfo) o; | ||
return principal.equals(that.principal) && roleDescriptor.equals(that.roleDescriptor); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(principal, roleDescriptor); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...a/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountRequestTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.common.io.stream.Writeable; | ||
import org.elasticsearch.test.AbstractWireSerializingTestCase; | ||
|
||
import java.io.IOException; | ||
|
||
public class GetServiceAccountRequestTests extends AbstractWireSerializingTestCase<GetServiceAccountRequest> { | ||
|
||
@Override | ||
protected Writeable.Reader<GetServiceAccountRequest> instanceReader() { | ||
return GetServiceAccountRequest::new; | ||
} | ||
|
||
@Override | ||
protected GetServiceAccountRequest createTestInstance() { | ||
return new GetServiceAccountRequest(randomFrom(randomAlphaOfLengthBetween(3, 8), null), | ||
randomFrom(randomAlphaOfLengthBetween(3, 8), null)); | ||
} | ||
|
||
@Override | ||
protected GetServiceAccountRequest mutateInstance(GetServiceAccountRequest instance) throws IOException { | ||
if (randomBoolean()) { | ||
return new GetServiceAccountRequest( | ||
randomValueOtherThan(instance.getNamespace(), () -> randomFrom(randomAlphaOfLengthBetween(3, 8), null)), | ||
instance.getServiceName()); | ||
} else { | ||
return new GetServiceAccountRequest( | ||
instance.getNamespace(), | ||
randomValueOtherThan(instance.getServiceName(), () -> randomFrom(randomAlphaOfLengthBetween(3, 8), null))); | ||
} | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
.../org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponseTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.core.security.action.service; | ||
|
||
import org.elasticsearch.common.bytes.BytesReference; | ||
import org.elasticsearch.common.io.stream.Writeable; | ||
import org.elasticsearch.common.xcontent.ToXContent; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
import org.elasticsearch.common.xcontent.XContentFactory; | ||
import org.elasticsearch.common.xcontent.XContentHelper; | ||
import org.elasticsearch.common.xcontent.XContentType; | ||
import org.elasticsearch.test.AbstractWireSerializingTestCase; | ||
import org.elasticsearch.test.XContentTestUtils; | ||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; | ||
|
||
import java.io.IOException; | ||
import java.util.Map; | ||
|
||
import static org.hamcrest.Matchers.anEmptyMap; | ||
import static org.hamcrest.Matchers.equalTo; | ||
|
||
public class GetServiceAccountResponseTests extends AbstractWireSerializingTestCase<GetServiceAccountResponse> { | ||
|
||
@Override | ||
protected Writeable.Reader<GetServiceAccountResponse> instanceReader() { | ||
return GetServiceAccountResponse::new; | ||
} | ||
|
||
@Override | ||
protected GetServiceAccountResponse createTestInstance() { | ||
final String principal = randomPrincipal(); | ||
return new GetServiceAccountResponse(randomBoolean() | ||
? new ServiceAccountInfo[]{new ServiceAccountInfo(principal, getRoleDescriptorFor(principal))} | ||
: new ServiceAccountInfo[0]); | ||
} | ||
|
||
@Override | ||
protected GetServiceAccountResponse mutateInstance(GetServiceAccountResponse instance) throws IOException { | ||
if (instance.getServiceAccountInfos().length == 0) { | ||
final String principal = randomPrincipal(); | ||
return new GetServiceAccountResponse(new ServiceAccountInfo[]{ | ||
new ServiceAccountInfo(principal, getRoleDescriptorFor(principal))}); | ||
} else { | ||
return new GetServiceAccountResponse(new ServiceAccountInfo[0]); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public void testToXContent() throws IOException { | ||
final GetServiceAccountResponse response = createTestInstance(); | ||
XContentBuilder builder = XContentFactory.jsonBuilder(); | ||
response.toXContent(builder, ToXContent.EMPTY_PARAMS); | ||
final Map<String, Object> responseMap = XContentHelper.convertToMap( | ||
BytesReference.bytes(builder), | ||
false, builder.contentType()).v2(); | ||
final ServiceAccountInfo[] serviceAccountInfos = response.getServiceAccountInfos(); | ||
if (serviceAccountInfos.length == 0) { | ||
assertThat(responseMap, anEmptyMap()); | ||
} else { | ||
assertThat(responseMap.size(), equalTo(serviceAccountInfos.length)); | ||
for (int i = 0; i < serviceAccountInfos.length - 1; i++) { | ||
final String key = serviceAccountInfos[i].getPrincipal(); | ||
assertRoleDescriptorEquals((Map<String, Object>) responseMap.get(key), serviceAccountInfos[i].getRoleDescriptor()); | ||
} | ||
} | ||
} | ||
|
||
private String randomPrincipal() { | ||
return randomAlphaOfLengthBetween(3, 8) + "/" + randomAlphaOfLengthBetween(3, 8); | ||
} | ||
|
||
private RoleDescriptor getRoleDescriptorFor(String name) { | ||
return new RoleDescriptor(name, | ||
new String[] { "monitor", "manage_own_api_key" }, | ||
new RoleDescriptor.IndicesPrivileges[] { | ||
RoleDescriptor.IndicesPrivileges.builder() | ||
.indices("logs-*", "metrics-*", "traces-*") | ||
.privileges("write", "create_index", "auto_configure").build() }, | ||
null, | ||
null, | ||
null, | ||
null, | ||
null); | ||
} | ||
|
||
private void assertRoleDescriptorEquals(Map<String, Object> responseFragment, RoleDescriptor roleDescriptor) throws IOException { | ||
@SuppressWarnings("unchecked") | ||
final Map<String, Object> descriptorMap = (Map<String, Object>) responseFragment.get("role_descriptor"); | ||
assertThat(RoleDescriptor.parse(roleDescriptor.getName(), | ||
XContentTestUtils.convertToXContent(descriptorMap, XContentType.JSON), false, XContentType.JSON), | ||
equalTo(roleDescriptor)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.