Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const once = (fn) => {
return function (...args) {
if (called) return;
called = true;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return fn.apply(this, args);
};
};
Expand Down
38 changes: 12 additions & 26 deletions src/services/account/errors.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import { StatusCodes } from 'http-status-codes';

import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk';
import { createError } from '@fastify/error';

export const GraaspAccountError = ErrorFactory('graasp-plugin-account');
import { FAILURE_MESSAGES } from '@graasp/sdk';

export class AccountNotFound extends GraaspAccountError {
constructor(data?: unknown) {
super(
{
code: 'GPAECCRR001',
statusCode: StatusCodes.NOT_FOUND,
message: FAILURE_MESSAGES.ACCOUNT_NOT_FOUND,
},
data,
);
}
}
export const AccountNotFound = createError(
'GPAECCRR001',
FAILURE_MESSAGES.ACCOUNT_NOT_FOUND,
StatusCodes.NOT_FOUND,
);

export class NotMemberOrGuest extends GraaspAccountError {
constructor(data?: unknown) {
super(
{
code: 'GPAECCRR002',
statusCode: StatusCodes.FORBIDDEN,
message: FAILURE_MESSAGES.NOT_MEMBER_OR_GUEST,
},
data,
);
}
}
export const NotMemberOrGuest = createError(
'GPAECCRR002',
FAILURE_MESSAGES.NOT_MEMBER_OR_GUEST,
StatusCodes.FORBIDDEN,
);
22 changes: 6 additions & 16 deletions src/services/action/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { StatusCodes } from 'http-status-codes';

import { ErrorFactory } from '@graasp/sdk';
import { createError } from '@fastify/error';

import { PLUGIN_NAME } from '../constants';

export const GraaspActionError = ErrorFactory(PLUGIN_NAME);
export class CannotWriteFileError extends GraaspActionError {
constructor(data?: unknown) {
super(
{
code: 'GPAERR001',
statusCode: StatusCodes.NOT_FOUND,
message: 'A file was not created properly for the requested archive',
},
data,
);
}
}
export const CannotWriteFileError = createError(
'GPAERR001',
'A file was not created properly for the requested archive',
StatusCodes.NOT_FOUND,
);
2 changes: 2 additions & 0 deletions src/services/auth/plugins/captcha/captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export default function captchaPreHandler(
action: RecaptchaActionType,
options?: { shouldFail: boolean },
): RouteHandlerMethod {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return async (request: FastifyRequest<{ Body: { captcha: string } }>, _reply: FastifyReply) => {
const { captcha } = request.body;
return await validateCaptcha(request, captcha, action, options);
Expand Down
2 changes: 2 additions & 0 deletions src/services/auth/plugins/magicLink/magicLink.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
schema: auth,
preHandler: fastifyPassport.authenticate(
PassportStrategy.WebMagicLink,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
async (request, reply, err, user?: PassportUser, info?: PassportInfo) => {
// This function is called after the strategy has been executed.
// It is necessary, so we match the behavior of the original implementation.
Expand Down
2 changes: 2 additions & 0 deletions src/services/auth/plugins/passport/preHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export const guestAuthenticateAppsJWT = fastifyPassport.authenticate(
export function matchOne<R extends RouteGenericInterface>(
...strategies: RessourceAuthorizationStrategy<R>[]
): RouteHandlerMethod {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return async (req: FastifyRequest<R>) => {
if (!strategies.some((strategy) => strategy.test(req))) {
// If none of the strategies pass, throw an error.
Expand Down
9 changes: 6 additions & 3 deletions src/services/auth/plugins/passport/strategies/emailChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Authenticator } from '@fastify/passport';

import { EMAIL_CHANGE_JWT_SECRET } from '../../../../../config/secrets';
import { db } from '../../../../../drizzle/db';
import { MemberNotFound, UnauthorizedMember } from '../../../../../utils/errors';
import { MemberNotFound, UnauthorizedMember, buildError } from '../../../../../utils/errors';
import { MemberRepository } from '../../../../member/member.repository';
import { PassportStrategy } from '../strategies';
import type { CustomStrategyOptions, StrictVerifiedCallback } from '../types';
Expand Down Expand Up @@ -47,9 +47,12 @@ export default (
false,
);
}
} catch (err) {
} catch (error: unknown) {
// Exception occurred while fetching member
return done(options?.propagateError ? err : new UnauthorizedMember(), false);
return done(
options?.propagateError ? buildError(error) : new UnauthorizedMember(),
false,
);
}
},
),
Expand Down
9 changes: 6 additions & 3 deletions src/services/auth/plugins/passport/strategies/jwtApps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Authenticator } from '@fastify/passport';

import { APPS_JWT_SECRET } from '../../../../../config/secrets';
import { db } from '../../../../../drizzle/db';
import { UnauthorizedMember } from '../../../../../utils/errors';
import { UnauthorizedMember, buildError } from '../../../../../utils/errors';
import { AccountRepository } from '../../../../account/account.repository';
import { ItemRepository } from '../../../../item/item.repository';
import { PassportStrategy } from '../strategies';
Expand Down Expand Up @@ -53,11 +53,14 @@ export default (
key,
},
});
} catch (err) {
} catch (error: unknown) {
// Exception occurred while fetching item
// itemRepository.getOneOrThrow() can fail for many reasons like the item was not found, database error, etc.
// To avoid leaking information, we prefer to return UnauthorizedMember error.
return done(options?.propagateError ? err : new UnauthorizedMember(), false);
return done(
options?.propagateError ? buildError(error) : new UnauthorizedMember(),
false,
);
}
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { Authenticator } from '@fastify/passport';

