add getHashCache where applicable=

This commit is contained in:
Michael C
2021-11-27 02:41:34 -05:00
parent 7e1550f3c0
commit a5a90e3c84
24 changed files with 90 additions and 47 deletions

View File

@@ -1,5 +1,6 @@
import { getIP } from "../utils/getIP";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import rateLimit from "express-rate-limit";
import { RateLimitConfig } from "../types/config.model";
import { Request } from "express";
@@ -17,7 +18,7 @@ export function rateLimitMiddleware(limitConfig: RateLimitConfig, getUserID?: (r
return getHash(getIP(req), 1);
},
handler: async (req, res, next) => {
if (getUserID === undefined || !await isUserVIP(getHash(getUserID(req)))) {
if (getUserID === undefined || !await isUserVIP(await getHashCache(getUserID(req)))) {
return res.status(limitConfig.statusCode).send(limitConfig.message);
} else {
return next();

View File

@@ -1,4 +1,4 @@
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { db } from "../databases/databases";
import { config } from "../config";
import { Request, Response } from "express";
@@ -24,7 +24,7 @@ export async function addUserAsVIP(req: AddUserAsVIPRequest, res: Response): Pro
}
// hash the userID
const adminUserIDInput = getHash(adminUserID);
const adminUserIDInput = await getHashCache(adminUserID);
if (adminUserIDInput !== config.adminUserID) {
// not authorized

View File

@@ -1,6 +1,6 @@
import { Request, Response } from "express";
import { isUserVIP } from "../utils/isUserVIP";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { db } from "../databases/databases";
import { Category, Service, VideoID } from "../types/segments.model";
import { UserID } from "../types/user.model";
@@ -39,7 +39,7 @@ export async function deleteLockCategoriesEndpoint(req: DeleteLockCategoriesRequ
}
// Check if user is VIP
const hashedUserID = getHash(userID);
const hashedUserID = await getHashCache(userID);
const userIsVIP = await isUserVIP(hashedUserID);
if (!userIsVIP) {

View File

@@ -1,5 +1,5 @@
import { Logger } from "../utils/logger";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { isUserVIP } from "../utils/isUserVIP";
import { Request, Response } from "express";
import { HashedUserID, UserID } from "../types/user.model";
@@ -13,7 +13,7 @@ export async function getIsUserVIP(req: Request, res: Response): Promise<Respons
}
//hash the userID
const hashedUserID: HashedUserID = getHash(userID);
const hashedUserID: HashedUserID = await getHashCache(userID);
try {
const vipState = await isUserVIP(hashedUserID);

View File

@@ -1,6 +1,6 @@
import { db } from "../databases/databases";
import { Request, Response } from "express";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { config } from "../config";
import { Logger } from "../utils/logger";
@@ -15,7 +15,7 @@ export async function getSavedTimeForUser(req: Request, res: Response): Promise<
}
//hash the userID
userID = getHash(userID);
userID = await getHashCache(userID);
try {
const row = await db.prepare("get", 'SELECT SUM(((CASE WHEN "endTime" - "startTime" > ? THEN ? ELSE "endTime" - "startTime" END) / 60) * "views") as "minutesSaved" FROM "sponsorTimes" WHERE "userID" = ? AND "votes" > -1 AND "shadowHidden" != 1 ', [maxRewardTimePerSegmentInSeconds, maxRewardTimePerSegmentInSeconds, userID]);

View File

@@ -5,7 +5,7 @@ import { skipSegmentsHashKey, skipSegmentsKey } from "../utils/redisKeys";
import { SBRecord } from "../types/lib.model";
import { ActionType, Category, CategoryActionType, DBSegment, HashedIP, IPAddress, OverlappingSegmentGroup, Segment, SegmentCache, SegmentUUID, Service, VideoData, VideoID, VideoIDHash, Visibility, VotableObject } from "../types/segments.model";
import { getCategoryActionType } from "../utils/categoryInfo";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { getIP } from "../utils/getIP";
import { Logger } from "../utils/logger";
import { QueryCacher } from "../utils/queryCacher";
@@ -33,10 +33,10 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category:
}
//if this isn't their ip, don't send it to them
return cache.shadowHiddenSegmentIPs[videoID][segment.timeSubmitted]?.some((shadowHiddenSegment) => {
return cache.shadowHiddenSegmentIPs[videoID][segment.timeSubmitted]?.some(async (shadowHiddenSegment) => {
if (cache.userHashedIP === undefined) {
//hash the IP only if it's strictly necessary
cache.userHashedIP = getHash((getIP(req) + config.globalSalt) as IPAddress);
cache.userHashedIP = await getHashCache((getIP(req) + config.globalSalt) as IPAddress);
}
return shadowHiddenSegment.hashedIP === cache.userHashedIP;

View File

@@ -1,5 +1,5 @@
import { db } from "../databases/databases";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { isUserVIP } from "../utils/isUserVIP";
import { Request, Response } from "express";
import { Logger } from "../utils/logger";
@@ -134,7 +134,7 @@ const dbGetValue = (userID: HashedUserID, property: string): Promise<string|Segm
async function getUserInfo(req: Request, res: Response): Promise<Response> {
const userID = req.query.userID as UserID;
const hashedUserID: HashedUserID = userID ? getHash(userID) : req.query.publicUserID as HashedUserID;
const hashedUserID: HashedUserID = userID ? await getHashCache(userID) : req.query.publicUserID as HashedUserID;
const defaultProperties: string[] = ["userID", "userName", "minutesSaved", "segmentCount", "ignoredSegmentCount",
"viewCount", "ignoredViewCount", "warnings", "warningReason", "reputation",
"vip", "lastSegmentID"];

View File

@@ -1,5 +1,5 @@
import { db } from "../databases/databases";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Request, Response } from "express";
import { HashedUserID, UserID } from "../types/user.model";
import { config } from "../config";
@@ -78,7 +78,7 @@ async function dbGetUsername(userID: HashedUserID) {
export async function getUserStats(req: Request, res: Response): Promise<Response> {
const userID = req.query.userID as UserID;
const hashedUserID: HashedUserID = userID ? getHash(userID) : req.query.publicUserID as HashedUserID;
const hashedUserID: HashedUserID = userID ? await getHashCache(userID) : req.query.publicUserID as HashedUserID;
const fetchCategoryStats = req.query.fetchCategoryStats == "true";
const fetchActionTypeStats = req.query.fetchActionTypeStats == "true";

View File

@@ -1,5 +1,5 @@
import { db } from "../databases/databases";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Logger } from "../utils/logger";
import { Request, Response } from "express";
@@ -12,7 +12,7 @@ export async function getUsername(req: Request, res: Response): Promise<Response
}
//hash the userID
userID = getHash(userID);
userID = await getHashCache(userID);
try {
const row = await db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);

View File

@@ -1,6 +1,6 @@
import { db } from "../databases/databases";
import { Request, Response } from "express";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Logger } from "../utils/logger";
export async function getViewsForUser(req: Request, res: Response): Promise<Response> {
@@ -12,7 +12,7 @@ export async function getViewsForUser(req: Request, res: Response): Promise<Resp
}
//hash the userID
userID = getHash(userID);
userID = await getHashCache(userID);
try {
const row = await db.prepare("get", `SELECT SUM("views") as "viewCount" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]);

View File

@@ -1,6 +1,6 @@
import { Logger } from "../utils/logger";
import { HashedUserID, UserID } from "../types/user.model";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Request, Response } from "express";
import { Service, VideoID } from "../types/segments.model";
import { QueryCacher } from "../utils/queryCacher";
@@ -28,9 +28,9 @@ export async function postClearCache(req: Request, res: Response): Promise<Respo
}
// hash the userID as early as possible
const hashedUserID: HashedUserID = getHash(userID);
const hashedUserID: HashedUserID = await getHashCache(userID);
// hash videoID
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
const hashedVideoID: VideoIDHash = await getHashCache(videoID, 1);
// Ensure user is a VIP
if (!(await isUserVIP(hashedUserID))){

View File

@@ -1,5 +1,5 @@
import { Logger } from "../utils/logger";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { isUserVIP } from "../utils/isUserVIP";
import { db } from "../databases/databases";
import { Request, Response } from "express";
@@ -28,7 +28,7 @@ export async function postLockCategories(req: Request, res: Response): Promise<s
}
// Check if user is VIP
userID = getHash(userID);
userID = await getHashCache(userID);
const userIsVIP = await isUserVIP(userID);
if (!userIsVIP) {
@@ -62,7 +62,7 @@ export async function postLockCategories(req: Request, res: Response): Promise<s
});
// calculate hash of videoID
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
const hashedVideoID: VideoIDHash = await getHashCache(videoID, 1);
// create database entry
for (const category of categoriesToMark) {

View File

@@ -1,5 +1,5 @@
import { Logger } from "../utils/logger";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { isUserVIP } from "../utils/isUserVIP";
import { Request, Response } from "express";
import { HashedUserID, UserID } from "../types/user.model";
@@ -18,7 +18,7 @@ export async function postPurgeAllSegments(req: Request, res: Response): Promise
}
//hash the userID
const hashedUserID: HashedUserID = getHash(userID);
const hashedUserID: HashedUserID = await getHashCache(userID);
try {
const vipState = await isUserVIP(hashedUserID);
@@ -30,7 +30,7 @@ export async function postPurgeAllSegments(req: Request, res: Response): Promise
await db.prepare("run", `UPDATE "sponsorTimes" SET "hidden" = 1 WHERE "videoID" = ?`, [videoID]);
const hashedVideoID: VideoIDHash = getHash(videoID, 1);
const hashedVideoID: VideoIDHash = await getHashCache(videoID, 1);
QueryCacher.clearSegmentCache({
videoID,
hashedVideoID,

View File

@@ -1,7 +1,7 @@
import { Request, Response } from "express";
import { Logger } from "../utils/logger";
import { isUserVIP } from "../utils/isUserVIP";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { db } from "../databases/databases";
const ACTION_NONE = Symbol("none");
@@ -64,7 +64,7 @@ export async function postSegmentShift(req: Request, res: Response): Promise<Res
}
// Check if user is VIP
userID = getHash(userID);
userID = await getHashCache(userID);
const userIsVIP = await isUserVIP(userID);
if (!userIsVIP) {

View File

@@ -4,6 +4,7 @@ import { db, privateDB } from "../databases/databases";
import { getMaxResThumbnail, YouTubeAPI } from "../utils/youtubeApi";
import { getSubmissionUUID } from "../utils/getSubmissionUUID";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { getIP } from "../utils/getIP";
import { getFormattedTime } from "../utils/getFormattedTime";
import { dispatchEvent } from "../utils/webhookUtils";
@@ -580,7 +581,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
}
//hash the userID
userID = getHash(userID);
userID = await getHashCache(userID);
const userWarningCheckResult = await checkUserActiveWarning(userID);
if (!userWarningCheckResult.pass) {
@@ -615,7 +616,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
const newSegments = [];
//hash the ip 5000 times so no one can get it from the database
const hashedIP = getHash(getIP(req) + config.globalSalt);
const hashedIP = await getHashCache(getIP(req) + config.globalSalt);
try {
//get current time

View File

@@ -2,7 +2,7 @@ import { Request, Response } from "express";
import { Logger } from "../utils/logger";
import { db } from "../databases/databases";
import { isUserVIP } from "../utils/isUserVIP";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { HashedUserID, UserID } from "../types/user.model";
import { config } from "../config";
@@ -25,7 +25,7 @@ export async function postWarning(req: Request, res: Response): Promise<Response
// exit early if no body passed in
if (!req.body.userID && !req.body.issuerUserID) return res.status(400).json({ "message": "Missing parameters" });
// Collect user input data
const issuerUserID: HashedUserID = getHash(<UserID> req.body.issuerUserID);
const issuerUserID: HashedUserID = await getHashCache(<UserID> req.body.issuerUserID);
const userID: UserID = req.body.userID;
const issueTime = new Date().getTime();
const enabled: boolean = req.body.enabled ?? true;

View File

@@ -1,6 +1,7 @@
import { Logger } from "../../utils/logger";
import { HashedUserID, UserID } from "../../types/user.model";
import { getHash } from "../../utils/getHash";
import { getHashCache } from "../../utils/getHashCache";
import { Request, Response } from "express";
import { Service, VideoID } from "../../types/segments.model";
import { QueryCacher } from "../../utils/queryCacher";
@@ -28,7 +29,7 @@ export async function postClearCache(req: Request, res: Response): Promise<Respo
}
// hash the userID as early as possible
const hashedUserID: HashedUserID = getHash(userID);
const hashedUserID: HashedUserID = await getHashCache(userID);
// hash videoID
const hashedVideoID: VideoIDHash = getHash(videoID, 1);

View File

@@ -1,5 +1,6 @@
import { db, privateDB } from "../../databases/databases";
import { getHash } from "../../utils/getHash";
import { getHashCache } from "../../utils/getHashCache";
import { Logger } from "../../utils/logger";
import { Request, Response } from "express";
import { HashedUserID, UserID } from "../../types/user.model";
@@ -25,7 +26,7 @@ export async function postRating(req: Request, res: Response): Promise<Response>
}
const hashedIP: HashedIP = getHash(getIP(req) + config.globalSalt as IPAddress, 1);
const hashedUserID: HashedUserID = getHash(privateUserID);
const hashedUserID: HashedUserID = await getHashCache(privateUserID);
const hashedVideoID = getHash(videoID, 1);
try {

View File

@@ -1,7 +1,7 @@
import { config } from "../config";
import { Logger } from "../utils/logger";
import { db, privateDB } from "../databases/databases";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Request, Response } from "express";
function logUserNameChange(userID: string, newUserName: string, oldUserName: string, updatedByAdmin: boolean): Promise<Response> {
@@ -34,7 +34,7 @@ export async function setUsername(req: Request, res: Response): Promise<Response
if (adminUserIDInput != undefined) {
//this is the admin controlling the other users account, don't hash the controling account's ID
adminUserIDInput = getHash(adminUserIDInput);
adminUserIDInput = await getHashCache(adminUserIDInput);
if (adminUserIDInput != config.adminUserID) {
//they aren't the admin
@@ -42,7 +42,7 @@ export async function setUsername(req: Request, res: Response): Promise<Response
}
} else {
//hash the userID
userID = getHash(userID);
userID = await getHashCache(userID);
}
try {

View File

@@ -1,5 +1,5 @@
import { db } from "../databases/databases";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { Request, Response } from "express";
import { config } from "../config";
import { Category, Service, VideoID, VideoIDHash } from "../types/segments.model";
@@ -28,7 +28,7 @@ export async function shadowBanUser(req: Request, res: Response): Promise<Respon
}
//hash the userID
const adminUserID = getHash(adminUserIDInput);
const adminUserID = await getHashCache(adminUserIDInput);
const isVIP = await isUserVIP(adminUserID);
if (!isVIP) {

View File

@@ -7,7 +7,7 @@ import { db, privateDB } from "../databases/databases";
import { dispatchEvent, getVoteAuthor, getVoteAuthorRaw } from "../utils/webhookUtils";
import { getFormattedTime } from "../utils/getFormattedTime";
import { getIP } from "../utils/getIP";
import { getHash } from "../utils/getHash";
import { getHashCache } from "../utils/getHashCache";
import { config } from "../config";
import { UserID } from "../types/user.model";
import { Category, CategoryActionType, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, Visibility, VideoDuration } from "../types/segments.model";
@@ -292,8 +292,8 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise<Re
}
//hash the userID
const nonAnonUserID = getHash(paramUserID);
const userID = getHash(paramUserID + UUID);
const nonAnonUserID = await getHashCache(paramUserID);
const userID = await getHashCache(paramUserID + UUID);
// To force a non 200, change this early
const finalResponse: FinalResponse = {
@@ -308,7 +308,7 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise<Re
const ip = getIP(req);
//hash the ip 5000 times so no one can get it from the database
const hashedIP: HashedIP = getHash((ip + config.globalSalt) as IPAddress);
const hashedIP: HashedIP = await getHashCache((ip + config.globalSalt) as IPAddress);
//check if this user is on the vip list
const isVIP = await isUserVIP(nonAnonUserID);

View File

@@ -10,4 +10,4 @@ export function getHash<T extends string>(value: T, times = 5000): T & HashedVal
}
return value as T & HashedValue;
}
}

32
src/utils/getHashCache.ts Normal file
View File

@@ -0,0 +1,32 @@
import redis from "../utils/redis";
import { userHashKey } from "../utils/redisKeys";
import { HashedValue } from "../types/hash.model";
import { Logger } from "../utils/logger";
import { getHash } from "../utils/getHash";
export async function getHashCache<T extends string>(value: T, times = 5000): Promise<T & HashedValue> {
if (times === 5000) {
const hashKey = getHash(value, 1);
const result: HashedValue = await getFromRedis(hashKey);
return result as T & HashedValue;
}
return getHash(value, times);
}
async function getFromRedis<T extends string>(key: HashedValue): Promise<T & HashedValue> {
const redisKey = userHashKey(key);
const { err, reply } = await redis.getAsync(redisKey);
if (!err && reply) {
try {
Logger.debug(`Got data from redis: ${reply}`);
return reply as T & HashedValue;
} catch (e) {
// If all else, continue on hashing
}
}
const data = getHash(key, 5000-1);
redis.setAsync(key, data);
return data as T & HashedValue;
}

View File

@@ -1,5 +1,6 @@
import { Service, VideoID, VideoIDHash } from "../types/segments.model";
import { UserID } from "../types/user.model";
import { HashedValue } from "../types/hash.model";
import { Logger } from "./logger";
export function skipSegmentsKey(videoID: VideoID, service: Service): string {
@@ -22,4 +23,10 @@ export function ratingHashKey(hashPrefix: VideoIDHash, service: Service): string
if (hashPrefix.length !== 4) Logger.warn(`Redis rating hash-prefix key is not length 4! ${hashPrefix}`);
return `rating.${service}.${hashPrefix}`;
}
export function userHashKey(userID: HashedValue): string {
if (userID.length !== 64) Logger.warn(`Redis userHash key is not length 64! ${userID}`);
return `user.${userID}`;
}