diff --git a/src/routes/index.js b/src/routes/index.js index d9cbf8074..4c945de94 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -9,9 +9,10 @@ exports = module.exports = { eventlog: require('./eventlog.js'), graphs: require('./graphs.js'), groups: require('./groups.js'), + mailboxes: require('./mailboxes.js'), oauth2: require('./oauth2.js'), profile: require('./profile.js'), - settings: require('./settings.js'), sysadmin: require('./sysadmin.js'), + settings: require('./settings.js'), user: require('./user.js') }; diff --git a/src/routes/mailboxes.js b/src/routes/mailboxes.js new file mode 100644 index 000000000..e7f0750d7 --- /dev/null +++ b/src/routes/mailboxes.js @@ -0,0 +1,58 @@ +'use strict'; + +exports = module.exports = { + list: list, + get: get, + remove: remove, + create: create +}; + +var assert = require('assert'), + HttpError = require('connect-lastmile').HttpError, + HttpSuccess = require('connect-lastmile').HttpSuccess, + mailboxes = require('../mailboxes.js'), + MailboxError = mailboxes.MailboxError; + +function create(req, res, next) { + assert.strictEqual(typeof req.body, 'object'); + + if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be string')); + + mailboxes.add(req.body.name, function (error, mailbox) { + if (error && error.reason === MailboxError.BAD_NAME) return next(new HttpError(400, error.message)); + if (error && error.reason === MailboxError.ALREADY_EXISTS) return next(new HttpError(409, 'Already exists')); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(201, mailbox)); + }); +} + +function get(req, res, next) { + assert.strictEqual(typeof req.params.mailboxId, 'string'); + + mailboxes.get(req.params.mailboxId, function (error, result) { + if (error && error.reason === MailboxError.NOT_FOUND) return next(new HttpError(404, 'No such group')); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, result)); + }); +} + +function list(req, res, next) { + mailboxes.getAll(function (error, result) { + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(200, { mailboxes: result })); + }); +} + +function remove(req, res, next) { + assert.strictEqual(typeof req.params.mailboxId, 'string'); + + mailboxes.del(req.params.mailboxId, function (error) { + if (error && error.reason === MailboxError.NOT_FOUND) return next(new HttpError(404, 'Mailbox not found')); + if (error) return next(new HttpError(500, error)); + + next(new HttpSuccess(204)); + }); +} diff --git a/src/routes/test/mailboxes-test.js b/src/routes/test/mailboxes-test.js new file mode 100644 index 000000000..21e4bf62a --- /dev/null +++ b/src/routes/test/mailboxes-test.js @@ -0,0 +1,139 @@ +/* jslint node:true */ +/* global it:false */ +/* global describe:false */ +/* global before:false */ +/* global after:false */ + +'use strict'; + +var async = require('async'), + config = require('../../config.js'), + database = require('../../database.js'), + expect = require('expect.js'), + superagent = require('superagent'), + server = require('../../server.js'), + nock = require('nock'), + userdb = require('../../userdb.js'); + +var SERVER_URL = 'http://localhost:' + config.get('port'); + +var MAILBOX_ID = 'mailbox'; + +var USERNAME = 'superadmin', PASSWORD = 'Foobar?1337', EMAIL ='silly@me.com'; +var token = null; + +var server; +function setup(done) { + config.set('fqdn', 'foobar.com'); + + async.series([ + server.start.bind(server), + + userdb._clear, + + function createAdmin(callback) { + var scope1 = nock(config.apiServerOrigin()).get('/api/v1/boxes/' + config.fqdn() + '/setup/verify?setupToken=somesetuptoken').reply(200, {}); + var scope2 = nock(config.apiServerOrigin()).post('/api/v1/boxes/' + config.fqdn() + '/setup/done?setupToken=somesetuptoken').reply(201, {}); + + superagent.post(SERVER_URL + '/api/v1/cloudron/activate') + .query({ setupToken: 'somesetuptoken' }) + .send({ username: USERNAME, password: PASSWORD, email: EMAIL }) + .end(function (error, result) { + expect(result).to.be.ok(); + expect(result.statusCode).to.eql(201); + expect(scope1.isDone()).to.be.ok(); + expect(scope2.isDone()).to.be.ok(); + + // stash token for further use + token = result.body.token; + + callback(); + }); + } + ], done); +} + +function cleanup(done) { + database._clear(function (error) { + expect(!error).to.be.ok(); + + server.stop(done); + }); +} + +describe('Mailbox API', function () { + this.timeout(10000); + + before(setup); + after(cleanup); + + it('cannot create a mailbox without name param', function (done) { + superagent.post(SERVER_URL + '/api/v1/mailboxes') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('cannot create a mailbox without token', function (done) { + superagent.post(SERVER_URL + '/api/v1/mailboxes') + .send({ name: MAILBOX_ID }) + .end(function (err, res) { + expect(res.statusCode).to.equal(401); + done(); + }); + }); + + it('cannot create invalid mailbox', function (done) { + superagent.post(SERVER_URL + '/api/v1/mailboxes') + .query({ access_token: token }) + .send({ name: 'no-reply' }) + .end(function (err, res) { + expect(res.statusCode).to.equal(400); + done(); + }); + }); + + it('can create mailbox', function (done) { + superagent.post(SERVER_URL + '/api/v1/mailboxes') + .query({ access_token: token }) + .send({ name: MAILBOX_ID }) + .end(function (err, res) { + expect(res.statusCode).to.equal(201); + done(); + }); + }); + + it('can get mailbox', function (done) { + superagent.get(SERVER_URL + '/api/v1/mailboxes/' + MAILBOX_ID) + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body.name).to.equal(MAILBOX_ID); + expect(res.body.creationTime).to.be.ok(); + done(); + }); + }); + + it('can list mailboxes', function (done) { + superagent.get(SERVER_URL + '/api/v1/mailboxes') + .query({ access_token: token }) + .end(function (err, res) { + expect(res.statusCode).to.equal(200); + expect(res.body.mailboxes).to.be.an(Array); + expect(res.body.mailboxes[0].name).to.be(MAILBOX_ID); + done(); + }); + }); + + it('can delete mailbox', function (done) { + superagent.del(SERVER_URL + '/api/v1/mailboxes/' + MAILBOX_ID) + .query({ access_token: token }) + .send({ name: MAILBOX_ID }) + .end(function (err, res) { + expect(res.statusCode).to.equal(204); + done(); + }); + }); +}); diff --git a/src/server.js b/src/server.js index 98f5023c8..afae33699 100644 --- a/src/server.js +++ b/src/server.js @@ -120,6 +120,12 @@ function initializeExpressSync() { router.get ('/api/v1/groups/:groupId', usersScope, routes.user.requireAdmin, routes.groups.get); router.del ('/api/v1/groups/:groupId', usersScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.groups.remove); + // Mailbox management + router.get ('/api/v1/mailboxes', usersScope, routes.user.requireAdmin, routes.mailboxes.list); + router.post('/api/v1/mailboxes', usersScope, routes.user.requireAdmin, routes.mailboxes.create); + router.get ('/api/v1/mailboxes/:mailboxId', usersScope, routes.user.requireAdmin, routes.mailboxes.get); + router.del ('/api/v1/mailboxes/:mailboxId', usersScope, routes.user.requireAdmin, routes.mailboxes.remove); + // form based login routes used by oauth2 frame router.get ('/api/v1/session/login', csrf, routes.oauth2.loginForm); router.post('/api/v1/session/login', csrf, routes.oauth2.login);