diff --git a/databases/_upgrade_private_2.sql b/databases/_upgrade_private_2.sql new file mode 100644 index 0000000..1a70e67 --- /dev/null +++ b/databases/_upgrade_private_2.sql @@ -0,0 +1,13 @@ +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS "userNameLogs" ( + "userID" TEXT NOT NULL, + "newUserName" TEXT NOT NULL, + "oldUserName" TEXT NOT NULL, + "updatedByAdmin" BOOLEAN NOT NULL, + "updatedAt" INTEGER NOT NULL +); + +UPDATE "config" SET value = 2 WHERE key = 'version'; + +COMMIT; \ No newline at end of file diff --git a/src/routes/setUsername.ts b/src/routes/setUsername.ts index fdd99dc..2a0e75b 100644 --- a/src/routes/setUsername.ts +++ b/src/routes/setUsername.ts @@ -1,9 +1,16 @@ import {config} from '../config'; import {Logger} from '../utils/logger'; -import {db} from '../databases/databases'; +import {db, privateDB} from '../databases/databases'; import {getHash} from '../utils/getHash'; import {Request, Response} from 'express'; +async function logUserNameChange(userID: string, newUserName: string, oldUserName: string, updatedByAdmin: boolean): Promise { + return privateDB.prepare('run', + `INSERT INTO "userNameLogs"("userID", "newUserName", "oldUserName", "updatedByAdmin", "updatedAt") VALUES(?, ?, ?, ?, ?)`, + [userID, newUserName, oldUserName, + updatedByAdmin, new Date().getTime()] + ); +} + export async function setUsername(req: Request, res: Response) { let userID = req.query.userID as string; let userName = req.query.username as string; @@ -55,16 +62,20 @@ export async function setUsername(req: Request, res: Response) { try { //check if username is already set - let row = await db.prepare('get', `SELECT count(*) as count FROM "userNames" WHERE "userID" = ?`, [userID]); + let row = await db.prepare('get', `SELECT userName FROM "userNames" WHERE "userID" = ? LIMIT 1`, [userID]); + let oldUserName = ''; - if (row.count > 0) { + if (row.userName && row.userName.length !== 0) { //already exists, update this row + oldUserName = row.userName; await db.prepare('run', `UPDATE "userNames" SET "userName" = ? WHERE "userID" = ?`, [userName, userID]); } else { //add to the db await db.prepare('run', `INSERT INTO "userNames"("userID", "userName") VALUES(?, ?)`, [userID, userName]); } + await logUserNameChange(userID, userName, oldUserName, adminUserIDInput !== undefined); + res.sendStatus(200); } catch (err) { Logger.error(err); diff --git a/test/cases/setUsername.ts b/test/cases/setUsername.ts index ae3a8f5..d82c121 100644 --- a/test/cases/setUsername.ts +++ b/test/cases/setUsername.ts @@ -1,6 +1,6 @@ import fetch from 'node-fetch'; import { Done, getbaseURL } from '../utils'; -import { db } from '../../src/databases/databases'; +import { db, privateDB } from '../../src/databases/databases'; import { getHash } from '../../src/utils/getHash'; const adminPrivateUserID = 'testUserId'; @@ -21,6 +21,7 @@ const username07 = 'Username 07'; async function addUsername(userID: string, userName: string, locked = 0) { await db.prepare('run', 'INSERT INTO "userNames" ("userID", "userName", "locked") VALUES(?, ?, ?)', [userID, userName, locked]); + await addLogUserNameChange(userID, userName); } async function getUsername(userID: string) { @@ -31,6 +32,40 @@ async function getUsername(userID: string) { return row.userName; } +async function addLogUserNameChange(userID: string, newUserName: string, oldUserName: string = '') { + privateDB.prepare('run', + `INSERT INTO "userNameLogs"("userID", "newUserName", "oldUserName", "updatedAt", "updatedByAdmin") VALUES(?, ?, ?, ?, ?)`, + [getHash(userID), newUserName, oldUserName, new Date().getTime(), + true] + ); +} + +async function getLastLogUserNameChange(userID: string) { + return privateDB.prepare('get', `SELECT * FROM "userNameLogs" WHERE "userID" = ? ORDER BY "updatedAt" DESC LIMIT 1`, [getHash(userID)]); +} + +function wellFormatUserName(userName: string) { + return userName.replace(/[\u0000-\u001F\u007F-\u009F]/g, ''); +} + +async function testUserNameChangelog(userID: string, newUserName: string, oldUserName: string, byAdmin: boolean, done: Done) { + + const log = await getLastLogUserNameChange(userID); + + if (newUserName !== log.newUserName) { + return done(`UserID '${userID}' incorrect log on newUserName: ${newUserName} !== ${log.newUserName}`); + } + + if (oldUserName !== log.oldUserName) { + return done(`UserID '${userID}' incorrect log on oldUserName: ${oldUserName} !== ${log.oldUserName}`); + } + + if (+byAdmin !== log.updatedByAdmin) { + return done(`UserID '${userID}' incorrect log on updatedByAdmin: ${byAdmin} !== ${log.updatedByAdmin}`); + } + + return done(); +} + describe('setUsername', () => { before(async () => { await addUsername(getHash(user01PrivateUserID), username01, 0); @@ -46,9 +81,11 @@ describe('setUsername', () => { fetch(`${getbaseURL()}/api/setUsername?userID=${user01PrivateUserID}&username=Changed%20Username`, { method: 'POST', }) - .then(res => { + .then(async res => { if (res.status !== 200) done(`Status code was ${res.status}`); - else done(); // pass + else { + testUserNameChangelog(user01PrivateUserID, decodeURIComponent('Changed%20Username'), username01, false, done); + } }) .catch(err => done(`couldn't call endpoint`)); }); @@ -113,7 +150,9 @@ describe('setUsername', () => { .then(async res => { const username = await getUsername(getHash(user03PrivateUserID)); if (username !== newUsername) done(`Username did not change`); - else done(); + else { + testUserNameChangelog(user03PrivateUserID, newUsername, username03, false, done); + } }) .catch(err => done(`couldn't call endpoint`)); }); @@ -139,7 +178,7 @@ describe('setUsername', () => { .then(async res => { const username = await getUsername(getHash(user05PrivateUserID)); if (username === newUsername) done(`Username contains unicode control characters`); - else done(); + else testUserNameChangelog(user05PrivateUserID, wellFormatUserName(newUsername), username05, false, done); }) .catch(err => done(`couldn't call endpoint`)); }); @@ -164,7 +203,7 @@ describe('setUsername', () => { .then(async res => { const username = await getUsername(getHash(user06PrivateUserID)); if (username !== newUsername) done(`Failed to change username from '${username06}' to '${newUsername}'`); - else done(); + else testUserNameChangelog(user06PrivateUserID, newUsername, username06, true, done); }) .catch(err => done(`couldn't call endpoint`)); }); @@ -175,9 +214,9 @@ describe('setUsername', () => { method: 'POST', }) .then(async res => { - const username = await getUsername(getHash(user06PrivateUserID)); - if (username !== newUsername) done(`Failed to change username from '${username06}' to '${newUsername}'`); - else done(); + const username = await getUsername(getHash(user07PrivateUserID)); + if (username !== newUsername) done(`Failed to change username from '${username07}' to '${newUsername}'`); + else testUserNameChangelog(user07PrivateUserID, newUsername, username07, true, done); }) .catch(err => done(`couldn't call endpoint`)); });