Skip to content

Commit 00496a5

Browse files
Merge branch '8.x' into backport/8.x/pr-115395
2 parents 340654f + 3b906f0 commit 00496a5

File tree

29 files changed

+1091
-184
lines changed

29 files changed

+1091
-184
lines changed

docs/changelog/114951.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 114951
2+
summary: Expose cluster-state role mappings in APIs
3+
area: Authentication
4+
type: bug
5+
issues: []

docs/changelog/115041.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 115041
2+
summary: Increase default `queue_capacity` to 10_000 and decrease max `queue_capacity`
3+
to 100_000
4+
area: Machine Learning
5+
type: enhancement
6+
issues: []

docs/changelog/115308.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 115308
2+
summary: "ESQL: Disable pushdown of WHERE past STATS"
3+
area: ES|QL
4+
type: bug
5+
issues:
6+
- 115281

muted-tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ tests:
409409
- class: org.elasticsearch.smoketest.DocsClientYamlTestSuiteIT
410410
method: test {yaml=reference/rest-api/usage/line_38}
411411
issue: https://github.com/elastic/elasticsearch/issues/113694
412+
- class: org.elasticsearch.xpack.security.operator.OperatorPrivilegesIT
413+
method: testEveryActionIsEitherOperatorOnlyOrNonOperator
414+
issue: https://github.com/elastic/elasticsearch/issues/102992
412415

413416
# Examples:
414417
#

qa/rolling-upgrade/src/javaRestTest/java/org/elasticsearch/upgrades/FileSettingsRoleMappingUpgradeIT.java

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

2626
import java.io.IOException;
2727
import java.util.List;
28+
import java.util.Map;
2829
import java.util.function.Supplier;
2930

31+
import static org.hamcrest.Matchers.contains;
3032
import static org.hamcrest.Matchers.equalTo;
33+
import static org.hamcrest.Matchers.instanceOf;
3134
import static org.hamcrest.Matchers.is;
3235
import static org.hamcrest.Matchers.not;
3336
import static org.hamcrest.Matchers.nullValue;
@@ -106,6 +109,10 @@ public void testRoleMappingsAppliedOnUpgrade() throws IOException {
106109
);
107110
assertThat(roleMappings, is(not(nullValue())));
108111
assertThat(roleMappings.size(), equalTo(1));
112+
assertThat(roleMappings, is(instanceOf(Map.class)));
113+
@SuppressWarnings("unchecked")
114+
Map<String, Object> roleMapping = (Map<String, Object>) roleMappings;
115+
assertThat(roleMapping.keySet(), contains("everyone_kibana-read-only-operator-mapping"));
109116
}
110117
}
111118
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/StartTrainedModelDeploymentAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public class StartTrainedModelDeploymentAction extends ActionType<CreateTrainedM
7171
public static final AllocationStatus.State DEFAULT_WAITFOR_STATE = AllocationStatus.State.STARTED;
7272
public static final int DEFAULT_NUM_ALLOCATIONS = 1;
7373
public static final int DEFAULT_NUM_THREADS = 1;
74-
public static final int DEFAULT_QUEUE_CAPACITY = 1024;
74+
public static final int DEFAULT_QUEUE_CAPACITY = 10_000;
7575
public static final Priority DEFAULT_PRIORITY = Priority.NORMAL;
7676

