Add name_dedupe column instead of using LIKE kludge for similar-looking names

This commit is contained in:
Calvin Montgomery 2017-03-11 17:09:50 -08:00
parent 4701e767b6
commit f8183bea1b
5 changed files with 80 additions and 17 deletions

View File

@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
"version": "3.31.0",
"version": "3.32.0",
"repository": {
"url": "http://github.com/calzoneman/sync"
},

View File

@ -48,6 +48,7 @@ module.exports.init = function () {
global_ipbans = {};
module.exports.users = require("./database/accounts");
module.exports.channels = require("./database/channels");
module.exports.pool = pool;
};
/**

View File

@ -7,15 +7,6 @@ var Logger = require("../logger");
var registrationLock = {};
var blackHole = function () { };
/**
* Replaces look-alike characters with "_" (single character wildcard) for
* use in LIKE queries. This prevents guests from taking names that look
* visually identical to existing names in certain fonts.
*/
function wildcardSimilarChars(name) {
return name.replace(/_/g, "\\_").replace(/[Il1oO0]/g, "_");
}
function parseProfile(data) {
try {
var profile = JSON.parse(data.profile);
@ -31,12 +22,23 @@ module.exports = {
init: function () {
},
/**
* Convert a username for deduplication purposes.
* Collapses visibily similar characters into a single character.
* @param name
*/
dedupeUsername: function dedupeUsername(name) {
return name.replace(/[Il1]/ig, '1')
.replace(/[o0]/ig, '0')
.replace(/[_-]/g, '_');
},
/**
* Check if a username is taken
*/
isUsernameTaken: function (name, callback) {
db.query("SELECT name FROM `users` WHERE name LIKE ? ESCAPE '\\\\'",
[wildcardSimilarChars(name)],
db.query("SELECT name FROM `users` WHERE name = ? or name_dedupe = ?",
[name, module.exports.dedupeUsername(name)],
function (err, rows) {
if (err) {
callback(err, true);
@ -175,10 +177,10 @@ module.exports = {
}
db.query("INSERT INTO `users` " +
"(`name`, `password`, `global_rank`, `email`, `profile`, `ip`, `time`)" +
"(`name`, `password`, `global_rank`, `email`, `profile`, `ip`, `time`, `name_dedupe`)" +
" VALUES " +
"(?, ?, ?, ?, '', ?, ?)",
[name, hash, 1, email, ip, Date.now()],
"(?, ?, ?, ?, '', ?, ?, ?)",
[name, hash, 1, email, ip, Date.now(), module.exports.dedupeUsername(name)],
function (err, res) {
delete registrationLock[lname];
if (err) {

View File

@ -8,8 +8,10 @@ const TBL_USERS = "" +
"`profile` TEXT CHARACTER SET utf8mb4 NOT NULL," +
"`ip` VARCHAR(39) NOT NULL," +
"`time` BIGINT NOT NULL," +
"`name_dedupe` VARCHAR(20) DEFAULT NULL," +
"PRIMARY KEY(`id`)," +
"UNIQUE(`name`)) " +
"UNIQUE(`name`)," +
"UNIQUE(`name_dedupe`)) " +
"CHARACTER SET utf8";
const TBL_CHANNELS = "" +

View File

@ -1,8 +1,9 @@
var db = require("../database");
var Logger = require("../logger");
var Q = require("q");
import Promise from 'bluebird';
const DB_VERSION = 7;
const DB_VERSION = 9;
var hasUpdates = [];
module.exports.checkVersion = function () {
@ -58,6 +59,10 @@ function update(version, cb) {
fixCustomEmbeds(cb);
} else if (version < 7) {
fixCustomEmbedsInUserPlaylists(cb);
} else if (version < 8) {
addUsernameDedupeColumn(cb);
} else if (version < 9) {
populateUsernameDedupeColumn(cb);
}
}
@ -330,3 +335,56 @@ function fixCustomEmbedsInUserPlaylists(cb) {
Logger.errlog.log(err.stack);
});
}
function addUsernameDedupeColumn(cb) {
Logger.syslog.log("Adding name_dedupe column on the users table");
db.query("ALTER TABLE users ADD COLUMN name_dedupe VARCHAR(20) UNIQUE DEFAULT NULL", (error) => {
if (error) {
Logger.errlog.log(`Unable to add name_dedupe column: ${error}`);
} else {
cb();
}
});
}
function populateUsernameDedupeColumn(cb) {
const dbUsers = require("./accounts");
Logger.syslog.log("Populating name_dedupe column on the users table");
db.query("SELECT id, name FROM users WHERE name_dedupe IS NULL", (err, rows) => {
if (err) {
Logger.errlog.log("Unable to perform database upgrade to add dedupe column: " + err);
return;
}
Promise.map(rows, row => {
return new Promise((resolve, reject) => {
db.pool.getConnection((error, conn) => {
if (error) {
reject(error);
return;
}
const dedupedName = dbUsers.dedupeUsername(row.name);
Logger.syslog.log(`Deduping [${row.name}] as [${dedupedName}]`);
conn.query("UPDATE users SET name_dedupe = ? WHERE id = ?", [dedupedName, row.id], (error, res) => {
conn.release();
if (error) {
if (error.errno === 1062) {
Logger.syslog.log(`WARNING: could not set name_dedupe for [${row.name}] due to an existing row for [${dedupedName}]`);
resolve();
} else {
reject(error);
}
} else {
resolve();
}
});
});
});
}, { concurrency: 10 }).then(() => {
cb();
}).catch(error => {
Logger.errlog.log("Unable to perform database upgrade to add dedupe column: " + (error.stack ? error.stack : error));
})
});
}