Browse Source
Closes gh-18013 Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>pull/18713/head
4 changed files with 322 additions and 0 deletions
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
/* |
||||
* Copyright 2025-present 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.AuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationManagerFactory; |
||||
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A factory for creating different kinds of {@link AuthorizationManager} instances. |
||||
* |
||||
* @param <T> the type of object that the authorization check is being done on |
||||
* @author Ngoc Nhan |
||||
* @since 7.1 |
||||
*/ |
||||
public final class DefaultOAuth2AuthorizationManagerFactory<T> implements OAuth2AuthorizationManagerFactory<T> { |
||||
|
||||
private String scopePrefix = "SCOPE_"; |
||||
|
||||
private final AuthorizationManagerFactory<T> authorizationManagerFactory; |
||||
|
||||
public DefaultOAuth2AuthorizationManagerFactory() { |
||||
this(new DefaultAuthorizationManagerFactory<>()); |
||||
} |
||||
|
||||
public DefaultOAuth2AuthorizationManagerFactory(AuthorizationManagerFactory<T> authorizationManagerFactory) { |
||||
Assert.notNull(authorizationManagerFactory, "authorizationManagerFactory can not be null"); |
||||
this.authorizationManagerFactory = authorizationManagerFactory; |
||||
} |
||||
|
||||
/** |
||||
* Sets the prefix used to create an authority name from a scope name. Can be an empty |
||||
* string. |
||||
* @param scopePrefix the scope prefix to use |
||||
*/ |
||||
public void setScopePrefix(String scopePrefix) { |
||||
Assert.notNull(scopePrefix, "scopePrefix can not be null"); |
||||
this.scopePrefix = scopePrefix; |
||||
} |
||||
|
||||
@Override |
||||
public AuthorizationManager<T> hasScope(String scope) { |
||||
Assert.notNull(scope, "scope can not be null"); |
||||
assertScope(scope); |
||||
return this.authorizationManagerFactory.hasAuthority(this.scopePrefix + scope); |
||||
} |
||||
|
||||
@Override |
||||
public AuthorizationManager<T> hasAnyScope(String... scopes) { |
||||
return this.authorizationManagerFactory.hasAnyAuthority(this.mappedScopes(scopes)); |
||||
} |
||||
|
||||
@Override |
||||
public AuthorizationManager<T> hasAllScopes(String... scopes) { |
||||
return this.authorizationManagerFactory.hasAllAuthorities(this.mappedScopes(scopes)); |
||||
} |
||||
|
||||
private String[] mappedScopes(String... scopes) { |
||||
Assert.notNull(scopes, "scopes can not be null"); |
||||
String[] mappedScopes = new String[scopes.length]; |
||||
for (int i = 0; i < scopes.length; i++) { |
||||
assertScope(scopes[i]); |
||||
mappedScopes[i] = this.scopePrefix + scopes[i]; |
||||
} |
||||
return mappedScopes; |
||||
} |
||||
|
||||
private void assertScope(String scope) { |
||||
Assert.isTrue(!scope.startsWith(this.scopePrefix), () -> scope + " should not start with '" + this.scopePrefix |
||||
+ "' since '" + this.scopePrefix |
||||
+ "' is automatically prepended when using hasScope and hasAnyScope. Consider using AuthorizationManagerFactory#hasAuthority or #hasAnyAuthority instead."); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright 2025-present 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.AuthorizationManager; |
||||
import org.springframework.security.core.Authentication; |
||||
|
||||
/** |
||||
* A factory for creating different kinds of {@link AuthorizationManager} instances. |
||||
* |
||||
* @param <T> the type of object that the authorization check is being done on |
||||
* @author Ngoc Nhan |
||||
* @since 7.1 |
||||
*/ |
||||
public interface OAuth2AuthorizationManagerFactory<T> { |
||||
|
||||
/** |
||||
* 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 |
||||
* @return an {@link AuthorizationManager} that requires a {@code "SCOPE_scope"} |
||||
* authority |
||||
*/ |
||||
default AuthorizationManager<T> hasScope(String scope) { |
||||
return OAuth2AuthorizationManagers.hasScope(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 |
||||
* @return an {@link AuthorizationManager} that requires at least one authority among |
||||
* {@code "SCOPE_scope1"}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. |
||||
*/ |
||||
default AuthorizationManager<T> hasAnyScope(String... scopes) { |
||||
return OAuth2AuthorizationManagers.hasAnyScope(scopes); |
||||
} |
||||
|
||||
/** |
||||
* Create an {@link AuthorizationManager} that requires an {@link Authentication} to |
||||
* have all authorities {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... |
||||
* {@code SCOPE_scopeN}. |
||||
* |
||||
* <p> |
||||
* For example, if you call {@code hasAllScopes("read", "write")}, then each |
||||
* {@link org.springframework.security.core.Authentication} must have all |
||||
* {@link org.springframework.security.core.GrantedAuthority} values of |
||||
* {@code SCOPE_read} and {@code SCOPE_write}. |
||||
* |
||||
* <p> |
||||
* This would be equivalent to calling |
||||
* {@code AllAuthoritiesAuthorizationManager#hasAllAuthorities("SCOPE_read", "SCOPE_write")}. |
||||
* @param scopes the scope values to require |
||||
* @return an {@link AuthorizationManager} that requires all authorities |
||||
* {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. |
||||
*/ |
||||
default AuthorizationManager<T> hasAllScopes(String... scopes) { |
||||
return OAuth2AuthorizationManagers.hasAllScopes(scopes); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,108 @@
@@ -0,0 +1,108 @@
|
||||
/* |
||||
* Copyright 2025-present 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.authentication.TestingAuthenticationToken; |
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationManager; |
||||
import org.springframework.security.authorization.AuthorizationManagerFactories; |
||||
import org.springframework.security.authorization.AuthorizationResult; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link OAuth2AuthorizationManagerFactory}. |
||||
* |
||||
* @author Ngoc Nhan |
||||
*/ |
||||
public class OAuth2AuthorizationManagerFactoryTests { |
||||
|
||||
private static final String MSG_READ = "message:read"; |
||||
|
||||
private static final String MSG_WRITE = "message:write"; |
||||
|
||||
private static final String SCOPE_MSG_READ = "SCOPE_message:read"; |
||||
|
||||
private static final String SCOPE_MSG_WRITE = "SCOPE_message:write"; |
||||
|
||||
@Test |
||||
public void hasScopeReturnsAuthorityAuthorizationManagerByDefault() { |
||||
OAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(); |
||||
AuthorizationManager<String> authorizationManager = factory.hasScope(MSG_READ); |
||||
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class); |
||||
} |
||||
|
||||
@Test |
||||
public void hasAnyScopeReturnsAuthorityAuthorizationManagerByDefault() { |
||||
OAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(); |
||||
AuthorizationManager<String> authorizationManager = factory.hasAnyScope(MSG_READ, MSG_WRITE); |
||||
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class); |
||||
} |
||||
|
||||
@Test |
||||
public void hasAllScopesReturnsAuthorityAuthorizationManagerByDefault() { |
||||
OAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>(); |
||||
AuthorizationManager<String> authorizationManager = factory.hasAnyScope(MSG_READ, MSG_WRITE); |
||||
assertThat(authorizationManager).isInstanceOf(AuthorityAuthorizationManager.class); |
||||
} |
||||
|
||||
@Test |
||||
public void hasScopeWhenSetAuthorizationManagerFactories() { |
||||
DefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>( |
||||
AuthorizationManagerFactories.<String>multiFactor().requireFactors(SCOPE_MSG_READ).build()); |
||||
assertUserGranted(factory.hasScope(MSG_READ), SCOPE_MSG_READ); |
||||
assertUserDenied(factory.hasScope(MSG_WRITE), SCOPE_MSG_READ); |
||||
} |
||||
|
||||
@Test |
||||
public void hasAnyScopeWhenSetAuthorizationManagerFactories() { |
||||
DefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>( |
||||
AuthorizationManagerFactories.<String>multiFactor().requireFactors(SCOPE_MSG_READ).build()); |
||||
assertUserGranted(factory.hasAnyScope(MSG_READ), SCOPE_MSG_READ); |
||||
assertUserDenied(factory.hasAnyScope(MSG_WRITE), SCOPE_MSG_READ); |
||||
} |
||||
|
||||
@Test |
||||
public void hasAllScopesWhenSetAuthorizationManagerFactories() { |
||||
DefaultOAuth2AuthorizationManagerFactory<String> factory = new DefaultOAuth2AuthorizationManagerFactory<>( |
||||
AuthorizationManagerFactories.<String>multiFactor() |
||||
.requireFactors(SCOPE_MSG_READ, SCOPE_MSG_WRITE) |
||||
.build()); |
||||
assertUserGranted(factory.hasAllScopes(MSG_READ, MSG_WRITE), SCOPE_MSG_READ, SCOPE_MSG_WRITE); |
||||
assertUserDenied(factory.hasAllScopes(MSG_READ, MSG_WRITE), SCOPE_MSG_READ); |
||||
} |
||||
|
||||
private void assertUserGranted(AuthorizationManager<String> manager, String... authorities) { |
||||
AuthorizationResult authorizationResult = createAuthorizationResult(manager, authorities); |
||||
assertThat(authorizationResult).isNotNull(); |
||||
assertThat(authorizationResult.isGranted()).isTrue(); |
||||
} |
||||
|
||||
private void assertUserDenied(AuthorizationManager<String> manager, String... authorities) { |
||||
AuthorizationResult authorizationResult = createAuthorizationResult(manager, authorities); |
||||
assertThat(authorizationResult).isNotNull(); |
||||
assertThat(authorizationResult.isGranted()).isFalse(); |
||||
} |
||||
|
||||
private AuthorizationResult createAuthorizationResult(AuthorizationManager<String> manager, String... authorities) { |
||||
TestingAuthenticationToken authenticatedUser = new TestingAuthenticationToken("user", "pass", authorities); |
||||
return manager.authorize(() -> authenticatedUser, ""); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue