From 95021f277967ad88e783f766b2e546e3e6ebebf2 Mon Sep 17 00:00:00 2001 From: Mikhail Cheshkov Date: Wed, 18 Dec 2024 19:08:11 +0200 Subject: [PATCH] fix(backend-native): Pass req.securityContext to Python config (#9049) This is necessary for `extend_context`. By the time `extendContext` is called, `req` in JS-land is already extended with `securityContext` after `checkAuth`, and JS config receives it with extensions. --- packages/cubejs-backend-native/js/index.ts | 6 +++++ packages/cubejs-backend-native/test/config.py | 17 ++++++++++++ .../cubejs-backend-native/test/python.test.ts | 26 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/packages/cubejs-backend-native/js/index.ts b/packages/cubejs-backend-native/js/index.ts index f484c2d6080b0..60d103b078779 100644 --- a/packages/cubejs-backend-native/js/index.ts +++ b/packages/cubejs-backend-native/js/index.ts @@ -352,6 +352,7 @@ export interface PyConfiguration { repositoryFactory?: (ctx: unknown) => Promise, logger?: (msg: string, params: Record) => void, checkAuth?: (req: unknown, authorization: string) => Promise<{ 'security_context'?: unknown }> + extendContext?: (req: unknown) => Promise queryRewrite?: (query: unknown, ctx: unknown) => Promise contextToApiScopes?: () => Promise contextToRoles?: (ctx: unknown) => Promise @@ -365,6 +366,11 @@ function simplifyExpressRequest(req: ExpressRequest) { method: req.method, headers: req.headers, ip: req.ip, + + // req.securityContext is an extension of request done by api-gateway + // But its typings currently live in api-gateway package, which has native-backend (this package) as it's dependency + // TODO extract typings to separate package and drop as any + ...(Object.hasOwn(req, 'securityContext') ? { securityContext: (req as any).securityContext } : {}), }; } diff --git a/packages/cubejs-backend-native/test/config.py b/packages/cubejs-backend-native/test/config.py index d5f17fed9173f..302ce693c133f 100644 --- a/packages/cubejs-backend-native/test/config.py +++ b/packages/cubejs-backend-native/test/config.py @@ -20,6 +20,23 @@ async def check_auth(req, authorization): } +@config('extend_context') +def extend_context(req): + print("[python] extend_context req=", req) + if "securityContext" not in req: + return { + "security_context": { + "error": "missing", + } + } + + req["securityContext"]["extended_by_config"] = True + + return { + "security_context": req["securityContext"], + } + + @config async def repository_factory(ctx): print("[python] repository_factory ctx=", ctx) diff --git a/packages/cubejs-backend-native/test/python.test.ts b/packages/cubejs-backend-native/test/python.test.ts index d2fa11de9e363..af78e48500e8c 100644 --- a/packages/cubejs-backend-native/test/python.test.ts +++ b/packages/cubejs-backend-native/test/python.test.ts @@ -43,6 +43,7 @@ suite('Python Config', () => { pgSqlPort: 5555, preAggregationsSchema: expect.any(Function), checkAuth: expect.any(Function), + extendContext: expect.any(Function), queryRewrite: expect.any(Function), repositoryFactory: expect.any(Function), schemaVersion: expect.any(Function), @@ -83,6 +84,31 @@ suite('Python Config', () => { expect(await config.contextToApiScopes()).toEqual(['meta', 'data', 'jobs']); }); + test('extend_context', async () => { + if (!config.extendContext) { + throw new Error('extendContext was not defined in config.py'); + } + + // Without security context + expect(await config.extendContext({})).toEqual({ + security_context: { + error: 'missing', + }, + }); + + // With security context + expect(await config.extendContext({ + securityContext: { sub: '1234567890', iat: 1516239022, user_id: 42 } + })).toEqual({ + security_context: { + extended_by_config: true, + sub: '1234567890', + iat: 1516239022, + user_id: 42 + }, + }); + }); + test('repository factory', async () => { if (!config.repositoryFactory) { throw new Error('repositoryFactory was not defined in config.py');