Skip to content
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

feat: enhance additional web filter with scope and portal interception #5301

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions api/src/main/java/run/halo/app/security/AdditionalWebFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,14 @@ public interface AdditionalWebFilter extends WebFilter, ExtensionPoint, Ordered
default int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}

default Scope getScope() {
return Scope.PROTECTED_API;
}

enum Scope {
PROTECTED_API,
PORTAL,
ALL
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import run.halo.app.infra.AnonymousUserConst;
import run.halo.app.infra.properties.HaloProperties;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
import run.halo.app.security.AdditionalWebFilter;
import run.halo.app.security.DefaultUserDetailService;
import run.halo.app.security.DynamicMatcherSecurityWebFilterChain;
import run.halo.app.security.authentication.SecurityConfigurer;
Expand Down Expand Up @@ -92,14 +93,16 @@ SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http,
// Integrate with other configurers separately
securityConfigurers.orderedStream()
.forEach(securityConfigurer -> securityConfigurer.configure(http));
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build());
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build(),
Set.of(AdditionalWebFilter.Scope.PROTECTED_API));
}

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityWebFilterChain portalFilterChain(ServerHttpSecurity http,
ServerSecurityContextRepository securityContextRepository,
HaloProperties haloProperties) {
HaloProperties haloProperties,
ExtensionGetter extensionGetter) {
var pathMatcher = pathMatchers(HttpMethod.GET, "/**");
var mediaTypeMatcher = new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML);
mediaTypeMatcher.setIgnoredMediaTypes(Set.of(MediaType.ALL));
Expand All @@ -126,7 +129,8 @@ SecurityWebFilterChain portalFilterChain(ServerHttpSecurity http,
new HaloAnonymousAuthenticationWebFilter("portal", AnonymousUserConst.PRINCIPAL,
AuthorityUtils.createAuthorityList(AnonymousUserConst.Role),
securityContextRepository)));
return http.build();
return new DynamicMatcherSecurityWebFilterChain(extensionGetter, http.build(),
Set.of(AdditionalWebFilter.Scope.PORTAL));
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package run.halo.app.security;

import java.util.Set;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.NonNull;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
Expand All @@ -25,10 +28,23 @@ public class DynamicMatcherSecurityWebFilterChain implements SecurityWebFilterCh

private final ExtensionGetter extensionGetter;

private final Set<AdditionalWebFilter.Scope> matchScopes;

/**
* Creates an aggregated {@link SecurityWebFilterChain} using the provided original and
* additional filters.
*
* @param matchScopes Only matched the given scopes will be added to the filter chain
*/
public DynamicMatcherSecurityWebFilterChain(ExtensionGetter extensionGetter,
SecurityWebFilterChain delegate) {
SecurityWebFilterChain delegate,
Set<AdditionalWebFilter.Scope> matchScopes) {
Assert.isTrue(!CollectionUtils.isEmpty(matchScopes), "Match scopes must not be empty");
Assert.notNull(extensionGetter, "Extension getter must not be null");
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
this.extensionGetter = extensionGetter;
this.matchScopes = matchScopes;
}

@Override
Expand All @@ -44,6 +60,10 @@ public Flux<WebFilter> getWebFilters() {

private Flux<WebFilter> getAdditionalFilters() {
return extensionGetter.getEnabledExtensionByDefinition(AdditionalWebFilter.class)
.filter(additionalWebFilter -> {
var scope = additionalWebFilter.getScope();
return scope == AdditionalWebFilter.Scope.ALL || matchScopes.contains(scope);
})
.map(additionalWebFilter -> new OrderedWebFilter(additionalWebFilter,
additionalWebFilter.getOrder())
);
Expand Down
Loading