From 0674b4be5036b44c2ac56d92479048cc7e3603ba Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Tue, 22 Jul 2025 13:57:22 +0200 Subject: [PATCH 1/2] improve socket handling --- client/src/layouts/default/Default.vue | 2 +- client/src/layouts/default/Popup.vue | 2 +- server/src/auth/auth.module.ts | 2 +- server/src/auth/strategies/ws-jwt.guard.ts | 60 ++++++++++++++++++++++ server/src/events/events.gateway.ts | 3 ++ server/src/events/events.module.ts | 2 + 6 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 server/src/auth/strategies/ws-jwt.guard.ts diff --git a/client/src/layouts/default/Default.vue b/client/src/layouts/default/Default.vue index 7dbab9805..fc6289800 100644 --- a/client/src/layouts/default/Default.vue +++ b/client/src/layouts/default/Default.vue @@ -23,7 +23,7 @@ import { useCookies } from "vue3-cookies"; import { useSocketIO } from '../../socket.io'; const { cookies } = useCookies(); -const token = cookies.get("kubero.websocketToken"); +const token = cookies.get("kubero.JWT_TOKEN"); //console.log("COOKIE token", token); const { socket } = useSocketIO(token); diff --git a/client/src/layouts/default/Popup.vue b/client/src/layouts/default/Popup.vue index 7bb5f558f..5f323cd59 100644 --- a/client/src/layouts/default/Popup.vue +++ b/client/src/layouts/default/Popup.vue @@ -15,7 +15,7 @@ import { useCookies } from "vue3-cookies"; import { useSocketIO } from '../../socket.io'; const { cookies } = useCookies(); -const token = cookies.get("kubero.websocketToken"); +const token = cookies.get("kubero.JWT_TOKEN"); //console.log("COOKIE token", token); const { socket } = useSocketIO(token); diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts index 8a865c598..21e811893 100644 --- a/server/src/auth/auth.module.ts +++ b/server/src/auth/auth.module.ts @@ -37,6 +37,6 @@ if (ConfigService.getGithubEnabled()) { ], providers: providers, controllers: [AuthController], - exports: [AuthService], + exports: [AuthService, JwtModule, UsersModule], }) export class AuthModule {} diff --git a/server/src/auth/strategies/ws-jwt.guard.ts b/server/src/auth/strategies/ws-jwt.guard.ts new file mode 100644 index 000000000..e013fb0d9 --- /dev/null +++ b/server/src/auth/strategies/ws-jwt.guard.ts @@ -0,0 +1,60 @@ +import { CanActivate, Injectable, Logger } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { Observable } from 'rxjs'; +import { UsersService } from 'src/users/users.service'; + +@Injectable() +export class WsJwtGuard implements CanActivate { + private logger = new Logger(WsJwtGuard.name); + + constructor( + private jwtService: JwtService, + private usersService: UsersService, + ) {} + + canActivate( + context: any, + ): boolean | any | Promise | Observable { + const client = context.switchToWs().getClient(); + const token = this.extractToken(client); + + if (!token) { + this.logger.debug('No token provided, disconnecting client'); + client.disconnect(); + return false; + } + + try { + const decoded = this.jwtService.verify(token); + if (!decoded || !decoded.username) { + this.logger.debug('Token verification failed, disconnecting client'); + client.disconnect(); + return false; + } + return new Promise((resolve, reject) => { + return this.usersService.findOne(decoded.username).then((user) => { + if (user) { + context.switchToWs().getClient().user = user; + resolve(true); + } else { + this.logger.debug('User not found, disconnecting client'); + client.disconnect(); + reject(false); + } + }); + }); + } catch (ex) { + this.logger.error('Token validation error', ex.message); + client.disconnect(); + return false; + } + } + + private extractToken(client: any): string | null { + const authHeader = client.handshake.headers.authorization; + if (authHeader && authHeader.startsWith('Bearer ')) { + return authHeader.split(' ')[1]; + } + return client.handshake.auth?.token || null; + } +} diff --git a/server/src/events/events.gateway.ts b/server/src/events/events.gateway.ts index eb6750996..984e7c389 100644 --- a/server/src/events/events.gateway.ts +++ b/server/src/events/events.gateway.ts @@ -7,12 +7,15 @@ import { WsResponse, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; +import { UseGuards } from '@nestjs/common'; +import { WsJwtGuard } from 'src/auth/strategies/ws-jwt.guard'; @WebSocketGateway({ cors: { origin: '*', }, }) +@UseGuards(WsJwtGuard) export class EventsGateway { @WebSocketServer() server: Server; diff --git a/server/src/events/events.module.ts b/server/src/events/events.module.ts index 1e723d35d..29151d00e 100644 --- a/server/src/events/events.module.ts +++ b/server/src/events/events.module.ts @@ -1,8 +1,10 @@ import { Module, Global } from '@nestjs/common'; import { EventsGateway } from './events.gateway'; +import { AuthModule } from 'src/auth/auth.module'; @Global() @Module({ + imports: [AuthModule], providers: [EventsGateway], exports: [EventsGateway], }) From 99c628a91056bc5b87b518a18840a7831ca4dc11 Mon Sep 17 00:00:00 2001 From: Gianni Carafa Date: Tue, 22 Jul 2025 03:49:13 +0200 Subject: [PATCH 2/2] fix tests --- server/src/audit/audit.module.ts | 3 ++- server/src/auth/auth.module.ts | 10 +++++++--- server/src/auth/strategies/github.strategy.ts | 2 +- server/src/auth/strategies/ws-jwt.guard.ts | 2 +- server/src/config/config.module.ts | 4 +++- server/src/events/events.gateway.ts | 2 +- server/src/events/events.module.ts | 2 +- 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/server/src/audit/audit.module.ts b/server/src/audit/audit.module.ts index 8e64cdd14..4ddde7712 100644 --- a/server/src/audit/audit.module.ts +++ b/server/src/audit/audit.module.ts @@ -1,10 +1,11 @@ import { Module, Global } from '@nestjs/common'; import { AuditService } from './audit.service'; import { AuditController } from './audit.controller'; +import { PrismaClient } from '@prisma/client'; @Global() @Module({ - providers: [AuditService], + providers: [AuditService, PrismaClient], controllers: [AuditController], exports: [AuditService], }) diff --git a/server/src/auth/auth.module.ts b/server/src/auth/auth.module.ts index 21e811893..e4ea9cb86 100644 --- a/server/src/auth/auth.module.ts +++ b/server/src/auth/auth.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, Provider } from '@nestjs/common'; import { AuthService } from './auth.service'; import { UsersModule } from '../users/users.module'; import { KubernetesModule } from '../kubernetes/kubernetes.module'; @@ -10,11 +10,12 @@ import { AuthController } from './auth.controller'; import { AuditModule } from '../audit/audit.module'; import { JwtModule } from '@nestjs/jwt'; import { ConfigService } from '../config/config.service'; +import { ConfigModule } from '../config/config.module'; import * as dotenv from 'dotenv'; -import { RolesService } from 'src/roles/roles.service'; +import { RolesService } from '../roles/roles.service'; dotenv.config(); -const providers = [AuthService, JwtStrategy, KubernetesModule, AuditModule, RolesService]; +const providers: Provider[] = [AuthService, JwtStrategy, RolesService]; if (ConfigService.getOauth2Enabled()) { providers.push(Oauth2Strategy); } @@ -26,6 +27,9 @@ if (ConfigService.getGithubEnabled()) { imports: [ UsersModule, PassportModule, + AuditModule, + KubernetesModule, + ConfigModule, JwtModule.register({ secret: process.env.JWT_SECRET || diff --git a/server/src/auth/strategies/github.strategy.ts b/server/src/auth/strategies/github.strategy.ts index 7eb07dde1..4025c9db7 100644 --- a/server/src/auth/strategies/github.strategy.ts +++ b/server/src/auth/strategies/github.strategy.ts @@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { Injectable } from '@nestjs/common'; import { Request } from 'express'; import { AuthenticateOptions } from 'passport'; -import { ConfigService } from 'src/config/config.service'; +import { ConfigService } from '../../config/config.service'; import * as dotenv from 'dotenv'; dotenv.config(); diff --git a/server/src/auth/strategies/ws-jwt.guard.ts b/server/src/auth/strategies/ws-jwt.guard.ts index e013fb0d9..0ed6b7888 100644 --- a/server/src/auth/strategies/ws-jwt.guard.ts +++ b/server/src/auth/strategies/ws-jwt.guard.ts @@ -1,7 +1,7 @@ import { CanActivate, Injectable, Logger } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { Observable } from 'rxjs'; -import { UsersService } from 'src/users/users.service'; +import { UsersService } from '../../users/users.service'; @Injectable() export class WsJwtGuard implements CanActivate { diff --git a/server/src/config/config.module.ts b/server/src/config/config.module.ts index 2070daf8f..5b1c89079 100644 --- a/server/src/config/config.module.ts +++ b/server/src/config/config.module.ts @@ -2,11 +2,13 @@ import { Global, Module } from '@nestjs/common'; import { ConfigController } from './config.controller'; import { ConfigService } from './config.service'; import { KubernetesModule } from '../kubernetes/kubernetes.module'; +import { NotificationsModule } from '../notifications/notifications.module'; @Global() @Module({ + imports: [KubernetesModule, NotificationsModule], controllers: [ConfigController], - providers: [ConfigService, KubernetesModule], + providers: [ConfigService], exports: [ConfigService], }) export class ConfigModule {} diff --git a/server/src/events/events.gateway.ts b/server/src/events/events.gateway.ts index 984e7c389..c953470b7 100644 --- a/server/src/events/events.gateway.ts +++ b/server/src/events/events.gateway.ts @@ -8,7 +8,7 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { UseGuards } from '@nestjs/common'; -import { WsJwtGuard } from 'src/auth/strategies/ws-jwt.guard'; +import { WsJwtGuard } from '../auth/strategies/ws-jwt.guard'; @WebSocketGateway({ cors: { diff --git a/server/src/events/events.module.ts b/server/src/events/events.module.ts index 29151d00e..5a2c2887a 100644 --- a/server/src/events/events.module.ts +++ b/server/src/events/events.module.ts @@ -1,6 +1,6 @@ import { Module, Global } from '@nestjs/common'; import { EventsGateway } from './events.gateway'; -import { AuthModule } from 'src/auth/auth.module'; +import { AuthModule } from '../auth/auth.module'; @Global() @Module({