Skip to content

Commit 5de3d08

Browse files
authored
Ensure entitlements have exactly one external constructor (elastic#120665) (elastic#120688)
When an entitlement is available to policy files, it should have one constructor that the parser uses. This commit adjusts the policy parser to scan the constructor to find that one annotated constructor, and errors if more than one is found.
1 parent af83c2e commit 5de3d08

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyParser.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class PolicyParser {
4848
protected final XContentParser policyParser;
4949
protected final String policyName;
5050
private final boolean isExternalPlugin;
51+
private final Map<String, Class<?>> externalEntitlements;
5152

5253
static String getEntitlementTypeName(Class<? extends Entitlement> entitlementClass) {
5354
var entitlementClassName = entitlementClass.getSimpleName();
@@ -66,9 +67,16 @@ static String getEntitlementTypeName(Class<? extends Entitlement> entitlementCla
6667
}
6768

6869
public PolicyParser(InputStream inputStream, String policyName, boolean isExternalPlugin) throws IOException {
70+
this(inputStream, policyName, isExternalPlugin, EXTERNAL_ENTITLEMENTS);
71+
}
72+
73+
// package private for tests
74+
PolicyParser(InputStream inputStream, String policyName, boolean isExternalPlugin, Map<String, Class<?>> externalEntitlements)
75+
throws IOException {
6976
this.policyParser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, Objects.requireNonNull(inputStream));
7077
this.policyName = policyName;
7178
this.isExternalPlugin = isExternalPlugin;
79+
this.externalEntitlements = externalEntitlements;
7280
}
7381

7482
public Policy parsePolicy() {
@@ -124,14 +132,29 @@ protected Scope parseScope(String scopeName) throws IOException {
124132

125133
protected Entitlement parseEntitlement(String scopeName, String entitlementType) throws IOException {
126134
XContentLocation startLocation = policyParser.getTokenLocation();
127-
Class<?> entitlementClass = EXTERNAL_ENTITLEMENTS.get(entitlementType);
135+
Class<?> entitlementClass = externalEntitlements.get(entitlementType);
128136

129137
if (entitlementClass == null) {
130138
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
131139
}
132140

133-
Constructor<?> entitlementConstructor = entitlementClass.getConstructors()[0];
134-
ExternalEntitlement entitlementMetadata = entitlementConstructor.getAnnotation(ExternalEntitlement.class);
141+
Constructor<?> entitlementConstructor = null;
142+
ExternalEntitlement entitlementMetadata = null;
143+
for (var ctor : entitlementClass.getConstructors()) {
144+
var metadata = ctor.getAnnotation(ExternalEntitlement.class);
145+
if (metadata != null) {
146+
if (entitlementMetadata != null) {
147+
throw new IllegalStateException(
148+
"entitlement class ["
149+
+ entitlementClass.getName()
150+
+ "] has more than one constructor annotated with ExternalEntitlement"
151+
);
152+
}
153+
entitlementConstructor = ctor;
154+
entitlementMetadata = metadata;
155+
}
156+
157+
}
135158
if (entitlementMetadata == null) {
136159
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
137160
}

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyParserTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.io.IOException;
1616
import java.nio.charset.StandardCharsets;
1717
import java.util.List;
18+
import java.util.Map;
1819
import java.util.Set;
1920

2021
import static org.hamcrest.Matchers.equalTo;
@@ -23,6 +24,14 @@ public class PolicyParserTests extends ESTestCase {
2324

2425
private static class TestWrongEntitlementName implements Entitlement {}
2526

27+
public static class ManyConstructorsEntitlement implements Entitlement {
28+
@ExternalEntitlement
29+
public ManyConstructorsEntitlement(String s) {}
30+
31+
@ExternalEntitlement
32+
public ManyConstructorsEntitlement(int i) {}
33+
}
34+
2635
public void testGetEntitlementTypeName() {
2736
assertEquals("create_class_loader", PolicyParser.getEntitlementTypeName(CreateClassLoaderEntitlement.class));
2837

@@ -135,4 +144,26 @@ public void testParseLoadNativeLibraries() throws IOException {
135144
);
136145
assertEquals(expected, parsedPolicy);
137146
}
147+
148+
public void testMultipleConstructorsAnnotated() throws IOException {
149+
var parser = new PolicyParser(
150+
new ByteArrayInputStream("""
151+
entitlement-module-name:
152+
- many_constructors
153+
""".getBytes(StandardCharsets.UTF_8)),
154+
"test-policy.yaml",
155+
true,
156+
Map.of("many_constructors", ManyConstructorsEntitlement.class)
157+
);
158+
159+
var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
160+
assertThat(
161+
e.getMessage(),
162+
equalTo(
163+
"entitlement class "
164+
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ManyConstructorsEntitlement]"
165+
+ " has more than one constructor annotated with ExternalEntitlement"
166+
)
167+
);
168+
}
138169
}

0 commit comments

Comments
 (0)