Skip to content

Commit 82b732f

Browse files
committed
Ensure legacy users with authData are not locked out (#4898)
* Adds fix for issue, ensuring legacy users with no ACL are properly handled * Runs tests only on mongo
1 parent d6eb4ae commit 82b732f

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

spec/ParseUser.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3706,4 +3706,36 @@ describe('Parse.User testing', () => {
37063706
expect(results.length).toBe(1);
37073707
}).then(done, done.fail);
37083708
});
3709+
3710+
describe('issue #4897', () => {
3711+
it_only_db('mongo')("should be able to login with a legacy user (no ACL)", async () => {
3712+
// This issue is a side effect of the locked users and legacy users which don't have ACL's
3713+
// In this scenario, a legacy user wasn't be able to login as there's no ACL on it
3714+
const database = Config.get(Parse.applicationId).database;
3715+
const collection = await database.adapter._adaptiveCollection('_User');
3716+
await collection.insertOne({
3717+
"_id": "ABCDEF1234",
3718+
"name": "<some_name>",
3719+
"email": "<some_email>",
3720+
"username": "<some_username>",
3721+
"_hashed_password": "<some_password>",
3722+
"_auth_data_facebook": {
3723+
"id": "8675309",
3724+
"access_token": "jenny"
3725+
},
3726+
"sessionToken": "<some_session_token>",
3727+
});
3728+
const provider = getMockFacebookProvider();
3729+
Parse.User._registerAuthenticationProvider(provider);
3730+
const model = await Parse.User._logInWith("facebook", {});
3731+
expect(model.id).toBe('ABCDEF1234');
3732+
ok(model instanceof Parse.User, "Model should be a Parse.User");
3733+
strictEqual(Parse.User.current(), model);
3734+
ok(model.extended(), "Should have used subclass.");
3735+
strictEqual(provider.authData.id, provider.synchronizedUserId);
3736+
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
3737+
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
3738+
ok(model._isLinked("facebook"), "User should be linked to facebook");
3739+
});
3740+
});
37093741
});

src/Controllers/DatabaseController.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ class DatabaseController {
427427
}
428428
});
429429
for (const updateOperation in update) {
430-
if (Object.keys(updateOperation).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) {
430+
if (update[updateOperation] && typeof update[updateOperation] === 'object' && Object.keys(update[updateOperation]).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) {
431431
throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
432432
}
433433
}
@@ -660,7 +660,7 @@ class DatabaseController {
660660
* @param {boolean} fast set to true if it's ok to just delete rows and not indexes
661661
* @returns {Promise<void>} when the deletions completes
662662
*/
663-
deleteEverything(fast: boolean = false): Promise<void> {
663+
deleteEverything(fast: boolean = false): Promise<any> {
664664
this.schemaPromise = null;
665665
return Promise.all([
666666
this.adapter.deleteAllClasses(fast),

src/RestWrite.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,13 +278,23 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) {
278278
return findPromise;
279279
}
280280

281+
RestWrite.prototype.filteredObjectsByACL = function(objects) {
282+
if (this.auth.isMaster) {
283+
return objects;
284+
}
285+
return objects.filter((object) => {
286+
if (!object.ACL) {
287+
return true; // legacy users that have no ACL field on them
288+
}
289+
// Regular users that have been locked out.
290+
return object.ACL && Object.keys(object.ACL).length > 0;
291+
});
292+
}
281293

282294
RestWrite.prototype.handleAuthData = function(authData) {
283295
let results;
284296
return this.findUsersWithAuthData(authData).then((r) => {
285-
results = r.filter((user) => {
286-
return !this.auth.isMaster && user.ACL && Object.keys(user.ACL).length > 0;
287-
});
297+
results = this.filteredObjectsByACL(r);
288298
if (results.length > 1) {
289299
// More than 1 user with the passed id's
290300
throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED,

0 commit comments

Comments
 (0)