feat: add question answer tag comment permission Control

This commit is contained in:
LinkinStar 2022-11-25 12:07:56 +08:00
parent f6911df6d9
commit ee7428cba5
13 changed files with 253 additions and 125 deletions

View File

@ -202,16 +202,28 @@ func (ac *AnswerController) Update(ctx *gin.Context) {
// @Security ApiKeyAuth
// @Accept json
// @Produce json
// @Param data body schema.AnswerList true "AnswerList"
// @Param data body schema.AnswerListReq true "AnswerListReq"
// @Success 200 {string} string ""
// @Router /answer/api/v1/answer/list [get]
func (ac *AnswerController) AnswerList(ctx *gin.Context) {
req := &schema.AnswerList{}
req := &schema.AnswerListReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
canList, err := ac.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.AnswerEditRank,
rank.AnswerDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanEdit = canList[0]
req.CanDelete = canList[1]
list, count, err := ac.answerService.SearchList(ctx, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)

View File

@ -41,12 +41,19 @@ func (cc *CommentController) AddComment(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, rank.CommentAddRank, "")
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.CommentAddRank,
rank.CommentEditRank,
rank.CommentDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !can {
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
@ -135,6 +142,16 @@ func (cc *CommentController) GetCommentWithPage(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.CommentEditRank,
rank.CommentDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanEdit = canList[0]
req.CanDelete = canList[1]
resp, err := cc.commentService.GetCommentWithPage(ctx, req)
handler.HandleResponse(ctx, err, resp)
@ -177,6 +194,16 @@ func (cc *CommentController) GetComment(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.CommentEditRank,
rank.CommentDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanEdit = canList[0]
req.CanDelete = canList[1]
resp, err := cc.commentService.GetComment(ctx, req)
handler.HandleResponse(ctx, err, resp)

View File

@ -91,8 +91,26 @@ func (qc *QuestionController) CloseQuestion(ctx *gin.Context) {
func (qc *QuestionController) GetQuestion(ctx *gin.Context) {
id := ctx.Query("id")
userID := middleware.GetLoginUserIDFromContext(ctx)
isAdmin := middleware.GetIsAdminFromContext(ctx)
info, err := qc.questionService.GetQuestion(ctx, id, userID, true, isAdmin)
req := &schema.QuestionPermission{}
canList, err := qc.rankService.CheckOperationPermissions(ctx, userID, []string{
rank.QuestionAddRank,
rank.QuestionEditRank,
rank.QuestionDeleteRank,
}, id)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.CanClose = middleware.GetIsAdminFromContext(ctx)
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
info, err := qc.questionService.GetQuestionAndAddPV(ctx, id, userID, req)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
@ -195,12 +213,20 @@ func (qc *QuestionController) AddQuestion(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
can, err := qc.rankService.CheckOperationPermission(ctx, req.UserID, rank.QuestionAddRank, "")
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.QuestionAddRank,
rank.QuestionEditRank,
rank.QuestionDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !can {
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.CanClose = middleware.GetIsAdminFromContext(ctx)
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
@ -225,20 +251,31 @@ func (qc *QuestionController) UpdateQuestion(ctx *gin.Context) {
return
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
canList, err := qc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.QuestionAddRank,
rank.QuestionEditRank,
rank.QuestionDeleteRank,
rank.QuestionEditWithoutReviewRank,
}, "")
}, req.ID)
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
if !canList[0] {
req.CanAdd = canList[0]
req.CanEdit = canList[1]
req.CanDelete = canList[2]
req.NoNeedReview = canList[3]
req.CanClose = middleware.GetIsAdminFromContext(ctx)
if !req.CanAdd {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
req.IsAdmin = middleware.GetIsAdminFromContext(ctx)
if !req.CanEdit {
handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil)
return
}
req.NoNeedReview = canList[1]
resp, err := qc.questionService.UpdateQuestion(ctx, req)
handler.HandleResponse(ctx, err, resp)

View File

@ -128,6 +128,16 @@ func (tc *TagController) GetTagInfo(ctx *gin.Context) {
}
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
canList, err := tc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{
rank.TagEditRank,
rank.TagDeleteRank,
}, "")
if err != nil {
handler.HandleResponse(ctx, err, nil)
return
}
req.CanEdit = canList[0]
req.CanDelete = canList[1]
resp, err := tc.tagService.GetTagInfo(ctx, req)
handler.HandleResponse(ctx, err, resp)

View File

@ -33,13 +33,17 @@ type AnswerUpdateReq struct {
NoNeedReview bool `json:"-"`
}
type AnswerList struct {
QuestionID string `json:"question_id" form:"question_id"` // question_id
Order string `json:"order" form:"order"` // 1 Default 2 time
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
LoginUserID string `json:"-" `
IsAdmin bool `json:"-"`
type AnswerListReq struct {
QuestionID string `json:"question_id" form:"question_id"` // question_id
Order string `json:"order" form:"order"` // 1 Default 2 time
Page int `json:"page" form:"page"` // Query number of pages
PageSize int `json:"page_size" form:"page_size"` // Search page size
UserID string `json:"-" `
IsAdmin bool `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
}
type AnswerInfo struct {

View File

@ -19,6 +19,12 @@ type AddCommentReq struct {
MentionUsernameList []string `validate:"omitempty" json:"mention_username_list"`
// user id
UserID string `json:"-"`
// whether user can add it
CanAdd bool `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
}
// RemoveCommentReq remove comment
@ -73,6 +79,10 @@ type GetCommentWithPageReq struct {
QueryCond string `validate:"omitempty,oneof=vote" form:"query_cond"`
// user id
UserID string `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
}
// GetCommentReq get comment list page request
@ -81,6 +91,10 @@ type GetCommentReq struct {
ID string `validate:"required" form:"id"`
// user id
UserID string `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
}
// GetCommentResp comment response

View File

@ -32,6 +32,18 @@ type QuestionAdd struct {
Tags []*TagItem `validate:"required,dive" json:"tags"`
// user id
UserID string `json:"-"`
*QuestionPermission
}
type QuestionPermission struct {
// whether user can add it
CanAdd bool `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
// whether user can close it
CanClose bool `json:"-"`
}
type CheckCanQuestionUpdate struct {
@ -59,6 +71,7 @@ type QuestionUpdate struct {
UserID string `json:"-"`
IsAdmin bool `json:"-"`
NoNeedReview bool `json:"-"`
*QuestionPermission
}
type QuestionBaseInfo struct {

View File

@ -23,6 +23,10 @@ type GetTagInfoReq struct {
Name string `validate:"omitempty,gt=0,lte=35" form:"name"`
// user id
UserID string `json:"-"`
// whether user can edit it
CanEdit bool `json:"-"`
// whether user can delete it
CanDelete bool `json:"-"`
}
func (r *GetTagInfoReq) Check() (errFields []*validator.FormErrorField, err error) {

View File

@ -425,39 +425,39 @@ func (as *AnswerService) AdminSetAnswerStatus(ctx context.Context, answerID stri
return nil
}
func (as *AnswerService) SearchList(ctx context.Context, search *schema.AnswerList) ([]*schema.AnswerInfo, int64, error) {
func (as *AnswerService) SearchList(ctx context.Context, req *schema.AnswerListReq) ([]*schema.AnswerInfo, int64, error) {
list := make([]*schema.AnswerInfo, 0)
dbSearch := entity.AnswerSearch{}
dbSearch.QuestionID = search.QuestionID
dbSearch.Page = search.Page
dbSearch.PageSize = search.PageSize
dbSearch.Order = search.Order
dblist, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
dbSearch.QuestionID = req.QuestionID
dbSearch.Page = req.Page
dbSearch.PageSize = req.PageSize
dbSearch.Order = req.Order
answerOriginalList, count, err := as.answerRepo.SearchList(ctx, &dbSearch)
if err != nil {
return list, count, err
}
AnswerList, err := as.SearchFormatInfo(ctx, dblist, search.LoginUserID, search.IsAdmin)
answerList, err := as.SearchFormatInfo(ctx, answerOriginalList, req)
if err != nil {
return AnswerList, count, err
return answerList, count, err
}
return AnswerList, count, nil
return answerList, count, nil
}
func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.Answer, loginUserID string, isAdmin bool) ([]*schema.AnswerInfo, error) {
func (as *AnswerService) SearchFormatInfo(ctx context.Context, answers []*entity.Answer, req *schema.AnswerListReq) (
[]*schema.AnswerInfo, error) {
list := make([]*schema.AnswerInfo, 0)
objectIds := make([]string, 0)
userIds := make([]string, 0)
for _, dbitem := range dblist {
item := as.ShowFormat(ctx, dbitem)
objectIDs := make([]string, 0)
userIDs := make([]string, 0)
for _, info := range answers {
item := as.ShowFormat(ctx, info)
list = append(list, item)
objectIds = append(objectIds, dbitem.ID)
userIds = append(userIds, dbitem.UserID)
if loginUserID != "" {
// item.VoteStatus = as.activityFunc.GetVoteStatus(ctx, item.TagID, loginUserId)
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, loginUserID)
objectIDs = append(objectIDs, info.ID)
userIDs = append(userIDs, info.UserID)
if req.UserID != "" {
item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, req.UserID)
}
}
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIds)
userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs)
if err != nil {
return list, err
}
@ -469,26 +469,25 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, dblist []*entity.
}
}
if loginUserID == "" {
if req.UserID == "" {
return list, nil
}
CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, objectIds)
searchObjectCollected, err := as.collectionCommon.SearchObjectCollected(ctx, req.UserID, objectIDs)
if err != nil {
log.Error("CollectionFunc.SearchObjectCollected error", err)
return nil, err
}
for _, item := range list {
_, ok := CollectedMap[item.ID]
_, ok := searchObjectCollected[item.ID]
if ok {
item.Collected = true
}
}
for _, item := range list {
item.MemberActions = permission.GetAnswerPermission(ctx, loginUserID, item.UserID, isAdmin)
item.MemberActions = permission.GetAnswerPermission(ctx, req.UserID, item.UserID, req.CanEdit, req.CanDelete)
}
return list, nil
}

View File

@ -12,7 +12,7 @@ import (
"github.com/answerdev/answer/internal/service/activity_queue"
"github.com/answerdev/answer/internal/service/comment_common"
"github.com/answerdev/answer/internal/service/notice_queue"
object_info "github.com/answerdev/answer/internal/service/object_info"
"github.com/answerdev/answer/internal/service/object_info"
"github.com/answerdev/answer/internal/service/permission"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/jinzhu/copier"
@ -122,7 +122,7 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment
resp = &schema.GetCommentResp{}
resp.SetFromComment(comment)
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID)
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID, req.CanEdit, req.CanDelete)
// get reply user info
if len(resp.ReplyUserID) > 0 {
@ -230,7 +230,7 @@ func (cs *CommentService) GetComment(ctx context.Context, req *schema.GetComment
// check if current user vote this comment
resp.IsVote = cs.checkIsVote(ctx, req.UserID, resp.CommentID)
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID)
resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID, req.CanEdit, req.CanDelete)
return resp, nil
}
@ -290,7 +290,7 @@ func (cs *CommentService) GetCommentWithPage(ctx context.Context, req *schema.Ge
// check if current user vote this comment
commentResp.IsVote = cs.checkIsVote(ctx, req.UserID, commentResp.CommentID)
commentResp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, commentResp.UserID)
commentResp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, commentResp.UserID, req.CanEdit, req.CanDelete)
resp = append(resp, commentResp)
}
return pager.NewPageModel(total, resp), nil

View File

@ -6,8 +6,8 @@ import (
"github.com/answerdev/answer/internal/schema"
)
// TODO: There is currently no permission management
func GetCommentPermission(ctx context.Context, userID string, commentCreatorUserID string) (
// GetCommentPermission get comment permission
func GetCommentPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) (
actions []*schema.PermissionMemberAction) {
actions = make([]*schema.PermissionMemberAction, 0)
if len(userID) > 0 {
@ -17,103 +17,105 @@ func GetCommentPermission(ctx context.Context, userID string, commentCreatorUser
Type: "reason",
})
}
if userID != commentCreatorUserID {
return actions
}
actions = append(actions, []*schema.PermissionMemberAction{
{
if canEdit || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "edit",
Name: "Edit",
Type: "edit",
},
{
})
}
if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "delete",
Name: "Delete",
Type: "reason",
},
}...)
})
}
return actions
}
func GetTagPermission(ctx context.Context, userID string, tagCreatorUserID string) (
// GetTagPermission get tag permission
func GetTagPermission(ctx context.Context, canEdit, canDelete bool) (
actions []*schema.PermissionMemberAction) {
if userID != tagCreatorUserID {
return []*schema.PermissionMemberAction{}
}
return []*schema.PermissionMemberAction{
{
if canEdit {
actions = append(actions, &schema.PermissionMemberAction{
Action: "edit",
Name: "Edit",
Type: "edit",
},
{
})
}
if canDelete {
actions = append(actions, &schema.PermissionMemberAction{
Action: "delete",
Name: "Delete",
Type: "reason",
},
})
}
return actions
}
func GetAnswerPermission(ctx context.Context, userID string, answerAuthID string, isAdmin bool) (
// GetAnswerPermission get answer permission
func GetAnswerPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete bool) (
actions []*schema.PermissionMemberAction) {
actions = make([]*schema.PermissionMemberAction, 0)
if !isAdmin {
if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{
Action: "report",
Name: "Flag",
Type: "reason",
})
}
if userID != answerAuthID {
return actions
}
if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{
Action: "report",
Name: "Flag",
Type: "reason",
})
}
actions = append(actions, []*schema.PermissionMemberAction{
{
if canEdit || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "edit",
Name: "Edit",
Type: "edit",
},
{
})
}
if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "delete",
Name: "Delete",
Type: "confirm",
},
}...)
})
}
return actions
}
func GetQuestionPermission(ctx context.Context, userID string, questionAuthID string, isAdmin bool) (actions []*schema.PermissionMemberAction) {
// GetQuestionPermission get question permission
func GetQuestionPermission(ctx context.Context, userID string, creatorUserID string, canEdit, canDelete, canClose bool) (
actions []*schema.PermissionMemberAction) {
actions = make([]*schema.PermissionMemberAction, 0)
if !isAdmin {
if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{
Action: "report",
Name: "Flag",
Type: "reason",
})
}
if userID != questionAuthID {
return actions
}
if len(userID) > 0 {
actions = append(actions, &schema.PermissionMemberAction{
Action: "report",
Name: "Flag",
Type: "reason",
})
}
actions = append(actions, []*schema.PermissionMemberAction{
{
if canEdit || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "edit",
Name: "Edit",
Type: "edit",
},
{
})
}
if canClose {
actions = append(actions, &schema.PermissionMemberAction{
Action: "close",
Name: "Close",
Type: "confirm",
},
{
})
}
if canDelete || userID == creatorUserID {
actions = append(actions, &schema.PermissionMemberAction{
Action: "delete",
Name: "Delete",
Type: "confirm",
},
}...)
})
}
return actions
}

View File

@ -197,7 +197,7 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
RevisionID: revisionID,
})
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, false, false)
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
return
}
@ -391,27 +391,33 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
})
}
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, false, false)
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
return
}
// GetQuestion get question one
func (qs *QuestionService) GetQuestion(ctx context.Context, id, loginUserID string, addpv bool, isAdmin bool) (resp *schema.QuestionInfo, err error) {
question, err := qs.questioncommon.Info(ctx, id, loginUserID)
func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID string,
per *schema.QuestionPermission) (resp *schema.QuestionInfo, err error) {
question, err := qs.questioncommon.Info(ctx, questionID, userID)
if err != nil {
return
}
if addpv {
err = qs.questioncommon.UpdataPv(ctx, id)
if err != nil {
log.Error("UpdataPv", err)
}
}
question.MemberActions = permission.GetQuestionPermission(ctx, loginUserID, question.UserID, isAdmin)
question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID,
per.CanEdit, per.CanDelete, per.CanClose)
return question, nil
}
// GetQuestionAndAddPV get question one
func (qs *QuestionService) GetQuestionAndAddPV(ctx context.Context, questionID, loginUserID string,
per *schema.QuestionPermission) (
resp *schema.QuestionInfo, err error) {
err = qs.questioncommon.UpdataPv(ctx, questionID)
if err != nil {
log.Error(err)
}
return qs.GetQuestion(ctx, questionID, loginUserID, per)
}
func (qs *QuestionService) ChangeTag(ctx context.Context, objectTagData *schema.TagChange) error {
return qs.tagCommon.ObjectChangeTag(ctx, objectTagData)
}
@ -635,12 +641,12 @@ func (qs *QuestionService) SearchByTitleLike(ctx context.Context, title string,
// SimilarQuestion
func (qs *QuestionService) SimilarQuestion(ctx context.Context, questionID string, loginUserID string) ([]*schema.QuestionInfo, int64, error) {
list := make([]*schema.QuestionInfo, 0)
questionInfo, err := qs.GetQuestion(ctx, questionID, loginUserID, false, false)
question, err := qs.questioncommon.Info(ctx, questionID, loginUserID)
if err != nil {
return list, 0, err
return list, 0, nil
}
tagNames := make([]string, 0, len(questionInfo.Tags))
for _, tag := range questionInfo.Tags {
tagNames := make([]string, 0, len(question.Tags))
for _, tag := range question.Tags {
tagNames = append(tagNames, tag.SlugName)
}
search := &schema.QuestionSearch{}

View File

@ -106,7 +106,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq)
resp.Recommend = tagInfo.Recommend
resp.Reserved = tagInfo.Reserved
resp.IsFollower = ts.checkTagIsFollow(ctx, req.UserID, tagInfo.ID)
resp.MemberActions = permission.GetTagPermission(ctx, req.UserID, req.UserID)
resp.MemberActions = permission.GetTagPermission(ctx, req.CanEdit, req.CanDelete)
resp.GetExcerpt()
return resp, nil
}