@teleporthq/teleport-plugin-next-data-source 0.43.75 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/_helpers/first-page-runtime.ts +184 -0
- package/__tests__/_helpers/pagination-widget-fixture.ts +154 -5
- package/__tests__/booking-item-transform.test.ts +120 -0
- package/__tests__/browser-row-policy.test.ts +210 -4
- package/__tests__/data-source-request-locale.test.ts +4 -3
- package/__tests__/fetchers.test.ts +7 -3
- package/__tests__/fixed-list-sort.test.ts +1 -1
- package/__tests__/infinite-scroll.test.ts +363 -9
- package/__tests__/list-sort-order.test.ts +61 -0
- package/__tests__/null-filter-where.test.ts +132 -0
- package/__tests__/page-data-visitor-read.test.ts +196 -0
- package/__tests__/pg-date-parser.test.ts +73 -0
- package/__tests__/plain-list-sort.test.ts +2 -2
- package/__tests__/plain-list-starting-sort.test.ts +323 -0
- package/__tests__/product-price-sort.ts +11 -6
- package/__tests__/range-control.test.ts +159 -0
- package/__tests__/refetch-loading-state.test.ts +158 -2
- package/__tests__/search-input-marker.test.ts +86 -0
- package/__tests__/search-url-sync.test.ts +34 -8
- package/__tests__/starting-sort-first-page.test.ts +285 -0
- package/__tests__/table-read-route-protected-tables.test.ts +33 -3
- package/dist/cjs/cache/ast.d.ts +2 -0
- package/dist/cjs/cache/ast.d.ts.map +1 -1
- package/dist/cjs/cache/ast.js +5 -2
- package/dist/cjs/cache/ast.js.map +1 -1
- package/dist/cjs/count-effect.d.ts +7 -1
- package/dist/cjs/count-effect.d.ts.map +1 -1
- package/dist/cjs/count-effect.js +15 -5
- package/dist/cjs/count-effect.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +10 -2
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/pg-client-import.d.ts +12 -0
- package/dist/cjs/fetchers/pg-client-import.d.ts.map +1 -0
- package/dist/cjs/fetchers/pg-client-import.js +15 -0
- package/dist/cjs/fetchers/pg-client-import.js.map +1 -0
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +3 -1
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +2 -1
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +4 -2
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.d.ts.map +1 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.js +5 -1
- package/dist/cjs/fetchers/utils/browser-row-policy.js.map +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/infinite-scroll.d.ts +89 -4
- package/dist/cjs/infinite-scroll.d.ts.map +1 -1
- package/dist/cjs/infinite-scroll.js +243 -33
- package/dist/cjs/infinite-scroll.js.map +1 -1
- package/dist/cjs/list-sort-order.d.ts +14 -0
- package/dist/cjs/list-sort-order.d.ts.map +1 -0
- package/dist/cjs/list-sort-order.js +36 -0
- package/dist/cjs/list-sort-order.js.map +1 -0
- package/dist/cjs/loading-state.d.ts +23 -2
- package/dist/cjs/loading-state.d.ts.map +1 -1
- package/dist/cjs/loading-state.js +50 -15
- package/dist/cjs/loading-state.js.map +1 -1
- package/dist/cjs/pagination-controls.d.ts +22 -3
- package/dist/cjs/pagination-controls.d.ts.map +1 -1
- package/dist/cjs/pagination-controls.js +29 -3
- package/dist/cjs/pagination-controls.js.map +1 -1
- package/dist/cjs/pagination-plugin.d.ts.map +1 -1
- package/dist/cjs/pagination-plugin.js +336 -164
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/range-control.d.ts +45 -0
- package/dist/cjs/range-control.d.ts.map +1 -0
- package/dist/cjs/range-control.js +165 -0
- package/dist/cjs/range-control.js.map +1 -0
- package/dist/cjs/row-order.d.ts +3 -2
- package/dist/cjs/row-order.d.ts.map +1 -1
- package/dist/cjs/row-order.js +5 -4
- package/dist/cjs/row-order.js.map +1 -1
- package/dist/cjs/search-input.d.ts +42 -0
- package/dist/cjs/search-input.d.ts.map +1 -0
- package/dist/cjs/search-input.js +158 -0
- package/dist/cjs/search-input.js.map +1 -0
- package/dist/cjs/sort-utils.d.ts +53 -1
- package/dist/cjs/sort-utils.d.ts.map +1 -1
- package/dist/cjs/sort-utils.js +105 -13
- package/dist/cjs/sort-utils.js.map +1 -1
- package/dist/cjs/transformations/booking-item.d.ts +24 -0
- package/dist/cjs/transformations/booking-item.d.ts.map +1 -0
- package/dist/cjs/transformations/booking-item.js +27 -0
- package/dist/cjs/transformations/booking-item.js.map +1 -0
- package/dist/cjs/transformations/index.d.ts +4 -2
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +14 -5
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +3 -2
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +48 -18
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/cache/ast.d.ts +2 -0
- package/dist/esm/cache/ast.d.ts.map +1 -1
- package/dist/esm/cache/ast.js +3 -1
- package/dist/esm/cache/ast.js.map +1 -1
- package/dist/esm/count-effect.d.ts +7 -1
- package/dist/esm/count-effect.d.ts.map +1 -1
- package/dist/esm/count-effect.js +15 -5
- package/dist/esm/count-effect.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +10 -2
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/pg-client-import.d.ts +12 -0
- package/dist/esm/fetchers/pg-client-import.d.ts.map +1 -0
- package/dist/esm/fetchers/pg-client-import.js +12 -0
- package/dist/esm/fetchers/pg-client-import.js.map +1 -0
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +3 -1
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +2 -1
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +4 -2
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.d.ts.map +1 -1
- package/dist/esm/fetchers/utils/browser-row-policy.js +6 -2
- package/dist/esm/fetchers/utils/browser-row-policy.js.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +14 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/infinite-scroll.d.ts +89 -4
- package/dist/esm/infinite-scroll.d.ts.map +1 -1
- package/dist/esm/infinite-scroll.js +237 -32
- package/dist/esm/infinite-scroll.js.map +1 -1
- package/dist/esm/list-sort-order.d.ts +14 -0
- package/dist/esm/list-sort-order.d.ts.map +1 -0
- package/dist/esm/list-sort-order.js +32 -0
- package/dist/esm/list-sort-order.js.map +1 -0
- package/dist/esm/loading-state.d.ts +23 -2
- package/dist/esm/loading-state.d.ts.map +1 -1
- package/dist/esm/loading-state.js +50 -15
- package/dist/esm/loading-state.js.map +1 -1
- package/dist/esm/pagination-controls.d.ts +22 -3
- package/dist/esm/pagination-controls.d.ts.map +1 -1
- package/dist/esm/pagination-controls.js +28 -2
- package/dist/esm/pagination-controls.js.map +1 -1
- package/dist/esm/pagination-plugin.d.ts.map +1 -1
- package/dist/esm/pagination-plugin.js +340 -168
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/range-control.d.ts +45 -0
- package/dist/esm/range-control.d.ts.map +1 -0
- package/dist/esm/range-control.js +137 -0
- package/dist/esm/range-control.js.map +1 -0
- package/dist/esm/row-order.d.ts +3 -2
- package/dist/esm/row-order.d.ts.map +1 -1
- package/dist/esm/row-order.js +5 -4
- package/dist/esm/row-order.js.map +1 -1
- package/dist/esm/search-input.d.ts +42 -0
- package/dist/esm/search-input.d.ts.map +1 -0
- package/dist/esm/search-input.js +129 -0
- package/dist/esm/search-input.js.map +1 -0
- package/dist/esm/sort-utils.d.ts +53 -1
- package/dist/esm/sort-utils.d.ts.map +1 -1
- package/dist/esm/sort-utils.js +100 -12
- package/dist/esm/sort-utils.js.map +1 -1
- package/dist/esm/transformations/booking-item.d.ts +24 -0
- package/dist/esm/transformations/booking-item.d.ts.map +1 -0
- package/dist/esm/transformations/booking-item.js +23 -0
- package/dist/esm/transformations/booking-item.js.map +1 -0
- package/dist/esm/transformations/index.d.ts +4 -2
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +14 -5
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +3 -2
- package/dist/esm/utils.d.ts.map +1 -1
- package/dist/esm/utils.js +49 -19
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/cache/ast.ts +4 -1
- package/src/count-effect.ts +31 -8
- package/src/data-source-fetchers.ts +33 -14
- package/src/fetchers/pg-client-import.ts +16 -0
- package/src/fetchers/postgresql.ts +15 -9
- package/src/fetchers/raw-query.ts +2 -1
- package/src/fetchers/teleport.ts +19 -10
- package/src/fetchers/utils/browser-row-policy.ts +79 -5
- package/src/index.ts +8 -2
- package/src/infinite-scroll.ts +420 -81
- package/src/list-sort-order.ts +34 -0
- package/src/loading-state.ts +85 -13
- package/src/pagination-controls.ts +26 -3
- package/src/pagination-plugin.ts +532 -218
- package/src/range-control.ts +207 -0
- package/src/row-order.ts +5 -4
- package/src/search-input.ts +190 -0
- package/src/sort-utils.ts +164 -21
- package/src/transformations/booking-item.ts +71 -0
- package/src/transformations/index.ts +25 -2
- package/src/utils.ts +55 -18
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionCookieResolver } from '@teleporthq/teleport-shared'
|
|
2
2
|
import { replaceSecretReference } from '../utils'
|
|
3
|
+
import { PG_CLIENT_IMPORT } from './pg-client-import'
|
|
3
4
|
|
|
4
5
|
interface PostgreSQLConfig {
|
|
5
6
|
host?: string
|
|
@@ -109,7 +110,7 @@ function sessionUserField(user, field) {
|
|
|
109
110
|
: '{ rejectUnauthorized: false }'
|
|
110
111
|
: 'false'
|
|
111
112
|
|
|
112
|
-
return
|
|
113
|
+
return `${PG_CLIENT_IMPORT}
|
|
113
114
|
|
|
114
115
|
const FORBIDDEN_KEYWORDS = ${JSON.stringify(FORBIDDEN_KEYWORDS)}
|
|
115
116
|
${sessionHelpers}
|
package/src/fetchers/teleport.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type EntityTransformOptions,
|
|
13
13
|
} from '../transformations'
|
|
14
14
|
import { generateSortFallbackFieldHelper, generateSortFieldSqlHelper } from '../product-price-sort'
|
|
15
|
+
import { SORT_NULLS_LAST_SQL } from '../list-sort-order'
|
|
15
16
|
import { generateRowOrderTiebreakHelper, rowIdentityColumn } from '../row-order'
|
|
16
17
|
import { generateProductFilterClauseHelper } from '../product-filter-fields'
|
|
17
18
|
import { collectLocalizedColumns, generateLocalizedColumnsHelper } from '../localized-columns'
|
|
@@ -19,6 +20,7 @@ import { REQUEST_LOCALE_PARAM } from '../request-locale'
|
|
|
19
20
|
import { generateReadGuardCall, generateTableAccessPreamble } from './utils/table-access-guard'
|
|
20
21
|
import { generateRequestSqlGuardsCode } from './utils/request-sql-guards'
|
|
21
22
|
import { generateArrayOverlapSqlCode } from '../array-overlap-sql'
|
|
23
|
+
import { PG_CLIENT_IMPORT } from './pg-client-import'
|
|
22
24
|
|
|
23
25
|
interface TeleportDBConfig {
|
|
24
26
|
host?: string
|
|
@@ -102,7 +104,7 @@ export const generateTeleportFetcher = (
|
|
|
102
104
|
}`
|
|
103
105
|
: `(process.env.${DEFAULT_ENV_KEYS.ssl} === 'false' ? false : process.env.${DEFAULT_ENV_KEYS.ssl} === 'true' ? { rejectUnauthorized: false } : undefined)`
|
|
104
106
|
|
|
105
|
-
return
|
|
107
|
+
return `${PG_CLIENT_IMPORT}
|
|
106
108
|
|
|
107
109
|
${generateTableAccessPreamble(transformOptions.trustedReaderRoles || [])}
|
|
108
110
|
function normalizePostgresConnectionString(connectionString) {
|
|
@@ -314,7 +316,10 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
314
316
|
// \`fetchData({ rawQuery })\` and expect it to execute verbatim.
|
|
315
317
|
if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {
|
|
316
318
|
assertRawQuerySafe(req.query.rawQuery)
|
|
317
|
-
|
|
319
|
+
// A page's own statement, read for a visitor, runs over the rows a
|
|
320
|
+
// visitor is served of every narrowed table it names.
|
|
321
|
+
const rawSql = __rowAccess.narrowRaw ? __brpNarrowRawQuery(req.query.rawQuery) : req.query.rawQuery
|
|
322
|
+
const rawResult = await client.query(rawSql)
|
|
318
323
|
const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []
|
|
319
324
|
const rawPlain = rawRows.map((row) =>
|
|
320
325
|
row && typeof row.toJSON === 'function' ? row.toJSON() : row
|
|
@@ -324,7 +329,10 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
324
329
|
success: true,
|
|
325
330
|
// Every row here ends up in a browser (a page's props or a client
|
|
326
331
|
// refetch), so no credential column survives, whatever joined it.
|
|
327
|
-
data: __taWithoutCredentials(
|
|
332
|
+
data: __taWithoutCredentials(
|
|
333
|
+
__rowAccess.narrowRaw ? __brpStripHiddenOfRaw(req.query.rawQuery, rawSafe) : rawSafe,
|
|
334
|
+
null
|
|
335
|
+
),
|
|
328
336
|
timestamp: Date.now()
|
|
329
337
|
})
|
|
330
338
|
}
|
|
@@ -391,8 +399,8 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
391
399
|
|
|
392
400
|
// Handle sorts - new array format. Two ORDER BY clauses are built: the one
|
|
393
401
|
// actually used (a translatable column in the request's language, the
|
|
394
|
-
// products table's price rewrites
|
|
395
|
-
// the fallback below.
|
|
402
|
+
// products table's price rewrites, rows without a value last), and a
|
|
403
|
+
// plain-column twin kept only as the fallback below.
|
|
396
404
|
let orderBySql = ''
|
|
397
405
|
let plainOrderBySql = ''
|
|
398
406
|
if (sorts) {
|
|
@@ -401,7 +409,7 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
401
409
|
const valid = parsedSorts.filter((sort) => sort && sort.field)
|
|
402
410
|
valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))
|
|
403
411
|
const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'
|
|
404
|
-
const orderClauses = valid.map((sort) => \`\${localizedSortFieldSql(sort.field, requestLocale) || sortFieldSql(sort.field)} \${orderOf(sort)}\`)
|
|
412
|
+
const orderClauses = valid.map((sort) => \`\${localizedSortFieldSql(sort.field, requestLocale) || sortFieldSql(sort.field)} \${orderOf(sort)}${SORT_NULLS_LAST_SQL}\`)
|
|
405
413
|
const plainClauses = valid.map((sort) => \`\${sortFallbackField(sort.field)} \${orderOf(sort)}\`)
|
|
406
414
|
|
|
407
415
|
if (orderClauses.length > 0) {
|
|
@@ -416,10 +424,10 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
416
424
|
assertRequestColumn(sortBy, 'sort field')
|
|
417
425
|
const sortByDirection = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'
|
|
418
426
|
const tiebreak = rowOrderTiebreak([sortBy], sortByDirection)
|
|
419
|
-
orderBySql = \` ORDER BY \${localizedSortFieldSql(sortBy, requestLocale) || sortBy} \${sortByDirection}\${tiebreak}\`
|
|
427
|
+
orderBySql = \` ORDER BY \${localizedSortFieldSql(sortBy, requestLocale) || sortBy} \${sortByDirection}${SORT_NULLS_LAST_SQL}\${tiebreak}\`
|
|
420
428
|
plainOrderBySql = \` ORDER BY \${sortBy} \${sortByDirection}\${tiebreak}\`
|
|
421
429
|
}
|
|
422
|
-
const
|
|
430
|
+
const usedRewrittenSort = orderBySql !== plainOrderBySql
|
|
423
431
|
|
|
424
432
|
const limitValue = requestRowCount(limit || perPage)
|
|
425
433
|
const offsetValue = offset !== undefined ? parseInt(offset) : (page && perPage ? (parseInt(page) - 1) * parseInt(perPage) : undefined)
|
|
@@ -441,12 +449,13 @@ ${generateReadGuardCall(tableName, 'req.query && req.query.rawQuery')}
|
|
|
441
449
|
// the whole table - so if a database ever rejects it, fall back to ordering
|
|
442
450
|
// by the stored list price rather than serving an empty products page. The
|
|
443
451
|
// translatable-column ordering takes the same exit: a per-language column
|
|
444
|
-
// the schema had at generation time may have been dropped since.
|
|
452
|
+
// the schema had at generation time may have been dropped since. So does
|
|
453
|
+
// the empty-values-last ordering.
|
|
445
454
|
let result
|
|
446
455
|
try {
|
|
447
456
|
result = await client.query(sql, queryParams)
|
|
448
457
|
} catch (sortError) {
|
|
449
|
-
if (!
|
|
458
|
+
if (!usedRewrittenSort) throw sortError
|
|
450
459
|
console.warn(
|
|
451
460
|
'Rewritten sort failed; falling back to the plain column:',
|
|
452
461
|
sortError && sortError.message
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContentTables } from '@teleporthq/teleport-shared'
|
|
1
|
+
import { ContentTables, TableAccess } from '@teleporthq/teleport-shared'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* What a per-table read route (`/api/teleport-<table>-<source>` and its count
|
|
@@ -42,9 +42,19 @@ export const generateBrowserRowPolicyCode =
|
|
|
42
42
|
(): string => `// GENERATED — see browser-row-policy.ts in teleport-plugin-next-data-source.
|
|
43
43
|
var __BRP_POLICIES = ${JSON.stringify(BROWSER_ROW_POLICIES)};
|
|
44
44
|
|
|
45
|
+
// What a page's own data carries of a member, read for a visitor: the public
|
|
46
|
+
// profile (TableAccess.PUBLIC_PROFILE_COLUMNS) — an allow-list, so a column the
|
|
47
|
+
// account row gains later stays out too. A browser reads the table not at all.
|
|
48
|
+
var __BRP_PUBLIC_PROFILE = ${JSON.stringify({
|
|
49
|
+
predicate: 'TRUE',
|
|
50
|
+
hiddenColumns: [],
|
|
51
|
+
visibleColumns: TableAccess.PUBLIC_PROFILE_COLUMNS,
|
|
52
|
+
})};
|
|
53
|
+
|
|
45
54
|
// Who is asking: the store's own server code and its administrators read every
|
|
46
|
-
// row; anyone else is a visitor.
|
|
55
|
+
// row; anyone else is a visitor — a page's own data included (\`__taIsVisitorView\`).
|
|
47
56
|
async function __brpReader(req) {
|
|
57
|
+
if (__taIsVisitorView(req)) return { trusted: false };
|
|
48
58
|
if (__taIsInternalRequest(req)) return { trusted: true };
|
|
49
59
|
var token = null;
|
|
50
60
|
try {
|
|
@@ -63,11 +73,15 @@ function __brpMentionsAny(rawQuery, tables) {
|
|
|
63
73
|
return false;
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
// A
|
|
67
|
-
//
|
|
68
|
-
//
|
|
76
|
+
// A visitor's browser may not send a statement that names a narrowed table: it
|
|
77
|
+
// is refused whole. A page's own statement, read for a visitor, is run over the
|
|
78
|
+
// rows a visitor is served instead (\`narrowRaw\`, see \`__brpNarrowRawQuery\`).
|
|
79
|
+
// The session is only decoded when the read touches one of these tables.
|
|
69
80
|
async function __brpAccess(req, tableName, rawQuery) {
|
|
70
81
|
var raw = typeof rawQuery === 'string' && rawQuery.length > 0 ? rawQuery : null;
|
|
82
|
+
if (!raw && __taIsVisitorView(req) && __taIsCredentialTable(tableName)) {
|
|
83
|
+
return { denied: null, policy: __BRP_PUBLIC_PROFILE };
|
|
84
|
+
}
|
|
71
85
|
var bare = __taNormalizeTable(tableName);
|
|
72
86
|
var rawNarrowed = !!raw && __brpMentionsAny(raw, Object.keys(__BRP_POLICIES));
|
|
73
87
|
var policy = raw ? null : (Object.prototype.hasOwnProperty.call(__BRP_POLICIES, bare) ? __BRP_POLICIES[bare] : null);
|
|
@@ -75,18 +89,78 @@ async function __brpAccess(req, tableName, rawQuery) {
|
|
|
75
89
|
var reader = await __brpReader(req);
|
|
76
90
|
if (reader.trusted) return { denied: null, policy: null };
|
|
77
91
|
if (rawNarrowed) {
|
|
92
|
+
if (__taIsVisitorView(req)) return { denied: null, policy: null, narrowRaw: true };
|
|
78
93
|
return { denied: { status: 403, message: 'Forbidden: raw queries over this table are only available to server-side calls' }, policy: null };
|
|
79
94
|
}
|
|
80
95
|
return { denied: null, policy: policy };
|
|
81
96
|
}
|
|
82
97
|
|
|
98
|
+
function __brpRefuseRaw(what) {
|
|
99
|
+
var err = new Error('Forbidden: ' + what + ' cannot be read for a visitor');
|
|
100
|
+
err.status = 403;
|
|
101
|
+
return err;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// A page's own statement over the rows a visitor is served: every narrowed
|
|
105
|
+
// table it reads becomes a WITH entry of the same name, ahead of the
|
|
106
|
+
// statement, which its own references — and its own WITH entries — resolve to:
|
|
107
|
+
//
|
|
108
|
+
// WITH "teleport_products" AS (SELECT * FROM "teleport_products" AS __brp_rows WHERE (<predicate>)) <statement>
|
|
109
|
+
//
|
|
110
|
+
// Inside its own definition the entry names the table itself (a WITH that is
|
|
111
|
+
// not RECURSIVE does not see itself). A hidden column reads NULL however the
|
|
112
|
+
// statement spells it — an alias, a whole-row value. A statement this cannot
|
|
113
|
+
// vouch for is refused: unreadable or opaque, RECURSIVE (its entries would see
|
|
114
|
+
// themselves), or naming a narrowed table through a schema, which reaches the
|
|
115
|
+
// table past the entry.
|
|
116
|
+
function __brpNarrowRawQuery(sql) {
|
|
117
|
+
var cleaned = __taCleanSql(sql);
|
|
118
|
+
if (!cleaned.ok || __TA_OPAQUE_SQL_RE.test(cleaned.text)) throw __brpRefuseRaw('this statement');
|
|
119
|
+
var tables = Object.keys(__BRP_POLICIES).filter(function(table) {
|
|
120
|
+
return new RegExp('(?:^|[^A-Za-z0-9_])"?' + table + '"?(?![A-Za-z0-9_])', 'i').test(cleaned.text);
|
|
121
|
+
});
|
|
122
|
+
if (tables.length === 0) return sql;
|
|
123
|
+
var leading = /^\\s*with\\b(\\s+recursive\\b)?/i.exec(cleaned.text);
|
|
124
|
+
if (leading && leading[1]) throw __brpRefuseRaw('a recursive statement');
|
|
125
|
+
tables.forEach(function(table) {
|
|
126
|
+
if (new RegExp('\\\\.\\\\s*"?' + table + '"?(?![A-Za-z0-9_])', 'i').test(cleaned.text)) throw __brpRefuseRaw(table);
|
|
127
|
+
});
|
|
128
|
+
var entries = tables.map(function(table) {
|
|
129
|
+
var policy = __BRP_POLICIES[table];
|
|
130
|
+
var hidden = {};
|
|
131
|
+
policy.hiddenColumns.forEach(function(column) { hidden[column] = null; });
|
|
132
|
+
var columns = policy.hiddenColumns.length > 0
|
|
133
|
+
? '(jsonb_populate_record(__brp_rows, ' + "'" + JSON.stringify(hidden) + "'" + '::jsonb)).*'
|
|
134
|
+
: '*';
|
|
135
|
+
return '"' + table + '" AS (SELECT ' + columns + ' FROM "' + table + '" AS __brp_rows WHERE (' + policy.predicate + '))';
|
|
136
|
+
});
|
|
137
|
+
var statement = String(sql).trim().replace(/;\\s*$/, '');
|
|
138
|
+
if (!leading) return 'WITH ' + entries.join(', ') + ' ' + statement;
|
|
139
|
+
// The statement's own WITH list follows ours, so its entries read ours too.
|
|
140
|
+
var own = /^with\\b/i.exec(statement);
|
|
141
|
+
if (!own) throw __brpRefuseRaw('this statement');
|
|
142
|
+
return 'WITH ' + entries.join(', ') + ', ' + statement.slice(own[0].length);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The rows of a statement narrowed for a visitor, without the hidden columns
|
|
146
|
+
// of the tables it read.
|
|
147
|
+
function __brpStripHiddenOfRaw(sql, rows) {
|
|
148
|
+
var cleaned = __taCleanSql(sql);
|
|
149
|
+
return Object.keys(__BRP_POLICIES).reduce(function(current, table) {
|
|
150
|
+
return cleaned.ok && __taSqlMentionsTable(sql, table) ? __brpStripHidden(__BRP_POLICIES[table], current) : current;
|
|
151
|
+
}, rows);
|
|
152
|
+
}
|
|
153
|
+
|
|
83
154
|
function __brpColumnName(field) {
|
|
84
155
|
var parts = String(field == null ? '' : field).replace(/"/g, '').trim().split('.');
|
|
85
156
|
return parts[parts.length - 1];
|
|
86
157
|
}
|
|
87
158
|
|
|
159
|
+
// A column the policy keeps from the reader: one it hides, or — for a policy
|
|
160
|
+
// that names the only columns it serves (\`visibleColumns\`) — any other.
|
|
88
161
|
function __brpIsHidden(policy, field) {
|
|
89
162
|
var name = __brpColumnName(field).toLowerCase();
|
|
163
|
+
if (policy.visibleColumns) return policy.visibleColumns.indexOf(name) === -1;
|
|
90
164
|
for (var i = 0; i < policy.hiddenColumns.length; i++) {
|
|
91
165
|
if (policy.hiddenColumns[i].toLowerCase() === name) return true;
|
|
92
166
|
}
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
UIDLDynamicReference,
|
|
9
9
|
} from '@teleporthq/teleport-types'
|
|
10
10
|
import { UIDLUtils, StringUtils } from '@teleporthq/teleport-shared'
|
|
11
|
+
import { RouteUtils } from '@teleporthq/teleport-plugin-common'
|
|
11
12
|
import {
|
|
12
13
|
extractDataSourceIntoNextAPIFolder,
|
|
13
14
|
extractDataSourceIntoGetStaticProps,
|
|
@@ -652,7 +653,9 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
|
|
|
652
653
|
dependencies,
|
|
653
654
|
dynamicRouteAttr,
|
|
654
655
|
uidl.outputOptions?.folderPath,
|
|
655
|
-
transformOptions
|
|
656
|
+
transformOptions,
|
|
657
|
+
() => RouteUtils.pageDataReadArguments(uidl, options.auth),
|
|
658
|
+
uidl.stateDefinitions
|
|
656
659
|
)
|
|
657
660
|
|
|
658
661
|
if (result.success && result.chunk) {
|
|
@@ -794,7 +797,10 @@ export const createNextPagesDataSourcePlugin: ComponentPluginFactory<{}> = () =>
|
|
|
794
797
|
types.identifier(fetcherImportName),
|
|
795
798
|
types.identifier('fetchData')
|
|
796
799
|
),
|
|
797
|
-
[
|
|
800
|
+
[
|
|
801
|
+
types.objectExpression([]),
|
|
802
|
+
...RouteUtils.pageDataReadArguments(uidl, options.auth),
|
|
803
|
+
]
|
|
798
804
|
)
|
|
799
805
|
|
|
800
806
|
const safeFetchExpression = types.callExpression(
|