|
17 | 17 | package org.springframework.security.config.annotation.method.configuration;
|
18 | 18 |
|
19 | 19 | import java.io.Serializable;
|
| 20 | +import java.lang.annotation.Retention; |
| 21 | +import java.lang.annotation.RetentionPolicy; |
20 | 22 | import java.util.ArrayList;
|
21 | 23 | import java.util.Arrays;
|
22 | 24 | import java.util.List;
|
|
49 | 51 | import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
50 | 52 | import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
51 | 53 | import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
| 54 | +import org.springframework.security.access.prepost.PostAuthorize; |
| 55 | +import org.springframework.security.access.prepost.PostFilter; |
| 56 | +import org.springframework.security.access.prepost.PreAuthorize; |
| 57 | +import org.springframework.security.access.prepost.PreFilter; |
52 | 58 | import org.springframework.security.authorization.AuthorizationDecision;
|
53 | 59 | import org.springframework.security.authorization.AuthorizationEventPublisher;
|
54 | 60 | import org.springframework.security.authorization.AuthorizationManager;
|
55 | 61 | import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
|
56 | 62 | import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
57 | 63 | import org.springframework.security.authorization.method.MethodInvocationResult;
|
| 64 | +import org.springframework.security.authorization.method.PrePostTemplateDefaults; |
58 | 65 | import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
59 | 66 | import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
60 | 67 | import org.springframework.security.config.test.SpringTestContext;
|
@@ -587,6 +594,74 @@ public void allAnnotationsWhenAdviceAfterAllOffsetThenReturnsFilteredList() {
|
587 | 594 | assertThat(filtered).containsExactly("DoNotDrop");
|
588 | 595 | }
|
589 | 596 |
|
| 597 | + @Test |
| 598 | + @WithMockUser |
| 599 | + public void methodeWhenParameterizedPreAuthorizeMetaAnnotationThenPasses() { |
| 600 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 601 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 602 | + assertThat(service.hasRole("USER")).isTrue(); |
| 603 | + } |
| 604 | + |
| 605 | + @Test |
| 606 | + @WithMockUser |
| 607 | + public void methodRoleWhenPreAuthorizeMetaAnnotationHardcodedParameterThenPasses() { |
| 608 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 609 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 610 | + assertThat(service.hasUserRole()).isTrue(); |
| 611 | + } |
| 612 | + |
| 613 | + @Test |
| 614 | + public void methodWhenParameterizedAnnotationThenFails() { |
| 615 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 616 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 617 | + assertThatExceptionOfType(IllegalArgumentException.class) |
| 618 | + .isThrownBy(service::placeholdersOnlyResolvedByMetaAnnotations); |
| 619 | + } |
| 620 | + |
| 621 | + @Test |
| 622 | + @WithMockUser(authorities = "SCOPE_message:read") |
| 623 | + public void methodWhenMultiplePlaceholdersHasAuthorityThenPasses() { |
| 624 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 625 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 626 | + assertThat(service.readMessage()).isEqualTo("message"); |
| 627 | + } |
| 628 | + |
| 629 | + @Test |
| 630 | + @WithMockUser(roles = "ADMIN") |
| 631 | + public void methodWhenMultiplePlaceholdersHasRoleThenPasses() { |
| 632 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 633 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 634 | + assertThat(service.readMessage()).isEqualTo("message"); |
| 635 | + } |
| 636 | + |
| 637 | + @Test |
| 638 | + @WithMockUser |
| 639 | + public void methodWhenPostAuthorizeMetaAnnotationThenAuthorizes() { |
| 640 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 641 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 642 | + service.startsWithDave("daveMatthews"); |
| 643 | + assertThatExceptionOfType(AccessDeniedException.class) |
| 644 | + .isThrownBy(() -> service.startsWithDave("jenniferHarper")); |
| 645 | + } |
| 646 | + |
| 647 | + @Test |
| 648 | + @WithMockUser |
| 649 | + public void methodWhenPreFilterMetaAnnotationThenFilters() { |
| 650 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 651 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 652 | + assertThat(service.parametersContainDave(new ArrayList<>(List.of("dave", "carla", "vanessa", "paul")))) |
| 653 | + .containsExactly("dave"); |
| 654 | + } |
| 655 | + |
| 656 | + @Test |
| 657 | + @WithMockUser |
| 658 | + public void methodWhenPostFilterMetaAnnotationThenFilters() { |
| 659 | + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); |
| 660 | + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); |
| 661 | + assertThat(service.resultsContainDave(new ArrayList<>(List.of("dave", "carla", "vanessa", "paul")))) |
| 662 | + .containsExactly("dave"); |
| 663 | + } |
| 664 | + |
590 | 665 | private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
591 | 666 | return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
592 | 667 | }
|
@@ -890,4 +965,100 @@ Authz authz() {
|
890 | 965 |
|
891 | 966 | }
|
892 | 967 |
|
| 968 | + @Configuration |
| 969 | + @EnableMethodSecurity |
| 970 | + static class MetaAnnotationPlaceholderConfig { |
| 971 | + |
| 972 | + @Bean |
| 973 | + PrePostTemplateDefaults methodSecurityDefaults() { |
| 974 | + return new PrePostTemplateDefaults(); |
| 975 | + } |
| 976 | + |
| 977 | + @Bean |
| 978 | + MetaAnnotationService methodSecurityService() { |
| 979 | + return new MetaAnnotationService(); |
| 980 | + } |
| 981 | + |
| 982 | + } |
| 983 | + |
| 984 | + static class MetaAnnotationService { |
| 985 | + |
| 986 | + @RequireRole(role = "#role") |
| 987 | + boolean hasRole(String role) { |
| 988 | + return true; |
| 989 | + } |
| 990 | + |
| 991 | + @RequireRole(role = "'USER'") |
| 992 | + boolean hasUserRole() { |
| 993 | + return true; |
| 994 | + } |
| 995 | + |
| 996 | + @PreAuthorize("hasRole({role})") |
| 997 | + void placeholdersOnlyResolvedByMetaAnnotations() { |
| 998 | + } |
| 999 | + |
| 1000 | + @HasClaim(claim = "message:read", roles = { "'ADMIN'" }) |
| 1001 | + String readMessage() { |
| 1002 | + return "message"; |
| 1003 | + } |
| 1004 | + |
| 1005 | + @ResultStartsWith("dave") |
| 1006 | + String startsWithDave(String value) { |
| 1007 | + return value; |
| 1008 | + } |
| 1009 | + |
| 1010 | + @ParameterContains("dave") |
| 1011 | + List<String> parametersContainDave(List<String> list) { |
| 1012 | + return list; |
| 1013 | + } |
| 1014 | + |
| 1015 | + @ResultContains("dave") |
| 1016 | + List<String> resultsContainDave(List<String> list) { |
| 1017 | + return list; |
| 1018 | + } |
| 1019 | + |
| 1020 | + } |
| 1021 | + |
| 1022 | + @Retention(RetentionPolicy.RUNTIME) |
| 1023 | + @PreAuthorize("hasRole({role})") |
| 1024 | + @interface RequireRole { |
| 1025 | + |
| 1026 | + String role(); |
| 1027 | + |
| 1028 | + } |
| 1029 | + |
| 1030 | + @Retention(RetentionPolicy.RUNTIME) |
| 1031 | + @PreAuthorize("hasAuthority('SCOPE_{claim}') || hasAnyRole({roles})") |
| 1032 | + @interface HasClaim { |
| 1033 | + |
| 1034 | + String claim(); |
| 1035 | + |
| 1036 | + String[] roles() default {}; |
| 1037 | + |
| 1038 | + } |
| 1039 | + |
| 1040 | + @Retention(RetentionPolicy.RUNTIME) |
| 1041 | + @PostAuthorize("returnObject.startsWith('{value}')") |
| 1042 | + @interface ResultStartsWith { |
| 1043 | + |
| 1044 | + String value(); |
| 1045 | + |
| 1046 | + } |
| 1047 | + |
| 1048 | + @Retention(RetentionPolicy.RUNTIME) |
| 1049 | + @PreFilter("filterObject.contains('{value}')") |
| 1050 | + @interface ParameterContains { |
| 1051 | + |
| 1052 | + String value(); |
| 1053 | + |
| 1054 | + } |
| 1055 | + |
| 1056 | + @Retention(RetentionPolicy.RUNTIME) |
| 1057 | + @PostFilter("filterObject.contains('{value}')") |
| 1058 | + @interface ResultContains { |
| 1059 | + |
| 1060 | + String value(); |
| 1061 | + |
| 1062 | + } |
| 1063 | + |
893 | 1064 | }
|
0 commit comments