Skip to content

Commit 4fe3ef9

Browse files
committed
Allow Fleet post secret and get secret to accept array of strings
Add tests Fix tests [CI] Auto commit changes from spotless
1 parent dd2a5c6 commit 4fe3ef9

File tree

7 files changed

+116
-20
lines changed

7 files changed

+116
-20
lines changed

x-pack/plugin/fleet/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/fleet/30_secrets_post.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@
77
fleet.delete_secret:
88
id: $id
99

10+
---
11+
"Create Fleet Secret with multiple values":
12+
- do:
13+
fleet.post_secret:
14+
body: '{"value": ["test secret 1", "test secret 2"]}'
15+
- set: { id: id }
16+
- do:
17+
fleet.delete_secret:
18+
id: $id
19+
1020
---
1121
"Create secret fails for unprivileged user":
1222
- skip:

x-pack/plugin/fleet/qa/rest/src/yamlRestTest/resources/rest-api-spec/test/fleet/40_secrets_get.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,27 @@
1818
fleet.delete_secret:
1919
id: $id
2020

21+
---
22+
"Get Fleet Secret with multiple values":
23+
- do:
24+
fleet.post_secret:
25+
body: '{"value": ["test secret 1", "test secret 2"]}'
26+
- set: { id: id }
27+
# search node needs to be available for fleet.get_secret to work in stateless.
28+
# The `.fleet-secrets` index is created on demand, and its search replica starts out unassigned,
29+
# so wait_for_no_uninitialized_shards can miss it.
30+
- do:
31+
cluster.health:
32+
wait_for_active_shards: all
33+
- do:
34+
fleet.get_secret:
35+
id: $id
36+
- match: { id: $id }
37+
- match: { value: ["test secret 1", "test secret 2"] }
38+
- do:
39+
fleet.delete_secret:
40+
id: $id
41+
2142
---
2243
"Get non existent Fleet Secret":
2344
- do:

x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/GetSecretResponse.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,19 @@
2020
public class GetSecretResponse extends ActionResponse implements ToXContentObject {
2121

2222
private final String id;
23-
private final String value;
23+
private final Object value;
2424

2525
public GetSecretResponse(StreamInput in) throws IOException {
2626
super(in);
2727
id = in.readString();
28-
value = in.readString();
28+
if (in.readByte() == 0) {
29+
value = in.readString();
30+
} else {
31+
value = in.readStringArray();
32+
}
2933
}
3034

31-
public GetSecretResponse(String id, String value) {
35+
public GetSecretResponse(String id, Object value) {
3236
this.id = id;
3337
this.value = value;
3438
}
@@ -40,15 +44,24 @@ public String id() {
4044
@Override
4145
public void writeTo(StreamOutput out) throws IOException {
4246
out.writeString(id);
43-
out.writeString(value);
47+
if (value instanceof String) {
48+
out.writeString((String) value);
49+
} else if (value instanceof String[]) {
50+
out.writeStringArray((String[]) value);
51+
}
4452
}
4553

4654
@Override
4755
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
4856
builder.startObject();
4957
builder.field("id", id);
50-
builder.field("value", value);
51-
return builder.endObject();
58+
if (value instanceof String) {
59+
builder.field("value", (String) value);
60+
} else if (value instanceof String[]) {
61+
builder.field("value", (String[]) value);
62+
}
63+
builder.endObject();
64+
return builder;
5265
}
5366

5467
@Override

x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/PostSecretRequest.java

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,49 +27,69 @@ public class PostSecretRequest extends ActionRequest {
2727
public static final ConstructingObjectParser<PostSecretRequest, Void> PARSER = new ConstructingObjectParser<>(
2828
"post_secret_request",
2929
args -> {
30-
return new PostSecretRequest((String) args[0]);
30+
return new PostSecretRequest(args[0]);
3131
}
3232
);
3333

3434
static {
35-
PARSER.declareField(
36-
ConstructingObjectParser.optionalConstructorArg(),
37-
(p, c) -> p.text(),
38-
VALUE_FIELD,
39-
ObjectParser.ValueType.STRING
40-
);
35+
PARSER.declareField(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> {
36+
if (p.currentToken() == XContentParser.Token.VALUE_STRING) {
37+
return p.text();
38+
} else if (p.currentToken() == XContentParser.Token.START_ARRAY) {
39+
return p.list().stream().map(s -> (String) s).toArray(String[]::new);
40+
} else {
41+
throw new IllegalArgumentException("Unexpected token: " + p.currentToken());
42+
}
43+
}, VALUE_FIELD, ObjectParser.ValueType.STRING_ARRAY);
4144
}
4245

4346
public static PostSecretRequest fromXContent(XContentParser parser) throws IOException {
4447
return PARSER.parse(parser, null);
4548
}
4649

47-
private final String value;
50+
private final Object value;
4851

49-
public PostSecretRequest(String value) {
52+
public PostSecretRequest(Object value) {
53+
if (!(value instanceof String) && !(value instanceof String[])) {
54+
throw new IllegalArgumentException("value must be a string or an array of strings");
55+
}
5056
this.value = value;
5157
}
5258

5359
public PostSecretRequest(StreamInput in) throws IOException {
5460
super(in);
55-
this.value = in.readString();
61+
if (in.readByte() == 0) {
62+
this.value = in.readString();
63+
} else {
64+
this.value = in.readStringArray();
65+
}
5666
}
5767

58-
public String value() {
68+
public Object value() {
5969
return value;
6070
}
6171

6272
public XContentBuilder toXContent(XContentBuilder builder) throws IOException {
6373
builder.startObject();
64-
builder.field(VALUE_FIELD.getPreferredName(), this.value);
74+
if (value instanceof String) {
75+
builder.field(VALUE_FIELD.getPreferredName(), (String) value);
76+
} else if (value instanceof String[]) {
77+
builder.field(VALUE_FIELD.getPreferredName(), (String[]) value);
78+
}
6579
builder.endObject();
6680
return builder;
6781
}
6882

6983
@Override
7084
public void writeTo(StreamOutput out) throws IOException {
7185
super.writeTo(out);
72-
out.writeString(value);
86+
if (value instanceof String) {
87+
out.writeBoolean(true);
88+
out.writeString((String) value);
89+
} else if (value instanceof String[]) {
90+
out.writeBoolean(false);
91+
out.writeStringArray((String[]) value);
92+
}
7393
}
7494

7595
@Override

x-pack/plugin/fleet/src/main/java/org/elasticsearch/xpack/fleet/action/TransportGetSecretAction.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import org.elasticsearch.tasks.Task;
1919
import org.elasticsearch.transport.TransportService;
2020

21+
import java.util.List;
22+
2123
import static org.elasticsearch.xpack.core.ClientHelper.FLEET_ORIGIN;
2224
import static org.elasticsearch.xpack.fleet.Fleet.FLEET_SECRETS_INDEX_NAME;
2325

@@ -36,7 +38,19 @@ protected void doExecute(Task task, GetSecretRequest request, ActionListener<Get
3638
delegate.onFailure(new ResourceNotFoundException("No secret with id [" + request.id() + "]"));
3739
return;
3840
}
39-
delegate.onResponse(new GetSecretResponse(getResponse.getId(), getResponse.getSource().get("value").toString()));
41+
Object value = getResponse.getSource().get("value");
42+
if (value instanceof String) {
43+
delegate.onResponse(new GetSecretResponse(getResponse.getId(), value.toString()));
44+
} else if (value instanceof List) {
45+
List<?> valueList = (List<?>) value;
46+
if (valueList.stream().allMatch(item -> item instanceof String)) {
47+
delegate.onResponse(new GetSecretResponse(getResponse.getId(), valueList.toArray(new String[0])));
48+
} else {
49+
delegate.onFailure(new IllegalArgumentException("Unexpected value type in list for id [" + request.id() + "]"));
50+
}
51+
} else {
52+
delegate.onFailure(new IllegalArgumentException("Unexpected value type for id [" + request.id() + "]"));
53+
}
4054
}));
4155
}
4256
}

