Skip to content

Commit 32f7230

Browse files
authored
Creates a new sessionToken when updating password (#2266)
* Creates a new sessionToken when updating password * Adds test ensuring email is properly sent when upgrading from anon
1 parent f1ff9fe commit 32f7230

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

spec/ParseUser.spec.js

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2257,12 +2257,14 @@ describe('Parse.User testing', () => {
22572257
})
22582258
});
22592259

2260-
it('should cleanup null authData keys ParseUser update (regression test for #1198)', (done) => {
2260+
it_exclude_dbs(['postgres'])('should cleanup null authData keys ParseUser update (regression test for #1198, #2252)', (done) => {
22612261
Parse.Cloud.beforeSave('_User', (req, res) => {
22622262
req.object.set('foo', 'bar');
22632263
res.success();
22642264
});
2265-
2265+
2266+
let originalSessionToken;
2267+
let originalUserId;
22662268
// Simulate anonymous user save
22672269
new Promise((resolve, reject) => {
22682270
request.post({
@@ -2280,6 +2282,8 @@ describe('Parse.User testing', () => {
22802282
}
22812283
});
22822284
}).then((user) => {
2285+
originalSessionToken = user.sessionToken;
2286+
originalUserId = user.objectId;
22832287
// Simulate registration
22842288
return new Promise((resolve, reject) => {
22852289
request.put({
@@ -2291,7 +2295,7 @@ describe('Parse.User testing', () => {
22912295
},
22922296
json: {
22932297
authData: {anonymous: null},
2294-
user: 'user',
2298+
username: 'user',
22952299
password: 'password',
22962300
}
22972301
}, (err, res, body) => {
@@ -2305,8 +2309,84 @@ describe('Parse.User testing', () => {
23052309
}).then((user) => {
23062310
expect(typeof user).toEqual('object');
23072311
expect(user.authData).toBeUndefined();
2312+
expect(user.sessionToken).not.toBeUndefined();
2313+
// Session token should have changed
2314+
expect(user.sessionToken).not.toEqual(originalSessionToken);
2315+
// test that the sessionToken is valid
2316+
return new Promise((resolve, reject) => {
2317+
request.get({
2318+
url: 'http://localhost:8378/1/users/me',
2319+
headers: {
2320+
'X-Parse-Application-Id': Parse.applicationId,
2321+
'X-Parse-Session-Token': user.sessionToken,
2322+
'X-Parse-REST-API-Key': 'rest',
2323+
},
2324+
json: true
2325+
}, (err, res, body) => {
2326+
expect(body.username).toEqual(user.username);
2327+
expect(body.objectId).toEqual(originalUserId);
2328+
if (err) {
2329+
reject(err);
2330+
} else {
2331+
resolve(body);
2332+
}
2333+
done();
2334+
});
2335+
});
2336+
}).catch((err) => {
2337+
fail('no request should fail: ' + JSON.stringify(err));
2338+
done();
2339+
});
2340+
});
2341+
2342+
it_exclude_dbs(['postgres'])('should send email when upgrading from anon', (done) => {
2343+
2344+
let emailCalled = false;
2345+
let emailOptions;
2346+
var emailAdapter = {
2347+
sendVerificationEmail: (options) => {
2348+
emailOptions = options;
2349+
emailCalled = true;
2350+
},
2351+
sendPasswordResetEmail: () => Promise.resolve(),
2352+
sendMail: () => Promise.resolve()
2353+
}
2354+
reconfigureServer({
2355+
appName: 'unused',
2356+
verifyUserEmails: true,
2357+
emailAdapter: emailAdapter,
2358+
publicServerURL: "http://localhost:8378/1"
2359+
})
2360+
// Simulate anonymous user save
2361+
return rp.post({
2362+
url: 'http://localhost:8378/1/classes/_User',
2363+
headers: {
2364+
'X-Parse-Application-Id': Parse.applicationId,
2365+
'X-Parse-REST-API-Key': 'rest',
2366+
},
2367+
json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}}
2368+
}).then((user) => {
2369+
return rp.put({
2370+
url: 'http://localhost:8378/1/classes/_User/' + user.objectId,
2371+
headers: {
2372+
'X-Parse-Application-Id': Parse.applicationId,
2373+
'X-Parse-Session-Token': user.sessionToken,
2374+
'X-Parse-REST-API-Key': 'rest',
2375+
},
2376+
json: {
2377+
authData: {anonymous: null},
2378+
username: 'user',
2379+
2380+
password: 'password',
2381+
}
2382+
});
2383+
}).then(() => {
2384+
expect(emailCalled).toBe(true);
2385+
expect(emailOptions).not.toBeUndefined();
2386+
expect(emailOptions.user.get('email')).toEqual('[email protected]');
23082387
done();
23092388
}).catch((err) => {
2389+
console.error(err);
23102390
fail('no request should fail: ' + JSON.stringify(err));
23112391
done();
23122392
});
@@ -2471,9 +2551,16 @@ describe('Parse.User testing', () => {
24712551
user.set('password', 'password');
24722552
return user.save()
24732553
})
2554+
.then(() => {
2555+
// Session token should have been recycled
2556+
expect(body.sessionToken).not.toEqual(user.getSessionToken());
2557+
})
24742558
.then(() => obj.fetch())
2559+
.then((res) => {
2560+
done();
2561+
})
24752562
.catch(error => {
2476-
expect(error.code).toEqual(Parse.Error.INVALID_SESSION_TOKEN);
2563+
fail('should not fail')
24772564
done();
24782565
});
24792566
})

src/RestWrite.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ RestWrite.prototype.transformUser = function() {
367367
}
368368
if (this.query && !this.auth.isMaster ) {
369369
this.storage['clearSessions'] = true;
370+
this.storage['generateNewSession'] = true;
370371
}
371372
return passwordCrypto.hash(this.data.password).then((hashedPassword) => {
372373
this.data._hashed_password = hashedPassword;
@@ -428,6 +429,10 @@ RestWrite.prototype.createSessionTokenIfNeeded = function() {
428429
if (this.query) {
429430
return;
430431
}
432+
return this.createSessionToken();
433+
}
434+
435+
RestWrite.prototype.createSessionToken = function() {
431436
var token = 'r:' + cryptoUtils.newToken();
432437

433438
var expiresAt = this.config.generateSessionExpiresAt();
@@ -464,15 +469,21 @@ RestWrite.prototype.handleFollowup = function() {
464469
}
465470
};
466471
delete this.storage['clearSessions'];
467-
this.config.database.destroy('_Session', sessionQuery)
472+
return this.config.database.destroy('_Session', sessionQuery)
473+
.then(this.handleFollowup.bind(this));
474+
}
475+
476+
if (this.storage && this.storage['generateNewSession']) {
477+
delete this.storage['generateNewSession'];
478+
return this.createSessionToken()
468479
.then(this.handleFollowup.bind(this));
469480
}
470481

471482
if (this.storage && this.storage['sendVerificationEmail']) {
472483
delete this.storage['sendVerificationEmail'];
473484
// Fire and forget!
474485
this.config.userController.sendVerificationEmail(this.data);
475-
this.handleFollowup.bind(this);
486+
return this.handleFollowup.bind(this);
476487
}
477488
};
478489

0 commit comments

Comments
 (0)