"use strict"; const rp = require('request-promise'); const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions'); const verifyPassword = function (login, password, isEmail = false) { const body = (!isEmail) ? { username: login, password } : { email: login, password }; return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, body, json: true }).then((res) => res) .catch((err) => err); }; const isAccountLockoutError = function (username, password, duration, waitTime) { return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) .then(() => reject('login should have failed')) .catch(err => { if (err.message === 'Your account is locked due to multiple failed login attempts. Please try again after ' + duration + ' minute(s)') { resolve(); } else { reject(err); } }); }, waitTime); }); }; describe("Verify User Password", () => { it('fails to verify password when masterKey has locked out user', (done) => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); user.setUsername('testuser'); user.setPassword('mypass'); user.setACL(ACL); user.signUp().then(() => { return Parse.User.logIn('testuser', 'mypass'); }).then((user) => { equal(user.get('username'), 'testuser'); // Lock the user down const ACL = new Parse.ACL(); user.setACL(ACL); return user.save(null, { useMasterKey: true }); }).then(() => { expect(user.getACL().getPublicReadAccess()).toBe(false); return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, qs: { username: 'testuser', password: 'mypass', } }); }).then((res) => { fail(res); done(); }).catch((err) => { expect(err.statusCode).toBe(404); expect(err.error).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }); }); it('fails to verify password when username is not provided in query string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, qs: { username: '', password: 'mypass', } }); }).then((res) => { fail(res); done(); }).catch((err) => { expect(err.statusCode).toBe(400); expect(err.error).toMatch('{"code":200,"error":"username/email is required."}'); done(); }); }); it('fails to verify password when email is not provided in query string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, qs: { email: '', password: 'mypass', } }); }).then((res) => { fail(res); done(); }).catch((err) => { expect(err.statusCode).toBe(400); expect(err.error).toMatch('{"code":200,"error":"username/email is required."}'); done(); }); }); it('fails to verify password when username is not provided with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('', 'mypass'); }).then((res) => { expect(res.statusCode).toBe(400); expect(JSON.stringify(res.error)).toMatch('{"code":200,"error":"username/email is required."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when email is not provided with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('', 'mypass', true); }).then((res) => { expect(res.statusCode).toBe(400); expect(JSON.stringify(res.error)).toMatch('{"code":200,"error":"username/email is required."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when password is not provided with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('testuser', ''); }).then((res) => { expect(res.statusCode).toBe(400); expect(JSON.stringify(res.error)).toMatch('{"code":201,"error":"password is required."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when username matches but password does not match hash with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('testuser', 'wrong password'); }).then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when email matches but password does not match hash with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('my@user.com', 'wrong password', true); }).then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when typeof username does not equal string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword(123, 'mypass'); }).then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when typeof email does not equal string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword(123, 'mypass', true); }).then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when typeof password does not equal string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('my@user.com', 123, true); }).then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when username cannot be found REST API', (done) => { verifyPassword('mytestuser', 'mypass') .then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when email cannot be found REST API', (done) => { verifyPassword('my@user.com', 'mypass', true) .then((res) => { expect(res.statusCode).toBe(404); expect(JSON.stringify(res.error)).toMatch('{"code":101,"error":"Invalid username/password."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API', (done) => { reconfigureServer({ publicServerURL: "http://localhost:8378/", appName: 'emailVerify', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ fromAddress: 'parse@example.com', apiKey: 'k', domain: 'd', }), }).then(() => { const user = new Parse.User(); return user.save({ username: 'unverified-user', password: 'mypass', email: 'unverified-email@user.com' }); }).then(() => { return verifyPassword('unverified-email@user.com', 'mypass', true); }).then((res) => { expect(res.statusCode).toBe(400); expect(JSON.stringify(res.error)).toMatch('{"code":205,"error":"User email is not verified."}'); done(); }).catch((err) => { fail(err); done(); }); }); it('verify password lock account if failed verify password attempts are above threshold', done => { reconfigureServer({ appName: 'lockout threshold', accountLockout: { duration: 1, threshold: 2 }, publicServerURL: "http://localhost:8378/" }) .then(() => { const user = new Parse.User(); return user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }) }) .then(() => { return verifyPassword('testuser', 'wrong password'); }) .then(() => { return verifyPassword('testuser', 'wrong password'); }) .then(() => { return verifyPassword('testuser', 'wrong password'); }) .then(() => { return isAccountLockoutError('testuser', 'wrong password', 1, 1); }) .then(() => { done(); }) .catch(err => { fail('lock account after failed login attempts test failed: ' + JSON.stringify(err)); done(); }); }); it('succeed in verifying password when username and email are provided and password matches hash with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, body: { username: 'testuser', email: 'my@user.com', password: 'mypass' }, json: true }).then((res) => res) .catch((err) => err); }).then((res) => { expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); expect(res.hasOwnProperty('sessionToken')).toEqual(false); expect(res.hasOwnProperty('password')).toEqual(false); done(); }).catch((err) => { fail(err); done(); }); }); it('succeed in verifying password when username and password matches hash with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('testuser', 'mypass'); }).then((res) => { expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); expect(res.hasOwnProperty('sessionToken')).toEqual(false); expect(res.hasOwnProperty('password')).toEqual(false); done(); }); }); it('succeed in verifying password when email and password matches hash with json payload REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return verifyPassword('my@user.com', 'mypass', true); }).then((res) => { expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); expect(res.hasOwnProperty('sessionToken')).toEqual(false); expect(res.hasOwnProperty('password')).toEqual(false); done(); }); }); it('succeed to verify password when username and password provided in query string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, qs: { username: 'testuser', password: 'mypass', } }); }).then((res) => { expect(typeof res).toBe('string'); const body = JSON.parse(res); expect(typeof body['objectId']).toEqual('string'); expect(body.hasOwnProperty('sessionToken')).toEqual(false); expect(body.hasOwnProperty('password')).toEqual(false); done(); }); }); it('succeed to verify password when email and password provided in query string REST API', (done) => { const user = new Parse.User(); user.save({ username: 'testuser', password: 'mypass', email: 'my@user.com' }).then(() => { return rp.get({ url: Parse.serverURL + '/verifyPassword', headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-REST-API-Key': 'rest' }, qs: { email: 'my@user.com', password: 'mypass', } }); }).then((res) => { expect(typeof res).toBe('string'); const body = JSON.parse(res); expect(typeof body['objectId']).toEqual('string'); expect(body.hasOwnProperty('sessionToken')).toEqual(false); expect(body.hasOwnProperty('password')).toEqual(false); done(); }); }); it('succeed to verify password with username when user1 has username === user2 email REST API', (done) => { const user1 = new Parse.User(); user1.save({ username: 'email@user.com', password: 'mypass1', email: '1@user.com' }).then(() => { const user2 = new Parse.User(); return user2.save({ username: 'user2', password: 'mypass2', email: 'email@user.com' }); }).then(() => { return verifyPassword('email@user.com', 'mypass1'); }).then((res) => { expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); expect(res.hasOwnProperty('sessionToken')).toEqual(false); expect(res.hasOwnProperty('password')).toEqual(false); done(); }); }); })