Skip to content

Commit 55c5a95

Browse files
feat(api-gateway)!: Move '/v1/sql' to new sql API Scope (#9406)
* feat(api-gateway)!: Move '/v1/sql' to new `sql` API Scope * fix tests * Update docs * Fix docs --------- Co-authored-by: Igor Lukanin <igor@cube.dev>
1 parent 74768e2 commit 55c5a95

File tree

7 files changed

+45
-29
lines changed

7 files changed

+45
-29
lines changed

docs/pages/product/apis-integrations/rest-api.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,13 @@ by making them accessible to specific users only or disallowing access for
124124
everyone. By default, API endpoints in all scopes, except for `jobs`, are
125125
accessible for everyone.
126126

127-
| API scope | REST API endpoints | Accessible by default? |
128-
| --------- | ----------------------------------------------------------------------------------------- | ---------------------- |
129-
| `meta` | [`/v1/meta`][ref-ref-meta] | ✅ Yes |
130-
| `data` | [`/v1/load`][ref-ref-load], [`/v1/sql`][ref-ref-sql] | ✅ Yes |
131-
| `graphql` | `/graphql` | ✅ Yes |
132-
| `jobs` | [`/v1/pre-aggregations/jobs`][ref-ref-paj] | ❌ No |
133-
| No scope | `/livez`, `/readyz` | ✅ Yes, always |
127+
| API scope | REST API endpoints | Accessible by default? |
128+
| --- | --- | --- |
129+
| `meta` | [`/v1/meta`][ref-ref-meta] | ✅ Yes |
130+
| `data` | [`/v1/load`][ref-ref-load] | ✅ Yes |
131+
| `graphql` | `/graphql` | ✅ Yes |
132+
| `jobs` | [`/v1/pre-aggregations/jobs`][ref-ref-paj] | ❌ No |
133+
| No scope | `/livez`, `/readyz` | ✅ Yes |
134134

135135
You can set accessible API scopes _for all requests_ using the
136136
`CUBEJS_DEFAULT_API_SCOPES` environment variable. For example, to disallow

docs/pages/reference/configuration/config.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,13 +1008,13 @@ from cube import config
10081008

10091009
@config('context_to_api_scopes')
10101010
def context_to_api_scopes(context: dict, default_scopes: list[str]) -> list[str]:
1011-
return ['meta', 'data', 'graphql']
1011+
return ['meta', 'data', 'graphql', 'sql']
10121012
```
10131013

10141014
```javascript
10151015
module.exports = {
10161016
contextToApiScopes: (securityContext, defaultScopes) => {
1017-
return ['meta', 'data', 'graphql'];
1017+
return ['meta', 'data', 'graphql', 'sql'];
10181018
},
10191019
};
10201020
```

docs/pages/reference/configuration/environment-variables.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ endpoints.
860860

861861
| Possible Values | Default in Development | Default in Production |
862862
| ------------------------------------------------------------------------------ | ---------------------- | --------------------- |
863-
| A comma-delimited string with any combination of [API scopes][ref-rest-scopes] | `meta,data,graphql` | `meta,data,graphql` |
863+
| A comma-delimited string with any combination of [API scopes][ref-rest-scopes] | `meta,data,graphql,sql`| `meta,data,graphql,sql`|
864864

865865
See also the [`context_to_api_scopes` configuration
866866
option](/reference/configuration/config#context_to_api_scopes).

packages/cubejs-api-gateway/src/gateway.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ class ApiGateway {
155155
public readonly contextToApiScopesFn: ContextToApiScopesFn;
156156

157157
public readonly contextToApiScopesDefFn: ContextToApiScopesFn =
158-
async () => ['graphql', 'meta', 'data'];
158+
async () => ['graphql', 'meta', 'data', 'sql'];
159159

160160
protected readonly requestLoggerMiddleware: RequestLoggerMiddlewareFn;
161161

@@ -1311,7 +1311,7 @@ class ApiGateway {
13111311
res,
13121312
}: {query: string, disablePostProcessing: boolean} & BaseRequest) {
13131313
try {
1314-
await this.assertApiScope('data', context.securityContext);
1314+
await this.assertApiScope('sql', context.securityContext);
13151315

13161316
const result = await this.sqlServer.sql4sql(query, disablePostProcessing, context.securityContext);
13171317
res({ sql: result });
@@ -1339,7 +1339,7 @@ class ApiGateway {
13391339
const requestStarted = new Date();
13401340

13411341
try {
1342-
await this.assertApiScope('data', context.securityContext);
1342+
await this.assertApiScope('sql', context.securityContext);
13431343

13441344
const [queryType, normalizedQueries] =
13451345
await this.getNormalizedQueries(query, context, disableLimitEnforcing, memberExpressions);
@@ -2448,7 +2448,7 @@ class ApiGateway {
24482448
);
24492449
} else {
24502450
scopes.forEach((p) => {
2451-
if (['graphql', 'meta', 'data', 'jobs'].indexOf(p) === -1) {
2451+
if (['graphql', 'meta', 'data', 'sql', 'jobs'].indexOf(p) === -1) {
24522452
throw new Error(
24532453
`A user-defined contextToApiScopes function returns a wrong scope: ${p}`
24542454
);

packages/cubejs-api-gateway/src/types/strings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ type ApiScopes =
107107
'graphql' |
108108
'meta' |
109109
'data' |
110+
'sql' |
110111
'jobs';
111112

112113
export {

packages/cubejs-api-gateway/test/permissions.test.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ describe('Gateway Api Scopes', () => {
6262
expect(res.body && res.body.error)
6363
.toStrictEqual('API scope is missing: data');
6464

65+
res = await request(app)
66+
.get('/cubejs-api/v1/sql')
67+
.set('Authorization', AUTH_TOKEN)
68+
.expect(403);
69+
expect(res.body && res.body.error)
70+
.toStrictEqual('API scope is missing: sql');
71+
6572
res = await request(app)
6673
.post('/cubejs-api/v1/pre-aggregations/jobs')
6774
.set('Authorization', AUTH_TOKEN)
@@ -175,39 +182,47 @@ describe('Gateway Api Scopes', () => {
175182
expect(res3.body && res3.body.error)
176183
.toStrictEqual('API scope is missing: data');
177184

178-
const res4 = await request(app)
179-
.get('/cubejs-api/v1/sql')
185+
const res6 = await request(app)
186+
.get('/cubejs-api/v1/dry-run')
180187
.set('Authorization', AUTH_TOKEN)
181188
.expect(403);
182189

183-
expect(res4.body && res4.body.error)
190+
expect(res6.body && res6.body.error)
184191
.toStrictEqual('API scope is missing: data');
185192

186-
const res5 = await request(app)
187-
.post('/cubejs-api/v1/sql')
193+
const res7 = await request(app)
194+
.post('/cubejs-api/v1/dry-run')
188195
.set('Content-type', 'application/json')
189196
.set('Authorization', AUTH_TOKEN)
190197
.expect(403);
191198

192-
expect(res5.body && res5.body.error)
199+
expect(res7.body && res7.body.error)
193200
.toStrictEqual('API scope is missing: data');
194201

195-
const res6 = await request(app)
196-
.get('/cubejs-api/v1/dry-run')
202+
apiGateway.release();
203+
});
204+
205+
test('Sql declined', async () => {
206+
const { app, apiGateway } = createApiGateway({
207+
contextToApiScopes: async () => ['graphql', 'meta', 'jobs', 'data'],
208+
});
209+
210+
const res1 = await request(app)
211+
.get('/cubejs-api/v1/sql')
197212
.set('Authorization', AUTH_TOKEN)
198213
.expect(403);
199214

200-
expect(res6.body && res6.body.error)
201-
.toStrictEqual('API scope is missing: data');
215+
expect(res1.body && res1.body.error)
216+
.toStrictEqual('API scope is missing: sql');
202217

203-
const res7 = await request(app)
204-
.post('/cubejs-api/v1/dry-run')
218+
const res2 = await request(app)
219+
.post('/cubejs-api/v1/sql')
205220
.set('Content-type', 'application/json')
206221
.set('Authorization', AUTH_TOKEN)
207222
.expect(403);
208223

209-
expect(res7.body && res7.body.error)
210-
.toStrictEqual('API scope is missing: data');
224+
expect(res2.body && res2.body.error)
225+
.toStrictEqual('API scope is missing: sql');
211226

212227
apiGateway.release();
213228
});

packages/cubejs-server-core/test/unit/OptsHandler.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ describe('OptsHandler class', () => {
11401140
const permissions = await gateway.contextToApiScopesFn();
11411141
expect(permissions).toBeDefined();
11421142
expect(Array.isArray(permissions)).toBeTruthy();
1143-
expect(permissions).toEqual(['graphql', 'meta', 'data']);
1143+
expect(permissions).toEqual(['graphql', 'meta', 'data', 'sql']);
11441144
});
11451145

11461146
test('must set env api scopes if fn not specified', async () => {

0 commit comments

Comments
 (0)