fix(security): enforce mutation visibility checks
diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go
index 6e16c6a..58e55c1 100644
--- a/internal/controller/answer_controller.go
+++ b/internal/controller/answer_controller.go
@@ -208,6 +208,7 @@
 	}()
 	req.QuestionID = uid.DeShortID(req.QuestionID)
 	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
+	req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
 
 	canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
 		permission.AnswerEdit,
@@ -220,7 +221,7 @@
 	}
 
 	linkUrlLimitUser := canList[2]
-	isAdmin := middleware.GetUserIsAdminModerator(ctx)
+	isAdmin := req.IsAdminModerator
 	if !isAdmin || !linkUrlLimitUser {
 		captchaPass := ac.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionAnswer, req.UserID, req.CaptchaID, req.CaptchaCode)
 		if !captchaPass {
diff --git a/internal/controller/comment_controller.go b/internal/controller/comment_controller.go
index e0f18a8..4bdd8ac 100644
--- a/internal/controller/comment_controller.go
+++ b/internal/controller/comment_controller.go
@@ -88,6 +88,7 @@
 	}()
 	req.ObjectID = uid.DeShortID(req.ObjectID)
 	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
+	req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
 
 	canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
 		permission.CommentAdd,
@@ -100,7 +101,7 @@
 		return
 	}
 	linkUrlLimitUser := canList[3]
-	isAdmin := middleware.GetUserIsAdminModerator(ctx)
+	isAdmin := req.IsAdminModerator
 	if !isAdmin || !linkUrlLimitUser {
 		captchaPass := cc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionComment, req.UserID, req.CaptchaID, req.CaptchaCode)
 		if !captchaPass {
diff --git a/internal/controller/report_controller.go b/internal/controller/report_controller.go
index 13b4c09..53b298e 100644
--- a/internal/controller/report_controller.go
+++ b/internal/controller/report_controller.go
@@ -73,7 +73,8 @@
 	}
 	req.ObjectID = uid.DeShortID(req.ObjectID)
 	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
-	isAdmin := middleware.GetUserIsAdminModerator(ctx)
+	req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
+	isAdmin := req.IsAdminModerator
 	if !isAdmin {
 		captchaPass := rc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionReport, req.UserID, req.CaptchaID, req.CaptchaCode)
 		if !captchaPass {
diff --git a/internal/controller/vote_controller.go b/internal/controller/vote_controller.go
index 3027966..10ce42a 100644
--- a/internal/controller/vote_controller.go
+++ b/internal/controller/vote_controller.go
@@ -72,6 +72,7 @@
 	}
 	req.ObjectID = uid.DeShortID(req.ObjectID)
 	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
+	req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
 
 	can, needRank, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, true)
 	if err != nil {
@@ -85,7 +86,7 @@
 		return
 	}
 
-	isAdmin := middleware.GetUserIsAdminModerator(ctx)
+	isAdmin := req.IsAdminModerator
 	if !isAdmin {
 		captchaPass := vc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionVote, req.UserID, req.CaptchaID, req.CaptchaCode)
 		if !captchaPass {
@@ -126,7 +127,8 @@
 	}
 	req.ObjectID = uid.DeShortID(req.ObjectID)
 	req.UserID = middleware.GetLoginUserIDFromContext(ctx)
-	isAdmin := middleware.GetUserIsAdminModerator(ctx)
+	req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
+	isAdmin := req.IsAdminModerator
 
 	can, needRank, err := vc.rankService.CheckVotePermission(ctx, req.UserID, req.ObjectID, false)
 	if err != nil {
diff --git a/internal/schema/answer_schema.go b/internal/schema/answer_schema.go
index bf80c56..e81b863 100644
--- a/internal/schema/answer_schema.go
+++ b/internal/schema/answer_schema.go
@@ -47,17 +47,18 @@
 )
 
 type AnswerAddReq struct {
-	QuestionID  string `json:"question_id"`
-	Content     string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
-	HTML        string `json:"-"`
-	UserID      string `json:"-"`
-	CanEdit     bool   `json:"-"`
-	CanDelete   bool   `json:"-"`
-	CanRecover  bool   `json:"-"`
-	CaptchaID   string `json:"captcha_id"`
-	CaptchaCode string `json:"captcha_code"`
-	IP          string `json:"-"`
-	UserAgent   string `json:"-"`
+	QuestionID       string `json:"question_id"`
+	Content          string `validate:"required,notblank,gte=6,lte=65535" json:"content"`
+	HTML             string `json:"-"`
+	UserID           string `json:"-"`
+	IsAdminModerator bool   `json:"-"`
+	CanEdit          bool   `json:"-"`
+	CanDelete        bool   `json:"-"`
+	CanRecover       bool   `json:"-"`
+	CaptchaID        string `json:"captcha_id"`
+	CaptchaCode      string `json:"captcha_code"`
+	IP               string `json:"-"`
+	UserAgent        string `json:"-"`
 }
 
 func (req *AnswerAddReq) Check() (errFields []*validator.FormErrorField, err error) {
diff --git a/internal/schema/comment_schema.go b/internal/schema/comment_schema.go
index 8297c88..0fe5ee0 100644
--- a/internal/schema/comment_schema.go
+++ b/internal/schema/comment_schema.go
@@ -44,7 +44,8 @@
 	CaptchaCode         string   `json:"captcha_code"`
 
 	// user id
-	UserID string `json:"-"`
+	UserID           string `json:"-"`
+	IsAdminModerator bool   `json:"-"`
 	// whether user can add it
 	CanAdd bool `json:"-"`
 	// whether user can edit it
diff --git a/internal/schema/report_schema.go b/internal/schema/report_schema.go
index 1f702df..3c1e332 100644
--- a/internal/schema/report_schema.go
+++ b/internal/schema/report_schema.go
@@ -28,9 +28,10 @@
 	// report content
 	Content string `validate:"omitempty,gt=0,lte=500" json:"content"`
 	// user id
-	UserID      string `json:"-"`
-	CaptchaID   string `json:"captcha_id"` // captcha_id
-	CaptchaCode string `json:"captcha_code"`
+	UserID           string `json:"-"`
+	IsAdminModerator bool   `json:"-"`
+	CaptchaID        string `json:"captcha_id"` // captcha_id
+	CaptchaCode      string `json:"captcha_code"`
 }
 
 // GetReportListReq get report list all request
