Selaa lähdekoodia

Add hasAnyAuthority() and hasAnyRole() in AuthorizeExchangeSpec

Fixes gh-6306
Robbie Martinus 6 vuotta sitten
vanhempi
commit
e60ae4984a

+ 18 - 0
config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

@@ -1563,6 +1563,15 @@ public class ServerHttpSecurity {
 				return access(AuthorityReactiveAuthorizationManager.hasRole(role));
 			}
 
+			/**
+			 * Require any specific role. This is a shortcut for {@link #hasAnyAuthority(String...)}
+			 * @param roles the roles (i.e. "USER" would require "ROLE_USER")
+			 * @return the {@link AuthorizeExchangeSpec} to configure
+			 */
+			public AuthorizeExchangeSpec hasAnyRole(String... roles) {
+				return access(AuthorityReactiveAuthorizationManager.hasAnyRole(roles));
+			}
+
 			/**
 			 * Require a specific authority.
 			 * @param authority the authority to require (i.e. "USER" woudl require authority of "USER").
@@ -1572,6 +1581,15 @@ public class ServerHttpSecurity {
 				return access(AuthorityReactiveAuthorizationManager.hasAuthority(authority));
 			}
 
+			/**
+			 * Require any authority
+			 * @param authorities the authorities to require (i.e. "USER" would require authority of "USER").
+			 * @return the {@link AuthorizeExchangeSpec} to configure
+			 */
+			public AuthorizeExchangeSpec hasAnyAuthority(String... authorities) {
+				return access(AuthorityReactiveAuthorizationManager.hasAnyAuthority(authorities));
+			}
+
 			/**
 			 * Require an authenticated user
 			 * @return the {@link AuthorizeExchangeSpec} to configure

+ 49 - 6
core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,10 @@ import org.springframework.security.core.Authentication;
 import org.springframework.util.Assert;
 import reactor.core.publisher.Mono;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
 /**
  * A {@link ReactiveAuthorizationManager} that determines if the current user is
  * authorized by evaluating if the {@link Authentication} contains a specified authority.
@@ -29,10 +33,10 @@ import reactor.core.publisher.Mono;
  * @param <T> the type of object being authorized
  */
 public class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
-	private final String authority;
+	private final List<String> authorities;
 
-	private AuthorityReactiveAuthorizationManager(String authority) {
-		this.authority = authority;
+	private AuthorityReactiveAuthorizationManager(String... authorities) {
+		this.authorities = Arrays.asList(authorities);
 	}
 
 	@Override
@@ -40,8 +44,8 @@ public class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthori
 		return authentication
 			.filter(a -> a.isAuthenticated())
 			.flatMapIterable( a -> a.getAuthorities())
-			.map( g-> g.getAuthority())
-			.hasElement(this.authority)
+			.map(g -> g.getAuthority())
+			.any(a -> this.authorities.contains(a))
 			.map( hasAuthority -> new AuthorizationDecision(hasAuthority))
 			.defaultIfEmpty(new AuthorizationDecision(false));
 	}
@@ -59,6 +63,24 @@ public class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthori
 		return new AuthorityReactiveAuthorizationManager<>(authority);
 	}
 
+	/**
+	 * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the
+	 * provided authorities.
+	 *
+	 * @author Robbie Martinus
+	 * @param authorities the authorities to check for
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityReactiveAuthorizationManager<T> hasAnyAuthority(String... authorities) {
+		Assert.notNull(authorities, "authorities cannot be null");
+		for (String authority : authorities) {
+			Assert.notNull(authority, "authority cannot be null");
+		}
+
+		return new AuthorityReactiveAuthorizationManager<>(authorities);
+	}
+
 	/**
 	 * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the
 	 * provided authority.
@@ -71,4 +93,25 @@ public class AuthorityReactiveAuthorizationManager<T> implements ReactiveAuthori
 		Assert.notNull(role, "role cannot be null");
 		return hasAuthority("ROLE_" + role);
 	}
+
+	/**
+	 * Creates an instance of {@link AuthorityReactiveAuthorizationManager} with the
+	 * provided authorities.
+	 *
+	 * @author Robbie Martinus
+	 * @param roles the authorities to check for prefixed with "ROLE_"
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityReactiveAuthorizationManager<T> hasAnyRole(String... roles) {
+		Assert.notNull(roles, "roles cannot be null");
+		for (String role : roles) {
+			Assert.notNull(role, "role cannot be null");
+		}
+
+		return hasAnyAuthority(Stream.of(roles)
+				.map(r -> "ROLE_" + r)
+				.toArray(String[]::new)
+		);
+	}
 }

+ 47 - 1
core/src/test/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManagerTests.java

@@ -105,7 +105,7 @@ public class AuthorityReactiveAuthorizationManagerTests {
 	}
 
 	@Test
-	public void checkWhenHasRoleAndNotAuthorizedThenReturnTrue() {
+	public void checkWhenHasRoleAndNotAuthorizedThenReturnFalse() {
 		manager = AuthorityReactiveAuthorizationManager.hasRole("ADMIN");
 		authentication = new TestingAuthenticationToken("rob", "secret", "ADMIN");
 
@@ -114,6 +114,26 @@ public class AuthorityReactiveAuthorizationManagerTests {
 		assertThat(granted).isFalse();
 	}
 
+	@Test
+	public void checkWhenHasAnyRoleAndAuthorizedThenReturnTrue() {
+		manager = AuthorityReactiveAuthorizationManager.hasAnyRole("GENERAL", "USER", "TEST");
+		authentication = new TestingAuthenticationToken("rob", "secret", "ROLE_USER", "ROLE_AUDITING", "ROLE_ADMIN");
+
+		boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
+
+		assertThat(granted).isTrue();
+	}
+
+	@Test
+	public void checkWhenHasAnyRoleAndNotAuthorizedThenReturnFalse() {
+		manager = AuthorityReactiveAuthorizationManager.hasAnyRole("GENERAL", "USER", "TEST");
+		authentication = new TestingAuthenticationToken("rob", "secret", "USER", "AUDITING", "ADMIN");
+
+		boolean granted = manager.check(Mono.just(authentication), null).block().isGranted();
+
+		assertThat(granted).isFalse();
+	}
+
 	@Test(expected = IllegalArgumentException.class)
 	public void hasRoleWhenNullThenException() {
 		String role = null;
@@ -125,4 +145,30 @@ public class AuthorityReactiveAuthorizationManagerTests {
 		String authority = null;
 		AuthorityReactiveAuthorizationManager.hasAuthority(authority);
 	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void hasAnyRoleWhenNullThenException() {
+		String role = null;
+		AuthorityReactiveAuthorizationManager.hasAnyRole(role);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void hasAnyAuthorityWhenNullThenException() {
+		String authority = null;
+		AuthorityReactiveAuthorizationManager.hasAnyAuthority(authority);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void hasAnyRoleWhenOneIsNullThenException() {
+		String role1 = "ROLE_ADMIN";
+		String role2 = null;
+		AuthorityReactiveAuthorizationManager.hasAnyRole(role1, role2);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void hasAnyAuthorityWhenOneIsNullThenException() {
+		String authority1 = "ADMIN";
+		String authority2 = null;
+		AuthorityReactiveAuthorizationManager.hasAnyAuthority(authority1, authority2);
+	}
 }