From 0c67b1e7810fbeb37cfe14529929735363c7a1d6 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 7 Jul 2014 14:09:45 -0700 Subject: [PATCH 1/4] Add an option to honor emailVerified See https://github.com/strongloop/loopback/pull/215 --- lib/models/user.js | 18 ++++++++++++ test/user.test.js | 73 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) 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/test/user.test.js b/test/user.test.js index 2b3805a1..1611995c 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,6 +171,26 @@ describe('User', function(){ }); }); }); + + it('Login a user by without email verification', function(done) { + User.settings.emailVerificationRequired = true; + User.login(validCredentials, function (err, accessToken) { + assert(err); + User.settings.emailVerificationRequired = false; + done(); + }); + }); + + it('Login a user by with email verification', function(done) { + User.settings.emailVerificationRequired = true; + User.login(validCredentialsEmailVerified, function (err, accessToken) { + assert(accessToken.userId); + assert(accessToken.id); + assert.equal(accessToken.id.length, 64); + User.settings.emailVerificationRequired = false; + done(); + }); + }); it('Login a user over REST by providing credentials', function(done) { request(app) @@ -175,6 +211,41 @@ describe('User', function(){ }); }); + it('Login a user over REST when email verification is required', function(done) { + User.settings.emailVerificationRequired = true; + 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); + + User.settings.emailVerificationRequired = false; + + done(); + }); + }); + + it('Login a user over REST without email verification when it is required', function(done) { + User.settings.emailVerificationRequired = true; + request(app) + .post('/users/login') + .expect('Content-Type', /json/) + .expect(401) + .send(validCredentials) + .end(function(err, res){ + User.settings.emailVerificationRequired = false; + done(); + }); + }); + it('Login a user over REST by providing invalid credentials', function(done) { request(app) .post('/users/login') From de831dbd33a860f59444aee076c24ceee90bbafe Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 8 Jul 2014 08:54:13 -0700 Subject: [PATCH 2/4] Fix the typo --- lib/connectors/mail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 = []; From 74a39f3fc25e35facbe71a7470c951dd54865acc Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 8 Jul 2014 08:54:50 -0700 Subject: [PATCH 3/4] Refactor email verification tests into a new group --- test/user.test.js | 112 +++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 55 deletions(-) diff --git a/test/user.test.js b/test/user.test.js index 1611995c..e0c53e32 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -172,26 +172,6 @@ describe('User', function(){ }); }); - it('Login a user by without email verification', function(done) { - User.settings.emailVerificationRequired = true; - User.login(validCredentials, function (err, accessToken) { - assert(err); - User.settings.emailVerificationRequired = false; - done(); - }); - }); - - it('Login a user by with email verification', function(done) { - User.settings.emailVerificationRequired = true; - User.login(validCredentialsEmailVerified, function (err, accessToken) { - assert(accessToken.userId); - assert(accessToken.id); - assert.equal(accessToken.id.length, 64); - User.settings.emailVerificationRequired = false; - done(); - }); - }); - it('Login a user over REST by providing credentials', function(done) { request(app) .post('/users/login') @@ -211,41 +191,6 @@ describe('User', function(){ }); }); - it('Login a user over REST when email verification is required', function(done) { - User.settings.emailVerificationRequired = true; - 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); - - User.settings.emailVerificationRequired = false; - - done(); - }); - }); - - it('Login a user over REST without email verification when it is required', function(done) { - User.settings.emailVerificationRequired = true; - request(app) - .post('/users/login') - .expect('Content-Type', /json/) - .expect(401) - .send(validCredentials) - .end(function(err, res){ - User.settings.emailVerificationRequired = false; - done(); - }); - }); - it('Login a user over REST by providing invalid credentials', function(done) { request(app) .post('/users/login') @@ -306,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) { From a98db0bf8661fc2e2fa4beb7576e716cacf9cb99 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 8 Jul 2014 08:55:06 -0700 Subject: [PATCH 4/4] Remove unused deps --- package.json | 2 -- 1 file changed, 2 deletions(-) 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": {