From b788480eb61e9fac3c8dde4ab98052bfc7c092b4 Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Sat, 16 Sep 2023 23:07:32 -0700 Subject: [PATCH] Tighten up user blocking in Notifications & Comments The following bugs are resolved: * A blocked user comments on a Photo that you have also commented on (are subscribed to), and you would be notified about their comment. * A blocked user comments on a Forum Thread that you are subscribed to, and you would be notified about their post. * Comments by blocked users (on photos and forum threads) were visible to you after you have blocked them. --- pkg/controller/api/likes.go | 26 +++++++++++++++++++++- pkg/controller/comment/post_comment.go | 4 ++-- pkg/controller/forum/new_post.go | 4 ++-- pkg/controller/photo/view.go | 2 +- pkg/models/blocklist.go | 30 ++++++++++++++++++++++++++ pkg/models/comment.go | 20 ++++++++++------- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/pkg/controller/api/likes.go b/pkg/controller/api/likes.go index dc38019..b83f153 100644 --- a/pkg/controller/api/likes.go +++ b/pkg/controller/api/likes.go @@ -81,6 +81,14 @@ func Likes() http.HandlerFunc { return } } + + // Blocking safety check: if either user blocks the other, liking is not allowed. + if models.IsBlocking(currentUser.ID, user.ID) { + SendJSON(w, http.StatusForbidden, Response{ + Error: "You are not allowed to like that photo.", + }) + return + } targetUser = user } } else { @@ -91,15 +99,31 @@ func Likes() http.HandlerFunc { if user, err := models.GetUser(req.TableID); err == nil { targetUser = user log.Warn("found user %s", targetUser.Username) + + // Blocking safety check: if either user blocks the other, liking is not allowed. + if models.IsBlocking(currentUser.ID, user.ID) { + SendJSON(w, http.StatusForbidden, Response{ + Error: "You are not allowed to like that profile.", + }) + return + } } else { log.Error("For like on users table: didn't find user %d: %s", req.TableID, err) } case "comments": - log.Error("subject is users, find %d", req.TableID) + log.Error("subject is comments, find %d", req.TableID) if comment, err := models.GetComment(req.TableID); err == nil { targetUser = &comment.User notificationMessage = comment.Message log.Warn("found user %s", targetUser.Username) + + // Blocking safety check: if either user blocks the other, liking is not allowed. + if models.IsBlocking(currentUser.ID, targetUser.ID) { + SendJSON(w, http.StatusForbidden, Response{ + Error: "You are not allowed to like that comment.", + }) + return + } } else { log.Error("For like on users table: didn't find user %d: %s", req.TableID, err) } diff --git a/pkg/controller/comment/post_comment.go b/pkg/controller/comment/post_comment.go index 8088b92..fd8eb2a 100644 --- a/pkg/controller/comment/post_comment.go +++ b/pkg/controller/comment/post_comment.go @@ -180,8 +180,8 @@ func PostComment() http.HandlerFunc { } } - // Notify subscribers to this comment thread. - for _, userID := range models.GetSubscribers(comment.TableName, comment.TableID) { + // Notify subscribers to this comment thread (filter the subscribers by the blocking status of the current user). + for _, userID := range models.FilterBlockingUserIDs(currentUser, models.GetSubscribers(comment.TableName, comment.TableID)) { if notifyUser != nil && userID == notifyUser.ID { // Don't notify the recipient twice. continue diff --git a/pkg/controller/forum/new_post.go b/pkg/controller/forum/new_post.go index 6d529b7..f3fabda 100644 --- a/pkg/controller/forum/new_post.go +++ b/pkg/controller/forum/new_post.go @@ -345,8 +345,8 @@ func NewPost() http.HandlerFunc { queryString = fmt.Sprintf("?page=%d", lastPage) } - // Notify watchers about this new post. - for _, userID := range models.GetSubscribers("threads", thread.ID) { + // Notify watchers about this new post. Filter by blocked user IDs. + for _, userID := range models.FilterBlockingUserIDs(currentUser, models.GetSubscribers("threads", thread.ID)) { if userID == currentUser.ID { continue } diff --git a/pkg/controller/photo/view.go b/pkg/controller/photo/view.go index cea7308..ede0a05 100644 --- a/pkg/controller/photo/view.go +++ b/pkg/controller/photo/view.go @@ -84,7 +84,7 @@ func View() http.HandlerFunc { commentMap := models.MapCommentCounts("photos", []uint64{photo.ID}) // Get all the comments. - comments, err := models.ListComments("photos", photo.ID) + comments, err := models.ListComments(currentUser, "photos", photo.ID) if err != nil { log.Error("Couldn't list comments for photo %d: %s", photo.ID, err) } diff --git a/pkg/models/blocklist.go b/pkg/models/blocklist.go index 92f82c2..860b740 100644 --- a/pkg/models/blocklist.go +++ b/pkg/models/blocklist.go @@ -109,6 +109,36 @@ func BlockedUserIDs(userId uint64) []uint64 { return userIDs } +// MapBlockedUserIDs returns BlockedUserIDs as a lookup hashmap (not for front-end templates currently). +func MapBlockedUserIDs(userId uint64) map[uint64]interface{} { + var ( + result = map[uint64]interface{}{} + blockedIDs = BlockedUserIDs(userId) + ) + for _, uid := range blockedIDs { + result[uid] = nil + } + return result +} + +// FilterBlockingUserIDs narrows down a set of User IDs to remove ones that block (or are blocked by) the current user. +func FilterBlockingUserIDs(currentUser *User, userIDs []uint64) []uint64 { + var ( + // Get the IDs to exclude. + blockedIDs = MapBlockedUserIDs(currentUser.ID) + + // Filter the result. + result = []uint64{} + ) + for _, uid := range userIDs { + if _, ok := blockedIDs[uid]; ok { + continue + } + result = append(result, uid) + } + return result +} + // BlockedUserIDsByUser returns all user IDs blocked by the user (one directional only) func BlockedUserIDsByUser(userId uint64) []uint64 { var ( diff --git a/pkg/models/comment.go b/pkg/models/comment.go index 2fd6dfd..2319357 100644 --- a/pkg/models/comment.go +++ b/pkg/models/comment.go @@ -68,13 +68,14 @@ func AddComment(user *User, tableName string, tableID uint64, message string) (* // PaginateComments provides a page of comments on something. func PaginateComments(user *User, tableName string, tableID uint64, pager *Pagination) ([]*Comment, error) { var ( - cs = []*Comment{} - query = (&Comment{}).Preload() + cs = []*Comment{} + query = (&Comment{}).Preload() + blockedUserIDs = BlockedUserIDs(user.ID) ) query = query.Where( - "table_name = ? AND table_id = ?", - tableName, tableID, + "table_name = ? AND table_id = ? AND user_id NOT IN ?", + tableName, tableID, blockedUserIDs, ).Order(pager.Sort) query.Model(&Comment{}).Count(&pager.Total) @@ -87,11 +88,14 @@ func PaginateComments(user *User, tableName string, tableID uint64, pager *Pagin } // ListComments returns a complete set of comments without paging. -func ListComments(tableName string, tableID uint64) ([]*Comment, error) { - var cs []*Comment +func ListComments(user *User, tableName string, tableID uint64) ([]*Comment, error) { + var ( + cs []*Comment + blockedUserIDs = BlockedUserIDs(user.ID) + ) result := (&Comment{}).Preload().Where( - "table_name = ? AND table_id = ?", - tableName, tableID, + "table_name = ? AND table_id = ? AND user_id NOT IN ?", + tableName, tableID, blockedUserIDs, ).Order("created_at asc").Find(&cs) return cs, result.Error }