From c2dd3410c574c8001ee82f7a300af1c80e073e12 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 12 Apr 2023 10:20:30 +0530 Subject: [PATCH] fix: socket with passport middleware Signed-off-by: Pranav C --- packages/nocodb-nest/src/app.module.ts | 4 +- packages/nocodb-nest/src/db/BaseModelSqlv2.ts | 3 +- .../nocodb-nest/src/helpers/populateMeta.ts | 2 +- .../src/modules/projects/projects.service.ts | 4 +- .../nocodb-nest/src/modules/users/helpers.ts | 2 +- .../src/modules/users/users.service.ts | 3 - .../src/modules/utils/utils.controller.ts | 11 ++- .../services/client/client.service.spec.ts | 18 ++++ .../src/services/client/client.service.ts | 84 +++++++++++++++++++ .../src/strategies/jwt.strategy.ts | 25 ++---- .../src/strategies/local.strategy.ts | 2 +- 11 files changed, 127 insertions(+), 31 deletions(-) create mode 100644 packages/nocodb-nest/src/services/client/client.service.spec.ts create mode 100644 packages/nocodb-nest/src/services/client/client.service.ts diff --git a/packages/nocodb-nest/src/app.module.ts b/packages/nocodb-nest/src/app.module.ts index 3d6bbbff1b..16801f7ad7 100644 --- a/packages/nocodb-nest/src/app.module.ts +++ b/packages/nocodb-nest/src/app.module.ts @@ -59,6 +59,7 @@ import type { OnApplicationBootstrap, Provider, } from '@nestjs/common'; +import { ClientService } from './services/client/client.service'; export const JwtStrategyProvider: Provider = { provide: JwtStrategy, @@ -133,6 +134,7 @@ export const JwtStrategyProvider: Provider = { JwtStrategyProvider, LocalStrategy, ExtractProjectIdMiddleware, + ClientService, ], }) export class AppModule implements OnApplicationBootstrap { @@ -159,6 +161,6 @@ export class AppModule implements OnApplicationBootstrap { Noco.config = this.connection.config; // run upgrader - await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); + // await NcUpgrader.upgrade({ ncMeta: Noco._ncMeta }); } } diff --git a/packages/nocodb-nest/src/db/BaseModelSqlv2.ts b/packages/nocodb-nest/src/db/BaseModelSqlv2.ts index 373c6442a5..4cf5118874 100644 --- a/packages/nocodb-nest/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb-nest/src/db/BaseModelSqlv2.ts @@ -50,8 +50,6 @@ import genRollupSelectv2 from './genRollupSelectv2'; import conditionV2 from './conditionV2'; import sortV2 from './sortV2'; import { customValidators } from './util/customValidators'; -import type LookupColumn from '../../../nocodb/src/lib/models/LookupColumn'; -import type { XKnex } from '../../../nocodb/src/lib/db/sql-data-mapper'; import type { XcFilter, XcFilterWithAlias, @@ -67,6 +65,7 @@ import type { } from '../models'; import type { Knex } from 'knex'; import type { SortType } from 'nocodb-sdk'; +import { XKnex } from './CustomKnex'; export async function getViewAndModelByAliasOrId(param: { projectName: string; diff --git a/packages/nocodb-nest/src/helpers/populateMeta.ts b/packages/nocodb-nest/src/helpers/populateMeta.ts index 384acfa647..1ce90708e3 100644 --- a/packages/nocodb-nest/src/helpers/populateMeta.ts +++ b/packages/nocodb-nest/src/helpers/populateMeta.ts @@ -1,5 +1,5 @@ import { ModelTypes, UITypes, ViewTypes } from 'nocodb-sdk'; -import { isVirtualCol, RelationTypes } from '../../../nocodb-sdk'; +import { isVirtualCol, RelationTypes } from 'nocodb-sdk'; import Column from '../models/Column'; import Model from '../models/Model'; import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; diff --git a/packages/nocodb-nest/src/modules/projects/projects.service.ts b/packages/nocodb-nest/src/modules/projects/projects.service.ts index f16fc5a208..5c57307a9a 100644 --- a/packages/nocodb-nest/src/modules/projects/projects.service.ts +++ b/packages/nocodb-nest/src/modules/projects/projects.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common'; import * as DOMPurify from 'isomorphic-dompurify'; import { customAlphabet } from 'nanoid'; import { T } from 'nc-help'; -import { OrgUserRoles } from '../../../../nocodb-sdk'; +import { OrgUserRoles } from 'nocodb-sdk'; import { populateMeta, validatePayload } from '../../helpers'; import { NcError } from '../../helpers/catchError'; import { extractPropsAndSanitize } from '../../helpers/extractProps'; @@ -12,7 +12,7 @@ import { Project, ProjectUser } from '../../models'; import Noco from '../../Noco'; import extractRolesObj from '../../utils/extractRolesObj'; import NcConfigFactory from '../../utils/NcConfigFactory'; -import type { ProjectUpdateReqType } from '../../../../nocodb-sdk'; +import type { ProjectUpdateReqType } from 'nocodb-sdk'; import type { ProjectReqType } from 'nocodb-sdk'; const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4); diff --git a/packages/nocodb-nest/src/modules/users/helpers.ts b/packages/nocodb-nest/src/modules/users/helpers.ts index 05f99cf2e7..3eab60c781 100644 --- a/packages/nocodb-nest/src/modules/users/helpers.ts +++ b/packages/nocodb-nest/src/modules/users/helpers.ts @@ -16,7 +16,7 @@ export function genJwt(user: User, config: NcConfig) { }, config.auth.jwt.secret, // todo: better typing - config.auth.jwt.options as any, + { expiresIn: '10h', ...(config.auth.jwt.options as any) }, ); } diff --git a/packages/nocodb-nest/src/modules/users/users.service.ts b/packages/nocodb-nest/src/modules/users/users.service.ts index cde89810b4..05d5934593 100644 --- a/packages/nocodb-nest/src/modules/users/users.service.ts +++ b/packages/nocodb-nest/src/modules/users/users.service.ts @@ -482,9 +482,6 @@ export class UsersService { } async login(user: any) { - delete user.password; - delete user.salt; - const payload = user; return { token: genJwt(user, Noco.getConfig()), //this.jwtService.sign(payload), }; diff --git a/packages/nocodb-nest/src/modules/utils/utils.controller.ts b/packages/nocodb-nest/src/modules/utils/utils.controller.ts index 76fa348988..40281b4667 100644 --- a/packages/nocodb-nest/src/modules/utils/utils.controller.ts +++ b/packages/nocodb-nest/src/modules/utils/utils.controller.ts @@ -53,10 +53,6 @@ export class UtilsController { async axiosRequestMake(@Body() body: any) { return await this.utilsService.axiosRequestMake({ body }); } - @Get('/api/v1/aggregated-meta-info') - async aggregatedMetaInfo() { - return await this.utilsService.aggregatedMetaInfo(); - } @Post('/api/v1/url_to_config') async urlToDbConfig(@Body() body: any) { @@ -64,4 +60,11 @@ export class UtilsController { body, }); } + + @Get('/api/v1/aggregated-meta-info') + async aggregatedMetaInfo() { + // todo: refactor + return (await this.utilsService.aggregatedMetaInfo()) as any; + } + } diff --git a/packages/nocodb-nest/src/services/client/client.service.spec.ts b/packages/nocodb-nest/src/services/client/client.service.spec.ts new file mode 100644 index 0000000000..b9218cfeee --- /dev/null +++ b/packages/nocodb-nest/src/services/client/client.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ClientService } from './client.service'; + +describe('ClientService', () => { + let service: ClientService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ClientService], + }).compile(); + + service = module.get(ClientService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/services/client/client.service.ts b/packages/nocodb-nest/src/services/client/client.service.ts new file mode 100644 index 0000000000..dc8fcd9546 --- /dev/null +++ b/packages/nocodb-nest/src/services/client/client.service.ts @@ -0,0 +1,84 @@ +import crypto from 'crypto'; +import { Inject, Injectable, UnauthorizedException } from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; +import { T } from 'nc-help'; +import { Server } from 'socket.io'; +import { AuthGuard } from '@nestjs/passport'; +import { JwtStrategy } from '../../strategies/jwt.strategy'; +import type { OnModuleInit } from '@nestjs/common'; +import type { Socket } from 'socket.io'; +import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; + +function getHash(str) { + return crypto.createHash('md5').update(str).digest('hex'); +} + +@Injectable() +export class ClientService implements OnModuleInit { + // private server: HttpServer; + private clients: { [id: string]: Socket } = {}; + private jobs: { [id: string]: { last_message: any } } = {}; + + constructor( + private jwtStrategy: JwtStrategy, + @Inject(HttpAdapterHost) private httpAdapterHost: HttpAdapterHost, + ) { + // this.server = this.httpAdapterHost.httpAdapter.getHttpServer(); + } + + async onModuleInit() { + const io = new Server(this.httpAdapterHost.httpAdapter.getHttpServer(), { + cors: { + origin: '*', + allowedHeaders: ['xc-auth'], + credentials: true, + }, + }); + io.use(async (socket, next) => { + // const authGuard = new (AuthGuard('jwt'))(); + // const result = await authGuard.canActivate(socket.handshake as any); + // if (!result) { + // throw new UnauthorizedException(); + // } + // return new Promise((resolve, reject) => { + // this.jwtStrategy.authenticate( + // socket.handshake as any, + // (error, user) => { + // if (error) { + // reject(new UnauthorizedException(error.message)); + // } else { + // resolve(user); + // } + // }, + // ); + // }); + try { + const context = new ExecutionContextHost([socket.handshake as any]); + const guard = new (AuthGuard('jwt'))(context); + const canActivate = await guard.canActivate(context); + } catch {} + + next() + }).on('connection', (socket) => { + this.clients[socket.id] = socket; + const id = getHash( + (process.env.NC_SERVER_UUID || T.id) + + (socket?.handshake as any)?.user?.id, + ); + + socket.on('page', (args) => { + T.page({ ...args, id }); + }); + socket.on('event', (args) => { + T.event({ ...args, id }); + }); + socket.on('subscribe', (room) => { + if (room in this.jobs) { + socket.join(room); + socket.emit('job'); + socket.emit('progress', this.jobs[room].last_message); + } + }); + }); + } +} diff --git a/packages/nocodb-nest/src/strategies/jwt.strategy.ts b/packages/nocodb-nest/src/strategies/jwt.strategy.ts index 679c3e7577..5645012dc3 100644 --- a/packages/nocodb-nest/src/strategies/jwt.strategy.ts +++ b/packages/nocodb-nest/src/strategies/jwt.strategy.ts @@ -1,31 +1,24 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; -import { OrgUserRoles } from '../../../nocodb-sdk'; +import { OrgUserRoles } from 'nocodb-sdk'; import NocoCache from '../cache/NocoCache'; import { ProjectUser, User } from '../models'; -import { genJwt } from '../modules/users/helpers' -import Noco from '../Noco' +import { genJwt } from '../modules/users/helpers'; +import Noco from '../Noco'; import extractRolesObj from '../utils/extractRolesObj'; import { CacheGetType, CacheScope } from '../utils/globals'; import { jwtConstants } from '../modules/auth/constants'; import { UsersService } from '../modules/users/users.service'; -import NcConfigFactory from '../utils/NcConfigFactory' +import NcConfigFactory from '../utils/NcConfigFactory'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { - constructor(options, private userService: UsersService, ) { - super( - options - ) - // { - // // ignoreExpiration: false, - // jwtFromRequest: ExtractJwt.fromHeader('xc-auth'), - // expiresIn: '10h', - // passReqToCallback: true, - // secretOrKey: process.env.NC_AUTH_JWT_SECRET ?? 'temporary-key' - // - // }); + constructor(options, private userService: UsersService) { + super({ + expiresIn: '10h', + ...options, + }); } async validate(req: any, jwtPayload: any) { diff --git a/packages/nocodb-nest/src/strategies/local.strategy.ts b/packages/nocodb-nest/src/strategies/local.strategy.ts index 2b61aadcb4..564ad6b9fc 100644 --- a/packages/nocodb-nest/src/strategies/local.strategy.ts +++ b/packages/nocodb-nest/src/strategies/local.strategy.ts @@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { Injectable } from '@nestjs/common'; import { AuthService } from '../modules/auth/auth.service'; import extractRolesObj from '../utils/extractRolesObj'; -import { NcError } from '../../../nocodb/src/lib/meta/helpers/catchError'; +import { NcError } from '../helpers/catchError'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) {