generated from micronaut-projects/micronaut-project-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds an api `Tenant`, which represents the abstract notion of a tenant. Moreover, it adds a RequestFilter to resolve the tenant and set it as a request attribute. This pull request adds an argument binder that uses the request attribute resolved in the filter. Because of this, a user can bind the tenant as a controller method parameter
- Loading branch information
Showing
20 changed files
with
663 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
multitenancy/src/main/java/io/micronaut/multitenancy/Tenant.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright 2017-2024 original 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 io.micronaut.multitenancy; | ||
|
||
/** | ||
* This interface represents the abstract notion of a tenant. | ||
* @author Sergio del Amo | ||
* @since 5.5.0 | ||
*/ | ||
|
||
@FunctionalInterface | ||
public interface Tenant { | ||
/** | ||
* A Unique identifier for the tenant. | ||
* @return A Unique identifier for the tenant. | ||
*/ | ||
String id(); | ||
} |
111 changes: 111 additions & 0 deletions
111
multitenancy/src/main/java/io/micronaut/multitenancy/filter/TenantResolverFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* Copyright 2017-2024 original 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 io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.core.annotation.Internal; | ||
import io.micronaut.core.annotation.Nullable; | ||
import io.micronaut.core.order.Ordered; | ||
import io.micronaut.http.HttpRequest; | ||
import io.micronaut.http.annotation.RequestFilter; | ||
import io.micronaut.http.annotation.ServerFilter; | ||
import io.micronaut.http.filter.FilterPatternStyle; | ||
import io.micronaut.http.filter.ServerFilterPhase; | ||
import io.micronaut.multitenancy.exceptions.TenantNotFoundException; | ||
import io.micronaut.multitenancy.tenantresolver.HttpRequestTenantResolver; | ||
import io.micronaut.multitenancy.tenantresolver.TenantResolver; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.io.Serializable; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Adds a tenant identifier if resolved as a request attribute. | ||
* @author Sergio del Amo | ||
* @since 5.5.0 | ||
*/ | ||
@ServerFilter(patternStyle = FilterPatternStyle.REGEX, | ||
value = "${" + TenantResolverFilterConfigurationProperties.PREFIX + ".regex-pattern:" + TenantResolverFilterConfigurationProperties.DEFAULT_REGEX_PATTERN + "}") | ||
@Internal | ||
final class TenantResolverFilter implements Ordered { | ||
/** | ||
* Request attribute for tenant Identifier. | ||
*/ | ||
public static final String ATTRIBUTE_TENANT = "tenantIdentifier"; | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(TenantResolverFilter.class); | ||
|
||
@Nullable | ||
private final HttpRequestTenantResolver httpRequestTenantResolver; | ||
|
||
@Nullable | ||
private final TenantResolver tenantResolver; | ||
|
||
/** | ||
* | ||
* @param httpRequestTenantResolver HTTP Request Tenant Resolver | ||
* @param tenantResolver Tenant Resolver | ||
*/ | ||
TenantResolverFilter(@Nullable HttpRequestTenantResolver httpRequestTenantResolver, | ||
@Nullable TenantResolver tenantResolver) { | ||
this.httpRequestTenantResolver = httpRequestTenantResolver; | ||
this.tenantResolver = tenantResolver; | ||
} | ||
|
||
@Override | ||
public int getOrder() { | ||
return ServerFilterPhase.SECURITY.before(); | ||
} | ||
|
||
@RequestFilter | ||
void filter(HttpRequest<?> request) { | ||
Optional<Serializable> tenantOptional = resolveTenant(request); | ||
if (tenantOptional.isEmpty()) { | ||
tenantOptional = resolveTenant(); | ||
} | ||
tenantOptional.ifPresent(tenant -> request.setAttribute(ATTRIBUTE_TENANT, tenant)); | ||
} | ||
|
||
private Optional<Serializable> resolveTenant(HttpRequest<?> request) { | ||
if (httpRequestTenantResolver == null) { | ||
return Optional.empty(); | ||
} | ||
try { | ||
return Optional.of(httpRequestTenantResolver.resolveTenantIdentifier(request)); | ||
} catch (TenantNotFoundException ex) { | ||
if (LOG.isTraceEnabled()) { | ||
LOG.trace("Tenant could not be resolved"); | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
|
||
private Optional<Serializable> resolveTenant() { | ||
if (tenantResolver == null) { | ||
return Optional.empty(); | ||
} | ||
try { | ||
return Optional.of(tenantResolver.resolveTenantIdentifier()); | ||
} catch (TenantNotFoundException ex) { | ||
if (LOG.isTraceEnabled()) { | ||
LOG.trace("Tenant could not be resolved"); | ||
} | ||
} | ||
return Optional.empty(); | ||
} | ||
} | ||
|
33 changes: 33 additions & 0 deletions
33
...ncy/src/main/java/io/micronaut/multitenancy/filter/TenantResolverFilterConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright 2017-2024 original 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 io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.core.annotation.NonNull; | ||
import io.micronaut.core.util.Toggleable; | ||
|
||
/** | ||
* Configuration for the {@link TenantResolverFilter}. | ||
* @author Sergio del Amo | ||
* @since 5.5.0 | ||
*/ | ||
public interface TenantResolverFilterConfiguration extends Toggleable { | ||
/** | ||
* | ||
* @return Regular expression pattern. Filter will only process requests whose path matches this pattern. | ||
*/ | ||
@NonNull | ||
String getRegexPattern(); | ||
} |
69 changes: 69 additions & 0 deletions
69
...in/java/io/micronaut/multitenancy/filter/TenantResolverFilterConfigurationProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* Copyright 2017-2024 original 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 io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.context.annotation.ConfigurationProperties; | ||
import io.micronaut.core.annotation.Internal; | ||
import io.micronaut.multitenancy.MultitenancyConfiguration; | ||
|
||
/** | ||
* {@link ConfigurationProperties} implementation of {@link TenantResolverFilterConfiguration}. | ||
*/ | ||
@ConfigurationProperties(TenantResolverFilterConfigurationProperties.PREFIX) | ||
@Internal | ||
final class TenantResolverFilterConfigurationProperties implements TenantResolverFilterConfiguration { | ||
public static final String PREFIX = MultitenancyConfiguration.PREFIX + ".filter"; | ||
/** | ||
* The default regex pattern. | ||
*/ | ||
@SuppressWarnings("WeakerAccess") | ||
public static final String DEFAULT_REGEX_PATTERN = "^.*$"; | ||
|
||
/** | ||
* The default enable value. | ||
*/ | ||
@SuppressWarnings("WeakerAccess") | ||
public static final boolean DEFAULT_ENABLED = true; | ||
|
||
private boolean enabled = DEFAULT_ENABLED; | ||
private String regexPattern = DEFAULT_REGEX_PATTERN; | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return enabled; | ||
} | ||
|
||
/** | ||
* Whether {@link io.micronaut.multitenancy.filter.TenantResolverFilter} should be enabled. Default value ({@value #DEFAULT_ENABLED}). | ||
* @param enabled enabled flag | ||
*/ | ||
public void setEnabled(boolean enabled) { | ||
this.enabled = enabled; | ||
} | ||
|
||
@Override | ||
public String getRegexPattern() { | ||
return regexPattern; | ||
} | ||
|
||
/** | ||
* Tenant Resolver filter processes only request paths matching this regular expression. Default Value: {@value #DEFAULT_REGEX_PATTERN} | ||
* @param regexPattern Regular expression pattern for the filter. | ||
*/ | ||
public void setRegexPattern(String regexPattern) { | ||
this.regexPattern = regexPattern; | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...ancy/src/main/java/io/micronaut/multitenancy/filter/TenantTypedRequestArgumentBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright 2017-2024 original 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 io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.core.annotation.Internal; | ||
import io.micronaut.core.convert.ArgumentConversionContext; | ||
import io.micronaut.core.type.Argument; | ||
import io.micronaut.http.HttpRequest; | ||
import io.micronaut.http.bind.binders.TypedRequestArgumentBinder; | ||
import io.micronaut.multitenancy.Tenant; | ||
import jakarta.inject.Singleton; | ||
|
||
import java.util.Optional; | ||
|
||
@Internal | ||
@Singleton | ||
final class TenantTypedRequestArgumentBinder implements TypedRequestArgumentBinder<Tenant> { | ||
@Override | ||
public Argument<Tenant> argumentType() { | ||
return Argument.of(Tenant.class); | ||
} | ||
|
||
@Override | ||
public BindingResult<Tenant> bind(ArgumentConversionContext<Tenant> context, HttpRequest<?> source) { | ||
if (!source.getAttributes().contains(TenantResolverFilter.ATTRIBUTE_TENANT)) { | ||
return BindingResult.UNSATISFIED; | ||
} | ||
Optional<BindingResult<Tenant>> bindingResult = source.getAttribute(TenantResolverFilter.ATTRIBUTE_TENANT, String.class) | ||
.map(tenantId -> (Tenant) () -> tenantId) | ||
.map(tenant -> () -> Optional.of(tenant)); | ||
return bindingResult.isEmpty() | ||
? BindingResult.EMPTY | ||
: bindingResult.get(); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
multitenancy/src/main/java/io/micronaut/multitenancy/filter/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* Copyright 2017-2024 original 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. | ||
*/ | ||
/** | ||
* Classes related to Tenant Resolution Filter. | ||
* @author Sergio del Amo | ||
* @since 5.5.0 | ||
*/ | ||
@Requires(property = TenantResolverFilterConfigurationProperties.PREFIX + ".enabled", | ||
notEquals = StringUtils.FALSE, | ||
defaultValue = StringUtils.TRUE) | ||
@Configuration | ||
package io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.context.annotation.Configuration; | ||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.core.util.StringUtils; |
15 changes: 15 additions & 0 deletions
15
...src/test/java/io/micronaut/multitenancy/filter/TenantResolverFilterConfigurationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.micronaut.multitenancy.filter; | ||
|
||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
@MicronautTest(startApplication = false) | ||
class TenantResolverFilterConfigurationTest { | ||
|
||
@Test | ||
void defaultEnabled(TenantResolverFilterConfiguration tenantResolverFilterConfiguration) { | ||
assertTrue(tenantResolverFilterConfiguration.isEnabled()); | ||
} | ||
} |
Oops, something went wrong.