import { JWT_SECRET } from '../../../../../config/secrets';
import { db } from '../../../../../drizzle/db';
import { ChallengeFailed, MemberNotFound, UnauthorizedMember } from '../../../../../utils/errors';
import {
ChallengeFailed,
MemberNotFound,
UnauthorizedMember,
buildError,
} from '../../../../../utils/errors';
import { AccountRepository } from '../../../../account/account.repository';
import { SHORT_TOKEN_PARAM } from '../constants';
import { PassportStrategy } from '../strategies';
Expand Down Expand Up @@ -57,9 +62,9 @@ export default (
false,
);
}
} catch (err) {
} catch (error: unknown) {
// Exception occurred while fetching member
return done(spreadException ? err : new UnauthorizedMember(), false);
return done(spreadException ? buildError(error) : new UnauthorizedMember(), false);
}
},
),
Expand Down
74 changes: 20 additions & 54 deletions src/services/chat/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,33 @@ import { StatusCodes } from 'http-status-codes';

import { createError } from '@fastify/error';

import { ErrorFactory } from '@graasp/sdk';

const PLUGIN_NAME = 'graasp-plugin-chatbox';

/**
* Errors thrown by the chat tasks
*/

export const GraaspChatboxError = ErrorFactory(PLUGIN_NAME);

export class ChatMessageNotFound extends GraaspChatboxError {
constructor(data?: unknown) {
super(
{
code: 'GICERR003',
statusCode: StatusCodes.NOT_FOUND,
message: 'Chat Message not found',
},
data,
);
}
}
export const ChatMessageNotFound = createError(
'GICERR003',
'Chat Message not found',
StatusCodes.NOT_FOUND,
);

export class MemberCannotEditMessage extends GraaspChatboxError {
constructor(data?: unknown) {
super(
{
code: 'GICERR002',
statusCode: StatusCodes.UNAUTHORIZED,
message: 'Member can only edit own messages',
},
data,
);
}
}
export const MemberCannotEditMessage = createError(
'GICERR002',
'Member can only edit own messages',
StatusCodes.UNAUTHORIZED,
);

export class MemberCannotDeleteMessage extends GraaspChatboxError {
constructor(data: { id: string }) {
super(
{
code: 'GICERR005',
statusCode: StatusCodes.UNAUTHORIZED,
message: 'Member can only delete own messages',
},
data.id,
);
}
}
export const MemberCannotDeleteMessage = createError(
'GICERR005',
'Member can only delete own messages',
StatusCodes.UNAUTHORIZED,
);

