mirror of https://github.com/halo-dev/halo
perf: data desensitization for comments and replies (#3936)
#### 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 对客户端评论及回复列表接口进行脱敏处理 ```pull/4005/head
parent
da5fb1a252
commit
f5493a6d86
|
@ -107,7 +107,7 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService
|
|||
);
|
||||
}
|
||||
|
||||
private Mono<CommentVo> toCommentVo(Comment comment) {
|
||||
Mono<CommentVo> 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<? extends CommentVo> 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 <E extends AbstractExtension, T extends ExtensionVoOperator> Mono<CommentStatsVo>
|
||||
|
@ -130,7 +149,7 @@ public class CommentPublicQueryServiceImpl implements CommentPublicQueryService
|
|||
.defaultIfEmpty(CommentStatsVo.empty());
|
||||
}
|
||||
|
||||
private Mono<ReplyVo> toReplyVo(Reply reply) {
|
||||
Mono<ReplyVo> 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<? extends ReplyVo> 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<OwnerInfo> getOwnerInfo(Comment.CommentOwner owner) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue