@@ -4,28 +4,57 @@ import express, { Application as ExpressApplication, RequestHandler } from 'expr
44import request from 'supertest' ;
55import jwt from 'jsonwebtoken' ;
66import { pausePromise } from '@cubejs-backend/shared' ;
7+ import { resetLogger } from '@cubejs-backend/native' ;
78
8- import { ApiGateway , ApiGatewayOptions , CubejsHandlerError , Request } from '../src' ;
9+ import { ApiGateway , ApiGatewayOptions , CubejsHandlerError , Request , RequestContext } from '../src' ;
910import { AdapterApiMock , DataSourceStorageMock } from './mocks' ;
10- import { RequestContext } from '../src/interfaces' ;
1111import { generateAuthToken } from './utils' ;
1212
13+ class ApiGatewayOpenAPI extends ApiGateway {
14+ protected isRunning : Promise < void > | null = null ;
15+
16+ public coerceForSqlQuery ( query , context : RequestContext ) {
17+ return super . coerceForSqlQuery ( query , context ) ;
18+ }
19+
20+ public async startSQLServer ( ) : Promise < void > {
21+ if ( this . isRunning ) {
22+ return this . isRunning ;
23+ }
24+
25+ this . isRunning = this . sqlServer . init ( { } ) ;
26+
27+ return this . isRunning ;
28+ }
29+
30+ public async shutdownSQLServer ( ) : Promise < void > {
31+ try {
32+ await this . sqlServer . shutdown ( 'fast' ) ;
33+ } finally {
34+ this . isRunning = null ;
35+ }
36+
37+ // SQLServer changes logger for rust side with setupLogger in the constructor, but it leads
38+ // to a memory leak, that's why jest doesn't allow to shut down tests
39+ resetLogger (
40+ process . env . CUBEJS_LOG_LEVEL === 'trace' ? 'trace' : 'warn'
41+ ) ;
42+ }
43+ }
44+
1345function createApiGateway ( handler : RequestHandler , logger : ( ) => any , options : Partial < ApiGatewayOptions > ) {
1446 const adapterApi : any = new AdapterApiMock ( ) ;
1547 const dataSourceStorage : any = new DataSourceStorageMock ( ) ;
1648
17- class ApiGatewayFake extends ApiGateway {
18- public coerceForSqlQuery ( query , context : RequestContext ) {
19- return super . coerceForSqlQuery ( query , context ) ;
20- }
21-
49+ class ApiGatewayFake extends ApiGatewayOpenAPI {
2250 public initApp ( app : ExpressApplication ) {
2351 const userMiddlewares : RequestHandler [ ] = [
2452 this . checkAuth ,
2553 this . requestContextMiddleware ,
2654 ] ;
2755
2856 app . get ( '/test-auth-fake' , userMiddlewares , handler ) ;
57+ this . enableNativeApiGateway ( app ) ;
2958
3059 app . use ( this . handleErrorMiddleware ) ;
3160 }
@@ -41,6 +70,7 @@ function createApiGateway(handler: RequestHandler, logger: () => any, options: P
4170 } ) ;
4271
4372 process . env . NODE_ENV = 'unknown' ;
73+
4474 const app = express ( ) ;
4575 apiGateway . initApp ( app ) ;
4676
@@ -50,6 +80,119 @@ function createApiGateway(handler: RequestHandler, logger: () => any, options: P
5080 } ;
5181}
5282
83+ describe ( 'test authorization with native gateway' , ( ) => {
84+ let app : ExpressApplication ;
85+ let apiGateway : ApiGatewayOpenAPI ;
86+
87+ const handlerMock = jest . fn ( ( ) => {
88+ // nothing, we are using it to verify that we don't got to express code
89+ } ) ;
90+ const loggerMock = jest . fn ( ( ) => {
91+ //
92+ } ) ;
93+ const checkAuthMock = jest . fn ( ( req , token ) => {
94+ jwt . verify ( token , 'secret' ) ;
95+
96+ return {
97+ security_context : { }
98+ } ;
99+ } ) ;
100+
101+ beforeAll ( async ( ) => {
102+ const result = createApiGateway ( handlerMock , loggerMock , {
103+ checkAuth : checkAuthMock ,
104+ gatewayPort : 8585 ,
105+ } ) ;
106+
107+ app = result . app ;
108+ apiGateway = result . apiGateway ;
109+
110+ await result . apiGateway . startSQLServer ( ) ;
111+ } ) ;
112+
113+ beforeEach ( ( ) => {
114+ handlerMock . mockClear ( ) ;
115+ loggerMock . mockClear ( ) ;
116+ checkAuthMock . mockClear ( ) ;
117+ } ) ;
118+
119+ afterAll ( async ( ) => {
120+ await apiGateway . shutdownSQLServer ( ) ;
121+ } ) ;
122+
123+ it ( 'default authorization - success' , async ( ) => {
124+ const token = generateAuthToken ( { uid : 5 , } ) ;
125+
126+ await request ( app )
127+ . get ( '/cubejs-api/v2/stream' )
128+ . set ( 'Authorization' , `${ token } ` )
129+ . send ( )
130+ . expect ( 501 ) ;
131+
132+ // No bad logs
133+ expect ( loggerMock . mock . calls . length ) . toEqual ( 0 ) ;
134+ // We should not call js handler, request should go into rust code
135+ expect ( handlerMock . mock . calls . length ) . toEqual ( 0 ) ;
136+
137+ // Verify that we passed token to JS side
138+ expect ( checkAuthMock . mock . calls . length ) . toEqual ( 1 ) ;
139+ expect ( checkAuthMock . mock . calls [ 0 ] [ 0 ] . protocol ) . toEqual ( 'http' ) ;
140+ expect ( checkAuthMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( token ) ;
141+ } ) ;
142+
143+ it ( 'default authorization - success (bearer prefix)' , async ( ) => {
144+ const token = generateAuthToken ( { uid : 5 , } ) ;
145+
146+ await request ( app )
147+ . get ( '/cubejs-api/v2/stream' )
148+ . set ( 'Authorization' , `Bearer ${ token } ` )
149+ . send ( )
150+ . expect ( 501 ) ;
151+
152+ // No bad logs
153+ expect ( loggerMock . mock . calls . length ) . toEqual ( 0 ) ;
154+ // We should not call js handler, request should go into rust code
155+ expect ( handlerMock . mock . calls . length ) . toEqual ( 0 ) ;
156+
157+ // Verify that we passed token to JS side
158+ expect ( checkAuthMock . mock . calls . length ) . toEqual ( 1 ) ;
159+ expect ( checkAuthMock . mock . calls [ 0 ] [ 0 ] . protocol ) . toEqual ( 'http' ) ;
160+ expect ( checkAuthMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( token ) ;
161+ } ) ;
162+
163+ it ( 'default authorization - wrong secret' , async ( ) => {
164+ const badToken = 'SUPER_LARGE_BAD_TOKEN_WHICH_IS_NOT_A_TOKEN' ;
165+
166+ await request ( app )
167+ . get ( '/cubejs-api/v2/stream' )
168+ . set ( 'Authorization' , `${ badToken } ` )
169+ . send ( )
170+ . expect ( 401 ) ;
171+
172+ // No bad logs
173+ expect ( loggerMock . mock . calls . length ) . toEqual ( 0 ) ;
174+ // We should not call js handler, request should go into rust code
175+ expect ( handlerMock . mock . calls . length ) . toEqual ( 0 ) ;
176+
177+ // Verify that we passed token to JS side
178+ expect ( checkAuthMock . mock . calls . length ) . toEqual ( 1 ) ;
179+ expect ( checkAuthMock . mock . calls [ 0 ] [ 0 ] . protocol ) . toEqual ( 'http' ) ;
180+ expect ( checkAuthMock . mock . calls [ 0 ] [ 1 ] ) . toEqual ( badToken ) ;
181+ } ) ;
182+
183+ it ( 'default authorization - missing auth header' , async ( ) => {
184+ await request ( app )
185+ . get ( '/cubejs-api/v2/stream' )
186+ . send ( )
187+ . expect ( 401 ) ;
188+
189+ // No bad logs
190+ expect ( loggerMock . mock . calls . length ) . toEqual ( 0 ) ;
191+ // We should not call js handler, request should go into rust code
192+ expect ( handlerMock . mock . calls . length ) . toEqual ( 0 ) ;
193+ } ) ;
194+ } ) ;
195+
53196describe ( 'test authorization' , ( ) => {
54197 test ( 'default authorization' , async ( ) => {
55198 const loggerMock = jest . fn ( ( ) => {
0 commit comments