export class MemberCannotAccessMention extends GraaspChatboxError {
constructor(data: { id: string }) {
super(
{
code: 'GICERR004',
statusCode: StatusCodes.UNAUTHORIZED,
message: 'Member can only view own mentions',
},
data.id,
);
}
}
export const MemberCannotAccessMention = createError(
'GICERR004',
'Member can only view own mentions',
StatusCodes.UNAUTHORIZED,
);

export const ChatMentionNotFound = createError(
'GICERR006',
Expand Down
6 changes: 3 additions & 3 deletions src/services/file/file.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,19 +276,19 @@ describe('FileService', () => {
const copyMock = jest
.spyOn(s3Repository, 'copyFile')
.mockImplementation(async () => 'string');
await s3FileService.copy(member, copyPayload);
await s3FileService.copy(member.id, copyPayload);
expect(copyMock).toHaveBeenCalled();
});

it('empty originalPath throws', async () => {
await expect(
s3FileService.copy(member, { ...copyPayload, originalPath: '' }),
s3FileService.copy(member.id, { ...copyPayload, originalPath: '' }),
).rejects.toMatchObject(new CopyFileInvalidPathError(expect.anything()));
});

it('empty newFilePath throws', async () => {
await expect(
s3FileService.copy(member, { ...copyPayload, newFilePath: '' }),
s3FileService.copy(member.id, { ...copyPayload, newFilePath: '' }),
).rejects.toMatchObject(new CopyFileInvalidPathError(expect.anything()));
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/services/file/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class FileService {
}

async copy(
member: MinimalMember,
memberId: MinimalMember['id'],
data: {
newId?: string;
newFilePath: string;
Expand All @@ -180,7 +180,7 @@ class FileService {

return this.repository.copyFile({
newId,
memberId: member.id,
memberId,
originalPath,
newFilePath,
mimetype,
Expand Down
2 changes: 1 addition & 1 deletion src/services/file/repositories/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class LocalFileRepository implements FileRepository {
try {
await access(this.buildFullPath(filepath));
} catch (e) {
if (e.code === 'ENOENT') {
if (e !== null && typeof e === 'object' && 'code' in e && e.code === 'ENOENT') {
throw new LocalFileNotFound({ filepath });
}
throw e;
Expand Down
37 changes: 12 additions & 25 deletions src/services/file/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { StatusCodes } from 'http-status-codes';

import { createError } from '@fastify/error';

import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk';

import { PLUGIN_NAME } from './constants';
Expand Down Expand Up @@ -108,32 +110,17 @@ export class S3FileNotFound extends GraaspFileError {
}
}

export class UploadEmptyFileError extends GraaspFileError {
constructor(data?: unknown) {
super(
{
code: 'GPFERR008',
statusCode: StatusCodes.BAD_REQUEST,
message: FAILURE_MESSAGES.UPLOAD_EMPTY_FILE,
},
data,
);
}
}
export const UploadEmptyFileError = createError(
'GPFERR008',
FAILURE_MESSAGES.UPLOAD_EMPTY_FILE,
StatusCodes.BAD_REQUEST,
);

export class UploadFileUnexpectedError extends GraaspFileError {
constructor(data?: unknown) {
super(
{
code: 'GPFERR010',
statusCode: StatusCodes.INTERNAL_SERVER_ERROR,
// TODO: change message
message: FAILURE_MESSAGES.UPLOAD_FILE_UNEXPECTED_ERROR,
},
data,
);
}
}
export const UploadFileUnexpectedError = createError(
'GPFERR010',
FAILURE_MESSAGES.UPLOAD_FILE_UNEXPECTED_ERROR,
StatusCodes.INTERNAL_SERVER_ERROR,
);

export class DownloadFileUnexpectedError extends GraaspFileError {
constructor(data?: unknown) {
Expand Down
Loading
Loading