Commit 193870b2f938278b27a2d8347bd7e4db5f8f9dfc (PR #3781, fixing CVE-2026-69115) added authverify.CheckAdmin(ctx) to GetPaginationUsers, GetAllUserID, and GetGroups. Verification of sibling RPC handlers in internal/rpc/third/ and internal/rpc/user/ identified four handlers that still omit authverify.CheckAccess or authverify.CheckAdmin: FcmUpdateToken, SetGlobalRecvMessageOpt, UserRegisterCount, and SortQuery.
1. POST /third/fcm_update_token -> thirdServer.FcmUpdateToken (internal/rpc/third/third.go:124-130): Unlike its immediate sibling SetAppBadge (lines 132-135, which calls authverify.CheckAccess(ctx, req.FromUserID)), FcmUpdateToken does not call authverify.CheckAccess(ctx, req.Account) before writing req.FcmToken into Redis under req.Account. Because internal/push/offlinepush/fcm/push.go reads GetFcmToken(ctx, account, platformID) when delivering offline push notifications, any authenticated user can bind their own FCM token to an arbitrary userID and intercept offline push notifications.
2. POST /user/set_global_msg_recv_opt -> userServer.SetGlobalRecvMessageOpt (internal/rpc/user/user.go:229-241): Unlike UpdateUserInfo (line 161) and UpdateUserInfoEx (line 188), SetGlobalRecvMessageOpt does not call authverify.CheckAccess(ctx, req.UserID), allowing any authenticated user to modify global_recv_msg_opt for any target userID.
3. POST /statistics/user/register -> userServer.UserRegisterCount (internal/rpc/user/statistics.go:25-43): Unlike all three sibling routes in statisticsGroup (GetActiveUser, GroupCreateCount, and GetActiveGroup), UserRegisterCount omits authverify.CheckAdmin(ctx).
4. userServer.SortQuery (internal/rpc/user/user.go:721-727): The gRPC handler SortQuery queries sorted user records across the database without checking authverify.CheckAdmin(ctx).
Enforce authverify.CheckAccess(ctx, req.Account) in FcmUpdateToken, authverify.CheckAccess(ctx, req.UserID) in SetGlobalRecvMessageOpt, and authverify.CheckAdmin(ctx) in UserRegisterCount and SortQuery.
--- a/internal/rpc/third/third.go
+++ b/internal/rpc/third/third.go
@@ -122,6 +122,9 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
}
func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (*third.FcmUpdateTokenResp, error) {
+ if err := authverify.CheckAccess(ctx, req.Account); err != nil {
+ return nil, err
+ }
err := t.thirdDatabase.SetFcmToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime)
--- a/internal/rpc/user/user.go
+++ b/internal/rpc/user/user.go
@@ -227,6 +227,9 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
}
func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (*pbuser.SetGlobalRecvMessageOptResp, error) {
+ if err := authverify.CheckAccess(ctx, req.UserID); err != nil {
+ return nil, err
+ }
if _, err := s.db.GetUserByID(ctx, req.UserID); err != nil {
@@ -719,6 +722,9 @@ func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.Get
}
func (s *userServer) SortQuery(ctx context.Context, req *pbuser.SortQueryReq) (*pbuser.SortQueryResp, error) {
+ if err := authverify.CheckAdmin(ctx); err != nil {
+ return nil, err
+ }
users, err := s.db.SortQuery(ctx, req.UserIdName, req.Asc)
--- a/internal/rpc/user/statistics.go
+++ b/internal/rpc/user/statistics.go
@@ -23,6 +23,9 @@ import (
)
func (s *userServer) UserRegisterCount(ctx context.Context, req *pbuser.UserRegisterCountReq) (*pbuser.UserRegisterCountResp, error) {
+ if err := authverify.CheckAdmin(ctx); err != nil {
+ return nil, err
+ }
if req.Start <= 0 || req.End <= 0 {