Skip to content

Commit 5c8ad83

Browse files
committed
Merge pull request #1001 from ParsePlatform/flovilmart.queryStringForEmailResets
Properly querystring encode the parameters
2 parents 9624970 + 0bd21cb commit 5c8ad83

File tree

3 files changed

+67
-62
lines changed

3 files changed

+67
-62
lines changed

spec/ValidationAndPasswordsReset.spec.js

+16-17
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ describe("Custom Pages Configuration", () => {
2323
},
2424
publicServerURL: "https://my.public.server.com/1"
2525
});
26-
26+
2727
var config = new Config("test");
28-
28+
2929
expect(config.invalidLinkURL).toEqual("myInvalidLink");
3030
expect(config.verifyEmailSuccessURL).toEqual("myVerifyEmailSuccess");
3131
expect(config.choosePasswordURL).toEqual("myChoosePassword");
@@ -78,7 +78,7 @@ describe("Email Verification", () => {
7878
}
7979
});
8080
});
81-
81+
8282
it('does not send verification email when verification is enabled and email is not set', done => {
8383
var emailAdapter = {
8484
sendVerificationEmail: () => Promise.resolve(),
@@ -119,7 +119,7 @@ describe("Email Verification", () => {
119119
}
120120
});
121121
});
122-
122+
123123
it('does send a validation email when updating the email', done => {
124124
var emailAdapter = {
125125
sendVerificationEmail: () => Promise.resolve(),
@@ -169,7 +169,7 @@ describe("Email Verification", () => {
169169
}
170170
});
171171
});
172-
172+
173173
it('does send with a simple adapter', done => {
174174
var calls = 0;
175175
var emailAdapter = {
@@ -311,7 +311,7 @@ describe("Email Verification", () => {
311311
followRedirect: false,
312312
}, (error, response, body) => {
313313
expect(response.statusCode).toEqual(302);
314-
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=zxcv');
314+
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=user');
315315
user.fetch()
316316
.then(() => {
317317
expect(user.get('emailVerified')).toEqual(true);
@@ -342,7 +342,7 @@ describe("Email Verification", () => {
342342
publicServerURL: "http://localhost:8378/1"
343343
});
344344
user.setPassword("asdf");
345-
user.setUsername("zxcv");
345+
user.setUsername("user");
346346
user.set('email', '[email protected]');
347347
user.signUp();
348348
});
@@ -453,7 +453,7 @@ describe("Email Verification", () => {
453453
});
454454

455455
describe("Password Reset", () => {
456-
456+
457457
it('should send a password reset link', done => {
458458
var user = new Parse.User();
459459
var emailAdapter = {
@@ -468,7 +468,7 @@ describe("Password Reset", () => {
468468
return;
469469
}
470470
expect(response.statusCode).toEqual(302);
471-
var re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&username=zxcv/;
471+
var re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&username=zxcv%2Bzxcv/;
472472
expect(response.body.match(re)).not.toBe(null);
473473
done();
474474
});
@@ -491,7 +491,7 @@ describe("Password Reset", () => {
491491
publicServerURL: "http://localhost:8378/1"
492492
});
493493
user.setPassword("asdf");
494-
user.setUsername("zxcv");
494+
user.setUsername("zxcv+zxcv");
495495
user.set('email', '[email protected]');
496496
user.signUp().then(() => {
497497
Parse.User.requestPasswordReset('[email protected]', {
@@ -503,7 +503,7 @@ describe("Password Reset", () => {
503503
});
504504
});
505505
});
506-
506+
507507
it('redirects you to invalid link if you try to request password for a nonexistant users email', done => {
508508
setServerConfiguration({
509509
serverURL: 'http://localhost:8378/1',
@@ -555,8 +555,8 @@ describe("Password Reset", () => {
555555
return;
556556
}
557557
var token = match[1];
558-
559-
request.post({
558+
559+
request.post({
560560
url: "http://localhost:8378/1/apps/test/request_password_reset" ,
561561
body: `new_password=hello&token=${token}&username=zxcv`,
562562
headers: {
@@ -571,15 +571,15 @@ describe("Password Reset", () => {
571571
}
572572
expect(response.statusCode).toEqual(302);
573573
expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html');
574-
574+
575575
Parse.User.logIn("zxcv", "hello").then(function(user){
576576
done();
577577
}, (err) => {
578578
console.error(err);
579579
fail("should login with new password");
580580
done();
581581
});
582-
582+
583583
});
584584
});
585585
},
@@ -613,6 +613,5 @@ describe("Password Reset", () => {
613613
});
614614
});
615615
});
616-
617-
})
618616

617+
})

src/PromiseRouter.js

+15-13
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ export default class PromiseRouter {
2121
this.routes = routes;
2222
this.mountRoutes();
2323
}
24-
24+
2525
// Leave the opportunity to
2626
// subclasses to mount their routes by overriding
2727
mountRoutes() {}
28-
28+
2929
// Merge the routes into this one
3030
merge(router) {
3131
for (var route of router.routes) {
3232
this.routes.push(route);
3333
}
3434
};
35-
35+
3636
route(method, path, ...handlers) {
3737
switch(method) {
3838
case 'POST':
@@ -45,7 +45,7 @@ export default class PromiseRouter {
4545
}
4646

4747
let handler = handlers[0];
48-
48+
4949
if (handlers.length > 1) {
5050
const length = handlers.length;
5151
handler = function(req) {
@@ -63,7 +63,7 @@ export default class PromiseRouter {
6363
handler: handler
6464
});
6565
};
66-
66+
6767
// Returns an object with:
6868
// handler: the handler that should deal with this request
6969
// params: any :-params that got parsed from the path
@@ -99,7 +99,7 @@ export default class PromiseRouter {
9999
return {params: params, handler: route.handler};
100100
}
101101
};
102-
102+
103103
// Mount the routes on this router onto an express app (or express router)
104104
mountOnto(expressApp) {
105105
for (var route of this.routes) {
@@ -121,7 +121,7 @@ export default class PromiseRouter {
121121
}
122122
}
123123
};
124-
124+
125125
expressApp() {
126126
var expressApp = express();
127127
for (var route of this.routes) {
@@ -168,19 +168,21 @@ function makeExpressHandler(promiseHandler) {
168168
if (PromiseRouter.verbose) {
169169
console.log('response:', JSON.stringify(result, null, 2));
170170
}
171-
171+
172172
var status = result.status || 200;
173173
res.status(status);
174-
174+
175175
if (result.text) {
176176
return res.send(result.text);
177177
}
178-
179-
if (result.location && !result.response) {
180-
return res.redirect(result.location);
181-
}
178+
182179
if (result.location) {
183180
res.set('Location', result.location);
181+
// Override the default expressjs response
182+
// as it double encodes %encoded chars in URL
183+
if (!result.response) {
184+
return res.send('Found. Redirecting to '+result.location);
185+
}
184186
}
185187
res.json(result.response);
186188
}, (e) => {

src/Routers/PublicAPIRouter.js

+36-32
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,38 @@ import Config from '../Config';
44
import express from 'express';
55
import path from 'path';
66
import fs from 'fs';
7+
import qs from 'querystring';
78

89
let public_html = path.resolve(__dirname, "../../public_html");
910
let views = path.resolve(__dirname, '../../views');
1011

1112
export class PublicAPIRouter extends PromiseRouter {
12-
13+
1314
verifyEmail(req) {
1415
let { token, username }= req.query;
1516
let appId = req.params.appId;
1617
let config = new Config(appId);
17-
18+
1819
if (!config.publicServerURL) {
1920
return this.missingPublicServerURL();
2021
}
21-
22+
2223
if (!token || !username) {
2324
return this.invalidLink(req);
2425
}
2526

2627
let userController = config.userController;
2728
return userController.verifyEmail(username, token).then( () => {
29+
let params = qs.stringify({username});
2830
return Promise.resolve({
2931
status: 302,
30-
location: `${config.verifyEmailSuccessURL}?username=${username}`
32+
location: `${config.verifyEmailSuccessURL}?${params}`
3133
});
3234
}, ()=> {
3335
return this.invalidLink(req);
3436
})
3537
}
36-
38+
3739
changePassword(req) {
3840
return new Promise((resolve, reject) => {
3941
let config = new Config(req.query.id);
@@ -55,61 +57,63 @@ export class PublicAPIRouter extends PromiseRouter {
5557
});
5658
});
5759
}
58-
60+
5961
requestResetPassword(req) {
6062

6163
let config = req.config;
62-
64+
6365
if (!config.publicServerURL) {
6466
return this.missingPublicServerURL();
6567
}
66-
68+
6769
let { username, token } = req.query;
68-
70+
6971
if (!username || !token) {
7072
return this.invalidLink(req);
7173
}
72-
74+
7375
return config.userController.checkResetTokenValidity(username, token).then( (user) => {
76+
let params = qs.stringify({token, id: config.applicationId, username, app: config.appName, });
7477
return Promise.resolve({
7578
status: 302,
76-
location: `${config.choosePasswordURL}?token=${token}&id=${config.applicationId}&username=${username}&app=${config.appName}`
79+
location: `${config.choosePasswordURL}?${params}`
7780
})
7881
}, () => {
7982
return this.invalidLink(req);
8083
})
8184
}
82-
85+
8386
resetPassword(req) {
8487

8588
let config = req.config;
86-
89+
8790
if (!config.publicServerURL) {
8891
return this.missingPublicServerURL();
8992
}
90-
93+
9194
let {
9295
username,
9396
token,
9497
new_password
9598
} = req.body;
96-
99+
97100
if (!username || !token || !new_password) {
98101
return this.invalidLink(req);
99102
}
100-
103+
101104
return config.userController.updatePassword(username, token, new_password).then((result) => {
102105
return Promise.resolve({
103106
status: 302,
104107
location: config.passwordResetSuccessURL
105108
});
106109
}, (err) => {
110+
let params = qs.stringify({username: username, token: token, id: config.applicationId, error:err, app:config.appName})
107111
return Promise.resolve({
108112
status: 302,
109-
location: `${config.choosePasswordURL}?token=${token}&id=${config.applicationId}&username=${username}&error=${err}&app=${config.appName}`
113+
location: `${config.choosePasswordURL}?${params}`
110114
});
111115
});
112-
116+
113117
}
114118

115119
invalidLink(req) {
@@ -118,36 +122,36 @@ export class PublicAPIRouter extends PromiseRouter {
118122
location: req.config.invalidLinkURL
119123
});
120124
}
121-
125+
122126
missingPublicServerURL() {
123127
return Promise.resolve({
124128
text: 'Not found.',
125129
status: 404
126130
});
127131
}
128-
132+
129133
setConfig(req) {
130134
req.config = new Config(req.params.appId);
131135
return Promise.resolve();
132136
}
133-
137+
134138
mountRoutes() {
135-
this.route('GET','/apps/:appId/verify_email',
136-
req => { this.setConfig(req) },
139+
this.route('GET','/apps/:appId/verify_email',
140+
req => { this.setConfig(req) },
137141
req => { return this.verifyEmail(req); });
138-
139-
this.route('GET','/apps/choose_password',
142+
143+
this.route('GET','/apps/choose_password',
140144
req => { return this.changePassword(req); });
141-
142-
this.route('POST','/apps/:appId/request_password_reset',
143-
req => { this.setConfig(req) },
145+
146+
this.route('POST','/apps/:appId/request_password_reset',
147+
req => { this.setConfig(req) },
144148
req => { return this.resetPassword(req); });
145-
146-
this.route('GET','/apps/:appId/request_password_reset',
147-
req => { this.setConfig(req) },
149+
150+
this.route('GET','/apps/:appId/request_password_reset',
151+
req => { this.setConfig(req) },
148152
req => { return this.requestResetPassword(req); });
149153
}
150-
154+
151155
expressApp() {
152156
let router = express();
153157
router.use("/apps", express.static(public_html));

0 commit comments

Comments
 (0)