diff --git a/src/routes/cloudron.js b/src/routes/cloudron.js index 97ff6d857..e905d3f7c 100644 --- a/src/routes/cloudron.js +++ b/src/routes/cloudron.js @@ -124,34 +124,20 @@ function setupAccount(req, res, next) { if (!req.body.resetToken || typeof req.body.resetToken !== 'string') return next(new HttpError(400, 'resetToken must be a non-empty string')); if (!req.body.password || typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be a non-empty string')); - if (!req.body.username || typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be a non-empty string')); - if (!req.body.displayName || typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be a non-empty string')); + + // only sent if profile is not locked + if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be a non-empty string')); + if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be a non-empty string')); 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)); - if (error && error.reason === BoxError.NOT_FOUND) return next(new HttpError(404, 'No such user')); - if (error) return next(new HttpError(500, error)); + users.setupAccount(userObject, req.body, auditSource.fromRequest(req), function (error, accessToken) { + if (error) return next(BoxError.toHttpError(error)); - userObject.username = req.body.username; - userObject.displayName = req.body.displayName; - - // setPassword clears the resetToken - users.setPassword(userObject, req.body.password, function (error) { - if (error && error.reason === BoxError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); - - tokens.add(tokens.ID_WEBADMIN, userObject.id, Date.now() + constants.DEFAULT_TOKEN_EXPIRATION, {}, function (error, result) { - if (error) return next(new HttpError(500, error)); - - next(new HttpSuccess(201, { accessToken: result.accessToken })); - }); - }); + next(new HttpSuccess(201, { accessToken })); }); }); } diff --git a/src/users.js b/src/users.js index 7378962c7..02520a97a 100644 --- a/src/users.js +++ b/src/users.js @@ -29,6 +29,8 @@ exports = module.exports = { sendPasswordResetByIdentifier: sendPasswordResetByIdentifier, + setupAccount, + count: count, AP_MAIL: 'mail', @@ -63,6 +65,7 @@ let assert = require('assert'), safe = require('safetydance'), settings = require('./settings.js'), speakeasy = require('speakeasy'), + tokens = require('./tokens.js'), userdb = require('./userdb.js'), uuid = require('uuid'), validator = require('validator'), @@ -566,11 +569,12 @@ function getOwner(callback) { }); } -function inviteLink(user) { +function inviteLink(user, directoryConfig) { let link = `${settings.adminOrigin()}/setupaccount.html?resetToken=${user.resetToken}&email=${encodeURIComponent(user.email)}`; if (user.username) link += `&username=${encodeURIComponent(user.username)}`; if (user.displayName) link += `&displayName=${encodeURIComponent(user.displayName)}`; + if (directoryConfig.lockUserProfiles) link += '&profileLocked=true'; return link; } @@ -583,11 +587,16 @@ function createInvite(user, callback) { const resetToken = hat(256), resetTokenCreationTime = new Date(); - userdb.update(user.id, { resetToken, resetTokenCreationTime }, function (error) { + settings.getDirectoryConfig(function (error, directoryConfig) { if (error) return callback(error); - user.resetToken = resetToken; - callback(null, { resetToken, inviteLink: inviteLink(user) }); + userdb.update(user.id, { resetToken, resetTokenCreationTime }, function (error) { + if (error) return callback(error); + + user.resetToken = resetToken; + + callback(null, { resetToken, inviteLink: inviteLink(user, directoryConfig) }); + }); }); } @@ -599,9 +608,43 @@ function sendInvite(user, options, callback) { if (user.source) return callback(new BoxError(BoxError.CONFLICT, 'User is from an external directory')); if (!user.resetToken) return callback(new BoxError(BoxError.CONFLICT, 'Must generate resetToken to send invitation')); - mailer.sendInvite(user, options.invitor || null, inviteLink(user)); + settings.getDirectoryConfig(function (error, directoryConfig) { + if (error) return callback(error); - callback(null); + mailer.sendInvite(user, options.invitor || null, inviteLink(user, directoryConfig)); + + callback(null); + }); +} + +function setupAccount(user, data, auditSource, callback) { + assert.strictEqual(typeof user, 'object'); + assert.strictEqual(typeof data, 'object'); + assert(auditSource && typeof auditSource === 'object'); + assert.strictEqual(typeof callback, 'function'); + + settings.getDirectoryConfig(function (error, directoryConfig) { + if (error) return callback(error); + + const updateFunc = (done) => { + if (directoryConfig.lockUserProfiles) return done(); + update(user, _.pick(data, 'username', 'displayName'), auditSource, done); + }; + + updateFunc(function (error) { + if (error) return callback(error); + + setPassword(user, data.password, function (error) { // setPassword clears the resetToken + if (error) return callback(error); + + tokens.add(tokens.ID_WEBADMIN, user.id, Date.now() + constants.DEFAULT_TOKEN_EXPIRATION, {}, function (error, result) { + if (error) return callback(error); + + callback(null, result.accessToken); + }); + }); + }); + }); } function setTwoFactorAuthenticationSecret(userId, callback) {