/** * Base requests handler * * @author Pierre Hubert */ import { UserHelper } from "../helpers/UserHelper"; import { removeHTMLNodes, checkMail, checkURL } from "../utils/StringUtils"; import { FriendsHelper } from "../helpers/FriendsHelper"; import { AccountHelper } from "../helpers/AccountHelper"; import { GroupsHelper } from "../helpers/GroupsHelper"; import { GroupsAccessLevel } from "./Group"; import { PostsHelper } from "../helpers/PostsHelper"; import { PostAccessLevel } from "./Post"; import { CommentsHelper } from "../helpers/CommentsHelper"; import { checkVirtualDirectory } from "../utils/VirtualDirsUtils"; export abstract class BaseRequestsHandler { protected abstract get userID() : number; protected abstract getPostParam(name : string) : any; public abstract hasPostParameter(name: string) : boolean; public abstract error(code : number, message : string) : void; public abstract success(message: string) : void; public abstract send(data: any): void; /** * Get information about current user */ public getUserId() : number { if(this.userID < 1) throw Error("Trying to get user ID but none are available!"); return this.userID; } /** * Get the ID of the current user (if any) * or 0 if the user is not signed in */ public get optionnalUserID(): number { return this.userID >= 1 ? this.userID : 0; } /** * Check out whether user is signed in or not */ public get signedIn() : boolean { return this.userID > 0; } /** * Check out whether a POST string is present in the request or not * * @param name The name of the POST field to check * @param minLength Minimal length of the parameter */ public hasPostString(name: string, minLength: number = 0) : boolean { return this.hasPostParameter(name) && this.getPostParam(name).length >= minLength; } /** * Get a String from the request * * @param name The name of the string to get * @param minLength Minimal required size of the string * @param required If set to true (true by default), an error will * be thrown if the string is not included in the request */ public postString(name : string, minLength : number = 1, required : boolean = true) : string { const param = this.getPostParam(name); // Check if parameter was not found if(param == undefined) { if(required) this.error(400, "Could not find required string: '"+name+"'"); return ""; } if(param.length < minLength) this.error(400, "Parameter "+name+" is too short!"); return param; } /** * Get some content for post and satinize it (remove HTML nodes) * * @param name The name of the POST field * @param minLength Optionnal minimal length for the post */ public postContent(name: string, minLength ?: number) : string { const content = this.postString(name, minLength); if(content.match(/data:image/)) this.error(401, "Please do not include inline images!"); return removeHTMLNodes(content); } /** * Get an email address included in a post request * * @param name The name of the POST filed */ public postEmail(name: string) : string { const email = this.postString(name, 3); if(!checkMail(email)) this.error(400, email + " is not a valid email address!"); return email; } /** * Get an integer included in the request * * @param name Name of POST field * @param fallback Fallback value (if none, throw an error) * @returns The number (throws in case of error) */ public postInt(name: string, fallback ?: number) : number { const param = this.getPostParam(name); if(param == undefined) { if(fallback == undefined) this.error(400, "Missing integer '"+name+"' in the request!"); return fallback; } // Check number if(Number.parseInt(param).toString() !== param.toString()) this.error(400, "'"+name+"' is an invalid integer!"); return Number.parseInt(param); } /** * Get a list of integeres included in the request * * @param name The name of the post field * @param minEntries Specify the minimum number of entries required */ public postNumbersList(name: string, minEntries : number = 1) : Array { const param = this.postString(name, minEntries < 1 ? 0 : minEntries, minEntries > 0); let list = []; for (const el of param.split(",")) { if(el == "") continue; if(Number.parseInt(el).toString() != el) this.error(400, "Invalid number detected in '"+name+"'!"); list.push(Number.parseInt(el)); } if(list.length < minEntries) this.error(400, "Not enough entries in '" + name + "'!") return list; } /** * Turn a list of string into a Set object * * @param name Name of POST field * @param minEntries Minimum number of entries to specify */ public postNumbersSet(name : string, minEntries : number = 1) : Set { return new Set(this.postNumbersList(name, minEntries)); } /** * Attempt to decode JSON included in a POST request * * @param name Name of POST field */ public postJSON(name: string) : any { const src = this.getPostParam(name); if(src == undefined) this.error(400, "Missing JSON '" + name + "' in the request!"); try { const response = JSON.parse(src); return response; } catch(e) { this.error(500, "'" + name + "' is not a valid JSON !"); } } /** * Get a boolean included in the request * * @param name The name of the POST field * @param fallback Fallback value to use if the value is not * found in the request */ public postBool(name: string, fallback ?: boolean) : boolean { const param = this.getPostParam(name); if(param == undefined) { if(fallback != undefined) return fallback; this.error(400, "Missing boolean '" + name + "' in the request!"); } return param === "true" || param === true; } /** * Get the ID of a user specified in a POST request * * @param name Name of the POST field */ public async postUserId(name: string) : Promise { const userID = this.postInt(name); if(userID < 1) this.error(400, "Invalid user ID specified in '" + name +"'!"); if(!await UserHelper.Exists(userID)) this.error(404, "User with ID " + userID + " not found!"); return userID; } /** * Get the ID of a friend included in a POST request * * @param name Name of the POST field */ public async postFriendId(name: string) : Promise { const friendID = await this.postUserId(name); if(!await FriendsHelper.AreFriend(this.getUserId(), friendID)) this.error(401, "You are not friend with this personn!"); return friendID; } /** * Find user ID based on its email address, included in a POST request * * @param name The name of the POST field containing the email address of the user */ public async postUserIdFromEmail(name: string) : Promise { const email = this.postEmail(name); const userID = await AccountHelper.FindIDFromEmail(email); if(userID < 1) this.error(404, "Email not found!"); return userID; } /** * Get a POST group ID * * @param name The name of the POST field */ public async postGroupID(name: string) : Promise { const groupID = this.postInt(name); if(!await GroupsHelper.Exists(groupID)) this.error(404, "Specified group not found!"); return groupID; } /** * Get a POST group ID with a check for access level of current user * * @param name The name of the POST field containing group ID * @param minVisibility Minimum visiblity requested to the group * @returns The ID of the group (throws in case of failure) */ public async postGroupIDWithAccess(name: string, minVisibility : GroupsAccessLevel) : Promise { const groupID = await this.postGroupID(name); const access = await GroupsHelper.GetAccessLevel(groupID, this.optionnalUserID); if(access == GroupsAccessLevel.NO_ACCESS) this.error(404, "Specified group not found!"); if(access < minVisibility) this.error(401, "You do not have enough rights to perform what you intend to do on this group!"); return groupID; } /** * Get the ID of post included in a POST request * * @param name The name of the POST field containing the id of the target post */ public async postPostID(name: string) : Promise { const postID = this.postInt(name); if(postID < 1) this.error(400, "Invalid post ID!"); if(!await PostsHelper.Exists(postID)) this.error(404, "Specified post does not exists!"); return postID; } /** * Get the ID of a post a user has access to * * @param name The name of the POST field containing the ID of the target post */ public async postPostIDWithAccess(name: string, minLevel: PostAccessLevel = PostAccessLevel.BASIC_ACCESS) : Promise { const postID = await this.postPostID(name); if(await PostsHelper.GetAccessLevelFromPostID(this.optionnalUserID, postID) < minLevel) this.error(401, "Your are not allowed to access this post information!"); return postID; } /** * Get the ID of a comment that the user is allowed to access * * @param name The name of the comment field */ public async postCommentIDWithAccess(name: string) : Promise { const commentID = this.postInt(name); if(!await CommentsHelper.Exists(commentID)) this.error(404, "Specified comment not found!"); const postID = await CommentsHelper.GetAssociatedPost(commentID); const post = await PostsHelper.GetSingle(postID); if(await PostsHelper.GetAccessLevel(this.getUserId(), post) == PostAccessLevel.NO_ACCESS) this.error(401, "You are not allowed to acess this post information!"); return commentID; } /** * Get a virtual directory included in a POST request * * @param name The name of the POST variable * @return The virtual directory, if found as valid */ public postVirtualDirectory(name: string) : string { const dir = this.postString(name); if(!checkVirtualDirectory(dir)) this.error(401, "Specified directory seems to be invalid!"); return dir; } /** * Get an URL included in a POST request * * @param name The name of the POST field containing * the URL */ public postURL(name: string) : string { const url = this.postString(name); if(!checkURL(url)) this.error(401, "Specified URL in '"+name+"' seems to be invalid!"); return url; } /** * Check the user password included in the request * * @param postField The name of the post field * containing user password */ public async needUserPostPassword(postField: string) { const password = this.postString(postField, 3); if(!await AccountHelper.CheckUserPassword(this.getUserId(), password)) this.error(401, "Invalid password!"); } }