Commit 936f456c authored by Girish Ramakrishnan's avatar Girish Ramakrishnan

make reset tokens only valid for a day

fixes #563

mysql timestamps cannot be null. it will become current timestamp when
set as null
parent 5d6a02f7
......@@ -1861,4 +1861,5 @@
* mail: Fix listing of mailboxes and aliases in the UI
* branding: fix login page title
* Only a Cloudron owner can install/update/exec apps with the docker addon
* security: reset tokens are only valid for a day
'use strict';
exports.up = function(db, callback) {
db.runSql('ALTER TABLE users ADD COLUMN resetTokenCreationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP', function (error) {
if (error) console.error(error);
callback(error);
});
};
exports.down = function(db, callback) {
db.runSql('ALTER TABLE users DROP COLUMN resetTokenCreationTime', function (error) {
if (error) console.error(error);
callback(error);
});
};
......@@ -29,6 +29,7 @@ CREATE TABLE IF NOT EXISTS users(
source VARCHAR(128) DEFAULT "",
role VARCHAR(32),
resetToken VARCHAR(128) DEFAULT "",
resetTokenCreationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
active BOOLEAN DEFAULT 1,
PRIMARY KEY(id));
......
......@@ -102,6 +102,7 @@ function passwordReset(req, res, next) {
users.getByResetToken(req.body.resetToken, function (error, userObject) {
if (error) return next(new HttpError(401, 'Invalid resetToken'));
if (Date.now() - userObject.resetTokenCreationTime > 24 * 60 * 60 * 1000) return next(new HttpError(401, 'Token expired'));
if (!userObject.username) return next(new HttpError(409, 'No username set'));
// setPassword clears the resetToken
......@@ -130,6 +131,8 @@ function setupAccount(req, res, next) {
users.getByResetToken(req.body.resetToken, function (error, userObject) {
if (error) return next(new HttpError(401, 'Invalid Reset Token'));
if (Date.now() - userObject.resetTokenCreationTime > 24 * 60 * 60 * 1000) return next(new HttpError(401, 'Token expired'));
users.update(userObject, { username: req.body.username, displayName: req.body.displayName }, auditSource.fromRequest(req), function (error) {
if (error && error.reason === BoxError.ALREADY_EXISTS) return next(new HttpError(409, 'Username already used'));
if (error && error.reason === BoxError.BAD_FIELD) return next(new HttpError(400, error.message));
......
......@@ -40,7 +40,8 @@ var USER_0 = {
twoFactorAuthenticationSecret: '',
role: 'user',
active: true,
source: ''
source: '',
resetTokenCreationTime: Date.now()
};
var USER_1 = {
......@@ -58,7 +59,8 @@ var USER_1 = {
twoFactorAuthenticationSecret: '',
role: 'user',
active: true,
source: ''
source: '',
resetTokenCreationTime: Date.now()
};
var USER_2 = {
......@@ -76,7 +78,8 @@ var USER_2 = {
twoFactorAuthenticationSecret: '',
role: 'user',
active: true,
source: ''
source: '',
resetTokenCreationTime: Date.now()
};
const DOMAIN_0 = {
......@@ -980,7 +983,7 @@ describe('database', function () {
appdb.get(APP_0.id, function (error, result) {
expect(error).to.be(null);
expect(result).to.be.an('object');
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime'])).to.be.eql(APP_0);
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_0);
done();
});
});
......@@ -1020,7 +1023,7 @@ describe('database', function () {
appdb.get(APP_0.id, function (error, result) {
expect(error).to.be(null);
expect(result).to.be.an('object');
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime'])).to.be.eql(APP_0);
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime','resetTokenCreationTime'])).to.be.eql(APP_0);
done();
});
});
......@@ -1030,7 +1033,7 @@ describe('database', function () {
appdb.getByHttpPort(APP_0.httpPort, function (error, result) {
expect(error).to.be(null);
expect(result).to.be.an('object');
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime'])).to.be.eql(APP_0);
expect(_.omit(result, ['creationTime', 'updateTime', 'ts', 'healthTime','resetTokenCreationTime'])).to.be.eql(APP_0);
done();
});
});
......@@ -1055,8 +1058,8 @@ describe('database', function () {
expect(error).to.be(null);
expect(result).to.be.an(Array);
expect(result.length).to.be(2);
expect(_.omit(result[0], ['creationTime', 'updateTime','ts', 'healthTime'])).to.be.eql(APP_0);
expect(_.omit(result[1], ['creationTime', 'updateTime','ts', 'healthTime'])).to.be.eql(APP_1);
expect(_.omit(result[0], ['creationTime', 'updateTime','ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_0);
expect(_.omit(result[1], ['creationTime', 'updateTime','ts', 'healthTime', 'resetTokenCreationTime'])).to.be.eql(APP_1);
done();
});
});
......
......@@ -29,7 +29,7 @@ var assert = require('assert'),
mysql = require('mysql');
var USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'createdAt', 'modifiedAt', 'resetToken', 'displayName',
'twoFactorAuthenticationEnabled', 'twoFactorAuthenticationSecret', 'active', 'source', 'role' ].join(',');
'twoFactorAuthenticationEnabled', 'twoFactorAuthenticationSecret', 'active', 'source', 'role', 'resetTokenCreationTime' ].join(',');
var APP_PASSWORD_FIELDS = [ 'id', 'name', 'userId', 'identifier', 'hashedPassword', 'creationTime' ].join(',');
......
......@@ -495,10 +495,11 @@ function resetPasswordByIdentifier(identifier, callback) {
getter(identifier.toLowerCase(), function (error, result) {
if (error) return callback(error);
let resetToken = hat(256);
let resetToken = hat(256), resetTokenCreationTime = new Date();
result.resetToken = resetToken;
result.resetTokenCreationTime = resetTokenCreationTime;
userdb.update(result.id, { resetToken }, function (error) {
userdb.update(result.id, { resetToken, resetTokenCreationTime }, function (error) {
if (error) return callback(error);
mailer.passwordReset(result);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment