Merge branch 'master' of github.com:coralproject/talk into moderate-bio

This commit is contained in:
David Jay
2017-01-09 18:20:44 -05:00
7 changed files with 168 additions and 23 deletions
+2 -1
View File
@@ -5,6 +5,7 @@ const path = require('path');
const helmet = require('helmet');
const passport = require('./services/passport');
const session = require('express-session');
const enabled = require('debug').enabled;
const RedisStore = require('connect-redis')(session);
const redis = require('./services/redis');
const csrf = require('csurf');
@@ -120,7 +121,7 @@ app.use((req, res, next) => {
// returning a status code that makes sense.
app.use('/api', (err, req, res, next) => {
if (err !== ErrNotFound) {
if (app.get('env') !== 'test') {
if (app.get('env') !== 'test' || enabled('talk:errors')) {
console.error(err);
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ router.post('/password/reset', (req, res, next) => {
token,
rootURL: process.env.TALK_ROOT_URL
},
subject: 'Password Reset Requested - Talk',
subject: 'Password Reset',
to: email
});
})
+62 -17
View File
@@ -80,6 +80,34 @@ router.post('/:user_id/email', authorization.needed('admin'), (req, res, next) =
.catch(next);
});
// /**
// * SendEmailConfirmation sends a confirmation email to the user.
// * @param {Request} req express request object
// * @param {String} email user email address
// */
/**
* SendEmailConfirmation sends a confirmation email to the user.
* @param {ExpressApp} app the instance of the express app
* @param {String} userID the id for the user to send the email to
* @param {String} email the email for the user to send the email to
*/
const SendEmailConfirmation = (app, userID, email) => User
.createEmailConfirmToken(userID, email)
.then((token) => {
return mailer.sendSimple({
app, // needed to render the templates.
template: 'email/email-confirm', // needed to know which template to render!
locals: { // specifies the template locals.
token,
rootURL: process.env.TALK_ROOT_URL,
email
},
subject: 'Email Confirmation',
to: email
});
});
router.post('/', (req, res, next) => {
const {
email,
@@ -98,23 +126,7 @@ router.post('/', (req, res, next) => {
if (requireEmailConfirmation) {
// Email confirmation is required, let's generate that token and send
// the email.
return User
.createEmailConfirmToken(user.id, email)
.then((token) => {
return mailer.sendSimple({
app: req.app, // needed to render the templates.
template: 'email/email-confirm', // needed to know which template to render!
locals: { // specifies the template locals.
token,
rootURL: process.env.TALK_ROOT_URL,
email
},
subject: 'Email Confirmation - Talk',
to: email
});
})
SendEmailConfirmation(req.app, user.id, email)
.then(() => {
// Then send back the user.
@@ -158,4 +170,37 @@ router.post('/:user_id/actions', authorization.needed(), (req, res, next) => {
});
});
router.post('/:user_id/email/confirm', authorization.needed('admin'), (req, res, next) => {
const {
user_id
} = req.params;
User
.findById(user_id)
.then((user) => {
if (!user) {
res.status(404).end();
return;
}
// Find the first local profile.
let localProfile = user.profiles.find((profile) => profile.provider === 'local');
// If there was no local profile for the user, error out.
if (!localProfile) {
res.status(404).end();
return;
}
// Send the email to the first local profile that was found.
return SendEmailConfirmation(req.app, user.id, localProfile.id)
.then(() => {
res.status(204).end();
});
})
.catch((err) => {
next(err);
});
});
module.exports = router;
+57 -2
View File
@@ -14,7 +14,7 @@ const Queue = module.exports.queue = kue.createQueue({
}
});
module.exports.Task = class Task {
class Task {
constructor({name, attempts = 3, delay = 1000}) {
this.name = name;
@@ -76,4 +76,59 @@ module.exports.Task = class Task {
});
});
}
};
}
/**
* Stores the tasks during testing.
* @type {Array}
*/
const TestQueue = [];
/**
* TestTask is a Task queue that is implemented for when the application is in
* test mode, and does not send the jobs to redis, instead it queues them in
* an array which can be inspected.
*/
class TestTask {
constructor({name}) {
this.name = name;
}
/**
* Push the task into the fake queue.
*/
create(task) {
let id = TestQueue.push({
name: this.name,
task
});
return Promise.resolve({id});
}
// This is a NO-OP action simply provided to match the Task interface.
process() { return null; }
/**
* Returns the current tasks for this queue.
* @return {Array} the tasks in the queue
*/
get tasks() {
return TestQueue
.filter((testTask) => testTask.name === this.name)
.map((testTask) => testTask.task);
}
static shutdown() {
return Task.shutdown();
}
}
if (process.env.NODE_ENV === 'test') {
module.exports.Task = TestTask;
module.exports.TestQueue = TestQueue;
} else {
module.exports.Task = Task;
}
+5 -2
View File
@@ -65,13 +65,16 @@ const mailer = module.exports = {
return Promise.reject('sendSimple requires a subject for the email');
}
// Prefix the subject with `[Talk]`.
subject = `[Talk] ${subject}`;
return Promise.all([
// Render the HTML version of the email.
mailer.render(app, template, locals),
mailer.render(app, `${template}.ejs`, locals),
// Render the TEXT version of the email.
mailer.render(app, `${template}.txt`, locals)
mailer.render(app, `${template}.txt.ejs`, locals)
])
.then(([html, text]) => {
+7
View File
@@ -0,0 +1,7 @@
const kue = require('../services/kue');
beforeEach(() => {
// Empty the test tasks before finishing.
kue.TestQueue.splice(0, kue.TestQueue.length);
});
+34
View File
@@ -1,6 +1,7 @@
const passport = require('../../../passport');
const app = require('../../../../app');
const mailer = require('../../../../services/mailer');
const chai = require('chai');
const expect = chai.expect;
@@ -10,6 +11,39 @@ chai.use(require('chai-http'));
const User = require('../../../../models/user');
describe('/api/v1/users/:user_id/email/confirm', () => {
let mockUser;
beforeEach(() => User.createLocalUser('ana@gmail.com', '123', 'Ana').then((user) => {
mockUser = user;
}));
describe('#post', () => {
it('should send an email when we hit the endpoint', () => {
expect(mailer.task.tasks).to.have.length(0);
return chai.request(app)
.post(`/api/v1/users/${mockUser.id}/email/confirm`)
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(204);
expect(mailer.task.tasks).to.have.length(1);
});
});
it('should send a 404 on not matching a user', () => {
return chai.request(app)
.post(`/api/v1/users/${mockUser.id}/email/confirm`)
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(204);
expect(mailer.task.tasks).to.have.length(1);
});
});
});
});
describe('/api/v1/users/:user_id/actions', () => {
const users = [{