133 lines
4.3 KiB
TypeScript
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();
|
|
};
|