diff --git a/src/routes/test/users-test.js b/src/routes/test/users-test.js index bb0d0140b..a740c0fa6 100644 --- a/src/routes/test/users-test.js +++ b/src/routes/test/users-test.js @@ -76,6 +76,15 @@ describe('Users API', function () { expect(response.statusCode).to.equal(400); }); + it('cannot create user with non email fallbackEmail', async function () { + const response = await superagent.post(`${serverUrl}/api/v1/users`) + .query({ access_token: owner.token }) + .send({ username: user2.username, email: user2.email, fallbackEmail: 'notanemail' }) + .ok(() => true); + + expect(response.statusCode).to.equal(400); + }); + it('create second user succeeds', async function () { const response = await superagent.post(`${serverUrl}/api/v1/users`) .query({ access_token: owner.token }) @@ -299,6 +308,15 @@ describe('Users API', function () { expect(response.statusCode).to.equal(400); }); + it('change fallbackEmail fails due to invalid email', async function () { + const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`) + .query({ access_token: owner.token }) + .send({ fallbackEmail: 'newemail@cloudron' }) + .ok(() => true); + + expect(response.statusCode).to.equal(400); + }); + it('change user succeeds without email nor displayName', async function () { const response = await superagent.post(`${serverUrl}/api/v1/users/${user2.id}`) .query({ access_token: owner.token }) diff --git a/src/routes/users.js b/src/routes/users.js index 7a7e8b34f..a08e9c751 100644 --- a/src/routes/users.js +++ b/src/routes/users.js @@ -43,6 +43,7 @@ async function add(req, res, next) { if (typeof req.body.email !== 'string') return next(new HttpError(400, 'email must be string')); if ('username' in req.body && typeof req.body.username !== 'string') return next(new HttpError(400, 'username must be string')); + if ('fallbackEmail' in req.body && typeof req.body.fallbackEmail !== 'string') return next(new HttpError(400, 'fallbackEmail must be string')); if ('displayName' in req.body && typeof req.body.displayName !== 'string') return next(new HttpError(400, 'displayName must be string')); if ('password' in req.body && typeof req.body.password !== 'string') return next(new HttpError(400, 'password must be string')); if ('role' in req.body) { @@ -54,8 +55,9 @@ async function add(req, res, next) { const email = req.body.email; const username = 'username' in req.body ? req.body.username : null; const displayName = req.body.displayName || ''; + const fallbackEmail = req.body.fallbackEmail || ''; - const [error, id] = await safe(users.add(email, { username, password, displayName, invitor: req.user, role: req.body.role || users.ROLE_USER }, AuditSource.fromRequest(req))); + const [error, id] = await safe(users.add(email, { username, password, displayName, fallbackEmail, invitor: req.user, role: req.body.role || users.ROLE_USER }, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, { id })); diff --git a/src/test/common.js b/src/test/common.js index 0e6123bed..be2286984 100644 --- a/src/test/common.js +++ b/src/test/common.js @@ -63,7 +63,7 @@ const admin = { username: 'testadmin', password: 'secret123', email: 'admin@me.com', - fallbackEmail: 'admin@me.com', + fallbackEmail: 'admin@external.com', salt: 'morton', createdAt: 'sometime back', resetToken: '', @@ -80,7 +80,7 @@ const user = { username: 'user', password: '123secret', email: 'user@me.com', - fallbackEmail: 'user@me.com', + fallbackEmail: 'user@external.com', role: 'user', salt: 'morton', createdAt: 'sometime back', diff --git a/src/test/users-test.js b/src/test/users-test.js index f6e1c475d..071ec5d6c 100644 --- a/src/test/users-test.js +++ b/src/test/users-test.js @@ -83,6 +83,12 @@ describe('User', function () { expect(error.reason).to.equal(BoxError.BAD_FIELD); }); + it('fails because fallbackEmail is not an email', async function () { + const user = Object.assign({}, admin, { fallbackEmail: 'notanemail' }); + const [error] = await safe(users.add(user.email, user, auditSource)); + expect(error.reason).to.equal(BoxError.BAD_FIELD); + }); + it('can add user', async function () { const id = await users.add(admin.email, admin, auditSource); admin.id = id; @@ -403,7 +409,7 @@ describe('User', function () { const result = await users.get(admin.id); expect(result.id).to.equal(admin.id); expect(result.email).to.equal(admin.email.toLowerCase()); - expect(result.fallbackEmail).to.equal(admin.email.toLowerCase()); + expect(result.fallbackEmail).to.equal(admin.fallbackEmail.toLowerCase()); expect(result.username).to.equal(admin.username.toLowerCase()); expect(result.displayName).to.equal(admin.displayName); }); diff --git a/src/users.js b/src/users.js index 76be96d8e..0fe7a57ef 100644 --- a/src/users.js +++ b/src/users.js @@ -180,8 +180,9 @@ async function add(email, data, auditSource) { assert(data.username === null || typeof data.username === 'string'); assert(data.password === null || typeof data.password === 'string'); assert.strictEqual(typeof data.displayName, 'string'); + assert.strictEqual(typeof data.fallbackEmail, 'string'); - let { username, password, displayName } = data; + let { username, password, displayName, fallbackEmail } = data; const source = data.source || ''; // empty is local user const role = data.role || exports.ROLE_USER; @@ -204,6 +205,12 @@ async function add(email, data, auditSource) { error = validateEmail(email); if (error) throw error; + fallbackEmail = fallbackEmail.toLowerCase(); + if (fallbackEmail) { + let error = validateEmail(fallbackEmail); + if (error) throw error; + } + error = validateDisplayName(displayName); if (error) throw error; @@ -222,7 +229,7 @@ async function add(email, data, auditSource) { id: 'uid-' + uuid.v4(), username: username, email: email, - fallbackEmail: email, + fallbackEmail: fallbackEmail, password: Buffer.from(derivedKey, 'binary').toString('hex'), salt: salt.toString('hex'), resetToken: '', @@ -701,7 +708,7 @@ async function createOwner(email, username, password, displayName, auditSource) const activated = await isActivated(); if (activated) throw new BoxError(BoxError.ALREADY_EXISTS, 'Cloudron already activated'); - return await add(email, { username, password, displayName, role: exports.ROLE_OWNER }, auditSource); + return await add(email, { username, password, fallbackEmail: '', displayName, role: exports.ROLE_OWNER }, auditSource); } async function sendInvite(user, options, auditSource) {