Separate invite and password reset token
This commit is contained in:
34
src/users.js
34
src/users.js
@@ -11,6 +11,7 @@ exports = module.exports = {
|
||||
list,
|
||||
listPaged,
|
||||
get,
|
||||
getByInviteToken,
|
||||
getByResetToken,
|
||||
getByUsername,
|
||||
getByEmail,
|
||||
@@ -57,7 +58,7 @@ exports = module.exports = {
|
||||
const ORDERED_ROLES = [ exports.ROLE_USER, exports.ROLE_USER_MANAGER, exports.ROLE_ADMIN, exports.ROLE_OWNER ];
|
||||
|
||||
// the avatar field is special and not added here to reduce response sizes
|
||||
const USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'creationTime', 'resetToken', 'displayName',
|
||||
const USERS_FIELDS = [ 'id', 'username', 'email', 'fallbackEmail', 'password', 'salt', 'creationTime', 'inviteToken', 'resetToken', 'displayName',
|
||||
'twoFactorAuthenticationEnabled', 'twoFactorAuthenticationSecret', 'active', 'source', 'role', 'resetTokenCreationTime', 'loginLocationsJson' ].join(',');
|
||||
|
||||
const DEFAULT_GHOST_LIFETIME = 6 * 60 * 60 * 1000; // 6 hours
|
||||
@@ -133,7 +134,7 @@ function validateEmail(email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateResetToken(token) {
|
||||
function validateToken(token) {
|
||||
assert.strictEqual(typeof token, 'string');
|
||||
|
||||
if (token.length !== 64) return new BoxError(BoxError.BAD_FIELD, 'Invalid token'); // 256-bit hex coded token
|
||||
@@ -220,6 +221,7 @@ async function add(email, data, auditSource) {
|
||||
password: Buffer.from(derivedKey, 'binary').toString('hex'),
|
||||
salt: salt.toString('hex'),
|
||||
resetToken: '',
|
||||
inviteToken: '',
|
||||
displayName: displayName,
|
||||
source: source,
|
||||
role: role,
|
||||
@@ -477,7 +479,7 @@ async function getByRole(role) {
|
||||
async function getByResetToken(resetToken) {
|
||||
assert.strictEqual(typeof resetToken, 'string');
|
||||
|
||||
let error = validateResetToken(resetToken);
|
||||
let error = validateToken(resetToken);
|
||||
if (error) throw error;
|
||||
|
||||
const result = await database.query(`SELECT ${USERS_FIELDS} FROM users WHERE resetToken=?`, [ resetToken ]);
|
||||
@@ -486,6 +488,18 @@ async function getByResetToken(resetToken) {
|
||||
return postProcess(result[0]);
|
||||
}
|
||||
|
||||
async function getByInviteToken(inviteToken) {
|
||||
assert.strictEqual(typeof inviteToken, 'string');
|
||||
|
||||
let error = validateToken(inviteToken);
|
||||
if (error) throw error;
|
||||
|
||||
const result = await database.query(`SELECT ${USERS_FIELDS} FROM users WHERE inviteToken=?`, [ inviteToken ]);
|
||||
if (result.length === 0) return null;
|
||||
|
||||
return postProcess(result[0]);
|
||||
}
|
||||
|
||||
async function getByUsername(username) {
|
||||
assert.strictEqual(typeof username, 'string');
|
||||
|
||||
@@ -691,15 +705,13 @@ async function sendInvite(user, options, auditSource) {
|
||||
|
||||
if (user.source) throw new BoxError(BoxError.CONFLICT, 'User is from an external directory');
|
||||
|
||||
const resetToken = hat(256);
|
||||
const resetTokenCreationTime = new Date();
|
||||
const inviteToken = hat(256);
|
||||
|
||||
user.resetToken = resetToken;
|
||||
user.resetTokenCreationTime = resetTokenCreationTime;
|
||||
await update(user, { resetToken, resetTokenCreationTime }, auditSource);
|
||||
user.inviteToken = inviteToken;
|
||||
await update(user, { inviteToken }, auditSource);
|
||||
|
||||
const directoryConfig = await settings.getDirectoryConfig();
|
||||
let inviteLink = `${settings.dashboardOrigin()}/setupaccount.html?resetToken=${user.resetToken}&email=${encodeURIComponent(user.email)}`;
|
||||
let inviteLink = `${settings.dashboardOrigin()}/setupaccount.html?inviteToken=${user.inviteToken}&email=${encodeURIComponent(user.email)}`;
|
||||
|
||||
if (user.username) inviteLink += `&username=${encodeURIComponent(user.username)}`;
|
||||
if (user.displayName) inviteLink += `&displayName=${encodeURIComponent(user.displayName)}`;
|
||||
@@ -718,9 +730,9 @@ async function setupAccount(user, data, auditSource) {
|
||||
const directoryConfig = await settings.getDirectoryConfig();
|
||||
if (directoryConfig.lockUserProfiles) return;
|
||||
|
||||
await update(user, _.pick(data, 'username', 'displayName'), auditSource);
|
||||
await update(user, { username: data.username, displayName: data.displayName, inviteToken: '' }, auditSource);
|
||||
|
||||
await setPassword(user, data.password, auditSource); // setPassword clears the resetToken
|
||||
await setPassword(user, data.password, auditSource);
|
||||
|
||||
const token = { clientId: tokens.ID_WEBADMIN, identifier: user.id, expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS };
|
||||
const result = await tokens.add(token);
|
||||
|
||||
Reference in New Issue
Block a user