Skip to content

Commit a080cda

Browse files
committed
Add validation for requested issuer in multitenancy how-to guide
Issue gh-663
1 parent 54cfdb9 commit a080cda

6 files changed

+40
-48
lines changed

docs/modules/ROOT/pages/guides/how-to-multitenancy.adoc

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ We will use the following class in each of the delegating implementations below:
6565
include::{examples-dir}/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java[]
6666
----
6767

68+
<1> Component registration implicitly enables an allowlist of approved issuers that can be used.
69+
6870
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.
6971

7072
[[multi-tenant-create-components]]
@@ -98,6 +100,7 @@ TIP: Click on the "Expand folded text" icon in the code sample above to display
98100
<2> A `JdbcRegisteredClientRepository` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`.
99101
<3> A composite implementation of a `RegisteredClientRepository` that delegates to a `JdbcRegisteredClientRepository` mapped to the _"requested"_ issuer identifier.
100102
<4> Obtain the `JdbcRegisteredClientRepository` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`.
103+
<5> If unable to find `JdbcRegisteredClientRepository`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers.
101104

102105
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.
103106

@@ -132,6 +135,7 @@ include::{examples-dir}/main/java/sample/multitenancy/OAuth2AuthorizationService
132135
<2> A `JdbcOAuth2AuthorizationService` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`.
133136
<3> A composite implementation of an `OAuth2AuthorizationService` that delegates to a `JdbcOAuth2AuthorizationService` mapped to the _"requested"_ issuer identifier.
134137
<4> Obtain the `JdbcOAuth2AuthorizationService` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`.
138+
<5> If unable to find `JdbcOAuth2AuthorizationService`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers.
135139

