Add beforeLogin trigger with support for auth providers (#5445)

* Add beforeLogin trigger with support for auth providers

* adjust comment that boxed off beforeLogin to a negative use-case only

* add internal error to help future maintainers regarding use of beforeLogin

* let beforeLogin accept className or constructor like other hook types

* add assertions for beforeLogin trigger className validation
This commit is contained in:
Omair Vaiyani
2019-04-23 16:24:20 +01:00
committed by Arthur Cinader
parent 3e003ee9f4
commit a1e1cef6d2
6 changed files with 340 additions and 60 deletions

View File

@@ -2021,6 +2021,24 @@ describe('afterFind hooks', () => {
expect(() => {
Parse.Cloud.afterSave('_PushStatus', () => {});
}).not.toThrow();
expect(() => {
Parse.Cloud.beforeLogin(() => {});
}).not.toThrow(
'Only the _User class is allowed for the beforeLogin trigger'
);
expect(() => {
Parse.Cloud.beforeLogin('_User', () => {});
}).not.toThrow(
'Only the _User class is allowed for the beforeLogin trigger'
);
expect(() => {
Parse.Cloud.beforeLogin(Parse.User, () => {});
}).not.toThrow(
'Only the _User class is allowed for the beforeLogin trigger'
);
expect(() => {
Parse.Cloud.beforeLogin('SomeClass', () => {});
}).toThrow('Only the _User class is allowed for the beforeLogin trigger');
});
it('should skip afterFind hooks for aggregate', done => {
@@ -2115,3 +2133,88 @@ describe('afterFind hooks', () => {
expect(calledAfter).toBe(true);
});
});
describe('beforeLogin hook', () => {
it('should run beforeLogin with correct credentials', async done => {
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
expect(req.object.get('username')).toEqual('tupac');
});
await Parse.User.signUp('tupac', 'shakur');
const user = await Parse.User.logIn('tupac', 'shakur');
expect(hit).toBe(1);
expect(user).toBeDefined();
expect(user.getUsername()).toBe('tupac');
expect(user.getSessionToken()).toBeDefined();
done();
});
it('should be able to block login if an error is thrown', async done => {
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
if (req.object.get('isBanned')) {
throw new Error('banned account');
}
});
const user = await Parse.User.signUp('tupac', 'shakur');
await user.save({ isBanned: true });
try {
await Parse.User.logIn('tupac', 'shakur');
throw new Error('should not have been logged in.');
} catch (e) {
expect(e.message).toBe('banned account');
}
expect(hit).toBe(1);
done();
});
it('should not run beforeLogin with incorrect credentials', async done => {
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
expect(req.object.get('username')).toEqual('tupac');
});
await Parse.User.signUp('tupac', 'shakur');
try {
await Parse.User.logIn('tony', 'shakur');
} catch (e) {
expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND);
}
expect(hit).toBe(0);
done();
});
it('should not run beforeLogin on sign up', async done => {
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
expect(req.object.get('username')).toEqual('tupac');
});
const user = await Parse.User.signUp('tupac', 'shakur');
expect(user).toBeDefined();
expect(hit).toBe(0);
done();
});
it('should have expected data in request', async done => {
Parse.Cloud.beforeLogin(req => {
expect(req.object).toBeDefined();
expect(req.user).toBeUndefined();
expect(req.headers).toBeDefined();
expect(req.ip).toBeDefined();
expect(req.installationId).toBeDefined();
expect(req.context).toBeUndefined();
});
await Parse.User.signUp('tupac', 'shakur');
await Parse.User.logIn('tupac', 'shakur');
done();
});
});

View File

@@ -440,6 +440,22 @@ describe('Parse.User testing', () => {
);
});
it('should not call beforeLogin with become', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
let hit = 0;
Parse.Cloud.beforeLogin(() => {
hit++;
});
await Parse.User._logInWith('facebook');
const sessionToken = Parse.User.current().getSessionToken();
await Parse.User.become(sessionToken);
expect(hit).toBe(0);
done();
});
it('cannot save non-authed user', async done => {
let user = new Parse.User();
user.set({
@@ -1403,6 +1419,84 @@ describe('Parse.User testing', () => {
});
});
it('signup with provider should not call beforeLogin trigger', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
let hit = 0;
Parse.Cloud.beforeLogin(() => {
hit++;
});
await Parse.User._logInWith('facebook');
expect(hit).toBe(0);
done();
});
it('login with provider should call beforeLogin trigger', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
expect(req.object.get('authData')).toBeDefined();
expect(req.object.get('name')).toBe('tupac shakur');
});
await Parse.User._logInWith('facebook');
await Parse.User.current().save({ name: 'tupac shakur' });
await Parse.User.logOut();
await Parse.User._logInWith('facebook');
expect(hit).toBe(1);
done();
});
it('incorrect login with provider should not call beforeLogin trigger', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
let hit = 0;
Parse.Cloud.beforeLogin(() => {
hit++;
});
await Parse.User._logInWith('facebook');
await Parse.User.logOut();
provider.shouldError = true;
try {
await Parse.User._logInWith('facebook');
} catch (e) {
expect(e).toBeDefined();
}
expect(hit).toBe(0);
done();
});
it('login with provider should be blockable by beforeLogin', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);
let hit = 0;
Parse.Cloud.beforeLogin(req => {
hit++;
if (req.object.get('isBanned')) {
throw new Error('banned account');
}
});
await Parse.User._logInWith('facebook');
await Parse.User.current().save({ isBanned: true });
await Parse.User.logOut();
try {
await Parse.User._logInWith('facebook');
throw new Error('should not have continued login.');
} catch (e) {
expect(e.message).toBe('banned account');
}
expect(hit).toBe(1);
done();
});
it('link with provider', async done => {
const provider = getMockFacebookProvider();
Parse.User._registerAuthenticationProvider(provider);