diff --git a/src/api/private/auth/auth.controller.ts b/src/api/private/auth/auth.controller.ts index 334a1d29b..b2f0b3bc7 100644 --- a/src/api/private/auth/auth.controller.ts +++ b/src/api/private/auth/auth.controller.ts @@ -6,6 +6,7 @@ import { BadRequestException, Body, + ConflictException, Controller, Delete, NotFoundException, @@ -56,7 +57,7 @@ export class AuthController { return; } catch (e) { if (e instanceof AlreadyInDBError) { - throw new BadRequestException(e.message); + throw new ConflictException(e.message); } throw e; } diff --git a/test/private-api/auth.e2e-spec.ts b/test/private-api/auth.e2e-spec.ts index 7df133331..c1976f219 100644 --- a/test/private-api/auth.e2e-spec.ts +++ b/test/private-api/auth.e2e-spec.ts @@ -75,7 +75,7 @@ describe('Auth', () => { .post('/api/private/auth/local') .set('Content-Type', 'application/json') .send(JSON.stringify(registrationDto)) - .expect(400); + .expect(409); }); it('when registration is disabled', async () => { testSetup.configService.get('authConfig').local.enableRegister = false; diff --git a/test/private-api/register-and-login.e2e-spec.ts b/test/private-api/register-and-login.e2e-spec.ts new file mode 100644 index 000000000..e84520fef --- /dev/null +++ b/test/private-api/register-and-login.e2e-spec.ts @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import request from 'supertest'; + +import { LoginDto } from '../../src/identity/local/login.dto'; +import { RegisterDto } from '../../src/identity/local/register.dto'; +import { TestSetup } from '../test-setup'; + +describe('Register and Login', () => { + let testSetup: TestSetup; + + const USERNAME = 'testuser'; + const DISPLAYNAME = 'A Test User'; + const PASSWORD = 'secure'; + + beforeEach(async () => { + testSetup = await TestSetup.create(); + await testSetup.app.init(); + }); + + afterEach(async () => { + await testSetup.app.close(); + }); + + test('a user can successfully create a local account and log in', async () => { + // register a new user + const registrationDto: RegisterDto = { + displayname: DISPLAYNAME, + password: PASSWORD, + username: USERNAME, + }; + await request(testSetup.app.getHttpServer()) + .post('/api/private/auth/local') + .set('Content-Type', 'application/json') + .send(JSON.stringify(registrationDto)) + .expect(201); + + // log in with the new user and create a session + const loginDto: LoginDto = { + password: PASSWORD, + username: USERNAME, + }; + const session = request.agent(testSetup.app.getHttpServer()); + await session + .post('/api/private/auth/local/login') + .set('Content-Type', 'application/json') + .send(JSON.stringify(loginDto)) + .expect(201); + + // request user profile + const profile = await session.get('/api/private/me').expect(200); + expect(profile.body.username).toEqual(USERNAME); + expect(profile.body.displayName).toEqual(DISPLAYNAME); + + // logout again + await session.delete('/api/private/auth/logout').expect(200); + + // not allowed to request profile now + await session.get('/api/private/me').expect(401); + }); + + test('a username cannot be used twice', async () => { + // register a new user + const registrationDto: RegisterDto = { + displayname: DISPLAYNAME, + password: PASSWORD, + username: USERNAME, + }; + await request(testSetup.app.getHttpServer()) + .post('/api/private/auth/local') + .set('Content-Type', 'application/json') + .send(JSON.stringify(registrationDto)) + .expect(201); + + // try to use the same username again + await request(testSetup.app.getHttpServer()) + .post('/api/private/auth/local') + .set('Content-Type', 'application/json') + .send(JSON.stringify(registrationDto)) + .expect(409); + }); + + test('a user can create a local account and change the password', async () => { + // register a new user + const registrationDto: RegisterDto = { + displayname: DISPLAYNAME, + password: PASSWORD, + username: USERNAME, + }; + await request(testSetup.app.getHttpServer()) + .post('/api/private/auth/local') + .set('Content-Type', 'application/json') + .send(JSON.stringify(registrationDto)) + .expect(201); + + // log in with the new user and create a session + const loginDto: LoginDto = { + password: PASSWORD, + username: USERNAME, + }; + let session = request.agent(testSetup.app.getHttpServer()); + await session + .post('/api/private/auth/local/login') + .set('Content-Type', 'application/json') + .send(JSON.stringify(loginDto)) + .expect(201); + + // change the password + await session + .put('/api/private/auth/local') + .set('Content-Type', 'application/json') + .send( + JSON.stringify({ + newPassword: 'newPassword', + }), + ) + .expect(200); + + // get new session + session = request.agent(testSetup.app.getHttpServer()); + + // not allowed to request profile now + await session.get('/api/private/me').expect(401); + + // login with new password + loginDto.password = 'newPassword'; + await session + .post('/api/private/auth/local/login') + .set('Content-Type', 'application/json') + .send(JSON.stringify(loginDto)) + .expect(201); + + // allowed to request profile now + await session.get('/api/private/me').expect(200); + }); +}); diff --git a/test/test-setup.ts b/test/test-setup.ts index d283c9c45..4ee00950b 100644 --- a/test/test-setup.ts +++ b/test/test-setup.ts @@ -14,24 +14,33 @@ import { PublicApiModule } from '../src/api/public/public-api.module'; import { AuthModule } from '../src/auth/auth.module'; import { MockAuthGuard } from '../src/auth/mock-auth.guard'; import { TokenAuthGuard } from '../src/auth/token.strategy'; +import { AuthorsModule } from '../src/authors/authors.module'; +import { AuthConfig } from '../src/config/auth.config'; import appConfigMock from '../src/config/mock/app.config.mock'; import authConfigMock from '../src/config/mock/auth.config.mock'; import customizationConfigMock from '../src/config/mock/customization.config.mock'; import externalServicesConfigMock from '../src/config/mock/external-services.config.mock'; import mediaConfigMock from '../src/config/mock/media.config.mock'; +import { FrontendConfigModule } from '../src/frontend-config/frontend-config.module'; import { GroupsModule } from '../src/groups/groups.module'; import { HistoryModule } from '../src/history/history.module'; import { HistoryService } from '../src/history/history.service'; +import { IdentityModule } from '../src/identity/identity.module'; import { IdentityService } from '../src/identity/identity.service'; +import { ConsoleLoggerService } from '../src/logger/console-logger.service'; import { LoggerModule } from '../src/logger/logger.module'; import { MediaModule } from '../src/media/media.module'; import { MediaService } from '../src/media/media.service'; +import { MonitoringModule } from '../src/monitoring/monitoring.module'; import { AliasService } from '../src/notes/alias.service'; import { NotesModule } from '../src/notes/notes.module'; import { NotesService } from '../src/notes/notes.service'; import { PermissionsModule } from '../src/permissions/permissions.module'; +import { RevisionsModule } from '../src/revisions/revisions.module'; import { UsersModule } from '../src/users/users.module'; import { UsersService } from '../src/users/users.service'; +import { setupSessionMiddleware } from '../src/utils/session'; +import { setupValidationPipe } from '../src/utils/setup-pipes'; export class TestSetup { moduleRef: TestingModule; @@ -78,16 +87,21 @@ export class TestSetup { externalServicesConfigMock, ], }), + NotesModule, + UsersModule, + RevisionsModule, + AuthorsModule, PublicApiModule, PrivateApiModule, - NotesModule, + HistoryModule, + MonitoringModule, PermissionsModule, GroupsModule, LoggerModule, - AuthModule, - UsersModule, MediaModule, - HistoryModule, + AuthModule, + FrontendConfigModule, + IdentityModule, ], }) .overrideGuard(TokenAuthGuard) @@ -110,6 +124,11 @@ export class TestSetup { testSetup.app = testSetup.moduleRef.createNestApplication(); + setupSessionMiddleware( + testSetup.app, + testSetup.configService.get('authConfig'), + ); + return testSetup; } }