136140
[[multi-tenant-oauth2-authorization-consent-service]]
137141
=== Multi-tenant OAuth2AuthorizationConsentService
@@ -148,6 +152,7 @@ include::{examples-dir}/main/java/sample/multitenancy/OAuth2AuthorizationConsent
148152
<2> A `JdbcOAuth2AuthorizationConsentService` instance mapped to issuer identifier `issuer2` and using a dedicated `DataSource`.
149153
<3> A composite implementation of an `OAuth2AuthorizationConsentService` that delegates to a `JdbcOAuth2AuthorizationConsentService` mapped to the _"requested"_ issuer identifier.
150154
<4> Obtain the `JdbcOAuth2AuthorizationConsentService` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`.
155+
<5> If unable to find `JdbcOAuth2AuthorizationConsentService`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers.
151156

152157
[[multi-tenant-jwk-source]]
153158
=== Multi-tenant JWKSource
@@ -164,6 +169,7 @@ include::{examples-dir}/main/java/sample/multitenancy/JWKSourceConfig.java[]
164169
<2> A `JWKSet` instance mapped to issuer identifier `issuer2`.
165170
<3> A composite implementation of an `JWKSource<SecurityContext>` that uses the `JWKSet` mapped to the _"requested"_ issuer identifier.
166171
<4> Obtain the `JWKSet` that is mapped to the _"requested"_ issuer identifier indicated by `AuthorizationServerContext.getIssuer()`.
172+
<5> If unable to find `JWKSet`, then error since the _"requested"_ issuer identifier is not in the allowlist of approved issuers.
167173

168174
[[multi-tenant-add-tenants-dynamically]]
169175
== Add Tenants Dynamically

docs/src/main/java/sample/multitenancy/JWKSourceConfig.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.security.KeyPairGenerator;
2020
import java.security.interfaces.RSAPrivateKey;
2121
import java.security.interfaces.RSAPublicKey;
22-
import java.util.Collections;
2322
import java.util.List;
2423
import java.util.UUID;
2524

@@ -33,6 +32,7 @@
3332

3433
import org.springframework.context.annotation.Bean;
3534
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.util.Assert;
3636

3737
@Configuration(proxyBeanMethods = false)
3838
public class JWKSourceConfig {
@@ -77,12 +77,13 @@ private DelegatingJWKSource(TenantPerIssuerComponentRegistry componentRegistry)
7777

7878
@Override
7979
public List<JWK> get(JWKSelector jwkSelector, SecurityContext context) throws KeySourceException {
80-
JWKSet jwkSet = getJwkSet();
81-
return (jwkSet != null) ? jwkSelector.select(jwkSet) : Collections.emptyList();
80+
return jwkSelector.select(getJwkSet());
8281
}
8382

8483
private JWKSet getJwkSet() {
85-
return this.componentRegistry.get(JWKSet.class); // <4>
84+
JWKSet jwkSet = this.componentRegistry.get(JWKSet.class); // <4>
85+
Assert.state(jwkSet != null, "JWKSet not found for \"requested\" issuer identifier."); // <5>
86+
return jwkSet;
8687
}
8788

8889
}

docs/src/main/java/sample/multitenancy/OAuth2AuthorizationConsentServiceConfig.java

+9-13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
2626
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
2727
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
28+
import org.springframework.util.Assert;
2829

2930
@Configuration(proxyBeanMethods = false)
3031
public class OAuth2AuthorizationConsentServiceConfig {
@@ -56,30 +57,25 @@ private DelegatingOAuth2AuthorizationConsentService(TenantPerIssuerComponentRegi
5657

5758
@Override
5859
public void save(OAuth2AuthorizationConsent authorizationConsent) {
59-
OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService();
60-
if (authorizationConsentService != null) {
61-
authorizationConsentService.save(authorizationConsent);
62-
}
60+
getAuthorizationConsentService().save(authorizationConsent);
6361
}
6462

6563
@Override
6664
public void remove(OAuth2AuthorizationConsent authorizationConsent) {
67-
OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService();
68-
if (authorizationConsentService != null) {
69-
authorizationConsentService.remove(authorizationConsent);
70-
}
65+
getAuthorizationConsentService().remove(authorizationConsent);
7166
}
7267

7368
@Override
7469
public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
75-
OAuth2AuthorizationConsentService authorizationConsentService = getAuthorizationConsentService();
76-
return (authorizationConsentService != null) ?
77-
authorizationConsentService.findById(registeredClientId, principalName) :
78-
null;
70+
return getAuthorizationConsentService().findById(registeredClientId, principalName);
7971
}
8072

8173
private OAuth2AuthorizationConsentService getAuthorizationConsentService() {
82-
return this.componentRegistry.get(OAuth2AuthorizationConsentService.class); // <4>
74+
OAuth2AuthorizationConsentService authorizationConsentService =
75+
this.componentRegistry.get(OAuth2AuthorizationConsentService.class); // <4>
76+
Assert.state(authorizationConsentService != null,
77+
"OAuth2AuthorizationConsentService not found for \"requested\" issuer identifier."); // <5>
78+
return authorizationConsentService;
8379
}
8480

8581
}

docs/src/main/java/sample/multitenancy/OAuth2AuthorizationServiceConfig.java

+10-17
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
2727
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
2828
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
29+
import org.springframework.util.Assert;
2930

3031
@Configuration(proxyBeanMethods = false)
3132
public class OAuth2AuthorizationServiceConfig {
@@ -57,38 +58,30 @@ private DelegatingOAuth2AuthorizationService(TenantPerIssuerComponentRegistry co
5758

5859
@Override
5960
public void save(OAuth2Authorization authorization) {
60-
OAuth2AuthorizationService authorizationService = getAuthorizationService();
61-
if (authorizationService != null) {
62-
authorizationService.save(authorization);
63-
}
61+
getAuthorizationService().save(authorization);
6462
}
6563

6664
@Override
6765
public void remove(OAuth2Authorization authorization) {
68-
OAuth2AuthorizationService authorizationService = getAuthorizationService();
69-
if (authorizationService != null) {
70-
authorizationService.remove(authorization);
71-
}
66+
getAuthorizationService().remove(authorization);
7267
}
7368

7469
@Override
7570
public OAuth2Authorization findById(String id) {
76-
OAuth2AuthorizationService authorizationService = getAuthorizationService();
77-
return (authorizationService != null) ?
78-
authorizationService.findById(id) :
79-
null;
71+
return getAuthorizationService().findById(id);
8072
}
8173

8274
@Override
8375
public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {
84-
OAuth2AuthorizationService authorizationService = getAuthorizationService();
85-
return (authorizationService != null) ?
86-
authorizationService.findByToken(token, tokenType) :
87-
null;
76+
return getAuthorizationService().findByToken(token, tokenType);
8877
}
8978

9079
private OAuth2AuthorizationService getAuthorizationService() {
91-
return this.componentRegistry.get(OAuth2AuthorizationService.class); // <4>
80+
OAuth2AuthorizationService authorizationService =
81+
this.componentRegistry.get(OAuth2AuthorizationService.class); // <4>
82+
Assert.state(authorizationService != null,
83+
"OAuth2AuthorizationService not found for \"requested\" issuer identifier."); // <5>
84+
return authorizationService;
9285
}
9386

9487
}

docs/src/main/java/sample/multitenancy/RegisteredClientRepositoryConfig.java

+9-13
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
2929
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
3030
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
31+
import org.springframework.util.Assert;
3132

3233
@Configuration(proxyBeanMethods = false)
3334
public class RegisteredClientRepositoryConfig {
@@ -88,30 +89,25 @@ private DelegatingRegisteredClientRepository(TenantPerIssuerComponentRegistry co
8889

8990
@Override
9091
public void save(RegisteredClient registeredClient) {
91-
RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository();
92-
if (registeredClientRepository != null) {
93-
registeredClientRepository.save(registeredClient);
94-
}
92+
getRegisteredClientRepository().save(registeredClient);
9593
}
9694

9795
@Override
9896
public RegisteredClient findById(String id) {
99-
RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository();
100-
return (registeredClientRepository != null) ?
101-
registeredClientRepository.findById(id) :
102-
null;
97+
return getRegisteredClientRepository().findById(id);
10398
}
10499

105100
@Override
106101
public RegisteredClient findByClientId(String clientId) {
107-
RegisteredClientRepository registeredClientRepository = getRegisteredClientRepository();
108-
return (registeredClientRepository != null) ?
109-
registeredClientRepository.findByClientId(clientId) :
110-
null;
102+
return getRegisteredClientRepository().findByClientId(clientId);
111103
}
112104

113105
private RegisteredClientRepository getRegisteredClientRepository() {
114-
return this.componentRegistry.get(RegisteredClientRepository.class); // <4>
106+
RegisteredClientRepository registeredClientRepository =
107+
this.componentRegistry.get(RegisteredClientRepository.class); // <4>
108+
Assert.state(registeredClientRepository != null,
109+
"RegisteredClientRepository not found for \"requested\" issuer identifier."); // <5>
110+
return registeredClientRepository;
115111
}
116112

117113
}

docs/src/main/java/sample/multitenancy/TenantPerIssuerComponentRegistry.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public class TenantPerIssuerComponentRegistry {
3030
private final ConcurrentMap<String, Map<Class<?>, Object>> registry = new ConcurrentHashMap<>();
3131

32-
public <T> void register(String tenantId, Class<T> componentClass, T component) {
32+
public <T> void register(String tenantId, Class<T> componentClass, T component) { // <1>
3333
Assert.hasText(tenantId, "tenantId cannot be empty");
3434
Assert.notNull(componentClass, "componentClass cannot be null");
3535
Assert.notNull(component, "component cannot be null");

0 commit comments

Comments
 (0)