mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import { ForbiddenException, Injectable, NestMiddleware } from '@nestjs/common';
|
|
import { InjectDataSource } from '@nestjs/typeorm';
|
|
import { DataSource } from 'typeorm';
|
|
import { NextFunction, Request, Response } from 'express';
|
|
|
|
export const CLIENT_APP_HEADER = 'x-client-app';
|
|
|
|
// EUserType values from @tria-plc/api-common, duplicated here to avoid
|
|
// pulling in the full enum just for this string comparison.
|
|
const ALLOWED_USER_TYPES_BY_CLIENT: Record<string, string[]> = {
|
|
backoffice: ['employee'],
|
|
portal: ['individual', 'external_organization'],
|
|
};
|
|
|
|
/**
|
|
* Blocks EDRFREIGHT-415: /auth/login and /auth/mfa-verify match credentials
|
|
* against email/username/phone_number only (see vendor
|
|
* findUserForLogin), with no check that the account's userType belongs on
|
|
* the app that's asking. A backoffice (employee) client presenting a
|
|
* customer's credentials — or vice versa — must not get a session.
|
|
*/
|
|
@Injectable()
|
|
export class LoginAudienceMiddleware implements NestMiddleware {
|
|
constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
|
|
|
|
async use(req: Request, _res: Response, next: NextFunction) {
|
|
const clientApp = req.header(CLIENT_APP_HEADER);
|
|
const allowedUserTypes = clientApp
|
|
? ALLOWED_USER_TYPES_BY_CLIENT[clientApp]
|
|
: undefined;
|
|
if (!allowedUserTypes) {
|
|
throw new ForbiddenException(
|
|
`Missing or unrecognized ${CLIENT_APP_HEADER} header`,
|
|
);
|
|
}
|
|
|
|
const identifier: unknown = req.body?.email;
|
|
if (typeof identifier !== 'string' || !identifier) {
|
|
// No identifier to look up — the vendor DTO validation rejects the
|
|
// request on its own.
|
|
return next();
|
|
}
|
|
|
|
const [user] = await this.dataSource.query(
|
|
`SELECT user_type AS "userType" FROM iam.users
|
|
WHERE email = $1 OR username = $1 OR phone_number = $1 LIMIT 1`,
|
|
[identifier],
|
|
);
|
|
|
|
if (user && !allowedUserTypes.includes(user.userType)) {
|
|
throw new ForbiddenException(
|
|
`This account cannot sign in through the ${clientApp} application`,
|
|
);
|
|
}
|
|
|
|
next();
|
|
}
|
|
}
|