diff --git a/databases/_upgrade_sponsorTimes_5.sql b/databases/_upgrade_sponsorTimes_5.sql new file mode 100644 index 0000000..1132af2 --- /dev/null +++ b/databases/_upgrade_sponsorTimes_5.sql @@ -0,0 +1,17 @@ +BEGIN TRANSACTION; + +/* Add enabled field */ +CREATE TABLE "sqlb_temp_table_5" ( + userID TEXT NOT NULL, + issueTime INTEGER NOT NULL, + issuerUserID TEXT NOT NULL, + enabled INTEGER NOT NULL +); +INSERT INTO sqlb_temp_table_5 SELECT userID,issueTime,issuerUserID,1 FROM warnings; + +DROP TABLE warnings; +ALTER TABLE sqlb_temp_table_5 RENAME TO "warnings"; + +UPDATE config SET value = 5 WHERE key = "version"; + +COMMIT; \ No newline at end of file diff --git a/src/routes/getIsUserVIP.ts b/src/routes/getIsUserVIP.ts index 3cc23f0..597de25 100644 --- a/src/routes/getIsUserVIP.ts +++ b/src/routes/getIsUserVIP.ts @@ -2,9 +2,10 @@ import {Logger} from '../utils/logger'; import {getHash} from '../utils/getHash'; import {isUserVIP} from '../utils/isUserVIP'; import {Request, Response} from 'express'; +import { HashedUserID, UserID } from '../types/user.model'; export function getIsUserVIP(req: Request, res: Response): void { - let userID = req.query.userID as string; + const userID = req.query.userID as UserID; if (userID == undefined) { //invalid request @@ -13,12 +14,12 @@ export function getIsUserVIP(req: Request, res: Response): void { } //hash the userID - userID = getHash(userID); + const hashedUserID: HashedUserID = getHash(userID); try { - let vipState = isUserVIP(userID); + let vipState = isUserVIP(hashedUserID); res.status(200).json({ - hashedUserID: userID, + hashedUserID: hashedUserID, vip: vipState, }); } catch (err) { diff --git a/src/routes/postWarning.ts b/src/routes/postWarning.ts index 3361f18..b34c99c 100644 --- a/src/routes/postWarning.ts +++ b/src/routes/postWarning.ts @@ -3,23 +3,33 @@ import {Logger} from '../utils/logger'; import {db} from '../databases/databases'; import {isUserVIP} from '../utils/isUserVIP'; import {getHash} from '../utils/getHash'; +import { HashedUserID, UserID } from '../types/user.model'; export function postWarning(req: Request, res: Response) { // Collect user input data - let issuerUserID = getHash(req.body.issuerUserID); - let userID = req.body.userID; + let issuerUserID: HashedUserID = getHash( req.body.issuerUserID); + let userID: UserID = req.body.userID; let issueTime = new Date().getTime(); + let enabled: boolean = req.body.enabled ?? true; // Ensure user is a VIP if (!isUserVIP(issuerUserID)) { - Logger.debug("Permission violation: User " + issuerUserID + " attempted to warn user " + userID + "."); // maybe warn? + Logger.warn("Permission violation: User " + issuerUserID + " attempted to warn user " + userID + "."); res.status(403).json({"message": "Not a VIP"}); return; } - db.prepare('run', 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES (?, ?, ?)', [userID, issueTime, issuerUserID]); - res.status(200).json({ - message: "Warning issued to user '" + userID + "'.", - }); + let resultStatus = ""; + if (enabled) { + db.prepare('run', 'INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES (?, ?, ?, 1)', [userID, issueTime, issuerUserID]); + resultStatus = "issued to"; + } else { + db.prepare('run', 'UPDATE warnings SET enabled = 0', []); + resultStatus = "removed from"; + } + + res.status(200).json({ + message: "Warning " + resultStatus + " user '" + userID + "'.", + }); } diff --git a/src/types/hash.model.ts b/src/types/hash.model.ts new file mode 100644 index 0000000..56abc62 --- /dev/null +++ b/src/types/hash.model.ts @@ -0,0 +1 @@ +export type HashedValue = string & { __hashBrand: unknown }; \ No newline at end of file diff --git a/src/types/user.model.ts b/src/types/user.model.ts new file mode 100644 index 0000000..102126a --- /dev/null +++ b/src/types/user.model.ts @@ -0,0 +1,4 @@ +import { HashedValue } from "./hash.model"; + +export type UserID = string & { __userIDBrand: unknown }; +export type HashedUserID = UserID & HashedValue; \ No newline at end of file diff --git a/src/utils/getHash.ts b/src/utils/getHash.ts index b9d770d..950a134 100644 --- a/src/utils/getHash.ts +++ b/src/utils/getHash.ts @@ -1,12 +1,13 @@ import crypto from 'crypto'; +import { HashedValue } from '../types/hash.model'; -export function getHash(value: string, times = 5000) { - if (times <= 0) return ""; +export function getHash(value: T, times = 5000): T & HashedValue { + if (times <= 0) return "" as T & HashedValue; for (let i = 0; i < times; i++) { let hashCreator = crypto.createHash('sha256'); - value = hashCreator.update(value).digest('hex'); + value = hashCreator.update(value).digest('hex') as T; } - return value; + return value as T & HashedValue; } diff --git a/src/utils/isUserVIP.ts b/src/utils/isUserVIP.ts index 14c2f33..3704791 100644 --- a/src/utils/isUserVIP.ts +++ b/src/utils/isUserVIP.ts @@ -1,6 +1,7 @@ import {db} from '../databases/databases'; +import { HashedUserID } from '../types/user.model'; -export function isUserVIP(userID: string): boolean { +export function isUserVIP(userID: HashedUserID): boolean { return db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0; } diff --git a/test/cases/getUserInfo.ts b/test/cases/getUserInfo.ts index 7724222..e2ba17c 100644 --- a/test/cases/getUserInfo.ts +++ b/test/cases/getUserInfo.ts @@ -18,9 +18,9 @@ describe('getUserInfo', () => { db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000008', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 1)"); - db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_0') + "', 10, 'getuserinfo_vip')"); - db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')"); - db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')"); + db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES ('" + getHash('getuserinfo_warning_0') + "', 10, 'getuserinfo_vip', 1)"); + db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip', 1)"); + db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip', 1)"); }); it('Should be able to get a 200', (done: Done) => { diff --git a/test/cases/postSkipSegments.ts b/test/cases/postSkipSegments.ts index 5c68f40..8830d6a 100644 --- a/test/cases/postSkipSegments.ts +++ b/test/cases/postSkipSegments.ts @@ -25,15 +25,15 @@ describe('postSkipSegments', () => { const warnUser02Hash = getHash("warn-user02"); const MILLISECONDS_IN_HOUR = 3600000; const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires; - const startOfWarningQuery = 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES'; - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 1000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 2000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 3601000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 1000)) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 2000)) + "', '" + warnVip01Hash + "')"); + const startOfWarningQuery = 'INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES'; + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 1000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 2000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 3601000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 1000)) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 2000)) + "', '" + warnVip01Hash + "', 1)"); }); it('Should be able to submit a single time (Params method)', (done: Done) => { diff --git a/test/cases/postWarning.ts b/test/cases/postWarning.ts index 97b13e4..7122334 100644 --- a/test/cases/postWarning.ts +++ b/test/cases/postWarning.ts @@ -19,13 +19,44 @@ describe('postWarning', () => { (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { - done(); + let row = db.prepare('get', "SELECT userID, issueTime, issuerUserID, enabled FROM warnings WHERE userID = ?", [json.userID]); + if (row?.enabled == 1 && row?.issuerUserID == getHash(json.issuerUserID)) { + done(); + } else { + done("Warning missing from database"); + } } else { console.log(body); done("Status code was " + res.statusCode); } }); }); + + it('Should be able to remove warning if vip', (done: Done) => { + let json = { + issuerUserID: 'warning-vip', + userID: 'warning-0', + enabled: false + }; + + request.post(getbaseURL() + + "/api/warnUser", {json}, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let row = db.prepare('get', "SELECT userID, issueTime, issuerUserID, enabled FROM warnings WHERE userID = ?", [json.userID]); + if (row?.enabled == 0) { + done(); + } else { + done("Warning missing from database"); + } + } else { + console.log(body); + done("Status code was " + res.statusCode); + } + }); + }); + it('Should not be able to create warning if vip (exp 403)', (done: Done) => { let json = { issuerUserID: 'warning-not-vip', diff --git a/test/cases/voteOnSponsorTime.ts b/test/cases/voteOnSponsorTime.ts index 29a28e3..2ba1e35 100644 --- a/test/cases/voteOnSponsorTime.ts +++ b/test/cases/voteOnSponsorTime.ts @@ -20,7 +20,7 @@ describe('voteOnSponsorTime', () => { const MILLISECONDS_IN_HOUR = 3600000; const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires; let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES"; - const startOfWarningQuery = 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES'; + const startOfWarningQuery = 'INSERT INTO warnings (userID, issueTime, issuerUserID, enabled) VALUES'; db.exec(startOfQuery + "('vote-testtesttest', 1, 11, 2, 'vote-uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('vote-testtesttest', 1) + "')"); db.exec(startOfQuery + "('vote-testtesttest2', 1, 11, 2, 'vote-uuid-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('vote-testtesttest2', 1) + "')"); @@ -45,14 +45,14 @@ describe('voteOnSponsorTime', () => { db.exec(startOfQuery + "('no-sponsor-segments-video', 1, 11, 2, 'no-sponsor-segments-uuid-0', 'no-sponsor-segments', 0, 50, 'sponsor', 0, '" + getHash('no-sponsor-segments-video', 1) + "')"); db.exec(startOfQuery + "('no-sponsor-segments-video', 1, 11, 2, 'no-sponsor-segments-uuid-1', 'no-sponsor-segments', 0, 50, 'intro', 0, '" + getHash('no-sponsor-segments-video', 1) + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 1000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 2000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 3601000) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 1000)) + "', '" + warnVip01Hash + "')"); - db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 2000)) + "', '" + warnVip01Hash + "')"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 1000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 2000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser01Hash + "', '" + (now - 3601000) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + now + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 1000)) + "', '" + warnVip01Hash + "', 1)"); + db.exec(startOfWarningQuery + "('" + warnUser02Hash + "', '" + (now - (warningExpireTime + 2000)) + "', '" + warnVip01Hash + "', 1)"); db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser") + "')");