diff --git a/backend/src/schema/resolvers/user_management.js b/backend/src/schema/resolvers/user_management.js index e1528cc9e..b7584dd68 100644 --- a/backend/src/schema/resolvers/user_management.js +++ b/backend/src/schema/resolvers/user_management.js @@ -2,6 +2,7 @@ import encode from '../../jwt/encode' import bcrypt from 'bcryptjs' import { AuthenticationError } from 'apollo-server' import { neode } from '../../bootstrap/neo4j' +import { normalizeEmail } from 'validator' const instance = neode() @@ -21,6 +22,7 @@ export default { // if (user && user.id) { // throw new Error('Already logged in.') // } + email = normalizeEmail(email) const session = driver.session() const result = await session.run( ` diff --git a/backend/src/schema/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js index 9d7dff2a3..df8454ebb 100644 --- a/backend/src/schema/resolvers/user_management.spec.js +++ b/backend/src/schema/resolvers/user_management.spec.js @@ -5,8 +5,10 @@ import { gql } from '../../helpers/jest' import { createTestClient } from 'apollo-server-testing' import createServer, { context } from '../../server' import encode from '../../jwt/encode' +import { neode as getNeode } from '../../bootstrap/neo4j' const factory = Factory() +const neode = getNeode() let query let mutate let variables @@ -214,6 +216,28 @@ describe('login', () => { }) }) }) + + describe('normalization', () => { + describe('email address is a gmail address ', () => { + beforeEach(async () => { + const email = await neode.first('EmailAddress', { email: 'test@example.org' }) + await email.update({ email: 'someuser@gmail.com' }) + }) + + describe('supplied email contains dots', () => { + beforeEach(() => { + variables = { ...variables, email: 'some.user@gmail.com' } + }) + + it('normalizes email, issue #2329', async () => { + await respondsWith({ + data: { login: expect.any(String) }, + errors: undefined, + }) + }) + }) + }) + }) }) describe('with a valid email but incorrect password', () => { diff --git a/webapp/components/LoginForm/LoginForm.spec.js b/webapp/components/LoginForm/LoginForm.spec.js new file mode 100644 index 000000000..b60680f37 --- /dev/null +++ b/webapp/components/LoginForm/LoginForm.spec.js @@ -0,0 +1,62 @@ +import LoginForm from './LoginForm.vue' +import Styleguide from '@human-connection/styleguide' +import Vuex from 'vuex' +import { config, mount, createLocalVue } from '@vue/test-utils' + +const localVue = createLocalVue() +localVue.use(Vuex) +localVue.use(Styleguide) + +config.stubs['nuxt-link'] = '' +config.stubs['locale-switch'] = '' +config.stubs['client-only'] = '' + +describe('LoginForm', () => { + let mocks + let propsData + let storeMocks + + beforeEach(() => { + propsData = {} + }) + + describe('mount', () => { + const Wrapper = () => { + storeMocks = { + getters: { + 'auth/pending': () => false, + }, + actions: { + 'auth/login': jest.fn(), + }, + } + const store = new Vuex.Store(storeMocks) + mocks = { + $t: () => {}, + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + } + return mount(LoginForm, { mocks, localVue, propsData, store }) + } + + describe('fill in email and password and submit', () => { + const fillIn = (wrapper, opts = {}) => { + const { email = 'email@example.org', password = '1234' } = opts + wrapper.find('input[name="email"]').setValue(email) + wrapper.find('input[name="password"]').setValue(password) + wrapper.find('form').trigger('submit') + } + + it('dispatches login with form data', () => { + fillIn(Wrapper()) + expect(storeMocks.actions['auth/login']).toHaveBeenCalledWith( + expect.any(Object), + { email: 'email@example.org', password: '1234' }, + undefined, + ) + }) + }) + }) +}) diff --git a/webapp/components/LoginForm/LoginForm.vue b/webapp/components/LoginForm/LoginForm.vue index 91693ed4b..d61a5675a 100644 --- a/webapp/components/LoginForm/LoginForm.vue +++ b/webapp/components/LoginForm/LoginForm.vue @@ -93,8 +93,9 @@ export default { }, methods: { async onSubmit() { + const { email, password } = this.form try { - await this.$store.dispatch('auth/login', { ...this.form }) + await this.$store.dispatch('auth/login', { email, password }) this.$toast.success(this.$t('login.success')) this.$emit('success') } catch (err) {