import { crypt, sha1, randomStr } from "../utils/CryptUtils"; import { APIClient } from "../entities/APIClient"; import { UserLoginTokens } from "../entities/UserLoginTokens"; import { DatabaseHelper } from "./DatabaseHelper"; import { UserHelper } from "./UserHelper"; import { time, mysql_date } from "../utils/DateUtils"; import { NewAccount } from "../entities/NewAccount"; import { GeneralSettings, UserPageStatus, LangSettings, SecuritySettings } from "../entities/User"; import { AccountExport } from "../entities/AccountExport"; import { PostsHelper } from "./PostsHelper"; import { CommentsHelper } from "./CommentsHelper"; import { LikesHelper, LikesType } from "./LikesHelper"; import { SurveyHelper } from "./SurveyHelper"; import { MoviesHelper } from "./MoviesHelper"; import { ConversationsHelper } from "./ConversationsHelper"; import { FriendsHelper } from "./FriendsHelper"; import { GroupsHelper } from "./GroupsHelper"; import { NotificationsHelper } from "./NotificationsHelper"; import { AccountImageHelper } from "./AccountImageHelper"; import { BackgroundImageHelper } from "./BackgroundImageHelper"; import { EventsHelper } from "./EventsHelper"; /** * Account helper * * @author Pierre HUBERT */ export const USER_TABLE = "utilisateurs"; const USERS_TOKENS_TABLE = "comunic_api_users_tokens"; export class AccountHelper { /** * Create a new account * * @param info Information about the new account */ public static async Create(info: NewAccount) { const data = { nom: info.lastName, prenom: info.firstName, date_creation: mysql_date(), mail: info.email, password: this.CryptPassword(info.password) }; await DatabaseHelper.InsertRow(USER_TABLE, data); } /** * Given email address and password, try to sign in user * * @param email The email of the user * @param password User password * @param client Information about associated client / null if none found */ static async LoginUser(email: string, password: string, client: APIClient) : Promise { // Perform a request on the database const row = await DatabaseHelper.QueryRow({ table: USER_TABLE, fields: ["ID"], where: { mail: email, password: this.CryptPassword(password) } }); // Check if user was found if(row == null) return null; const userID = row.ID; // Check for existing tokens let tokens = await this.GetClientTokens(userID, client); if(tokens != null) return tokens; const newTokens : UserLoginTokens = { userID: userID, clientID: client.id, token1: randomStr(150), token2: "dummy_data" } // Save new tokens await DatabaseHelper.InsertRow(USERS_TOKENS_TABLE, { user_id: newTokens.userID, service_id: newTokens.clientID, token1: newTokens.token1, token2: newTokens.token2 }); return newTokens; } /** * Get user client tokens (if it exists) * * @param userID Target user ID * @param client Information about associated client / null if none found */ private static async GetClientTokens(userID: number, client: APIClient): Promise { const row = await DatabaseHelper.QueryRow({ table: USERS_TOKENS_TABLE, where: { user_id: userID, service_id: client.id } }); return row == null ? null : this.DBToUserTokens(row); } /** * Find a user using its tokens * * @param client Information about the client * @param token1 First token * @param token2 Second token * @returns The ID of the target user / -1 if none found */ public static async GetUserIdFromTokens(client : APIClient, token1: string, token2: string) : Promise { const row = await DatabaseHelper.QueryRow({ table: USERS_TOKENS_TABLE, fields: ["user_id"], where: { service_id: client.id, token1: token1, token2: token2 } }); if(!row) return -1; return Number(row.user_id); } /** * Destroy user tokens * * @param client Information about the client * @param userID Target user ID */ public static async DestroyUserTokens(client: APIClient, userID: number) { // Push the event EventsHelper.Emit("destroyed_login_tokens", { client: client, userID: userID }); return DatabaseHelper.DeleteRows(USERS_TOKENS_TABLE, { service_id: client.id, user_id: userID }); } /** * Check out whether the password of a user is valid * or not * * @param userID Target user ID * @param password Target password */ public static async CheckUserPassword(userID: number, password: string) : Promise { const crypt_pass = this.CryptPassword(password); return await DatabaseHelper.Count({ table: USER_TABLE, where: { ID: userID, password: crypt_pass } }) > 0; } /** * Crypt a password * * @param pass The password to crypt * @return Encrypted string */ private static CryptPassword(pass: string) : string { return crypt(sha1(pass), sha1(pass)); } private static DBToUserTokens(row : any) : UserLoginTokens { return { userID: row.user_id, clientID: row.service_id, token1: row.token1, token2: row.token2 }; } /** * Check out whether an email address exists or not * * @param email Email address to check */ public static async ExistsEmail(email: string) : Promise { return await DatabaseHelper.Count({ table: USER_TABLE, where: { mail: email } }) > 0; } /** * Find user ID from its email address * * @param email Email address * @returns The ID of the matching user / -1 if none found */ public static async FindIDFromEmail(email: string) : Promise { const result = await DatabaseHelper.QueryRow({ table: USER_TABLE, where: { mail: email, }, fields: ["ID"] }); return result == null ? -1 : result.ID; } /** * Update the last activity time of the user * * @param userID Target user ID */ public static async UpdateLastActivity(userID: number) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: userID }, set: { last_activity: time() } }) } /** * Check out whether a virtual directory is available or not * * @param dir Virtual directory to check * @param userID Target user ID */ public static async CheckUserDirectoryAvailability(dir: string, userID: number = -1) : Promise { const foundUser = await UserHelper.FindByFolder(dir); return foundUser < 1 || userID == foundUser; } /** * Generate a new token to reset an account password * * @param userID Target user ID * @returns Generated token */ public static async GenerateNewPasswordResetToken(userID: number) : Promise { // Generate a token const token = randomStr(255); await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: userID }, set: { password_reset_token: token, password_reset_token_time_create: time() } }); return token; } /** * Destroy password reset token for a given user * * @param userID Target user ID */ public static async DestroyPasswordResetTokenForUser(userID: number) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: userID }, set: { password_reset_token: "", password_reset_token_time_create: 85 // Value too low to be valid } }); } /** * Delete all user login tokens * * This method has for immediate effect to disconnect * the user from all its devices * * @param userID Target user ID */ public static async DeleteAllUserLoginTokens(userID: number) { await DatabaseHelper.DeleteRows(USERS_TOKENS_TABLE, { user_id: userID }) } /** * Get the ID of a user from a password reset token * * @param token The token to use * @returns The ID of the user associated to the token, if it is valid / -1 else */ public static async GetUserIDFromPasswordResetToken(token: string) : Promise { // Query the database const result = await DatabaseHelper.QueryRow({ table: USER_TABLE, where: { password_reset_token: token, }, customWhere: "password_reset_token_time_create > ?", customWhereArgs:[(time()-60*60*24).toString()] // Tokens are valid for 24 hours }); if(result == null) return -1; return result.ID; } /** * Change the password of the user * * @param userID Target user ID * @param password Target password */ public static async ChangePassword(userID: number, password: string) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: userID }, set: { password: this.CryptPassword(password) } }); } /** * Set (save) new general settings * * @param settings New settings */ public static async SetGeneral(settings: GeneralSettings) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: settings.id }, set: { prenom: settings.firstName, nom: settings.lastName, public: settings.pageStatus != UserPageStatus.PRIVATE ? 1 : 0, pageouverte: settings.pageStatus == UserPageStatus.OPEN ? 1 : 0, bloquecommentaire: settings.blockComments ? 1 : 0, autoriser_post_amis: settings.allowPostsFromFriends ? 1 : 0, autorise_mail: settings.allowMails ? 1 : 0, liste_amis_publique: settings.friendsListPublic ? 1 : 0, sous_repertoire: settings.virtualDirectory, site_web: settings.personnalWebsite, public_note: settings.publicNote } }); } /** * Set (save) new language settings * * @param settings New settings */ public static async SetLanguageSettings(settings: LangSettings) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: settings.id }, set: { lang: settings.lang } }); } /** * Update (set) new security settings * * @param settings New settings */ public static async SetSecuritySettings(settings: SecuritySettings) { await DatabaseHelper.UpdateRows({ table: USER_TABLE, where: { ID: settings.id }, set: { question1: settings.security_question_1, reponse1: settings.security_answer_1, question2: settings.security_question_2, reponse2: settings.security_answer_2 } }); } /** * Export all the data of an account * * @param userID Target user ID */ public static async Export(userID: number) : Promise { const data = new AccountExport({ // General info userID: userID, userInfo: await UserHelper.GetUserInfo(userID), // Export the list of posts postsList: await PostsHelper.ExportAllPostsUser(userID), // Export the list of comments comments: await CommentsHelper.ExportAllUser(userID), // Export user likes likes: await LikesHelper.ExportAllUser(userID), // Export all responses of user to surveys surveyResponses: await SurveyHelper.ExportAllUserResponses(userID), // User movies movies: await MoviesHelper.GetListUser(userID), // Conversation messages allConversationMessages: await ConversationsHelper.ExportAllMessages(userID), // Conversations list conversations: await ConversationsHelper.GetListUser(userID), // Conversations messages (completed after) conversationsMessages: new Map(), // Friends friendsList: await FriendsHelper.GetList(userID), // Groups membership groups: await GroupsHelper.GetListUser(userID), }) // Process conversation messages for(const conv of data.conversations) { data.conversationsMessages[conv.id] = await ConversationsHelper.GetAllMessages(conv.id); } return data; } /** * Remove completely a user account * * @param userID Target user ID */ public static async Delete(userID: number) { /*// Delete all groups memberships await GroupsHelper.DeleteAllUsersGroups(userID); // Delete all user comments await CommentsHelper.DeleteAllUser(userID); // Delete all user posts await PostsHelper.DeleteAllUser(userID); // Delete all responses of user to surveys await SurveyHelper.DeleteAllUserResponses(userID); // Delete all the likes created by the user await LikesHelper.DeleteAllUser(userID); // Delete all user movies await MoviesHelper.DeleteAllUser(userID); // Delete conversation messages await ConversationsHelper.DeleteAllUserMessages(userID); // Remove the user from all its conversations await ConversationsHelper.DeleteAllUserConversations(userID); // Delete all the notifications related with the user await NotificationsHelper.DeleteAllRelatedWithUser(userID); // Delete all user friends, including friendship requests await FriendsHelper.DeleteAllUser(userID); // Delete user account image await AccountImageHelper.Delete(userID); // Delete all the likes on the user page await LikesHelper.DeleteAll(userID, LikesType.USER); // Delete user background image await BackgroundImageHelper.Delete(userID); // TODO : Delete custom user emojies // Delete connections to all services await this.DeleteAllUserLoginTokens(userID); */ // Delete user from the database // WILL BE IMPLEMENTED LATER } }