mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
fix(api): 🔒 Correctly check for note ownership when editing
This commit is contained in:
parent
653cf712ea
commit
9682cd0f99
|
|
@ -73,7 +73,7 @@ export default apiRoute((app) =>
|
||||||
|
|
||||||
const note = await Note.fromId(id, user?.id);
|
const note = await Note.fromId(id, user?.id);
|
||||||
|
|
||||||
if (!note?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,9 +80,9 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = await Note.fromId(id, user?.id);
|
const note = await Note.fromId(id, user?.id);
|
||||||
|
|
||||||
if (!status?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ export default apiRoute((app) =>
|
||||||
max_id ? lt(Users.id, max_id) : undefined,
|
max_id ? lt(Users.id, max_id) : undefined,
|
||||||
since_id ? gte(Users.id, since_id) : undefined,
|
since_id ? gte(Users.id, since_id) : undefined,
|
||||||
min_id ? gt(Users.id, min_id) : undefined,
|
min_id ? gt(Users.id, min_id) : undefined,
|
||||||
sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${status.id} AND "Likes"."likerId" = ${Users.id})`,
|
sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${note.id} AND "Likes"."likerId" = ${Users.id})`,
|
||||||
),
|
),
|
||||||
limit,
|
limit,
|
||||||
context.req.url,
|
context.req.url,
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ export default apiRoute((app) => {
|
||||||
|
|
||||||
const note = await Note.fromId(id, user?.id);
|
const note = await Note.fromId(id, user?.id);
|
||||||
|
|
||||||
if (!note?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ export default apiRoute((app) => {
|
||||||
|
|
||||||
const note = await Note.fromId(id, user?.id);
|
const note = await Note.fromId(id, user?.id);
|
||||||
|
|
||||||
if (!note?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ export default apiRoute((app) => {
|
||||||
|
|
||||||
const note = await Note.fromId(id, user?.id);
|
const note = await Note.fromId(id, user?.id);
|
||||||
|
|
||||||
if (!note?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,17 +104,14 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundStatus = await Note.fromId(id, user.id);
|
const note = await Note.fromId(id, user.id);
|
||||||
|
|
||||||
if (!foundStatus?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingReblog = await Note.fromSql(
|
const existingReblog = await Note.fromSql(
|
||||||
and(
|
and(eq(Notes.authorId, user.id), eq(Notes.reblogId, note.data.id)),
|
||||||
eq(Notes.authorId, user.id),
|
|
||||||
eq(Notes.reblogId, foundStatus.data.id),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingReblog) {
|
if (existingReblog) {
|
||||||
|
|
@ -123,7 +120,7 @@ export default apiRoute((app) =>
|
||||||
|
|
||||||
const newReblog = await Note.insert({
|
const newReblog = await Note.insert({
|
||||||
authorId: user.id,
|
authorId: user.id,
|
||||||
reblogId: foundStatus.data.id,
|
reblogId: note.data.id,
|
||||||
visibility,
|
visibility,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
|
|
@ -140,10 +137,10 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Failed to reblog" }, 500);
|
return context.json({ error: "Failed to reblog" }, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundStatus.author.isLocal() && user.isLocal()) {
|
if (note.author.isLocal() && user.isLocal()) {
|
||||||
await Notification.insert({
|
await Notification.insert({
|
||||||
accountId: user.id,
|
accountId: user.id,
|
||||||
notifiedId: foundStatus.author.id,
|
notifiedId: note.author.id,
|
||||||
type: "reblog",
|
type: "reblog",
|
||||||
noteId: newReblog.data.reblogId,
|
noteId: newReblog.data.reblogId,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,9 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = await Note.fromId(id, user.id);
|
const note = await Note.fromId(id, user.id);
|
||||||
|
|
||||||
if (!status?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ export default apiRoute((app) =>
|
||||||
max_id ? lt(Users.id, max_id) : undefined,
|
max_id ? lt(Users.id, max_id) : undefined,
|
||||||
since_id ? gte(Users.id, since_id) : undefined,
|
since_id ? gte(Users.id, since_id) : undefined,
|
||||||
min_id ? gt(Users.id, min_id) : undefined,
|
min_id ? gt(Users.id, min_id) : undefined,
|
||||||
sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."reblogId" = ${status.id} AND "Notes"."authorId" = ${Users.id})`,
|
sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."reblogId" = ${note.id} AND "Notes"."authorId" = ${Users.id})`,
|
||||||
),
|
),
|
||||||
limit,
|
limit,
|
||||||
context.req.url,
|
context.req.url,
|
||||||
|
|
|
||||||
|
|
@ -75,18 +75,18 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const status = await Note.fromId(id, user.id);
|
const note = await Note.fromId(id, user.id);
|
||||||
|
|
||||||
if (!status?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.json(
|
return context.json(
|
||||||
{
|
{
|
||||||
id: status.id,
|
id: note.id,
|
||||||
// TODO: Give real source for spoilerText
|
// TODO: Give real source for spoilerText
|
||||||
spoiler_text: status.data.spoilerText,
|
spoiler_text: note.data.spoilerText,
|
||||||
text: status.data.contentSource,
|
text: note.data.contentSource,
|
||||||
} satisfies ApiStatusSource,
|
} satisfies ApiStatusSource,
|
||||||
200,
|
200,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export default apiRoute((app) =>
|
||||||
|
|
||||||
const note = await Note.fromId(id, user.id);
|
const note = await Note.fromId(id, user.id);
|
||||||
|
|
||||||
if (!note?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,18 +79,15 @@ export default apiRoute((app) =>
|
||||||
return context.json({ error: "Unauthorized" }, 401);
|
return context.json({ error: "Unauthorized" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundStatus = await Note.fromId(id, user.id);
|
const note = await Note.fromId(id, user.id);
|
||||||
|
|
||||||
// Check if user is authorized to view this status (if it's private)
|
// Check if user is authorized to view this status (if it's private)
|
||||||
if (!foundStatus?.isViewableByUser(user)) {
|
if (!(note && (await note?.isViewableByUser(user)))) {
|
||||||
return context.json({ error: "Record not found" }, 404);
|
return context.json({ error: "Record not found" }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingReblog = await Note.fromSql(
|
const existingReblog = await Note.fromSql(
|
||||||
and(
|
and(eq(Notes.authorId, user.id), eq(Notes.reblogId, note.data.id)),
|
||||||
eq(Notes.authorId, user.id),
|
|
||||||
eq(Notes.reblogId, foundStatus.data.id),
|
|
||||||
),
|
|
||||||
undefined,
|
undefined,
|
||||||
user?.id,
|
user?.id,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export default apiRoute((app) =>
|
||||||
foundAuthor = foundObject ? foundObject.author : null;
|
foundAuthor = foundObject ? foundObject.author : null;
|
||||||
|
|
||||||
if (foundObject) {
|
if (foundObject) {
|
||||||
if (!foundObject.isViewableByUser(null)) {
|
if (!(await foundObject.isViewableByUser(null))) {
|
||||||
return context.json({ error: "Object not found" }, 404);
|
return context.json({ error: "Object not found" }, 404);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1099,8 +1099,13 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter for posts that are viewable by the user
|
// Filter for posts that are viewable by the user
|
||||||
const viewableAncestors = ancestors.filter((ancestor) =>
|
const viewableAncestors = await Promise.all(
|
||||||
ancestor.isViewableByUser(fetcher),
|
ancestors.map(async (ancestor) => {
|
||||||
|
const isViewable = await ancestor.isViewableByUser(fetcher);
|
||||||
|
return isViewable ? ancestor : null;
|
||||||
|
}),
|
||||||
|
).then((filteredAncestors) =>
|
||||||
|
filteredAncestors.filter((n) => n !== null),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reverse the order so that the oldest posts are first
|
// Reverse the order so that the oldest posts are first
|
||||||
|
|
@ -1133,8 +1138,13 @@ export class Note extends BaseInterface<typeof Notes, NoteTypeWithRelations> {
|
||||||
|
|
||||||
// Filter for posts that are viewable by the user
|
// Filter for posts that are viewable by the user
|
||||||
|
|
||||||
const viewableDescendants = descendants.filter((descendant) =>
|
const viewableDescendants = await Promise.all(
|
||||||
descendant.isViewableByUser(fetcher),
|
descendants.map(async (descendant) => {
|
||||||
|
const isViewable = await descendant.isViewableByUser(fetcher);
|
||||||
|
return isViewable ? descendant : null;
|
||||||
|
}),
|
||||||
|
).then((filteredDescendants) =>
|
||||||
|
filteredDescendants.filter((n) => n !== null),
|
||||||
);
|
);
|
||||||
|
|
||||||
return viewableDescendants;
|
return viewableDescendants;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue