From e4ec0a0de7653a3c4bd24aad4bffee3adc3155c4 Mon Sep 17 00:00:00 2001 From: Brian Warner <warner@lothar.com> Date: Mon, 30 Jul 2012 17:38:31 -0700 Subject: [PATCH] mysql schema: use TIMESTAMP for lastPasswordReset, not BIGINT Since MySQL TIMESTAMP is quantized to whole seconds, also change tests to add a 2s stall before changing the password, to make sure lastPasswordReset gets a new value. --- lib/db/json.js | 4 ++-- lib/db/mysql.js | 14 +++++++------- tests/password-update-test.js | 32 +++++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/db/json.js b/lib/db/json.js index 946f74366..6c9555b65 100644 --- a/lib/db/json.js +++ b/lib/db/json.js @@ -33,7 +33,7 @@ var dbPath = path.join(configuration.get('var_path'), "authdb.json"); * { * id: <numerical user id> * password: "somepass", - * lastPasswordReset: 123456, (ms-since-epoch, integer) + * lastPasswordReset: 123456, (seconds-since-epoch, integer) * emails: { * "lloyd@hilaiel.com": { * type: 'secondary' @@ -43,7 +43,7 @@ var dbPath = path.join(configuration.get('var_path'), "authdb.json"); * ] */ -function now() { return new Date().getTime(); } +function now() { return Math.floor(new Date().getTime() / 1000); } function getNextUserID() { var max = 1; diff --git a/lib/db/mysql.js b/lib/db/mysql.js index 3ed307250..62839715c 100644 --- a/lib/db/mysql.js +++ b/lib/db/mysql.js @@ -65,7 +65,7 @@ const schemas = [ "CREATE TABLE IF NOT EXISTS user (" + "id BIGINT AUTO_INCREMENT PRIMARY KEY," + "passwd CHAR(64)," + - "lastPasswordReset BIGINT" + + "lastPasswordReset TIMESTAMP DEFAULT 0 NOT NULL" + ") ENGINE=InnoDB;", "CREATE TABLE IF NOT EXISTS email (" + @@ -89,7 +89,7 @@ const schemas = [ ") ENGINE=InnoDB;", ]; -function now() { return new Date().getTime().toString(); } +function now() { return Math.floor(new Date().getTime() / 1000); } // log an unexpected database error function logUnexpectedError(detail) { @@ -371,7 +371,7 @@ exports.completeCreateUser = function(secret, cb) { // we're creating a new account, add appropriate entries into user and email tables. client.query( - "INSERT INTO user(passwd, lastPasswordReset) VALUES(?,?)", + "INSERT INTO user(passwd, lastPasswordReset) VALUES(?,FROM_UNIXTIME(?))", [ o.passwd, now() ], function(err, info) { if (err) return cb(err); @@ -451,7 +451,7 @@ exports.addPrimaryEmailToAccount = function(uid, emailToAdd, cb) { exports.createUserWithPrimaryEmail = function(email, cb) { // create a new user acct with no password client.query( - "INSERT INTO user(lastPasswordReset) VALUES(?)", + "INSERT INTO user(lastPasswordReset) VALUES(FROM_UNIXTIME(?))", [ now() ], function(err, info) { if (err) return cb(err); @@ -515,7 +515,7 @@ exports.checkAuth = function(uid, cb) { exports.lastPasswordReset = function(uid, cb) { client.query( - 'SELECT lastPasswordReset FROM user WHERE id = ?', + 'SELECT UNIX_TIMESTAMP(lastPasswordReset) AS lastPasswordReset FROM user WHERE id = ?', [ uid ], function (err, rows) { cb(err, (rows && rows.length == 1) ? rows[0].lastPasswordReset : undefined); @@ -524,7 +524,7 @@ exports.lastPasswordReset = function(uid, cb) { exports.updatePassword = function(uid, hash, invalidateSessions, cb) { var query = invalidateSessions ? - 'UPDATE user SET passwd = ?, lastPasswordReset = ? WHERE id = ?' : + 'UPDATE user SET passwd = ?, lastPasswordReset = FROM_UNIXTIME(?) WHERE id = ?' : 'UPDATE user SET passwd = ? WHERE id = ?'; var args = invalidateSessions ? [ hash, now(), uid ] : [ hash, uid ]; client.query(query, args, @@ -591,7 +591,7 @@ exports.cancelAccount = function(uid, cb) { exports.addTestUser = function(email, hash, cb) { client.query( - "INSERT INTO user(passwd, lastPasswordReset) VALUES(?)", + "INSERT INTO user(passwd, lastPasswordReset) VALUES(FROM_UNIXTIME(?))", [ hash, now() ], function(err, info) { if (err) return cb(err); diff --git a/tests/password-update-test.js b/tests/password-update-test.js index 173794d51..e2d9b77a3 100755 --- a/tests/password-update-test.js +++ b/tests/password-update-test.js @@ -141,13 +141,31 @@ suite.addBatch({ }); suite.addBatch({ - "updating the password": { - topic: wsapi.post('/wsapi/update_password', { - oldpass: OLD_PASSWORD, - newpass: NEW_PASSWORD - }), - "works as expected": function(err, r) { - assert.strictEqual(JSON.parse(r.body).success, true); + "after waiting for lastPasswordReset's now() to increment": { + topic: function() { + // we introduce a 2s delay here to ensure that the now() call in + // lib/db/{json,mysql}.js will return a different value than it did + // during complete_user_creation(), thus expiring the old session still + // hanging out in context2. now() returns an integer + // seconds-since-epoch, so the shortest delay that will reliably get a + // different result is 1.0s+epsilon (depending upon the resolution of + // the system clock). To avoid this stall (and make the test suite run + // 2s faster), either: + // 1: change now() to include a mutable offset, expose a + // db.addNowOffset() to "accelerate the universe", have this code + // add 1s instead of using setTimeout. Or: + // 2: add a db function to modify (increment) lastPasswordReset by 1s, + // have this code call it instead of using setTimeout + setTimeout(this.callback, 2000); + }, + "updating the password": { + topic: wsapi.post('/wsapi/update_password', { + oldpass: OLD_PASSWORD, + newpass: NEW_PASSWORD + }), + "works as expected": function(err, r) { + assert.strictEqual(JSON.parse(r.body).success, true); + } } } }); -- GitLab