Allow auto banning ips

This commit is contained in:
Ajay 2023-01-28 14:00:12 -05:00
parent 5426ae826e
commit d5c544f1ee
2 changed files with 164 additions and 84 deletions

View file

@ -17,6 +17,10 @@ export async function shadowBanUser(req: Request, res: Response): Promise<Respon
const enabled = req.query.enabled === undefined
? true
: req.query.enabled === "true";
const lookForIPs = req.query.lookForIPs === "true";
const banUsers = req.query.banUsers === undefined
? true
: req.query.banUsers === "true";
//if enabled is false and the old submissions should be made visible again
const unHideOldSubmissions = req.query.unHideOldSubmissions !== "false";
@ -38,93 +42,118 @@ export async function shadowBanUser(req: Request, res: Response): Promise<Respon
}
if (userID) {
//check to see if this user is already shadowbanned
const row = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
const result = await banUser(userID, enabled, unHideOldSubmissions, type, categories);
if (result) {
res.sendStatus(result);
}
if (enabled && row.userCount == 0) {
//add them to the shadow ban list
if (enabled && lookForIPs) {
const ipLoggingFixedTime = 1674590916069;
const timeSubmitted = (await db.prepare("all", `SELECT "timeSubmitted" FROM "sponsorTimes" WHERE "timeSubmitted" > ? AND "userID" = ?`, [ipLoggingFixedTime, userID])) as { timeSubmitted: number }[];
const ips = (await Promise.all(timeSubmitted.map((s) => {
return privateDB.prepare("all", `SELECT "hashedIP" FROM "sponsorTimes" WHERE "timeSubmitted" = ?`, [s.timeSubmitted]) as Promise<{ hashedIP: HashedIP }[]>;
}))).flat();
//add it to the table
await db.prepare("run", `INSERT INTO "shadowBannedUsers" VALUES(?)`, [userID]);
//find all previous submissions and hide them
if (unHideOldSubmissions) {
await unHideSubmissionsByUser(categories, userID, type);
}
} else if (!enabled && row.userCount > 0) {
//remove them from the shadow ban list
await db.prepare("run", `DELETE FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
//find all previous submissions and unhide them
if (unHideOldSubmissions) {
const segmentsToIgnore = (await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" st
JOIN "lockCategories" ns on "st"."videoID" = "ns"."videoID" AND st.category = ns.category AND "st"."service" = "ns"."service" WHERE "st"."userID" = ?`
, [userID])).map((item: { UUID: string }) => item.UUID);
const allSegments = (await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" st WHERE "st"."userID" = ?`, [userID]))
.map((item: { UUID: string }) => item.UUID);
await Promise.all(allSegments.filter((item: { uuid: string }) => {
return segmentsToIgnore.indexOf(item) === -1;
}).map(async (UUID: string) => {
// collect list for unshadowbanning
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "UUID" = ? AND "shadowHidden" >= 1 AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]))
.forEach((videoInfo: { category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID }) => {
QueryCacher.clearSegmentCache(videoInfo);
}
);
return db.prepare("run", `UPDATE "sponsorTimes" SET "shadowHidden" = 0 WHERE "UUID" = ? AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]);
}));
}
// already shadowbanned
} else if (enabled && row.userCount > 0) {
// apply unHideOldSubmissions if applicable
if (unHideOldSubmissions) {
await unHideSubmissionsByUser(categories, userID, type);
return res.sendStatus(200);
}
// otherwise ban already exists, send 409
return res.sendStatus(409);
await Promise.all(ips.map((ip) => {
return banIP(ip.hashedIP, enabled, unHideOldSubmissions, type, categories, true);
}));
}
} else if (hashedIP) {
//check to see if this user is already shadowbanned
const row = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedIPs" WHERE "hashedIP" = ?`, [hashedIP]);
if (enabled && row.userCount == 0) {
//add them to the shadow ban list
//add it to the table
await db.prepare("run", `INSERT INTO "shadowBannedIPs" VALUES(?)`, [hashedIP]);
//find all previous submissions and hide them
if (unHideOldSubmissions) {
await unHideSubmissionsByIP(categories, hashedIP, type);
}
} else if (!enabled) {
if (row.userCount > 0) {
//remove them from the shadow ban list
await db.prepare("run", `DELETE FROM "shadowBannedIPs" WHERE "hashedIP" = ?`, [hashedIP]);
}
//find all previous submissions and unhide them
if (unHideOldSubmissions) {
await unHideSubmissionsByIP(categories, hashedIP, "0");
}
} else if (enabled && row.userCount > 0) {
// apply unHideOldSubmissions if applicable
if (unHideOldSubmissions) {
await unHideSubmissionsByIP(categories, hashedIP, type);
return res.sendStatus(200);
}
// otherwise ban already exists, send 409
return res.sendStatus(409);
const result = await banIP(hashedIP, enabled, unHideOldSubmissions, type, categories, banUsers);
if (result) {
res.sendStatus(result);
}
}
return res.sendStatus(200);
}
async function banUser(userID: UserID, enabled: boolean, unHideOldSubmissions: boolean, type: string, categories: Category[]): Promise<number> {
//check to see if this user is already shadowbanned
const row = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
if (enabled && row.userCount == 0) {
//add them to the shadow ban list
//add it to the table
await db.prepare("run", `INSERT INTO "shadowBannedUsers" VALUES(?)`, [userID]);
//find all previous submissions and hide them
if (unHideOldSubmissions) {
await unHideSubmissionsByUser(categories, userID, type);
}
} else if (enabled && row.userCount > 0) {
// apply unHideOldSubmissions if applicable
if (unHideOldSubmissions) {
await unHideSubmissionsByUser(categories, userID, type);
} else {
// otherwise ban already exists, send 409
return 409;
}
} else if (!enabled && row.userCount > 0) {
//remove them from the shadow ban list
await db.prepare("run", `DELETE FROM "shadowBannedUsers" WHERE "userID" = ?`, [userID]);
//find all previous submissions and unhide them
if (unHideOldSubmissions) {
const segmentsToIgnore = (await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" st
JOIN "lockCategories" ns on "st"."videoID" = "ns"."videoID" AND st.category = ns.category AND "st"."service" = "ns"."service" WHERE "st"."userID" = ?`
, [userID])).map((item: { UUID: string }) => item.UUID);
const allSegments = (await db.prepare("all", `SELECT "UUID" FROM "sponsorTimes" st WHERE "st"."userID" = ?`, [userID]))
.map((item: { UUID: string }) => item.UUID);
await Promise.all(allSegments.filter((item: { uuid: string }) => {
return segmentsToIgnore.indexOf(item) === -1;
}).map(async (UUID: string) => {
// collect list for unshadowbanning
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "UUID" = ? AND "shadowHidden" >= 1 AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]))
.forEach((videoInfo: { category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID }) => {
QueryCacher.clearSegmentCache(videoInfo);
}
);
return db.prepare("run", `UPDATE "sponsorTimes" SET "shadowHidden" = 0 WHERE "UUID" = ? AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [UUID]);
}));
}
// already shadowbanned
}
return 200;
}
async function banIP(hashedIP: HashedIP, enabled: boolean, unHideOldSubmissions: boolean, type: string, categories: Category[], banUsers: boolean): Promise<number> {
//check to see if this user is already shadowbanned
const row = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedIPs" WHERE "hashedIP" = ?`, [hashedIP]);
if (enabled) {
if (row.userCount == 0) {
await db.prepare("run", `INSERT INTO "shadowBannedIPs" VALUES(?)`, [hashedIP]);
}
//find all previous submissions and hide them
if (unHideOldSubmissions) {
const users = await unHideSubmissionsByIP(categories, hashedIP, type);
await Promise.all([...users].map((user) => {
return banUser(user, enabled, unHideOldSubmissions, type, categories);
}))
} else if (row.userCount > 0) {
// Nothing to do, and already added
return 409;
}
} else if (!enabled) {
if (row.userCount > 0) {
//remove them from the shadow ban list
await db.prepare("run", `DELETE FROM "shadowBannedIPs" WHERE "hashedIP" = ?`, [hashedIP]);
}
//find all previous submissions and unhide them
if (unHideOldSubmissions) {
await unHideSubmissionsByIP(categories, hashedIP, "0");
}
}
return 200;
}
async function unHideSubmissionsByUser(categories: string[], userID: UserID, type = "1") {
if (!["1", "2"].includes(type)) return;
@ -139,15 +168,17 @@ async function unHideSubmissionsByUser(categories: string[], userID: UserID, typ
});
}
async function unHideSubmissionsByIP(categories: string[], hashedIP: HashedIP, type = "1") {
async function unHideSubmissionsByIP(categories: string[], hashedIP: HashedIP, type = "1"): Promise<Set<UserID>> {
if (!["0", "1", "2"].includes(type)) return;
const submissions = await privateDB.prepare("all", `SELECT "timeSubmitted" FROM "sponsorTimes" WHERE "hashedIP" = ?`, [hashedIP]) as { timeSubmitted: number }[];
const users: Set<UserID> = new Set();
await Promise.all(submissions.map(async (submission) => {
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "timeSubmitted" = ? AND "shadowHidden" >= 1 AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [submission.timeSubmitted]))
(await db.prepare("all", `SELECT "videoID", "hashedVideoID", "service", "votes", "views", "userID" FROM "sponsorTimes" WHERE "timeSubmitted" = ? AND "category" in (${categories.map((c) => `'${c}'`).join(",")})`, [submission.timeSubmitted]))
.forEach((videoInfo: { category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID }) => {
QueryCacher.clearSegmentCache(videoInfo);
users.add(videoInfo.userID);
}
);
@ -155,4 +186,6 @@ async function unHideSubmissionsByIP(categories: string[], hashedIP: HashedIP, t
AND NOT EXISTS ( SELECT "videoID", "category" FROM "lockCategories" WHERE
"sponsorTimes"."videoID" = "lockCategories"."videoID" AND "sponsorTimes"."service" = "lockCategories"."service" AND "sponsorTimes"."category" = "lockCategories"."category")`, [submission.timeSubmitted]);
}));
}
return users;
}

View file

@ -41,6 +41,13 @@ describe("shadowBanUser", () => {
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-71", "shadowBanned7", 2332, 50, "intro", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-72", "shadowBanned7", 4923, 50, "interaction", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-80", "shadowBanned8", 1674590916068933, 50, "sponsor", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-81", "shadowBanned8", 1674590916062936, 50, "intro", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-82", "shadowBanned8", 1674590916064324, 50, "interaction", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-90", "shadowBanned9", 1674590916062443, 50, "sponsor", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-91", "shadowBanned9", 1674590916062342, 50, "intro", "YouTube", 0, videohash]);
await db.prepare("run", insertQuery, [video, 20, 10, 2, 0, "shadow-92", "shadowBanned9", 1674590916069491, 50, "interaction", "YouTube", 0, videohash]);
await db.prepare("run", `INSERT INTO "shadowBannedUsers" ("userID") VALUES(?)`, ["shadowBanned3"]);
await db.prepare("run", `INSERT INTO "shadowBannedUsers" ("userID") VALUES(?)`, ["shadowBanned4"]);
@ -53,6 +60,13 @@ describe("shadowBanUser", () => {
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP7", 383848, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP7", 2332, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP7", 4923, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916068933, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916062936, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916064324, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916062443, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916062342, "YouTube"]);
await privateDB.prepare("run", privateInsertQuery, [video, "shadowBannedIP8", 1674590916069491, "YouTube"]);
});
it("Should be able to ban user and hide submissions", (done) => {
@ -336,7 +350,7 @@ describe("shadowBanUser", () => {
const normalShadowRow = await getShadowBan(userID);
const ipShadowRow = await getIPShadowBan(hashedIP);
assert.ok(ipShadowRow);
assert.ok(!normalShadowRow);
assert.ok(normalShadowRow);
assert.strictEqual(videoRow.length, 2);
done();
})
@ -362,7 +376,7 @@ describe("shadowBanUser", () => {
const normalShadowRow = await getShadowBan(userID);
const ipShadowRow = await getIPShadowBan(hashedIP);
assert.ok(!ipShadowRow);
assert.ok(!normalShadowRow);
assert.ok(normalShadowRow);
assert.strictEqual(videoRow.length, 2);
done();
})
@ -389,10 +403,43 @@ describe("shadowBanUser", () => {
const normalShadowRow = await getShadowBan(userID);
const ipShadowRow = await getIPShadowBan(hashedIP);
assert.ok(!ipShadowRow);
assert.ok(!normalShadowRow);
assert.ok(normalShadowRow);
assert.strictEqual(videoRow.length, 1);
done();
})
.catch(err => done(err));
});
it("Should be able to ban user by userID and other users who used that IP and hide specific category", (done) => {
const hashedIP = "shadowBannedIP8";
const userID = "shadowBanned8";
const userID2 = "shadowBanned9";
client({
method: "POST",
url: endpoint,
params: {
userID,
enabled: true,
categories: `["sponsor", "intro"]`,
unHideOldSubmissions: true,
adminUserID: VIPuserID,
lookForIPs: true
}
})
.then(async res => {
assert.strictEqual(res.status, 200);
const videoRow = await getShadowBanSegments(userID, 1);
const videoRow2 = await getShadowBanSegments(userID2, 1);
const normalShadowRow = await getShadowBan(userID);
const normalShadowRow2 = await getShadowBan(userID2);
const ipShadowRow = await getIPShadowBan(hashedIP);
assert.ok(ipShadowRow);
assert.ok(normalShadowRow);
assert.ok(normalShadowRow2);
assert.strictEqual(videoRow.length, 2);
assert.strictEqual(videoRow2.length, 2);
done();
})
.catch(err => done(err));
});
});