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
This commit is contained in:
Florent Vilmart
2016-07-13 07:18:24 -04:00
committed by GitHub
parent f1ff9fecce
commit 32f7230aca
2 changed files with 104 additions and 6 deletions

View File

@@ -2257,12 +2257,14 @@ describe('Parse.User testing', () => {
}) })
}); });
it('should cleanup null authData keys ParseUser update (regression test for #1198)', (done) => { it_exclude_dbs(['postgres'])('should cleanup null authData keys ParseUser update (regression test for #1198, #2252)', (done) => {
Parse.Cloud.beforeSave('_User', (req, res) => { Parse.Cloud.beforeSave('_User', (req, res) => {
req.object.set('foo', 'bar'); req.object.set('foo', 'bar');
res.success(); res.success();
}); });
let originalSessionToken;
let originalUserId;
// Simulate anonymous user save // Simulate anonymous user save
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
request.post({ request.post({
@@ -2280,6 +2282,8 @@ describe('Parse.User testing', () => {
} }
}); });
}).then((user) => { }).then((user) => {
originalSessionToken = user.sessionToken;
originalUserId = user.objectId;
// Simulate registration // Simulate registration
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request.put({ request.put({
@@ -2291,7 +2295,7 @@ describe('Parse.User testing', () => {
}, },
json: { json: {
authData: {anonymous: null}, authData: {anonymous: null},
user: 'user', username: 'user',
password: 'password', password: 'password',
} }
}, (err, res, body) => { }, (err, res, body) => {
@@ -2305,8 +2309,84 @@ describe('Parse.User testing', () => {
}).then((user) => { }).then((user) => {
expect(typeof user).toEqual('object'); expect(typeof user).toEqual('object');
expect(user.authData).toBeUndefined(); expect(user.authData).toBeUndefined();
expect(user.sessionToken).not.toBeUndefined();
// Session token should have changed
expect(user.sessionToken).not.toEqual(originalSessionToken);
// test that the sessionToken is valid
return new Promise((resolve, reject) => {
request.get({
url: 'http://localhost:8378/1/users/me',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Session-Token': user.sessionToken,
'X-Parse-REST-API-Key': 'rest',
},
json: true
}, (err, res, body) => {
expect(body.username).toEqual(user.username);
expect(body.objectId).toEqual(originalUserId);
if (err) {
reject(err);
} else {
resolve(body);
}
done();
});
});
}).catch((err) => {
fail('no request should fail: ' + JSON.stringify(err));
done();
});
});
it_exclude_dbs(['postgres'])('should send email when upgrading from anon', (done) => {
let emailCalled = false;
let emailOptions;
var emailAdapter = {
sendVerificationEmail: (options) => {
emailOptions = options;
emailCalled = true;
},
sendPasswordResetEmail: () => Promise.resolve(),
sendMail: () => Promise.resolve()
}
reconfigureServer({
appName: 'unused',
verifyUserEmails: true,
emailAdapter: emailAdapter,
publicServerURL: "http://localhost:8378/1"
})
// Simulate anonymous user save
return rp.post({
url: 'http://localhost:8378/1/classes/_User',
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-REST-API-Key': 'rest',
},
json: {authData: {anonymous: {id: '00000000-0000-0000-0000-000000000001'}}}
}).then((user) => {
return rp.put({
url: 'http://localhost:8378/1/classes/_User/' + user.objectId,
headers: {
'X-Parse-Application-Id': Parse.applicationId,
'X-Parse-Session-Token': user.sessionToken,
'X-Parse-REST-API-Key': 'rest',
},
json: {
authData: {anonymous: null},
username: 'user',
email: 'user@email.com',
password: 'password',
}
});
}).then(() => {
expect(emailCalled).toBe(true);
expect(emailOptions).not.toBeUndefined();
expect(emailOptions.user.get('email')).toEqual('user@email.com');
done(); done();
}).catch((err) => { }).catch((err) => {
console.error(err);
fail('no request should fail: ' + JSON.stringify(err)); fail('no request should fail: ' + JSON.stringify(err));
done(); done();
}); });
@@ -2471,9 +2551,16 @@ describe('Parse.User testing', () => {
user.set('password', 'password'); user.set('password', 'password');
return user.save() return user.save()
}) })
.then(() => {
// Session token should have been recycled
expect(body.sessionToken).not.toEqual(user.getSessionToken());
})
.then(() => obj.fetch()) .then(() => obj.fetch())
.then((res) => {
done();
})
.catch(error => { .catch(error => {
expect(error.code).toEqual(Parse.Error.INVALID_SESSION_TOKEN); fail('should not fail')
done(); done();
}); });
}) })

View File

@@ -367,6 +367,7 @@ RestWrite.prototype.transformUser = function() {
} }
if (this.query && !this.auth.isMaster ) { if (this.query && !this.auth.isMaster ) {
this.storage['clearSessions'] = true; this.storage['clearSessions'] = true;
this.storage['generateNewSession'] = true;
} }
return passwordCrypto.hash(this.data.password).then((hashedPassword) => { return passwordCrypto.hash(this.data.password).then((hashedPassword) => {
this.data._hashed_password = hashedPassword; this.data._hashed_password = hashedPassword;
@@ -428,6 +429,10 @@ RestWrite.prototype.createSessionTokenIfNeeded = function() {
if (this.query) { if (this.query) {
return; return;
} }
return this.createSessionToken();
}
RestWrite.prototype.createSessionToken = function() {
var token = 'r:' + cryptoUtils.newToken(); var token = 'r:' + cryptoUtils.newToken();
var expiresAt = this.config.generateSessionExpiresAt(); var expiresAt = this.config.generateSessionExpiresAt();
@@ -464,7 +469,13 @@ RestWrite.prototype.handleFollowup = function() {
} }
}; };
delete this.storage['clearSessions']; delete this.storage['clearSessions'];
this.config.database.destroy('_Session', sessionQuery) return this.config.database.destroy('_Session', sessionQuery)
.then(this.handleFollowup.bind(this));
}
if (this.storage && this.storage['generateNewSession']) {
delete this.storage['generateNewSession'];
return this.createSessionToken()
.then(this.handleFollowup.bind(this)); .then(this.handleFollowup.bind(this));
} }
@@ -472,7 +483,7 @@ RestWrite.prototype.handleFollowup = function() {
delete this.storage['sendVerificationEmail']; delete this.storage['sendVerificationEmail'];
// Fire and forget! // Fire and forget!
this.config.userController.sendVerificationEmail(this.data); this.config.userController.sendVerificationEmail(this.data);
this.handleFollowup.bind(this); return this.handleFollowup.bind(this);
} }
}; };