@teleporthq/teleport-shared 0.43.67 → 0.43.69
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/embed-runtime-emitter.ts +197 -0
- package/__tests__/utils/discount-engine.ts +1548 -0
- package/__tests__/utils/product-options.ts +2458 -0
- package/__tests__/utils/rich-text-embeds-runtime-module.ts +34 -0
- package/__tests__/utils/subscription-access.ts +37 -0
- package/dist/cjs/index.d.ts +9 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +17 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils/cart-currency.d.ts +27 -0
- package/dist/cjs/utils/cart-currency.d.ts.map +1 -0
- package/dist/cjs/utils/cart-currency.js +31 -0
- package/dist/cjs/utils/cart-currency.js.map +1 -0
- package/dist/cjs/utils/cart-line-pricing.d.ts +25 -0
- package/dist/cjs/utils/cart-line-pricing.d.ts.map +1 -0
- package/dist/cjs/utils/cart-line-pricing.js +29 -0
- package/dist/cjs/utils/cart-line-pricing.js.map +1 -0
- package/dist/cjs/utils/discount-engine.d.ts +74 -0
- package/dist/cjs/utils/discount-engine.d.ts.map +1 -0
- package/dist/cjs/utils/discount-engine.js +435 -0
- package/dist/cjs/utils/discount-engine.js.map +1 -0
- package/dist/cjs/utils/product-options.d.ts +25 -0
- package/dist/cjs/utils/product-options.d.ts.map +1 -0
- package/dist/cjs/utils/product-options.js +1155 -0
- package/dist/cjs/utils/product-options.js.map +1 -0
- package/dist/cjs/utils/rich-text-embeds-codegen.d.ts +13 -0
- package/dist/cjs/utils/rich-text-embeds-codegen.d.ts.map +1 -1
- package/dist/cjs/utils/rich-text-embeds-codegen.js +15 -104
- package/dist/cjs/utils/rich-text-embeds-codegen.js.map +1 -1
- package/dist/cjs/utils/rich-text-embeds-runtime-source.d.ts +2 -0
- package/dist/cjs/utils/rich-text-embeds-runtime-source.d.ts.map +1 -0
- package/dist/cjs/utils/rich-text-embeds-runtime-source.js +409 -0
- package/dist/cjs/utils/rich-text-embeds-runtime-source.js.map +1 -0
- package/dist/cjs/utils/session-cookie-resolver.d.ts +85 -0
- package/dist/cjs/utils/session-cookie-resolver.d.ts.map +1 -0
- package/dist/cjs/utils/session-cookie-resolver.js +108 -0
- package/dist/cjs/utils/session-cookie-resolver.js.map +1 -0
- package/dist/cjs/utils/subscription-access.d.ts +36 -0
- package/dist/cjs/utils/subscription-access.d.ts.map +1 -0
- package/dist/cjs/utils/subscription-access.js +61 -0
- package/dist/cjs/utils/subscription-access.js.map +1 -0
- package/dist/cjs/utils/table-access.d.ts +114 -0
- package/dist/cjs/utils/table-access.d.ts.map +1 -0
- package/dist/cjs/utils/table-access.js +238 -0
- package/dist/cjs/utils/table-access.js.map +1 -0
- package/dist/cjs/utils/web-push.d.ts +43 -0
- package/dist/cjs/utils/web-push.d.ts.map +1 -0
- package/dist/cjs/utils/web-push.js +62 -0
- package/dist/cjs/utils/web-push.js.map +1 -0
- package/dist/esm/index.d.ts +9 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +9 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils/cart-currency.d.ts +27 -0
- package/dist/esm/utils/cart-currency.d.ts.map +1 -0
- package/dist/esm/utils/cart-currency.js +27 -0
- package/dist/esm/utils/cart-currency.js.map +1 -0
- package/dist/esm/utils/cart-line-pricing.d.ts +25 -0
- package/dist/esm/utils/cart-line-pricing.d.ts.map +1 -0
- package/dist/esm/utils/cart-line-pricing.js +25 -0
- package/dist/esm/utils/cart-line-pricing.js.map +1 -0
- package/dist/esm/utils/discount-engine.d.ts +74 -0
- package/dist/esm/utils/discount-engine.d.ts.map +1 -0
- package/dist/esm/utils/discount-engine.js +431 -0
- package/dist/esm/utils/discount-engine.js.map +1 -0
- package/dist/esm/utils/product-options.d.ts +25 -0
- package/dist/esm/utils/product-options.d.ts.map +1 -0
- package/dist/esm/utils/product-options.js +1151 -0
- package/dist/esm/utils/product-options.js.map +1 -0
- package/dist/esm/utils/rich-text-embeds-codegen.d.ts +13 -0
- package/dist/esm/utils/rich-text-embeds-codegen.d.ts.map +1 -1
- package/dist/esm/utils/rich-text-embeds-codegen.js +16 -105
- package/dist/esm/utils/rich-text-embeds-codegen.js.map +1 -1
- package/dist/esm/utils/rich-text-embeds-runtime-source.d.ts +2 -0
- package/dist/esm/utils/rich-text-embeds-runtime-source.d.ts.map +1 -0
- package/dist/esm/utils/rich-text-embeds-runtime-source.js +406 -0
- package/dist/esm/utils/rich-text-embeds-runtime-source.js.map +1 -0
- package/dist/esm/utils/session-cookie-resolver.d.ts +85 -0
- package/dist/esm/utils/session-cookie-resolver.d.ts.map +1 -0
- package/dist/esm/utils/session-cookie-resolver.js +103 -0
- package/dist/esm/utils/session-cookie-resolver.js.map +1 -0
- package/dist/esm/utils/subscription-access.d.ts +36 -0
- package/dist/esm/utils/subscription-access.d.ts.map +1 -0
- package/dist/esm/utils/subscription-access.js +57 -0
- package/dist/esm/utils/subscription-access.js.map +1 -0
- package/dist/esm/utils/table-access.d.ts +114 -0
- package/dist/esm/utils/table-access.d.ts.map +1 -0
- package/dist/esm/utils/table-access.js +233 -0
- package/dist/esm/utils/table-access.js.map +1 -0
- package/dist/esm/utils/web-push.d.ts +43 -0
- package/dist/esm/utils/web-push.d.ts.map +1 -0
- package/dist/esm/utils/web-push.js +59 -0
- package/dist/esm/utils/web-push.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +16 -0
- package/src/utils/cart-currency.ts +76 -0
- package/src/utils/cart-line-pricing.ts +151 -0
- package/src/utils/discount-engine.ts +437 -0
- package/src/utils/product-options.ts +1150 -0
- package/src/utils/rich-text-embeds-codegen.ts +16 -183
- package/src/utils/rich-text-embeds-runtime-source.ts +405 -0
- package/src/utils/session-cookie-resolver.ts +305 -0
- package/src/utils/subscription-access.ts +59 -0
- package/src/utils/table-access.ts +516 -0
- package/src/utils/web-push.ts +61 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resolving the NextAuth session token in a generated route, WITHOUT asking
|
|
4
|
+
* `process.env.NEXTAUTH_URL` what the session cookie is called.
|
|
5
|
+
*
|
|
6
|
+
* ## ⛔ THE REPORTED DEFECT
|
|
7
|
+
*
|
|
8
|
+
* A published project showed "Failed to load dashboard data. Please refresh."
|
|
9
|
+
* on the home page, "Failed to load meeting data." on the list page and "could
|
|
10
|
+
* not fetch the templates" on a third — for a user who was signed in, on a page
|
|
11
|
+
* whose own `/api/auth/session` returned that user. The failing call was the
|
|
12
|
+
* page-load workflow's server segment, answering **401 `Unauthenticated`** from
|
|
13
|
+
* `guardWorkflowRequest`. Reloading sometimes fixed it; fanning 50 concurrent
|
|
14
|
+
* requests at the same route returned **33 × 401 and 17 × 200**, while 40
|
|
15
|
+
* concurrent `/api/auth/session` calls resolved the user 40/40.
|
|
16
|
+
*
|
|
17
|
+
* ## The mechanism
|
|
18
|
+
*
|
|
19
|
+
* next-auth v4's `getToken()` derives the cookie it reads from an env var:
|
|
20
|
+
*
|
|
21
|
+
* ```js
|
|
22
|
+
* secureCookie = process.env.NEXTAUTH_URL?.startsWith('https://') ?? !!process.env.VERCEL,
|
|
23
|
+
* cookieName = secureCookie ? '__Secure-next-auth.session-token' : 'next-auth.session-token',
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* A generated project ships `NEXTAUTH_URL=http://localhost:3000` — the build
|
|
27
|
+
* cannot know the domain it will be published to, so `pages/api/auth/[...nextauth].js`
|
|
28
|
+
* repairs `process.env.NEXTAUTH_URL` **from the incoming request** instead. That
|
|
29
|
+
* repair is process-local: it fixes the value for whichever serverless instance
|
|
30
|
+
* happened to serve an auth request, and for no other. Every other instance
|
|
31
|
+
* still holds `http://localhost:3000`, so `startsWith('https://')` is `false`
|
|
32
|
+
* (NOT `undefined` — the `??` fallback to `!!process.env.VERCEL` never runs),
|
|
33
|
+
* `getToken` looks for `next-auth.session-token`, the browser is holding
|
|
34
|
+
* `__Secure-next-auth.session-token`, nothing is found, and a signed-in user is
|
|
35
|
+
* reported as anonymous.
|
|
36
|
+
*
|
|
37
|
+
* Hence the coin-flip: it depends on whether the instance that served your
|
|
38
|
+
* request had previously served an auth request. Nothing about the session, the
|
|
39
|
+
* secret or the cookie is wrong.
|
|
40
|
+
*
|
|
41
|
+
* The same defect is why the generated middleware carries a `hasSessionCookie`
|
|
42
|
+
* fallback and a round trip to `/api/auth/session`, attributed there to an
|
|
43
|
+
* "Edge runtime quirk" — the Edge process holds that same localhost value.
|
|
44
|
+
*
|
|
45
|
+
* ## The rule
|
|
46
|
+
*
|
|
47
|
+
* ⭐ **A request must be authenticated from the REQUEST, never from an ambient
|
|
48
|
+
* env var that describes the deployment.** So this resolver reads which session
|
|
49
|
+
* cookie the caller actually sent, and tells `getToken` exactly that — falling
|
|
50
|
+
* back to the request's own protocol when the cookie header is unavailable.
|
|
51
|
+
*
|
|
52
|
+
* Emitted as plain ES5 text and inlined into each generated route: a route that
|
|
53
|
+
* carries its own copy cannot be broken by a missing sibling module or an
|
|
54
|
+
* untraced dependency, and every call site gets the identical implementation
|
|
55
|
+
* from this one definition. It lives in teleport-shared because both the
|
|
56
|
+
* workflow routes and the per-table data routes decode the session.
|
|
57
|
+
*/
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.generateCommonJsSessionTokenResolverCode = exports.generateSessionTokenResolverCode = exports.SESSION_TOKEN_RESOLVER_FN = void 0;
|
|
60
|
+
/** The `__tq`-prefixed globals the emitted snippet declares. */
|
|
61
|
+
exports.SESSION_TOKEN_RESOLVER_FN = '__tqResolveSessionToken';
|
|
62
|
+
/**
|
|
63
|
+
* Session-cookie names next-auth/Auth.js writes, with the `secureCookie` flag
|
|
64
|
+
* that produces each. Order within a protocol is irrelevant — only ONE of these
|
|
65
|
+
* is ever present — but the https/http split decides which pair is tried first.
|
|
66
|
+
*
|
|
67
|
+
* `__Host-` is deliberately absent: next-auth uses it only for the CSRF and
|
|
68
|
+
* callback cookies, never for the session token.
|
|
69
|
+
*/
|
|
70
|
+
var KNOWN_SESSION_COOKIES = [
|
|
71
|
+
{ name: '__Secure-next-auth.session-token', secure: true },
|
|
72
|
+
{ name: 'next-auth.session-token', secure: false },
|
|
73
|
+
{ name: '__Secure-authjs.session-token', secure: true },
|
|
74
|
+
{ name: 'authjs.session-token', secure: false },
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* ES5 source for the shared session-token resolver, safe to inline into any
|
|
78
|
+
* generated CommonJS route or ESM middleware.
|
|
79
|
+
*
|
|
80
|
+
* Declares:
|
|
81
|
+
* - `__tqCookieNamesPresent(req)` — the set of cookie names on the request,
|
|
82
|
+
* parsed properly (a substring test would see `next-auth.session-token`
|
|
83
|
+
* inside `__Secure-next-auth.session-token` and pick the wrong one).
|
|
84
|
+
* - `__tqRequestIsSecure(req)` — https as the REQUEST reports it.
|
|
85
|
+
* - `__tqSessionTokenCandidates(req)` — `{cookieName, secureCookie}` pairs to try.
|
|
86
|
+
* - `__tqResolveSessionToken(getToken, req, secret)` — the token, or null.
|
|
87
|
+
*
|
|
88
|
+
* `getToken` is passed IN rather than required here, so the same body serves a
|
|
89
|
+
* CommonJS route (lazy `require('next-auth/jwt')`) and the Edge middleware
|
|
90
|
+
* (static `import`). Pure, total, never throws, no env reads.
|
|
91
|
+
*/
|
|
92
|
+
var generateSessionTokenResolverCode = function () {
|
|
93
|
+
return "// GENERATED \u2014 see generateSessionTokenResolverCode in\n// @teleporthq/teleport-shared/src/utils/session-cookie-resolver.ts.\n//\n// next-auth's getToken() picks the session cookie name from\n// process.env.NEXTAUTH_URL, which a generated project ships as a localhost\n// default \u2014 so on a published https domain it looks for the non-secure cookie\n// and reports a signed-in user as anonymous. These helpers read the cookie the\n// request actually carries and tell getToken exactly which one it is.\nvar __TQ_SESSION_COOKIES = ".concat(JSON.stringify(KNOWN_SESSION_COOKIES.map(function (c) { return [c.name, c.secure]; })), ";\n\n// Every cookie NAME on the request. Parsed on the \"; \" separator rather than\n// matched as a substring: \"next-auth.session-token\" occurs inside\n// \"__Secure-next-auth.session-token\", so a substring test reports the plain\n// cookie as present whenever the secure one is, and getToken then decodes the\n// wrong (missing) cookie.\nfunction __tqCookieNamesPresent(req) {\n var present = {};\n var raw = '';\n try {\n var headers = req && req.headers;\n if (headers && typeof headers.get === 'function') {\n // Edge / fetch Headers.\n raw = headers.get('cookie') || '';\n } else if (headers) {\n var value = headers.cookie || headers.Cookie;\n raw = Array.isArray(value) ? value.join('; ') : value || '';\n }\n } catch (e) {\n raw = '';\n }\n var parts = String(raw).split(';');\n for (var i = 0; i < parts.length; i++) {\n var eq = parts[i].indexOf('=');\n if (eq > 0) {\n present[parts[i].slice(0, eq).trim()] = true;\n }\n }\n // Second source: the runtime's own parsed cookies. A Next.js API route hands\n // over a plain object; the Edge RequestCookies exposes getAll().\n try {\n var cookies = req && req.cookies;\n if (cookies && typeof cookies.getAll === 'function') {\n var all = cookies.getAll() || [];\n for (var a = 0; a < all.length; a++) {\n if (all[a] && all[a].name) {\n present[all[a].name] = true;\n }\n }\n } else if (cookies && typeof cookies === 'object') {\n for (var key in cookies) {\n if (Object.prototype.hasOwnProperty.call(cookies, key)) {\n present[key] = true;\n }\n }\n }\n } catch (e) {\n /* the header pass above is enough */\n }\n return present;\n}\n\n// Is this request https? Read from the proxy header the platform sets, then the\n// socket, then the URL \u2014 never from an env var describing the deployment.\nfunction __tqRequestIsSecure(req) {\n try {\n var headers = (req && req.headers) || {};\n var proto =\n typeof headers.get === 'function'\n ? headers.get('x-forwarded-proto')\n : headers['x-forwarded-proto'];\n if (Array.isArray(proto)) {\n proto = proto[0];\n }\n if (typeof proto === 'string' && proto) {\n // A chain of proxies appends: \"https,http\" \u2014 the client-facing hop is first.\n return proto.split(',')[0].trim().toLowerCase() === 'https';\n }\n if (req && req.nextUrl && req.nextUrl.protocol === 'https:') {\n return true;\n }\n if (req && req.socket && req.socket.encrypted) {\n return true;\n }\n if (req && req.connection && req.connection.encrypted) {\n return true;\n }\n if (typeof (req && req.url) === 'string' && req.url.indexOf('https://') === 0) {\n return true;\n }\n } catch (e) {\n /* fall through to false */\n }\n return false;\n}\n\n// The { cookieName, secureCookie } pairs worth trying, most likely first.\n// Normally exactly one cookie is present and this returns a single entry; the\n// list only grows when a site has been reached over both http and https.\nfunction __tqSessionTokenCandidates(req) {\n var present = __tqCookieNamesPresent(req);\n var secure = __tqRequestIsSecure(req);\n var candidates = [];\n for (var i = 0; i < __TQ_SESSION_COOKIES.length; i++) {\n var name = __TQ_SESSION_COOKIES[i][0];\n var isSecureName = __TQ_SESSION_COOKIES[i][1];\n if (present[name] || present[name + '.0']) {\n candidates.push({ cookieName: name, secureCookie: isSecureName });\n }\n }\n candidates.sort(function (a, b) {\n if (a.secureCookie === b.secureCookie) {\n return 0;\n }\n return a.secureCookie === secure ? -1 : 1;\n });\n if (candidates.length === 0) {\n // No cookie we recognise. The project may configure a custom cookie name in\n // authOptions, so hand getToken the request's own protocol and let it apply\n // its defaults \u2014 same behaviour as before this resolver existed, minus the\n // localhost env var.\n candidates.push({ secureCookie: secure });\n }\n return candidates;\n}\n\n// When a browser carries BOTH session cookies, both usually decode \u2014 they are\n// two snapshots of the same account written at different times (NextAuth\n// switches between the plain and the \"__Secure-\" name with the origin it\n// derives, and never clears the other one). The one NextAuth is maintaining is\n// re-issued on every session read, so it is the most recently issued; the\n// other is frozen at whatever the account looked like when NextAuth last used\n// that name, role included. Trusting it by protocol alone made a promoted admin\n// read as \"user\" at the edge while /api/auth/session said \"admin\".\nfunction __tqTokenIssuedAt(token) {\n var issued = token && typeof token === 'object' ? token.iat : null;\n return typeof issued === 'number' && isFinite(issued) ? issued : 0;\n}\n\n// The decoded session token, or null. Decodes every candidate cookie (normally\n// exactly one) and keeps the most recently issued, so a project that renamed\n// its cookie, or one reached over both protocols, resolves to the session\n// NextAuth is actually maintaining. A tie keeps the protocol-matching\n// candidate. Never throws.\nfunction ").concat(exports.SESSION_TOKEN_RESOLVER_FN, "(getToken, req, secret) {\n if (!secret || typeof getToken !== 'function') {\n return Promise.resolve(null);\n }\n var candidates = __tqSessionTokenCandidates(req);\n var decode = function (candidate) {\n var params = { req: req, secret: secret, secureCookie: candidate.secureCookie };\n if (candidate.cookieName) {\n params.cookieName = candidate.cookieName;\n }\n return Promise.resolve()\n .then(function () {\n return getToken(params);\n })\n .catch(function () {\n return null;\n });\n };\n var decodes = [];\n for (var i = 0; i < candidates.length; i++) {\n decodes.push(decode(candidates[i]));\n }\n return Promise.all(decodes)\n .then(function (tokens) {\n var best = null;\n for (var t = 0; t < tokens.length; t++) {\n if (tokens[t] && (best === null || __tqTokenIssuedAt(tokens[t]) > __tqTokenIssuedAt(best))) {\n best = tokens[t];\n }\n }\n return best;\n })\n .catch(function () {\n return null;\n });\n}\n");
|
|
94
|
+
};
|
|
95
|
+
exports.generateSessionTokenResolverCode = generateSessionTokenResolverCode;
|
|
96
|
+
/**
|
|
97
|
+
* The resolver plus a lazy `require('next-auth/jwt')`, for a CommonJS route.
|
|
98
|
+
*
|
|
99
|
+
* The require is lazy and guarded because a project generated WITHOUT
|
|
100
|
+
* authentication does not ship `next-auth`; such a project also has no session
|
|
101
|
+
* to read, so resolving to `null` there is the correct answer rather than a
|
|
102
|
+
* module-load crash.
|
|
103
|
+
*/
|
|
104
|
+
var generateCommonJsSessionTokenResolverCode = function () {
|
|
105
|
+
return "".concat((0, exports.generateSessionTokenResolverCode)(), "\n// Lazy + guarded: a project generated without authentication has no next-auth\n// installed, and no session to read either \u2014 null is the right answer there.\nfunction __tqGetTokenFn() {\n try {\n var jwt = require('next-auth/jwt');\n return jwt && typeof jwt.getToken === 'function' ? jwt.getToken : null;\n } catch (e) {\n return null;\n }\n}\n\n// The session token for this request, or null. Reads NEXTAUTH_SECRET itself.\nfunction __tqSessionToken(req) {\n var secret = process.env.NEXTAUTH_SECRET;\n if (!secret) {\n return Promise.resolve(null);\n }\n return ").concat(exports.SESSION_TOKEN_RESOLVER_FN, "(__tqGetTokenFn(), req, secret);\n}\n");
|
|
106
|
+
};
|
|
107
|
+
exports.generateCommonJsSessionTokenResolverCode = generateCommonJsSessionTokenResolverCode;
|
|
108
|
+
//# sourceMappingURL=session-cookie-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-cookie-resolver.js","sourceRoot":"","sources":["../../../src/utils/session-cookie-resolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;;;AAEH,gEAAgE;AACnD,QAAA,yBAAyB,GAAG,yBAAyB,CAAA;AAElE;;;;;;;GAOG;AACH,IAAM,qBAAqB,GAAqD;IAC9E,EAAE,IAAI,EAAE,kCAAkC,EAAE,MAAM,EAAE,IAAI,EAAE;IAC1D,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,KAAK,EAAE;IAClD,EAAE,IAAI,EAAE,+BAA+B,EAAE,MAAM,EAAE,IAAI,EAAE;IACvD,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,KAAK,EAAE;CAChD,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACI,IAAM,gCAAgC,GAAG;IAC9C,OAAO,qiBAQoB,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EAAlB,CAAkB,CAAC,CAAC,mqKAuItF,iCAAyB,yhCAoCnC,CAAA;AACD,CAAC,CAAA;AArLY,QAAA,gCAAgC,oCAqL5C;AAED;;;;;;;GAOG;AACI,IAAM,wCAAwC,GAAG;IACtD,OAAO,UAAG,IAAA,wCAAgC,GAAE,6lBAkBnC,iCAAyB,0CAEnC,CAAA;AACD,CAAC,CAAA;AAtBY,QAAA,wCAAwC,4CAsBpD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which subscription rows entitle a buyer to a subscriber-only page.
|
|
3
|
+
*
|
|
4
|
+
* `teleport_subscriptions.status` moves through `pending` (the first order is
|
|
5
|
+
* placed, the provider has not confirmed the mandate), `trialing`, `active`,
|
|
6
|
+
* `past_due` (a renewal charge failed and the provider retries), `paused`,
|
|
7
|
+
* `cancelled` and `expired`. A buyer keeps their content while the provider is
|
|
8
|
+
* still honouring the mandate — through a trial, an active period and the
|
|
9
|
+
* retry window of a failed charge. `pending` never confirmed, `paused` stopped
|
|
10
|
+
* collecting, and the two closed statuses are over. A row with
|
|
11
|
+
* `cancel_at_period_end` stays entitled: its status only turns `cancelled`
|
|
12
|
+
* once the provider ends it at the period end.
|
|
13
|
+
*
|
|
14
|
+
* The GUI keeps the same list as `ENTITLED_SUBSCRIPTION_STATUS_VALUES`
|
|
15
|
+
* (`features/e-commerce/constants/subscriptions.ts`), pinned by a parity spec.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SUBSCRIPTION_ENTITLED_STATUSES: ReadonlyArray<string>;
|
|
18
|
+
export declare const SUBSCRIPTIONS_TABLE = "teleport_subscriptions";
|
|
19
|
+
/**
|
|
20
|
+
* The one parameterised statement every entitlement check runs:
|
|
21
|
+
* $1 — the session user id (compared as text: auth ids are strings, the
|
|
22
|
+
* column is a uuid),
|
|
23
|
+
* $2 — the entitled statuses,
|
|
24
|
+
* $3 — the product ids the page names; an EMPTY array means any product.
|
|
25
|
+
*
|
|
26
|
+
* A subscription the STORE bills (a provider that cannot charge a saved
|
|
27
|
+
* payment method) carries `access_until`: the end of the paid period plus the
|
|
28
|
+
* grace for the next payment. Past it the row stops entitling on its own, so
|
|
29
|
+
* access ends on time even when the scheduled billing run is late or off.
|
|
30
|
+
* Read through `to_jsonb` so a store whose table predates the column still
|
|
31
|
+
* answers (a missing key reads NULL, which never limits).
|
|
32
|
+
*/
|
|
33
|
+
export declare const SUBSCRIPTION_ENTITLEMENT_SQL: string;
|
|
34
|
+
/** Trimmed, de-duplicated, non-empty ids — whatever shape the caller had them in. */
|
|
35
|
+
export declare const normalizeSubscriptionProductIds: (value: unknown) => string[];
|
|
36
|
+
//# sourceMappingURL=subscription-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-access.d.ts","sourceRoot":"","sources":["../../../src/utils/subscription-access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,8BAA8B,EAAE,aAAa,CAAC,MAAM,CAIhE,CAAA;AAED,eAAO,MAAM,mBAAmB,2BAA2B,CAAA;AAE3D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,QAK0B,CAAA;AAEnE,qFAAqF;AACrF,eAAO,MAAM,+BAA+B,UAAW,OAAO,KAAG,MAAM,EAYtE,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeSubscriptionProductIds = exports.SUBSCRIPTION_ENTITLEMENT_SQL = exports.SUBSCRIPTIONS_TABLE = exports.SUBSCRIPTION_ENTITLED_STATUSES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Which subscription rows entitle a buyer to a subscriber-only page.
|
|
6
|
+
*
|
|
7
|
+
* `teleport_subscriptions.status` moves through `pending` (the first order is
|
|
8
|
+
* placed, the provider has not confirmed the mandate), `trialing`, `active`,
|
|
9
|
+
* `past_due` (a renewal charge failed and the provider retries), `paused`,
|
|
10
|
+
* `cancelled` and `expired`. A buyer keeps their content while the provider is
|
|
11
|
+
* still honouring the mandate — through a trial, an active period and the
|
|
12
|
+
* retry window of a failed charge. `pending` never confirmed, `paused` stopped
|
|
13
|
+
* collecting, and the two closed statuses are over. A row with
|
|
14
|
+
* `cancel_at_period_end` stays entitled: its status only turns `cancelled`
|
|
15
|
+
* once the provider ends it at the period end.
|
|
16
|
+
*
|
|
17
|
+
* The GUI keeps the same list as `ENTITLED_SUBSCRIPTION_STATUS_VALUES`
|
|
18
|
+
* (`features/e-commerce/constants/subscriptions.ts`), pinned by a parity spec.
|
|
19
|
+
*/
|
|
20
|
+
exports.SUBSCRIPTION_ENTITLED_STATUSES = [
|
|
21
|
+
'trialing',
|
|
22
|
+
'active',
|
|
23
|
+
'past_due',
|
|
24
|
+
];
|
|
25
|
+
exports.SUBSCRIPTIONS_TABLE = 'teleport_subscriptions';
|
|
26
|
+
/**
|
|
27
|
+
* The one parameterised statement every entitlement check runs:
|
|
28
|
+
* $1 — the session user id (compared as text: auth ids are strings, the
|
|
29
|
+
* column is a uuid),
|
|
30
|
+
* $2 — the entitled statuses,
|
|
31
|
+
* $3 — the product ids the page names; an EMPTY array means any product.
|
|
32
|
+
*
|
|
33
|
+
* A subscription the STORE bills (a provider that cannot charge a saved
|
|
34
|
+
* payment method) carries `access_until`: the end of the paid period plus the
|
|
35
|
+
* grace for the next payment. Past it the row stops entitling on its own, so
|
|
36
|
+
* access ends on time even when the scheduled billing run is late or off.
|
|
37
|
+
* Read through `to_jsonb` so a store whose table predates the column still
|
|
38
|
+
* answers (a missing key reads NULL, which never limits).
|
|
39
|
+
*/
|
|
40
|
+
exports.SUBSCRIPTION_ENTITLEMENT_SQL = "SELECT 1 FROM ".concat(exports.SUBSCRIPTIONS_TABLE, " s ") +
|
|
41
|
+
'WHERE s.user_id::text = $1 AND s.status = ANY($2::text[]) ' +
|
|
42
|
+
'AND (cardinality($3::text[]) = 0 OR s.product_id::text = ANY($3::text[])) ' +
|
|
43
|
+
"AND ((to_jsonb(s) ->> 'access_until') IS NULL " +
|
|
44
|
+
"OR (to_jsonb(s) ->> 'access_until')::timestamp > NOW()) LIMIT 1";
|
|
45
|
+
/** Trimmed, de-duplicated, non-empty ids — whatever shape the caller had them in. */
|
|
46
|
+
var normalizeSubscriptionProductIds = function (value) {
|
|
47
|
+
var raw = Array.isArray(value) ? value : typeof value === 'string' ? value.split(',') : [];
|
|
48
|
+
var seen = new Set();
|
|
49
|
+
var ids = [];
|
|
50
|
+
for (var _i = 0, raw_1 = raw; _i < raw_1.length; _i++) {
|
|
51
|
+
var entry = raw_1[_i];
|
|
52
|
+
var id = String(entry !== null && entry !== void 0 ? entry : '').trim();
|
|
53
|
+
if (id && !seen.has(id)) {
|
|
54
|
+
seen.add(id);
|
|
55
|
+
ids.push(id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return ids;
|
|
59
|
+
};
|
|
60
|
+
exports.normalizeSubscriptionProductIds = normalizeSubscriptionProductIds;
|
|
61
|
+
//# sourceMappingURL=subscription-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-access.js","sourceRoot":"","sources":["../../../src/utils/subscription-access.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;GAeG;AACU,QAAA,8BAA8B,GAA0B;IACnE,UAAU;IACV,QAAQ;IACR,UAAU;CACX,CAAA;AAEY,QAAA,mBAAmB,GAAG,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;GAaG;AACU,QAAA,4BAA4B,GACvC,wBAAiB,2BAAmB,QAAK;IACzC,4DAA4D;IAC5D,4EAA4E;IAC5E,gDAAgD;IAChD,iEAAiE,CAAA;AAEnE,qFAAqF;AAC9E,IAAM,+BAA+B,GAAG,UAAC,KAAc;IAC5D,IAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAC5F,IAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,IAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAoB,UAAG,EAAH,WAAG,EAAH,iBAAG,EAAH,IAAG,EAAE;QAApB,IAAM,KAAK,YAAA;QACd,IAAM,EAAE,GAAG,MAAM,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACrC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YACvB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACZ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;SACb;KACF;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAZY,QAAA,+BAA+B,mCAY3C"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { UIDLAuthentication } from '@teleporthq/teleport-types';
|
|
2
|
+
/**
|
|
3
|
+
* Which store tables a browser may touch through the generated data routes.
|
|
4
|
+
*
|
|
5
|
+
* Every generated Next store exposes two families of data routes: the
|
|
6
|
+
* workflow data API (`/api/data/<source>/<op>`, called by workflow data nodes)
|
|
7
|
+
* and one read route per bound table (`/api/teleport-<table>-<source>`, called
|
|
8
|
+
* by the pages' data providers, with a `rawQuery` escape hatch). Both accept
|
|
9
|
+
* a browser's request. For most tables that is the product: a catalogue is
|
|
10
|
+
* public. For the tables below it is theft.
|
|
11
|
+
*
|
|
12
|
+
* `MONEY_TABLES` are money and the keys to it: a gift card's code and
|
|
13
|
+
* balance, its ledger, every voucher code (a generated batch is a list of
|
|
14
|
+
* single-use coupons) and the redemption ledger. A browser reads or writes
|
|
15
|
+
* them only as a signed-in member of a trusted role (the generated admin) —
|
|
16
|
+
* the server-side workflow segments that look a card up, debit it or redeem a
|
|
17
|
+
* code identify themselves with the app secret instead.
|
|
18
|
+
*
|
|
19
|
+
* `CUSTOMER_RECORD_TABLES` are the customers' own records: who ordered what
|
|
20
|
+
* and where it ships, their invoices, parcels, returns, carts and reviews,
|
|
21
|
+
* and the accounts themselves. A buyer is served their own order through the
|
|
22
|
+
* page-load workflow (a server segment), the merchant every order through
|
|
23
|
+
* the admin's session — nothing in a generated storefront lists them from
|
|
24
|
+
* the browser, so a browser that asks for the list is not the store.
|
|
25
|
+
*
|
|
26
|
+
* `PROTECTED_TABLES` is both, spliced into both route families and the
|
|
27
|
+
* restricted-caller SQL check, so no reader can disagree about what is
|
|
28
|
+
* private.
|
|
29
|
+
*
|
|
30
|
+
* `WRITE_PROTECTED_TABLES` decide how much is given away but must stay readable:
|
|
31
|
+
* the storefront provider loads the active automatic rules to price the order
|
|
32
|
+
* summary. A browser that could update `usage_count` or `discount_value` could
|
|
33
|
+
* discount forever, so writes need the same trust.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MONEY_TABLES: ReadonlyArray<string>;
|
|
36
|
+
export declare const CUSTOMER_RECORD_TABLES: ReadonlyArray<string>;
|
|
37
|
+
export declare const PROTECTED_TABLES: ReadonlyArray<string>;
|
|
38
|
+
export declare const WRITE_PROTECTED_TABLES: ReadonlyArray<string>;
|
|
39
|
+
/**
|
|
40
|
+
* The auth users table's CREDENTIALS — the password hash and the OAuth
|
|
41
|
+
* provider's tokens (an access or refresh token IS the user's account at the
|
|
42
|
+
* provider). Profile columns stay readable; these never leave the server in a
|
|
43
|
+
* row, whoever asks: the generated admin's users list shows no password either.
|
|
44
|
+
*/
|
|
45
|
+
export declare const AUTH_CREDENTIAL_COLUMNS: ReadonlyArray<string>;
|
|
46
|
+
/** The table the generated auth reads and writes those credentials in. */
|
|
47
|
+
export declare const AUTH_CREDENTIAL_TABLES: ReadonlyArray<string>;
|
|
48
|
+
export interface PublicTableFeed {
|
|
49
|
+
/** The only columns a browser is ever served. */
|
|
50
|
+
columns: ReadonlyArray<string>;
|
|
51
|
+
/** SQL every browser read of the table is narrowed with. */
|
|
52
|
+
predicate: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A table a browser may read ONLY as a fixed projection of the rows the
|
|
56
|
+
* DATABASE considers live — never whole, never through raw SQL, and never
|
|
57
|
+
* filtered or sorted by a column outside the projection (a filter is an
|
|
58
|
+
* oracle: `usage_count > 100` answers for a column that is never returned).
|
|
59
|
+
*
|
|
60
|
+
* `teleport_discounts` is the merchant's campaign plan: drafts, paused rules,
|
|
61
|
+
* launches scheduled for next month, how often each has been used. The
|
|
62
|
+
* storefront needs none of that; it prices the order summary from the rules
|
|
63
|
+
* that apply right now. Serving exactly those rows is what keeps an
|
|
64
|
+
* unannounced campaign unannounced, and it makes the WINDOW the database's
|
|
65
|
+
* judgement, the same clock that re-prices the order at submit: the storefront
|
|
66
|
+
* prices every row it is served and re-derives nothing from the browser clock,
|
|
67
|
+
* so a disagreement with the server's re-pricing is resolved by re-reading the
|
|
68
|
+
* feed.
|
|
69
|
+
*/
|
|
70
|
+
export declare const PUBLIC_FEED_TABLES: Readonly<Record<string, PublicTableFeed>>;
|
|
71
|
+
/**
|
|
72
|
+
* The roles allowed to read a protected table from a browser: whoever may open
|
|
73
|
+
* the generated admin panel, read off the protection the UIDL records for its
|
|
74
|
+
* folder and pages. A project without an admin panel has no page that binds a
|
|
75
|
+
* money table, so the empty list (server-side callers only) is the right answer.
|
|
76
|
+
*/
|
|
77
|
+
export declare const resolveTrustedReaderRoles: (auth?: UIDLAuthentication | null) => string[];
|
|
78
|
+
/**
|
|
79
|
+
* ES5 source of the guard, inlined into a generated route. Declares:
|
|
80
|
+
* - `__taIsInternalRequest(req)` — the caller presented the app secret, or
|
|
81
|
+
* is the module's own server-side `fetchData` (`req.__tqServerCall`).
|
|
82
|
+
* - `__taIsRestrictedRequest(req)` — a server-side node that presents the app
|
|
83
|
+
* secret but runs SQL it did not write (the AI data-query node): trusted to
|
|
84
|
+
* run raw SQL, never trusted with the protected tables.
|
|
85
|
+
* - `__taBrowserIsReadOnly(req)` — true for a browser in an app that has an
|
|
86
|
+
* app secret: every server-side caller identifies itself there, so a browser
|
|
87
|
+
* reads through the structured routes only — no raw SQL, no writes.
|
|
88
|
+
* - `__taNormalizeTable(name)` — the bare table a spelling names.
|
|
89
|
+
* - `__taSqlMentionsTable(sql, table)` — the statement names the table as an
|
|
90
|
+
* identifier (bare, double-quoted or schema-qualified; comments and string
|
|
91
|
+
* literals cannot hide or fake a mention).
|
|
92
|
+
* - `__taAssertSqlTextAllowed(sql)` — throws a 403 error when an untrusted
|
|
93
|
+
* caller's raw statement touches a protected table or a feed table, or
|
|
94
|
+
* writes a write-protected one.
|
|
95
|
+
* - `__taPublicFeed(req, tableName)` — the projection an untrusted caller is
|
|
96
|
+
* served of a feed table (`{ columns, predicate }`), or `null` when the
|
|
97
|
+
* caller may read the table as asked (a server-side one) or the table has
|
|
98
|
+
* no feed.
|
|
99
|
+
* - `__taAssertFeedFields(tableName, feed, fields)` — throws a 403 error when
|
|
100
|
+
* a filter or sort field is not one of the feed's columns.
|
|
101
|
+
* - `__taWithoutCredentials(rows, tableName)` — the rows with the auth users
|
|
102
|
+
* table's credential columns removed: all of them for that table's rows,
|
|
103
|
+
* the unambiguous ones for a raw statement's (`tableName` null).
|
|
104
|
+
* - `__taIsCredentialTable(tableName)` — the table holds those credentials.
|
|
105
|
+
* - `__taGuardRead(req, tableName, rawQuery)` — for the per-table read routes:
|
|
106
|
+
* resolves to `null` when the request may proceed, else `{status, message}`.
|
|
107
|
+
* A protected table is readable by an internal caller or by a session whose
|
|
108
|
+
* role is in `trustedReaderRoles`; the session is decoded by the
|
|
109
|
+
* `__tqSessionToken` resolver, which the host route must inline as well.
|
|
110
|
+
*/
|
|
111
|
+
export declare const generateTableAccessHelperCode: (params: {
|
|
112
|
+
trustedReaderRoles: ReadonlyArray<string>;
|
|
113
|
+
}) => string;
|
|
114
|
+
//# sourceMappingURL=table-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-access.d.ts","sourceRoot":"","sources":["../../../src/utils/table-access.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,YAAY,EAAE,aAAa,CAAC,MAAM,CAiB9C,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,MAAM,CAqCxD,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAgD,CAAA;AAEnG,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,MAAM,CAA0B,CAAA;AAEnF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CAAC,MAAM,CASzD,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,EAAE,aAAa,CAAC,MAAM,CAAa,CAAA;AAYtE,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAC9B,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CA0BxE,CAAA;AAMD;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,UAAW,kBAAkB,GAAG,IAAI,KAAG,MAAM,EAiBlF,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,6BAA6B,WAAY;IACpD,kBAAkB,EAAE,cAAc,MAAM,CAAC,CAAA;CAC1C,KAAG,MAiRH,CAAA"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
3
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
4
|
+
if (ar || !(i in from)) {
|
|
5
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
6
|
+
ar[i] = from[i];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.generateTableAccessHelperCode = exports.resolveTrustedReaderRoles = exports.PUBLIC_FEED_TABLES = exports.AUTH_CREDENTIAL_TABLES = exports.AUTH_CREDENTIAL_COLUMNS = exports.WRITE_PROTECTED_TABLES = exports.PROTECTED_TABLES = exports.CUSTOMER_RECORD_TABLES = exports.MONEY_TABLES = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Which store tables a browser may touch through the generated data routes.
|
|
15
|
+
*
|
|
16
|
+
* Every generated Next store exposes two families of data routes: the
|
|
17
|
+
* workflow data API (`/api/data/<source>/<op>`, called by workflow data nodes)
|
|
18
|
+
* and one read route per bound table (`/api/teleport-<table>-<source>`, called
|
|
19
|
+
* by the pages' data providers, with a `rawQuery` escape hatch). Both accept
|
|
20
|
+
* a browser's request. For most tables that is the product: a catalogue is
|
|
21
|
+
* public. For the tables below it is theft.
|
|
22
|
+
*
|
|
23
|
+
* `MONEY_TABLES` are money and the keys to it: a gift card's code and
|
|
24
|
+
* balance, its ledger, every voucher code (a generated batch is a list of
|
|
25
|
+
* single-use coupons) and the redemption ledger. A browser reads or writes
|
|
26
|
+
* them only as a signed-in member of a trusted role (the generated admin) —
|
|
27
|
+
* the server-side workflow segments that look a card up, debit it or redeem a
|
|
28
|
+
* code identify themselves with the app secret instead.
|
|
29
|
+
*
|
|
30
|
+
* `CUSTOMER_RECORD_TABLES` are the customers' own records: who ordered what
|
|
31
|
+
* and where it ships, their invoices, parcels, returns, carts and reviews,
|
|
32
|
+
* and the accounts themselves. A buyer is served their own order through the
|
|
33
|
+
* page-load workflow (a server segment), the merchant every order through
|
|
34
|
+
* the admin's session — nothing in a generated storefront lists them from
|
|
35
|
+
* the browser, so a browser that asks for the list is not the store.
|
|
36
|
+
*
|
|
37
|
+
* `PROTECTED_TABLES` is both, spliced into both route families and the
|
|
38
|
+
* restricted-caller SQL check, so no reader can disagree about what is
|
|
39
|
+
* private.
|
|
40
|
+
*
|
|
41
|
+
* `WRITE_PROTECTED_TABLES` decide how much is given away but must stay readable:
|
|
42
|
+
* the storefront provider loads the active automatic rules to price the order
|
|
43
|
+
* summary. A browser that could update `usage_count` or `discount_value` could
|
|
44
|
+
* discount forever, so writes need the same trust.
|
|
45
|
+
*/
|
|
46
|
+
exports.MONEY_TABLES = [
|
|
47
|
+
'teleport_gift_cards',
|
|
48
|
+
'teleport_gift_card_transactions',
|
|
49
|
+
'teleport_vouchers',
|
|
50
|
+
'teleport_voucher_redemptions',
|
|
51
|
+
// The sent-email log keeps the RENDERED message, and a gift-card email
|
|
52
|
+
// carries the full code — protecting the card table while serving its
|
|
53
|
+
// codes from the email log would be no protection at all.
|
|
54
|
+
'teleport_sent_emails',
|
|
55
|
+
// A live reset token IS the account: whoever reads one sets the password.
|
|
56
|
+
// Only the server-side reset workflows ever read or write the table.
|
|
57
|
+
'password_reset_tokens',
|
|
58
|
+
// A digital product's files and license keys ARE the product: the order
|
|
59
|
+
// page's server segment mints a download link per paid line, and the
|
|
60
|
+
// download route assigns a key; nothing else reads them.
|
|
61
|
+
'teleport_product_files',
|
|
62
|
+
'teleport_license_keys',
|
|
63
|
+
];
|
|
64
|
+
exports.CUSTOMER_RECORD_TABLES = [
|
|
65
|
+
'users',
|
|
66
|
+
'teleport_orders',
|
|
67
|
+
'teleport_order_items',
|
|
68
|
+
'teleport_invoices',
|
|
69
|
+
'teleport_invoice_items',
|
|
70
|
+
'teleport_events',
|
|
71
|
+
'teleport_shipments',
|
|
72
|
+
'teleport_shipment_items',
|
|
73
|
+
'teleport_withdrawal_requests',
|
|
74
|
+
'teleport_withdrawal_request_items',
|
|
75
|
+
'teleport_abandoned_carts',
|
|
76
|
+
'teleport_cart',
|
|
77
|
+
'teleport_cart_items',
|
|
78
|
+
'teleport_favourites',
|
|
79
|
+
// Who subscribed to what and what they downloaded: the buyer's own page reads
|
|
80
|
+
// them through a server segment, the admin through its session.
|
|
81
|
+
'teleport_subscriptions',
|
|
82
|
+
'teleport_downloads',
|
|
83
|
+
// A review is public once approved, but the row carries the reviewer's
|
|
84
|
+
// email and the ones still in moderation; the product page reads the
|
|
85
|
+
// approved ones server-side.
|
|
86
|
+
'teleport_product_reviews',
|
|
87
|
+
// A push subscription is a capability: whoever holds the endpoint and its
|
|
88
|
+
// keys can notify that device. Only the app's push routes read the table.
|
|
89
|
+
'teleport_push_subscriptions',
|
|
90
|
+
// A comment is public once approved, but the row carries the commenter's
|
|
91
|
+
// email and the comments still waiting for approval; the post page reads
|
|
92
|
+
// the approved ones server-side, public columns only.
|
|
93
|
+
'teleport_blog_comments',
|
|
94
|
+
// What visitors told the AI assistant, and the merchant's private knowledge
|
|
95
|
+
// base it answers from: the assistant reads them in its own server
|
|
96
|
+
// segments, the admin lists them with its session.
|
|
97
|
+
'teleport_ai_chat_knowledge_sources',
|
|
98
|
+
'teleport_ai_chat_documents',
|
|
99
|
+
'teleport_ai_chat_conversations',
|
|
100
|
+
'teleport_ai_chat_messages',
|
|
101
|
+
];
|
|
102
|
+
exports.PROTECTED_TABLES = __spreadArray(__spreadArray([], exports.MONEY_TABLES, true), exports.CUSTOMER_RECORD_TABLES, true);
|
|
103
|
+
exports.WRITE_PROTECTED_TABLES = ['teleport_discounts'];
|
|
104
|
+
/**
|
|
105
|
+
* The auth users table's CREDENTIALS — the password hash and the OAuth
|
|
106
|
+
* provider's tokens (an access or refresh token IS the user's account at the
|
|
107
|
+
* provider). Profile columns stay readable; these never leave the server in a
|
|
108
|
+
* row, whoever asks: the generated admin's users list shows no password either.
|
|
109
|
+
*/
|
|
110
|
+
exports.AUTH_CREDENTIAL_COLUMNS = [
|
|
111
|
+
'password',
|
|
112
|
+
'access_token',
|
|
113
|
+
'refresh_token',
|
|
114
|
+
'id_token',
|
|
115
|
+
'session_state',
|
|
116
|
+
'token_type',
|
|
117
|
+
'scope',
|
|
118
|
+
'expires_at',
|
|
119
|
+
];
|
|
120
|
+
/** The table the generated auth reads and writes those credentials in. */
|
|
121
|
+
exports.AUTH_CREDENTIAL_TABLES = ['users'];
|
|
122
|
+
/**
|
|
123
|
+
* The credential names no other table has a reason to use: dropped from the
|
|
124
|
+
* rows of a raw statement too, where a column cannot be traced to its table
|
|
125
|
+
* (`scope` and `expires_at` are ordinary columns elsewhere — a gift card
|
|
126
|
+
* expires).
|
|
127
|
+
*/
|
|
128
|
+
var UNAMBIGUOUS_CREDENTIAL_COLUMNS = exports.AUTH_CREDENTIAL_COLUMNS.filter(function (column) { return column !== 'scope' && column !== 'expires_at'; });
|
|
129
|
+
/**
|
|
130
|
+
* A table a browser may read ONLY as a fixed projection of the rows the
|
|
131
|
+
* DATABASE considers live — never whole, never through raw SQL, and never
|
|
132
|
+
* filtered or sorted by a column outside the projection (a filter is an
|
|
133
|
+
* oracle: `usage_count > 100` answers for a column that is never returned).
|
|
134
|
+
*
|
|
135
|
+
* `teleport_discounts` is the merchant's campaign plan: drafts, paused rules,
|
|
136
|
+
* launches scheduled for next month, how often each has been used. The
|
|
137
|
+
* storefront needs none of that; it prices the order summary from the rules
|
|
138
|
+
* that apply right now. Serving exactly those rows is what keeps an
|
|
139
|
+
* unannounced campaign unannounced, and it makes the WINDOW the database's
|
|
140
|
+
* judgement, the same clock that re-prices the order at submit: the storefront
|
|
141
|
+
* prices every row it is served and re-derives nothing from the browser clock,
|
|
142
|
+
* so a disagreement with the server's re-pricing is resolved by re-reading the
|
|
143
|
+
* feed.
|
|
144
|
+
*/
|
|
145
|
+
exports.PUBLIC_FEED_TABLES = {
|
|
146
|
+
teleport_discounts: {
|
|
147
|
+
// Exactly what the shared engine normalises a rule from (`__deNormalizeRule`),
|
|
148
|
+
// plus `status`, which the storefront still checks. The window bounds,
|
|
149
|
+
// `description` and `usage_count` are the merchant's, and stay with the
|
|
150
|
+
// merchant.
|
|
151
|
+
columns: [
|
|
152
|
+
'id',
|
|
153
|
+
'name',
|
|
154
|
+
'status',
|
|
155
|
+
'priority',
|
|
156
|
+
'discount_type',
|
|
157
|
+
'discount_value',
|
|
158
|
+
'applies_to_all_products',
|
|
159
|
+
'product_ids',
|
|
160
|
+
'category_ids',
|
|
161
|
+
'min_order_value',
|
|
162
|
+
'min_quantity',
|
|
163
|
+
'first_order_only',
|
|
164
|
+
'buy_quantity',
|
|
165
|
+
'get_quantity',
|
|
166
|
+
'stackable',
|
|
167
|
+
],
|
|
168
|
+
predicate: "status = 'active' AND (starts_at IS NULL OR starts_at <= NOW()) AND (ends_at IS NULL OR ends_at > NOW())",
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
/** The generated back office: its folder in the pages tree and its route prefix. */
|
|
172
|
+
var ADMIN_FOLDER_NAME = 'admin-panel';
|
|
173
|
+
var ADMIN_ROUTE_PREFIX = '/admin';
|
|
174
|
+
/**
|
|
175
|
+
* The roles allowed to read a protected table from a browser: whoever may open
|
|
176
|
+
* the generated admin panel, read off the protection the UIDL records for its
|
|
177
|
+
* folder and pages. A project without an admin panel has no page that binds a
|
|
178
|
+
* money table, so the empty list (server-side callers only) is the right answer.
|
|
179
|
+
*/
|
|
180
|
+
var resolveTrustedReaderRoles = function (auth) {
|
|
181
|
+
var roles = new Set();
|
|
182
|
+
if (!auth) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
Object.values(auth.folderProtection || {}).forEach(function (folder) {
|
|
186
|
+
if (folder && folder.folderName === ADMIN_FOLDER_NAME) {
|
|
187
|
+
;
|
|
188
|
+
(folder.allowedRoles || []).forEach(function (role) { return roles.add(role); });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
Object.values(auth.pageProtection || {}).forEach(function (page) {
|
|
192
|
+
var route = page && typeof page.route === 'string' ? page.route : '';
|
|
193
|
+
if (route === ADMIN_ROUTE_PREFIX || route.startsWith("".concat(ADMIN_ROUTE_PREFIX, "/"))) {
|
|
194
|
+
;
|
|
195
|
+
(page.allowedRoles || []).forEach(function (role) { return roles.add(role); });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return Array.from(roles).sort();
|
|
199
|
+
};
|
|
200
|
+
exports.resolveTrustedReaderRoles = resolveTrustedReaderRoles;
|
|
201
|
+
/**
|
|
202
|
+
* ES5 source of the guard, inlined into a generated route. Declares:
|
|
203
|
+
* - `__taIsInternalRequest(req)` — the caller presented the app secret, or
|
|
204
|
+
* is the module's own server-side `fetchData` (`req.__tqServerCall`).
|
|
205
|
+
* - `__taIsRestrictedRequest(req)` — a server-side node that presents the app
|
|
206
|
+
* secret but runs SQL it did not write (the AI data-query node): trusted to
|
|
207
|
+
* run raw SQL, never trusted with the protected tables.
|
|
208
|
+
* - `__taBrowserIsReadOnly(req)` — true for a browser in an app that has an
|
|
209
|
+
* app secret: every server-side caller identifies itself there, so a browser
|
|
210
|
+
* reads through the structured routes only — no raw SQL, no writes.
|
|
211
|
+
* - `__taNormalizeTable(name)` — the bare table a spelling names.
|
|
212
|
+
* - `__taSqlMentionsTable(sql, table)` — the statement names the table as an
|
|
213
|
+
* identifier (bare, double-quoted or schema-qualified; comments and string
|
|
214
|
+
* literals cannot hide or fake a mention).
|
|
215
|
+
* - `__taAssertSqlTextAllowed(sql)` — throws a 403 error when an untrusted
|
|
216
|
+
* caller's raw statement touches a protected table or a feed table, or
|
|
217
|
+
* writes a write-protected one.
|
|
218
|
+
* - `__taPublicFeed(req, tableName)` — the projection an untrusted caller is
|
|
219
|
+
* served of a feed table (`{ columns, predicate }`), or `null` when the
|
|
220
|
+
* caller may read the table as asked (a server-side one) or the table has
|
|
221
|
+
* no feed.
|
|
222
|
+
* - `__taAssertFeedFields(tableName, feed, fields)` — throws a 403 error when
|
|
223
|
+
* a filter or sort field is not one of the feed's columns.
|
|
224
|
+
* - `__taWithoutCredentials(rows, tableName)` — the rows with the auth users
|
|
225
|
+
* table's credential columns removed: all of them for that table's rows,
|
|
226
|
+
* the unambiguous ones for a raw statement's (`tableName` null).
|
|
227
|
+
* - `__taIsCredentialTable(tableName)` — the table holds those credentials.
|
|
228
|
+
* - `__taGuardRead(req, tableName, rawQuery)` — for the per-table read routes:
|
|
229
|
+
* resolves to `null` when the request may proceed, else `{status, message}`.
|
|
230
|
+
* A protected table is readable by an internal caller or by a session whose
|
|
231
|
+
* role is in `trustedReaderRoles`; the session is decoded by the
|
|
232
|
+
* `__tqSessionToken` resolver, which the host route must inline as well.
|
|
233
|
+
*/
|
|
234
|
+
var generateTableAccessHelperCode = function (params) {
|
|
235
|
+
return "// GENERATED \u2014 see generateTableAccessHelperCode in\n// @teleporthq/teleport-shared/src/utils/table-access.ts.\n//\n// Tables that ARE money, and tables that decide how much of it is given away.\n// A browser reads the first group only as a trusted role (the generated admin)\n// and writes the second group never; server-side workflow segments present the\n// app secret instead. One list for both data-route families.\nvar __TA_PROTECTED_TABLES = ".concat(JSON.stringify(exports.PROTECTED_TABLES), ";\nvar __TA_WRITE_PROTECTED_TABLES = ").concat(JSON.stringify(exports.WRITE_PROTECTED_TABLES), ";\n// Readable from a browser only as this projection of the rows the database\n// calls live \u2014 see PUBLIC_FEED_TABLES. The per-table routes serve a whole\n// table (and raw SQL), which cannot express a feed, so there they are refused\n// like a protected table.\nvar __TA_PUBLIC_FEED_TABLES = ").concat(JSON.stringify(exports.PUBLIC_FEED_TABLES), ";\nvar __TA_FEED_TABLES = Object.keys(__TA_PUBLIC_FEED_TABLES);\nvar __TA_TRUSTED_READER_ROLES = ").concat(JSON.stringify(params.trustedReaderRoles), ";\nvar __TA_CREDENTIAL_COLUMNS = ").concat(JSON.stringify(exports.AUTH_CREDENTIAL_COLUMNS), ";\nvar __TA_UNAMBIGUOUS_CREDENTIAL_COLUMNS = ").concat(JSON.stringify(UNAMBIGUOUS_CREDENTIAL_COLUMNS), ";\nvar __TA_CREDENTIAL_TABLES = ").concat(JSON.stringify(exports.AUTH_CREDENTIAL_TABLES), ";\nvar __TA_SQL_WRITE_RE = /\\b(?:insert|update|delete|merge|truncate)\\b/i;\n// What a statement can reach without spelling a table's name where this scan\n// can see it: a Unicode-escaped identifier (U&\"\\0075sers\" is `users`), the\n// functions that run SQL text or dump whole tables, schemas or the database\n// (the table name then sits in a string, or is not named at all), and the\n// planner statistics, which hold sampled column values of every table.\nvar __TA_OPAQUE_SQL_RE = /u&\"|\\b(?:query|table|cursor|schema|database)_to_xml\\w*\\s*\\(|\\bts_(?:stat|rewrite)\\s*\\(|\\bdblink\\w*\\s*\\(|\\bpg_read_(?:binary_)?file\\s*\\(|\\blo_(?:import|export|get)\\s*\\(|\\bpg_stat(?:s|istic)\\w*/i;\n\n// Only server code can read NEXTAUTH_SECRET, so a browser cannot forge the\n// header a server segment sends. An in-process call (getStaticProps and the\n// page-load workflows call the handler directly with a synthetic request)\n// marks its request object instead \u2014 a property, never a header, so nothing\n// on the wire can claim it.\nfunction __taPresentsAppSecret(req) {\n var secret = req && req.headers && req.headers['x-internal-data-secret'];\n return !!(secret && process.env.NEXTAUTH_SECRET && secret === process.env.NEXTAUTH_SECRET);\n}\n\nfunction __taIsRestrictedRequest(req) {\n return __taPresentsAppSecret(req) && !!(req.headers && req.headers['x-tq-restricted-sql'] === '1');\n}\n\nfunction __taIsInternalRequest(req) {\n if (req && req.__tqServerCall === true) return true;\n return __taPresentsAppSecret(req) && !__taIsRestrictedRequest(req);\n}\n\n// Generated browser code reads through the structured routes and never sends\n// SQL or a write of its own \u2014 every data node runs server-side and presents\n// the secret. So where a secret exists to tell the two apart, anything else a\n// browser sends is refused whole: no statement scan has to be right about it.\n// An app without one (no authentication, hence no store) keeps the scan.\nfunction __taBrowserIsReadOnly(req) {\n return !!process.env.NEXTAUTH_SECRET && !__taIsInternalRequest(req) && !__taIsRestrictedRequest(req);\n}\n\n// Comments and string literals out, so a table name inside a string neither\n// hides a reference nor fakes one. Quoted identifiers stay, whole: in Postgres\n// they name tables, which is exactly what is being looked for.\n//\n// \u26D4 This is a lexer, not a search: a quote or comment marker inside a quoted\n// identifier (\"x'\", \"--\") is part of the name, a `$` or an `E` inside an\n// identifier (a$b$, ELSE'\u2026') starts neither a dollar quote nor an escape\n// string, and a dollar-quoted body is consumed by its own tag. An UNTERMINATED\n// literal, identifier, comment or dollar quote makes the whole scan fail closed\n// (ok:false): a statement this cleaner cannot read is one it cannot vouch for.\nfunction __taCleanSql(sql) {\n var text = String(sql || '');\n var out = '';\n var i = 0;\n function inIdentifier(at) { return at > 0 && /[A-Za-z0-9_$]/.test(text[at - 1]); }\n while (i < text.length) {\n var ch = text[i];\n var next = text[i + 1];\n if (ch === '-' && next === '-') {\n while (i < text.length && text[i] !== '\\n') i++;\n continue;\n }\n if (ch === '/' && next === '*') {\n i += 2;\n var depth = 1;\n while (i < text.length && depth > 0) {\n if (text[i] === '/' && text[i + 1] === '*') { depth++; i += 2; continue; }\n if (text[i] === '*' && text[i + 1] === '/') { depth--; i += 2; continue; }\n i++;\n }\n if (depth > 0) return { ok: false, text: out };\n out += ' ';\n continue;\n }\n // \"\u2026\" is one identifier token; \"\" inside it is an escaped quote.\n if (ch === '\"') {\n var end = i + 1;\n var closedIdentifier = false;\n while (end < text.length) {\n if (text[end] === '\"' && text[end + 1] === '\"') { end += 2; continue; }\n if (text[end] === '\"') { closedIdentifier = true; break; }\n end++;\n }\n if (!closedIdentifier) return { ok: false, text: out };\n out += text.slice(i, end + 1);\n i = end + 1;\n continue;\n }\n // $tag$ \u2026 $tag$ \u2014 the body is data whatever it holds.\n if (ch === '$' && !inIdentifier(i)) {\n var tagMatch = /^\\$[A-Za-z_][A-Za-z0-9_]*\\$|^\\$\\$/.exec(text.slice(i));\n if (tagMatch) {\n var tag = tagMatch[0];\n var close = text.indexOf(tag, i + tag.length);\n if (close === -1) return { ok: false, text: out };\n i = close + tag.length;\n out += ' ';\n continue;\n }\n }\n // E'\u2026' understands backslash escapes; a plain '\u2026' doubles the quote.\n if (ch === \"'\" || ((ch === 'E' || ch === 'e') && next === \"'\" && !inIdentifier(i))) {\n var escapes = ch !== \"'\";\n if (escapes) i++;\n i++;\n var closed = false;\n while (i < text.length) {\n if (escapes && text[i] === '\\\\') { i += 2; continue; }\n if (text[i] === \"'\" && text[i + 1] === \"'\") { i += 2; continue; }\n if (text[i] === \"'\") { i++; closed = true; break; }\n i++;\n }\n if (!closed) return { ok: false, text: out };\n out += ' ';\n continue;\n }\n out += ch;\n i++;\n }\n return { ok: true, text: out };\n}\n\nfunction __taNormalizeTable(tableName) {\n var name = String(tableName || '').trim().toLowerCase();\n var parts = name.split('.');\n return parts[parts.length - 1].replace(/\"/g, '');\n}\n\n// An unreadable statement, or one that can reach a table without naming it,\n// counts as mentioning EVERY protected table: fail closed rather than let a\n// spelling this scan cannot see decide it is harmless.\nfunction __taSqlMentionsTable(sql, table) {\n var cleaned = __taCleanSql(sql);\n if (!cleaned.ok || __TA_OPAQUE_SQL_RE.test(cleaned.text)) return true;\n var pattern = new RegExp('(?:^|[^A-Za-z0-9_])\"?' + table + '\"?(?![A-Za-z0-9_])', 'i');\n return pattern.test(cleaned.text);\n}\n\nfunction __taIsCredentialTable(tableName) {\n return __TA_CREDENTIAL_TABLES.indexOf(__taNormalizeTable(tableName)) !== -1;\n}\n\n// Credentials never travel in a row: a column is dropped by name, however the\n// caller spelled it in its projection.\nfunction __taWithoutCredentials(rows, tableName) {\n if (!Array.isArray(rows)) return rows;\n var dropped = tableName && __taIsCredentialTable(tableName)\n ? __TA_CREDENTIAL_COLUMNS\n : tableName ? [] : __TA_UNAMBIGUOUS_CREDENTIAL_COLUMNS;\n if (dropped.length === 0) return rows;\n return rows.map(function(row) {\n if (!row || typeof row !== 'object') return row;\n var copy = {};\n Object.keys(row).forEach(function(key) {\n if (dropped.indexOf(String(key).toLowerCase()) === -1) copy[key] = row[key];\n });\n return copy;\n });\n}\n\nfunction __taForbid(table) {\n var err = new Error('Forbidden: ' + table + ' is only accessible to server-side workflow nodes');\n err.status = 403;\n throw err;\n}\n\nfunction __taAssertSqlTextAllowed(sql) {\n var text = String(sql || '');\n // A statement can hand back a whole row (`SELECT u FROM users u`,\n // `row_to_json(u)`) without naming a credential column, so the table itself\n // is out of reach of anything but the server's own calls.\n for (var ci = 0; ci < __TA_CREDENTIAL_TABLES.length; ci++) {\n if (__taSqlMentionsTable(text, __TA_CREDENTIAL_TABLES[ci])) __taForbid(__TA_CREDENTIAL_TABLES[ci]);\n }\n for (var pi = 0; pi < __TA_PROTECTED_TABLES.length; pi++) {\n if (__taSqlMentionsTable(text, __TA_PROTECTED_TABLES[pi])) __taForbid(__TA_PROTECTED_TABLES[pi]);\n }\n // A feed table has exactly one browser-readable shape, and a statement the\n // caller wrote is not it.\n for (var fi = 0; fi < __TA_FEED_TABLES.length; fi++) {\n if (__taSqlMentionsTable(text, __TA_FEED_TABLES[fi])) __taForbid(__TA_FEED_TABLES[fi]);\n }\n var cleaned = __taCleanSql(text);\n // Unreadable, or a write: either way the write-protected tables are refused.\n if (cleaned.ok && !__TA_SQL_WRITE_RE.test(cleaned.text)) return;\n for (var wi = 0; wi < __TA_WRITE_PROTECTED_TABLES.length; wi++) {\n if (__taSqlMentionsTable(text, __TA_WRITE_PROTECTED_TABLES[wi])) __taForbid(__TA_WRITE_PROTECTED_TABLES[wi]);\n }\n}\n\nfunction __taRoleOf(token) {\n if (!token || typeof token !== 'object') return null;\n if (typeof token.role === 'string') return token.role;\n if (typeof token.roleName === 'string') return token.roleName;\n if (Array.isArray(token.roles) && typeof token.roles[0] === 'string') return token.roles[0];\n return null;\n}\n\n// The tables a read of `tableName` (or the raw statement) touches that this\n// route family may not serve to a browser: the protected ones, and the feed\n// tables, whose one browser-readable projection lives on the data API instead.\nvar __TA_ROUTE_PROTECTED_TABLES = __TA_PROTECTED_TABLES.concat(__TA_FEED_TABLES);\n\nfunction __taProtectedTablesRead(tableName, rawQuery) {\n var touched = [];\n var bare = __taNormalizeTable(tableName);\n if (__TA_ROUTE_PROTECTED_TABLES.indexOf(bare) !== -1) touched.push(bare);\n if (typeof rawQuery === 'string' && rawQuery.length > 0) {\n for (var i = 0; i < __TA_ROUTE_PROTECTED_TABLES.length; i++) {\n if (touched.indexOf(__TA_ROUTE_PROTECTED_TABLES[i]) === -1 && __taSqlMentionsTable(rawQuery, __TA_ROUTE_PROTECTED_TABLES[i])) {\n touched.push(__TA_ROUTE_PROTECTED_TABLES[i]);\n }\n }\n }\n return touched;\n}\n\n// The projection an untrusted caller gets of a feed table, or null when the\n// table has no feed or the caller is a server-side one reading it whole.\nfunction __taPublicFeed(req, tableName) {\n if (__taIsInternalRequest(req)) return null;\n var feed = __TA_PUBLIC_FEED_TABLES[__taNormalizeTable(tableName)];\n return feed || null;\n}\n\n// A feed is filtered and sorted only by the columns it serves: a condition on\n// any other column (or an expression over one) would answer for it.\nfunction __taAssertFeedFields(tableName, feed, fields) {\n for (var i = 0; i < fields.length; i++) {\n if (!fields[i]) continue;\n var column = String(fields[i]).trim().replace(/\"/g, '').toLowerCase();\n if (feed.columns.indexOf(column) === -1) __taForbid(__taNormalizeTable(tableName) + '.' + fields[i]);\n }\n}\n\n// null when the read may proceed, else { status, message }.\nasync function __taGuardRead(req, tableName, rawQuery) {\n if (typeof rawQuery === 'string' && rawQuery.length > 0 && __taBrowserIsReadOnly(req)) {\n return { status: 403, message: 'Forbidden: raw queries are only available to server-side calls' };\n }\n var touched = __taProtectedTablesRead(tableName, rawQuery);\n if (touched.length === 0) return null;\n if (__taIsInternalRequest(req)) return null;\n var token = null;\n try {\n token = await __tqSessionToken(req);\n } catch (e) {\n token = null;\n }\n if (!token) return { status: 401, message: 'Unauthenticated' };\n var role = __taRoleOf(token);\n if (!role || __TA_TRUSTED_READER_ROLES.indexOf(role) === -1) {\n return { status: 403, message: 'Forbidden: ' + touched[0] + ' is only readable by the store administrators' };\n }\n return null;\n}\n");
|
|
236
|
+
};
|
|
237
|
+
exports.generateTableAccessHelperCode = generateTableAccessHelperCode;
|
|
238
|
+
//# sourceMappingURL=table-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-access.js","sourceRoot":"","sources":["../../../src/utils/table-access.ts"],"names":[],"mappings":"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"}
|