@10x-media/impersonation 0.1.0-beta.0

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.
Files changed (163) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +49 -0
  4. package/dist/access/filterTargets.js +51 -0
  5. package/dist/access/filterTargets.js.map +1 -0
  6. package/dist/auth/cookies.js +49 -0
  7. package/dist/auth/cookies.js.map +1 -0
  8. package/dist/auth/expiryCookies.js +59 -0
  9. package/dist/auth/expiryCookies.js.map +1 -0
  10. package/dist/auth/issue.js +126 -0
  11. package/dist/auth/issue.js.map +1 -0
  12. package/dist/auth/mode.js +69 -0
  13. package/dist/auth/mode.js.map +1 -0
  14. package/dist/auth/origin.js +55 -0
  15. package/dist/auth/origin.js.map +1 -0
  16. package/dist/auth/revoke.js +24 -0
  17. package/dist/auth/revoke.js.map +1 -0
  18. package/dist/auth/tenantCookie.js +74 -0
  19. package/dist/auth/tenantCookie.js.map +1 -0
  20. package/dist/endpoints/codes.d.ts +6 -0
  21. package/dist/endpoints/current.js +66 -0
  22. package/dist/endpoints/current.js.map +1 -0
  23. package/dist/endpoints/exit.js +242 -0
  24. package/dist/endpoints/exit.js.map +1 -0
  25. package/dist/endpoints/shared.js +76 -0
  26. package/dist/endpoints/shared.js.map +1 -0
  27. package/dist/endpoints/start.js +332 -0
  28. package/dist/endpoints/start.js.map +1 -0
  29. package/dist/endpoints/terminate.js +73 -0
  30. package/dist/endpoints/terminate.js.map +1 -0
  31. package/dist/exports/client.d.ts +9 -0
  32. package/dist/exports/client.js +10 -0
  33. package/dist/exports/i18n.d.ts +3 -0
  34. package/dist/exports/i18n.js +3 -0
  35. package/dist/exports/rsc.d.ts +4 -0
  36. package/dist/exports/rsc.js +4 -0
  37. package/dist/exports/types.d.ts +3 -0
  38. package/dist/exports/types.js +1 -0
  39. package/dist/getImpersonation.d.ts +49 -0
  40. package/dist/getImpersonation.js +78 -0
  41. package/dist/getImpersonation.js.map +1 -0
  42. package/dist/ids.js +27 -0
  43. package/dist/ids.js.map +1 -0
  44. package/dist/index.d.ts +20 -0
  45. package/dist/index.js +55 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/plugin/assertTargets.js +23 -0
  48. package/dist/plugin/assertTargets.js.map +1 -0
  49. package/dist/plugin/constants.d.ts +6 -0
  50. package/dist/plugin/constants.js +20 -0
  51. package/dist/plugin/constants.js.map +1 -0
  52. package/dist/plugin/decorateAuth.js +99 -0
  53. package/dist/plugin/decorateAuth.js.map +1 -0
  54. package/dist/plugin/lookup.js +23 -0
  55. package/dist/plugin/lookup.js.map +1 -0
  56. package/dist/plugin/normalizeOptions.js +83 -0
  57. package/dist/plugin/normalizeOptions.js.map +1 -0
  58. package/dist/plugin/registerAdmin.js +49 -0
  59. package/dist/plugin/registerAdmin.js.map +1 -0
  60. package/dist/plugin/registerCollection.js +183 -0
  61. package/dist/plugin/registerCollection.js.map +1 -0
  62. package/dist/plugin/registerEndpoints.js +19 -0
  63. package/dist/plugin/registerEndpoints.js.map +1 -0
  64. package/dist/plugin/registerHooks.js +59 -0
  65. package/dist/plugin/registerHooks.js.map +1 -0
  66. package/dist/plugin/registerRetention.js +57 -0
  67. package/dist/plugin/registerRetention.js.map +1 -0
  68. package/dist/plugin/registerTranslations.js +19 -0
  69. package/dist/plugin/registerTranslations.js.map +1 -0
  70. package/dist/plugin/registry.js +11 -0
  71. package/dist/plugin/registry.js.map +1 -0
  72. package/dist/plugin/startable.js +21 -0
  73. package/dist/plugin/startable.js.map +1 -0
  74. package/dist/session/close.js +65 -0
  75. package/dist/session/close.js.map +1 -0
  76. package/dist/session/closeStale.d.ts +11 -0
  77. package/dist/session/closeStale.js +65 -0
  78. package/dist/session/closeStale.js.map +1 -0
  79. package/dist/session/locks.js +32 -0
  80. package/dist/session/locks.js.map +1 -0
  81. package/dist/session/resolve.js +91 -0
  82. package/dist/session/resolve.js.map +1 -0
  83. package/dist/translations/ar.js +52 -0
  84. package/dist/translations/ar.js.map +1 -0
  85. package/dist/translations/de.js +52 -0
  86. package/dist/translations/de.js.map +1 -0
  87. package/dist/translations/en.js +52 -0
  88. package/dist/translations/en.js.map +1 -0
  89. package/dist/translations/es.js +52 -0
  90. package/dist/translations/es.js.map +1 -0
  91. package/dist/translations/fr.js +52 -0
  92. package/dist/translations/fr.js.map +1 -0
  93. package/dist/translations/id.js +52 -0
  94. package/dist/translations/id.js.map +1 -0
  95. package/dist/translations/index.d.ts +24 -0
  96. package/dist/translations/index.js +49 -0
  97. package/dist/translations/index.js.map +1 -0
  98. package/dist/translations/keys.d.ts +56 -0
  99. package/dist/translations/keys.js +56 -0
  100. package/dist/translations/keys.js.map +1 -0
  101. package/dist/translations/ko.js +52 -0
  102. package/dist/translations/ko.js.map +1 -0
  103. package/dist/translations/lookup.js +17 -0
  104. package/dist/translations/lookup.js.map +1 -0
  105. package/dist/translations/pt.js +52 -0
  106. package/dist/translations/pt.js.map +1 -0
  107. package/dist/translations/ru.js +52 -0
  108. package/dist/translations/ru.js.map +1 -0
  109. package/dist/translations/server.js +13 -0
  110. package/dist/translations/server.js.map +1 -0
  111. package/dist/translations/uk.js +52 -0
  112. package/dist/translations/uk.js.map +1 -0
  113. package/dist/translations/useTranslation.js +12 -0
  114. package/dist/translations/useTranslation.js.map +1 -0
  115. package/dist/translations/zh.js +52 -0
  116. package/dist/translations/zh.js.map +1 -0
  117. package/dist/types.d.ts +214 -0
  118. package/dist/types.js +11 -0
  119. package/dist/types.js.map +1 -0
  120. package/dist/ui/EndSessionMenuItem.d.ts +5 -0
  121. package/dist/ui/EndSessionMenuItem.js +45 -0
  122. package/dist/ui/EndSessionMenuItem.js.map +1 -0
  123. package/dist/ui/ImpersonateIcon.js +20 -0
  124. package/dist/ui/ImpersonateIcon.js.map +1 -0
  125. package/dist/ui/ImpersonationAction.d.ts +10 -0
  126. package/dist/ui/ImpersonationAction.js +48 -0
  127. package/dist/ui/ImpersonationAction.js.map +1 -0
  128. package/dist/ui/ImpersonationBar.d.ts +36 -0
  129. package/dist/ui/ImpersonationBar.js +357 -0
  130. package/dist/ui/ImpersonationBar.js.map +1 -0
  131. package/dist/ui/ImpersonationConfig.js +25 -0
  132. package/dist/ui/ImpersonationConfig.js.map +1 -0
  133. package/dist/ui/ImpersonationDocumentButton.d.ts +5 -0
  134. package/dist/ui/ImpersonationDocumentButton.js +85 -0
  135. package/dist/ui/ImpersonationDocumentButton.js.map +1 -0
  136. package/dist/ui/ImpersonationFrontendBanner.d.ts +41 -0
  137. package/dist/ui/ImpersonationFrontendBanner.js +58 -0
  138. package/dist/ui/ImpersonationFrontendBanner.js.map +1 -0
  139. package/dist/ui/ImpersonationFrontendExit.d.ts +17 -0
  140. package/dist/ui/ImpersonationFrontendExit.js +45 -0
  141. package/dist/ui/ImpersonationFrontendExit.js.map +1 -0
  142. package/dist/ui/ImpersonationProvider.d.ts +16 -0
  143. package/dist/ui/ImpersonationProvider.js +127 -0
  144. package/dist/ui/ImpersonationProvider.js.map +1 -0
  145. package/dist/ui/ImpersonationSwitcher.d.ts +21 -0
  146. package/dist/ui/ImpersonationSwitcher.js +251 -0
  147. package/dist/ui/ImpersonationSwitcher.js.map +1 -0
  148. package/dist/ui/ImpersonationUserCard.d.ts +31 -0
  149. package/dist/ui/ImpersonationUserCard.js +51 -0
  150. package/dist/ui/ImpersonationUserCard.js.map +1 -0
  151. package/dist/ui/StartConfirmModal.d.ts +28 -0
  152. package/dist/ui/StartConfirmModal.js +65 -0
  153. package/dist/ui/StartConfirmModal.js.map +1 -0
  154. package/dist/ui/api.js +66 -0
  155. package/dist/ui/api.js.map +1 -0
  156. package/dist/ui/barPlacement.js +107 -0
  157. package/dist/ui/barPlacement.js.map +1 -0
  158. package/dist/ui/frontendBanner.css +22 -0
  159. package/dist/ui/impersonation.css +244 -0
  160. package/dist/ui/useImpersonation.d.ts +43 -0
  161. package/dist/ui/useImpersonation.js +36 -0
  162. package/dist/ui/useImpersonation.js.map +1 -0
  163. package/package.json +126 -0