x-pack/plugin/fleet/src/test/java/org/elasticsearch/xpack/fleet/action/GetSecretResponseTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77

88
package org.elasticsearch.xpack.fleet.action;
99

10+
import org.elasticsearch.action.ActionResponseValidationException;
1011
import org.elasticsearch.common.io.stream.Writeable;
1112
import org.elasticsearch.test.AbstractWireSerializingTestCase;
1213

14+
import static org.junit.Assert.assertArrayEquals;
15+
1316
public class GetSecretResponseTests extends AbstractWireSerializingTestCase<GetSecretResponse> {
1417

1518
@Override
@@ -26,4 +29,12 @@ protected GetSecretResponse createTestInstance() {
2629
protected GetSecretResponse mutateInstance(GetSecretResponse instance) {
2730
return new GetSecretResponse(instance.id(), randomAlphaOfLength(10));
2831
}
32+
33+
public void testValidateResponseWithMultiValue() {
34+
String[] secrets = { "secret1", "secret2" };
35+
GetSecretResponse res = new GetSecretResponse(randomAlphaOfLength(10), secrets);
36+
ActionResponseValidationException e = res.validate();
37+
assertNull(e);
38+
assertArrayEquals(secrets, (String[]) res.value());
39+
}
2940
}

x-pack/plugin/fleet/src/test/java/org/elasticsearch/xpack/fleet/action/PostSecretRequestTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ public void testValidateRequest() {
3737
assertNull(e);
3838
}
3939

40+
public void testValidateRequestWithMultiValue() {
41+
String[] secrets = { "secret1", "secret2" };
42+
PostSecretRequest req = new PostSecretRequest(secrets);
43+
ActionRequestValidationException e = req.validate();
44+
assertNull(e);
45+
}
46+
4047
public void testValidateRequestWithoutValue() {
4148
PostSecretRequest req = new PostSecretRequest((String) null);
4249
ActionRequestValidationException e = req.validate();

0 commit comments

Comments
 (0)