@@ -30,6 +30,7 @@ import (
3030
3131 "github.com/apache/answer/internal/base/constant"
3232 "github.com/apache/answer/internal/base/handler"
33+ "github.com/apache/answer/internal/base/reason"
3334 "github.com/apache/answer/internal/entity"
3435 "github.com/apache/answer/internal/schema"
3536 "github.com/apache/answer/internal/service/comment_common"
@@ -41,6 +42,7 @@ import (
4142 "github.com/apache/answer/pkg/converter"
4243 "github.com/apache/answer/pkg/obj"
4344 "github.com/apache/answer/pkg/uid"
45+ "github.com/segmentfault/pacman/errors"
4446 "github.com/segmentfault/pacman/log"
4547)
4648
@@ -90,6 +92,10 @@ func NewActivityService(
9092// GetObjectTimeline get object timeline
9193func (as * ActivityService ) GetObjectTimeline (ctx context.Context , req * schema.GetObjectTimelineReq ) (
9294 resp * schema.GetObjectTimelineResp , err error ) {
95+ if err = as .ensureTimelineObjectVisible (ctx , req .ObjectID , req .UserID , req .IsAdminModerator ); err != nil {
96+ return nil , err
97+ }
98+
9399 resp = & schema.GetObjectTimelineResp {
94100 ObjectInfo : & schema.ActObjectInfo {},
95101 Timeline : make ([]* schema.ActObjectTimeline , 0 ),
@@ -254,12 +260,114 @@ func (as *ActivityService) formatTimelineUserInfo(ctx context.Context, timeline
254260// GetObjectTimelineDetail get object timeline
255261func (as * ActivityService ) GetObjectTimelineDetail (ctx context.Context , req * schema.GetObjectTimelineDetailReq ) (
256262 resp * schema.GetObjectTimelineDetailResp , err error ) {
263+ if err = as .ensureTimelineRevisionVisible (ctx , req .NewRevisionID , req .UserID , req .IsAdminModerator ); err != nil {
264+ return nil , err
265+ }
266+ if err = as .ensureTimelineRevisionVisible (ctx , req .OldRevisionID , req .UserID , req .IsAdminModerator ); err != nil {
267+ return nil , err
268+ }
269+
257270 resp = & schema.GetObjectTimelineDetailResp {}
258271 resp .OldRevision , _ = as .getOneObjectDetail (ctx , req .OldRevisionID )
259272 resp .NewRevision , _ = as .getOneObjectDetail (ctx , req .NewRevisionID )
260273 return resp , nil
261274}
262275
276+ func (as * ActivityService ) ensureTimelineRevisionVisible (ctx context.Context , revisionID , userID string ,
277+ isAdminModerator bool ) error {
278+ if revisionID == "0" {
279+ return nil
280+ }
281+ revisionInfo , err := as .revisionService .GetRevision (ctx , revisionID )
282+ if err != nil {
283+ return err
284+ }
285+ return as .ensureTimelineObjectVisible (ctx , revisionInfo .ObjectID , userID , isAdminModerator )
286+ }
287+
288+ func (as * ActivityService ) ensureTimelineObjectVisible (ctx context.Context , objectID , userID string ,
289+ isAdminModerator bool ) error {
290+ objInfo , err := as .objectInfoService .GetInfo (ctx , objectID )
291+ if err != nil {
292+ return err
293+ }
294+
295+ var parentQuestionInfo * schema.SimpleObjectInfo
296+ if objInfo .ObjectType != constant .QuestionObjectType && len (objInfo .QuestionID ) > 0 && objInfo .QuestionID != "0" {
297+ parentQuestionInfo , err = as .objectInfoService .GetInfo (ctx , objInfo .QuestionID )
298+ if err != nil {
299+ return err
300+ }
301+ }
302+
303+ return validateTimelineObjectVisibility (objInfo , parentQuestionInfo , userID , isAdminModerator )
304+ }
305+
306+ func validateTimelineObjectVisibility (objInfo , parentQuestionInfo * schema.SimpleObjectInfo ,
307+ userID string , isAdminModerator bool ) error {
308+ if objInfo == nil {
309+ return errors .NotFound (reason .ObjectNotFound )
310+ }
311+ if isTimelineObjectRestricted (objInfo ) &&
312+ ! canViewRestrictedTimelineObject (objInfo .ObjectType , objInfo .ObjectCreatorUserID , userID , isAdminModerator ) {
313+ return errors .NotFound (timelineNotFoundReasonByObjectType (objInfo .ObjectType ))
314+ }
315+ if parentQuestionInfo != nil && isTimelineQuestionRestricted (parentQuestionInfo ) &&
316+ ! canViewRestrictedTimelineObject (parentQuestionInfo .ObjectType , parentQuestionInfo .ObjectCreatorUserID ,
317+ userID , isAdminModerator ) {
318+ return errors .NotFound (reason .QuestionNotFound )
319+ }
320+ return nil
321+ }
322+
323+ func canViewRestrictedTimelineObject (objectType , creatorUserID , userID string , isAdminModerator bool ) bool {
324+ if isAdminModerator {
325+ return true
326+ }
327+ switch objectType {
328+ case constant .QuestionObjectType , constant .AnswerObjectType , constant .CommentObjectType :
329+ return creatorUserID == userID
330+ default :
331+ return false
332+ }
333+ }
334+
335+ func isTimelineObjectRestricted (objInfo * schema.SimpleObjectInfo ) bool {
336+ switch objInfo .ObjectType {
337+ case constant .QuestionObjectType :
338+ return isTimelineQuestionRestricted (objInfo )
339+ case constant .AnswerObjectType :
340+ return objInfo .AnswerStatus == entity .AnswerStatusDeleted || objInfo .AnswerStatus == entity .AnswerStatusPending
341+ case constant .CommentObjectType :
342+ return objInfo .CommentStatus == entity .CommentStatusDeleted || objInfo .CommentStatus == entity .CommentStatusPending
343+ case constant .TagObjectType :
344+ return objInfo .TagStatus == entity .TagStatusDeleted
345+ default :
346+ return false
347+ }
348+ }
349+
350+ func isTimelineQuestionRestricted (questionInfo * schema.SimpleObjectInfo ) bool {
351+ return questionInfo .QuestionStatus == entity .QuestionStatusDeleted ||
352+ questionInfo .QuestionStatus == entity .QuestionStatusPending ||
353+ questionInfo .QuestionShow == entity .QuestionHide
354+ }
355+
356+ func timelineNotFoundReasonByObjectType (objectType string ) string {
357+ switch objectType {
358+ case constant .QuestionObjectType :
359+ return reason .QuestionNotFound
360+ case constant .AnswerObjectType :
361+ return reason .AnswerNotFound
362+ case constant .CommentObjectType :
363+ return reason .CommentNotFound
364+ case constant .TagObjectType :
365+ return reason .TagNotFound
366+ default :
367+ return reason .ObjectNotFound
368+ }
369+ }
370+
263371// getOneObjectDetail get object detail
264372func (as * ActivityService ) getOneObjectDetail (ctx context.Context , revisionID string ) (
265373 resp * schema.ObjectTimelineDetail , err error ) {
0 commit comments