refactor: remove try catches from controllers

This is handled by the ErrorExceptionMapping class

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2022-01-17 10:48:33 +01:00
parent 2cd4f412e5
commit d142cbadeb
10 changed files with 133 additions and 424 deletions

View file

@ -9,7 +9,6 @@ import {
Controller, Controller,
Delete, Delete,
HttpCode, HttpCode,
NotFoundException,
Param, Param,
Post, Post,
Put, Put,
@ -19,11 +18,7 @@ import {
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { import {
AlreadyInDBError,
ForbiddenIdError,
NotInDBError,
PrimaryAliasDeletionForbiddenError,
} from '../../../errors/errors';
import { SessionGuard } from '../../../identity/session.guard'; import { SessionGuard } from '../../../identity/session.guard';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { AliasCreateDto } from '../../../notes/alias-create.dto'; import { AliasCreateDto } from '../../../notes/alias-create.dto';
@ -49,32 +44,23 @@ export class AliasController {
) { ) {
this.logger.setContext(AliasController.name); this.logger.setContext(AliasController.name);
} }
@Post() @Post()
async addAlias( async addAlias(
@RequestUser() user: User, @RequestUser() user: User,
@Body() newAliasDto: AliasCreateDto, @Body() newAliasDto: AliasCreateDto,
): Promise<AliasDto> { ): Promise<AliasDto> {
try { const note = await this.noteService.getNoteByIdOrAlias(
const note = await this.noteService.getNoteByIdOrAlias( newAliasDto.noteIdOrAlias,
newAliasDto.noteIdOrAlias, );
); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
const updatedAlias = await this.aliasService.addAlias(
note,
newAliasDto.newAlias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} catch (e) {
if (e instanceof AlreadyInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
const updatedAlias = await this.aliasService.addAlias(
note,
newAliasDto.newAlias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} }
@Put(':alias') @Put(':alias')
@ -88,25 +74,12 @@ export class AliasController {
`The field 'primaryAlias' must be set to 'true'.`, `The field 'primaryAlias' must be set to 'true'.`,
); );
} }
try { const note = await this.noteService.getNoteByIdOrAlias(alias);
const note = await this.noteService.getNoteByIdOrAlias(alias); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
const updatedAlias = await this.aliasService.makeAliasPrimary(
note,
alias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
const updatedAlias = await this.aliasService.makeAliasPrimary(note, alias);
return this.aliasService.toAliasDto(updatedAlias, note);
} }
@Delete(':alias') @Delete(':alias')
@ -115,24 +88,11 @@ export class AliasController {
@RequestUser() user: User, @RequestUser() user: User,
@Param('alias') alias: string, @Param('alias') alias: string,
): Promise<void> { ): Promise<void> {
try { const note = await this.noteService.getNoteByIdOrAlias(alias);
const note = await this.noteService.getNoteByIdOrAlias(alias); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
await this.aliasService.removeAlias(note, alias);
return;
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof PrimaryAliasDeletionForbiddenError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
await this.aliasService.removeAlias(note, alias);
return;
} }
} }

View file

@ -12,17 +12,12 @@ import {
Post, Post,
Put, Put,
Req, Req,
UnauthorizedException,
UseGuards, UseGuards,
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { Session } from 'express-session'; import { Session } from 'express-session';
import { import { AlreadyInDBError } from '../../../errors/errors';
AlreadyInDBError,
InvalidCredentialsError,
NoLocalIdentityError,
} from '../../../errors/errors';
import { IdentityService } from '../../../identity/identity.service'; import { IdentityService } from '../../../identity/identity.service';
import { LocalAuthGuard } from '../../../identity/local/local.strategy'; import { LocalAuthGuard } from '../../../identity/local/local.strategy';
import { LoginDto } from '../../../identity/local/login.dto'; import { LoginDto } from '../../../identity/local/login.dto';
@ -62,6 +57,7 @@ export class AuthController {
); );
return; return;
} catch (e) { } catch (e) {
// This special handling can't be omitted since AlreadyInDBErrors get mapped to BadRequestException usually.
if (e instanceof AlreadyInDBError) { if (e instanceof AlreadyInDBError) {
throw new ConflictException(e.message); throw new ConflictException(e.message);
} }

View file

@ -1,15 +1,13 @@
/* /*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { import {
BadRequestException,
Body, Body,
Controller, Controller,
Delete, Delete,
Get, Get,
NotFoundException,
Post, Post,
Put, Put,
UseGuards, UseGuards,
@ -17,7 +15,6 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { ForbiddenIdError, NotInDBError } from '../../../../errors/errors';
import { HistoryEntryImportDto } from '../../../../history/history-entry-import.dto'; import { HistoryEntryImportDto } from '../../../../history/history-entry-import.dto';
import { HistoryEntryUpdateDto } from '../../../../history/history-entry-update.dto'; import { HistoryEntryUpdateDto } from '../../../../history/history-entry-update.dto';
import { HistoryEntryDto } from '../../../../history/history-entry.dto'; import { HistoryEntryDto } from '../../../../history/history-entry.dto';
@ -43,19 +40,10 @@ export class HistoryController {
@Get() @Get()
async getHistory(@RequestUser() user: User): Promise<HistoryEntryDto[]> { async getHistory(@RequestUser() user: User): Promise<HistoryEntryDto[]> {
try { const foundEntries = await this.historyService.getEntriesByUser(user);
const foundEntries = await this.historyService.getEntriesByUser(user); return await Promise.all(
return await Promise.all( foundEntries.map((entry) => this.historyService.toHistoryEntryDto(entry)),
foundEntries.map((entry) => );
this.historyService.toHistoryEntryDto(entry),
),
);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Post() @Post()
@ -63,26 +51,12 @@ export class HistoryController {
@RequestUser() user: User, @RequestUser() user: User,
@Body('history') history: HistoryEntryImportDto[], @Body('history') history: HistoryEntryImportDto[],
): Promise<void> { ): Promise<void> {
try { await this.historyService.setHistory(user, history);
await this.historyService.setHistory(user, history);
} catch (e) {
if (e instanceof NotInDBError || e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
}
} }
@Delete() @Delete()
async deleteHistory(@RequestUser() user: User): Promise<void> { async deleteHistory(@RequestUser() user: User): Promise<void> {
try { await this.historyService.deleteHistory(user);
await this.historyService.deleteHistory(user);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Put(':noteIdOrAlias') @Put(':noteIdOrAlias')
@ -92,19 +66,12 @@ export class HistoryController {
@RequestUser() user: User, @RequestUser() user: User,
@Body() entryUpdateDto: HistoryEntryUpdateDto, @Body() entryUpdateDto: HistoryEntryUpdateDto,
): Promise<HistoryEntryDto> { ): Promise<HistoryEntryDto> {
try { const newEntry = await this.historyService.updateHistoryEntry(
const newEntry = await this.historyService.updateHistoryEntry( note,
note, user,
user, entryUpdateDto,
entryUpdateDto, );
); return await this.historyService.toHistoryEntryDto(newEntry);
return await this.historyService.toHistoryEntryDto(newEntry);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Delete(':noteIdOrAlias') @Delete(':noteIdOrAlias')
@ -113,13 +80,6 @@ export class HistoryController {
@RequestNote() note: Note, @RequestNote() note: Note,
@RequestUser() user: User, @RequestUser() user: User,
): Promise<void> { ): Promise<void> {
try { await this.historyService.deleteHistoryEntry(note, user);
await this.historyService.deleteHistoryEntry(note, user);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
} }

View file

@ -4,16 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { import {
BadRequestException,
Controller, Controller,
Delete, Delete,
Headers, Headers,
HttpCode, HttpCode,
InternalServerErrorException,
NotFoundException,
Param, Param,
Post, Post,
UnauthorizedException,
UploadedFile, UploadedFile,
UseGuards, UseGuards,
UseInterceptors, UseInterceptors,
@ -30,12 +26,7 @@ import {
ApiUnauthorizedResponse, ApiUnauthorizedResponse,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { import { PermissionError } from '../../../errors/errors';
ClientError,
MediaBackendError,
NotInDBError,
PermissionError,
} from '../../../errors/errors';
import { SessionGuard } from '../../../identity/session.guard'; import { SessionGuard } from '../../../identity/session.guard';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto'; import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
@ -94,26 +85,14 @@ export class MediaController {
@Headers('HedgeDoc-Note') noteId: string, @Headers('HedgeDoc-Note') noteId: string,
@RequestUser() user: User, @RequestUser() user: User,
): Promise<MediaUploadUrlDto> { ): Promise<MediaUploadUrlDto> {
try { // TODO: Move getting the Note object into a decorator
// TODO: Move getting the Note object into a decorator const note: Note = await this.noteService.getNoteByIdOrAlias(noteId);
const note: Note = await this.noteService.getNoteByIdOrAlias(noteId); this.logger.debug(
this.logger.debug( `Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.username}'`,
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.username}'`, 'uploadMedia',
'uploadMedia', );
); const url = await this.mediaService.saveFile(file.buffer, user, note);
const url = await this.mediaService.saveFile(file.buffer, user, note); return this.mediaService.toMediaUploadUrlDto(url);
return this.mediaService.toMediaUploadUrlDto(url);
} catch (e) {
if (e instanceof ClientError || e instanceof NotInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof MediaBackendError) {
throw new InternalServerErrorException(
'There was an error in the media backend',
);
}
throw e;
}
} }
@Delete(':filename') @Delete(':filename')
@ -125,37 +104,20 @@ export class MediaController {
@Param('filename') filename: string, @Param('filename') filename: string,
): Promise<void> { ): Promise<void> {
const username = user.username; const username = user.username;
try { this.logger.debug(
this.logger.debug( `Deleting '${filename}' for user '${username}'`,
`Deleting '${filename}' for user '${username}'`, 'deleteMedia',
);
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
if ((await mediaUpload.user).username !== username) {
this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia', 'deleteMedia',
); );
const mediaUpload = await this.mediaService.findUploadByFilename( throw new PermissionError(
filename, `File '${filename}' is not owned by '${username}'`,
); );
if ((await mediaUpload.user).username !== username) {
this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia',
);
throw new PermissionError(
`File '${filename}' is not owned by '${username}'`,
);
}
await this.mediaService.deleteFile(mediaUpload);
} catch (e) {
if (e instanceof PermissionError) {
throw new UnauthorizedException(e.message);
}
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof MediaBackendError) {
throw new InternalServerErrorException(
'There was an error in the media backend',
);
}
throw e;
} }
await this.mediaService.deleteFile(mediaUpload);
} }
} }

View file

@ -4,13 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { import {
BadRequestException,
Body, Body,
Controller, Controller,
Delete, Delete,
Get, Get,
HttpCode, HttpCode,
NotFoundException,
Param, Param,
Post, Post,
UseGuards, UseGuards,
@ -18,11 +16,6 @@ import {
} from '@nestjs/common'; } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import {
AlreadyInDBError,
ForbiddenIdError,
NotInDBError,
} from '../../../errors/errors';
import { HistoryService } from '../../../history/history.service'; import { HistoryService } from '../../../history/history.service';
import { SessionGuard } from '../../../identity/session.guard'; import { SessionGuard } from '../../../identity/session.guard';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
@ -91,7 +84,7 @@ export class NotesController {
@RequestUser() user: User, @RequestUser() user: User,
@MarkdownBody() text: string, @MarkdownBody() text: string,
): Promise<NoteDto> { ): Promise<NoteDto> {
this.logger.debug('Got raw markdown:\n' + text); this.logger.debug('Got raw markdown:\n' + text, 'createNote');
return await this.noteService.toNoteDto( return await this.noteService.toNoteDto(
await this.noteService.createNote(text, user), await this.noteService.createNote(text, user),
); );
@ -107,19 +100,9 @@ export class NotesController {
@MarkdownBody() text: string, @MarkdownBody() text: string,
): Promise<NoteDto> { ): Promise<NoteDto> {
this.logger.debug('Got raw markdown:\n' + text, 'createNamedNote'); this.logger.debug('Got raw markdown:\n' + text, 'createNamedNote');
try { return await this.noteService.toNoteDto(
return await this.noteService.toNoteDto( await this.noteService.createNote(text, user, noteAlias),
await this.noteService.createNote(text, user, noteAlias), );
);
} catch (e) {
if (e instanceof AlreadyInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
}
} }
@Delete(':noteIdOrAlias') @Delete(':noteIdOrAlias')
@ -192,15 +175,8 @@ export class NotesController {
@RequestNote() note: Note, @RequestNote() note: Note,
@Param('revisionId') revisionId: number, @Param('revisionId') revisionId: number,
): Promise<RevisionDto> { ): Promise<RevisionDto> {
try { return this.revisionsService.toRevisionDto(
return this.revisionsService.toRevisionDto( await this.revisionsService.getRevision(note, revisionId),
await this.revisionsService.getRevision(note, revisionId), );
);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
} }

View file

@ -9,7 +9,6 @@ import {
Delete, Delete,
Get, Get,
HttpCode, HttpCode,
NotFoundException,
Param, Param,
Post, Post,
UnauthorizedException, UnauthorizedException,
@ -20,7 +19,6 @@ import { ApiTags } from '@nestjs/swagger';
import { AuthTokenWithSecretDto } from '../../../auth/auth-token-with-secret.dto'; import { AuthTokenWithSecretDto } from '../../../auth/auth-token-with-secret.dto';
import { AuthTokenDto } from '../../../auth/auth-token.dto'; import { AuthTokenDto } from '../../../auth/auth-token.dto';
import { AuthService } from '../../../auth/auth.service'; import { AuthService } from '../../../auth/auth.service';
import { NotInDBError } from '../../../errors/errors';
import { SessionGuard } from '../../../identity/session.guard'; import { SessionGuard } from '../../../identity/session.guard';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { User } from '../../../users/user.entity'; import { User } from '../../../users/user.entity';
@ -61,15 +59,9 @@ export class TokensController {
@Param('keyId') keyId: string, @Param('keyId') keyId: string,
): Promise<void> { ): Promise<void> {
const tokens = await this.authService.getTokensByUser(user); const tokens = await this.authService.getTokensByUser(user);
try { for (const token of tokens) {
for (const token of tokens) { if (token.keyId == keyId) {
if (token.keyId == keyId) { return await this.authService.removeToken(keyId);
return await this.authService.removeToken(keyId);
}
}
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
} }
} }
throw new UnauthorizedException( throw new UnauthorizedException(

View file

@ -9,7 +9,6 @@ import {
Controller, Controller,
Delete, Delete,
HttpCode, HttpCode,
NotFoundException,
Param, Param,
Post, Post,
Put, Put,
@ -24,12 +23,6 @@ import {
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy'; import { TokenAuthGuard } from '../../../auth/token.strategy';
import {
AlreadyInDBError,
ForbiddenIdError,
NotInDBError,
PrimaryAliasDeletionForbiddenError,
} from '../../../errors/errors';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { AliasCreateDto } from '../../../notes/alias-create.dto'; import { AliasCreateDto } from '../../../notes/alias-create.dto';
import { AliasUpdateDto } from '../../../notes/alias-update.dto'; import { AliasUpdateDto } from '../../../notes/alias-update.dto';
@ -41,6 +34,7 @@ import { User } from '../../../users/user.entity';
import { FullApi } from '../../utils/fullapi-decorator'; import { FullApi } from '../../utils/fullapi-decorator';
import { RequestUser } from '../../utils/request-user.decorator'; import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@ApiTags('alias') @ApiTags('alias')
@ApiSecurity('token') @ApiSecurity('token')
@Controller('alias') @Controller('alias')
@ -54,7 +48,6 @@ export class AliasController {
this.logger.setContext(AliasController.name); this.logger.setContext(AliasController.name);
} }
@UseGuards(TokenAuthGuard)
@Post() @Post()
@ApiOkResponse({ @ApiOkResponse({
description: 'The new alias', description: 'The new alias',
@ -65,30 +58,19 @@ export class AliasController {
@RequestUser() user: User, @RequestUser() user: User,
@Body() newAliasDto: AliasCreateDto, @Body() newAliasDto: AliasCreateDto,
): Promise<AliasDto> { ): Promise<AliasDto> {
try { const note = await this.noteService.getNoteByIdOrAlias(
const note = await this.noteService.getNoteByIdOrAlias( newAliasDto.noteIdOrAlias,
newAliasDto.noteIdOrAlias, );
); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
const updatedAlias = await this.aliasService.addAlias(
note,
newAliasDto.newAlias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} catch (e) {
if (e instanceof AlreadyInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
const updatedAlias = await this.aliasService.addAlias(
note,
newAliasDto.newAlias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} }
@UseGuards(TokenAuthGuard)
@Put(':alias') @Put(':alias')
@ApiOkResponse({ @ApiOkResponse({
description: 'The updated alias', description: 'The updated alias',
@ -105,28 +87,14 @@ export class AliasController {
`The field 'primaryAlias' must be set to 'true'.`, `The field 'primaryAlias' must be set to 'true'.`,
); );
} }
try { const note = await this.noteService.getNoteByIdOrAlias(alias);
const note = await this.noteService.getNoteByIdOrAlias(alias); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
const updatedAlias = await this.aliasService.makeAliasPrimary(
note,
alias,
);
return this.aliasService.toAliasDto(updatedAlias, note);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
const updatedAlias = await this.aliasService.makeAliasPrimary(note, alias);
return this.aliasService.toAliasDto(updatedAlias, note);
} }
@UseGuards(TokenAuthGuard)
@Delete(':alias') @Delete(':alias')
@HttpCode(204) @HttpCode(204)
@ApiNoContentResponse({ @ApiNoContentResponse({
@ -137,23 +105,10 @@ export class AliasController {
@RequestUser() user: User, @RequestUser() user: User,
@Param('alias') alias: string, @Param('alias') alias: string,
): Promise<void> { ): Promise<void> {
try { const note = await this.noteService.getNoteByIdOrAlias(alias);
const note = await this.noteService.getNoteByIdOrAlias(alias); if (!(await this.permissionsService.isOwner(user, note))) {
if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!');
throw new UnauthorizedException('Reading note denied!');
}
await this.aliasService.removeAlias(note, alias);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof PrimaryAliasDeletionForbiddenError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
} }
await this.aliasService.removeAlias(note, alias);
} }
} }

View file

@ -46,6 +46,7 @@ import { GetNoteInterceptor } from '../../utils/get-note.interceptor';
import { RequestNote } from '../../utils/request-note.decorator'; import { RequestNote } from '../../utils/request-note.decorator';
import { RequestUser } from '../../utils/request-user.decorator'; import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@ApiTags('me') @ApiTags('me')
@ApiSecurity('token') @ApiSecurity('token')
@Controller('me') @Controller('me')
@ -60,7 +61,6 @@ export class MeController {
this.logger.setContext(MeController.name); this.logger.setContext(MeController.name);
} }
@UseGuards(TokenAuthGuard)
@Get() @Get()
@ApiOkResponse({ @ApiOkResponse({
description: 'The user information', description: 'The user information',
@ -71,7 +71,6 @@ export class MeController {
return this.usersService.toUserDto(user); return this.usersService.toUserDto(user);
} }
@UseGuards(TokenAuthGuard)
@Get('history') @Get('history')
@ApiOkResponse({ @ApiOkResponse({
description: 'The history entries of the user', description: 'The history entries of the user',
@ -87,7 +86,6 @@ export class MeController {
} }
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@UseGuards(TokenAuthGuard)
@Get('history/:noteIdOrAlias') @Get('history/:noteIdOrAlias')
@ApiOkResponse({ @ApiOkResponse({
description: 'The history entry of the user which points to the note', description: 'The history entry of the user which points to the note',
@ -99,19 +97,11 @@ export class MeController {
@RequestUser() user: User, @RequestUser() user: User,
@RequestNote() note: Note, @RequestNote() note: Note,
): Promise<HistoryEntryDto> { ): Promise<HistoryEntryDto> {
try { const foundEntry = await this.historyService.getEntryByNote(note, user);
const foundEntry = await this.historyService.getEntryByNote(note, user); return await this.historyService.toHistoryEntryDto(foundEntry);
return await this.historyService.toHistoryEntryDto(foundEntry);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@UseGuards(TokenAuthGuard)
@Put('history/:noteIdOrAlias') @Put('history/:noteIdOrAlias')
@ApiOkResponse({ @ApiOkResponse({
description: 'The updated history entry', description: 'The updated history entry',
@ -125,24 +115,12 @@ export class MeController {
@Body() entryUpdateDto: HistoryEntryUpdateDto, @Body() entryUpdateDto: HistoryEntryUpdateDto,
): Promise<HistoryEntryDto> { ): Promise<HistoryEntryDto> {
// ToDo: Check if user is allowed to pin this history entry // ToDo: Check if user is allowed to pin this history entry
try { return await this.historyService.toHistoryEntryDto(
return await this.historyService.toHistoryEntryDto( await this.historyService.updateHistoryEntry(note, user, entryUpdateDto),
await this.historyService.updateHistoryEntry( );
note,
user,
entryUpdateDto,
),
);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@UseGuards(TokenAuthGuard)
@Delete('history/:noteIdOrAlias') @Delete('history/:noteIdOrAlias')
@HttpCode(204) @HttpCode(204)
@ApiNoContentResponse({ description: successfullyDeletedDescription }) @ApiNoContentResponse({ description: successfullyDeletedDescription })
@ -153,17 +131,9 @@ export class MeController {
@RequestNote() note: Note, @RequestNote() note: Note,
): Promise<void> { ): Promise<void> {
// ToDo: Check if user is allowed to delete note // ToDo: Check if user is allowed to delete note
try { await this.historyService.deleteHistoryEntry(note, user);
await this.historyService.deleteHistoryEntry(note, user);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@UseGuards(TokenAuthGuard)
@Get('notes') @Get('notes')
@ApiOkResponse({ @ApiOkResponse({
description: 'Metadata of all notes of the user', description: 'Metadata of all notes of the user',
@ -178,7 +148,6 @@ export class MeController {
); );
} }
@UseGuards(TokenAuthGuard)
@Get('media') @Get('media')
@ApiOkResponse({ @ApiOkResponse({
description: 'All media uploads of the user', description: 'All media uploads of the user',

View file

@ -4,16 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { import {
BadRequestException,
Controller, Controller,
Delete, Delete,
Headers, Headers,
HttpCode, HttpCode,
InternalServerErrorException,
NotFoundException,
Param, Param,
Post, Post,
UnauthorizedException,
UploadedFile, UploadedFile,
UseGuards, UseGuards,
UseInterceptors, UseInterceptors,
@ -32,12 +28,7 @@ import {
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy'; import { TokenAuthGuard } from '../../../auth/token.strategy';
import { import { PermissionError } from '../../../errors/errors';
ClientError,
MediaBackendError,
NotInDBError,
PermissionError,
} from '../../../errors/errors';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto'; import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
import { MediaService } from '../../../media/media.service'; import { MediaService } from '../../../media/media.service';
@ -53,6 +44,7 @@ import {
import { FullApi } from '../../utils/fullapi-decorator'; import { FullApi } from '../../utils/fullapi-decorator';
import { RequestUser } from '../../utils/request-user.decorator'; import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@ApiTags('media') @ApiTags('media')
@ApiSecurity('token') @ApiSecurity('token')
@Controller('media') @Controller('media')
@ -65,7 +57,6 @@ export class MediaController {
this.logger.setContext(MediaController.name); this.logger.setContext(MediaController.name);
} }
@UseGuards(TokenAuthGuard)
@Post() @Post()
@ApiConsumes('multipart/form-data') @ApiConsumes('multipart/form-data')
@ApiBody({ @ApiBody({
@ -96,29 +87,16 @@ export class MediaController {
@UploadedFile() file: MulterFile, @UploadedFile() file: MulterFile,
@Headers('HedgeDoc-Note') noteId: string, @Headers('HedgeDoc-Note') noteId: string,
): Promise<MediaUploadUrlDto> { ): Promise<MediaUploadUrlDto> {
try { // TODO: Move getting the Note object into a decorator
// TODO: Move getting the Note object into a decorator const note: Note = await this.noteService.getNoteByIdOrAlias(noteId);
const note: Note = await this.noteService.getNoteByIdOrAlias(noteId); this.logger.debug(
this.logger.debug( `Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.username}'`,
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.username}'`, 'uploadMedia',
'uploadMedia', );
); const url = await this.mediaService.saveFile(file.buffer, user, note);
const url = await this.mediaService.saveFile(file.buffer, user, note); return this.mediaService.toMediaUploadUrlDto(url);
return this.mediaService.toMediaUploadUrlDto(url);
} catch (e) {
if (e instanceof ClientError || e instanceof NotInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof MediaBackendError) {
throw new InternalServerErrorException(
'There was an error in the media backend',
);
}
throw e;
}
} }
@UseGuards(TokenAuthGuard)
@Delete(':filename') @Delete(':filename')
@HttpCode(204) @HttpCode(204)
@ApiNoContentResponse({ description: successfullyDeletedDescription }) @ApiNoContentResponse({ description: successfullyDeletedDescription })
@ -128,37 +106,20 @@ export class MediaController {
@Param('filename') filename: string, @Param('filename') filename: string,
): Promise<void> { ): Promise<void> {
const username = user.username; const username = user.username;
try { this.logger.debug(
this.logger.debug( `Deleting '${filename}' for user '${username}'`,
`Deleting '${filename}' for user '${username}'`, 'deleteMedia',
);
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
if ((await mediaUpload.user).username !== username) {
this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia', 'deleteMedia',
); );
const mediaUpload = await this.mediaService.findUploadByFilename( throw new PermissionError(
filename, `File '${filename}' is not owned by '${username}'`,
); );
if ((await mediaUpload.user).username !== username) {
this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia',
);
throw new PermissionError(
`File '${filename}' is not owned by '${username}'`,
);
}
await this.mediaService.deleteFile(mediaUpload);
} catch (e) {
if (e instanceof PermissionError) {
throw new UnauthorizedException(e.message);
}
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
if (e instanceof MediaBackendError) {
throw new InternalServerErrorException(
'There was an error in the media backend',
);
}
throw e;
} }
await this.mediaService.deleteFile(mediaUpload);
} }
} }

View file

@ -11,7 +11,6 @@ import {
Get, Get,
Header, Header,
HttpCode, HttpCode,
NotFoundException,
Param, Param,
Post, Post,
Put, Put,
@ -30,11 +29,6 @@ import {
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { TokenAuthGuard } from '../../../auth/token.strategy'; import { TokenAuthGuard } from '../../../auth/token.strategy';
import {
AlreadyInDBError,
ForbiddenIdError,
NotInDBError,
} from '../../../errors/errors';
import { HistoryService } from '../../../history/history.service'; import { HistoryService } from '../../../history/history.service';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { MediaUploadDto } from '../../../media/media-upload.dto'; import { MediaUploadDto } from '../../../media/media-upload.dto';
@ -66,6 +60,7 @@ import { PermissionsGuard } from '../../utils/permissions.guard';
import { RequestNote } from '../../utils/request-note.decorator'; import { RequestNote } from '../../utils/request-note.decorator';
import { RequestUser } from '../../utils/request-user.decorator'; import { RequestUser } from '../../utils/request-user.decorator';
@UseGuards(TokenAuthGuard)
@ApiTags('notes') @ApiTags('notes')
@ApiSecurity('token') @ApiSecurity('token')
@Controller('notes') @Controller('notes')
@ -98,7 +93,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias') @Get(':noteIdOrAlias')
@ApiOkResponse({ @ApiOkResponse({
description: 'Get information about the newly created note', description: 'Get information about the newly created note',
@ -114,7 +109,7 @@ export class NotesController {
} }
@Permissions(Permission.CREATE) @Permissions(Permission.CREATE)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Post(':noteAlias') @Post(':noteAlias')
@HttpCode(201) @HttpCode(201)
@ApiCreatedResponse({ @ApiCreatedResponse({
@ -129,24 +124,14 @@ export class NotesController {
@MarkdownBody() text: string, @MarkdownBody() text: string,
): Promise<NoteDto> { ): Promise<NoteDto> {
this.logger.debug('Got raw markdown:\n' + text, 'createNamedNote'); this.logger.debug('Got raw markdown:\n' + text, 'createNamedNote');
try { return await this.noteService.toNoteDto(
return await this.noteService.toNoteDto( await this.noteService.createNote(text, user, noteAlias),
await this.noteService.createNote(text, user, noteAlias), );
);
} catch (e) {
if (e instanceof AlreadyInDBError) {
throw new BadRequestException(e.message);
}
if (e instanceof ForbiddenIdError) {
throw new BadRequestException(e.message);
}
throw e;
}
} }
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.OWNER) @Permissions(Permission.OWNER)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Delete(':noteIdOrAlias') @Delete(':noteIdOrAlias')
@HttpCode(204) @HttpCode(204)
@ApiNoContentResponse({ description: successfullyDeletedDescription }) @ApiNoContentResponse({ description: successfullyDeletedDescription })
@ -172,7 +157,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.WRITE) @Permissions(Permission.WRITE)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Put(':noteIdOrAlias') @Put(':noteIdOrAlias')
@ApiOkResponse({ @ApiOkResponse({
description: 'The new, changed note', description: 'The new, changed note',
@ -192,7 +177,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias/content') @Get(':noteIdOrAlias/content')
@ApiProduces('text/markdown') @ApiProduces('text/markdown')
@ApiOkResponse({ @ApiOkResponse({
@ -209,7 +194,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias/metadata') @Get(':noteIdOrAlias/metadata')
@ApiOkResponse({ @ApiOkResponse({
description: 'The metadata of the note', description: 'The metadata of the note',
@ -225,7 +210,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.OWNER) @Permissions(Permission.OWNER)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Put(':noteIdOrAlias/metadata/permissions') @Put(':noteIdOrAlias/metadata/permissions')
@ApiOkResponse({ @ApiOkResponse({
description: 'The updated permissions of the note', description: 'The updated permissions of the note',
@ -244,7 +229,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias/revisions') @Get(':noteIdOrAlias/revisions')
@ApiOkResponse({ @ApiOkResponse({
description: 'Revisions of the note', description: 'Revisions of the note',
@ -266,7 +251,7 @@ export class NotesController {
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias/revisions/:revisionId') @Get(':noteIdOrAlias/revisions/:revisionId')
@ApiOkResponse({ @ApiOkResponse({
description: 'Revision of the note for the given id or alias', description: 'Revision of the note for the given id or alias',
@ -278,21 +263,14 @@ export class NotesController {
@RequestNote() note: Note, @RequestNote() note: Note,
@Param('revisionId') revisionId: number, @Param('revisionId') revisionId: number,
): Promise<RevisionDto> { ): Promise<RevisionDto> {
try { return this.revisionsService.toRevisionDto(
return this.revisionsService.toRevisionDto( await this.revisionsService.getRevision(note, revisionId),
await this.revisionsService.getRevision(note, revisionId), );
);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@UseInterceptors(GetNoteInterceptor) @UseInterceptors(GetNoteInterceptor)
@Permissions(Permission.READ) @Permissions(Permission.READ)
@UseGuards(TokenAuthGuard, PermissionsGuard) @UseGuards(PermissionsGuard)
@Get(':noteIdOrAlias/media') @Get(':noteIdOrAlias/media')
@ApiOkResponse({ @ApiOkResponse({
description: 'All media uploads of the note', description: 'All media uploads of the note',