|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2020 the original author or authors. |
| 2 | + * Copyright 2002-2021 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
33 | 33 | import org.springframework.context.annotation.Bean;
|
34 | 34 | import org.springframework.context.annotation.Configuration;
|
35 | 35 | import org.springframework.context.annotation.Import;
|
| 36 | +import org.springframework.core.Ordered; |
36 | 37 | import org.springframework.core.annotation.Order;
|
37 | 38 | import org.springframework.expression.EvaluationContext;
|
38 | 39 | import org.springframework.expression.Expression;
|
|
62 | 63 | import org.springframework.security.web.FilterChainProxy;
|
63 | 64 | import org.springframework.security.web.FilterInvocation;
|
64 | 65 | import org.springframework.security.web.SecurityFilterChain;
|
65 |
| -import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator; |
| 66 | +import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator; |
66 | 67 | import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
67 | 68 | import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
68 | 69 | import org.springframework.test.web.servlet.MockMvc;
|
|
84 | 85 | * @author Rob Winch
|
85 | 86 | * @author Joe Grandja
|
86 | 87 | * @author Evgeniy Cheban
|
| 88 | + * @author Marcus Da Coregio |
87 | 89 | */
|
88 | 90 | @ExtendWith(SpringTestContextExtension.class)
|
89 | 91 | public class WebSecurityConfigurationTests {
|
@@ -218,18 +220,18 @@ public void securityExpressionHandlerWhenPermissionEvaluatorBeanThenPermissionEv
|
218 | 220 | }
|
219 | 221 |
|
220 | 222 | @Test
|
221 |
| - public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() { |
| 223 | + public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenRequestMatcherIsRegistered() { |
222 | 224 | this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
|
223 | 225 | assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
224 |
| - .isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class); |
| 226 | + .isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class); |
225 | 227 | }
|
226 | 228 |
|
227 | 229 | @Test
|
228 | 230 | public void loadConfigWhenSecurityFilterChainBeanThenDefaultWebInvocationPrivilegeEvaluatorIsRegistered() {
|
229 | 231 | this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
|
230 | 232 |
|
231 | 233 | assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
232 |
| - .isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class); |
| 234 | + .isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class); |
233 | 235 | }
|
234 | 236 |
|
235 | 237 | // SEC-2303
|
@@ -375,6 +377,69 @@ public void loadConfigWhenMultipleAuthenticationManagersAndWebSecurityConfigurer
|
375 | 377 | assertThat(filterChains.get(1).matches(request)).isTrue();
|
376 | 378 | }
|
377 | 379 |
|
| 380 | + @Test |
| 381 | + public void loadConfigWhenTwoSecurityFilterChainsThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() { |
| 382 | + this.spring.register(TwoSecurityFilterChainConfig.class).autowire(); |
| 383 | + assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class)) |
| 384 | + .isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class); |
| 385 | + } |
| 386 | + |
| 387 | + @Test |
| 388 | + public void loadConfigWhenTwoSecurityFilterChainDebugThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() { |
| 389 | + this.spring.register(TwoSecurityFilterChainConfig.class).autowire(); |
| 390 | + assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class)) |
| 391 | + .isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class); |
| 392 | + } |
| 393 | + |
| 394 | + // gh-10554 |
| 395 | + @Test |
| 396 | + public void loadConfigWhenMultipleSecurityFilterChainsThenWebInvocationPrivilegeEvaluatorApplySecurity() { |
| 397 | + this.spring.register(MultipleSecurityFilterChainConfig.class).autowire(); |
| 398 | + WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext() |
| 399 | + .getBean(WebInvocationPrivilegeEvaluator.class); |
| 400 | + assertUserPermissions(privilegeEvaluator); |
| 401 | + assertAdminPermissions(privilegeEvaluator); |
| 402 | + assertAnotherUserPermission(privilegeEvaluator); |
| 403 | + } |
| 404 | + |
| 405 | + // gh-10554 |
| 406 | + @Test |
| 407 | + public void loadConfigWhenMultipleSecurityFilterChainAndIgnoringThenWebInvocationPrivilegeEvaluatorAcceptsNullAuthenticationOnIgnored() { |
| 408 | + this.spring.register(MultipleSecurityFilterChainIgnoringConfig.class).autowire(); |
| 409 | + WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext() |
| 410 | + .getBean(WebInvocationPrivilegeEvaluator.class); |
| 411 | + assertUserPermissions(privilegeEvaluator); |
| 412 | + assertAdminPermissions(privilegeEvaluator); |
| 413 | + assertAnotherUserPermission(privilegeEvaluator); |
| 414 | + // null authentication |
| 415 | + assertThat(privilegeEvaluator.isAllowed("/user", null)).isFalse(); |
| 416 | + assertThat(privilegeEvaluator.isAllowed("/admin", null)).isFalse(); |
| 417 | + assertThat(privilegeEvaluator.isAllowed("/another", null)).isFalse(); |
| 418 | + assertThat(privilegeEvaluator.isAllowed("/ignoring1", null)).isTrue(); |
| 419 | + assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue(); |
| 420 | + } |
| 421 | + |
| 422 | + private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) { |
| 423 | + Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER"); |
| 424 | + assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse(); |
| 425 | + assertThat(privilegeEvaluator.isAllowed("/admin", anotherUser)).isFalse(); |
| 426 | + assertThat(privilegeEvaluator.isAllowed("/another", anotherUser)).isTrue(); |
| 427 | + } |
| 428 | + |
| 429 | + private void assertAdminPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) { |
| 430 | + Authentication admin = new TestingAuthenticationToken("admin", "password", "ROLE_ADMIN"); |
| 431 | + assertThat(privilegeEvaluator.isAllowed("/user", admin)).isFalse(); |
| 432 | + assertThat(privilegeEvaluator.isAllowed("/admin", admin)).isTrue(); |
| 433 | + assertThat(privilegeEvaluator.isAllowed("/another", admin)).isTrue(); |
| 434 | + } |
| 435 | + |
| 436 | + private void assertUserPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) { |
| 437 | + Authentication user = new TestingAuthenticationToken("user", "password", "ROLE_USER"); |
| 438 | + assertThat(privilegeEvaluator.isAllowed("/user", user)).isTrue(); |
| 439 | + assertThat(privilegeEvaluator.isAllowed("/admin", user)).isFalse(); |
| 440 | + assertThat(privilegeEvaluator.isAllowed("/another", user)).isTrue(); |
| 441 | + } |
| 442 | + |
378 | 443 | @EnableWebSecurity
|
379 | 444 | @Import(AuthenticationTestConfiguration.class)
|
380 | 445 | static class SortedWebSecurityConfigurerAdaptersConfig {
|
@@ -1008,4 +1073,125 @@ protected AuthenticationManager authenticationManager() {
|
1008 | 1073 |
|
1009 | 1074 | }
|
1010 | 1075 |
|
| 1076 | + @EnableWebSecurity |
| 1077 | + static class TwoSecurityFilterChainConfig { |
| 1078 | + |
| 1079 | + @Bean |
| 1080 | + @Order(Ordered.HIGHEST_PRECEDENCE) |
| 1081 | + public SecurityFilterChain path1(HttpSecurity http) throws Exception { |
| 1082 | + // @formatter:off |
| 1083 | + http |
| 1084 | + .requestMatchers((requests) -> requests.antMatchers("/path1/**")) |
| 1085 | + .authorizeRequests((requests) -> requests.anyRequest().authenticated()); |
| 1086 | + // @formatter:on |
| 1087 | + return http.build(); |
| 1088 | + } |
| 1089 | + |
| 1090 | + @Bean |
| 1091 | + @Order(Ordered.LOWEST_PRECEDENCE) |
| 1092 | + public SecurityFilterChain permitAll(HttpSecurity http) throws Exception { |
| 1093 | + http.authorizeRequests((requests) -> requests.anyRequest().permitAll()); |
| 1094 | + return http.build(); |
| 1095 | + } |
| 1096 | + |
| 1097 | + } |
| 1098 | + |
| 1099 | + @EnableWebSecurity(debug = true) |
| 1100 | + static class TwoSecurityFilterChainDebugConfig { |
| 1101 | + |
| 1102 | + @Bean |
| 1103 | + @Order(Ordered.HIGHEST_PRECEDENCE) |
| 1104 | + public SecurityFilterChain path1(HttpSecurity http) throws Exception { |
| 1105 | + // @formatter:off |
| 1106 | + http |
| 1107 | + .requestMatchers((requests) -> requests.antMatchers("/path1/**")) |
| 1108 | + .authorizeRequests((requests) -> requests.anyRequest().authenticated()); |
| 1109 | + // @formatter:on |
| 1110 | + return http.build(); |
| 1111 | + } |
| 1112 | + |
| 1113 | + @Bean |
| 1114 | + @Order(Ordered.LOWEST_PRECEDENCE) |
| 1115 | + public SecurityFilterChain permitAll(HttpSecurity http) throws Exception { |
| 1116 | + http.authorizeRequests((requests) -> requests.anyRequest().permitAll()); |
| 1117 | + return http.build(); |
| 1118 | + } |
| 1119 | + |
| 1120 | + } |
| 1121 | + |
| 1122 | + @EnableWebSecurity |
| 1123 | + @Import(AuthenticationTestConfiguration.class) |
| 1124 | + static class MultipleSecurityFilterChainConfig { |
| 1125 | + |
| 1126 | + @Bean |
| 1127 | + @Order(Ordered.HIGHEST_PRECEDENCE) |
| 1128 | + public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception { |
| 1129 | + // @formatter:off |
| 1130 | + http |
| 1131 | + .requestMatchers((requests) -> requests.antMatchers("/user")) |
| 1132 | + .authorizeRequests((requests) -> requests.anyRequest().hasRole("USER")); |
| 1133 | + // @formatter:on |
| 1134 | + return http.build(); |
| 1135 | + } |
| 1136 | + |
| 1137 | + @Bean |
| 1138 | + @Order(Ordered.HIGHEST_PRECEDENCE + 1) |
| 1139 | + public SecurityFilterChain path1(HttpSecurity http) throws Exception { |
| 1140 | + // @formatter:off |
| 1141 | + http |
| 1142 | + .requestMatchers((requests) -> requests.antMatchers("/admin")) |
| 1143 | + .authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN")); |
| 1144 | + // @formatter:on |
| 1145 | + return http.build(); |
| 1146 | + } |
| 1147 | + |
| 1148 | + @Bean |
| 1149 | + @Order(Ordered.LOWEST_PRECEDENCE) |
| 1150 | + public SecurityFilterChain permitAll(HttpSecurity http) throws Exception { |
| 1151 | + http.authorizeRequests((requests) -> requests.anyRequest().permitAll()); |
| 1152 | + return http.build(); |
| 1153 | + } |
| 1154 | + |
| 1155 | + } |
| 1156 | + |
| 1157 | + @EnableWebSecurity |
| 1158 | + @Import(AuthenticationTestConfiguration.class) |
| 1159 | + static class MultipleSecurityFilterChainIgnoringConfig { |
| 1160 | + |
| 1161 | + @Bean |
| 1162 | + public WebSecurityCustomizer webSecurityCustomizer() { |
| 1163 | + return (web) -> web.ignoring().antMatchers("/ignoring1/**"); |
| 1164 | + } |
| 1165 | + |
| 1166 | + @Bean |
| 1167 | + @Order(Ordered.HIGHEST_PRECEDENCE) |
| 1168 | + public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception { |
| 1169 | + // @formatter:off |
| 1170 | + http |
| 1171 | + .requestMatchers((requests) -> requests.antMatchers("/user")) |
| 1172 | + .authorizeRequests((requests) -> requests.anyRequest().hasRole("USER")); |
| 1173 | + // @formatter:on |
| 1174 | + return http.build(); |
| 1175 | + } |
| 1176 | + |
| 1177 | + @Bean |
| 1178 | + @Order(Ordered.HIGHEST_PRECEDENCE + 1) |
| 1179 | + public SecurityFilterChain admin(HttpSecurity http) throws Exception { |
| 1180 | + // @formatter:off |
| 1181 | + http |
| 1182 | + .requestMatchers((requests) -> requests.antMatchers("/admin")) |
| 1183 | + .authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN")); |
| 1184 | + // @formatter:on |
| 1185 | + return http.build(); |
| 1186 | + } |
| 1187 | + |
| 1188 | + @Bean |
| 1189 | + @Order(Ordered.LOWEST_PRECEDENCE) |
| 1190 | + public SecurityFilterChain permitAll(HttpSecurity http) throws Exception { |
| 1191 | + http.authorizeRequests((requests) -> requests.anyRequest().permitAll()); |
| 1192 | + return http.build(); |
| 1193 | + } |
| 1194 | + |
| 1195 | + } |
| 1196 | + |
1011 | 1197 | }
|
0 commit comments