Skip to content

Commit

Permalink
fix: Incomplete user object in verifyEmail function if both usernam…
Browse files Browse the repository at this point in the history
…e and email are changed (#8889)
  • Loading branch information
mtrezza authored Jan 15, 2024
1 parent 355baf9 commit 1eb95ae
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 43 deletions.
1 change: 1 addition & 0 deletions spec/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"fit_exclude_node_version": true,
"it_exclude_dbs": true,
"describe_only_db": true,
"fdescribe_only_db": true,
"describe_only": true,
"on_db": true,
"defaultConfiguration": true,
Expand Down
50 changes: 50 additions & 0 deletions spec/EmailVerificationToken.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -168,6 +169,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -215,6 +217,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -388,6 +391,7 @@ describe('Email Verification Token Expiration: ', () => {
user2.setPassword('expiringToken');
user2.set('email', 'user2@example.com');
await user2.signUp();
await jasmine.timeout();
expect(user2.getSessionToken()).toBeUndefined();
expect(sendEmailOptions).toBeDefined();
expect(verifySpy).toHaveBeenCalledTimes(5);
Expand Down Expand Up @@ -422,10 +426,47 @@ describe('Email Verification Token Expiration: ', () => {
newUser.set('email', 'user@example.com');
await newUser.signUp();
await Parse.User.requestEmailVerification('user@example.com');
await jasmine.timeout();
expect(sendSpy).toHaveBeenCalledTimes(2);
expect(emailSpy).toHaveBeenCalledTimes(0);
});

it('provides full user object in email verification function on email and username change', async () => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
const sendVerificationEmail = {
method(req) {
expect(req.user).toBeDefined();
expect(req.user.id).toBeDefined();
expect(req.user.get('createdAt')).toBeDefined();
expect(req.user.get('updatedAt')).toBeDefined();
expect(req.master).toBeDefined();
return false;
},
};
await reconfigureServer({
appName: 'emailVerifyToken',
verifyUserEmails: true,
emailAdapter: emailAdapter,
emailVerifyTokenValidityDuration: 5,
publicServerURL: 'http://localhost:8378/1',
sendUserEmailVerification: sendVerificationEmail.method,
});
const user = new Parse.User();
user.setPassword('password');
user.setUsername('new@example.com');
user.setEmail('user@example.com');
await user.save(null, { useMasterKey: true });

// Update email and username
user.setUsername('new@example.com');
user.setEmail('new@example.com');
await user.save(null, { useMasterKey: true });
});

it('beforeSave options do not change existing behaviour', async () => {
let sendEmailOptions;
const emailAdapter = {
Expand All @@ -448,6 +489,7 @@ describe('Email Verification Token Expiration: ', () => {
newUser.setPassword('expiringToken');
newUser.set('email', 'user@parse.com');
await newUser.signUp();
await jasmine.timeout();
const response = await request({
url: sendEmailOptions.link,
followRedirects: false,
Expand Down Expand Up @@ -490,6 +532,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -549,6 +592,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
return request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -766,6 +810,9 @@ describe('Email Verification Token Expiration: ', () => {
})
.then(response => {
expect(response.status).toBe(200);
})
.then(() => jasmine.timeout())
.then(() => {
expect(sendVerificationEmailCallCount).toBe(2);
expect(sendEmailOptions).toBeDefined();

Expand Down Expand Up @@ -917,6 +964,7 @@ describe('Email Verification Token Expiration: ', () => {
'Content-Type': 'application/json',
},
});
await jasmine.timeout();
expect(response.status).toBe(200);
expect(sendVerificationEmailCallCount).toBe(2);
expect(sendEmailOptions).toBeDefined();
Expand Down Expand Up @@ -959,6 +1007,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
return request({
url: sendEmailOptions.link,
Expand Down Expand Up @@ -1197,6 +1246,7 @@ describe('Email Verification Token Expiration: ', () => {
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => jasmine.timeout())
.then(() => {
request({
url: sendEmailOptions.link,
Expand Down
7 changes: 6 additions & 1 deletion spec/PagesRouter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ describe('Pages Router', () => {
user.setPassword('examplePassword');
user.set('email', 'mail@example.com');
await user.signUp();
await jasmine.timeout();

const link = sendVerificationEmail.calls.all()[0].args[0].link;
const linkWithLocale = new URL(link);
Expand Down Expand Up @@ -777,6 +778,7 @@ describe('Pages Router', () => {
user.setPassword('examplePassword');
user.set('email', 'mail@example.com');
await user.signUp();
await jasmine.timeout();

const link = sendVerificationEmail.calls.all()[0].args[0].link;
const linkWithLocale = new URL(link);
Expand Down Expand Up @@ -830,6 +832,7 @@ describe('Pages Router', () => {
user.setPassword('examplePassword');
user.set('email', 'mail@example.com');
await user.signUp();
await jasmine.timeout();

const link = sendVerificationEmail.calls.all()[0].args[0].link;
const linkWithLocale = new URL(link);
Expand All @@ -846,6 +849,8 @@ describe('Pages Router', () => {
const locale = linkResponse.headers['x-parse-page-param-locale'];
const username = linkResponse.headers['x-parse-page-param-username'];
const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl'];
await jasmine.timeout();

const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0];
expect(appId).toBeDefined();
expect(locale).toBe(exampleLocale);
Expand Down Expand Up @@ -1190,14 +1195,14 @@ describe('Pages Router', () => {
user.setPassword('examplePassword');
user.set('email', 'mail@example.com');
await user.signUp();
await jasmine.timeout();

const link = sendVerificationEmail.calls.all()[0].args[0].link;
const linkResponse = await request({
url: link,
followRedirects: false,
});
expect(linkResponse.status).toBe(200);

const pagePath = pageResponse.calls.all()[0].args[0];
expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`));
});
Expand Down
4 changes: 1 addition & 3 deletions spec/Parse.Push.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

const request = require('../lib/request');

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const pushCompleted = async pushId => {
const query = new Parse.Query('_PushStatus');
query.equalTo('objectId', pushId);
let result = await query.first({ useMasterKey: true });
while (!(result && result.get('status') === 'succeeded')) {
await sleep(100);
await jasmine.timeout();
result = await query.first({ useMasterKey: true });
}
};
Expand Down
2 changes: 1 addition & 1 deletion spec/ParseHooks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ describe('Hooks', () => {

it('should fail trying to create two times the same function', done => {
Parse.Hooks.createFunction('my_new_function', 'http://url.com')
.then(() => new Promise(resolve => setTimeout(resolve, 100)))
.then(() => jasmine.timeout())
.then(
() => {
return Parse.Hooks.createFunction('my_new_function', 'http://url.com');
Expand Down
1 change: 1 addition & 0 deletions spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3067,6 +3067,7 @@ describe('Parse.User testing', () => {
},
});
})
.then(() => jasmine.timeout())
.then(() => {
expect(emailCalled).toBe(true);
expect(emailOptions).not.toBeUndefined();
Expand Down
10 changes: 4 additions & 6 deletions spec/PushController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@ const successfulIOS = function (body, installations) {
return Promise.all(promises);
};

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

const pushCompleted = async pushId => {
const query = new Parse.Query('_PushStatus');
query.equalTo('objectId', pushId);
let result = await query.first({ useMasterKey: true });
while (!(result && result.get('status') === 'succeeded')) {
await sleep(100);
await jasmine.timeout();
result = await query.first({ useMasterKey: true });
}
};
Expand Down Expand Up @@ -562,7 +560,7 @@ describe('PushController', () => {
});
const pushStatusId = await sendPush(payload, {}, config, auth);
// it is enqueued so it can take time
await sleep(1000);
await jasmine.timeout(1000);
Parse.serverURL = 'http://localhost:8378/1'; // GOOD url
const result = await Parse.Push.getPushStatus(pushStatusId);
expect(result).toBeDefined();
Expand Down Expand Up @@ -801,7 +799,7 @@ describe('PushController', () => {
});
await Parse.Object.saveAll(installations);
await pushController.sendPush(payload, {}, config, auth);
await sleep(1000);
await jasmine.timeout(1000);
const query = new Parse.Query('_PushStatus');
const results = await query.find({ useMasterKey: true });
expect(results.length).toBe(1);
Expand Down Expand Up @@ -856,7 +854,7 @@ describe('PushController', () => {
const config = Config.get(Parse.applicationId);
await Parse.Object.saveAll(installations);
await pushController.sendPush(payload, {}, config, auth);
await sleep(1000);
await jasmine.timeout(1000);
const query = new Parse.Query('_PushStatus');
const results = await query.find({ useMasterKey: true });
expect(results.length).toBe(1);
Expand Down
66 changes: 44 additions & 22 deletions spec/UserController.spec.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
const emailAdapter = require('./support/MockEmailAdapter');
const Config = require('../lib/Config');
const Auth = require('../lib/Auth');

describe('UserController', () => {
const user = {
_email_verify_token: 'testToken',
username: 'testUser',
email: 'test@example.com',
};

describe('sendVerificationEmail', () => {
describe('parseFrameURL not provided', () => {
it('uses publicServerURL', async done => {
const server = await reconfigureServer({
it('uses publicServerURL', async () => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: undefined,
Expand All @@ -19,20 +15,33 @@ describe('UserController', () => {
emailAdapter,
appName: 'test',
});

let emailOptions;
emailAdapter.sendVerificationEmail = options => {
expect(options.link).toEqual(
'http://www.example.com/apps/test/verify_email?token=testToken&username=testUser'
);
emailAdapter.sendVerificationEmail = () => Promise.resolve();
done();
emailOptions = options;
return Promise.resolve();
};
server.config.userController.sendVerificationEmail(user);

const username = 'verificationUser';
const user = new Parse.User();
user.setUsername(username);
user.setPassword('pass');
user.setEmail('verification@example.com');
await user.signUp();

const config = Config.get('test');
const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config));
const rawUsername = rawUser[0].username;
const rawToken = rawUser[0]._email_verify_token;
expect(rawToken).toBeDefined();
expect(rawUsername).toBe(username);
expect(emailOptions.link).toEqual(`http://www.example.com/apps/test/verify_email?token=${rawToken}&username=${username}`);
});
});

describe('parseFrameURL provided', () => {
it('uses parseFrameURL and includes the destination in the link parameter', async done => {
const server = await reconfigureServer({
it('uses parseFrameURL and includes the destination in the link parameter', async () => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: 'http://someother.example.com/handle-parse-iframe',
Expand All @@ -41,14 +50,27 @@ describe('UserController', () => {
emailAdapter,
appName: 'test',
});

let emailOptions;
emailAdapter.sendVerificationEmail = options => {
expect(options.link).toEqual(
'http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=testToken&username=testUser'
);
emailAdapter.sendVerificationEmail = () => Promise.resolve();
done();
emailOptions = options;
return Promise.resolve();
};
server.config.userController.sendVerificationEmail(user);

const username = 'verificationUser';
const user = new Parse.User();
user.setUsername(username);
user.setPassword('pass');
user.setEmail('verification@example.com');
await user.signUp();

const config = Config.get('test');
const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config));
const rawUsername = rawUser[0].username;
const rawToken = rawUser[0]._email_verify_token;
expect(rawToken).toBeDefined();
expect(rawUsername).toBe(username);
expect(emailOptions.link).toEqual(`http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=${rawToken}&username=${username}`);
});
});
});
Expand Down
Loading

0 comments on commit 1eb95ae

Please sign in to comment.