Files
kami-parse-server/spec/ValidationAndPasswordsReset.spec.js
dblythy ed499e32a2 feat: Remove deprecation DEPPS3: Config option enforcePrivateUsers defaults to true (#8283)
BREAKING CHANGE: The Parse Server option `enforcePrivateUsers` is set to `true` by default; in previous releases this option defaults to `false`; this change improves the default security configuration of Parse Server (#8283)
2022-11-16 22:59:44 +01:00

1086 lines
35 KiB
JavaScript

'use strict';
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
const request = require('../lib/request');
const Config = require('../lib/Config');
describe('Custom Pages, Email Verification, Password Reset', () => {
it('should set the custom pages', done => {
reconfigureServer({
appName: 'unused',
customPages: {
invalidLink: 'myInvalidLink',
verifyEmailSuccess: 'myVerifyEmailSuccess',
choosePassword: 'myChoosePassword',
passwordResetSuccess: 'myPasswordResetSuccess',
parseFrameURL: 'http://example.com/handle-parse-iframe',
},
publicServerURL: 'https://my.public.server.com/1',
}).then(() => {
const config = Config.get('test');
expect(config.invalidLinkURL).toEqual('myInvalidLink');
expect(config.verifyEmailSuccessURL).toEqual('myVerifyEmailSuccess');
expect(config.choosePasswordURL).toEqual('myChoosePassword');
expect(config.passwordResetSuccessURL).toEqual('myPasswordResetSuccess');
expect(config.parseFrameURL).toEqual('http://example.com/handle-parse-iframe');
expect(config.verifyEmailURL).toEqual(
'https://my.public.server.com/1/apps/test/verify_email'
);
expect(config.requestResetPasswordURL).toEqual(
'https://my.public.server.com/1/apps/test/request_password_reset'
);
done();
});
});
it('sends verification email if email verification is enabled', done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
reconfigureServer({
appName: 'unused',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(async () => {
spyOn(emailAdapter, 'sendVerificationEmail');
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.setEmail('testIfEnabled@parse.com');
await user.signUp();
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
user.fetch().then(() => {
expect(user.get('emailVerified')).toEqual(false);
done();
});
});
});
it('does not send verification email when verification is enabled and email is not set', done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
reconfigureServer({
appName: 'unused',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(async () => {
spyOn(emailAdapter, 'sendVerificationEmail');
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
await user.signUp();
expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled();
user.fetch().then(() => {
expect(user.get('emailVerified')).toEqual(undefined);
done();
});
});
});
it('does send a validation email when updating the email', done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
reconfigureServer({
appName: 'unused',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(async () => {
spyOn(emailAdapter, 'sendVerificationEmail');
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
await user.signUp();
expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled();
user
.fetch()
.then(user => {
user.set('email', 'testWhenUpdating@parse.com');
return user.save();
})
.then(user => {
return user.fetch();
})
.then(() => {
expect(user.get('emailVerified')).toEqual(false);
// Wait as on update email, we need to fetch the username
setTimeout(function () {
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
done();
}, 200);
});
});
});
it('does send a validation email with valid verification link when updating the email', async done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
await reconfigureServer({
appName: 'unused',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
});
spyOn(emailAdapter, 'sendVerificationEmail').and.callFake(options => {
expect(options.link).not.toBeNull();
expect(options.link).not.toMatch(/token=undefined/);
Promise.resolve();
});
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
await user.signUp();
expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled();
await user.fetch();
user.set('email', 'testValidLinkWhenUpdating@parse.com');
await user.save();
await user.fetch();
expect(user.get('emailVerified')).toEqual(false);
// Wait as on update email, we need to fetch the username
setTimeout(function () {
expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled();
done();
}, 200);
});
it('does send with a simple adapter', done => {
let calls = 0;
const emailAdapter = {
sendMail: function (options) {
expect(options.to).toBe('testSendSimpleAdapter@parse.com');
if (calls == 0) {
expect(options.subject).toEqual('Please verify your e-mail for My Cool App');
expect(options.text.match(/verify_email/)).not.toBe(null);
} else if (calls == 1) {
expect(options.subject).toEqual('Password Reset for My Cool App');
expect(options.text.match(/request_password_reset/)).not.toBe(null);
}
calls++;
return Promise.resolve();
},
};
reconfigureServer({
appName: 'My Cool App',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(async () => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testSendSimpleAdapter@parse.com');
await user.signUp();
expect(calls).toBe(1);
user
.fetch()
.then(user => {
return user.save();
})
.then(() => {
return Parse.User.requestPasswordReset('testSendSimpleAdapter@parse.com').catch(() => {
fail('Should not fail requesting a password');
done();
});
})
.then(() => {
expect(calls).toBe(2);
done();
});
});
});
it('prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true', done => {
reconfigureServer({
appName: 'test',
publicServerURL: 'http://localhost:1337/1',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: true,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(user => {
expect(user.getSessionToken()).toBe(undefined);
return Parse.User.logIn('zxcv', 'asdf');
})
.then(
() => {
fail('login should have failed');
done();
},
error => {
expect(error.message).toEqual('User email is not verified.');
done();
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
})
.then(() => {
user.setPassword('other-password');
user.setUsername('user');
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => {
expect(sendEmailOptions).not.toBeUndefined();
request({
url: sendEmailOptions.link,
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user'
);
user
.fetch({ useMasterKey: true })
.then(
() => {
expect(user.get('emailVerified')).toEqual(true);
Parse.User.logIn('user', 'other-password').then(
user => {
expect(typeof user).toBe('object');
expect(user.get('emailVerified')).toBe(true);
done();
},
() => {
fail('login should have succeeded');
done();
}
);
},
err => {
jfail(err);
fail('this should not fail');
done();
}
)
.catch(err => {
jfail(err);
done();
});
});
});
});
it('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => {
reconfigureServer({
appName: 'test',
publicServerURL: 'http://localhost:1337/1',
verifyUserEmails: true,
preventLoginWithUnverifiedEmail: false,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.logIn('zxcv', 'asdf'))
.then(
user => {
expect(typeof user).toBe('object');
expect(user.get('emailVerified')).toBe(false);
done();
},
() => {
fail('login should have succeeded');
done();
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => {
reconfigureServer({
appName: undefined,
publicServerURL: 'http://localhost:1337/1',
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com'))
.then(
() => {
fail('sending password reset email should not have succeeded');
done();
},
error => {
expect(error.message).toEqual(
'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.'
);
done();
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('fails if you include an emailAdapter, have an appName, but have no publicServerURL and send a password reset email', done => {
reconfigureServer({
appName: undefined,
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com'))
.then(
() => {
fail('sending password reset email should not have succeeded');
done();
},
error => {
expect(error.message).toEqual(
'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.'
);
done();
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('fails if you set a publicServerURL, have an appName, but no emailAdapter and send a password reset email', done => {
reconfigureServer({
appName: 'unused',
publicServerURL: 'http://localhost:1337/1',
emailAdapter: undefined,
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com'))
.then(
() => {
fail('sending password reset email should not have succeeded');
done();
},
error => {
expect(error.message).toEqual(
'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.'
);
done();
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('succeeds sending a password reset email if appName, publicServerURL, and email adapter are provided', done => {
reconfigureServer({
appName: 'coolapp',
publicServerURL: 'http://localhost:1337/1',
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'testInvalidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com'))
.then(
() => {
done();
},
error => {
done(error);
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('succeeds sending a password reset username if appName, publicServerURL, and email adapter are provided', done => {
const adapter = MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
sendMail: function (options) {
expect(options.to).toEqual('testValidConfig@parse.com');
return Promise.resolve();
},
});
// delete that handler to force using the default
delete adapter.sendPasswordResetEmail;
spyOn(adapter, 'sendMail').and.callThrough();
reconfigureServer({
appName: 'coolapp',
publicServerURL: 'http://localhost:1337/1',
emailAdapter: adapter,
})
.then(() => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('testValidConfig@parse.com');
user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('testValidConfig@parse.com'))
.then(
() => {
expect(adapter.sendMail).toHaveBeenCalled();
done();
},
error => {
done(error);
}
);
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
it('does not send verification email if email verification is disabled', done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve(),
};
reconfigureServer({
appName: 'unused',
publicServerURL: 'http://localhost:1337/1',
verifyUserEmails: false,
emailAdapter: emailAdapter,
}).then(async () => {
spyOn(emailAdapter, 'sendVerificationEmail');
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
await user.signUp();
await user.fetch();
expect(emailAdapter.sendVerificationEmail.calls.count()).toEqual(0);
expect(user.get('emailVerified')).toEqual(undefined);
done();
});
});
it('receives the app name and user in the adapter', done => {
let emailSent = false;
const emailAdapter = {
sendVerificationEmail: options => {
expect(options.appName).toEqual('emailing app');
expect(options.user.get('email')).toEqual('user@parse.com');
emailSent = true;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(async () => {
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'user@parse.com');
await user.signUp();
expect(emailSent).toBe(true);
done();
});
});
it('when you click the link in the email it sets emailVerified to true and redirects you', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
sendEmailOptions = options;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
})
.then(() => {
user.setPassword('other-password');
user.setUsername('user');
user.set('email', 'user@parse.com');
return user.signUp();
})
.then(() => {
expect(sendEmailOptions).not.toBeUndefined();
request({
url: sendEmailOptions.link,
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user'
);
user
.fetch()
.then(
() => {
expect(user.get('emailVerified')).toEqual(true);
done();
},
err => {
jfail(err);
fail('this should not fail');
done();
}
)
.catch(err => {
jfail(err);
done();
});
});
});
});
it('redirects you to invalid link if you try to verify email incorrectly', done => {
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
},
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
request({
url: 'http://localhost:8378/1/apps/test/verify_email',
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html'
);
done();
});
});
});
it('redirects you to invalid verification link page if you try to validate a nonexistant users email', done => {
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
},
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
request({
url: 'http://localhost:8378/1/apps/test/verify_email?token=asdfasdf&username=sadfasga',
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=sadfasga&appId=test'
);
done();
});
});
});
it('redirects you to link send fail page if you try to resend a link for a nonexistant user', done => {
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
},
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
request({
url: 'http://localhost:8378/1/apps/test/resend_verification_email',
method: 'POST',
followRedirects: false,
body: {
username: 'sadfasga',
},
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html'
);
done();
});
});
});
it('does not update email verified if you use an invalid token', done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => {
request({
url: 'http://localhost:8378/1/apps/test/verify_email?token=invalid&username=zxcv',
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?username=zxcv&appId=test'
);
user.fetch().then(() => {
expect(user.get('emailVerified')).toEqual(false);
done();
});
});
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'user@parse.com');
user.signUp(null, {
success: () => {},
error: function () {
fail('Failed to save user');
done();
},
});
});
});
it('should send a password reset link', done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: options => {
request({
url: options.link,
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&username=zxcv%2Bzxcv/;
expect(response.text.match(re)).not.toBe(null);
done();
});
},
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
user.setPassword('asdf');
user.setUsername('zxcv+zxcv');
user.set('email', 'user@parse.com');
user.signUp().then(() => {
Parse.User.requestPasswordReset('user@parse.com', {
error: err => {
jfail(err);
fail('Should not fail requesting a password');
done();
},
});
});
});
});
it('redirects you to invalid link if you try to request password for a nonexistant users email', done => {
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => {},
},
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
request({
url:
'http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf&username=sadfasga',
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html'
);
done();
});
});
});
it('should programmatically reset password', done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: options => {
request({
url: options.link,
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&username=zxcv/;
const match = response.text.match(re);
if (!match) {
fail('should have a token');
done();
return;
}
const token = match[1];
request({
url: 'http://localhost:8378/1/apps/test/request_password_reset',
method: 'POST',
body: { new_password: 'hello', token, username: 'zxcv' },
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html?username=zxcv'
);
Parse.User.logIn('zxcv', 'hello').then(
function () {
const config = Config.get('test');
config.database.adapter
.find('_User', { fields: {} }, { username: 'zxcv' }, { limit: 1 })
.then(results => {
// _perishable_token should be unset after reset password
expect(results.length).toEqual(1);
expect(results[0]['_perishable_token']).toEqual(undefined);
done();
});
},
err => {
jfail(err);
fail('should login with new password');
done();
}
);
});
});
},
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'user@parse.com');
user.signUp().then(() => {
Parse.User.requestPasswordReset('user@parse.com', {
error: err => {
jfail(err);
fail('Should not fail');
done();
},
});
});
});
});
it('should redirect with username encoded on success page', done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: options => {
request({
url: options.link,
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&username=zxcv%2B1/;
const match = response.text.match(re);
if (!match) {
fail('should have a token');
done();
return;
}
const token = match[1];
request({
url: 'http://localhost:8378/1/apps/test/request_password_reset',
method: 'POST',
body: { new_password: 'hello', token, username: 'zxcv+1' },
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
followRedirects: false,
}).then(response => {
expect(response.status).toEqual(302);
expect(response.text).toEqual(
'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html?username=zxcv%2B1'
);
done();
});
});
},
sendMail: () => {},
};
reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
}).then(() => {
user.setPassword('asdf');
user.setUsername('zxcv+1');
user.set('email', 'user@parse.com');
user.signUp().then(() => {
Parse.User.requestPasswordReset('user@parse.com', {
error: err => {
jfail(err);
fail('Should not fail');
done();
},
});
});
});
});
it('should programmatically reset password on ajax request', async done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: async options => {
const response = await request({
url: options.link,
followRedirects: false,
});
expect(response.status).toEqual(302);
const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&username=zxcv/;
const match = response.text.match(re);
if (!match) {
fail('should have a token');
return;
}
const token = match[1];
const resetResponse = await request({
url: 'http://localhost:8378/1/apps/test/request_password_reset',
method: 'POST',
body: { new_password: 'hello', token, username: 'zxcv' },
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest',
},
followRedirects: false,
});
expect(resetResponse.status).toEqual(200);
expect(resetResponse.text).toEqual('"Password successfully reset"');
await Parse.User.logIn('zxcv', 'hello');
const config = Config.get('test');
const results = await config.database.adapter.find(
'_User',
{ fields: {} },
{ username: 'zxcv' },
{ limit: 1 }
);
// _perishable_token should be unset after reset password
expect(results.length).toEqual(1);
expect(results[0]['_perishable_token']).toEqual(undefined);
done();
},
sendMail: () => {},
};
await reconfigureServer({
appName: 'emailing app',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: 'http://localhost:8378/1',
});
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'user@parse.com');
await user.signUp();
await Parse.User.requestPasswordReset('user@parse.com');
});
it('should return ajax failure error on ajax request with wrong data provided', async () => {
await reconfigureServer({
publicServerURL: 'http://localhost:8378/1',
});
try {
await request({
method: 'POST',
url: 'http://localhost:8378/1/apps/test/request_password_reset',
body: `new_password=user1&token=12345&username=Johnny`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest',
},
followRedirects: false,
});
} catch (error) {
expect(error.status).not.toBe(302);
expect(error.text).toEqual(
'{"code":-1,"error":"Failed to reset password: username / email / token is invalid"}'
);
}
});
it('deletes password reset token on email address change', done => {
reconfigureServer({
appName: 'coolapp',
publicServerURL: 'http://localhost:1337/1',
emailAdapter: MockEmailAdapterWithOptions({
fromAddress: 'parse@example.com',
apiKey: 'k',
domain: 'd',
}),
})
.then(() => {
const config = Config.get('test');
const user = new Parse.User();
user.setPassword('asdf');
user.setUsername('zxcv');
user.set('email', 'test@parse.com');
return user
.signUp(null)
.then(() => Parse.User.requestPasswordReset('test@parse.com'))
.then(() =>
config.database.adapter.find(
'_User',
{ fields: {} },
{ username: 'zxcv' },
{ limit: 1 }
)
)
.then(results => {
// validate that there is a token
expect(results.length).toEqual(1);
expect(results[0]['_perishable_token']).not.toBeNull();
user.set('email', 'test2@parse.com');
return user.save();
})
.then(() =>
config.database.adapter.find(
'_User',
{ fields: {} },
{ username: 'zxcv' },
{ limit: 1 }
)
)
.then(results => {
expect(results.length).toEqual(1);
expect(results[0]['_perishable_token']).toBeUndefined();
done();
});
})
.catch(error => {
fail(JSON.stringify(error));
done();
});
});
});