Skip to content

updated multi-tenancy example to add TenantRepository and corrected t… #7643

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,29 @@ http
Resolving the tenant by claim is similar to doing so by request material.
The only real difference is the `toTenant` method implementation:

[source,java]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this snippet might be in the wrong place. Note the previous sentence, which is introducing the code snippet that is now below this one. I think we'd need to change the description a bit, too, for the reader to be able to follow.

----
@Component
public class TenantRepository {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point in adding this sample TenantRepository. But, I think this demonstrates actually that it would be better to change the classes below to simply use a pair of Maps.

Would you mind changing the examples to use a Map<String, String> instead? Then, the members might be called issuersByTenant and jwkUrisByTenant instead of tenants or tenantRepository.

@Autowired
private AuthenticationProperties properties;

/**
* return issuer location
*
* @param tenant
* @return
*/
public String get(String tenant) {
if (Arrays.asList(properties.getTenants()).contains(tenant)) {
return tenant;
}
return null;
}
}
----
An example source for a tenant, the repository check and return the tenant from a tenant whitelist in the properties.

[source,java]
----
@Component
Expand All @@ -1273,13 +1296,13 @@ public class TenantAuthenticationManagerResolver implements AuthenticationManage

@Override
public AuthenticationManager resolve(HttpServletRequest request) {
return this.authenticationManagers.computeIfAbsent(toTenant(request), this::fromTenant); <3>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this removed? It should be linked to the <3> in the comments below.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see... The reference was aimed at the two lines. I thought it was just aimed at the second code referenced. My bad!

return this.authenticationManagers.computeIfAbsent(toTenant(request), this::fromTenant);
}

private String toTenant(HttpServletRequest request) {
try {
String token = this.resolver.resolve(request);
return (String) JWTParser.parse(token).getJWTClaimsSet().getIssuer();
return JWTParser.parse(token).getJWTClaimsSet().getIssuer();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
Expand Down Expand Up @@ -1318,13 +1341,13 @@ This extra parsing can be alleviated by configuring the `JwtDecoder` directly wi
----
@Component
public class TenantJWSKeySelector
implements JWTClaimSetAwareJWSKeySelector<SecurityContext> {
implements JWTClaimsSetAwareJWSKeySelector<SecurityContext> {

private final TenantRepository tenants; <1>
private final TenantRepository tenantRepository; <1>
private final Map<String, JWSKeySelector<SecurityContext>> selectors = new ConcurrentHashMap<>(); <2>

public TenantJWSKeySelector(TenantRepository tenants) {
this.tenants = tenants;
this.tenantRepository = tenants;
}

@Override
Expand All @@ -1339,10 +1362,9 @@ public class TenantJWSKeySelector
}

private JWSKeySelector<SecurityContext> fromTenant(String tenant) {
return Optional.ofNullable(this.tenantRepository.findById(tenant)) <3>
.map(t -> t.getAttrbute("jwks_uri"))
.map(this::fromUri)
.orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
return Optional.ofNullable(this.tenantRepository.get(tenant)) <3>
.map(this::fromUri)
.orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
}

private JWSKeySelector<SecurityContext> fromUri(String uri) {
Expand Down Expand Up @@ -1370,10 +1392,10 @@ Next, we can construct a `JWTProcessor`:
[source,java]
----
@Bean
JWTProcessor jwtProcessor(JWTClaimSetJWSKeySelector keySelector) {
JWTProcessor jwtProcessor(JWTClaimsSetAwareJWSKeySelector<SecurityContext> keySelector) {
ConfigurableJWTProcessor<SecurityContext> jwtProcessor =
new DefaultJWTProcessor();
jwtProcessor.setJWTClaimSetJWSKeySelector(keySelector);
new DefaultJWTProcessor<>();
jwtProcessor.setJWTClaimsSetAwareJWSKeySelector(keySelector);
return jwtProcessor;
}
----
Expand Down Expand Up @@ -1402,14 +1424,13 @@ public class TenantJwtIssuerValidator implements OAuth2TokenValidator<Jwt> {
}

private String toTenant(Jwt jwt) {
return jwt.getIssuer();
return jwt.getIssuer().toString();
}

private JwtIssuerValidator fromTenant(String tenant) {
return Optional.ofNullable(this.tenants.findById(tenant))
.map(t -> t.getAttribute("issuer"))
.map(JwtIssuerValidator::new)
.orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
return Optional.ofNullable(this.tenants.get(tenant))
.map(JwtIssuerValidator::new)
.orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
}
}
----
Expand All @@ -1419,10 +1440,10 @@ Now that we have a tenant-aware processor and a tenant-aware validator, we can p
[source,java]
----
@Bean
JwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
JwtDecoder jwtDecoder(JWTProcessor<SecurityContext> jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>
(JwtValidators.createDefault(), this.jwtValidator);
(JwtValidators.createDefault(), jwtValidator);
decoder.setJwtValidator(validator);
return decoder;
}
Expand Down