From f5493a6d86343df12385f04864488eb13809043b Mon Sep 17 00:00:00 2001 From: Li Date: Fri, 26 May 2023 22:52:21 +0800 Subject: [PATCH] perf: data desensitization for comments and replies (#3936) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /milestone 2.6.x #### What this PR does / why we need it: 对客户端评论接口进行脱敏处理,移除 `ipAddress` 属性以及 owner 下的 `email` 及 `name` 属性。 UA 由于主题端有使用的可能以及敏感性不强,因此未移除。 对于 #3915 中提到的评论时间为排序时间,需要在 [`https://github.com/halo-sigs/plugin-comment-widget`](https://github.com/halo-sigs/plugin-comment-widget) 插件中做处理。 #### Which issue(s) this PR fixes: #3915 #### Special notes for your reviewer: 查看评论接口 `/apis/api.halo.run/v1alpha1/comments` 及回复接口 `/apis/api.halo.run/v1alpha1/comments/{commentName}/reply` 返回字段是否存在 `spec.ipAddress` 、`owner.email`与 `owner.name` 字段。 #### Does this PR introduce a user-facing change? ```release-note 对客户端评论及回复列表接口进行脱敏处理 ``` --- .../impl/CommentPublicQueryServiceImpl.java | 46 +++++++- .../CommentPublicQueryServiceImplTest.java | 106 ++++++++++++++++++ 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImpl.java b/application/src/main/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImpl.java index 4f5be5cbf..634c5dcd5 100644 --- a/application/src/main/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImpl.java +++ b/application/src/main/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImpl.java @@ -107,7 +107,7 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService ); } - private Mono toCommentVo(Comment comment) { + Mono toCommentVo(Comment comment) { Comment.CommentOwner owner = comment.getSpec().getOwner(); return Mono.just(CommentVo.from(comment)) .flatMap(commentVo -> populateStats(Comment.class, commentVo) @@ -116,7 +116,26 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService .flatMap(commentVo -> getOwnerInfo(owner) .doOnNext(commentVo::setOwner) .thenReturn(commentVo) - ); + ) + .flatMap(commentVo -> filterCommentSensitiveData(commentVo)); + } + + private Mono filterCommentSensitiveData(CommentVo commentVo) { + var owner = commentVo.getOwner(); + commentVo.setOwner(OwnerInfo + .builder() + .displayName(owner.getDisplayName()) + .avatar(owner.getAvatar()) + .kind(owner.getKind()) + .build()); + + commentVo.getSpec().setIpAddress(""); + var specOwner = commentVo.getSpec().getOwner(); + specOwner.setName(""); + if (specOwner.getAnnotations() != null) { + specOwner.getAnnotations().remove("Email"); + } + return Mono.just(commentVo); } private Mono @@ -130,7 +149,7 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService .defaultIfEmpty(CommentStatsVo.empty()); } - private Mono toReplyVo(Reply reply) { + Mono toReplyVo(Reply reply) { return Mono.just(ReplyVo.from(reply)) .flatMap(replyVo -> populateStats(Reply.class, replyVo) .doOnNext(replyVo::setStats) @@ -138,7 +157,26 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService .flatMap(replyVo -> getOwnerInfo(reply.getSpec().getOwner()) .doOnNext(replyVo::setOwner) .thenReturn(replyVo) - ); + ) + .flatMap(replyVo -> filterReplySensitiveData(replyVo)); + } + + private Mono filterReplySensitiveData(ReplyVo replyVo) { + var owner = replyVo.getOwner(); + replyVo.setOwner(OwnerInfo + .builder() + .displayName(owner.getDisplayName()) + .avatar(owner.getAvatar()) + .kind(owner.getKind()) + .build()); + + replyVo.getSpec().setIpAddress(""); + var specOwner = replyVo.getSpec().getOwner(); + specOwner.setName(""); + if (specOwner.getAnnotations() != null) { + specOwner.getAnnotations().remove("Email"); + } + return Mono.just(replyVo); } private Mono getOwnerInfo(Comment.CommentOwner owner) { diff --git a/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java b/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java index 5ccc83366..04b2d42a2 100644 --- a/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java +++ b/application/src/test/java/run/halo/app/theme/finders/impl/CommentPublicQueryServiceImplTest.java @@ -6,10 +6,12 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import java.time.Instant; +import java.util.HashMap; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.json.JSONException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -17,6 +19,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.stubbing.Answer; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; import reactor.core.publisher.Mono; @@ -34,6 +37,7 @@ import run.halo.app.extension.MetadataOperator; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Ref; import run.halo.app.infra.AnonymousUserConst; +import run.halo.app.infra.utils.JsonUtils; import run.halo.app.metrics.CounterService; /** @@ -163,6 +167,57 @@ class CommentPublicQueryServiceImplTest { assertThat(result).isEqualTo("1, 2, 3, 4, 5, 6, 9, 14, 10, 8, 7, 13, 12, 11"); } + @Test + void desensitizeComment() throws JSONException { + var commentOwner = new Comment.CommentOwner(); + commentOwner.setName("fake-user"); + commentOwner.setDisplayName("Fake User"); + commentOwner.setAnnotations(new HashMap<>() { + { + put(Comment.CommentOwner.KIND_EMAIL, "mail@halo.run"); + } + }); + var comment = commentForCompare("1", null, true, 0); + comment.getSpec().setIpAddress("127.0.0.1"); + comment.getSpec().setOwner(commentOwner); + + Counter counter = new Counter(); + counter.setUpvote(0); + when(counterService.getByName(any())).thenReturn(Mono.just(counter)); + + var result = commentPublicQueryService.toCommentVo(comment).block(); + result.getMetadata().setCreationTimestamp(null); + result.getSpec().setCreationTime(null); + JSONAssert.assertEquals(""" + { + "metadata":{ + "name":"1" + }, + "spec":{ + "owner":{ + "name":"", + "displayName":"Fake User", + "annotations":{ + + } + }, + "ipAddress":"", + "priority":0, + "top":true + }, + "owner":{ + "kind":"User", + "displayName":"fake-display-name" + }, + "stats":{ + "upvote":0 + } + } + """, + JsonUtils.objectToJson(result), + true); + } + Comment commentForCompare(String name, Instant creationTime, boolean top, int priority) { Comment comment = new Comment(); comment.setMetadata(new Metadata()); @@ -312,6 +367,57 @@ class CommentPublicQueryServiceImplTest { .verifyComplete(); } + @Test + void desensitizeReply() throws JSONException { + var reply = createReply(); + reply.getSpec().getOwner() + .setAnnotations(new HashMap<>() { + { + put(Comment.CommentOwner.KIND_EMAIL, "mail@halo.run"); + } + }); + reply.getSpec().setIpAddress("127.0.0.1"); + + Counter counter = new Counter(); + counter.setUpvote(0); + when(counterService.getByName(any())).thenReturn(Mono.just(counter)); + + var result = commentPublicQueryService.toReplyVo(reply).block(); + result.getMetadata().setCreationTimestamp(null); + result.getSpec().setCreationTime(null); + JSONAssert.assertEquals(""" + { + "metadata":{ + "name":"fake-reply" + }, + "spec":{ + "raw":"fake-raw", + "content":"fake-content", + "owner":{ + "kind":"User", + "name":"", + "displayName":"fake-display-name", + "annotations":{ + + } + }, + "ipAddress":"", + "hidden":false, + "commentName":"fake-comment" + }, + "owner":{ + "kind":"User", + "displayName":"fake-display-name" + }, + "stats":{ + "upvote":0 + } + } + """, + JsonUtils.objectToJson(result), + true); + } + @SuppressWarnings("unchecked") private void mockWhenListRely() { // Mock