Bläddra i källkod

Make withRoles Check Only Roles

This commit clarifies the semantics of withRoles,
which is to check the role-based authorities in an
authentication.

Closes gh-17843
Josh Cummings 1 vecka sedan
förälder
incheckning
de10e08348

+ 2 - 6
config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderBuilderSecurityBuilderTests.java

@@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.authentication.ldap;
 
 import java.io.IOException;
 import java.net.ServerSocket;
-import java.util.Collections;
 import java.util.List;
 
 import javax.naming.directory.SearchControls;
@@ -39,7 +38,6 @@ import org.springframework.security.config.annotation.configuration.ObjectPostPr
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
@@ -120,8 +118,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
 		this.spring.register(BindAuthenticationConfig.class).autowire();
 
 		this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
-			.andExpect(authenticated().withUsername("bob")
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
+			.andExpect(authenticated().withUsername("bob").withRoles("DEVELOPERS"));
 	}
 
 	// SEC-2472
@@ -130,8 +127,7 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
 		this.spring.register(PasswordEncoderConfig.class).autowire();
 
 		this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
-			.andExpect(authenticated().withUsername("bcrypt")
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
+			.andExpect(authenticated().withUsername("bcrypt").withRoles("DEVELOPERS"));
 	}
 
 	private LdapAuthenticationProvider ldapProvider() {

+ 3 - 8
config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/LdapAuthenticationProviderConfigurerTests.java

@@ -16,8 +16,6 @@
 
 package org.springframework.security.config.annotation.authentication.ldap;
 
-import java.util.Collections;
-
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
@@ -28,8 +26,6 @@ import org.springframework.security.config.annotation.authentication.ldap.LdapAu
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
-import org.springframework.security.core.authority.AuthorityUtils;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
 import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
 import org.springframework.test.web.servlet.MockMvc;
@@ -64,7 +60,7 @@ public class LdapAuthenticationProviderConfigurerTests {
 				.password("bobspassword");
 		SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
 				.withUsername("bob")
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS")));
+				.withRoles("DEVELOPERS");
 		// @formatter:on
 		this.mockMvc.perform(request).andExpect(expectedUser);
 	}
@@ -79,7 +75,7 @@ public class LdapAuthenticationProviderConfigurerTests {
 				.password("bobspassword");
 		SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
 				.withUsername("bob")
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS")));
+				.withRoles("ROL_", new String[] { "DEVELOPERS" });
 		// @formatter:on
 		this.mockMvc.perform(request).andExpect(expectedUser);
 	}
@@ -108,8 +104,7 @@ public class LdapAuthenticationProviderConfigurerTests {
 				.password("otherbenspassword");
 		SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
 				.withUsername("otherben")
-				.withAuthorities(
-						AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS"));
+				.withRoles("SUBMANAGERS", "MANAGERS", "DEVELOPERS");
 		// @formatter:on
 		this.mockMvc.perform(request).andExpect(expectedUser);
 	}

+ 2 - 4
config/src/integration-test/java/org/springframework/security/config/annotation/authentication/ldap/NamespaceLdapAuthenticationProviderTests.java

@@ -16,7 +16,6 @@
 
 package org.springframework.security.config.annotation.authentication.ldap;
 
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -34,7 +33,6 @@ import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
 import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
 import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
@@ -79,7 +77,7 @@ public class NamespaceLdapAuthenticationProviderTests {
 				.user("bob")
 				.password("bobspassword");
 		SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS")));
+				.withRoles("PREFIX_", new String[] { "DEVELOPERS" });
 		// @formatter:on
 		this.mockMvc.perform(request).andExpect(user);
 	}
@@ -103,7 +101,7 @@ public class NamespaceLdapAuthenticationProviderTests {
 				.user("bob")
 				.password("bobspassword");
 		SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
-				.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")));
+				.withRoles("EXTRA");
 		// @formatter:on
 		this.mockMvc.perform(request).andExpect(user);
 	}

+ 37 - 5
test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java

@@ -18,7 +18,9 @@ package org.springframework.security.test.web.servlet.response;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 import org.jspecify.annotations.NullUnmarked;
 import org.jspecify.annotations.Nullable;
@@ -94,6 +96,8 @@ public final class SecurityMockMvcResultMatchers {
 
 		private @Nullable Collection<? extends GrantedAuthority> expectedGrantedAuthorities;
 
+		private Predicate<GrantedAuthority> ignoreAuthorities = (authority) -> false;
+
 		private @Nullable Consumer<Authentication> assertAuthentication;
 
 		AuthenticatedMatcher() {
@@ -132,7 +136,8 @@ public final class SecurityMockMvcResultMatchers {
 			}
 			if (this.expectedGrantedAuthorities != null) {
 				AssertionErrors.assertTrue("Authentication cannot be null", auth != null);
-				Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
+				Collection<? extends GrantedAuthority> authorities = new ArrayList<>(auth.getAuthorities());
+				authorities.removeIf(this.ignoreAuthorities);
 				AssertionErrors.assertTrue(
 						authorities + " does not contain the same authorities as " + this.expectedGrantedAuthorities,
 						authorities.containsAll(this.expectedGrantedAuthorities));
@@ -212,16 +217,43 @@ public final class SecurityMockMvcResultMatchers {
 		}
 
 		/**
-		 * Specifies the {@link Authentication#getAuthorities()}
+		 * Specifies the expected roles.
+		 * <p>
+		 * Since a set of authorities can contain more than just roles, this method
+		 * differs from {@link #withAuthorities} in that it only verifies the authorities
+		 * prefixed by {@code ROLE_}. Other authorities are ignored.
+		 * <p>
+		 * If you want to validate more than just roles, please use
+		 * {@link #withAuthorities}.
 		 * @param roles the roles. Each value is automatically prefixed with "ROLE_"
 		 * @return the {@link AuthenticatedMatcher} for further customization
 		 */
 		public AuthenticatedMatcher withRoles(String... roles) {
-			Collection<GrantedAuthority> authorities = new ArrayList<>();
+			return withRoles("ROLE_", roles);
+		}
+
+		/**
+		 * Specifies the expected roles.
+		 * <p>
+		 * Since a set of authorities can contain more than just roles, this method
+		 * differs from {@link #withAuthorities} in that it only verifies the authorities
+		 * prefixed by {@code ROLE_}. Other authorities are ignored.
+		 * <p>
+		 * If you want to validate more than just roles, please use
+		 * {@link #withAuthorities}.
+		 * @param rolePrefix the role prefix
+		 * @param roles the roles. Each value is automatically prefixed with the
+		 * {@code rolePrefix}
+		 * @return the {@link AuthenticatedMatcher} for further customization
+		 * @since 7.0
+		 */
+		public AuthenticatedMatcher withRoles(String rolePrefix, String[] roles) {
+			List<GrantedAuthority> withPrefix = new ArrayList<>();
 			for (String role : roles) {
-				authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
+				withPrefix.add(new SimpleGrantedAuthority(rolePrefix + role));
 			}
-			return withAuthorities(authorities);
+			this.ignoreAuthorities = (authority) -> !authority.getAuthority().startsWith(rolePrefix);
+			return withAuthorities(withPrefix);
 		}
 
 	}