diff --git a/src/groups.js b/src/groups.js index db9c5a341..1c359e43b 100644 --- a/src/groups.js +++ b/src/groups.js @@ -1,8 +1,6 @@ 'use strict'; exports = module.exports = { - GroupsError: GroupsError, - create: create, remove: remove, get: get, @@ -26,50 +24,24 @@ exports = module.exports = { }; var assert = require('assert'), + BoxError = require('./boxerror.js'), constants = require('./constants.js'), DatabaseError = require('./databaseerror.js'), groupdb = require('./groupdb.js'), - util = require('util'), uuid = require('uuid'), _ = require('underscore'); -// http://dustinsenos.com/articles/customErrorsInNode -// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi -function GroupsError(reason, errorOrMessage) { - assert.strictEqual(typeof reason, 'string'); - assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined'); - - Error.call(this); - Error.captureStackTrace(this, this.constructor); - - this.name = this.constructor.name; - this.reason = reason; - if (typeof errorOrMessage === 'undefined') { - this.message = reason; - } else if (typeof errorOrMessage === 'string') { - this.message = errorOrMessage; - } else { - this.message = 'Internal error'; - this.nestedError = errorOrMessage; - } -} -util.inherits(GroupsError, Error); -GroupsError.INTERNAL_ERROR = 'Internal Error'; -GroupsError.ALREADY_EXISTS = 'Already Exists'; -GroupsError.NOT_FOUND = 'Not Found'; -GroupsError.BAD_FIELD = 'Field error'; - // keep this in sync with validateUsername function validateGroupname(name) { assert.strictEqual(typeof name, 'string'); - if (name.length < 1) return new GroupsError(GroupsError.BAD_FIELD, 'name must be atleast 1 char'); - if (name.length >= 200) return new GroupsError(GroupsError.BAD_FIELD, 'name too long'); + if (name.length < 1) return new BoxError(BoxError.BAD_FIELD, 'name must be atleast 1 char', { field: 'name' }); + if (name.length >= 200) return new BoxError(BoxError.BAD_FIELD, 'name too long', { field: 'name' }); - if (constants.RESERVED_NAMES.indexOf(name) !== -1) return new GroupsError(GroupsError.BAD_FIELD, 'name is reserved'); + if (constants.RESERVED_NAMES.indexOf(name) !== -1) return new BoxError(BoxError.BAD_FIELD, 'name is reserved', { field: name }); // need to consider valid LDAP characters here (e.g '+' is reserved) - if (/[^a-zA-Z0-9.-]/.test(name)) return new GroupsError(GroupsError.BAD_FIELD, 'name can only contain alphanumerals, hyphen and dot'); + if (/[^a-zA-Z0-9.-]/.test(name)) return new BoxError(BoxError.BAD_FIELD, 'name can only contain alphanumerals, hyphen and dot', { field: 'name' }); return null; } @@ -86,8 +58,8 @@ function create(name, callback) { var id = 'gid-' + uuid.v4(); groupdb.add(id, name, function (error) { - if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new GroupsError(GroupsError.ALREADY_EXISTS)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(new BoxError(BoxError.ALREADY_EXISTS)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, { id: id, name: name }); }); @@ -98,8 +70,8 @@ function remove(id, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.del(id, function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null); }); @@ -110,8 +82,8 @@ function get(id, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.get(id, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -122,8 +94,8 @@ function getWithMembers(id, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getWithMembers(id, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -133,7 +105,7 @@ function getAll(callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getAll(function (error, result) { - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -143,7 +115,7 @@ function getAllWithMembers(callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getAllWithMembers(function (error, result) { - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -154,8 +126,8 @@ function getMembers(groupId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getMembers(groupId, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -166,8 +138,8 @@ function getMembership(userId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getMembership(userId, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -179,8 +151,8 @@ function setMembership(userId, groupIds, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.setMembership(userId, groupIds, function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null); }); @@ -192,8 +164,8 @@ function addMember(groupId, userId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.addMember(groupId, userId, function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null); }); @@ -205,8 +177,8 @@ function setMembers(groupId, userIds, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.setMembers(groupId, userIds, function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND, 'Invalid group or user id')); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND, 'Invalid group or user id')); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null); }); @@ -218,8 +190,8 @@ function removeMember(groupId, userId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.removeMember(groupId, userId, function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null); }); @@ -231,8 +203,8 @@ function isMember(groupId, userId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.isMember(groupId, userId, function (error, result) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); return callback(null, result); }); @@ -251,8 +223,8 @@ function update(groupId, data, callback) { } groupdb.update(groupId, _.pick(data, 'name'), function (error) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new GroupsError(GroupsError.NOT_FOUND)); - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null); }); @@ -263,7 +235,7 @@ function getGroups(userId, callback) { assert.strictEqual(typeof callback, 'function'); groupdb.getGroups(userId, function (error, results) { - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, results); }); @@ -273,7 +245,7 @@ function count(callback) { assert.strictEqual(typeof callback, 'function'); groupdb.count(function (error, count) { - if (error) return callback(new GroupsError(GroupsError.INTERNAL_ERROR, error)); + if (error) return callback(new BoxError(BoxError.DATABASE_ERROR, error)); callback(null, count); }); diff --git a/src/routes/groups.js b/src/routes/groups.js index f9ebeb870..81604e161 100644 --- a/src/routes/groups.js +++ b/src/routes/groups.js @@ -10,10 +10,24 @@ exports = module.exports = { }; var assert = require('assert'), + BoxError = require('../boxerror.js'), groups = require('../groups.js'), HttpError = require('connect-lastmile').HttpError, - HttpSuccess = require('connect-lastmile').HttpSuccess, - GroupsError = groups.GroupsError; + HttpSuccess = require('connect-lastmile').HttpSuccess; + +function toHttpError(error) { + switch (error.reason) { + case BoxError.NOT_FOUND: + return new HttpError(404, error); + case BoxError.ALREADY_EXISTS: + return new HttpError(409, error); + case BoxError.BAD_FIELD: + return new HttpError(400, error); + case BoxError.INTERNAL_ERROR: + default: + return new HttpError(500, error); + } +} function create(req, res, next) { assert.strictEqual(typeof req.body, 'object'); @@ -21,9 +35,7 @@ function create(req, res, next) { if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be string')); groups.create(req.body.name, function (error, group) { - if (error && error.reason === GroupsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error && error.reason === GroupsError.ALREADY_EXISTS) return next(new HttpError(409, 'Already exists')); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); var groupInfo = { id: group.id, @@ -38,8 +50,7 @@ function get(req, res, next) { assert.strictEqual(typeof req.params.groupId, 'string'); groups.getWithMembers(req.params.groupId, function (error, result) { - if (error && error.reason === GroupsError.NOT_FOUND) return next(new HttpError(404, 'No such group')); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, result)); }); @@ -52,8 +63,7 @@ function update(req, res, next) { if ('name' in req.body && typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be a string')); groups.update(req.params.groupId, req.body, function (error) { - if (error && error.reason === GroupsError.BAD_FIELD) return next(new HttpError(400, error.message)); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, { })); }); @@ -66,8 +76,7 @@ function updateMembers(req, res, next) { if (!Array.isArray(req.body.userIds)) return next(new HttpError(404, 'userIds must be an array')); groups.setMembers(req.params.groupId, req.body.userIds, function (error) { - if (error && error.reason === GroupsError.NOT_FOUND) return next(new HttpError(404, 'Invalid group or user id')); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, { })); }); @@ -75,7 +84,7 @@ function updateMembers(req, res, next) { function list(req, res, next) { groups.getAll(function (error, result) { - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(200, { groups: result })); }); @@ -85,8 +94,7 @@ function remove(req, res, next) { assert.strictEqual(typeof req.params.groupId, 'string'); groups.remove(req.params.groupId, function (error) { - if (error && error.reason === GroupsError.NOT_FOUND) return next(new HttpError(404, 'Group not found')); - if (error) return next(new HttpError(500, error)); + if (error) return next(toHttpError(error)); next(new HttpSuccess(204)); }); diff --git a/src/test/groups-test.js b/src/test/groups-test.js index c2096a6ef..3e41ace62 100644 --- a/src/test/groups-test.js +++ b/src/test/groups-test.js @@ -7,11 +7,11 @@ 'use strict'; var async = require('async'), + BoxError = require('../boxerror.js'), database = require('../database.js'), DatabaseError = require('../databaseerror.js'), expect = require('expect.js'), groups = require('../groups.js'), - GroupsError = groups.GroupsError, hat = require('../hat.js'), mailboxdb = require('../mailboxdb.js'), userdb = require('../userdb.js'); @@ -79,35 +79,35 @@ describe('Groups', function () { it('cannot create group - too small', function (done) { groups.create('', function (error) { - expect(error.reason).to.be(GroupsError.BAD_FIELD); + expect(error.reason).to.be(BoxError.BAD_FIELD); done(); }); }); it('cannot create group - too big', function (done) { groups.create(new Array(256).join('a'), function (error) { - expect(error.reason).to.be(GroupsError.BAD_FIELD); + expect(error.reason).to.be(BoxError.BAD_FIELD); done(); }); }); it('cannot create group - bad name', function (done) { groups.create('bad:name', function (error) { - expect(error.reason).to.be(GroupsError.BAD_FIELD); + expect(error.reason).to.be(BoxError.BAD_FIELD); done(); }); }); it('cannot create group - reserved', function (done) { groups.create('users', function (error) { - expect(error.reason).to.be(GroupsError.BAD_FIELD); + expect(error.reason).to.be(BoxError.BAD_FIELD); done(); }); }); it('cannot create group - invalid', function (done) { groups.create('cloudron+admin', function (error) { - expect(error.reason).to.be(GroupsError.BAD_FIELD); + expect(error.reason).to.be(BoxError.BAD_FIELD); done(); }); }); @@ -123,21 +123,21 @@ describe('Groups', function () { it('cannot create existing group with mixed case', function (done) { var name = GROUP0_NAME[0].toUpperCase() + GROUP0_NAME.substr(1); groups.create(name, function (error) { - expect(error.reason).to.be(GroupsError.ALREADY_EXISTS); + expect(error.reason).to.be(BoxError.ALREADY_EXISTS); done(); }); }); it('cannot add existing group', function (done) { groups.create(GROUP0_NAME, function (error) { - expect(error.reason).to.be(GroupsError.ALREADY_EXISTS); + expect(error.reason).to.be(BoxError.ALREADY_EXISTS); done(); }); }); it('cannot get invalid group', function (done) { groups.get('sometrandom', function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); @@ -152,7 +152,7 @@ describe('Groups', function () { it('cannot delete invalid group', function (done) { groups.remove('random', function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); @@ -191,14 +191,14 @@ describe('Group membership', function () { it('cannot add non-existent user', function (done) { groups.addMember(group0Object.id, 'randomuser', function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('cannot add non-existent group', function (done) { groups.addMember('randomgroup', USER_0.id, function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); @@ -252,14 +252,14 @@ describe('Group membership', function () { it('cannot remove non-existent user', function (done) { groups.removeMember(group0Object.id, 'randomuser', function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); it('cannot remove non-existent group', function (done) { groups.removeMember('randomgroup', USER_0.id, function (error) { - expect(error.reason).to.be(GroupsError.NOT_FOUND); + expect(error.reason).to.be(BoxError.NOT_FOUND); done(); }); }); diff --git a/src/users.js b/src/users.js index f6557edce..0c72b1d5b 100644 --- a/src/users.js +++ b/src/users.js @@ -34,6 +34,7 @@ exports = module.exports = { }; let assert = require('assert'), + BoxError = require('./boxerror.js'), crypto = require('crypto'), constants = require('./constants.js'), debug = require('debug')('box:user'), @@ -42,7 +43,6 @@ let assert = require('assert'), externalldap = require('./externalldap.js'), ExternalLdapError = externalldap.ExternalLdapError, groups = require('./groups.js'), - GroupsError = groups.GroupsError, hat = require('./hat.js'), mailer = require('./mailer.js'), qrcode = require('qrcode'), @@ -472,7 +472,7 @@ function setMembership(userId, groupIds, callback) { assert.strictEqual(typeof callback, 'function'); groups.setMembership(userId, groupIds, function (error) { - if (error && error.reason === GroupsError.NOT_FOUND) return callback(new UsersError(UsersError.NOT_FOUND, 'One or more groups not found')); + if (error && error.reason === BoxError.NOT_FOUND) return callback(new UsersError(UsersError.NOT_FOUND, 'One or more groups not found')); if (error) return callback(new UsersError(UsersError.INTERNAL_ERROR, error)); callback(null);