feat: add some security
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"local": {
|
"local": {
|
||||||
"baseUrl": "http://localhost:3000"
|
"baseUrl": "http://localhost:3000/v1"
|
||||||
},
|
},
|
||||||
"dev": {
|
"dev": {
|
||||||
"baseUrl": "http://localhost:3000"
|
"baseUrl": "http://localhost:3000"
|
||||||
|
|||||||
@@ -2,4 +2,11 @@ import { registerAs } from '@nestjs/config';
|
|||||||
|
|
||||||
export default registerAs('app', () => ({
|
export default registerAs('app', () => ({
|
||||||
environment: process.env.NODE_ENV || 'production',
|
environment: process.env.NODE_ENV || 'production',
|
||||||
|
corsOrigins: process.env.CORS_ORIGINS?.split(',').map((origin) =>
|
||||||
|
origin.trim(),
|
||||||
|
),
|
||||||
|
corsSubdomain: process.env.CORS_SUBDOMAIN,
|
||||||
|
swaggerServers: process.env.SWAGGER_SERVERS?.split(',').map((origin) =>
|
||||||
|
origin.trim(),
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -18,4 +18,7 @@ export default Joi.object({
|
|||||||
JWT_AUDIENCE: Joi.string().required(),
|
JWT_AUDIENCE: Joi.string().required(),
|
||||||
REDIS_HOST: Joi.string().required(),
|
REDIS_HOST: Joi.string().required(),
|
||||||
REDIS_PORT: Joi.number().port().default(6379),
|
REDIS_PORT: Joi.number().port().default(6379),
|
||||||
|
CORS_ORIGINS: Joi.string().required(),
|
||||||
|
CORS_SUBDOMAIN: Joi.string().required(),
|
||||||
|
SWAGGER_SERVERS: Joi.string().required(),
|
||||||
});
|
});
|
||||||
|
|||||||
+44
-8
@@ -4,10 +4,13 @@ import { I18nValidationExceptionFilter, I18nValidationPipe } from 'nestjs-i18n';
|
|||||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
|
app.setGlobalPrefix('v1');
|
||||||
|
|
||||||
app.useGlobalPipes(
|
app.useGlobalPipes(
|
||||||
new I18nValidationPipe({
|
new I18nValidationPipe({
|
||||||
whitelist: true,
|
whitelist: true,
|
||||||
@@ -32,24 +35,57 @@ async function bootstrap() {
|
|||||||
|
|
||||||
app.useGlobalInterceptors(new ResponseInterceptor());
|
app.useGlobalInterceptors(new ResponseInterceptor());
|
||||||
|
|
||||||
|
const configService = app.get(ConfigService);
|
||||||
|
|
||||||
|
const origins = configService.get<string[]>('app.corsOrigins');
|
||||||
|
const subdomain = configService.get<string>('app.corsSubdomain');
|
||||||
|
|
||||||
|
console.log(subdomain, origins);
|
||||||
|
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: ['http://localhost:5173'],
|
origin: (origin: string, callback: any) => {
|
||||||
|
if (!origin) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origins?.includes(origin)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const hostname = new URL(origin).hostname;
|
||||||
|
|
||||||
|
if (hostname === subdomain || hostname.endsWith(`.${subdomain}`)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
||||||
|
return callback(null, true);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
callback(new Error('Not allowed by CORS'));
|
||||||
|
},
|
||||||
credentials: true,
|
credentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
|
|
||||||
const config = new DocumentBuilder()
|
const config = new DocumentBuilder()
|
||||||
.setTitle('Borna API')
|
.setTitle('Heala API')
|
||||||
.setDescription('Borna API Documentation')
|
.setDescription('Heala API Documentation')
|
||||||
.setVersion('1.0')
|
.setVersion('1.0')
|
||||||
.setLicense('MIT', 'https://opensource.org/license/mit')
|
.setLicense('MIT', 'https://opensource.org/license/mit');
|
||||||
.addServer('http://localhost:3000')
|
|
||||||
.build();
|
|
||||||
|
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
const swaggerServers = configService.get<string[]>('app.swaggerServers');
|
||||||
|
|
||||||
SwaggerModule.setup('api/v1', app, document);
|
swaggerServers?.forEach((server) => config.addServer(server));
|
||||||
|
|
||||||
|
const document = SwaggerModule.createDocument(app, config.build());
|
||||||
|
|
||||||
|
SwaggerModule.setup('v1/docs', app, document);
|
||||||
|
|
||||||
await app.listen(process.env.PORT ?? 3000);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
|||||||
import { AdminAuthService } from './providers/admin-auth.service';
|
import { AdminAuthService } from './providers/admin-auth.service';
|
||||||
import { AdminLoginDTO } from './dtos/admin-login.dto';
|
import { AdminLoginDTO } from './dtos/admin-login.dto';
|
||||||
import { Public } from '@/modules/auth/decorators/public.decorator';
|
import { Public } from '@/modules/auth/decorators/public.decorator';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('admin/auth')
|
@Controller('admin/auth')
|
||||||
|
@Throttle({ login: {} })
|
||||||
export class AdminAuthController {
|
export class AdminAuthController {
|
||||||
constructor(
|
constructor(
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('admin/choices')
|
@Controller('admin/choices')
|
||||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||||
|
@Throttle({ admin: {} })
|
||||||
export class AdminChoicesController {
|
export class AdminChoicesController {
|
||||||
constructor(
|
constructor(
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('admin/questions')
|
@Controller('admin/questions')
|
||||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||||
|
@Throttle({ admin: {} })
|
||||||
export class AdminQuestionsController {
|
export class AdminQuestionsController {
|
||||||
constructor(
|
constructor(
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('admin/scoring')
|
@Controller('admin/scoring')
|
||||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||||
|
@Throttle({ admin: {} })
|
||||||
export class AdminScoringController {
|
export class AdminScoringController {
|
||||||
constructor(
|
constructor(
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('admin/tests')
|
@Controller('admin/tests')
|
||||||
@Roles(Role.SUPERUSER, Role.ADMIN)
|
@Roles(Role.SUPERUSER, Role.ADMIN)
|
||||||
|
@Throttle({ admin: {} })
|
||||||
export class AdminTestsController {
|
export class AdminTestsController {
|
||||||
constructor(
|
constructor(
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AuthService } from './providers/auth.service';
|
|||||||
import { VerifyOtpDTO } from './dtos/verify-otp.dto';
|
import { VerifyOtpDTO } from './dtos/verify-otp.dto';
|
||||||
import { Public } from './decorators/public.decorator';
|
import { Public } from './decorators/public.decorator';
|
||||||
import { RefreshTokenDTO } from './dtos/refresh-token.dto';
|
import { RefreshTokenDTO } from './dtos/refresh-token.dto';
|
||||||
|
import { Throttle } from '@nestjs/throttler';
|
||||||
|
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
@@ -17,6 +18,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('login')
|
@Post('login')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Throttle({ login: {} })
|
||||||
public async logIn(@Body() loginDto: LoginDTO) {
|
public async logIn(@Body() loginDto: LoginDTO) {
|
||||||
return await this.authService.logIn(loginDto);
|
return await this.authService.logIn(loginDto);
|
||||||
}
|
}
|
||||||
@@ -24,6 +26,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('verify-otp')
|
@Post('verify-otp')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Throttle({ otp: {} })
|
||||||
public async verifyOTP(@Body() verifyOtpDto: VerifyOtpDTO) {
|
public async verifyOTP(@Body() verifyOtpDto: VerifyOtpDTO) {
|
||||||
return await this.authService.verifyOTP(verifyOtpDto);
|
return await this.authService.verifyOTP(verifyOtpDto);
|
||||||
}
|
}
|
||||||
@@ -31,6 +34,7 @@ export class AuthController {
|
|||||||
@Public()
|
@Public()
|
||||||
@Post('refresh')
|
@Post('refresh')
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Throttle({ login: {} })
|
||||||
public async refreshToken(@Body() refreshTokenDto: RefreshTokenDTO) {
|
public async refreshToken(@Body() refreshTokenDto: RefreshTokenDTO) {
|
||||||
return await this.authService.refreshToken(refreshTokenDto);
|
return await this.authService.refreshToken(refreshTokenDto);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user