@@ -0,0 +1,74 @@
1
+ import { expireCookie, writeCookie } from "./cookies.js";
2
+ import { defaultClearOnSwitch } from "../plugin/constants.js";
3
+ import { parseCookies } from "payload/shared";
4
+ //#region src/auth/tenantCookie.ts
5
+ /** Same charset Payload tenant ids use (ObjectId, int, uuid). Rejects cookie injection. */
6
+ const TENANT_COOKIE_VALUE = /^[\w.-]{1,128}$/;
7
+ const tenantCookieName = (cookiePrefix) => defaultClearOnSwitch(cookiePrefix)[0];
8
+ const isManagedTenantCookie = (clearOnSwitch, cookiePrefix) => clearOnSwitch.includes(tenantCookieName(cookiePrefix));
9
+ const clearOnSwitchWithoutTenant = (clearOnSwitch, cookiePrefix) => clearOnSwitch.filter((name) => name !== tenantCookieName(cookiePrefix));
10
+ const readTenantCookie = (headers, cookiePrefix) => {
11
+ const raw = parseCookies(headers).get(tenantCookieName(cookiePrefix));
12
+ return raw && TENANT_COOKIE_VALUE.test(raw) ? raw : void 0;
13
+ };
14
+ const setTenantCookie = ({ authConfig, cookiePrefix, value }) => writeCookie({
15
+ authConfig,
16
+ httpOnly: false,
17
+ name: tenantCookieName(cookiePrefix),
18
+ value
19
+ });
20
+ const expireTenantCookie = ({ authConfig, cookiePrefix }) => expireCookie({
21
+ authConfig,
22
+ httpOnly: false,
23
+ name: tenantCookieName(cookiePrefix)
24
+ });
25
+ const idsFrom = (value) => {
26
+ if (value == null) return [];
27
+ if (Array.isArray(value)) return value.flatMap(idsFrom);
28
+ if (typeof value === "string" || typeof value === "number") {
29
+ const id = String(value);
30
+ return TENANT_COOKIE_VALUE.test(id) ? [id] : [];
31
+ }
32
+ if (typeof value === "object") {
33
+ if ("id" in value) return idsFrom(value.id);
34
+ if ("value" in value) return idsFrom(value.value);
35
+ }
36
+ return [];
37
+ };
38
+ /**
39
+ * `@payloadcms/plugin-multi-tenant` puts assignments on `tenants` (hasMany) and
40
+ * sometimes a single `tenant`. Only a unique readable id is safe to write as the selector.
41
+ */
42
+ const uniqueAssignedTenantId = (user) => {
43
+ const ids = [...new Set([...idsFrom(user.tenants), ...idsFrom(user.tenant)])];
44
+ return ids.length === 1 ? ids[0] : void 0;
45
+ };
46
+ const startTenantCookies = ({ authConfig, cookiePrefix, mode, options, target }) => {
47
+ if (!isManagedTenantCookie(options.cookies.clearOnSwitch, cookiePrefix) || mode === "parallel") return [];
48
+ const assigned = uniqueAssignedTenantId(target);
49
+ if (assigned) return [setTenantCookie({
50
+ authConfig,
51
+ cookiePrefix,
52
+ value: assigned
53
+ })];
54
+ return [expireTenantCookie({
55
+ authConfig,
56
+ cookiePrefix
57
+ })];
58
+ };
59
+ const exitTenantCookies = ({ authConfig, cookiePrefix, mode, options, snapshot }) => {
60
+ if (!isManagedTenantCookie(options.cookies.clearOnSwitch, cookiePrefix) || mode === "parallel") return [];
61
+ if (snapshot && TENANT_COOKIE_VALUE.test(snapshot)) return [setTenantCookie({
62
+ authConfig,
63
+ cookiePrefix,
64
+ value: snapshot
65
+ })];
66
+ return [expireTenantCookie({
67
+ authConfig,
68
+ cookiePrefix
69
+ })];
70
+ };
71
+ //#endregion
72
+ export { clearOnSwitchWithoutTenant, exitTenantCookies, readTenantCookie, startTenantCookies };
73
+
74
+ //# sourceMappingURL=tenantCookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenantCookie.js","names":[],"sources":["../../src/auth/tenantCookie.ts"],"sourcesContent":["import type { SanitizedCollectionConfig } from 'payload'\nimport { parseCookies } from 'payload/shared'\n\nimport { defaultClearOnSwitch } from '../plugin/constants'\nimport type { ImpersonationMode, ResolvedOptions } from '../types'\nimport { expireCookie, writeCookie } from './cookies'\n\ntype AuthConfig = SanitizedCollectionConfig['auth']\n\n/** Same charset Payload tenant ids use (ObjectId, int, uuid). Rejects cookie injection. */\nconst TENANT_COOKIE_VALUE = /^[\\w.-]{1,128}$/\n\nexport const tenantCookieName = (cookiePrefix: string): string =>\n\tdefaultClearOnSwitch(cookiePrefix)[0] as string\n\nexport const isManagedTenantCookie = (clearOnSwitch: string[], cookiePrefix: string): boolean =>\n\tclearOnSwitch.includes(tenantCookieName(cookiePrefix))\n\nexport const clearOnSwitchWithoutTenant = (\n\tclearOnSwitch: string[],\n\tcookiePrefix: string\n): string[] => clearOnSwitch.filter((name) => name !== tenantCookieName(cookiePrefix))\n\nexport const readTenantCookie = (headers: Headers, cookiePrefix: string): string | undefined => {\n\tconst raw = parseCookies(headers).get(tenantCookieName(cookiePrefix))\n\treturn raw && TENANT_COOKIE_VALUE.test(raw) ? raw : undefined\n}\n\nexport const setTenantCookie = ({\n\tauthConfig,\n\tcookiePrefix,\n\tvalue,\n}: {\n\tauthConfig: AuthConfig\n\tcookiePrefix: string\n\tvalue: string\n}): string =>\n\twriteCookie({\n\t\tauthConfig,\n\t\thttpOnly: false,\n\t\tname: tenantCookieName(cookiePrefix),\n\t\tvalue,\n\t})\n\nexport const expireTenantCookie = ({\n\tauthConfig,\n\tcookiePrefix,\n}: {\n\tauthConfig: AuthConfig\n\tcookiePrefix: string\n}): string =>\n\texpireCookie({\n\t\tauthConfig,\n\t\thttpOnly: false,\n\t\tname: tenantCookieName(cookiePrefix),\n\t})\n\nconst idsFrom = (value: unknown): string[] => {\n\tif (value == null) {\n\t\treturn []\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.flatMap(idsFrom)\n\t}\n\tif (typeof value === 'string' || typeof value === 'number') {\n\t\tconst id = String(value)\n\t\treturn TENANT_COOKIE_VALUE.test(id) ? [id] : []\n\t}\n\tif (typeof value === 'object') {\n\t\tif ('id' in value) {\n\t\t\treturn idsFrom((value as { id: unknown }).id)\n\t\t}\n\t\tif ('value' in value) {\n\t\t\treturn idsFrom((value as { value: unknown }).value)\n\t\t}\n\t}\n\treturn []\n}\n\n/**\n * `@payloadcms/plugin-multi-tenant` puts assignments on `tenants` (hasMany) and\n * sometimes a single `tenant`. Only a unique readable id is safe to write as the selector.\n */\nexport const uniqueAssignedTenantId = (user: Record<string, unknown>): string | undefined => {\n\tconst ids = [...new Set([...idsFrom(user.tenants), ...idsFrom(user.tenant)])]\n\treturn ids.length === 1 ? ids[0] : undefined\n}\n\nexport const startTenantCookies = ({\n\tauthConfig,\n\tcookiePrefix,\n\tmode,\n\toptions,\n\ttarget,\n}: {\n\tauthConfig: AuthConfig\n\tcookiePrefix: string\n\tmode: ImpersonationMode\n\toptions: ResolvedOptions\n\ttarget: Record<string, unknown>\n}): string[] => {\n\tif (!isManagedTenantCookie(options.cookies.clearOnSwitch, cookiePrefix) || mode === 'parallel') {\n\t\treturn []\n\t}\n\tconst assigned = uniqueAssignedTenantId(target)\n\tif (assigned) {\n\t\treturn [setTenantCookie({ authConfig, cookiePrefix, value: assigned })]\n\t}\n\treturn [expireTenantCookie({ authConfig, cookiePrefix })]\n}\n\nexport const exitTenantCookies = ({\n\tauthConfig,\n\tcookiePrefix,\n\tmode,\n\toptions,\n\tsnapshot,\n}: {\n\tauthConfig: AuthConfig\n\tcookiePrefix: string\n\tmode: ImpersonationMode\n\toptions: ResolvedOptions\n\tsnapshot?: null | string\n}): string[] => {\n\tif (!isManagedTenantCookie(options.cookies.clearOnSwitch, cookiePrefix) || mode === 'parallel') {\n\t\treturn []\n\t}\n\tif (snapshot && TENANT_COOKIE_VALUE.test(snapshot)) {\n\t\treturn [setTenantCookie({ authConfig, cookiePrefix, value: snapshot })]\n\t}\n\treturn [expireTenantCookie({ authConfig, cookiePrefix })]\n}\n"],"mappings":";;;;;AAUA,MAAM,sBAAsB;AAE5B,MAAa,oBAAoB,iBAChC,qBAAqB,YAAY,EAAE;AAEpC,MAAa,yBAAyB,eAAyB,iBAC9D,cAAc,SAAS,iBAAiB,YAAY,CAAC;AAEtD,MAAa,8BACZ,eACA,iBACc,cAAc,QAAQ,SAAS,SAAS,iBAAiB,YAAY,CAAC;AAErF,MAAa,oBAAoB,SAAkB,iBAA6C;CAC/F,MAAM,MAAM,aAAa,OAAO,EAAE,IAAI,iBAAiB,YAAY,CAAC;CACpE,OAAO,OAAO,oBAAoB,KAAK,GAAG,IAAI,MAAM,KAAA;AACrD;AAEA,MAAa,mBAAmB,EAC/B,YACA,cACA,YAMA,YAAY;CACX;CACA,UAAU;CACV,MAAM,iBAAiB,YAAY;CACnC;AACD,CAAC;AAEF,MAAa,sBAAsB,EAClC,YACA,mBAKA,aAAa;CACZ;CACA,UAAU;CACV,MAAM,iBAAiB,YAAY;AACpC,CAAC;AAEF,MAAM,WAAW,UAA6B;CAC7C,IAAI,SAAS,MACZ,OAAO,CAAC;CAET,IAAI,MAAM,QAAQ,KAAK,GACtB,OAAO,MAAM,QAAQ,OAAO;CAE7B,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;EAC3D,MAAM,KAAK,OAAO,KAAK;EACvB,OAAO,oBAAoB,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;CAC/C;CACA,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,QAAQ,OACX,OAAO,QAAS,MAA0B,EAAE;EAE7C,IAAI,WAAW,OACd,OAAO,QAAS,MAA6B,KAAK;CAEpD;CACA,OAAO,CAAC;AACT;;;;;AAMA,MAAa,0BAA0B,SAAsD;CAC5F,MAAM,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,QAAQ,KAAK,OAAO,GAAG,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;CAC5E,OAAO,IAAI,WAAW,IAAI,IAAI,KAAK,KAAA;AACpC;AAEA,MAAa,sBAAsB,EAClC,YACA,cACA,MACA,SACA,aAOe;CACf,IAAI,CAAC,sBAAsB,QAAQ,QAAQ,eAAe,YAAY,KAAK,SAAS,YACnF,OAAO,CAAC;CAET,MAAM,WAAW,uBAAuB,MAAM;CAC9C,IAAI,UACH,OAAO,CAAC,gBAAgB;EAAE;EAAY;EAAc,OAAO;CAAS,CAAC,CAAC;CAEvE,OAAO,CAAC,mBAAmB;EAAE;EAAY;CAAa,CAAC,CAAC;AACzD;AAEA,MAAa,qBAAqB,EACjC,YACA,cACA,MACA,SACA,eAOe;CACf,IAAI,CAAC,sBAAsB,QAAQ,QAAQ,eAAe,YAAY,KAAK,SAAS,YACnF,OAAO,CAAC;CAET,IAAI,YAAY,oBAAoB,KAAK,QAAQ,GAChD,OAAO,CAAC,gBAAgB;EAAE;EAAY;EAAc,OAAO;CAAS,CAAC,CAAC;CAEvE,OAAO,CAAC,mBAAmB;EAAE;EAAY;CAAa,CAAC,CAAC;AACzD"}
@@ -0,0 +1,6 @@
1
+ //#region src/endpoints/codes.d.ts
2
+ declare const FAILURE_CODES: readonly ["alreadyImpersonating", "failed", "forbidden", "impersonatorGone", "impersonatorSessionExpired", "invalidBody", "notImpersonating", "origin", "reasonRequired", "selfTarget", "targetNotFound", "targetTrashed", "targetUnverified", "unsupportedAuth", "unsupportedCollection"];
3
+ type FailureCode = (typeof FAILURE_CODES)[number];
4
+ //#endregion
5
+ export { FailureCode };
6
+ //# sourceMappingURL=codes.d.ts.map
@@ -0,0 +1,66 @@
1
+ import { isPastAbsoluteExpiry, resolveCurrent } from "../session/resolve.js";
2
+ import { absoluteExpiryCookies } from "../auth/expiryCookies.js";
3
+ import { closeAndRevoke } from "../session/close.js";
4
+ import { boundSid } from "../types.js";
5
+ import { statusFromRow } from "../getImpersonation.js";
6
+ import { getOptions, json } from "./shared.js";
7
+ //#region src/endpoints/current.ts
8
+ const currentHandler = async (req) => {
9
+ const options = getOptions(req);
10
+ const user = req.user;
11
+ const sid = boundSid(user, options.session.binding);
12
+ if (!user || !sid) return json({
13
+ body: { active: false },
14
+ req,
15
+ status: 200
16
+ });
17
+ const row = await resolveCurrent({
18
+ headers: req.headers,
19
+ options,
20
+ payload: req.payload,
21
+ req,
22
+ sid
23
+ });
24
+ if (!row || isPastAbsoluteExpiry(row)) {
25
+ if (row && isPastAbsoluteExpiry(row)) {
26
+ await closeAndRevoke({
27
+ endedBy: "expired",
28
+ options,
29
+ payload: req.payload,
30
+ record: row,
31
+ req
32
+ });
33
+ return json({
34
+ body: { active: false },
35
+ cookies: await absoluteExpiryCookies({
36
+ options,
37
+ payload: req.payload,
38
+ row,
39
+ sid,
40
+ user
41
+ }),
42
+ req,
43
+ status: 200
44
+ });
45
+ }
46
+ return json({
47
+ body: { active: false },
48
+ req,
49
+ status: 200
50
+ });
51
+ }
52
+ return json({
53
+ body: statusFromRow(row, sid),
54
+ req,
55
+ status: 200
56
+ });
57
+ };
58
+ const currentEndpoint = (path) => ({
59
+ handler: currentHandler,
60
+ method: "get",
61
+ path
62
+ });
63
+ //#endregion
64
+ export { currentEndpoint };
65
+
66
+ //# sourceMappingURL=current.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current.js","names":[],"sources":["../../src/endpoints/current.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport { absoluteExpiryCookies } from '../auth/expiryCookies'\nimport { statusFromRow } from '../getImpersonation'\nimport { closeAndRevoke } from '../session/close'\nimport { isPastAbsoluteExpiry, resolveCurrent } from '../session/resolve'\nimport { boundSid } from '../types'\nimport { getOptions, json } from './shared'\n\nexport const currentHandler = async (req: PayloadRequest): Promise<Response> => {\n\tconst options = getOptions(req)\n\tconst user = req.user\n\tconst sid = boundSid(user, options.session.binding)\n\tif (!user || !sid) {\n\t\treturn json({ body: { active: false }, req, status: 200 })\n\t}\n\n\tconst row = await resolveCurrent({\n\t\theaders: req.headers,\n\t\toptions,\n\t\tpayload: req.payload,\n\t\treq,\n\t\tsid,\n\t})\n\tif (!row || isPastAbsoluteExpiry(row)) {\n\t\tif (row && isPastAbsoluteExpiry(row)) {\n\t\t\tawait closeAndRevoke({\n\t\t\t\tendedBy: 'expired',\n\t\t\t\toptions,\n\t\t\t\tpayload: req.payload,\n\t\t\t\trecord: row,\n\t\t\t\treq,\n\t\t\t})\n\t\t\tconst cookies = await absoluteExpiryCookies({\n\t\t\t\toptions,\n\t\t\t\tpayload: req.payload,\n\t\t\t\trow,\n\t\t\t\tsid,\n\t\t\t\tuser,\n\t\t\t})\n\t\t\treturn json({ body: { active: false }, cookies, req, status: 200 })\n\t\t}\n\t\treturn json({ body: { active: false }, req, status: 200 })\n\t}\n\n\treturn json({\n\t\tbody: statusFromRow(row, sid),\n\t\treq,\n\t\tstatus: 200,\n\t})\n}\n\nexport const currentEndpoint = (path: string): Endpoint => ({\n\thandler: currentHandler,\n\tmethod: 'get',\n\tpath,\n})\n"],"mappings":";;;;;;;AASA,MAAa,iBAAiB,OAAO,QAA2C;CAC/E,MAAM,UAAU,WAAW,GAAG;CAC9B,MAAM,OAAO,IAAI;CACjB,MAAM,MAAM,SAAS,MAAM,QAAQ,QAAQ,OAAO;CAClD,IAAI,CAAC,QAAQ,CAAC,KACb,OAAO,KAAK;EAAE,MAAM,EAAE,QAAQ,MAAM;EAAG;EAAK,QAAQ;CAAI,CAAC;CAG1D,MAAM,MAAM,MAAM,eAAe;EAChC,SAAS,IAAI;EACb;EACA,SAAS,IAAI;EACb;EACA;CACD,CAAC;CACD,IAAI,CAAC,OAAO,qBAAqB,GAAG,GAAG;EACtC,IAAI,OAAO,qBAAqB,GAAG,GAAG;GACrC,MAAM,eAAe;IACpB,SAAS;IACT;IACA,SAAS,IAAI;IACb,QAAQ;IACR;GACD,CAAC;GAQD,OAAO,KAAK;IAAE,MAAM,EAAE,QAAQ,MAAM;IAAG,SAAA,MAPjB,sBAAsB;KAC3C;KACA,SAAS,IAAI;KACb;KACA;KACA;IACD,CAAC;IAC+C;IAAK,QAAQ;GAAI,CAAC;EACnE;EACA,OAAO,KAAK;GAAE,MAAM,EAAE,QAAQ,MAAM;GAAG;GAAK,QAAQ;EAAI,CAAC;CAC1D;CAEA,OAAO,KAAK;EACX,MAAM,cAAc,KAAK,GAAG;EAC5B;EACA,QAAQ;CACT,CAAC;AACF;AAEA,MAAa,mBAAmB,UAA4B;CAC3D,SAAS;CACT,QAAQ;CACR;AACD"}
@@ -0,0 +1,242 @@
1
+ import { collectionBySlug } from "../ids.js";
2
+ import { findOpenBySid, relationOf } from "../session/resolve.js";
3
+ import { expireCookie, expireCookies, expirePayloadCookie } from "../auth/cookies.js";
4
+ import { isolatedCookieNameFor } from "../auth/mode.js";
5
+ import { closeAndRevoke } from "../session/close.js";
6
+ import { boundSid } from "../types.js";
7
+ import { callerSid, fail, getOptions, isApiKeyStrategy, json, prepareMutation, rejectGate } from "./shared.js";
8
+ import { resignSession } from "../auth/issue.js";
9
+ import { clearOnSwitchWithoutTenant, exitTenantCookies } from "../auth/tenantCookie.js";
10
+ import { releaseLocks } from "../session/locks.js";
11
+ //#region src/endpoints/exit.ts
12
+ const exitHandler = async (req) => {
13
+ const gate = await prepareMutation(req);
14
+ if (gate) return rejectGate(req, gate);
15
+ const options = getOptions(req);
16
+ const user = req.user;
17
+ if (!user) return fail({
18
+ error: "forbidden",
19
+ req,
20
+ status: 403
21
+ });
22
+ if (isApiKeyStrategy(user) || !callerSid(user)) return fail({
23
+ error: "unsupportedAuth",
24
+ req,
25
+ status: 403
26
+ });
27
+ const sid = boundSid(user, options.session.binding);
28
+ if (!sid) return fail({
29
+ error: "unsupportedAuth",
30
+ req,
31
+ status: 403
32
+ });
33
+ const row = await findOpenBySid({
34
+ options,
35
+ payload: req.payload,
36
+ req,
37
+ sid
38
+ });
39
+ if (!row) return fail({
40
+ error: "notImpersonating",
41
+ req,
42
+ status: 409
43
+ });
44
+ const impersonator = relationOf(row.impersonator);
45
+ const target = relationOf(row.target);
46
+ const authConfig = (target ? collectionBySlug(req.payload, target.collection) : void 0)?.config.auth ?? collectionBySlug(req.payload, req.payload.config.admin.user)?.config.auth;
47
+ if (!authConfig) return fail({
48
+ error: "failed",
49
+ req,
50
+ status: 500
51
+ });
52
+ const cookiePrefix = req.payload.config.cookiePrefix;
53
+ const extraClearOnSwitch = clearOnSwitchWithoutTenant(options.cookies.clearOnSwitch, cookiePrefix);
54
+ const expireHintAndClear = expireCookies({
55
+ authConfig,
56
+ cookiePrefix,
57
+ names: [...extraClearOnSwitch, options.hintCookieName]
58
+ });
59
+ if (!impersonator || !collectionBySlug(req.payload, impersonator.collection)) {
60
+ await closeAndRevoke({
61
+ endedBy: "impersonatorGone",
62
+ options,
63
+ payload: req.payload,
64
+ record: row,
65
+ req
66
+ });
67
+ await releaseLocks({
68
+ payload: req.payload,
69
+ record: row,
70
+ req
71
+ });
72
+ return fail({
73
+ cookies: [expirePayloadCookie({
74
+ authConfig,
75
+ cookiePrefix
76
+ }), ...expireHintAndClear],
77
+ error: "impersonatorGone",
78
+ req,
79
+ status: 409
80
+ });
81
+ }
82
+ let impersonatorDoc = null;
83
+ try {
84
+ impersonatorDoc = await req.payload.findByID({
85
+ id: impersonator.id,
86
+ collection: impersonator.collection,
87
+ depth: 0,
88
+ overrideAccess: true,
89
+ req
90
+ });
91
+ } catch {
92
+ impersonatorDoc = null;
93
+ }
94
+ if (!impersonatorDoc || impersonatorDoc.deletedAt) {
95
+ await closeAndRevoke({
96
+ endedBy: "impersonatorGone",
97
+ options,
98
+ payload: req.payload,
99
+ record: row,
100
+ req
101
+ });
102
+ await releaseLocks({
103
+ payload: req.payload,
104
+ record: row,
105
+ req
106
+ });
107
+ return fail({
108
+ cookies: [expirePayloadCookie({
109
+ authConfig,
110
+ cookiePrefix
111
+ }), ...expireHintAndClear],
112
+ error: "impersonatorGone",
113
+ req,
114
+ status: 409
115
+ });
116
+ }
117
+ if (row.mode === "parallel") {
118
+ const closed = await closeAndRevoke({
119
+ endedBy: "exit",
120
+ options,
121
+ payload: req.payload,
122
+ record: row,
123
+ req
124
+ });
125
+ await releaseLocks({
126
+ payload: req.payload,
127
+ record: closed,
128
+ req
129
+ });
130
+ let isolatedName;
131
+ if (target) {
132
+ const targetUser = await req.payload.db.findOne({
133
+ collection: target.collection,
134
+ req,
135
+ where: { id: { equals: target.id } }
136
+ });
137
+ if (targetUser) {
138
+ targetUser.collection = target.collection;
139
+ isolatedName = await isolatedCookieNameFor({
140
+ collection: target.collection,
141
+ payload: req.payload,
142
+ user: targetUser
143
+ });
144
+ }
145
+ }
146
+ return json({
147
+ body: { ok: true },
148
+ cookies: [
149
+ ...isolatedName ? [expireCookie({
150
+ authConfig,
151
+ name: isolatedName
152
+ })] : [],
153
+ expireCookie({
154
+ authConfig,
155
+ name: options.hintCookieName
156
+ }),
157
+ ...expireCookies({
158
+ authConfig,
159
+ cookiePrefix,
160
+ names: extraClearOnSwitch
161
+ })
162
+ ],
163
+ req,
164
+ status: 200
165
+ });
166
+ }
167
+ const restored = await resignSession({
168
+ collection: impersonator.collection,
169
+ payload: req.payload,
170
+ req,
171
+ sid: row.impersonatorSid,
172
+ userId: impersonator.id
173
+ });
174
+ if (!restored) {
175
+ await closeAndRevoke({
176
+ endedBy: "expired",
177
+ options,
178
+ payload: req.payload,
179
+ record: row,
180
+ req
181
+ });
182
+ await releaseLocks({
183
+ payload: req.payload,
184
+ record: row,
185
+ req
186
+ });
187
+ return fail({
188
+ cookies: [expirePayloadCookie({
189
+ authConfig,
190
+ cookiePrefix
191
+ }), ...expireHintAndClear],
192
+ error: "impersonatorSessionExpired",
193
+ req,
194
+ status: 409
195
+ });
196
+ }
197
+ const closed = await closeAndRevoke({
198
+ endedBy: "exit",
199
+ options,
200
+ payload: req.payload,
201
+ record: row,
202
+ req
203
+ });
204
+ await releaseLocks({
205
+ payload: req.payload,
206
+ record: closed,
207
+ req
208
+ });
209
+ return json({
210
+ body: { ok: true },
211
+ cookies: [
212
+ restored.cookie,
213
+ expireCookie({
214
+ authConfig,
215
+ name: options.hintCookieName
216
+ }),
217
+ ...exitTenantCookies({
218
+ authConfig,
219
+ cookiePrefix,
220
+ mode: closed.mode,
221
+ options,
222
+ snapshot: row.impersonatorTenantCookie
223
+ }),
224
+ ...expireCookies({
225
+ authConfig,
226
+ cookiePrefix,
227
+ names: extraClearOnSwitch
228
+ })
229
+ ],
230
+ req,
231
+ status: 200
232
+ });
233
+ };
234
+ const exitEndpoint = (path) => ({
235
+ handler: exitHandler,
236
+ method: "post",
237
+ path
238
+ });
239
+ //#endregion
240
+ export { exitEndpoint };
241
+
242
+ //# sourceMappingURL=exit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit.js","names":[],"sources":["../../src/endpoints/exit.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint, PayloadRequest } from 'payload'\n\nimport { expireCookie, expireCookies, expirePayloadCookie } from '../auth/cookies'\nimport { resignSession } from '../auth/issue'\nimport { isolatedCookieNameFor } from '../auth/mode'\nimport { clearOnSwitchWithoutTenant, exitTenantCookies } from '../auth/tenantCookie'\nimport { collectionBySlug } from '../ids'\nimport { closeAndRevoke } from '../session/close'\nimport { releaseLocks } from '../session/locks'\nimport { findOpenBySid, relationOf } from '../session/resolve'\nimport { boundSid } from '../types'\nimport {\n\tcallerSid,\n\tfail,\n\tgetOptions,\n\tisApiKeyStrategy,\n\tjson,\n\tprepareMutation,\n\trejectGate,\n} from './shared'\n\nexport const exitHandler = async (req: PayloadRequest): Promise<Response> => {\n\tconst gate = await prepareMutation(req)\n\tif (gate) {\n\t\treturn rejectGate(req, gate)\n\t}\n\n\tconst options = getOptions(req)\n\tconst user = req.user\n\tif (!user) {\n\t\treturn fail({ error: 'forbidden', req, status: 403 })\n\t}\n\tif (isApiKeyStrategy(user) || !callerSid(user)) {\n\t\treturn fail({ error: 'unsupportedAuth', req, status: 403 })\n\t}\n\n\tconst sid = boundSid(user, options.session.binding)\n\tif (!sid) {\n\t\treturn fail({ error: 'unsupportedAuth', req, status: 403 })\n\t}\n\n\tconst row = await findOpenBySid({ options, payload: req.payload, req, sid })\n\tif (!row) {\n\t\treturn fail({ error: 'notImpersonating', req, status: 409 })\n\t}\n\n\tconst impersonator = relationOf(row.impersonator)\n\tconst target = relationOf(row.target)\n\tconst targetCollection = target ? collectionBySlug(req.payload, target.collection) : undefined\n\tconst authConfig =\n\t\ttargetCollection?.config.auth ??\n\t\tcollectionBySlug(req.payload, req.payload.config.admin.user)?.config.auth\n\tif (!authConfig) {\n\t\treturn fail({ error: 'failed', req, status: 500 })\n\t}\n\n\tconst cookiePrefix = req.payload.config.cookiePrefix\n\tconst extraClearOnSwitch = clearOnSwitchWithoutTenant(options.cookies.clearOnSwitch, cookiePrefix)\n\tconst expireHintAndClear = expireCookies({\n\t\tauthConfig,\n\t\tcookiePrefix,\n\t\tnames: [...extraClearOnSwitch, options.hintCookieName],\n\t})\n\n\tif (!impersonator || !collectionBySlug(req.payload, impersonator.collection)) {\n\t\tawait closeAndRevoke({\n\t\t\tendedBy: 'impersonatorGone',\n\t\t\toptions,\n\t\t\tpayload: req.payload,\n\t\t\trecord: row,\n\t\t\treq,\n\t\t})\n\t\tawait releaseLocks({ payload: req.payload, record: row, req })\n\t\treturn fail({\n\t\t\tcookies: [\n\t\t\t\texpirePayloadCookie({\n\t\t\t\t\tauthConfig,\n\t\t\t\t\tcookiePrefix,\n\t\t\t\t}),\n\t\t\t\t...expireHintAndClear,\n\t\t\t],\n\t\t\terror: 'impersonatorGone',\n\t\t\treq,\n\t\t\tstatus: 409,\n\t\t})\n\t}\n\n\tlet impersonatorDoc: { deletedAt?: string; lockUntil?: Date | string } | null = null\n\ttry {\n\t\timpersonatorDoc = (await req.payload.findByID({\n\t\t\tid: impersonator.id,\n\t\t\tcollection: impersonator.collection as CollectionSlug,\n\t\t\tdepth: 0,\n\t\t\toverrideAccess: true,\n\t\t\treq,\n\t\t})) as { deletedAt?: string; lockUntil?: Date | string }\n\t} catch {\n\t\timpersonatorDoc = null\n\t}\n\n\tif (!impersonatorDoc || impersonatorDoc.deletedAt) {\n\t\tawait closeAndRevoke({\n\t\t\tendedBy: 'impersonatorGone',\n\t\t\toptions,\n\t\t\tpayload: req.payload,\n\t\t\trecord: row,\n\t\t\treq,\n\t\t})\n\t\tawait releaseLocks({ payload: req.payload, record: row, req })\n\t\treturn fail({\n\t\t\tcookies: [\n\t\t\t\texpirePayloadCookie({\n\t\t\t\t\tauthConfig,\n\t\t\t\t\tcookiePrefix,\n\t\t\t\t}),\n\t\t\t\t...expireHintAndClear,\n\t\t\t],\n\t\t\terror: 'impersonatorGone',\n\t\t\treq,\n\t\t\tstatus: 409,\n\t\t})\n\t}\n\n\tif (row.mode === 'parallel') {\n\t\tconst closed = await closeAndRevoke({\n\t\t\tendedBy: 'exit',\n\t\t\toptions,\n\t\t\tpayload: req.payload,\n\t\t\trecord: row,\n\t\t\treq,\n\t\t})\n\t\tawait releaseLocks({ payload: req.payload, record: closed, req })\n\t\tlet isolatedName: string | undefined\n\t\tif (target) {\n\t\t\tconst targetUser = (await req.payload.db.findOne({\n\t\t\t\tcollection: target.collection as CollectionSlug,\n\t\t\t\treq,\n\t\t\t\twhere: { id: { equals: target.id } },\n\t\t\t})) as { collection?: string } | null\n\t\t\tif (targetUser) {\n\t\t\t\ttargetUser.collection = target.collection\n\t\t\t\tisolatedName = await isolatedCookieNameFor({\n\t\t\t\t\tcollection: target.collection as CollectionSlug,\n\t\t\t\t\tpayload: req.payload,\n\t\t\t\t\tuser: targetUser as never,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\treturn json({\n\t\t\tbody: { ok: true },\n\t\t\tcookies: [\n\t\t\t\t...(isolatedName ? [expireCookie({ authConfig, name: isolatedName })] : []),\n\t\t\t\texpireCookie({ authConfig, name: options.hintCookieName }),\n\t\t\t\t...expireCookies({\n\t\t\t\t\tauthConfig,\n\t\t\t\t\tcookiePrefix,\n\t\t\t\t\tnames: extraClearOnSwitch,\n\t\t\t\t}),\n\t\t\t],\n\t\t\treq,\n\t\t\tstatus: 200,\n\t\t})\n\t}\n\n\tconst restored = await resignSession({\n\t\tcollection: impersonator.collection as CollectionSlug,\n\t\tpayload: req.payload,\n\t\treq,\n\t\tsid: row.impersonatorSid,\n\t\tuserId: impersonator.id,\n\t})\n\n\tif (!restored) {\n\t\tawait closeAndRevoke({\n\t\t\tendedBy: 'expired',\n\t\t\toptions,\n\t\t\tpayload: req.payload,\n\t\t\trecord: row,\n\t\t\treq,\n\t\t})\n\t\tawait releaseLocks({ payload: req.payload, record: row, req })\n\t\treturn fail({\n\t\t\tcookies: [\n\t\t\t\texpirePayloadCookie({\n\t\t\t\t\tauthConfig,\n\t\t\t\t\tcookiePrefix,\n\t\t\t\t}),\n\t\t\t\t...expireHintAndClear,\n\t\t\t],\n\t\t\terror: 'impersonatorSessionExpired',\n\t\t\treq,\n\t\t\tstatus: 409,\n\t\t})\n\t}\n\n\tconst closed = await closeAndRevoke({\n\t\tendedBy: 'exit',\n\t\toptions,\n\t\tpayload: req.payload,\n\t\trecord: row,\n\t\treq,\n\t})\n\tawait releaseLocks({ payload: req.payload, record: closed, req })\n\n\treturn json({\n\t\tbody: { ok: true },\n\t\tcookies: [\n\t\t\trestored.cookie,\n\t\t\texpireCookie({ authConfig, name: options.hintCookieName }),\n\t\t\t...exitTenantCookies({\n\t\t\t\tauthConfig,\n\t\t\t\tcookiePrefix,\n\t\t\t\tmode: closed.mode,\n\t\t\t\toptions,\n\t\t\t\tsnapshot: row.impersonatorTenantCookie,\n\t\t\t}),\n\t\t\t...expireCookies({\n\t\t\t\tauthConfig,\n\t\t\t\tcookiePrefix,\n\t\t\t\tnames: extraClearOnSwitch,\n\t\t\t}),\n\t\t],\n\t\treq,\n\t\tstatus: 200,\n\t})\n}\n\nexport const exitEndpoint = (path: string): Endpoint => ({\n\thandler: exitHandler,\n\tmethod: 'post',\n\tpath,\n})\n"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import { getRegistry } from "../plugin/registry.js";
2
+ import { asAuthUser } from "../types.js";
3
+ import { isJsonContentType, verifyMutationOrigin } from "../auth/origin.js";
4
+ import { addDataAndFileToRequest, headersWithCors } from "payload";
5
+ //#region src/endpoints/shared.ts
6
+ const NO_STORE = { "Cache-Control": "no-store" };
7
+ const json = ({ body, cookies = [], req, status }) => {
8
+ const headers = headersWithCors({
9
+ headers: new Headers(NO_STORE),
10
+ req
11
+ });
12
+ for (const cookie of cookies) headers.append("Set-Cookie", cookie);
13
+ return Response.json(body, {
14
+ headers,
15
+ status
16
+ });
17
+ };
18
+ const fail = ({ cookies, error, req, status }) => json({
19
+ body: { error },
20
+ cookies,
21
+ req,
22
+ status
23
+ });
24
+ const rejectGate = (req, gate) => fail({
25
+ error: gate,
26
+ req,
27
+ status: gate === "invalidBody" ? 400 : 403
28
+ });
29
+ const getOptions = (req) => {
30
+ const options = getRegistry(req.payload.config);
31
+ if (!options) throw new Error("@10x-media/impersonation: plugin options were not registered");
32
+ return options;
33
+ };
34
+ const callerSid = (user) => {
35
+ if (!user) return;
36
+ return asAuthUser(user)._sid;
37
+ };
38
+ const isApiKeyStrategy = (user) => {
39
+ const strategy = asAuthUser(user)._strategy;
40
+ return typeof strategy === "string" && strategy.endsWith("-api-key");
41
+ };
42
+ const prepareMutation = async (req) => {
43
+ const options = getOptions(req);
44
+ if (!verifyMutationOrigin({
45
+ headers: req.headers,
46
+ options,
47
+ payload: req.payload
48
+ })) return "origin";
49
+ if (!isJsonContentType(req.headers)) return "invalidBody";
50
+ await addDataAndFileToRequest(req);
51
+ return null;
52
+ };
53
+ /** Only the literal `true` allows. A Payload `Where` object is deny, never allow-all. */
54
+ const isLiteralTrue = (value) => value === true;
55
+ const safeRedirect = (value, fallback) => {
56
+ if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//")) return fallback;
57
+ try {
58
+ if (new URL(value, "http://x").origin !== "http://x") return fallback;
59
+ } catch {
60
+ return fallback;
61
+ }
62
+ return value;
63
+ };
64
+ const trimReason = (value, maxLength) => {
65
+ if (typeof value !== "string") return;
66
+ const trimmed = value.trim().slice(0, maxLength);
67
+ return trimmed.length > 0 ? trimmed : void 0;
68
+ };
69
+ const defaultRedirect = (req, collection) => {
70
+ if (collection === req.payload.config.admin.user) return req.payload.config.routes.admin;
71
+ return "/";
72
+ };
73
+ //#endregion
74
+ export { callerSid, defaultRedirect, fail, getOptions, isApiKeyStrategy, isLiteralTrue, json, prepareMutation, rejectGate, safeRedirect, trimReason };
75
+
76
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","names":[],"sources":["../../src/endpoints/shared.ts"],"sourcesContent":["import {\n\taddDataAndFileToRequest,\n\theadersWithCors,\n\ttype PayloadRequest,\n\ttype TypedUser,\n} from 'payload'\n\nimport { isJsonContentType, verifyMutationOrigin } from '../auth/origin'\nimport { getRegistry } from '../plugin/registry'\nimport { asAuthUser, type FailureCode, type ResolvedOptions } from '../types'\n\nexport const NO_STORE = { 'Cache-Control': 'no-store' }\n\nexport const json = ({\n\tbody,\n\tcookies = [],\n\treq,\n\tstatus,\n}: {\n\tbody: unknown\n\tcookies?: string[]\n\treq: PayloadRequest\n\tstatus: number\n}): Response => {\n\tconst headers = headersWithCors({ headers: new Headers(NO_STORE), req })\n\tfor (const cookie of cookies) {\n\t\theaders.append('Set-Cookie', cookie)\n\t}\n\treturn Response.json(body, { headers, status })\n}\n\nexport const fail = ({\n\tcookies,\n\terror,\n\treq,\n\tstatus,\n}: {\n\tcookies?: string[]\n\terror: FailureCode\n\treq: PayloadRequest\n\tstatus: number\n}): Response => json({ body: { error }, cookies, req, status })\n\nexport const rejectGate = (req: PayloadRequest, gate: FailureCode): Response =>\n\tfail({ error: gate, req, status: gate === 'invalidBody' ? 400 : 403 })\n\nexport const getOptions = (req: PayloadRequest): ResolvedOptions => {\n\tconst options = getRegistry(req.payload.config)\n\tif (!options) {\n\t\tthrow new Error('@10x-media/impersonation: plugin options were not registered')\n\t}\n\treturn options\n}\n\nexport const callerSid = (user: null | TypedUser | undefined): string | undefined => {\n\tif (!user) {\n\t\treturn undefined\n\t}\n\treturn asAuthUser(user)._sid\n}\n\nexport const isApiKeyStrategy = (user: TypedUser): boolean => {\n\tconst strategy = asAuthUser(user)._strategy\n\treturn typeof strategy === 'string' && strategy.endsWith('-api-key')\n}\n\nexport const prepareMutation = async (req: PayloadRequest): Promise<FailureCode | null> => {\n\tconst options = getOptions(req)\n\tif (!verifyMutationOrigin({ headers: req.headers, options, payload: req.payload })) {\n\t\treturn 'origin'\n\t}\n\tif (!isJsonContentType(req.headers)) {\n\t\treturn 'invalidBody'\n\t}\n\tawait addDataAndFileToRequest(req)\n\treturn null\n}\n\n/** Only the literal `true` allows. A Payload `Where` object is deny, never allow-all. */\nexport const isLiteralTrue = (value: unknown): value is true => value === true\n\nexport const safeRedirect = (value: unknown, fallback: string): string => {\n\tif (typeof value !== 'string' || !value.startsWith('/') || value.startsWith('//')) {\n\t\treturn fallback\n\t}\n\ttry {\n\t\tif (new URL(value, 'http://x').origin !== 'http://x') {\n\t\t\treturn fallback\n\t\t}\n\t} catch {\n\t\treturn fallback\n\t}\n\treturn value\n}\n\nexport const trimReason = (value: unknown, maxLength: number): string | undefined => {\n\tif (typeof value !== 'string') {\n\t\treturn undefined\n\t}\n\tconst trimmed = value.trim().slice(0, maxLength)\n\treturn trimmed.length > 0 ? trimmed : undefined\n}\n\nexport const defaultRedirect = (req: PayloadRequest, collection: string): string => {\n\tconst adminCollection = req.payload.config.admin.user\n\tif (collection === adminCollection) {\n\t\treturn req.payload.config.routes.admin\n\t}\n\treturn '/'\n}\n"],"mappings":";;;;;AAWA,MAAa,WAAW,EAAE,iBAAiB,WAAW;AAEtD,MAAa,QAAQ,EACpB,MACA,UAAU,CAAC,GACX,KACA,aAMe;CACf,MAAM,UAAU,gBAAgB;EAAE,SAAS,IAAI,QAAQ,QAAQ;EAAG;CAAI,CAAC;CACvE,KAAK,MAAM,UAAU,SACpB,QAAQ,OAAO,cAAc,MAAM;CAEpC,OAAO,SAAS,KAAK,MAAM;EAAE;EAAS;CAAO,CAAC;AAC/C;AAEA,MAAa,QAAQ,EACpB,SACA,OACA,KACA,aAMe,KAAK;CAAE,MAAM,EAAE,MAAM;CAAG;CAAS;CAAK;AAAO,CAAC;AAE9D,MAAa,cAAc,KAAqB,SAC/C,KAAK;CAAE,OAAO;CAAM;CAAK,QAAQ,SAAS,gBAAgB,MAAM;AAAI,CAAC;AAEtE,MAAa,cAAc,QAAyC;CACnE,MAAM,UAAU,YAAY,IAAI,QAAQ,MAAM;CAC9C,IAAI,CAAC,SACJ,MAAM,IAAI,MAAM,8DAA8D;CAE/E,OAAO;AACR;AAEA,MAAa,aAAa,SAA2D;CACpF,IAAI,CAAC,MACJ;CAED,OAAO,WAAW,IAAI,EAAE;AACzB;AAEA,MAAa,oBAAoB,SAA6B;CAC7D,MAAM,WAAW,WAAW,IAAI,EAAE;CAClC,OAAO,OAAO,aAAa,YAAY,SAAS,SAAS,UAAU;AACpE;AAEA,MAAa,kBAAkB,OAAO,QAAqD;CAC1F,MAAM,UAAU,WAAW,GAAG;CAC9B,IAAI,CAAC,qBAAqB;EAAE,SAAS,IAAI;EAAS;EAAS,SAAS,IAAI;CAAQ,CAAC,GAChF,OAAO;CAER,IAAI,CAAC,kBAAkB,IAAI,OAAO,GACjC,OAAO;CAER,MAAM,wBAAwB,GAAG;CACjC,OAAO;AACR;;AAGA,MAAa,iBAAiB,UAAkC,UAAU;AAE1E,MAAa,gBAAgB,OAAgB,aAA6B;CACzE,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,GAAG,KAAK,MAAM,WAAW,IAAI,GAC/E,OAAO;CAER,IAAI;EACH,IAAI,IAAI,IAAI,OAAO,UAAU,EAAE,WAAW,YACzC,OAAO;CAET,QAAQ;EACP,OAAO;CACR;CACA,OAAO;AACR;AAEA,MAAa,cAAc,OAAgB,cAA0C;CACpF,IAAI,OAAO,UAAU,UACpB;CAED,MAAM,UAAU,MAAM,KAAK,EAAE,MAAM,GAAG,SAAS;CAC/C,OAAO,QAAQ,SAAS,IAAI,UAAU,KAAA;AACvC;AAEA,MAAa,mBAAmB,KAAqB,eAA+B;CAEnF,IAAI,eADoB,IAAI,QAAQ,OAAO,MAAM,MAEhD,OAAO,IAAI,QAAQ,OAAO,OAAO;CAElC,OAAO;AACR"}