Browse Source
- Add OAuth2ReactiveAuthorizationManagers - Code to interfaces - Align error message with the same in AuthorityAuthorizationManager - Adjust expectations in tests to confirm an appropriately constructed authorizaion manager - Add JavaDoc and reference documentation Issue gh-13654pull/13716/head
10 changed files with 399 additions and 143 deletions
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core; |
||||
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
|
||||
/** |
||||
* @author Mario Petrovski |
||||
* @since 6.2 |
||||
*/ |
||||
public final class OAuth2AuthorizationManagers { |
||||
|
||||
private OAuth2AuthorizationManagers() { |
||||
} |
||||
|
||||
public static <T> AuthorityAuthorizationManager<T> hasScope(String scope) { |
||||
verifyScope(scope); |
||||
return AuthorityAuthorizationManager.hasAuthority("SCOPE_" + scope); |
||||
} |
||||
|
||||
public static <T> AuthorityAuthorizationManager<T> hasAnyScope(String... scopes) { |
||||
verifyScopes(scopes); |
||||
String[] mappedScopes = new String[scopes.length]; |
||||
for (int i = 0; i < scopes.length; i++) { |
||||
mappedScopes[i] = "SCOPE_" + scopes[i]; |
||||
} |
||||
return AuthorityAuthorizationManager.hasAnyAuthority(mappedScopes); |
||||
} |
||||
|
||||
private static void verifyScopes(String... scopes) throws IllegalArgumentException { |
||||
for (String scope : scopes) { |
||||
verifyScope(scope); |
||||
} |
||||
} |
||||
|
||||
private static void verifyScope(String scope) { |
||||
if (scope.startsWith("SCOPE_")) { |
||||
throw new IllegalArgumentException("Scope '" + scope + "' start with 'SCOPE_' prefix."); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core.authorization; |
||||
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s. |
||||
* |
||||
* @author Mario Petrovski |
||||
* @author Josh Cummings |
||||
* @since 6.2 |
||||
* @see AuthorityAuthorizationManager |
||||
*/ |
||||
public final class OAuth2AuthorizationManagers { |
||||
|
||||
private OAuth2AuthorizationManagers() { |
||||
} |
||||
|
||||
/** |
||||
* Create an {@link AuthorizationManager} that requires an {@link Authentication} to |
||||
* have a {@code SCOPE_scope} authority. |
||||
* |
||||
* <p> |
||||
* For example, if you call {@code hasScope("read")}, then this will require that each |
||||
* authentication have a {@link org.springframework.security.core.GrantedAuthority} |
||||
* whose value is {@code SCOPE_read}. |
||||
* |
||||
* <p> |
||||
* This would equivalent to calling |
||||
* {@code AuthorityAuthorizationManager#hasAuthority("SCOPE_read")}. |
||||
* @param scope the scope value to require |
||||
* @param <T> the secure object |
||||
* @return an {@link AuthorizationManager} that requires a {@code "SCOPE_scope"} |
||||
* authority |
||||
*/ |
||||
public static <T> AuthorizationManager<T> hasScope(String scope) { |
||||
assertScope(scope); |
||||
return AuthorityAuthorizationManager.hasAuthority("SCOPE_" + scope); |
||||
} |
||||
|
||||
/** |
||||
* Create an {@link AuthorizationManager} that requires an {@link Authentication} to |
||||
* have at least one authority among {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... |
||||
* {@code SCOPE_scopeN}. |
||||
* |
||||
* <p> |
||||
* For example, if you call {@code hasAnyScope("read", "write")}, then this will |
||||
* require that each authentication have at least a |
||||
* {@link org.springframework.security.core.GrantedAuthority} whose value is either |
||||
* {@code SCOPE_read} or {@code SCOPE_write}. |
||||
* |
||||
* <p> |
||||
* This would equivalent to calling |
||||
* {@code AuthorityAuthorizationManager#hasAnyAuthority("SCOPE_read", "SCOPE_write")}. |
||||
* @param scopes the scope values to allow |
||||
* @param <T> the secure object |
||||
* @return an {@link AuthorizationManager} that requires at least one authority among |
||||
* {@code "SCOPE_scope1"}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. |
||||
* |
||||
*/ |
||||
public static <T> AuthorizationManager<T> hasAnyScope(String... scopes) { |
||||
String[] mappedScopes = new String[scopes.length]; |
||||
for (int i = 0; i < scopes.length; i++) { |
||||
assertScope(scopes[i]); |
||||
mappedScopes[i] = "SCOPE_" + scopes[i]; |
||||
} |
||||
return AuthorityAuthorizationManager.hasAnyAuthority(mappedScopes); |
||||
} |
||||
|
||||
private static void assertScope(String scope) { |
||||
Assert.isTrue(!scope.startsWith("SCOPE_"), |
||||
() -> scope + " should not start with SCOPE_ since SCOPE_" |
||||
+ " is automatically prepended when using hasScope and hasAnyScope. Consider using " |
||||
+ " AuthorityAuthorizationManager#hasAuthority or #hasAnyAuthority instead."); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core.authorization; |
||||
|
||||
import org.springframework.security.authorization.AuthorityReactiveAuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s. |
||||
* |
||||
* @author Josh Cummings |
||||
* @since 6.2 |
||||
* @see AuthorityReactiveAuthorizationManager |
||||
*/ |
||||
public final class OAuth2ReactiveAuthorizationManagers { |
||||
|
||||
private OAuth2ReactiveAuthorizationManagers() { |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link ReactiveAuthorizationManager} that requires an |
||||
* {@link Authentication} to have a {@code SCOPE_scope} authority. |
||||
* |
||||
* <p> |
||||
* For example, if you call {@code hasScope("read")}, then this will require that each |
||||
* authentication have a {@link org.springframework.security.core.GrantedAuthority} |
||||
* whose value is {@code SCOPE_read}. |
||||
* |
||||
* <p> |
||||
* This would equivalent to calling |
||||
* {@code AuthorityReactiveAuthorizationManager#hasAuthority("SCOPE_read")}. |
||||
* @param scope the scope value to require |
||||
* @param <T> the secure object |
||||
* @return an {@link ReactiveAuthorizationManager} that requires a |
||||
* {@code "SCOPE_scope"} authority |
||||
*/ |
||||
public static <T> ReactiveAuthorizationManager<T> hasScope(String scope) { |
||||
assertScope(scope); |
||||
return AuthorityReactiveAuthorizationManager.hasAuthority("SCOPE_" + scope); |
||||
} |
||||
|
||||
/** |
||||
* Create a {@link ReactiveAuthorizationManager} that requires an |
||||
* {@link Authentication} to have at least one authority among {@code SCOPE_scope1}, |
||||
* {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. |
||||
* |
||||
* <p> |
||||
* For example, if you call {@code hasAnyScope("read", "write")}, then this will |
||||
* require that each authentication have at least a |
||||
* {@link org.springframework.security.core.GrantedAuthority} whose value is either |
||||
* {@code SCOPE_read} or {@code SCOPE_write}. |
||||
* |
||||
* <p> |
||||
* This would equivalent to calling |
||||
* {@code AuthorityReactiveAuthorizationManager#hasAnyAuthority("SCOPE_read", "SCOPE_write")}. |
||||
* @param scopes the scope values to allow |
||||
* @param <T> the secure object |
||||
* @return an {@link ReactiveAuthorizationManager} that requires at least one |
||||
* authority among {@code "SCOPE_scope1"}, {@code SCOPE_scope2}, ... |
||||
* {@code SCOPE_scopeN}. |
||||
*/ |
||||
public static <T> ReactiveAuthorizationManager<T> hasAnyScope(String... scopes) { |
||||
String[] mappedScopes = new String[scopes.length]; |
||||
for (int i = 0; i < scopes.length; i++) { |
||||
assertScope(scopes[i]); |
||||
mappedScopes[i] = "SCOPE_" + scopes[i]; |
||||
} |
||||
return AuthorityReactiveAuthorizationManager.hasAnyAuthority(mappedScopes); |
||||
} |
||||
|
||||
private static void assertScope(String scope) { |
||||
Assert.isTrue(!scope.startsWith("SCOPE_"), |
||||
() -> scope + " should not start with SCOPE_ since SCOPE_" |
||||
+ " is automatically prepended when using hasScope and hasAnyScope. Consider using " |
||||
+ " AuthorityReactiveAuthorizationManager#hasAuthority or #hasAnyAuthority instead."); |
||||
} |
||||
|
||||
} |
||||
@ -1,63 +0,0 @@
@@ -1,63 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2AuthorizationManagers} |
||||
* |
||||
* @author Mario Petrovski |
||||
*/ |
||||
public class OAuth2AuthorizationManagersTests { |
||||
|
||||
@Test |
||||
void hasScope_withInvalidScope_shouldThrowIllegalArgumentException() { |
||||
String scope = "SCOPE_invalid"; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2AuthorizationManagers.hasScope(scope)) |
||||
.withMessage("Scope 'SCOPE_invalid' start with 'SCOPE_' prefix."); |
||||
} |
||||
|
||||
@Test |
||||
void hasScopes_withInvalidScope_shouldThrowIllegalArgumentException() { |
||||
String[] scopes = { "read", "write", "SCOPE_invalid" }; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2AuthorizationManagers.hasAnyScope(scopes)) |
||||
.withMessage("Scope 'SCOPE_invalid' start with 'SCOPE_' prefix."); |
||||
} |
||||
|
||||
@Test |
||||
void hasScope_withValidScope_shouldPass() { |
||||
String scope = "read"; |
||||
AuthorityAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasScope(scope); |
||||
assertThat(authorizationManager).isNotNull(); |
||||
} |
||||
|
||||
@Test |
||||
void hasScope_withValidScopes_shouldPass() { |
||||
String[] scopes = { "read", "write" }; |
||||
AuthorityAuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasAnyScope(scopes); |
||||
assertThat(authorizationManager).isNotNull(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core.authorization; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2AuthorizationManagers} |
||||
* |
||||
* @author Mario Petrovski |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class OAuth2AuthorizationManagersTests { |
||||
|
||||
@Test |
||||
void hasScopeWhenInvalidScopeThenThrowIllegalArgument() { |
||||
String scope = "SCOPE_invalid"; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2AuthorizationManagers.hasScope(scope)) |
||||
.withMessageContaining("SCOPE_invalid should not start with SCOPE_"); |
||||
} |
||||
|
||||
@Test |
||||
void hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() { |
||||
String[] scopes = { "read", "write", "SCOPE_invalid" }; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2AuthorizationManagers.hasAnyScope(scopes)) |
||||
.withMessageContaining("SCOPE_invalid should not start with SCOPE_"); |
||||
} |
||||
|
||||
@Test |
||||
void hasScopeWhenValidScopeThenAuthorizationManager() { |
||||
String scope = "read"; |
||||
AuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasScope(scope); |
||||
authorizationManager.verify(() -> hasScope(scope), new Object()); |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> authorizationManager.verify(() -> hasScope("wrong"), new Object())); |
||||
} |
||||
|
||||
@Test |
||||
void hasAnyScopeWhenValidScopesThenAuthorizationManager() { |
||||
String[] scopes = { "read", "write" }; |
||||
AuthorizationManager<Object> authorizationManager = OAuth2AuthorizationManagers.hasAnyScope(scopes); |
||||
for (String scope : scopes) { |
||||
authorizationManager.verify(() -> hasScope(scope), new Object()); |
||||
} |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> authorizationManager.verify(() -> hasScope("wrong"), new Object())); |
||||
} |
||||
|
||||
Authentication hasScope(String scope) { |
||||
return new TestingAuthenticationToken("user", "pass", "SCOPE_" + scope); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
|
||||
/* |
||||
* Copyright 2002-2023 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. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.security.oauth2.core.authorization; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import org.springframework.security.access.AccessDeniedException; |
||||
import org.springframework.security.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2ReactiveAuthorizationManagers} |
||||
* |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class OAuth2ReactiveAuthorizationManagersTests { |
||||
|
||||
@Test |
||||
void hasScopeWhenInvalidScopeThenThrowIllegalArgument() { |
||||
String scope = "SCOPE_invalid"; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasScope(scope)) |
||||
.withMessageContaining("SCOPE_invalid should not start with SCOPE_"); |
||||
} |
||||
|
||||
@Test |
||||
void hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() { |
||||
String[] scopes = { "read", "write", "SCOPE_invalid" }; |
||||
assertThatExceptionOfType(IllegalArgumentException.class) |
||||
.isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasAnyScope(scopes)) |
||||
.withMessageContaining("SCOPE_invalid should not start with SCOPE_"); |
||||
} |
||||
|
||||
@Test |
||||
void hasScopeWhenValidScopeThenAuthorizationManager() { |
||||
String scope = "read"; |
||||
ReactiveAuthorizationManager<Object> authorizationManager = OAuth2ReactiveAuthorizationManagers.hasScope(scope); |
||||
authorizationManager.verify(hasScope(scope), new Object()).block(); |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> authorizationManager.verify(hasScope("wrong"), new Object()).block()); |
||||
} |
||||
|
||||
@Test |
||||
void hasAnyScopeWhenValidScopesThenAuthorizationManager() { |
||||
String[] scopes = { "read", "write" }; |
||||
ReactiveAuthorizationManager<Object> authorizationManager = OAuth2ReactiveAuthorizationManagers |
||||
.hasAnyScope(scopes); |
||||
for (String scope : scopes) { |
||||
authorizationManager.verify(hasScope(scope), new Object()).block(); |
||||
} |
||||
assertThatExceptionOfType(AccessDeniedException.class) |
||||
.isThrownBy(() -> authorizationManager.verify(hasScope("wrong"), new Object()).block()); |
||||
} |
||||
|
||||
Mono<Authentication> hasScope(String scope) { |
||||
return Mono.just(new TestingAuthenticationToken("user", "pass", "SCOPE_" + scope)); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue