diff --git a/lib/connectors/mail.js b/lib/connectors/mail.js index 5660adcc..cd9df7b8 100644 --- a/lib/connectors/mail.js +++ b/lib/connectors/mail.js @@ -18,7 +18,7 @@ module.exports = MailConnector; */ function MailConnector(settings) { - assert(typeof settings === 'object', 'cannot initiaze MailConnector without a settings object'); + assert(typeof settings === 'object', 'cannot initialize MailConnector without a settings object'); var transports = settings.transports || []; this.transportsIndex = {}; this.transports = []; diff --git a/lib/models/user.js b/lib/models/user.js index 48a15eea..877eb36b 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -186,6 +186,15 @@ User.login = function (credentials, include, fn) { debug('An error is reported from User.findOne: %j', err); fn(defaultError); } else if(user) { + if (self.settings.emailVerificationRequired) { + if (!user.emailVerified) { + // Fail to log in if email verification is not done yet + debug('User email has not been verified'); + err = new Error('login failed as the email has not been verified'); + err.statusCode = 401; + return fn(err); + } + } user.hasPassword(credentials.password, function(err, isMatch) { if(err) { debug('An error is reported from User.hasPassword: %j', err); @@ -441,6 +450,15 @@ User.setup = function () { this.$password = bcrypt.hashSync(plain, salt); } + // Make sure emailVerified is not set by creation + UserModel.beforeRemote('create', function(ctx, user, next) { + var body = ctx.req.body; + if (body && body.emailVerified) { + body.emailVerified = false; + } + next(); + }); + loopback.remoteMethod( UserModel.login, { diff --git a/package.json b/package.json index 5e3756b2..14e7e4e9 100644 --- a/package.json +++ b/package.json @@ -76,8 +76,6 @@ "browser": { "express": "./lib/browser-express.js", "connect": false, - "passport": false, - "passport-local": false, "nodemailer": false }, "license": { diff --git a/test/user.test.js b/test/user.test.js index 2b3805a1..e0c53e32 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -8,6 +8,8 @@ var userMemory = loopback.createDataSource({ describe('User', function(){ var validCredentials = {email: 'foo@bar.com', password: 'bar'}; + var validCredentialsEmailVerified = {email: 'foo1@bar.com', password: 'bar1', emailVerified: true}; + var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true}; var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600}; var invalidCredentials = {email: 'foo1@bar.com', password: 'bar1'}; var incompleteCredentials = {password: 'bar1'}; @@ -30,7 +32,9 @@ describe('User', function(){ app.use(loopback.rest()); app.model(User); - User.create(validCredentials, done); + User.create(validCredentials, function(err, user) { + User.create(validCredentialsEmailVerified, done); + }); }); afterEach(function (done) { @@ -105,6 +109,18 @@ describe('User', function(){ var u = new User({username: 'foo', password: 'bar'}); assert(u.password !== 'bar'); }); + + it('Create a user over REST should remove emailVerified property', function(done) { + request(app) + .post('/users') + .expect('Content-Type', /json/) + .expect(200) + .send(validCredentialsEmailVerifiedOverREST) + .end(function(err, res){ + assert(!res.body.emailVerified); + done(); + }); + }); }); describe('User.login', function() { @@ -155,7 +171,7 @@ describe('User', function(){ }); }); }); - + it('Login a user over REST by providing credentials', function(done) { request(app) .post('/users/login') @@ -235,6 +251,63 @@ describe('User', function(){ }); }); }); + + describe('User.login requiring email verification', function() { + beforeEach(function() { + User.settings.emailVerificationRequired = true; + }); + + afterEach(function() { + User.settings.emailVerificationRequired = false; + }); + + it('Login a user by without email verification', function(done) { + User.login(validCredentials, function (err, accessToken) { + assert(err); + done(); + }); + }); + + it('Login a user by with email verification', function(done) { + User.login(validCredentialsEmailVerified, function (err, accessToken) { + assert(accessToken.userId); + assert(accessToken.id); + assert.equal(accessToken.id.length, 64); + done(); + }); + }); + + it('Login a user over REST when email verification is required', function(done) { + request(app) + .post('/users/login') + .expect('Content-Type', /json/) + .expect(200) + .send(validCredentialsEmailVerified) + .end(function(err, res){ + if(err) return done(err); + var accessToken = res.body; + + assert(accessToken.userId); + assert(accessToken.id); + assert.equal(accessToken.id.length, 64); + assert(accessToken.user === undefined); + + done(); + }); + }); + + it('Login a user over REST without email verification when it is required', function(done) { + request(app) + .post('/users/login') + .expect('Content-Type', /json/) + .expect(401) + .send(validCredentials) + .end(function(err, res) { + done(); + }); + }); + + }); describe('User.logout', function() { it('Logout a user by providing the current accessToken id (using node)', function(done) {