diff --git a/core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScanner.java b/core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScanner.java index 83d6242d33..f9082181ff 100644 --- a/core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScanner.java +++ b/core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScanner.java @@ -59,13 +59,18 @@ * {@code @HasRole} annotation found on a given {@link AnnotatedElement}. * *
+ * Meta-annotations that use enum values can use {@link ExpressionTemplateValueProvider} + * to provide custom placeholder values. + * + *
* Since the process of synthesis is expensive, it is recommended to cache the synthesized
* result to prevent multiple computations.
*
* @param the annotation to search for and synthesize
* @author Josh Cummings
* @author DingHao
- * @since 6.4
+ * @author Mike Heath
+ * @since 7.0
*/
final class ExpressionTemplateSecurityAnnotationScanner
extends AbstractSecurityAnnotationScanner {
@@ -74,6 +79,7 @@ final class ExpressionTemplateSecurityAnnotationScanner
static {
conversionService.addConverter(new ClassToStringConverter());
+ conversionService.addConverter(new ExpressionTemplateValueProviderConverter());
}
private final Class type;
@@ -162,4 +168,18 @@ public Set
+ * enum Permission implements ExpressionTemplateValueProvider {
+ * READ,
+ * WRITE;
+ *
+ * @Override
+ * public String getExpressionTemplateValue() {
+ * return switch (this) {
+ * case READ -> "user.permission-read";
+ * case WRITE -> "user.permission-write";
+ * }
+ * }
+ *
+ * }
+ *
+ *
+ * @author Mike Heath
+ * @since 7.0
+ */
+public interface ExpressionTemplateValueProvider {
+
+ /**
+ * Returns the value to be used in an expression template.
+ * @return the value to be used in an expression template
+ */
+ String getExpressionTemplateValue();
+
+}
diff --git a/core/src/test/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScannerTests.java b/core/src/test/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScannerTests.java
index fcb5eb86de..684393a65d 100644
--- a/core/src/test/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScannerTests.java
+++ b/core/src/test/java/org/springframework/security/core/annotation/ExpressionTemplateSecurityAnnotationScannerTests.java
@@ -54,6 +54,45 @@ void parseMultipleMetaSourceAnnotationParameterWithAliasFor() throws Exception {
assertThat(preAuthorize.value()).isEqualTo("check(#name)");
}
+ @Test
+ void parseMetaSourceAnnotationWithEnumImplementingExpressionTemplateValueProvider() throws Exception {
+ Method method = MessageService.class.getDeclaredMethod("process");
+ PreAuthorize preAuthorize = this.scanner.scan(method, method.getDeclaringClass());
+ assertThat(preAuthorize.value()).isEqualTo("hasAnyAuthority('user.READ','user.WRITE')");
+ }
+
+ enum Permission implements ExpressionTemplateValueProvider {
+
+ READ, WRITE;
+
+ @Override
+ public String getExpressionTemplateValue() {
+ return switch (this) {
+ case READ -> "'user.READ'";
+ case WRITE -> "'user.WRITE'";
+ };
+ }
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.TYPE, ElementType.METHOD })
+ @PreAuthorize("hasAnyAuthority({permissions})")
+ @interface HasAnyCustomPermissions {
+
+ Permission[] permissions();
+
+ }
+
+ @Documented
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.TYPE, ElementType.METHOD })
+ @HasAnyCustomPermissions(permissions = { Permission.READ, Permission.WRITE })
+ @interface HasAllCustomPermissions {
+
+ }
+
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@@ -86,6 +125,9 @@ void parseMultipleMetaSourceAnnotationParameterWithAliasFor() throws Exception {
private interface MessageService {
+ @HasAllCustomPermissions
+ void process();
+
@HasReadPermission("#name")
String sayHello(String name);