Add unique indexing for username/email

WIP

Notes on how to upgrade to 2.3.0 safely

index on unique-indexes: c454180 Revert "Log objects rather than JSON stringified objects (#1922)"

reconfigure username/email tests

Start dealing with test shittyness

most tests passing

Make specific server config for tests async

Fix more tests

Save callback to variable

undo

remove uses of _collection

reorder some params

reorder find() arguments

finishsh touching up argument order

Accept a database adapter as a parameter

First passing test with postgres!

Fix tests

Setup travis

sudo maybe?

use postgres username

reorder find() arguments

Build objects with default fields correctly

Don't tell adapter about ACL

WIP

Passing postgres test with user

Fix up createdAt, updatedAt, nad _hashed_password handling
This commit is contained in:
Drew Gross
2016-05-27 19:41:09 -07:00
parent 2cafd6919f
commit 803b9be97a
5 changed files with 121 additions and 17 deletions

View File

@@ -77,6 +77,6 @@ coll.aggregate([
{$match: {count: {"$gt": 1}}},
{$project: {id: "$uniqueIds", username: "$_id", _id : 0} },
{$unwind: "$id" },
{$out: '_duplicates'} // Save the list of duplicates to a new, "_duplicates collection. Remove this line to just output the list.
{$out: '_duplicates'} // Save the list of duplicates to a new, "_duplicates" collection. Remove this line to just output the list.
], {allowDiskUse:true})
```

View File

@@ -207,7 +207,100 @@ describe('miscellaneous', function() {
});
});
it('succeed in logging in', function(done) {
it('ensure that email is uniquely indexed', done => {
let numCreated = 0;
let numFailed = 0;
let user1 = new Parse.User();
user1.setPassword('asdf');
user1.setUsername('u1');
user1.setEmail('dupe@dupe.dupe');
let p1 = user1.signUp();
p1.then(user => {
numCreated++;
expect(numCreated).toEqual(1);
})
.catch(error => {
numFailed++;
expect(numFailed).toEqual(1);
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
});
let user2 = new Parse.User();
user2.setPassword('asdf');
user2.setUsername('u2');
user2.setEmail('dupe@dupe.dupe');
let p2 = user2.signUp();
p2.then(user => {
numCreated++;
expect(numCreated).toEqual(1);
})
.catch(error => {
numFailed++;
expect(numFailed).toEqual(1);
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
});
Parse.Promise.all([p1, p2])
.then(() => {
fail('one of the users should not have been created');
done();
})
.catch(done);
});
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
let config = new Config('test');
// Remove existing data to clear out unique index
TestUtils.destroyAllDataPermanently()
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', email: 'a@b.c' }))
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', email: 'a@b.c' }))
.then(reconfigureServer)
.catch(() => {
let user = new Parse.User();
user.setPassword('asdf');
user.setUsername('qqq');
user.setEmail('unique@unique.unique');
return user.signUp().catch(fail);
})
.then(() => {
let user = new Parse.User();
user.setPassword('asdf');
user.setUsername('www');
user.setEmail('a@b.c');
return user.signUp()
})
.catch(error => {
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
done();
});
});
it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', done => {
let config = new Config('test');
config.database.adapter.ensureUniqueness('_User', requiredUserFields, ['randomField'])
.then(() => {
let user = new Parse.User();
user.setPassword('asdf');
user.setUsername('1');
user.setEmail('1@b.c');
user.set('randomField', 'a');
return user.signUp()
})
.then(() => {
let user = new Parse.User();
user.setPassword('asdf');
user.setUsername('2');
user.setEmail('2@b.c');
user.set('randomField', 'a');
return user.signUp()
})
.catch(error => {
expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE);
done();
});
});
fit('succeed in logging in', function(done) {
createTestUser(function(u) {
expect(typeof u.id).toEqual('string');
@@ -217,8 +310,9 @@ describe('miscellaneous', function() {
expect(user.get('password')).toBeUndefined();
expect(user.getSessionToken()).not.toBeUndefined();
Parse.User.logOut().then(done);
}, error: function(error) {
fail(error);
}, error: error => {
fail(JSON.stringify(error));
done();
}
});
}, fail);

View File

@@ -100,4 +100,12 @@ describe('Uniqueness', function() {
done();
});
});
it('adding a unique index to an existing field works even if it has nulls', done => {
});
it('adding a unique index to an existing field doesnt prevent you from adding new documents with nulls', done => {
});
});

View File

@@ -197,20 +197,12 @@ function transformWhere(className, restWhere, schema) {
return mongoWhere;
}
const parseObjectKeyValueToMongoObjectKeyValue = (className, restKey, restValue, schema) => {
const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => {
// Check if the schema is known since it's a built-in field.
let transformedValue;
let coercedToDate;
switch(restKey) {
case 'objectId': return {key: '_id', value: restValue};
case 'createdAt':
transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
return {key: '_created_at', value: coercedToDate};
case 'updatedAt':
transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
return {key: '_updated_at', value: coercedToDate};
case 'expiresAt':
transformedValue = transformTopLevelAtom(restValue);
coercedToDate = typeof transformedValue === 'string' ? new Date(transformedValue) : transformedValue
@@ -271,8 +263,6 @@ const parseObjectKeyValueToMongoObjectKeyValue = (className, restKey, restValue,
return {key: restKey, value};
}
// Main exposed method to create new objects.
// restCreate is the "create" clause in REST API form.
const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
if (className == '_User') {
restCreate = transformAuthData(restCreate);
@@ -281,7 +271,6 @@ const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
let mongoCreate = {}
for (let restKey in restCreate) {
let { key, value } = parseObjectKeyValueToMongoObjectKeyValue(
className,
restKey,
restCreate[restKey],
schema
@@ -290,6 +279,13 @@ const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
mongoCreate[key] = value;
}
}
// Use the legacy mongo format for createdAt and updatedAt
mongoCreate._created_at = mongoCreate.createdAt.iso;
delete mongoCreate.createdAt;
mongoCreate._updated_at = mongoCreate.updatedAt.iso;
delete mongoCreate.updatedAt;
return mongoCreate;
}
@@ -735,7 +731,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
restObject['objectId'] = '' + mongoObject[key];
break;
case '_hashed_password':
restObject['password'] = mongoObject[key];
restObject._hashed_password = mongoObject[key];
break;
case '_acl':
case '_email_verify_token':

View File

@@ -159,6 +159,9 @@ const filterSensitiveData = (isMaster, aclGroup, className, object) => {
return object;
}
object.password = object._hashed_password;
delete object._hashed_password;
delete object.sessionToken;
if (isMaster || (aclGroup.indexOf(object.objectId) > -1)) {
@@ -400,6 +403,9 @@ DatabaseController.prototype.create = function(className, object, { acl } = {})
let originalObject = object;
object = transformObjectACL(object);
object.createdAt = { iso: object.createdAt, __type: 'Date' };
object.updatedAt = { iso: object.updatedAt, __type: 'Date' };
var isMaster = acl === undefined;
var aclGroup = acl || [];