Skip to content

Commit 659d1f6

Browse files
committed
Merge remote-tracking branch 'origin/fix/2.0.1/chat' into test
2 parents 7952075 + 869b040 commit 659d1f6

File tree

10 files changed

+236
-17
lines changed

10 files changed

+236
-17
lines changed

internal/base/middleware/auth.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,22 @@ func (am *AuthUserMiddleware) AdminAuth() gin.HandlerFunc {
184184
return
185185
}
186186
if userInfo != nil {
187+
if userInfo.EmailStatus == entity.EmailStatusToBeVerified {
188+
_ = am.authService.RemoveAdminUserCacheInfo(ctx, token)
189+
handler.HandleResponse(ctx, errors.Forbidden(reason.EmailNeedToBeVerified),
190+
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeInactive})
191+
ctx.Abort()
192+
return
193+
}
194+
if userInfo.UserStatus == entity.UserStatusSuspended {
195+
_ = am.authService.RemoveAdminUserCacheInfo(ctx, token)
196+
handler.HandleResponse(ctx, errors.Forbidden(reason.UserSuspended),
197+
&schema.ForbiddenResp{Type: schema.ForbiddenReasonTypeUserSuspended})
198+
ctx.Abort()
199+
return
200+
}
187201
if userInfo.UserStatus == entity.UserStatusDeleted {
202+
_ = am.authService.RemoveAdminUserCacheInfo(ctx, token)
188203
handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
189204
ctx.Abort()
190205
return

internal/controller/activity_controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func (ac *ActivityController) GetObjectTimeline(ctx *gin.Context) {
5858
req.ObjectID = uid.DeShortID(req.ObjectID)
5959

6060
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
61+
req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
6162
if userInfo := middleware.GetUserInfoFromContext(ctx); userInfo != nil {
6263
req.IsAdmin = userInfo.RoleID == role.RoleAdminID
6364
}
@@ -81,6 +82,7 @@ func (ac *ActivityController) GetObjectTimelineDetail(ctx *gin.Context) {
8182
}
8283

8384
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
85+
req.IsAdminModerator = middleware.GetUserIsAdminModerator(ctx)
8486

8587
resp, err := ac.activityService.GetObjectTimelineDetail(ctx, req)
8688
handler.HandleResponse(ctx, err, resp)

internal/schema/activity.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ type ActivityMsg struct {
3434

3535
// GetObjectTimelineReq get object timeline request
3636
type GetObjectTimelineReq struct {
37-
ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"`
38-
ShowVote bool `validate:"omitempty" form:"show_vote"`
39-
UserID string `json:"-"`
40-
IsAdmin bool `json:"-"`
37+
ObjectID string `validate:"omitempty,gt=0,lte=100" form:"object_id"`
38+
ShowVote bool `validate:"omitempty" form:"show_vote"`
39+
UserID string `json:"-"`
40+
IsAdmin bool `json:"-"`
41+
IsAdminModerator bool `json:"-"`
4142
}
4243

4344
// GetObjectTimelineResp get object timeline response
@@ -73,9 +74,10 @@ type ActObjectInfo struct {
7374

7475
// GetObjectTimelineDetailReq get object timeline detail request
7576
type GetObjectTimelineDetailReq struct {
76-
NewRevisionID string `validate:"required,gt=0,lte=100" form:"new_revision_id"`
77-
OldRevisionID string `validate:"required,gt=0,lte=100" form:"old_revision_id"`
78-
UserID string `json:"-"`
77+
NewRevisionID string `validate:"required,gt=0,lte=100" form:"new_revision_id"`
78+
OldRevisionID string `validate:"required,gt=0,lte=100" form:"old_revision_id"`
79+
UserID string `json:"-"`
80+
IsAdminModerator bool `json:"-"`
7981
}
8082

8183
// GetObjectTimelineDetailResp get object timeline detail response

internal/schema/simple_obj_info_schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type SimpleObjectInfo struct {
3030
ObjectCreatorUserID string `json:"object_creator_user_id"`
3131
QuestionID string `json:"question_id"`
3232
QuestionStatus int `json:"question_status"`
33+
QuestionShow int `json:"question_show"`
3334
AnswerID string `json:"answer_id"`
3435
AnswerStatus int `json:"answer_status"`
3536
CommentID string `json:"comment_id"`

internal/service/activity/activity.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
9193
func (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
255261
func (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
264372
func (as *ActivityService) getOneObjectDetail(ctx context.Context, revisionID string) (
265373
resp *schema.ObjectTimelineDetail, err error) {

internal/service/auth/auth.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,39 @@ func (as *AuthService) RemoveTokensExceptCurrentUser(ctx context.Context, userID
145145
// Admin
146146

147147
func (as *AuthService) GetAdminUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) {
148-
return as.authRepo.GetAdminUserCacheInfo(ctx, accessToken)
148+
adminCacheInfo, err := as.authRepo.GetAdminUserCacheInfo(ctx, accessToken)
149+
if err != nil {
150+
return nil, err
151+
}
152+
if adminCacheInfo == nil {
153+
return nil, nil
154+
}
155+
156+
// Keep admin authorization aligned with user-token lifecycle and status refresh.
157+
refreshedUserCacheInfo, err := as.GetUserCacheInfo(ctx, accessToken)
158+
if err != nil {
159+
return nil, err
160+
}
161+
if refreshedUserCacheInfo == nil {
162+
if err = as.authRepo.RemoveAdminUserCacheInfo(ctx, accessToken); err != nil {
163+
return nil, err
164+
}
165+
return nil, nil
166+
}
167+
168+
adminCacheInfo.UserStatus = refreshedUserCacheInfo.UserStatus
169+
adminCacheInfo.EmailStatus = refreshedUserCacheInfo.EmailStatus
170+
if refreshedUserCacheInfo.RoleID > 0 {
171+
adminCacheInfo.RoleID = refreshedUserCacheInfo.RoleID
172+
}
173+
if len(refreshedUserCacheInfo.ExternalID) > 0 {
174+
adminCacheInfo.ExternalID = refreshedUserCacheInfo.ExternalID
175+
}
176+
177+
if err = as.authRepo.SetAdminUserCacheInfo(ctx, accessToken, adminCacheInfo); err != nil {
178+
return nil, err
179+
}
180+
return adminCacheInfo, nil
149181
}
150182

151183
func (as *AuthService) SetAdminUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) (err error) {

internal/service/object_info/object_info.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
204204
ObjectCreatorUserID: questionInfo.UserID,
205205
QuestionID: questionInfo.ID,
206206
QuestionStatus: questionInfo.Status,
207+
QuestionShow: questionInfo.Show,
207208
ObjectType: objectType,
208209
Title: questionInfo.Title,
209210
Content: questionInfo.ParsedText, // todo trim
@@ -228,6 +229,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
228229
ObjectCreatorUserID: answerInfo.UserID,
229230
QuestionID: answerInfo.QuestionID,
230231
QuestionStatus: questionInfo.Status,
232+
QuestionShow: questionInfo.Show,
231233
AnswerStatus: answerInfo.Status,
232234
AnswerID: answerInfo.ID,
233235
ObjectType: objectType,
@@ -258,6 +260,7 @@ func (os *ObjService) GetInfo(ctx context.Context, objectID string) (objInfo *sc
258260
if exist {
259261
objInfo.QuestionID = questionInfo.ID
260262
objInfo.QuestionStatus = questionInfo.Status
263+
objInfo.QuestionShow = questionInfo.Show
261264
objInfo.Title = questionInfo.Title
262265
}
263266
answerInfo, exist, err := os.answerRepo.GetAnswer(ctx, commentInfo.ObjectID)

0 commit comments

Comments
 (0)