refactor: restrict author page access to users with post permission (#7279)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.20.x

#### What this PR does / why we need it:
仅对包含文章权限的用户生成作者页面

#### Which issue(s) this PR fixes:

Fixes #7202

#### Does this PR introduce a user-facing change?

```release-note
仅对包含文章权限的用户生成作者页面
```
pull/7282/head
guqing 2025-03-10 12:37:01 +08:00 committed by GitHub
parent 5c2c298bc3
commit 2c4c876ef2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 8 deletions

View File

@ -26,6 +26,8 @@ public enum AuthorityUtils {
public static final String COMMENT_MANAGEMENT_ROLE_NAME = "role-template-manage-comments"; public static final String COMMENT_MANAGEMENT_ROLE_NAME = "role-template-manage-comments";
public static final String POST_CONTRIBUTOR_ROLE_NAME = "role-template-post-contributor";
/** /**
* Converts an array of GrantedAuthority objects to a role set. * Converts an array of GrantedAuthority objects to a role set.
* *

View File

@ -5,6 +5,7 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
import static run.halo.app.theme.router.PageUrlUtils.totalPage; import static run.halo.app.theme.router.PageUrlUtils.totalPage;
import java.util.Map; import java.util.Map;
import java.util.Set;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -16,10 +17,12 @@ import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.i18n.LocaleContextResolver; import org.springframework.web.server.i18n.LocaleContextResolver;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import run.halo.app.core.extension.User; import run.halo.app.core.extension.User;
import run.halo.app.core.user.service.RoleService;
import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
import run.halo.app.infra.SystemSetting; import run.halo.app.infra.SystemSetting;
import run.halo.app.infra.exception.NotFoundException; import run.halo.app.infra.exception.NotFoundException;
import run.halo.app.security.authorization.AuthorityUtils;
import run.halo.app.theme.DefaultTemplateEnum; import run.halo.app.theme.DefaultTemplateEnum;
import run.halo.app.theme.finders.PostFinder; import run.halo.app.theme.finders.PostFinder;
import run.halo.app.theme.finders.vo.ListedPostVo; import run.halo.app.theme.finders.vo.ListedPostVo;
@ -42,6 +45,7 @@ public class AuthorPostsRouteFactory implements RouteFactory {
private final PostFinder postFinder; private final PostFinder postFinder;
private final ReactiveExtensionClient client; private final ReactiveExtensionClient client;
private final RoleService roleService;
private SystemConfigurableEnvironmentFetcher environmentFetcher; private SystemConfigurableEnvironmentFetcher environmentFetcher;
private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator; private final TitleVisibilityIdentifyCalculator titleVisibilityIdentifyCalculator;
@ -58,16 +62,31 @@ public class AuthorPostsRouteFactory implements RouteFactory {
HandlerFunction<ServerResponse> handlerFunction() { HandlerFunction<ServerResponse> handlerFunction() {
return request -> { return request -> {
String name = request.pathVariable("name"); String name = request.pathVariable("name");
return ServerResponse.ok() return hasPostManageRole(name)
.render(DefaultTemplateEnum.AUTHOR.getValue(), .flatMap(hasPostManageRole -> {
Map.of("author", getByName(name), if (hasPostManageRole) {
"posts", postList(request, name), return ServerResponse.ok()
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.AUTHOR.getValue() .render(DefaultTemplateEnum.AUTHOR.getValue(),
) Map.of("author", getByName(name),
); "posts", postList(request, name),
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.AUTHOR.getValue()
)
);
}
return Mono.error(new NotFoundException("Author page not found."));
});
}; };
} }
protected Mono<Boolean> hasPostManageRole(String username) {
return roleService.getRolesByUsername(username)
.collectList()
.flatMap(roles -> roleService.contains(roles,
Set.of(AuthorityUtils.POST_CONTRIBUTOR_ROLE_NAME))
)
.defaultIfEmpty(false);
}
private Mono<UrlContextListResult<ListedPostVo>> postList(ServerRequest request, String name) { private Mono<UrlContextListResult<ListedPostVo>> postList(ServerRequest request, String name) {
String path = request.path(); String path = request.path();
int pageNum = pageNumInPathVariable(request); int pageNum = pageNumInPathVariable(request);

View File

@ -1,6 +1,9 @@
package run.halo.app.theme.router.factories; package run.halo.app.theme.router.factories;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -30,7 +33,10 @@ class AuthorPostsRouteFactoryTest extends RouteFactoryTestSuite {
@Test @Test
void create() { void create() {
RouterFunction<ServerResponse> routerFunction = authorPostsRouteFactory.create(null); var spyAuthorRoute = spy(authorPostsRouteFactory);
doReturn(Mono.just(true)).when(spyAuthorRoute).hasPostManageRole(anyString());
RouterFunction<ServerResponse> routerFunction = spyAuthorRoute.create(null);
WebTestClient webClient = getWebTestClient(routerFunction); WebTestClient webClient = getWebTestClient(routerFunction);
when(client.fetch(eq(User.class), eq("fake-user"))) when(client.fetch(eq(User.class), eq("fake-user")))