Skip to content

Commit

Permalink
Add TRUST_PROXY config
Browse files Browse the repository at this point in the history
  • Loading branch information
marode-cap committed Nov 7, 2023
1 parent c184bc9 commit 0e5ee18
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 3 deletions.
7 changes: 5 additions & 2 deletions src/backend-for-frontend/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
import { INestApplication } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { DocumentBuilder, OpenAPIObject, SwaggerModule } from '@nestjs/swagger';
import RedisStore from 'connect-redis';
import session from 'express-session';
Expand All @@ -13,7 +13,8 @@ import { GlobalValidationPipe } from '../shared/validation/index.js';
import { BackendForFrontendModule } from './backend-for-frontend.module.js';

async function bootstrap(): Promise<void> {
const app: INestApplication = await NestFactory.create(BackendForFrontendModule);
const app: NestExpressApplication = await NestFactory.create<NestExpressApplication>(BackendForFrontendModule);

app.useGlobalPipes(new GlobalValidationPipe());
const swagger: Omit<OpenAPIObject, 'paths'> = new DocumentBuilder()
.setTitle('dBildungs IAM')
Expand All @@ -30,6 +31,8 @@ async function bootstrap(): Promise<void> {
const frontendConfig: FrontendConfig = configService.getOrThrow<FrontendConfig>('FRONTEND');
const redisConfig: RedisConfig = configService.getOrThrow<RedisConfig>('REDIS');

app.set('trust proxy', frontendConfig.TRUST_PROXY);

const redisClient: RedisClientType = createClient({
username: redisConfig.USERNAME,
password: redisConfig.PASSWORD,
Expand Down
20 changes: 19 additions & 1 deletion src/shared/config/frontend.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { IsBoolean, IsInt, IsNotEmpty, IsPositive, IsString, Max, Min } from 'class-validator';
import {
IsBoolean,
IsInt,
IsNotEmpty,
IsOptional,
IsPositive,
IsString,
Max,
Min,
isBoolean,
isInt,
isString,
} from 'class-validator';

import { OneOf } from '../util/one-of.validator.decorator.js';

export class FrontendConfig {
@IsInt()
@Min(1024)
@Max(10000)
public readonly PORT!: number;

@IsOptional()
@OneOf(isString, isInt, isBoolean)
public readonly TRUST_PROXY?: string | number | boolean;

@IsBoolean()
@IsNotEmpty()
public readonly SECURE_COOKIE!: boolean;
Expand Down
34 changes: 34 additions & 0 deletions src/shared/util/one-of.validator.decorator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { IsNotEmpty, ValidationError, isInt, isString, validateSync } from 'class-validator';
import { OneOf } from './one-of.validator.decorator.js';

describe('OneOf decorator', () => {
class Test {
@OneOf(isInt, isString)
@IsNotEmpty()
public test?: number | string;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public constructor(test: any) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.test = test;
}
}

it('should validate int', () => {
const errors: ValidationError[] = validateSync(new Test(5));

expect(errors.length).toBe(0);
});

it('should validate string', () => {
const errors: ValidationError[] = validateSync(new Test('TEST'));

expect(errors.length).toBe(0);
});

it('should fail on boolean', () => {
const errors: ValidationError[] = validateSync(new Test(true));

expect(errors.length).toBeGreaterThan(0);
});
});
23 changes: 23 additions & 0 deletions src/shared/util/one-of.validator.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ValidateBy, buildMessage } from 'class-validator';

type Predicate<T = unknown> = (val: unknown) => val is T;

/**
* class-validator decorator that tries to apply predicates until one matches
*
* @example
* @OneOf(isInt, isString)
* public intOrString: number | string;
*/
export function OneOf(...predicates: Predicate[]): PropertyDecorator {
return ValidateBy({
name: 'one-of-' + predicates.map((p: Predicate) => p.name).join('-'),
validator: {
validate: (value: unknown) => predicates.some((p: Predicate) => p(value)),
defaultMessage: buildMessage(
(eachPrefix: string) =>
eachPrefix + '$property must match one of ' + predicates.map((p: Predicate) => p.name).join(),
),
},
});
}

0 comments on commit 0e5ee18

Please sign in to comment.