Case insensitive signup (#5634)

* Always delete data after each, even for mongo.

* Add failing simple case test

* run all tests

* 1. when validating username be case insensitive

2. add _auth_data_anonymous to specialQueryKeys...whatever that is!

* More case sensitivity

1. also make email validation case insensitive
2. update comments to reflect what this change does

* wordsmithery and grammar

* first pass at a preformant case insensitive query.  mongo only so far.

* change name of parameter from insensitive to
caseInsensitive

* Postgres support

* properly handle auth data null

* wip

* use 'caseInsensitive' instead of 'insensitive' in all places.

* update commenet to reclect current plan

* skip the mystery test for now

* create case insensitive indecies for
mongo to support case insensitive
checks for email and username

* remove unneeded specialKey

* pull collation out to a function.

* not sure what i planned
to do with this test.
removing.

* remove typo

* remove another unused flag

* maintain order

* maintain order of params

* boil the ocean on param sequence
i like having explain last cause it seems
like something you would
change/remove after getting what you want
from the explain?

* add test to verify creation
and use of caseInsensitive index

* add no op func to prostgress

* get collation object from mongocollection
make flow lint happy by declaring things Object.

* fix typo

* add changelog

* kick travis

* properly reference static method

* add a test to confirm that anonymous users with
unique username that do collide when compared
insensitively can still be created.

* minot doc nits

* add a few tests to make sure our spy is working as expected
wordsmith the changelog

Co-authored-by: Diamond Lewis <findlewis@gmail.com>
This commit is contained in:
Arthur Cinader
2020-02-14 09:44:51 -08:00
committed by GitHub
parent 1ea3f864a8
commit fd0b535159
10 changed files with 413 additions and 35 deletions

View File

@@ -12,6 +12,7 @@ const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageA
const request = require('../lib/request');
const passwordCrypto = require('../lib/password');
const Config = require('../lib/Config');
const cryptoUtils = require('../lib/cryptoUtils');
function verifyACL(user) {
const ACL = user.getACL();
@@ -2244,6 +2245,128 @@ describe('Parse.User testing', () => {
);
});
describe('case insensitive signup not allowed', () => {
it('signup should fail with duplicate case insensitive username with basic setter', async () => {
const user = new Parse.User();
user.set('username', 'test1');
user.set('password', 'test');
await user.signUp();
const user2 = new Parse.User();
user2.set('username', 'Test1');
user2.set('password', 'test');
await expectAsync(user2.signUp()).toBeRejectedWith(
new Parse.Error(
Parse.Error.USERNAME_TAKEN,
'Account already exists for this username.'
)
);
});
it('signup should fail with duplicate case insensitive username with field specific setter', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
await user.signUp();
const user2 = new Parse.User();
user2.setUsername('Test1');
user2.setPassword('test');
await expectAsync(user2.signUp()).toBeRejectedWith(
new Parse.Error(
Parse.Error.USERNAME_TAKEN,
'Account already exists for this username.'
)
);
});
it('signup should fail with duplicate case insensitive email', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
user.setEmail('test@example.com');
await user.signUp();
const user2 = new Parse.User();
user2.setUsername('test2');
user2.setPassword('test');
user2.setEmail('Test@Example.Com');
await expectAsync(user2.signUp()).toBeRejectedWith(
new Parse.Error(
Parse.Error.EMAIL_TAKEN,
'Account already exists for this email address.'
)
);
});
it('edit should fail with duplicate case insensitive email', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
user.setEmail('test@example.com');
await user.signUp();
const user2 = new Parse.User();
user2.setUsername('test2');
user2.setPassword('test');
user2.setEmail('Foo@Example.Com');
await user2.signUp();
user2.setEmail('Test@Example.Com');
await expectAsync(user2.save()).toBeRejectedWith(
new Parse.Error(
Parse.Error.EMAIL_TAKEN,
'Account already exists for this email address.'
)
);
});
describe('anonymous users', () => {
beforeEach(() => {
const insensitiveCollisions = [
'abcdefghijklmnop',
'Abcdefghijklmnop',
'ABcdefghijklmnop',
'ABCdefghijklmnop',
'ABCDefghijklmnop',
'ABCDEfghijklmnop',
'ABCDEFghijklmnop',
'ABCDEFGhijklmnop',
'ABCDEFGHijklmnop',
'ABCDEFGHIjklmnop',
'ABCDEFGHIJklmnop',
'ABCDEFGHIJKlmnop',
'ABCDEFGHIJKLmnop',
'ABCDEFGHIJKLMnop',
'ABCDEFGHIJKLMnop',
'ABCDEFGHIJKLMNop',
'ABCDEFGHIJKLMNOp',
'ABCDEFGHIJKLMNOP',
];
// need a bunch of spare random strings per api request
spyOn(cryptoUtils, 'randomString').and.returnValues(
...insensitiveCollisions
);
});
it('should not fail on case insensitive matches', async () => {
const user1 = await Parse.AnonymousUtils.logIn();
const username1 = user1.get('username');
const user2 = await Parse.AnonymousUtils.logIn();
const username2 = user2.get('username');
expect(username1).not.toBeUndefined();
expect(username2).not.toBeUndefined();
expect(username1.toLowerCase()).toBe('abcdefghijklmnop');
expect(username2.toLowerCase()).toBe('abcdefghijklmnop');
expect(username2).not.toBe(username1);
expect(username2.toLowerCase()).toBe(username1.toLowerCase()); // this is redundant :).
});
});
});
it('user cannot update email to existing user', done => {
const user = new Parse.User();
user.set('username', 'test1');