Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -22,7 +22,7 @@
* using this annotation is considered parseable as part of a policy file
* for entitlements.
*/
@Target(ElementType.CONSTRUCTOR)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExternalEntitlement {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -147,6 +149,7 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
}

Constructor<?> entitlementConstructor = null;
Method entitlementMethod = null;
ExternalEntitlement entitlementMetadata = null;
for (var ctor : entitlementClass.getConstructors()) {
var metadata = ctor.getAnnotation(ExternalEntitlement.class);
Expand All @@ -161,8 +164,27 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
entitlementConstructor = ctor;
entitlementMetadata = metadata;
}

}
for (var method : entitlementClass.getMethods()) {
var metadata = method.getAnnotation(ExternalEntitlement.class);
if (metadata != null) {
if (Modifier.isStatic(method.getModifiers()) == false) {
throw new IllegalStateException(
"entitlement class [" + entitlementClass.getName() + "] has non-static method annotated with ExternalEntitlement"
);
}
if (entitlementMetadata != null) {
throw new IllegalStateException(
"entitlement class ["
+ entitlementClass.getName()
+ "] has more than one constructor and/or method annotated with ExternalEntitlement"
);
}
entitlementMethod = method;
entitlementMetadata = metadata;
}
}

if (entitlementMetadata == null) {
throw newPolicyParserException(scopeName, "unknown entitlement type [" + entitlementType + "]");
}
Expand All @@ -171,7 +193,9 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
throw newPolicyParserException("entitlement type [" + entitlementType + "] is allowed only on modules");
}

Class<?>[] parameterTypes = entitlementConstructor.getParameterTypes();
Class<?>[] parameterTypes = entitlementConstructor != null
? entitlementConstructor.getParameterTypes()
: entitlementMethod.getParameterTypes();
String[] parametersNames = entitlementMetadata.parameterNames();

if (parameterTypes.length != 0 || parametersNames.length != 0) {
Expand Down Expand Up @@ -204,7 +228,11 @@ protected Entitlement parseEntitlement(String scopeName, String entitlementType)
}

try {
return (Entitlement) entitlementConstructor.newInstance(parameterValues);
if (entitlementConstructor != null) {
return (Entitlement) entitlementConstructor.newInstance(parameterValues);
} else {
return (Entitlement) entitlementMethod.invoke(null, parameterValues);
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
if (e.getCause() instanceof PolicyValidationException piae) {
throw newPolicyParserException(startLocation, scopeName, entitlementType, piae);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static Mode parseMode(String mode) {
}

@ExternalEntitlement(parameterNames = { "path", "mode" }, esModulesOnly = false)
public FileEntitlement(String path, String mode) {
this(path, parseMode(mode));
public static FileEntitlement create(String path, String mode) {
return new FileEntitlement(path, parseMode(mode));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ public void testNormalizePath() {

FileEntitlement entitlement(String path, String mode) {
Path p = path(path);
return new FileEntitlement(p.toString(), mode);
return FileEntitlement.create(p.toString(), mode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ public ManyConstructorsEntitlement(String s) {}
public ManyConstructorsEntitlement(int i) {}
}

public static class ManyMethodsEntitlement implements Entitlement {
@ExternalEntitlement
public static ManyMethodsEntitlement create(String s) {
return new ManyMethodsEntitlement();
}

@ExternalEntitlement
public static ManyMethodsEntitlement create(int i) {
return new ManyMethodsEntitlement();
}
}

public static class ConstructorAndMethodEntitlement implements Entitlement {
@ExternalEntitlement
public static ConstructorAndMethodEntitlement create(String s) {
return new ConstructorAndMethodEntitlement(s);
}

@ExternalEntitlement
public ConstructorAndMethodEntitlement(String s) {}
}

public static class NonStaticMethodEntitlement implements Entitlement {
@ExternalEntitlement
public NonStaticMethodEntitlement create() {
return new NonStaticMethodEntitlement();
}
}

public void testGetEntitlementTypeName() {
assertEquals("create_class_loader", PolicyParser.getEntitlementTypeName(CreateClassLoaderEntitlement.class));

Expand All @@ -55,7 +84,7 @@ public void testPolicyBuilder() throws IOException {
.parsePolicy();
Policy expected = new Policy(
"test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write"))))
List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
);
assertEquals(expected, parsedPolicy);
}
Expand All @@ -65,7 +94,7 @@ public void testPolicyBuilderOnExternalPlugin() throws IOException {
.parsePolicy();
Policy expected = new Policy(
"test-policy.yaml",
List.of(new Scope("entitlement-module-name", List.of(new FileEntitlement("test/path/to/file", "read_write"))))
List.of(new Scope("entitlement-module-name", List.of(FileEntitlement.create("test/path/to/file", "read_write"))))
);
assertEquals(expected, parsedPolicy);
}
Expand Down Expand Up @@ -174,4 +203,60 @@ public void testMultipleConstructorsAnnotated() throws IOException {
)
);
}

public void testMultipleMethodsAnnotated() throws IOException {
var parser = new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name:
- many_methods
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("many_methods", ManyMethodsEntitlement.class));

var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ManyMethodsEntitlement]"
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
)
);
}

public void testConstructorAndMethodAnnotated() throws IOException {
var parser = new PolicyParser(
new ByteArrayInputStream("""
entitlement-module-name:
- constructor_and_method
""".getBytes(StandardCharsets.UTF_8)),
"test-policy.yaml",
true,
Map.of("constructor_and_method", ConstructorAndMethodEntitlement.class)
);

var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$ConstructorAndMethodEntitlement]"
+ " has more than one constructor and/or method annotated with ExternalEntitlement"
)
);
}

public void testNonStaticMethodAnnotated() throws IOException {
var parser = new PolicyParser(new ByteArrayInputStream("""
entitlement-module-name:
- non_static
""".getBytes(StandardCharsets.UTF_8)), "test-policy.yaml", true, Map.of("non_static", NonStaticMethodEntitlement.class));

var e = expectThrows(IllegalStateException.class, parser::parsePolicy);
assertThat(
e.getMessage(),
equalTo(
"entitlement class "
+ "[org.elasticsearch.entitlement.runtime.policy.PolicyParserTests$NonStaticMethodEntitlement]"
+ " has non-static method annotated with ExternalEntitlement"
)
);
}
}