diff --git a/CHANGES b/CHANGES index d4b733325..a8d44603f 100644 --- a/CHANGES +++ b/CHANGES @@ -2260,4 +2260,5 @@ * clone: copy over redis memory limit * namecheap: fix bug where records were not removed * add UI to disable 2FA of a user +* mail: add active flag to mailboxes and lists diff --git a/migrations/20210415030645-mailboxes-add-active.js b/migrations/20210415030645-mailboxes-add-active.js new file mode 100644 index 000000000..942a48310 --- /dev/null +++ b/migrations/20210415030645-mailboxes-add-active.js @@ -0,0 +1,17 @@ +'use strict'; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE mailboxes ADD COLUMN active BOOLEAN DEFAULT 1', function (error) { + if (error) return callback(error); + + callback(); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE mailboxes DROP COLUMN active', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + diff --git a/migrations/schema.sql b/migrations/schema.sql index 90a5f145a..1b8d060bd 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -191,6 +191,7 @@ CREATE TABLE IF NOT EXISTS mailboxes( membersOnly BOOLEAN DEFAULT false, creationTime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, domain VARCHAR(128), + active BOOLEAN DEFAULT 1, FOREIGN KEY(domain) REFERENCES mail(domain), FOREIGN KEY(aliasDomain) REFERENCES mail(domain), diff --git a/src/cloudron.js b/src/cloudron.js index b74b5a040..884bb008f 100644 --- a/src/cloudron.js +++ b/src/cloudron.js @@ -52,7 +52,7 @@ const apps = require('./apps.js'), tasks = require('./tasks.js'), users = require('./users.js'); -var REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh'); +const REBOOT_CMD = path.join(__dirname, 'scripts/reboot.sh'); const NOOP_CALLBACK = function (error) { if (error) debug(error); }; diff --git a/src/ldap.js b/src/ldap.js index 5de827b64..64725542e 100644 --- a/src/ldap.js +++ b/src/ldap.js @@ -264,7 +264,9 @@ function mailboxSearch(req, res, next) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); - var obj = { + if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString())); + + let obj = { dn: req.dn.toString(), attributes: { objectclass: ['mailbox'], @@ -289,10 +291,11 @@ function mailboxSearch(req, res, next) { var domain = req.dn.rdns[0].attrs.domain.value.toLowerCase(); mailboxdb.listMailboxes(domain, 1, 1000, function (error, mailboxes) { - if (error) return next(new ldap.OperationsError(error.toString())); - var results = []; + mailboxes = mailboxes.filter(m => m.active); + + let results = []; // send mailbox objects mailboxes.forEach(function (mailbox) { @@ -323,7 +326,9 @@ function mailboxSearch(req, res, next) { mailboxdb.listAllMailboxes(1, 1000, function (error, mailboxes) { if (error) return next(new ldap.OperationsError(error.toString())); - var results = []; + mailboxes = mailboxes.filter(m => m.active); + + let results = []; // send mailbox objects async.eachSeries(mailboxes, function (mailbox, callback) { @@ -382,10 +387,12 @@ function mailAliasSearch(req, res, next) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); + if (!alias.active) return next(new ldap.NoSuchObjectError(req.dn.toString())); // there is no way to disable an alias. this is just here for completeness + // https://wiki.debian.org/LDAP/MigrationTools/Examples // https://docs.oracle.com/cd/E19455-01/806-5580/6jej518pp/index.html // member is fully qualified - https://docs.oracle.com/cd/E19957-01/816-6082-10/chap4.doc.html#43314 - var obj = { + let obj = { dn: req.dn.toString(), attributes: { objectclass: ['nisMailAlias'], @@ -421,6 +428,8 @@ function mailingListSearch(req, res, next) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.toString())); + if (!list.active) return next(new ldap.NoSuchObjectError(req.dn.toString())); + // http://ldapwiki.willeke.com/wiki/Original%20Mailgroup%20Schema%20From%20Netscape // members are fully qualified (https://docs.oracle.com/cd/E19444-01/816-6018-10/groups.htm#13356) var obj = { @@ -537,6 +546,8 @@ function authenticateUserMailbox(req, res, next) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.message)); + if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString())); + verifyMailboxPassword(mailbox, req.credentials || '', function (error, result) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString())); @@ -677,6 +688,8 @@ function authenticateMailAddon(req, res, next) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error) return next(new ldap.OperationsError(error.message)); + if (!mailbox.active) return next(new ldap.NoSuchObjectError(req.dn.toString())); + verifyMailboxPassword(mailbox, req.credentials || '', function (error, result) { if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError(req.dn.toString())); if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new ldap.InvalidCredentialsError(req.dn.toString())); diff --git a/src/mail.js b/src/mail.js index b17a90163..e9f855fc4 100644 --- a/src/mail.js +++ b/src/mail.js @@ -39,7 +39,7 @@ exports = module.exports = { listMailboxes, getMailbox, addMailbox, - updateMailboxOwner, + updateMailbox, removeMailbox, getAliases, @@ -1182,14 +1182,18 @@ function getMailbox(name, domain, callback) { }); } -function addMailbox(name, domain, ownerId, ownerType, auditSource, callback) { +function addMailbox(name, domain, data, auditSource, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert.strictEqual(typeof ownerId, 'string'); - assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); + const { ownerId, ownerType, active } = data; + assert.strictEqual(typeof ownerId, 'string'); + assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof active, 'boolean'); + name = name.toLowerCase(); var error = validateName(name); @@ -1197,23 +1201,27 @@ function addMailbox(name, domain, ownerId, ownerType, auditSource, callback) { if (ownerType !== exports.OWNERTYPE_USER && ownerType !== exports.OWNERTYPE_GROUP) return callback(new BoxError(BoxError.BAD_FIELD, 'bad owner type')); - mailboxdb.addMailbox(name, domain, ownerId, ownerType, function (error) { + mailboxdb.addMailbox(name, domain, data, function (error) { if (error) return callback(error); - eventlog.add(eventlog.ACTION_MAIL_MAILBOX_ADD, auditSource, { name, domain, ownerId, ownerType }); + eventlog.add(eventlog.ACTION_MAIL_MAILBOX_ADD, auditSource, { name, domain, ownerId, ownerType, active }); callback(null); }); } -function updateMailboxOwner(name, domain, ownerId, ownerType, auditSource, callback) { +function updateMailbox(name, domain, data, auditSource, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert.strictEqual(typeof ownerId, 'string'); - assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); + const { ownerId, ownerType, active } = data; + assert.strictEqual(typeof ownerId, 'string'); + assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof active, 'boolean'); + name = name.toLowerCase(); if (ownerType !== exports.OWNERTYPE_USER && ownerType !== exports.OWNERTYPE_GROUP) return callback(new BoxError(BoxError.BAD_FIELD, 'bad owner type')); @@ -1221,10 +1229,10 @@ function updateMailboxOwner(name, domain, ownerId, ownerType, auditSource, callb getMailbox(name, domain, function (error, result) { if (error) return callback(error); - mailboxdb.updateMailboxOwner(name, domain, ownerId, ownerType, function (error) { + mailboxdb.updateMailbox(name, domain, data, function (error) { if (error) return callback(error); - eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldUserId: result.userId, ownerId, ownerType }); + eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldUserId: result.userId, ownerId, ownerType, active }); callback(null); }); @@ -1337,14 +1345,18 @@ function getList(name, domain, callback) { }); } -function addList(name, domain, members, membersOnly, auditSource, callback) { +function addList(name, domain, data, auditSource, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof name, 'string'); - assert(Array.isArray(members)); - assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); + const { members, membersOnly, active } = data; + assert(Array.isArray(members)); + assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof active, 'boolean'); + name = name.toLowerCase(); var error = validateName(name); @@ -1354,23 +1366,27 @@ function addList(name, domain, members, membersOnly, auditSource, callback) { if (!validator.isEmail(members[i])) return callback(new BoxError(BoxError.BAD_FIELD, 'Invalid mail member: ' + members[i])); } - mailboxdb.addList(name, domain, members, membersOnly, function (error) { + mailboxdb.addList(name, domain, data, function (error) { if (error) return callback(error); - eventlog.add(eventlog.ACTION_MAIL_LIST_ADD, auditSource, { name, domain, members, membersOnly }); + eventlog.add(eventlog.ACTION_MAIL_LIST_ADD, auditSource, { name, domain, members, membersOnly, active }); callback(); }); } -function updateList(name, domain, members, membersOnly, auditSource, callback) { +function updateList(name, domain, data, auditSource, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert(Array.isArray(members)); - assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); + const { members, membersOnly, active } = data; + assert(Array.isArray(members)); + assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof active, 'boolean'); + name = name.toLowerCase(); var error = validateName(name); @@ -1383,10 +1399,10 @@ function updateList(name, domain, members, membersOnly, auditSource, callback) { getList(name, domain, function (error, result) { if (error) return callback(error); - mailboxdb.updateList(name, domain, members, membersOnly, function (error) { + mailboxdb.updateList(name, domain, data, function (error) { if (error) return callback(error); - eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldMembers: result.members, members, membersOnly }); + eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, oldMembers: result.members, members, membersOnly, active }); callback(null); }); diff --git a/src/mailboxdb.js b/src/mailboxdb.js index dad59e153..e32d5be71 100644 --- a/src/mailboxdb.js +++ b/src/mailboxdb.js @@ -4,7 +4,7 @@ exports = module.exports = { addMailbox, addList, - updateMailboxOwner, + updateMailbox, updateList, del, @@ -42,13 +42,14 @@ var assert = require('assert'), safe = require('safetydance'), util = require('util'); -var MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain' ].join(','); +var MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active' ].join(','); function postProcess(data) { data.members = safe.JSON.parse(data.membersJson) || [ ]; delete data.membersJson; data.membersOnly = !!data.membersOnly; + data.active = !!data.active; return data; } @@ -63,14 +64,18 @@ function postProcessAliases(data) { } } -function addMailbox(name, domain, ownerId, ownerType, callback) { +function addMailbox(name, domain, data, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert.strictEqual(typeof ownerId, 'string'); - assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof callback, 'function'); - database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType) VALUES (?, ?, ?, ?, ?)', [ name, exports.TYPE_MAILBOX, domain, ownerId, ownerType ], function (error) { + const { ownerId, ownerType, active } = data; + assert.strictEqual(typeof ownerId, 'string'); + assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof active, 'boolean'); + + database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, active) VALUES (?, ?, ?, ?, ?, ?)', [ name, exports.TYPE_MAILBOX, domain, ownerId, ownerType, active ], function (error) { if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, 'mailbox already exists')); if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); @@ -78,14 +83,18 @@ function addMailbox(name, domain, ownerId, ownerType, callback) { }); } -function updateMailboxOwner(name, domain, ownerId, ownerType, callback) { +function updateMailbox(name, domain, data, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert.strictEqual(typeof ownerId, 'string'); - assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof callback, 'function'); - database.query('UPDATE mailboxes SET ownerId = ?, ownerType = ? WHERE name = ? AND domain = ?', [ ownerId, ownerType, name, domain ], function (error, result) { + const { ownerId, ownerType, active } = data; + assert.strictEqual(typeof ownerId, 'string'); + assert.strictEqual(typeof ownerType, 'string'); + assert.strictEqual(typeof active, 'boolean'); + + database.query('UPDATE mailboxes SET ownerId = ?, ownerType = ?, active = ? WHERE name = ? AND domain = ?', [ ownerId, ownerType, active, name, domain ], function (error, result) { if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); if (result.affectedRows === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'Mailbox not found')); @@ -93,15 +102,19 @@ function updateMailboxOwner(name, domain, ownerId, ownerType, callback) { }); } -function addList(name, domain, members, membersOnly, callback) { +function addList(name, domain, data, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert(Array.isArray(members)); - assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof callback, 'function'); - database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, membersJson, membersOnly) VALUES (?, ?, ?, ?, ?, ?, ?)', - [ name, exports.TYPE_LIST, domain, 'admin', 'user', JSON.stringify(members), membersOnly ], function (error) { + const { members, membersOnly, active } = data; + assert(Array.isArray(members)); + assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof active, 'boolean'); + + database.query('INSERT INTO mailboxes (name, type, domain, ownerId, ownerType, membersJson, membersOnly, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [ name, exports.TYPE_LIST, domain, 'admin', 'user', JSON.stringify(members), membersOnly, active ], function (error) { if (error && error.code === 'ER_DUP_ENTRY') return callback(new BoxError(BoxError.ALREADY_EXISTS, 'mailbox already exists')); if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); @@ -109,15 +122,19 @@ function addList(name, domain, members, membersOnly, callback) { }); } -function updateList(name, domain, members, membersOnly, callback) { +function updateList(name, domain, data, callback) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - assert(Array.isArray(members)); - assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof data, 'object'); assert.strictEqual(typeof callback, 'function'); - database.query('UPDATE mailboxes SET membersJson = ?, membersOnly = ? WHERE name = ? AND domain = ?', - [ JSON.stringify(members), membersOnly, name, domain ], function (error, result) { + const { members, membersOnly, active } = data; + assert(Array.isArray(members)); + assert.strictEqual(typeof membersOnly, 'boolean'); + assert.strictEqual(typeof active, 'boolean'); + + database.query('UPDATE mailboxes SET membersJson = ?, membersOnly = ?, active = ? WHERE name = ? AND domain = ?', + [ JSON.stringify(members), membersOnly, active, name, domain ], function (error, result) { if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); if (result.affectedRows === 0) return callback(new BoxError(BoxError.NOT_FOUND, 'Mailbox not found')); @@ -238,7 +255,7 @@ function listMailboxes(domain, search, page, perPage, callback) { const escapedSearch = mysql.escape('%' + search + '%'); // this also quotes the string const searchQuery = search ? ` HAVING (name LIKE ${escapedSearch} OR aliasNames LIKE ${escapedSearch} OR aliasDomains LIKE ${escapedSearch})` : ''; // having instead of where because of aggregated columns use - const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, m1.active as active, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' @@ -250,6 +267,7 @@ function listMailboxes(domain, search, page, perPage, callback) { database.query(query, [ domain, (page-1)*perPage, perPage ], function (error, results) { if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + results.forEach(postProcess); results.forEach(postProcessAliases); callback(null, results); @@ -261,7 +279,7 @@ function listAllMailboxes(page, perPage, callback) { assert.strictEqual(typeof perPage, 'number'); assert.strictEqual(typeof callback, 'function'); - const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, m1.active as active, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains ' + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` + ` LEFT JOIN (SELECT * FROM mailboxes WHERE type='${exports.TYPE_ALIAS}') AS m2` + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' @@ -271,6 +289,7 @@ function listAllMailboxes(page, perPage, callback) { database.query(query, [ (page-1)*perPage, perPage ], function (error, results) { if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); + results.forEach(postProcess); results.forEach(postProcessAliases); callback(null, results); diff --git a/src/routes/mail.js b/src/routes/mail.js index 94f6228c6..16e91f65b 100644 --- a/src/routes/mail.js +++ b/src/routes/mail.js @@ -181,8 +181,9 @@ function addMailbox(req, res, next) { if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be a string')); if (typeof req.body.ownerId !== 'string') return next(new HttpError(400, 'ownerId must be a string')); if (typeof req.body.ownerType !== 'string') return next(new HttpError(400, 'ownerType must be a string')); + if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - mail.addMailbox(req.body.name, req.params.domain, req.body.ownerId, req.body.ownerType, auditSource.fromRequest(req), function (error) { + mail.addMailbox(req.body.name, req.params.domain, req.body, auditSource.fromRequest(req), function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); @@ -195,8 +196,9 @@ function updateMailbox(req, res, next) { if (typeof req.body.ownerId !== 'string') return next(new HttpError(400, 'ownerId must be a string')); if (typeof req.body.ownerType !== 'string') return next(new HttpError(400, 'ownerType must be a string')); + if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - mail.updateMailboxOwner(req.params.name, req.params.domain, req.body.ownerId, req.body.ownerType, auditSource.fromRequest(req), function (error) { + mail.updateMailbox(req.params.name, req.params.domain, req.body, auditSource.fromRequest(req), function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204)); @@ -302,8 +304,9 @@ function addList(req, res, next) { if (typeof req.body.members[i] !== 'string') return next(new HttpError(400, 'member must be a string')); } if (typeof req.body.membersOnly !== 'boolean') return next(new HttpError(400, 'membersOnly must be a boolean')); + if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - mail.addList(req.body.name, req.params.domain, req.body.members, req.body.membersOnly, auditSource.fromRequest(req), function (error) { + mail.addList(req.body.name, req.params.domain, req.body, auditSource.fromRequest(req), function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); @@ -321,8 +324,9 @@ function updateList(req, res, next) { if (typeof req.body.members[i] !== 'string') return next(new HttpError(400, 'member must be a string')); } if (typeof req.body.membersOnly !== 'boolean') return next(new HttpError(400, 'membersOnly must be a boolean')); + if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - mail.updateList(req.params.name, req.params.domain, req.body.members, req.body.membersOnly, auditSource.fromRequest(req), function (error) { + mail.updateList(req.params.name, req.params.domain, req.body, auditSource.fromRequest(req), function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204));