[backend/masto-api] Enforce renote/reply visibility for notifications

This commit is contained in:
Laura Hausmann 2024-02-22 20:42:47 +01:00
parent 4d7d8ee34e
commit b4fea308f7
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 46 additions and 12 deletions

View file

@ -50,6 +50,7 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
.FilterBlocked(p => p.Notifier, user)
.FilterBlocked(p => p.Note, user)
.Paginate(query, ControllerContext)
.PrecomputeNoteVisibilities(user)
.RenderAllForMastodonAsync(notificationRenderer, user);
//TODO: handle mutes
@ -69,12 +70,13 @@ public class NotificationController(DatabaseContext db, NotificationRenderer not
.IncludeCommonProperties()
.Where(p => p.Notifiee == user && p.Id == id)
.EnsureNoteVisibilityFor(p => p.Note, user)
.PrecomputeNoteVisibilities(user)
.FirstOrDefaultAsync() ??
throw GracefulException.RecordNotFound();
//TODO: handle reply/renote visibility
var res = await notificationRenderer.RenderAsync(notification, user);
var res = await notificationRenderer.RenderAsync(notification.EnforceRenoteReplyVisibility(p => p.Note), user);
return Ok(res);
}

View file

@ -18,19 +18,14 @@ public class NotificationRenderer(NoteRenderer noteRenderer, UserRenderer userRe
? notification.Note?.Renote
: notification.Note;
if (notification.Note != null && targetNote == null)
throw new Exception("targetNote must not be null at this stage");
var note = notification.Note != null
? statuses?.FirstOrDefault(p => p.Id == targetNote!.Id) ??
await noteRenderer.RenderAsync(targetNote!, user, accounts)
var note = targetNote != null
? statuses?.FirstOrDefault(p => p.Id == targetNote.Id) ??
await noteRenderer.RenderAsync(targetNote, user, accounts)
: null;
var notifier = accounts?.FirstOrDefault(p => p.Id == dbNotifier.Id) ??
await userRenderer.RenderAsync(dbNotifier);
//TODO: specially handle quotes
var res = new NotificationEntity
{
Id = notification.Id,

View file

@ -125,4 +125,10 @@ public class Notification : IEntity
[Column("id")]
[StringLength(32)]
public string Id { get; set; } = null!;
public Notification WithPrecomputedNoteVisibilities(bool reply, bool renote)
{
Note = Note?.WithPrecomputedVisibilities(reply, renote);
return this;
}
}

View file

@ -176,6 +176,16 @@ public static class QueryableExtensions
p.Renote.IsVisibleFor(user)));
}
public static IQueryable<Notification> PrecomputeNoteVisibilities(this IQueryable<Notification> query, User user)
{
return query.Select(p => p.WithPrecomputedNoteVisibilities(p.Note != null &&
p.Note.Reply != null &&
p.Note.Reply.IsVisibleFor(user),
p.Note != null &&
p.Note.Renote != null &&
p.Note.Renote.IsVisibleFor(user)));
}
public static IQueryable<User> PrecomputeRelationshipData(this IQueryable<User> query, User user)
{
return query.Select(p => p.WithPrecomputedBlockStatus(p.IsBlocking(user), p.IsBlockedBy(user))
@ -248,6 +258,25 @@ public static class QueryableExtensions
return list.Select(EnforceRenoteReplyVisibility);
}
public static T EnforceRenoteReplyVisibility<T>(this T source, Expression<Func<T, Note?>> predicate)
{
var note = predicate.Compile().Invoke(source);
if (note == null) return source;
if (!note.PrecomputedIsReplyVisible ?? false)
note.Reply = null;
if (!note.PrecomputedIsRenoteVisible ?? false)
note.Renote = null;
return source;
}
public static IEnumerable<T> EnforceRenoteReplyVisibility<T>(
this IEnumerable<T> list, Expression<Func<T, Note?>> predicate
)
{
return list.Select(p => EnforceRenoteReplyVisibility(p, predicate));
}
public static async Task<List<StatusEntity>> RenderAllForMastodonAsync(
this IQueryable<Note> notes, NoteRenderer renderer, User? user
)
@ -270,7 +299,9 @@ public static class QueryableExtensions
this IQueryable<Notification> notifications, NotificationRenderer renderer, User user
)
{
var list = await notifications.ToListAsync();
var list = (await notifications.ToListAsync())
.EnforceRenoteReplyVisibility(p => p.Note)
.ToList();
return (await renderer.RenderManyAsync(list, user)).ToList();
}