From e60ae4984abdbe3faf8a6ddc6cf2e03fe4828a5c Mon Sep 17 00:00:00 2001 From: Robbie Martinus <12573669+rmartinus@users.noreply.github.com> Date: Wed, 19 Dec 2018 22:36:30 +1100 Subject: [PATCH] Add hasAnyAuthority() and hasAnyRole() in AuthorizeExchangeSpec Fixes gh-6306 --- .../config/web/server/ServerHttpSecurity.java | 18 ++++++ ...AuthorityReactiveAuthorizationManager.java | 55 +++++++++++++++++-- ...rityReactiveAuthorizationManagerTests.java | 48 +++++++++++++++- 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java index 39acc42793..4efb1bebad 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java +++ b/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 diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java index 43169f177c..41a41119c1 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java +++ b/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 the type of object being authorized */ public class AuthorityReactiveAuthorizationManager implements ReactiveAuthorizationManager { - private final String authority; + private final List 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 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 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 the type of object being authorized + * @return the new instance + */ + public static AuthorityReactiveAuthorizationManager 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 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 the type of object being authorized + * @return the new instance + */ + public static AuthorityReactiveAuthorizationManager 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) + ); + } } diff --git a/core/src/test/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManagerTests.java index 406650f0b5..c3ce290c72 100644 --- a/core/src/test/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManagerTests.java +++ b/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); + } }