diff --git a/internal/schema/vote_schema.go b/internal/schema/vote_schema.go
index e82adcc..15a5479 100644
--- a/internal/schema/vote_schema.go
+++ b/internal/schema/vote_schema.go
@@ -20,11 +20,12 @@
 package schema
 
 type VoteReq struct {
-	ObjectID    string `validate:"required" json:"object_id"`
-	IsCancel    bool   `validate:"omitempty" json:"is_cancel"`
-	CaptchaID   string `json:"captcha_id"`
-	CaptchaCode string `json:"captcha_code"`
-	UserID      string `json:"-"`
+	ObjectID         string `validate:"required" json:"object_id"`
+	IsCancel         bool   `validate:"omitempty" json:"is_cancel"`
+	CaptchaID        string `json:"captcha_id"`
+	CaptchaCode      string `json:"captcha_code"`
+	UserID           string `json:"-"`
+	IsAdminModerator bool   `json:"-"`
 }
 
 type VoteResp struct {
diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go
index 455ab86..0cbf202 100644
--- a/internal/service/comment/comment_service.go
+++ b/internal/service/comment/comment_service.go
@@ -144,6 +144,9 @@
 	if objInfo.IsDeleted() {
 		return nil, errors.BadRequest(reason.NewObjectAlreadyDeleted)
 	}
+	if err := objInfo.CheckVisibility(req.UserID, req.IsAdminModerator); err != nil {
+		return nil, err
+	}
 	objInfo.ObjectID = uid.DeShortID(objInfo.ObjectID)
 	objInfo.QuestionID = uid.DeShortID(objInfo.QuestionID)
 	objInfo.AnswerID = uid.DeShortID(objInfo.AnswerID)
diff --git a/internal/service/content/answer_service.go b/internal/service/content/answer_service.go
index bda7b58..e43467a 100644
--- a/internal/service/content/answer_service.go
+++ b/internal/service/content/answer_service.go
@@ -258,6 +258,14 @@
 	if !exist {
 		return "", errors.BadRequest(reason.QuestionNotFound)
 	}
+	if err := (&schema.SimpleObjectInfo{
+		ObjectType:            constant.QuestionObjectType,
+		QuestionCreatorUserID: questionInfo.UserID,
+		QuestionStatus:        questionInfo.Status,
+		QuestionShow:          questionInfo.Show,
+	}).CheckVisibility(req.UserID, req.IsAdminModerator); err != nil {
+		return "", err
+	}
 	if questionInfo.Status == entity.QuestionStatusClosed || questionInfo.Status == entity.QuestionStatusDeleted {
 		err = errors.BadRequest(reason.AnswerCannotAddByClosedQuestion)
 		return "", err
diff --git a/internal/service/content/vote_service.go b/internal/service/content/vote_service.go
index 1f74769..0456543 100644
--- a/internal/service/content/vote_service.go
+++ b/internal/service/content/vote_service.go
@@ -94,6 +94,9 @@
 	if objectInfo.IsDeleted() {
 		return nil, errors.BadRequest(reason.NewObjectAlreadyDeleted)
 	}
+	if err := objectInfo.CheckVisibility(req.UserID, req.IsAdminModerator); err != nil {
+		return nil, err
+	}
 	// make object id must be decoded
 	objectInfo.ObjectID = req.ObjectID
 
@@ -145,6 +148,9 @@
 	if objectInfo.IsDeleted() {
 		return nil, errors.BadRequest(reason.NewObjectAlreadyDeleted)
 	}
+	if err := objectInfo.CheckVisibility(req.UserID, req.IsAdminModerator); err != nil {
+		return nil, err
+	}
 	// make object id must be decoded
 	objectInfo.ObjectID = req.ObjectID
 
diff --git a/internal/service/report/report_service.go b/internal/service/report/report_service.go
index 3edbc1b..c57d83e 100644
--- a/internal/service/report/report_service.go
+++ b/internal/service/report/report_service.go
@@ -99,6 +99,9 @@
 	if objInfo.IsDeleted() {
 		return errors.BadRequest(reason.NewObjectAlreadyDeleted)
 	}
+	if err := objInfo.CheckVisibility(req.UserID, req.IsAdminModerator); err != nil {
+		return err
+	}
 
 	cf, err := rs.configService.GetConfigByID(ctx, req.ReportType)
 	if err != nil || cf == nil {
diff --git a/internal/service/user_admin/user_backyard.go b/internal/service/user_admin/user_backyard.go
index 072feec..acbaf99 100644
--- a/internal/service/user_admin/user_backyard.go
+++ b/internal/service/user_admin/user_backyard.go
@@ -170,6 +170,7 @@
 		if err := us.revokeUserAPIKeys(ctx, userInfo.ID); err != nil {
 			return err
 		}
+		us.authService.RemoveUserAllTokens(ctx, userInfo.ID)
 	}
 
 	// remove all content that user created, such as question, answer, comment, etc.