diff --git a/docs/modules/ROOT/pages/guides/how-to-multitenancy.adoc b/docs/modules/ROOT/pages/guides/how-to-multitenancy.adoc index 1ecb3f44..830cf03a 100644 --- a/docs/modules/ROOT/pages/guides/how-to-multitenancy.adoc +++ b/docs/modules/ROOT/pages/guides/how-to-multitenancy.adoc @@ -65,6 +65,8 @@ We will use the following class in each of the delegating implementations below: include::{examples-dir}/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java[] ---- +<1> Component registration implicitly enables an allowlist of approved issuers that can be used. + TIP: This registry is designed to allow components to be easily registered at startup to support adding tenants statically, but also supports xref:guides/how-to-multitenancy.adoc#multi-tenant-add-tenants-dynamically[adding tenants dynamically] at runtime. [[multi-tenant-create-components]] @@ -98,6 +100,7 @@ TIP: Click on the "Expand folded text" icon in the code sample above to display <2> A `JdbcRegisteredClientRepository` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`. <3> A composite implementation of a `RegisteredClientRepository` that delegates to a `JdbcRegisteredClientRepository` mapped to the _"requested"_ issuer identifier. <4> Obtain the `JdbcRegisteredClientRepository` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`. +<5> If unable to find `JdbcRegisteredClientRepository`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers. IMPORTANT: Explicitly configuring the issuer identifier via `AuthorizationServerSettings.builder().issuer("http://localhost:9000")` forces to a single-tenant configuration. Avoid explicitly configuring the issuer identifier when using a multi-tenant hosting configuration. @@ -132,6 +135,7 @@ include::{examples-dir}/main/java/sample/multitenancy/OAuth2AuthorizationService <2> A `JdbcOAuth2AuthorizationService` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`. <3> A composite implementation of an `OAuth2AuthorizationService` that delegates to a `JdbcOAuth2AuthorizationService` mapped to the _"requested"_ issuer identifier. <4> Obtain the `JdbcOAuth2AuthorizationService` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`. +<5> If unable to find `JdbcOAuth2AuthorizationService`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers. [[multi-tenant-oauth2-authorization-consent-service]] === Multi-tenant OAuth2AuthorizationConsentService @@ -148,6 +152,7 @@ include::{examples-dir}/main/java/sample/multitenancy/OAuth2AuthorizationConsent <2> A `JdbcOAuth2AuthorizationConsentService` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`. <3> A composite implementation of an `OAuth2AuthorizationConsentService` that delegates to a `JdbcOAuth2AuthorizationConsentService` mapped to the _"requested"_ issuer identifier. <4> Obtain the `JdbcOAuth2AuthorizationConsentService` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`. +<5> If unable to find `JdbcOAuth2AuthorizationConsentService`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers. [[multi-tenant-jwk-source]] === Multi-tenant JWKSource @@ -164,6 +169,7 @@ include::{examples-dir}/main/java/sample/multitenancy/JWKSourceConfig.java[] <2> A `JWKSet` instance mapped to issuer identifier `issuer2`. <3> A composite implementation of an `JWKSource` that uses the `JWKSet` mapped to the _"requested"_ issuer identifier. <4> Obtain the `JWKSet` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`. +<5> If unable to find `JWKSet`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers. [[multi-tenant-add-tenants-dynamically]] == Add Tenants Dynamically diff --git a/docs/src/main/java/sample/multitenancy/JWKSourceConfig.java b/docs/src/main/java/sample/multitenancy/JWKSourceConfig.java index 096347e0..e26ac4e3 100644 --- a/docs/src/main/java/sample/multitenancy/JWKSourceConfig.java +++ b/docs/src/main/java/sample/multitenancy/JWKSourceConfig.java @@ -19,7 +19,6 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; -import java.util.Collections; import java.util.List; import java.util.UUID; @@ -33,6 +32,7 @@ import com.nimbusds.jose.proc.SecurityContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.Assert; @Configuration(proxyBeanMethods = false) public class JWKSourceConfig { @@ -77,12 +77,13 @@ public class JWKSourceConfig { @Override public List get(JWKSelector jwkSelector, SecurityContext context) throws KeySourceException { - JWKSet jwkSet = getJwkSet(); - return (jwkSet != null) ? jwkSelector.select(jwkSet) : Collections.emptyList(); + return jwkSelector.select(getJwkSet()); } private JWKSet getJwkSet() { - return this.componentRegistry.get(JWKSet.class); // <4> + JWKSet jwkSet = this.componentRegistry.get(JWKSet.class); // <4> + Assert.state(jwkSet != null, "JWKSet not found for \"requested\" issuer identifier."); // <5> + return jwkSet; } } diff --git a/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationConsentServiceConfig.java b/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationConsentServiceConfig.java index 062c8a8a..76c19fc5 100644 --- a/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationConsentServiceConfig.java +++ b/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationConsentServiceConfig.java @@ -25,6 +25,7 @@ import org.springframework.security.oauth2.server.authorization.JdbcOAuth2Author import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.util.Assert; @Configuration(proxyBeanMethods = false) public class OAuth2AuthorizationConsentServiceConfig { @@ -56,30 +57,25 @@ public class OAuth2AuthorizationConsentServiceConfig { @Override public void save(OAuth2AuthorizationConsent authorizationConsent) { - OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService(); - if (authorizationConsentService != null) { - authorizationConsentService.save(authorizationConsent); - } + getAuthorizationConsentService().save(authorizationConsent); } @Override public void remove(OAuth2AuthorizationConsent authorizationConsent) { - OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService(); - if (authorizationConsentService != null) { - authorizationConsentService.remove(authorizationConsent); - } + getAuthorizationConsentService().remove(authorizationConsent); } @Override public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) { - OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService(); - return (authorizationConsentService != null) ? - authorizationConsentService.findById(registeredClientId, principalName) : - null; + return getAuthorizationConsentService().findById(registeredClientId, principalName); } private OAuth2AuthorizationConsentService getAuthorizationConsentService() { - return this.componentRegistry.get(OAuth2AuthorizationConsentService.class); // <4> + OAuth2AuthorizationConsentService authorizationConsentService = + this.componentRegistry.get(OAuth2AuthorizationConsentService.class); // <4> + Assert.state(authorizationConsentService != null, + "OAuth2AuthorizationConsentService not found for \"requested\" issuer identifier."); // <5> + return authorizationConsentService; } } diff --git a/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationServiceConfig.java b/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationServiceConfig.java index 8529c482..b0daa6fa 100644 --- a/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationServiceConfig.java +++ b/docs/src/main/java/sample/multitenancy/OAuth2AuthorizationServiceConfig.java @@ -26,6 +26,7 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.util.Assert; @Configuration(proxyBeanMethods = false) public class OAuth2AuthorizationServiceConfig { @@ -57,38 +58,30 @@ public class OAuth2AuthorizationServiceConfig { @Override public void save(OAuth2Authorization authorization) { - OAuth2AuthorizationService authorizationService = getAuthorizationService(); - if (authorizationService != null) { - authorizationService.save(authorization); - } + getAuthorizationService().save(authorization); } @Override public void remove(OAuth2Authorization authorization) { - OAuth2AuthorizationService authorizationService = getAuthorizationService(); - if (authorizationService != null) { - authorizationService.remove(authorization); - } + getAuthorizationService().remove(authorization); } @Override public OAuth2Authorization findById(String id) { - OAuth2AuthorizationService authorizationService = getAuthorizationService(); - return (authorizationService != null) ? - authorizationService.findById(id) : - null; + return getAuthorizationService().findById(id); } @Override public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) { - OAuth2AuthorizationService authorizationService = getAuthorizationService(); - return (authorizationService != null) ? - authorizationService.findByToken(token, tokenType) : - null; + return getAuthorizationService().findByToken(token, tokenType); } private OAuth2AuthorizationService getAuthorizationService() { - return this.componentRegistry.get(OAuth2AuthorizationService.class); // <4> + OAuth2AuthorizationService authorizationService = + this.componentRegistry.get(OAuth2AuthorizationService.class); // <4> + Assert.state(authorizationService != null, + "OAuth2AuthorizationService not found for \"requested\" issuer identifier."); // <5> + return authorizationService; } } diff --git a/docs/src/main/java/sample/multitenancy/RegisteredClientRepositoryConfig.java b/docs/src/main/java/sample/multitenancy/RegisteredClientRepositoryConfig.java index 0a21c63c..6d0ccd30 100644 --- a/docs/src/main/java/sample/multitenancy/RegisteredClientRepositoryConfig.java +++ b/docs/src/main/java/sample/multitenancy/RegisteredClientRepositoryConfig.java @@ -28,6 +28,7 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; +import org.springframework.util.Assert; @Configuration(proxyBeanMethods = false) public class RegisteredClientRepositoryConfig { @@ -88,30 +89,25 @@ public class RegisteredClientRepositoryConfig { @Override public void save(RegisteredClient registeredClient) { - RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository(); - if (registeredClientRepository != null) { - registeredClientRepository.save(registeredClient); - } + getRegisteredClientRepository().save(registeredClient); } @Override public RegisteredClient findById(String id) { - RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository(); - return (registeredClientRepository != null) ? - registeredClientRepository.findById(id) : - null; + return getRegisteredClientRepository().findById(id); } @Override public RegisteredClient findByClientId(String clientId) { - RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository(); - return (registeredClientRepository != null) ? - registeredClientRepository.findByClientId(clientId) : - null; + return getRegisteredClientRepository().findByClientId(clientId); } private RegisteredClientRepository getRegisteredClientRepository() { - return this.componentRegistry.get(RegisteredClientRepository.class); // <4> + RegisteredClientRepository registeredClientRepository = + this.componentRegistry.get(RegisteredClientRepository.class); // <4> + Assert.state(registeredClientRepository != null, + "RegisteredClientRepository not found for \"requested\" issuer identifier."); // <5> + return registeredClientRepository; } } diff --git a/docs/src/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java b/docs/src/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java index 8ffee7be..084c0333 100644 --- a/docs/src/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java +++ b/docs/src/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java @@ -29,7 +29,7 @@ import org.springframework.util.Assert; public class TenantPerIssuerComponentRegistry { private final ConcurrentMap, Object>> registry = new ConcurrentHashMap<>(); - public void register(String tenantId, Class componentClass, T component) { + public void register(String tenantId, Class componentClass, T component) { // <1> Assert.hasText(tenantId, "tenantId cannot be empty"); Assert.notNull(componentClass, "componentClass cannot be null"); Assert.notNull(component, "component cannot be null");