permdock 0.0.0 → 0.2.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.
- package/README.md +327 -1
- package/dist/a2a/index.d.ts +169 -0
- package/dist/a2a/index.js +232 -0
- package/dist/adapter-context-mlWsVGGg.js +23 -0
- package/dist/ai-sdk/index.d.ts +95 -0
- package/dist/ai-sdk/index.js +154 -0
- package/dist/approval-required-TzNB9oQA.js +6 -0
- package/dist/approvals/index.d.ts +3 -0
- package/dist/approvals/index.js +5 -0
- package/dist/approvals-DbYAuNkG.js +224 -0
- package/dist/approvers-DT4AVyIg.js +52 -0
- package/dist/arazzo-BAgw9ap6.js +70 -0
- package/dist/arazzo-cmvX2jDe.js +54 -0
- package/dist/authzen/index.d.ts +39 -0
- package/dist/authzen/index.js +302 -0
- package/dist/better-auth/index.d.ts +109 -0
- package/dist/better-auth/index.js +344 -0
- package/dist/better-supabase/index.d.ts +206 -0
- package/dist/better-supabase/index.js +620 -0
- package/dist/bind-CVy3Espv.d.ts +25 -0
- package/dist/bind-VnYn2HKq.js +97 -0
- package/dist/boundary-BM8a_P6E.js +13 -0
- package/dist/boundary-BmHxVH1J.d.ts +15 -0
- package/dist/budget-JqcRqyhK.d.ts +11 -0
- package/dist/budget-NbEtbJC_.js +11 -0
- package/dist/canonical-json-DemiK4Vp.js +26 -0
- package/dist/capability-4qPdO9gk.js +173 -0
- package/dist/capability-Dq43tT1u.d.ts +99 -0
- package/dist/catalog/index.d.ts +12 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog-BjZSovXQ.js +2 -0
- package/dist/catalog-doc-Dsc9y7sd.js +309 -0
- package/dist/catalog-h2tlKFkq.js +48 -0
- package/dist/catalog-path-CnI0XTFK.js +12 -0
- package/dist/catalog-z8q6bi_r.js +55 -0
- package/dist/claude-agent/index.d.ts +72 -0
- package/dist/claude-agent/index.js +77 -0
- package/dist/clerk/index.d.ts +65 -0
- package/dist/clerk/index.js +301 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.js +5 -0
- package/dist/clients-C7zLFuWT.d.ts +12 -0
- package/dist/clients-DjU5ABMy.js +23 -0
- package/dist/cloud/index.d.ts +90 -0
- package/dist/cloud/index.js +404 -0
- package/dist/cloud-BGhHS-lY.js +50 -0
- package/dist/cloud-Bo3e9Ak3.js +118 -0
- package/dist/collect-DDDv_ESs.js +502 -0
- package/dist/collect-DLlujdHC.js +83 -0
- package/dist/collect-rG2IQ3t0.js +2 -0
- package/dist/commands-DWOrSQic.js +97 -0
- package/dist/compact-y6vfu2_f.js +19 -0
- package/dist/compare-pfyVnBjE.js +12 -0
- package/dist/compile/index.d.ts +3 -0
- package/dist/compile/index.js +2 -0
- package/dist/compile-Ba4blROR.js +613 -0
- package/dist/compile-CNnl6uIt.d.ts +94 -0
- package/dist/config-BJiM-xAm.js +157 -0
- package/dist/config-Bn00Z3ks.js +36 -0
- package/dist/config-report-Dw_JcUqT.js +44 -0
- package/dist/connection-CU8MQxRi.js +194 -0
- package/dist/context-Ttp0ekMT.js +82 -0
- package/dist/convex/index.d.ts +45 -0
- package/dist/convex/index.js +65 -0
- package/dist/create-BWyx3jua.d.ts +245 -0
- package/dist/create-CPhDL6qx.js +253 -0
- package/dist/credential-8P81ff4u.js +306 -0
- package/dist/delegation-D8pUEuce.js +103 -0
- package/dist/describe-B_gVZEVi.js +94 -0
- package/dist/describe-PdoqESvg.d.ts +36 -0
- package/dist/diff-B8Tunc3o.js +451 -0
- package/dist/diff-CzqLxZ77.js +48 -0
- package/dist/digest-B3kc_PgQ.d.ts +19 -0
- package/dist/digest-BI-6aa6d.js +27 -0
- package/dist/doctor-WpvG6p4k.js +1194 -0
- package/dist/doctor-collect-ByfYlNv1.js +664 -0
- package/dist/doctor-rOr-4ZfS.js +53 -0
- package/dist/drizzle/index.d.ts +73 -0
- package/dist/drizzle/index.js +160 -0
- package/dist/duration-xK2jGFVc.js +25 -0
- package/dist/elysia/index.d.ts +49 -0
- package/dist/elysia/index.js +59 -0
- package/dist/errors-CWfoNg9N.js +47 -0
- package/dist/errors-Cz5zn0mW.js +123 -0
- package/dist/errors-DAqevpNk.d.ts +125 -0
- package/dist/errors-DRnGZHzF.js +30 -0
- package/dist/evaluation-items-CfCCejQd.js +40 -0
- package/dist/evaluations-tz6D9A2G.js +461 -0
- package/dist/eve/index.d.ts +86 -0
- package/dist/eve/index.js +176 -0
- package/dist/expiry-BoBJ8Tvc.js +7 -0
- package/dist/express/index.d.ts +23 -0
- package/dist/express/index.js +78 -0
- package/dist/fastify/index.d.ts +33 -0
- package/dist/fastify/index.js +92 -0
- package/dist/fields-BMl1RxoX.js +26 -0
- package/dist/files-DWUN9PzI.js +125 -0
- package/dist/fixtures-ht28Cplr.js +67 -0
- package/dist/freeze-CrU8nQeX.js +60 -0
- package/dist/from-snapshot-C3sHpZNS.js +569 -0
- package/dist/generate-CH7ThJ14.js +173 -0
- package/dist/grantee-Cv5Sogfv.js +602 -0
- package/dist/graph-sql-B-dsAT6j.d.ts +29 -0
- package/dist/headers-BZSDieNT.js +6 -0
- package/dist/headers-Cf-zWvsO.d.ts +6 -0
- package/dist/helpers-H_KARDVC.js +421 -0
- package/dist/hono/index.d.ts +48 -0
- package/dist/hono/index.js +99 -0
- package/dist/hosted-DJQw46hn.js +266 -0
- package/dist/http-BIhmLfCc.js +95 -0
- package/dist/http-DPuDaChb.d.ts +13 -0
- package/dist/http-SO7qGttf.js +44 -0
- package/dist/ids-DC4V4CFu.js +18 -0
- package/dist/index-A0YNL8jZ.d.ts +177 -0
- package/dist/index-CtE07CBF.d.ts +6 -0
- package/dist/index-D3vbkoSI.d.ts +483 -0
- package/dist/index-D6_cSa1z.d.ts +201 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +26 -0
- package/dist/instance-options-BK_rNOSC.d.ts +11 -0
- package/dist/instance-options-Dhhcnm3y.js +22 -0
- package/dist/json-schema-BsAflUGW.js +106 -0
- package/dist/jwt/index.d.ts +91 -0
- package/dist/jwt/index.js +590 -0
- package/dist/kernel-GSQnMz3O.js +208 -0
- package/dist/kysely/index.d.ts +78 -0
- package/dist/kysely/index.js +129 -0
- package/dist/live-options-H2eG8dfJ.js +60 -0
- package/dist/load-DhWEB9P1.js +75 -0
- package/dist/load-DwJUafYN.js +2 -0
- package/dist/load-jose-BL6LH4bL.js +11 -0
- package/dist/lru-8q4WZjBV.js +28 -0
- package/dist/manifest-iaoBXHKc.js +783 -0
- package/dist/manifest-uDS9y5de.d.ts +206 -0
- package/dist/map-AcxK9o_D.js +147 -0
- package/dist/map-claims-Z4QZOiFF.js +369 -0
- package/dist/map-error-pgUWNNtG.js +23 -0
- package/dist/markers-B0hYLvMD.js +60 -0
- package/dist/may-use-DndQ5tQ-.js +77 -0
- package/dist/mcp/index.d.ts +159 -0
- package/dist/mcp/index.js +603 -0
- package/dist/nest/index.d.ts +75 -0
- package/dist/nest/index.js +261 -0
- package/dist/next/client.d.ts +23 -0
- package/dist/next/client.js +33 -0
- package/dist/next/index.d.ts +93 -0
- package/dist/next/index.js +180 -0
- package/dist/next/plugin.d.ts +14 -0
- package/dist/next/plugin.js +33 -0
- package/dist/node/index.d.ts +24 -0
- package/dist/node/index.js +34 -0
- package/dist/openai/index.d.ts +62 -0
- package/dist/openai/index.js +92 -0
- package/dist/openapi/index.d.ts +176 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi-Bbg4wGVL.js +164 -0
- package/dist/openapi-BqRBvAng.js +538 -0
- package/dist/openapi-CqqEbHsL.js +250 -0
- package/dist/openapi-import-cmwetbzw.js +5959 -0
- package/dist/openapi-schema--ARjyfQr.js +6902 -0
- package/dist/orpc/index.d.ts +51 -0
- package/dist/orpc/index.js +145 -0
- package/dist/otel/index.d.ts +13 -0
- package/dist/otel/index.js +202 -0
- package/dist/package-root-zGkxO3oN.js +30 -0
- package/dist/parse-BuyjxhZV.js +53 -0
- package/dist/parse-local-manifest-BkGQNzAh.d.ts +10 -0
- package/dist/parse-local-manifest-BrMozJCo.js +36 -0
- package/dist/pdp/index.d.ts +22 -0
- package/dist/pdp/index.js +682 -0
- package/dist/peer-BOr_kJE1.js +31 -0
- package/dist/permdock-CzqXjCpU.js +3471 -0
- package/dist/permissions-CDgvKG5L.d.ts +52 -0
- package/dist/permissions-CpZrivYv.js +142 -0
- package/dist/permissions-DVoF6dMt.js +662 -0
- package/dist/permissions-DttnqqxC.d.ts +214 -0
- package/dist/pins-8ILTftC9.js +12 -0
- package/dist/policy-CFZAr_Al.d.ts +2252 -0
- package/dist/policy-D8UFyERw.d.ts +20 -0
- package/dist/policy-xyWg7QdR.js +1122 -0
- package/dist/powersync-A8x_Yyvh.js +2 -0
- package/dist/powersync-C7kLglqX.js +540 -0
- package/dist/powersync-yW1UUFzp.js +62 -0
- package/dist/prisma/index.d.ts +136 -0
- package/dist/prisma/index.js +455 -0
- package/dist/problem-B43ztKIm.js +191 -0
- package/dist/problem-details-CkL50EEf.d.ts +7 -0
- package/dist/problem-details-_AMOYz-w.js +144 -0
- package/dist/prompt-CXYhLfsS.js +32 -0
- package/dist/protected-CNsMzrVd.d.ts +27 -0
- package/dist/protected-Cs6kTaHo.js +100 -0
- package/dist/protected-bZs6rbT1.d.ts +40 -0
- package/dist/provider-Cjr62YMV.d.ts +6 -0
- package/dist/provider-DLzpb2Xz.js +52 -0
- package/dist/provider-store-DNyC1y48.js +43 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.js +7 -0
- package/dist/react/provider-client.d.ts +2 -0
- package/dist/react/provider-client.js +3 -0
- package/dist/react-native/index.d.ts +198 -0
- package/dist/react-native/index.js +696 -0
- package/dist/refs-KYb4ntaz.js +91 -0
- package/dist/relations-cjOL01hZ.js +505 -0
- package/dist/replay-BsOyTrdV.js +28 -0
- package/dist/replay-sqBGMnNs.d.ts +5 -0
- package/dist/resource-ref-CaS9yVPi.js +20 -0
- package/dist/revocations-Bhr0aMIC.js +27 -0
- package/dist/revocations-Cy06JNx2.d.ts +21 -0
- package/dist/rls-BMNydpHk.js +2521 -0
- package/dist/rls-CEvs64Kj.js +238 -0
- package/dist/rls-fields-BMasREhR.js +3502 -0
- package/dist/rls-graph-pc9fuXYm.js +435 -0
- package/dist/rls-import-D8F7j35Q.js +943 -0
- package/dist/rls-migrate-7wikfrVI.js +367 -0
- package/dist/rls-ownership-B5Lkq62W.js +843 -0
- package/dist/rls-shared-aOQ4hGTO.js +493 -0
- package/dist/rls-verify-D8B7dOTh.js +745 -0
- package/dist/row-check-Dt35czLy.d.ts +40 -0
- package/dist/rpc-DOK54_CS.js +89 -0
- package/dist/run-YXCJDNYk.js +174 -0
- package/dist/runtime-CB8gQFvS.js +11 -0
- package/dist/saas-Bj_FFkjN.js +1203 -0
- package/dist/schema-D3MnDMxa.js +300 -0
- package/dist/scim/index.d.ts +2 -0
- package/dist/scim/index.js +2 -0
- package/dist/scim-BFGBBAqR.js +1445 -0
- package/dist/scopes-CVdPrWN-.js +239 -0
- package/dist/server/index.d.ts +134 -0
- package/dist/server/index.js +11 -0
- package/dist/server-DlZ28Oz5.js +268 -0
- package/dist/sha256-BO0swRXL.js +150 -0
- package/dist/shared-DRu8VFVM.js +123 -0
- package/dist/sink-Bd9PSyfq.js +141 -0
- package/dist/sink-Gs6TTVa-.d.ts +104 -0
- package/dist/skills-Biq5uup3.js +192 -0
- package/dist/skills-JaymNCdK.js +56 -0
- package/dist/snapshot-Enxd0n-s.js +975 -0
- package/dist/snapshot-cache-D6jLelln.js +44 -0
- package/dist/snapshot-cache-DwQH8ufx.d.ts +45 -0
- package/dist/solid/index.d.ts +93 -0
- package/dist/solid/index.js +188 -0
- package/dist/sources-B0UvrJ21.js +534 -0
- package/dist/sources-BcJgw0lt.d.ts +192 -0
- package/dist/sql-xdg5f4Fi.js +27 -0
- package/dist/src-CSbUiozF.js +568 -0
- package/dist/ssf/index.d.ts +4 -0
- package/dist/ssf/index.js +596 -0
- package/dist/store-DMAWwAJV.js +371 -0
- package/dist/store-D_pjn91-.d.ts +36 -0
- package/dist/store-options-yO0V6gRn.js +716 -0
- package/dist/stores-4_sqiOA1.js +87 -0
- package/dist/stores-DVR5NHLE.d.ts +52 -0
- package/dist/stream-DDpvynSB.js +66 -0
- package/dist/stream-DlvG2hs-.d.ts +12 -0
- package/dist/style-CTm1AXDT.js +22 -0
- package/dist/subject-C6dLxqHU.js +24 -0
- package/dist/subject-settings-BwBmCdTR.js +128 -0
- package/dist/supabase/index.d.ts +183 -0
- package/dist/supabase/index.js +5 -0
- package/dist/supabase/middleware.d.ts +82 -0
- package/dist/supabase/middleware.js +102 -0
- package/dist/supabase-CwafwxxF.js +83 -0
- package/dist/supabase-DTBaC20O.js +774 -0
- package/dist/supabase-config-Bs44dare.js +472 -0
- package/dist/supabase-hook-BuxLQLpU.js +1814 -0
- package/dist/supabase-hook-D5qVd_D8.js +2 -0
- package/dist/supautils-DYZnzRWV.js +91 -0
- package/dist/svelte/PermissionBoundary.svelte +37 -0
- package/dist/svelte/Protected.svelte +40 -0
- package/dist/svelte/index.d.ts +13 -0
- package/dist/svelte/index.js +107 -0
- package/dist/svelte/runtime.d.ts +7 -0
- package/dist/svelte/runtime.js +4 -0
- package/dist/svelte/source.d.ts +13 -0
- package/dist/svelte/source.js +11 -0
- package/dist/terminal/index.d.ts +163 -0
- package/dist/terminal/index.js +833 -0
- package/dist/testing/index.d.ts +745 -0
- package/dist/testing/index.js +3672 -0
- package/dist/testing/saas/index.d.ts +194 -0
- package/dist/testing/saas/index.js +3 -0
- package/dist/testing/saas/permissions.d.ts +2 -0
- package/dist/testing/saas/permissions.js +2 -0
- package/dist/text-diff-BdkkYSbi.js +15 -0
- package/dist/thenable-okc2xDsj.js +10 -0
- package/dist/timeout-D51niuQP.js +8 -0
- package/dist/token-CIWxJOCh.js +50 -0
- package/dist/trpc/index.d.ts +61 -0
- package/dist/trpc/index.js +122 -0
- package/dist/types-9AWO9EGt.d.ts +197 -0
- package/dist/types-BPxgszlT.d.ts +550 -0
- package/dist/types-BVyImy5I.d.ts +120 -0
- package/dist/types-Bvc4oWT6.d.ts +9 -0
- package/dist/types-C3s9xVJG.d.ts +48 -0
- package/dist/types-Dnemwd77.d.ts +183 -0
- package/dist/types-kxznc7cn.d.ts +52 -0
- package/dist/types-vKyAMtcX.d.ts +157 -0
- package/dist/types-vNSDF7Dq.d.ts +66 -0
- package/dist/types-yF5LmQVD.d.ts +306 -0
- package/dist/unplugin/index.d.ts +7 -0
- package/dist/unplugin/index.js +31 -0
- package/dist/usage-D07N9QJ3.js +42 -0
- package/dist/usage-D82l1CwK.js +2 -0
- package/dist/usage-DG1oeCca.js +693 -0
- package/dist/validation-error-CnjZ4t92.js +33 -0
- package/dist/validation-error-CtF23SrC.d.ts +26 -0
- package/dist/validity-uOp8UpYn.js +245 -0
- package/dist/verifier-BxBo6I44.js +466 -0
- package/dist/version-BvacVTZR.js +17 -0
- package/dist/views-Soe7bGSC.js +95 -0
- package/dist/vocabulary-DA8a8zif.d.ts +56 -0
- package/dist/vue/index.d.ts +79 -0
- package/dist/vue/index.js +196 -0
- package/dist/watch-Etoa_Mvr.js +2 -0
- package/dist/watch-UihESpsJ.js +128 -0
- package/dist/webmcp/index.d.ts +75 -0
- package/dist/webmcp/index.js +307 -0
- package/package.json +450 -5
- package/rulesets/permdock.yaml +29 -0
- package/schemas/approval-request-v1.json +175 -0
- package/schemas/catalog-v1.json +535 -0
- package/schemas/openapi/oas-3.1.json +1298 -0
- package/schemas/openapi/oas-3.2.json +1516 -0
- package/schemas/openapi/overlay-1.1.json +79 -0
- package/schemas/supabase-claims-v1.json +144 -0
- package/schemas/supabase-manifest-v1.json +361 -0
- package/skills/permdock/SKILL.md +66 -0
- package/skills/permdock-agents/SKILL.md +77 -0
- package/skills/permdock-agents/references/runtimes.md +213 -0
- package/skills/permdock-approvals/SKILL.md +80 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +94 -0
- package/skills/permdock-audit/SKILL.md +67 -0
- package/skills/permdock-credentials/SKILL.md +55 -0
- package/skills/permdock-credentials/references/tokens-keys-links.md +132 -0
- package/skills/permdock-data/SKILL.md +57 -0
- package/skills/permdock-data/references/queries-and-rls.md +62 -0
- package/skills/permdock-data/references/relationships.md +67 -0
- package/skills/permdock-tenancy/SKILL.md +80 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +89 -0
- package/skills/permdock-wire/SKILL.md +116 -0
- package/skills/permdock-wire/references/adapters.md +492 -0
- package/skills/permdock-wire/references/policy-options.md +45 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
import { n as isReadonlyArray, t as compact } from "../compact-y6vfu2_f.js";
|
|
2
|
+
import { n as freezeDeep } from "../freeze-CrU8nQeX.js";
|
|
3
|
+
import { _ as listPermissions, c as getResource } from "../permissions-DVoF6dMt.js";
|
|
4
|
+
import { n as PermDockValidationError } from "../validation-error-CnjZ4t92.js";
|
|
5
|
+
import { a as deniedMessage, i as approvalMessage, n as PermDockDeniedError, t as PermDockApprovalRequiredError } from "../errors-Cz5zn0mW.js";
|
|
6
|
+
import { d as isArazzoSimulateInput } from "../snapshot-Enxd0n-s.js";
|
|
7
|
+
import { n as isThenable } from "../thenable-okc2xDsj.js";
|
|
8
|
+
import { t as createPermDock$1 } from "../permdock-CzqXjCpU.js";
|
|
9
|
+
import { s as isRecord } from "../map-AcxK9o_D.js";
|
|
10
|
+
import { a as postJson, c as ttlMs, i as joinUrl, n as denied, o as resourceIdOf, r as granted, s as ttlCache, t as cacheKey } from "../shared-DRu8VFVM.js";
|
|
11
|
+
//#region src/pdp/create.ts
|
|
12
|
+
function withoutProviders(policy) {
|
|
13
|
+
if (policy.providers === void 0 || policy.providers.length === 0) return policy;
|
|
14
|
+
return freezeDeep(compact({
|
|
15
|
+
permissions: policy.permissions,
|
|
16
|
+
roles: policy.roles,
|
|
17
|
+
rolesByName: policy.rolesByName,
|
|
18
|
+
grants: policy.grants,
|
|
19
|
+
vocabulary: policy.vocabulary,
|
|
20
|
+
scopes: policy.scopes,
|
|
21
|
+
principal(user) {
|
|
22
|
+
return policy.principal(user);
|
|
23
|
+
},
|
|
24
|
+
subject(user) {
|
|
25
|
+
return policy.subject(user);
|
|
26
|
+
},
|
|
27
|
+
context(user) {
|
|
28
|
+
return policy.context?.(user);
|
|
29
|
+
},
|
|
30
|
+
validate: policy.validate,
|
|
31
|
+
onDenied: policy.onDenied,
|
|
32
|
+
fingerprint: policy.fingerprint,
|
|
33
|
+
resources: policy.resources,
|
|
34
|
+
index: policy.index
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function providerFor(providers, permission) {
|
|
38
|
+
for (const provider of providers) if (provider.handles(permission)) return provider;
|
|
39
|
+
}
|
|
40
|
+
function idFieldOf(policy, permission) {
|
|
41
|
+
return getResource(policy.permissions, permission.resource)?.id ?? "id";
|
|
42
|
+
}
|
|
43
|
+
function isExplicitDeny(decision) {
|
|
44
|
+
return decision.outcome === "denied" && decision.denials.some((denial) => denial.reason === "deny");
|
|
45
|
+
}
|
|
46
|
+
function isLocalShortCircuit(decision) {
|
|
47
|
+
if (decision.outcome !== "denied") return false;
|
|
48
|
+
if (isExplicitDeny(decision)) return true;
|
|
49
|
+
return decision.denials.some((denial) => {
|
|
50
|
+
switch (denial.reason) {
|
|
51
|
+
case "no-grant":
|
|
52
|
+
case "inactive-grant": return false;
|
|
53
|
+
case "pdp-denied":
|
|
54
|
+
case "pdp-unavailable":
|
|
55
|
+
case "pdp-invalid-response": return false;
|
|
56
|
+
case "anonymous":
|
|
57
|
+
case "validation":
|
|
58
|
+
case "not-delegated":
|
|
59
|
+
case "no-delegation":
|
|
60
|
+
case "condition":
|
|
61
|
+
case "deny":
|
|
62
|
+
case "closure-error":
|
|
63
|
+
case "opaque-condition":
|
|
64
|
+
case "server-only":
|
|
65
|
+
case "insufficient-user-authentication":
|
|
66
|
+
case "not-entitled":
|
|
67
|
+
case "purpose":
|
|
68
|
+
case "reason-required":
|
|
69
|
+
case "actor-required":
|
|
70
|
+
case "limit":
|
|
71
|
+
case "limit-unavailable":
|
|
72
|
+
case "relation-depth":
|
|
73
|
+
case "relation-unavailable":
|
|
74
|
+
case "tenant-mismatch":
|
|
75
|
+
case "no-membership":
|
|
76
|
+
case "scope":
|
|
77
|
+
case "expired-membership":
|
|
78
|
+
case "stale-credentials":
|
|
79
|
+
case "unknown-role":
|
|
80
|
+
case "last-holder":
|
|
81
|
+
case "max-holders":
|
|
82
|
+
case "transfer-only":
|
|
83
|
+
case "not-assignable-by":
|
|
84
|
+
case "self-demotion":
|
|
85
|
+
case "externally-managed":
|
|
86
|
+
case "not-allowed-for-membership":
|
|
87
|
+
case "conflicting-role":
|
|
88
|
+
case "approval":
|
|
89
|
+
case "stale-approval":
|
|
90
|
+
case "undocumented":
|
|
91
|
+
case "unsupported":
|
|
92
|
+
case "exceeds-creator":
|
|
93
|
+
case "credential-policy": return true;
|
|
94
|
+
default: return denial.reason;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function wrap(permdock, policy, subject, providers) {
|
|
99
|
+
const decideLocal = permdock.decide;
|
|
100
|
+
const decide = async (permission, data, options) => {
|
|
101
|
+
const local = decideLocal(permission, data, options);
|
|
102
|
+
const provider = providerFor(providers, permission);
|
|
103
|
+
if (provider === void 0 || isLocalShortCircuit(local)) return local;
|
|
104
|
+
try {
|
|
105
|
+
return await provider.decide({
|
|
106
|
+
permission,
|
|
107
|
+
data,
|
|
108
|
+
subject,
|
|
109
|
+
local
|
|
110
|
+
});
|
|
111
|
+
} catch {
|
|
112
|
+
return denied("pdp-unavailable");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
/** The local trace; a remote decision evaluated no local grant, so its trace is empty. */
|
|
116
|
+
const explain = async (permission, data, options) => {
|
|
117
|
+
const decision = await decide(permission, data, {
|
|
118
|
+
...options,
|
|
119
|
+
source: options?.source ?? "explain",
|
|
120
|
+
explain: true
|
|
121
|
+
});
|
|
122
|
+
if (decision.trace !== void 0) return decision;
|
|
123
|
+
return freezeDeep({
|
|
124
|
+
...decision,
|
|
125
|
+
trace: {
|
|
126
|
+
evaluated: 0,
|
|
127
|
+
allows: [],
|
|
128
|
+
denies: [],
|
|
129
|
+
skipped: []
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const permittedIds = async (provider, permission) => {
|
|
134
|
+
if (provider?.permitted === void 0 || subject.principal === null) return;
|
|
135
|
+
try {
|
|
136
|
+
return await provider.permitted({
|
|
137
|
+
permission,
|
|
138
|
+
subject
|
|
139
|
+
});
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const can = async (permission, data, options) => {
|
|
145
|
+
try {
|
|
146
|
+
return (await decide(permission, data, options)).outcome === "granted";
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const assert = async (permission, data, options) => {
|
|
152
|
+
const decision = await decide(permission, data, {
|
|
153
|
+
...options,
|
|
154
|
+
source: options?.source ?? "assert"
|
|
155
|
+
});
|
|
156
|
+
if (decision.outcome === "granted") return decision;
|
|
157
|
+
const onDenied = options?.onDenied ?? policy.onDenied;
|
|
158
|
+
if (onDenied !== void 0) onDenied(decision);
|
|
159
|
+
const resource = getResource(policy.permissions, permission.resource);
|
|
160
|
+
const resourceId = data !== null && typeof data === "object" ? data[resource?.id ?? "id"] : void 0;
|
|
161
|
+
const resourceRef = compact({
|
|
162
|
+
type: permission.resource,
|
|
163
|
+
id: resourceId === void 0 ? void 0 : String(resourceId)
|
|
164
|
+
});
|
|
165
|
+
if (decision.outcome === "approval-required") throw new PermDockApprovalRequiredError({
|
|
166
|
+
decision,
|
|
167
|
+
permission: permission.key,
|
|
168
|
+
scope: permission.scope,
|
|
169
|
+
resource: resourceRef,
|
|
170
|
+
message: approvalMessage(permission.key, decision.reason, decision.token)
|
|
171
|
+
});
|
|
172
|
+
if (decision.denials.some((denial) => denial.reason === "validation")) {
|
|
173
|
+
const detail = decision.denials[0]?.detail;
|
|
174
|
+
if (detail instanceof PermDockValidationError) throw detail;
|
|
175
|
+
}
|
|
176
|
+
throw new PermDockDeniedError({
|
|
177
|
+
decision,
|
|
178
|
+
permission: permission.key,
|
|
179
|
+
scope: permission.scope,
|
|
180
|
+
resource: resourceRef,
|
|
181
|
+
subject,
|
|
182
|
+
message: deniedMessage(permission.key, subject.principal?.id, decision.denials, decision.alternatives.map((leaf) => leaf.key))
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
const simulate = ((input, options) => {
|
|
186
|
+
if (isReadonlyArray(input)) return Promise.all(input.map(([permission, data]) => decide(permission, data, compact({
|
|
187
|
+
source: "simulate",
|
|
188
|
+
now: options?.now
|
|
189
|
+
}))));
|
|
190
|
+
if (isArazzoSimulateInput(input)) return permdock.simulate(input);
|
|
191
|
+
return wrap(permdock.simulate(input), policy, subject, providers);
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
can,
|
|
195
|
+
decide,
|
|
196
|
+
assert,
|
|
197
|
+
explain,
|
|
198
|
+
async filter(permission, rows, options) {
|
|
199
|
+
const next = {
|
|
200
|
+
...options,
|
|
201
|
+
source: "filter"
|
|
202
|
+
};
|
|
203
|
+
const provider = providerFor(providers, permission);
|
|
204
|
+
const ids = await permittedIds(provider, permission);
|
|
205
|
+
if (ids === void 0) {
|
|
206
|
+
const decisions = await Promise.all(rows.map((row) => decide(permission, row, next)));
|
|
207
|
+
return rows.filter((_, index) => decisions[index]?.outcome === "granted");
|
|
208
|
+
}
|
|
209
|
+
const allowed = new Set(ids ?? []);
|
|
210
|
+
const field = idFieldOf(policy, permission);
|
|
211
|
+
return rows.filter((row) => {
|
|
212
|
+
const id = resourceIdOf(row, field);
|
|
213
|
+
return allowed.has(id) && !isLocalShortCircuit(decideLocal(permission, row, next));
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
pick: permdock.pick.bind(permdock),
|
|
217
|
+
async where(permission) {
|
|
218
|
+
const provider = providerFor(providers, permission);
|
|
219
|
+
if (provider === void 0) return permdock.where(permission);
|
|
220
|
+
const ids = await permittedIds(provider, permission);
|
|
221
|
+
if (ids === void 0) return {
|
|
222
|
+
condition: {
|
|
223
|
+
op: "or",
|
|
224
|
+
conditions: []
|
|
225
|
+
},
|
|
226
|
+
partial: true
|
|
227
|
+
};
|
|
228
|
+
if (ids === null || ids.length === 0) return {
|
|
229
|
+
condition: {
|
|
230
|
+
op: "or",
|
|
231
|
+
conditions: []
|
|
232
|
+
},
|
|
233
|
+
partial: false
|
|
234
|
+
};
|
|
235
|
+
const remote = {
|
|
236
|
+
op: "in",
|
|
237
|
+
field: idFieldOf(policy, permission),
|
|
238
|
+
value: [...ids]
|
|
239
|
+
};
|
|
240
|
+
const local = permdock.where(permission);
|
|
241
|
+
return local.condition.op !== "or" || local.condition.conditions.length > 0 ? {
|
|
242
|
+
condition: {
|
|
243
|
+
op: "and",
|
|
244
|
+
conditions: [local.condition, remote]
|
|
245
|
+
},
|
|
246
|
+
partial: local.partial
|
|
247
|
+
} : {
|
|
248
|
+
condition: remote,
|
|
249
|
+
partial: true
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
actions: permdock.actions.bind(permdock),
|
|
253
|
+
simulate,
|
|
254
|
+
snapshot: permdock.snapshot.bind(permdock),
|
|
255
|
+
on: permdock.on.bind(permdock),
|
|
256
|
+
tenant: (id) => {
|
|
257
|
+
const next = permdock.tenant(id);
|
|
258
|
+
return wrap(next, policy, next.subject, providers);
|
|
259
|
+
},
|
|
260
|
+
team: (id) => {
|
|
261
|
+
const next = permdock.team(id);
|
|
262
|
+
return wrap(next, policy, next.subject, providers);
|
|
263
|
+
},
|
|
264
|
+
derive: (options) => {
|
|
265
|
+
const next = permdock.derive(options);
|
|
266
|
+
return isThenable(next) ? next.then((derived) => wrap(derived, policy, derived.subject, providers)) : wrap(next, policy, next.subject, providers);
|
|
267
|
+
},
|
|
268
|
+
memberships: permdock.memberships.bind(permdock),
|
|
269
|
+
tenants: permdock.tenants.bind(permdock),
|
|
270
|
+
heldRoles: permdock.heldRoles.bind(permdock),
|
|
271
|
+
audiences: permdock.audiences.bind(permdock),
|
|
272
|
+
assignableRoles: permdock.assignableRoles.bind(permdock),
|
|
273
|
+
assignablePermissions: permdock.assignablePermissions.bind(permdock),
|
|
274
|
+
assignableLevels: permdock.assignableLevels.bind(permdock),
|
|
275
|
+
decideRoleChange: permdock.decideRoleChange.bind(permdock),
|
|
276
|
+
loadRelations: permdock.loadRelations.bind(permdock),
|
|
277
|
+
whoCan: permdock.whoCan.bind(permdock),
|
|
278
|
+
activate: permdock.activate.bind(permdock),
|
|
279
|
+
permissions: permdock.permissions,
|
|
280
|
+
roles: permdock.roles,
|
|
281
|
+
plans: permdock.plans,
|
|
282
|
+
subject
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async function createPermDock(policy, user, options = {}) {
|
|
286
|
+
const localPolicy = withoutProviders(policy);
|
|
287
|
+
const permdock = await createPermDock$1(localPolicy, user, options);
|
|
288
|
+
return wrap(permdock, localPolicy, permdock.subject, policy.providers ?? []);
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/pdp/relations.ts
|
|
292
|
+
function relationProvider(backend) {
|
|
293
|
+
const callbacks = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const [permission, callback] of backend.map) callbacks.set(permission.key, callback);
|
|
295
|
+
const timeout = backend.timeout ?? 300;
|
|
296
|
+
const decisions = ttlCache(ttlMs(backend.cache));
|
|
297
|
+
const lists = ttlCache(ttlMs(backend.cache));
|
|
298
|
+
const fetcher = backend.fetch ?? fetch;
|
|
299
|
+
const post = async (url, body) => {
|
|
300
|
+
const posted = await postJson(fetcher, url, body, {
|
|
301
|
+
auth: backend.auth,
|
|
302
|
+
timeout
|
|
303
|
+
});
|
|
304
|
+
return posted.ok ? posted.response : null;
|
|
305
|
+
};
|
|
306
|
+
function tupleFor(permission, subject, data) {
|
|
307
|
+
const callback = callbacks.get(permission.key);
|
|
308
|
+
if (callback === void 0) return null;
|
|
309
|
+
try {
|
|
310
|
+
return callback(subject, data);
|
|
311
|
+
} catch {
|
|
312
|
+
return "invalid";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
name: backend.name,
|
|
317
|
+
handles(permission) {
|
|
318
|
+
return callbacks.has(permission.key);
|
|
319
|
+
},
|
|
320
|
+
async decide(request) {
|
|
321
|
+
if (request.subject.principal === null) return denied("anonymous");
|
|
322
|
+
const tuple = tupleFor(request.permission, request.subject, request.data);
|
|
323
|
+
if (tuple === "invalid") return denied("pdp-invalid-response");
|
|
324
|
+
if (tuple === null) return denied("pdp-denied");
|
|
325
|
+
const key = cacheKey(request.subject, request.permission, tuple);
|
|
326
|
+
const hit = decisions.get(key);
|
|
327
|
+
if (hit !== void 0) return hit;
|
|
328
|
+
const result = await backend.check(tuple, post);
|
|
329
|
+
if (result === "unavailable") return denied("pdp-unavailable");
|
|
330
|
+
if (result === "invalid") return denied("pdp-invalid-response");
|
|
331
|
+
const decision = result ? granted(backend.name, request.permission, request.subject, request.data) : denied("pdp-denied");
|
|
332
|
+
decisions.set(key, decision);
|
|
333
|
+
return decision;
|
|
334
|
+
},
|
|
335
|
+
async permitted(request) {
|
|
336
|
+
if (request.subject.principal === null) return [];
|
|
337
|
+
const tuple = tupleFor(request.permission, request.subject, void 0);
|
|
338
|
+
if (tuple === "invalid") return null;
|
|
339
|
+
if (tuple === null) return [];
|
|
340
|
+
const key = cacheKey(request.subject, request.permission, tuple);
|
|
341
|
+
const hit = lists.get(key);
|
|
342
|
+
if (hit !== void 0) return hit;
|
|
343
|
+
const ids = await backend.list(tuple, post);
|
|
344
|
+
if (ids !== null) lists.set(key, ids);
|
|
345
|
+
return ids;
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
async function jsonOf(response) {
|
|
350
|
+
if (response === null) return;
|
|
351
|
+
try {
|
|
352
|
+
return await response.json();
|
|
353
|
+
} catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* An OpenFGA store as a decision provider: `check` decides one row and
|
|
359
|
+
* `list-objects` lists the ids `filter` and `where` use.
|
|
360
|
+
*/
|
|
361
|
+
function openfga(options) {
|
|
362
|
+
const store = joinUrl(options.url, `/stores/${encodeURIComponent(options.storeId)}`);
|
|
363
|
+
const model = compact({ authorization_model_id: options.authorizationModelId });
|
|
364
|
+
return relationProvider(compact({
|
|
365
|
+
name: "openfga",
|
|
366
|
+
map: options.map,
|
|
367
|
+
auth: options.auth,
|
|
368
|
+
timeout: options.timeout,
|
|
369
|
+
cache: options.cache,
|
|
370
|
+
fetch: options.fetch,
|
|
371
|
+
async check(tuple, post) {
|
|
372
|
+
if (tuple.id === void 0) return "invalid";
|
|
373
|
+
const body = await jsonOf(await post(`${store}/check`, {
|
|
374
|
+
...model,
|
|
375
|
+
tuple_key: {
|
|
376
|
+
user: tuple.user,
|
|
377
|
+
relation: tuple.relation,
|
|
378
|
+
object: `${tuple.type}:${tuple.id}`
|
|
379
|
+
}
|
|
380
|
+
}));
|
|
381
|
+
if (body === void 0) return "unavailable";
|
|
382
|
+
return isRecord(body) && typeof body["allowed"] === "boolean" ? body["allowed"] : "invalid";
|
|
383
|
+
},
|
|
384
|
+
async list(tuple, post) {
|
|
385
|
+
const body = await jsonOf(await post(`${store}/list-objects`, {
|
|
386
|
+
...model,
|
|
387
|
+
type: tuple.type,
|
|
388
|
+
relation: tuple.relation,
|
|
389
|
+
user: tuple.user
|
|
390
|
+
}));
|
|
391
|
+
if (!isRecord(body) || !Array.isArray(body["objects"])) return null;
|
|
392
|
+
const prefix = `${tuple.type}:`;
|
|
393
|
+
const ids = [];
|
|
394
|
+
for (const object of body["objects"]) {
|
|
395
|
+
if (typeof object !== "string" || !object.startsWith(prefix)) return null;
|
|
396
|
+
ids.push(object.slice(prefix.length));
|
|
397
|
+
}
|
|
398
|
+
return Object.freeze(ids);
|
|
399
|
+
}
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
402
|
+
function subjectReference(check) {
|
|
403
|
+
return compact({
|
|
404
|
+
object: {
|
|
405
|
+
objectType: check.subject.type,
|
|
406
|
+
objectId: check.subject.id
|
|
407
|
+
},
|
|
408
|
+
optionalRelation: check.subject.relation
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* A SpiceDB HTTP gateway as a decision provider: `CheckPermission` decides
|
|
413
|
+
* one row and `LookupResources` lists the ids `filter` and `where` use. A
|
|
414
|
+
* conditional permissionship (a caveat missing context) is a denial.
|
|
415
|
+
*/
|
|
416
|
+
function spicedb(options) {
|
|
417
|
+
const consistency = options.consistency === "fully-consistent" ? { fullyConsistent: true } : { minimizeLatency: true };
|
|
418
|
+
return relationProvider(compact({
|
|
419
|
+
name: "spicedb",
|
|
420
|
+
map: options.map,
|
|
421
|
+
auth: { bearer: options.token },
|
|
422
|
+
timeout: options.timeout,
|
|
423
|
+
cache: options.cache,
|
|
424
|
+
fetch: options.fetch,
|
|
425
|
+
async check(check, post) {
|
|
426
|
+
if (check.resource.id === void 0) return "invalid";
|
|
427
|
+
const body = await jsonOf(await post(joinUrl(options.url, "/v1/permissions/check"), {
|
|
428
|
+
consistency,
|
|
429
|
+
resource: {
|
|
430
|
+
objectType: check.resource.type,
|
|
431
|
+
objectId: check.resource.id
|
|
432
|
+
},
|
|
433
|
+
permission: check.permission,
|
|
434
|
+
subject: subjectReference(check)
|
|
435
|
+
}));
|
|
436
|
+
if (body === void 0) return "unavailable";
|
|
437
|
+
if (!isRecord(body)) return "invalid";
|
|
438
|
+
switch (body["permissionship"]) {
|
|
439
|
+
case "PERMISSIONSHIP_HAS_PERMISSION": return true;
|
|
440
|
+
case "PERMISSIONSHIP_NO_PERMISSION":
|
|
441
|
+
case "PERMISSIONSHIP_CONDITIONAL_PERMISSION": return false;
|
|
442
|
+
default: return "invalid";
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
async list(check, post) {
|
|
446
|
+
const response = await post(joinUrl(options.url, "/v1/permissions/resources"), {
|
|
447
|
+
consistency,
|
|
448
|
+
resourceObjectType: check.resource.type,
|
|
449
|
+
permission: check.permission,
|
|
450
|
+
subject: subjectReference(check)
|
|
451
|
+
});
|
|
452
|
+
if (response === null) return null;
|
|
453
|
+
let text;
|
|
454
|
+
try {
|
|
455
|
+
text = await response.text();
|
|
456
|
+
} catch {
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
const ids = [];
|
|
460
|
+
for (const line of text.split("\n")) {
|
|
461
|
+
if (line.trim() === "") continue;
|
|
462
|
+
let parsed;
|
|
463
|
+
try {
|
|
464
|
+
parsed = JSON.parse(line);
|
|
465
|
+
} catch {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
const result = isRecord(parsed) ? parsed["result"] : void 0;
|
|
469
|
+
if (!isRecord(result) || typeof result["resourceObjectId"] !== "string") return null;
|
|
470
|
+
if (result["permissionship"] === "LOOKUP_PERMISSIONSHIP_HAS_PERMISSION") ids.push(result["resourceObjectId"]);
|
|
471
|
+
}
|
|
472
|
+
return Object.freeze(ids);
|
|
473
|
+
}
|
|
474
|
+
}));
|
|
475
|
+
}
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region src/pdp/remote.ts
|
|
478
|
+
const DEFAULT_EVALUATION = "/access/v1/evaluation";
|
|
479
|
+
const DEFAULT_EVALUATIONS = "/access/v1/evaluations";
|
|
480
|
+
const MAX_SEARCH_PAGES = 100;
|
|
481
|
+
function delegatedKeys(listed) {
|
|
482
|
+
if (listed === void 0) return null;
|
|
483
|
+
const keys = /* @__PURE__ */ new Set();
|
|
484
|
+
for (const item of listed) for (const leaf of listPermissions(item)) keys.add(leaf.key);
|
|
485
|
+
return keys;
|
|
486
|
+
}
|
|
487
|
+
function parseDiscovery(url, body) {
|
|
488
|
+
if (!isRecord(body)) return null;
|
|
489
|
+
const evaluation = typeof body["access_evaluation_endpoint"] === "string" ? body["access_evaluation_endpoint"] : typeof body["policy_decision_point"] === "string" ? joinUrl(body["policy_decision_point"], DEFAULT_EVALUATION) : joinUrl(url, DEFAULT_EVALUATION);
|
|
490
|
+
return compact({
|
|
491
|
+
evaluation,
|
|
492
|
+
evaluations: typeof body["access_evaluations_endpoint"] === "string" ? body["access_evaluations_endpoint"] : joinUrl(url, DEFAULT_EVALUATIONS),
|
|
493
|
+
searchResource: typeof body["search_resource_endpoint"] === "string" ? body["search_resource_endpoint"] : void 0
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
function mapEvaluationBody(permission, data, subject, mapping) {
|
|
497
|
+
const defaultId = data !== null && typeof data === "object" ? data["id"] : void 0;
|
|
498
|
+
const mappedSubject = mapping?.subject?.(subject) ?? compact({
|
|
499
|
+
type: "user",
|
|
500
|
+
id: subject.principal?.id,
|
|
501
|
+
properties: compact({
|
|
502
|
+
orgId: subject.principal?.tenant,
|
|
503
|
+
roles: subject.principal?.roles,
|
|
504
|
+
actor: subject.actor,
|
|
505
|
+
delegation: subject.delegation
|
|
506
|
+
})
|
|
507
|
+
});
|
|
508
|
+
const mappedResource = mapping?.resource?.(permission, data) ?? compact({
|
|
509
|
+
type: permission.resource,
|
|
510
|
+
id: typeof defaultId === "string" || typeof defaultId === "number" ? String(defaultId) : resourceIdOf(data),
|
|
511
|
+
properties: data
|
|
512
|
+
});
|
|
513
|
+
return {
|
|
514
|
+
subject: mappedSubject,
|
|
515
|
+
action: mapping?.action?.(permission) ?? { name: permission.action },
|
|
516
|
+
resource: mappedResource,
|
|
517
|
+
context: compact({
|
|
518
|
+
tenant: subject.principal?.tenant,
|
|
519
|
+
actor: subject.actor,
|
|
520
|
+
delegation: subject.delegation
|
|
521
|
+
})
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function parseRemoteDecision(body, permission, subject, data) {
|
|
525
|
+
if (!isRecord(body) || typeof body["decision"] !== "boolean") return denied("pdp-invalid-response");
|
|
526
|
+
if (body["decision"]) return granted("pdp", permission, subject, data);
|
|
527
|
+
const context = isRecord(body["context"]) && isRecord(body["context"]["permdock"]) ? body["context"]["permdock"] : {};
|
|
528
|
+
if (context["outcome"] === "approval-required") {
|
|
529
|
+
const token = typeof context["token"] === "string" ? context["token"] : "";
|
|
530
|
+
return {
|
|
531
|
+
outcome: "approval-required",
|
|
532
|
+
grant: {
|
|
533
|
+
role: "pdp",
|
|
534
|
+
permission: permission.key,
|
|
535
|
+
provider: "pdp",
|
|
536
|
+
approval: "human"
|
|
537
|
+
},
|
|
538
|
+
reason: "human",
|
|
539
|
+
token
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (Array.isArray(context["denials"])) {
|
|
543
|
+
const denials = context["denials"].flatMap((item) => {
|
|
544
|
+
if (!isRecord(item) || typeof item["reason"] !== "string") return [];
|
|
545
|
+
return [compact({
|
|
546
|
+
role: typeof item["role"] === "string" ? item["role"] : null,
|
|
547
|
+
reason: item["reason"],
|
|
548
|
+
detail: item["detail"]
|
|
549
|
+
})];
|
|
550
|
+
});
|
|
551
|
+
if (denials.length > 0) return {
|
|
552
|
+
outcome: "denied",
|
|
553
|
+
denials,
|
|
554
|
+
alternatives: []
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
return denied("pdp-denied");
|
|
558
|
+
}
|
|
559
|
+
function mapSearchBody(permission, subject, mapping) {
|
|
560
|
+
const body = mapEvaluationBody(permission, void 0, subject, mapping);
|
|
561
|
+
const resource = isRecord(body["resource"]) ? body["resource"] : {};
|
|
562
|
+
return {
|
|
563
|
+
...body,
|
|
564
|
+
resource: { type: resource["type"] ?? permission.resource }
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function searchIds(body, type) {
|
|
568
|
+
if (!isRecord(body) || !Array.isArray(body["results"])) return null;
|
|
569
|
+
const ids = [];
|
|
570
|
+
for (const entity of body["results"]) {
|
|
571
|
+
if (!isRecord(entity) || entity["type"] !== type || typeof entity["id"] !== "string") return null;
|
|
572
|
+
ids.push(entity["id"]);
|
|
573
|
+
}
|
|
574
|
+
return ids;
|
|
575
|
+
}
|
|
576
|
+
function nextToken(body) {
|
|
577
|
+
if (!isRecord(body) || !isRecord(body["page"])) return;
|
|
578
|
+
const token = body["page"]["next_token"];
|
|
579
|
+
return typeof token === "string" && token !== "" ? token : void 0;
|
|
580
|
+
}
|
|
581
|
+
function remotePdp(options) {
|
|
582
|
+
const keys = delegatedKeys(options.permissions);
|
|
583
|
+
const timeout = options.timeout ?? 300;
|
|
584
|
+
const decisions = ttlCache(ttlMs(options.cache));
|
|
585
|
+
const lists = ttlCache(ttlMs(options.cache));
|
|
586
|
+
const fetcher = options.fetch ?? fetch;
|
|
587
|
+
const post = {
|
|
588
|
+
auth: options.auth,
|
|
589
|
+
timeout
|
|
590
|
+
};
|
|
591
|
+
let discovery = options.endpoints?.evaluation === void 0 ? null : compact({
|
|
592
|
+
evaluation: options.endpoints.evaluation,
|
|
593
|
+
evaluations: options.endpoints.evaluations,
|
|
594
|
+
searchResource: options.endpoints.searchResource
|
|
595
|
+
});
|
|
596
|
+
async function discover() {
|
|
597
|
+
if (discovery !== null) return discovery;
|
|
598
|
+
try {
|
|
599
|
+
const signal = AbortSignal.timeout(timeout);
|
|
600
|
+
const response = await fetcher(joinUrl(options.url, "/.well-known/authzen-configuration"), { signal });
|
|
601
|
+
if (!response.ok) return null;
|
|
602
|
+
const parsed = parseDiscovery(options.url, await response.json());
|
|
603
|
+
if (parsed === null) return null;
|
|
604
|
+
discovery = parsed;
|
|
605
|
+
return parsed;
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async function readJson(url, body) {
|
|
611
|
+
const posted = await postJson(fetcher, url, body, post);
|
|
612
|
+
if (!posted.ok) return posted;
|
|
613
|
+
try {
|
|
614
|
+
return {
|
|
615
|
+
ok: true,
|
|
616
|
+
body: await posted.response.json()
|
|
617
|
+
};
|
|
618
|
+
} catch {
|
|
619
|
+
return { ok: false };
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
name: "pdp",
|
|
624
|
+
handles(permission) {
|
|
625
|
+
return keys === null || keys.has(permission.key);
|
|
626
|
+
},
|
|
627
|
+
async decide(request) {
|
|
628
|
+
if (request.subject.principal === null) return denied("anonymous");
|
|
629
|
+
let body;
|
|
630
|
+
try {
|
|
631
|
+
body = mapEvaluationBody(request.permission, request.data, request.subject, options.mapping);
|
|
632
|
+
} catch {
|
|
633
|
+
return denied("pdp-invalid-response");
|
|
634
|
+
}
|
|
635
|
+
const key = cacheKey(request.subject, request.permission, body);
|
|
636
|
+
const hit = decisions.get(key);
|
|
637
|
+
if (hit !== void 0) return hit;
|
|
638
|
+
const endpoints = await discover();
|
|
639
|
+
if (endpoints === null) return denied("pdp-unavailable");
|
|
640
|
+
const posted = await readJson(endpoints.evaluation, body);
|
|
641
|
+
if (!posted.ok) return denied("pdp-unavailable");
|
|
642
|
+
const decision = parseRemoteDecision(posted.body, request.permission, request.subject, request.data);
|
|
643
|
+
if (decision.outcome === "granted" || decision.outcome === "approval-required" || decision.outcome === "denied" && decision.denials[0]?.reason === "pdp-denied") decisions.set(key, decision);
|
|
644
|
+
return decision;
|
|
645
|
+
},
|
|
646
|
+
async permitted(request) {
|
|
647
|
+
if (request.subject.principal === null) return [];
|
|
648
|
+
const endpoints = await discover();
|
|
649
|
+
if (endpoints === null) return null;
|
|
650
|
+
if (endpoints.searchResource === void 0) return;
|
|
651
|
+
let body;
|
|
652
|
+
try {
|
|
653
|
+
body = mapSearchBody(request.permission, request.subject, options.mapping);
|
|
654
|
+
} catch {
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
const key = cacheKey(request.subject, request.permission, body);
|
|
658
|
+
const hit = lists.get(key);
|
|
659
|
+
if (hit !== void 0) return hit;
|
|
660
|
+
const type = isRecord(body["resource"]) ? body["resource"]["type"] : void 0;
|
|
661
|
+
const url = endpoints.searchResource;
|
|
662
|
+
const collect = async (token, page, ids) => {
|
|
663
|
+
if (page >= MAX_SEARCH_PAGES) return null;
|
|
664
|
+
const posted = await readJson(url, token === void 0 ? body : {
|
|
665
|
+
...body,
|
|
666
|
+
page: { token }
|
|
667
|
+
});
|
|
668
|
+
const found = posted.ok ? searchIds(posted.body, type) : null;
|
|
669
|
+
if (!posted.ok || found === null) return null;
|
|
670
|
+
const next = nextToken(posted.body);
|
|
671
|
+
return next === void 0 ? [...ids, ...found] : collect(next, page + 1, [...ids, ...found]);
|
|
672
|
+
};
|
|
673
|
+
const ids = await collect(void 0, 0, []);
|
|
674
|
+
if (ids === null) return null;
|
|
675
|
+
const frozen = Object.freeze([...ids]);
|
|
676
|
+
lists.set(key, frozen);
|
|
677
|
+
return frozen;
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
//#endregion
|
|
682
|
+
export { createPermDock, openfga, remotePdp, spicedb };
|