backend/lib/seed/graphql/MiddlewareV2.ts
2025-05-14 21:45:16 +02:00

133 lines
4.3 KiB
TypeScript

import _ from 'lodash';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
import { ApolloError } from 'apollo-server-lambda';
import { ClassType, createMethodDecorator, MiddlewareFn } from 'type-graphql';
import Firebase from '@seed/services/auth/FirebaseService';
import { ApolloContext } from '@seed/interfaces/context';
import AccountModel from 'src/accounts/account.model';
import { AccountTypeEnum } from '@src/accounts/account.components';
import { validateOrReject } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { newError } from '@seed/helpers/Error';
export interface EngineMiddlewareInput {
authorization?: AccountTypeEnum[];
apiKey?: boolean;
checkOrganisation?: boolean;
noOrganisationCheck?: boolean;
noPermissionCheck?: boolean;
validations?: {
inputName?: string;
schema: ClassType<any>;
}[];
}
export const checkApiKey = async ({ context }): Promise<boolean> => {
const user: AccountModel = new AccountModel();
let headers: any = {};
if (context.event) headers = context.event.headers;
else if (context.req) headers = context.req.headers;
/*
* Deal with Auth
*/
const apiKey: string = headers['x-api-key'];
if (apiKey) {
// Verify that exists in DB
try {
const account = await user.getOne({ 'apiKeys.key': apiKey }, null);
context.ctx.user = account;
return true;
} catch (error) {
throw newError(2200, '403');
}
} else throw newError(2202, { required: 'X-API-Key' });
};
export const checkAuth = ({ context }, user, roles: string[]): boolean => {
let headers: any = {};
if (context.event) headers = context.event.headers;
else if (context.req) headers = context.req.headers;
if (roles.includes(AccountTypeEnum.public)) return true;
const authorization: string = headers['x-authorization'] || headers.authorization || headers.Authorization;
if (!authorization) {
throw newError(2002);
}
if (!user || !user._id) {
throw newError(2004, '403');
}
if (roles.length > 0) {
roles.push(AccountTypeEnum.admin);
user.types.push(AccountTypeEnum.public);
const rolesToVerif = _.uniq(roles);
const userTypes = _.uniq(user.types);
const intersection = _.intersection(rolesToVerif, userTypes);
if (intersection.length > 0) return true;
// no roles matched, restrict access
throw newError(2000, { you: user.types, allowed: roles });
}
return true;
};
export const EngineMiddleware = (init: EngineMiddlewareInput): any => {
return createMethodDecorator(async (data, next) => {
// console.log('engine', data);
const { context, args } = data;
let returnError;
if (init.authorization) {
const user = (context as any).ctx.user;
try {
checkAuth({ context }, user, init.authorization);
} catch (error) {
returnError = error;
}
}
if (init.apiKey) {
try {
await checkApiKey({ context });
returnError = null;
} catch (error) {
returnError = error;
}
}
if (returnError) throw returnError;
if (init.checkOrganisation && !(context as any).ctx.organisationId) throw newError(2102, '400');
if (init.noOrganisationCheck) (context as any).ctx.noOrganisationCheck = true;
if (init.noPermissionCheck) (context as any).ctx.noPermissionCheck = true;
if (init.validations)
for (let index = 0; index < init.validations.length; index++) {
const element = init.validations[index];
if (!element.inputName) element.inputName = 'input';
try {
const instance = plainToClass(element.schema, args[element.inputName]);
await validateOrReject(instance);
} catch (error) {
throw newError(3001, error);
}
}
return next();
});
};
export const inputValidation: MiddlewareFn<any> = async ({ root, args, context, info }, next) => {
console.log({ root, args, context, info });
return next();
};