7777
public StartTrainedModelDeploymentAction() {
@@ -89,7 +89,7 @@ public static class Request extends MasterNodeRequest<Request> implements ToXCon
8989
/**
9090
* If the queue is created then we can OOM when we create the queue.
9191
*/
92-
private static final int MAX_QUEUE_CAPACITY = 1_000_000;
92+
private static final int MAX_QUEUE_CAPACITY = 100_000;
9393

9494
public static final ParseField MODEL_ID = new ParseField("model_id");
9595
public static final ParseField DEPLOYMENT_ID = new ParseField("deployment_id");

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/ExpressionRoleMapping.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@
5454
*/
5555
public class ExpressionRoleMapping implements ToXContentObject, Writeable {
5656

57+
/**
58+
* Reserved suffix for read-only operator-defined role mappings.
59+
* This suffix is added to the name of all cluster-state role mappings returned via
60+
* the {@code TransportGetRoleMappingsAction} action.
61+
*/
62+
public static final String READ_ONLY_ROLE_MAPPING_SUFFIX = "-read-only-operator-mapping";
63+
/**
64+
* Reserved metadata field to mark role mappings as read-only.
65+
* This field is added to the metadata of all cluster-state role mappings returned via
66+
* the {@code TransportGetRoleMappingsAction} action.
67+
*/
68+
public static final String READ_ONLY_ROLE_MAPPING_METADATA_FLAG = "_read_only";
5769
private static final ObjectParser<Builder, String> PARSER = new ObjectParser<>("role-mapping", Builder::new);
5870

5971
/**
@@ -136,6 +148,28 @@ public ExpressionRoleMapping(StreamInput in) throws IOException {
136148
this.metadata = in.readGenericMap();
137149
}
138150

151+
public static boolean hasReadOnlySuffix(String name) {
152+
return name.endsWith(READ_ONLY_ROLE_MAPPING_SUFFIX);
153+
}
154+
155+
public static void validateNoReadOnlySuffix(String name) {
156+
if (hasReadOnlySuffix(name)) {
157+
throw new IllegalArgumentException(
158+
"Invalid mapping name [" + name + "]. [" + READ_ONLY_ROLE_MAPPING_SUFFIX + "] is not an allowed suffix"
159+
);
160+
}
161+
}
162+
163+
public static String addReadOnlySuffix(String name) {
164+
return name + READ_ONLY_ROLE_MAPPING_SUFFIX;
165+
}
166+
167+
public static String removeReadOnlySuffixIfPresent(String name) {
168+
return name.endsWith(READ_ONLY_ROLE_MAPPING_SUFFIX)
169+
? name.substring(0, name.length() - READ_ONLY_ROLE_MAPPING_SUFFIX.length())
170+
: name;
171+
}
172+
139173
@Override
140174
public void writeTo(StreamOutput out) throws IOException {
141175
out.writeString(name);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleMappingMetadata.java

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
package org.elasticsearch.xpack.core.security.authz;
99

10+
import org.apache.logging.log4j.LogManager;
11+
import org.apache.logging.log4j.Logger;
1012
import org.elasticsearch.TransportVersion;
1113
import org.elasticsearch.TransportVersions;
1214
import org.elasticsearch.cluster.AbstractNamedDiffable;
@@ -26,8 +28,10 @@
2628
import java.io.IOException;
2729
import java.util.Collection;
2830
import java.util.EnumSet;
31+
import java.util.HashMap;
2932
import java.util.Iterator;
3033
import java.util.LinkedHashSet;
34+
import java.util.Map;
3135
import java.util.Objects;
3236
import java.util.Set;
3337

@@ -36,7 +40,11 @@
3640

3741
public final class RoleMappingMetadata extends AbstractNamedDiffable<Metadata.Custom> implements Metadata.Custom {
3842

43+
private static final Logger logger = LogManager.getLogger(RoleMappingMetadata.class);
44+
3945
public static final String TYPE = "role_mappings";
46+
public static final String METADATA_NAME_FIELD = "_es_reserved_role_mapping_name";
47+
public static final String FALLBACK_NAME = "name_not_available_after_deserialization";
4048

4149
@SuppressWarnings("unchecked")
4250
private static final ConstructingObjectParser<RoleMappingMetadata, Void> PARSER = new ConstructingObjectParser<>(
@@ -46,12 +54,7 @@ public final class RoleMappingMetadata extends AbstractNamedDiffable<Metadata.Cu
4654
);
4755

4856
static {
49-
PARSER.declareObjectArray(
50-
constructorArg(),
51-
// role mapping names are lost when the role mapping metadata is serialized
52-
(p, c) -> ExpressionRoleMapping.parse("name_not_available_after_deserialization", p),
53-
new ParseField(TYPE)
54-
);
57+
PARSER.declareObjectArray(constructorArg(), (p, c) -> parseWithNameFromMetadata(p), new ParseField(TYPE));
5558
}
5659

5760
private static final RoleMappingMetadata EMPTY = new RoleMappingMetadata(Set.of());
@@ -153,4 +156,64 @@ public EnumSet<Metadata.XContentContext> context() {
153156
// are not persisted.
154157
return ALL_CONTEXTS;
155158
}
159+
160+
/**
161+
* Ensures role mapping names are preserved when stored on disk using XContent format,
162+
* which omits names. This method copies the role mapping's name into a reserved metadata field
163+
* during serialization, allowing recovery during deserialization (e.g., after a master-node restart).
164+
* {@link #parseWithNameFromMetadata(XContentParser)} restores the name during parsing.
165+
*/
166+
public static ExpressionRoleMapping copyWithNameInMetadata(ExpressionRoleMapping roleMapping) {
167+
Map<String, Object> metadata = new HashMap<>(roleMapping.getMetadata());
168+
// note: can't use Maps.copyWith... since these create maps that don't support `null` values in map entries
169+
if (metadata.put(METADATA_NAME_FIELD, roleMapping.getName()) != null) {
170+
logger.error(
171+
"Metadata field [{}] is reserved and will be overwritten with an internal system value. "
172+
+ "Rename this field in your role mapping configuration.",
173+
METADATA_NAME_FIELD
174+
);
175+
}
176+
return new ExpressionRoleMapping(
177+
roleMapping.getName(),
178+
roleMapping.getExpression(),
179+
roleMapping.getRoles(),
180+
roleMapping.getRoleTemplates(),
181+
metadata,
182+
roleMapping.isEnabled()
183+
);
184+
}
185+
186+
/**
187+
* If a role mapping does not yet have a name persisted in metadata, it will use a constant fallback name. This method checks if a
188+
* role mapping has the fallback name.
189+
*/
190+
public static boolean hasFallbackName(ExpressionRoleMapping expressionRoleMapping) {
191+
return expressionRoleMapping.getName().equals(FALLBACK_NAME);
192+
}
193+
194+
/**
195+
* Parse a role mapping from XContent, restoring the name from a reserved metadata field.
196+
* Used to parse a role mapping annotated with its name in metadata via @see {@link #copyWithNameInMetadata(ExpressionRoleMapping)}.
197+
*/
198+
public static ExpressionRoleMapping parseWithNameFromMetadata(XContentParser parser) throws IOException {
199+
ExpressionRoleMapping roleMapping = ExpressionRoleMapping.parse(FALLBACK_NAME, parser);
200+
return new ExpressionRoleMapping(
201+
getNameFromMetadata(roleMapping),
202+
roleMapping.getExpression(),
203+
roleMapping.getRoles(),
204+
roleMapping.getRoleTemplates(),
205+
roleMapping.getMetadata(),
206+
roleMapping.isEnabled()
207+
);
208+
}
209+
210+
private static String getNameFromMetadata(ExpressionRoleMapping roleMapping) {
211+
Map<String, Object> metadata = roleMapping.getMetadata();
212+
if (metadata.containsKey(METADATA_NAME_FIELD) && metadata.get(METADATA_NAME_FIELD) instanceof String name) {
213+
return name;
214+
} else {
215+
// This is valid the first time we recover from cluster-state: the old format metadata won't have a name stored in metadata yet
216+
return FALLBACK_NAME;
217+
}
218+
}
156219
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/action/StartTrainedModelDeploymentRequestTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static Request createRandom() {
6767
request.setNumberOfAllocations(randomIntBetween(1, 8));
6868
}
6969
if (randomBoolean()) {
70-
request.setQueueCapacity(randomIntBetween(1, 1000000));
70+
request.setQueueCapacity(randomIntBetween(1, 100_000));
7171
}
7272
if (randomBoolean()) {
7373
request.setPriority(randomFrom(Priority.values()).toString());
@@ -168,7 +168,7 @@ public void testValidate_GivenQueueCapacityIsNegative() {
168168

169169
public void testValidate_GivenQueueCapacityIsAtLimit() {
170170
Request request = createRandom();
171-
request.setQueueCapacity(1_000_000);
171+
request.setQueueCapacity(100_000);
172172

173173
ActionRequestValidationException e = request.validate();
174174

@@ -177,12 +177,12 @@ public void testValidate_GivenQueueCapacityIsAtLimit() {
177177

178178
public void testValidate_GivenQueueCapacityIsOverLimit() {
179179
Request request = createRandom();
180-
request.setQueueCapacity(1_000_001);
180+
request.setQueueCapacity(100_001);
181181

182182
ActionRequestValidationException e = request.validate();
183183

184184
assertThat(e, is(not(nullValue())));
185-
assertThat(e.getMessage(), containsString("[queue_capacity] must be less than 1000000"));
185+
assertThat(e.getMessage(), containsString("[queue_capacity] must be less than 100000"));
186186
}
187187

188188
public void testValidate_GivenTimeoutIsNegative() {
@@ -234,6 +234,6 @@ public void testDefaults() {
234234
assertThat(request.getNumberOfAllocations(), nullValue());
235235
assertThat(request.computeNumberOfAllocations(), equalTo(1));
236236
assertThat(request.getThreadsPerAllocation(), equalTo(1));
237-
assertThat(request.getQueueCapacity(), equalTo(1024));
237+
assertThat(request.getQueueCapacity(), equalTo(10_000));
238238
}
239239
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,33 @@ m:integer |a:double |x:integer
22912291
74999 |48249.0 |0
22922292
;
22932293

2294+
statsWithFilterOnGroups
2295+
required_capability: fix_filter_pushdown_past_stats
2296+
FROM employees
2297+
| STATS v = VALUES(emp_no) by job_positions | WHERE job_positions == "Accountant" | MV_EXPAND v | SORT v
2298+
;
2299+
2300+
v:integer | job_positions:keyword
2301+
10001 | Accountant
2302+
10012 | Accountant
2303+
10016 | Accountant
2304+
10023 | Accountant
2305+
10025 | Accountant
2306+
10028 | Accountant
2307+
10034 | Accountant
2308+
10037 | Accountant
2309+
10044 | Accountant
2310+
10045 | Accountant
2311+
10050 | Accountant
2312+
10051 | Accountant
2313+
10066 | Accountant
2314+
10081 | Accountant
2315+
10085 | Accountant
2316+
10089 | Accountant
2317+
10092 | Accountant
2318+
10094 | Accountant
2319+
;
2320+
22942321

22952322
statsWithFiltering
22962323
required_capability: per_agg_filtering

0 commit comments

Comments
 (0)