From 16f208480d91c804bc037ddea03b5f6d5ae05d61 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 12 Feb 2025 22:08:07 +0000 Subject: [PATCH] Added environment config. Added logging serializers. Updated logs to use serializers. --- backend/nestjs-seshat-api/src/app.module.ts | 14 ++++ .../src/auth/auth.access.service.ts | 4 +- .../src/auth/auth.controller.ts | 25 +++++++- .../src/auth/auth.refresh.service.ts | 14 ++++ .../src/logging.serializers.ts | 64 +++++++++++++++++++ 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 backend/nestjs-seshat-api/src/logging.serializers.ts diff --git a/backend/nestjs-seshat-api/src/app.module.ts b/backend/nestjs-seshat-api/src/app.module.ts index d550cf3..d3d82db 100644 --- a/backend/nestjs-seshat-api/src/app.module.ts +++ b/backend/nestjs-seshat-api/src/app.module.ts @@ -11,6 +11,7 @@ import { UsersModule } from './users/users.module'; import { UserEntity } from './users/users.entity'; import { AuthModule } from './auth/auth.module'; import { LoggerModule } from 'nestjs-pino'; +import { serialize_token, serialize_user_short, serialize_user_long, serialize_res, serialize_req } from './logging.serializers'; @Module({ imports: [ @@ -30,6 +31,19 @@ import { LoggerModule } from 'nestjs-pino'; pinoHttp: { level: config.get('LOG_LEVEL') ?? 'info', autoLogging: true, + serializers: config.get('ENVIRONMENT', 'production') == 'development' ? { + user: value => serialize_user_long(value), + access_token: value => serialize_token(value), + refresh_token: value => serialize_token(value), + req: value => serialize_req(value), + res: value => serialize_res(value), + } : { + user: value => serialize_user_short(value), + access_token: value => serialize_token(value), + refresh_token: value => serialize_token(value), + req: value => serialize_req(value), + res: value => serialize_res(value), + }, stream: pino.destination({ dest: path.join(config.get('LOG_DIRECTORY') ?? 'logs', 'backend.api.log'), minLength: 512, diff --git a/backend/nestjs-seshat-api/src/auth/auth.access.service.ts b/backend/nestjs-seshat-api/src/auth/auth.access.service.ts index 73cef35..83892b1 100644 --- a/backend/nestjs-seshat-api/src/auth/auth.access.service.ts +++ b/backend/nestjs-seshat-api/src/auth/auth.access.service.ts @@ -34,7 +34,9 @@ export class AuthAccessService { this.logger.debug({ class: AuthAccessService.name, method: this.generate.name, - user_login: user.userLogin, + user, + access_token: token, + exp: expiration, msg: 'User generated an access token.', }); diff --git a/backend/nestjs-seshat-api/src/auth/auth.controller.ts b/backend/nestjs-seshat-api/src/auth/auth.controller.ts index ea6f7b7..c7d90a2 100644 --- a/backend/nestjs-seshat-api/src/auth/auth.controller.ts +++ b/backend/nestjs-seshat-api/src/auth/auth.controller.ts @@ -38,6 +38,7 @@ export class AuthController { this.logger.error({ class: AuthController.name, method: this.login.name, + user: req.user, msg: 'Failed to login.', error: err, }); @@ -62,7 +63,9 @@ export class AuthController { this.logger.info({ class: AuthController.name, method: this.login.name, - user_login: req.user.userLogin, + user: req.user, + access_token: data.access_token, + refresh_token: data.refresh_token, msg: 'User logged in.', }); @@ -87,7 +90,7 @@ export class AuthController { this.logger.info({ class: AuthController.name, method: this.logout.name, - user_login: req.user.userLogin, + user: req.user, msg: 'User logged off', }); @@ -112,6 +115,8 @@ export class AuthController { this.logger.debug({ class: AuthController.name, method: this.refresh.name, + user: req.user, + access_token: data.access_token, msg: 'Updated Authentication cookie for access token.', }); @@ -124,6 +129,8 @@ export class AuthController { this.logger.debug({ class: AuthController.name, method: this.refresh.name, + user: req.user, + refresh_token: data.refresh_token, msg: 'Updated Refresh cookie for refresh token.', }); } @@ -133,6 +140,7 @@ export class AuthController { this.logger.error({ class: AuthController.name, method: this.refresh.name, + user: req.user, msg: 'Failed to refresh tokens.', error: err, }); @@ -158,7 +166,7 @@ export class AuthController { this.logger.info({ class: AuthController.name, method: this.register.name, - user_login: user.userLogin, + user: req.user, msg: 'User registered', }); } catch (err) { @@ -167,6 +175,7 @@ export class AuthController { this.logger.warn({ class: AuthController.name, method: this.register.name, + user: req.user, msg: 'Failed to register due to duplicate userLogin.', }); return { @@ -178,6 +187,7 @@ export class AuthController { this.logger.error({ class: AuthController.name, method: this.register.name, + user: req.user, msg: 'Failed to register.', error: err, }); @@ -190,6 +200,14 @@ export class AuthController { try { data = await this.auth.login(user); if (!data.access_token || !data.refresh_token || !data.refresh_exp) { + this.logger.error({ + class: AuthController.name, + method: this.register.name, + user: req.user, + access_token: data.access_token, + refresh_token: data.refresh_token, + msg: 'Failed to generate tokens after registering.', + }); return { success: false, error_message: 'Something went wrong with tokens while logging in.', @@ -199,6 +217,7 @@ export class AuthController { this.logger.error({ class: AuthController.name, method: this.register.name, + user: req.user, msg: 'Failed to login after registering.', error: err, }); diff --git a/backend/nestjs-seshat-api/src/auth/auth.refresh.service.ts b/backend/nestjs-seshat-api/src/auth/auth.refresh.service.ts index f1b9e77..c1d8468 100644 --- a/backend/nestjs-seshat-api/src/auth/auth.refresh.service.ts +++ b/backend/nestjs-seshat-api/src/auth/auth.refresh.service.ts @@ -29,6 +29,8 @@ export class AuthRefreshService { this.logger.warn({ class: AuthRefreshService.name, method: this.generate.name, + user, + refresh_token: refreshToken, msg: 'Refresh token given is invalid.', }); throw new UnauthorizedException('Invalid refresh token.'); @@ -37,6 +39,9 @@ export class AuthRefreshService { this.logger.warn({ class: AuthRefreshService.name, method: this.generate.name, + user, + refresh_token: refreshToken, + exp: expiration, msg: 'Refresh token given has expired.', }); throw new UnauthorizedException('Invalid refresh token.'); @@ -61,6 +66,9 @@ export class AuthRefreshService { this.logger.debug({ class: AuthRefreshService.name, method: this.generate.name, + user, + refresh_token: refreshToken, + exp: expiration, msg: 'Deleted previous refresh token.', }); } @@ -83,6 +91,9 @@ export class AuthRefreshService { this.logger.debug({ class: AuthRefreshService.name, method: this.generate.name, + user, + refresh_token: refreshToken, + exp: expiration, msg: 'Generated a new refresh token.', }); @@ -95,6 +106,9 @@ export class AuthRefreshService { this.logger.debug({ class: AuthRefreshService.name, method: this.generate.name, + user, + refresh_token: refreshToken, + exp: expiration, msg: 'Inserted the new refresh token.', }); } diff --git a/backend/nestjs-seshat-api/src/logging.serializers.ts b/backend/nestjs-seshat-api/src/logging.serializers.ts new file mode 100644 index 0000000..b2f1050 --- /dev/null +++ b/backend/nestjs-seshat-api/src/logging.serializers.ts @@ -0,0 +1,64 @@ +import { UserEntity } from "./users/users.entity"; + +export function serialize_user_short(value: UserEntity) { + if (!value) { + return value; + } + + return value.userLogin; +} + +export function serialize_user_long(value: UserEntity) { + if (!value) { + return value; + } + + return { + user_id: value.userId, + user_login: value.userLogin, + is_admin: value.isAdmin, + } +} + +export function serialize_token(value: string) { + return '...' + value.substring(Math.max(value.length - 12, value.length / 2) | 0); +} + +export function serialize_req(value) { + if (!value) { + return value; + } + + delete value['remoteAddress'] + delete value['remotePort'] + + if (value.headers) { + const headers = value.headers; + if (headers['Authorization']) { + headers['Authorization'] = headers['Authorization'].substring(Math.max(0, headers['Authorization'].length - 12)) + } + } + return value; +} + +export function serialize_res(value) { + if (!value || !value.headers) { + return value; + } + + const headers = value.headers; + delete headers['x-powered-by']; + + if (headers['set-cookie']) { + const cookies = headers['set-cookie']; + for (let i in cookies) { + const cookie: string = cookies[i]; + if (cookie.startsWith('Authentication=')) { + cookies[i] = 'Authentication=...' + cookie.substring(Math.max(0, cookie.indexOf(';') - 12)); + } else if (cookie.startsWith('Refresh=')) { + cookies[i] = 'Refresh=...' + cookie.substring(Math.max(0, cookie.indexOf(';') - 12)); + } + } + } + return value; +} \ No newline at end of file