mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04:00
feat: add auth controller with internal login, registration, password change and logout
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
parent
cd4ee84ec3
commit
366057fb8b
3 changed files with 180 additions and 0 deletions
71
src/api/private/auth/auth.controller.spec.ts
Normal file
71
src/api/private/auth/auth.controller.spec.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { getConnectionToken, getRepositoryToken } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import appConfigMock from '../../../config/mock/app.config.mock';
|
||||||
|
import authConfigMock from '../../../config/mock/auth.config.mock';
|
||||||
|
import { Identity } from '../../../identity/identity.entity';
|
||||||
|
import { IdentityModule } from '../../../identity/identity.module';
|
||||||
|
import { LoggerModule } from '../../../logger/logger.module';
|
||||||
|
import { Session } from '../../../users/session.entity';
|
||||||
|
import { User } from '../../../users/user.entity';
|
||||||
|
import { UsersModule } from '../../../users/users.module';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
|
||||||
|
describe('AuthController', () => {
|
||||||
|
let controller: AuthController;
|
||||||
|
|
||||||
|
type MockConnection = {
|
||||||
|
transaction: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function mockConnection(): MockConnection {
|
||||||
|
return {
|
||||||
|
transaction: jest.fn(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(Identity),
|
||||||
|
useClass: Repository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: getConnectionToken(),
|
||||||
|
useFactory: mockConnection,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
load: [appConfigMock, authConfigMock],
|
||||||
|
}),
|
||||||
|
LoggerModule,
|
||||||
|
UsersModule,
|
||||||
|
IdentityModule,
|
||||||
|
],
|
||||||
|
controllers: [AuthController],
|
||||||
|
})
|
||||||
|
.overrideProvider(getRepositoryToken(Identity))
|
||||||
|
.useClass(Repository)
|
||||||
|
.overrideProvider(getRepositoryToken(Session))
|
||||||
|
.useValue({})
|
||||||
|
.overrideProvider(getRepositoryToken(User))
|
||||||
|
.useValue({})
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
controller = module.get<AuthController>(AuthController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
105
src/api/private/auth/auth.controller.ts
Normal file
105
src/api/private/auth/auth.controller.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
NotFoundException,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Req,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Session } from 'express-session';
|
||||||
|
|
||||||
|
import { AlreadyInDBError, NotInDBError } from '../../../errors/errors';
|
||||||
|
import { IdentityService } from '../../../identity/identity.service';
|
||||||
|
import { LocalAuthGuard } from '../../../identity/local/local.strategy';
|
||||||
|
import { LoginDto } from '../../../identity/local/login.dto';
|
||||||
|
import { RegisterDto } from '../../../identity/local/register.dto';
|
||||||
|
import { UpdatePasswordDto } from '../../../identity/local/update-password.dto';
|
||||||
|
import { SessionGuard } from '../../../identity/session.guard';
|
||||||
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
|
import { User } from '../../../users/user.entity';
|
||||||
|
import { UsersService } from '../../../users/users.service';
|
||||||
|
import { LoginEnabledGuard } from '../../utils/login-enabled.guard';
|
||||||
|
import { RegistrationEnabledGuard } from '../../utils/registration-enabled.guard';
|
||||||
|
import { RequestUser } from '../../utils/request-user.decorator';
|
||||||
|
|
||||||
|
@Controller('auth')
|
||||||
|
export class AuthController {
|
||||||
|
constructor(
|
||||||
|
private readonly logger: ConsoleLoggerService,
|
||||||
|
private usersService: UsersService,
|
||||||
|
private identityService: IdentityService,
|
||||||
|
) {
|
||||||
|
this.logger.setContext(AuthController.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(RegistrationEnabledGuard)
|
||||||
|
@Post('local')
|
||||||
|
async registerUser(@Body() registerDto: RegisterDto): Promise<void> {
|
||||||
|
try {
|
||||||
|
const user = await this.usersService.createUser(
|
||||||
|
registerDto.username,
|
||||||
|
registerDto.displayname,
|
||||||
|
);
|
||||||
|
// ToDo: Figure out how to rollback user if anything with this calls goes wrong
|
||||||
|
await this.identityService.createLocalIdentity(
|
||||||
|
user,
|
||||||
|
registerDto.password,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof AlreadyInDBError) {
|
||||||
|
throw new BadRequestException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(LoginEnabledGuard, SessionGuard)
|
||||||
|
@Put('local')
|
||||||
|
async updatePassword(
|
||||||
|
@RequestUser() user: User,
|
||||||
|
@Body() changePasswordDto: UpdatePasswordDto,
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.identityService.updateLocalPassword(
|
||||||
|
user,
|
||||||
|
changePasswordDto.newPassword,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(LoginEnabledGuard, LocalAuthGuard)
|
||||||
|
@Post('local/login')
|
||||||
|
login(
|
||||||
|
@Req() request: Request & { session: { user: string } },
|
||||||
|
@Body() loginDto: LoginDto,
|
||||||
|
): void {
|
||||||
|
// There is no further testing needed as we only get to this point if LocalAuthGuard was successful
|
||||||
|
request.session.user = loginDto.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(SessionGuard)
|
||||||
|
@Delete('logout')
|
||||||
|
logout(@Req() request: Request & { session: Session }): void {
|
||||||
|
request.session.destroy((err) => {
|
||||||
|
if (err) {
|
||||||
|
this.logger.error('Encountered an error while logging out: ${err}');
|
||||||
|
throw new BadRequestException('Unable to log out');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,12 +8,14 @@ import { Module } from '@nestjs/common';
|
||||||
import { AuthModule } from '../../auth/auth.module';
|
import { AuthModule } from '../../auth/auth.module';
|
||||||
import { FrontendConfigModule } from '../../frontend-config/frontend-config.module';
|
import { FrontendConfigModule } from '../../frontend-config/frontend-config.module';
|
||||||
import { HistoryModule } from '../../history/history.module';
|
import { HistoryModule } from '../../history/history.module';
|
||||||
|
import { IdentityModule } from '../../identity/identity.module';
|
||||||
import { LoggerModule } from '../../logger/logger.module';
|
import { LoggerModule } from '../../logger/logger.module';
|
||||||
import { MediaModule } from '../../media/media.module';
|
import { MediaModule } from '../../media/media.module';
|
||||||
import { NotesModule } from '../../notes/notes.module';
|
import { NotesModule } from '../../notes/notes.module';
|
||||||
import { PermissionsModule } from '../../permissions/permissions.module';
|
import { PermissionsModule } from '../../permissions/permissions.module';
|
||||||
import { RevisionsModule } from '../../revisions/revisions.module';
|
import { RevisionsModule } from '../../revisions/revisions.module';
|
||||||
import { UsersModule } from '../../users/users.module';
|
import { UsersModule } from '../../users/users.module';
|
||||||
|
import { AuthController } from './auth/auth.controller';
|
||||||
import { ConfigController } from './config/config.controller';
|
import { ConfigController } from './config/config.controller';
|
||||||
import { HistoryController } from './me/history/history.controller';
|
import { HistoryController } from './me/history/history.controller';
|
||||||
import { MeController } from './me/me.controller';
|
import { MeController } from './me/me.controller';
|
||||||
|
@ -32,6 +34,7 @@ import { TokensController } from './tokens/tokens.controller';
|
||||||
NotesModule,
|
NotesModule,
|
||||||
MediaModule,
|
MediaModule,
|
||||||
RevisionsModule,
|
RevisionsModule,
|
||||||
|
IdentityModule,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
TokensController,
|
TokensController,
|
||||||
|
@ -40,6 +43,7 @@ import { TokensController } from './tokens/tokens.controller';
|
||||||
HistoryController,
|
HistoryController,
|
||||||
MeController,
|
MeController,
|
||||||
NotesController,
|
NotesController,
|
||||||
|
AuthController,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PrivateApiModule {}
|
export class PrivateApiModule {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue