permdock 0.3.1 → 0.4.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/dist/a2a/index.d.ts +7 -5
- package/dist/a2a/index.js +14 -21
- package/dist/ai-sdk/index.d.ts +18 -6
- package/dist/ai-sdk/index.js +8 -5
- package/dist/app-data-CV8pqqMi.js +59 -0
- package/dist/approvals/index.d.ts +2 -2
- package/dist/approvals/index.js +3 -3
- package/dist/{approvals-DbYAuNkG.js → approvals-DZL76lKW.js} +35 -19
- package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
- package/dist/{arazzo-BAgw9ap6.js → arazzo-C4BcskbX.js} +6 -5
- package/dist/{arazzo-C9chp3ST.js → arazzo-CD8d0WLb.js} +2 -2
- package/dist/authzen/index.d.ts +3 -3
- package/dist/authzen/index.js +161 -23
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +6 -19
- package/dist/better-supabase/index.d.ts +112 -87
- package/dist/better-supabase/index.js +257 -156
- package/dist/{bind-C_ob3LKa.d.ts → bind-BzqG8SMb.d.ts} +16 -5
- package/dist/{bind-CuzGytRf.js → bind-kk-uyCwH.js} +14 -6
- package/dist/build-instance-DIIV9DbC.js +51 -0
- package/dist/{canonical-json-DemiK4Vp.js → canonical-json-CFKyFOke.js} +1 -3
- package/dist/{capability-2kYTMWDv.d.ts → capability-CksyCWDH.d.ts} +2 -2
- package/dist/{capability-4qPdO9gk.js → capability-txATtKV4.js} +3 -2
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-6yWAX6Ps.js +2 -0
- package/dist/{catalog-CYj-mX2O.js → catalog-WHG4kBq7.js} +2 -2
- package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-DWtg0gGR.js} +17 -10
- package/dist/{catalog-DWF5cYqB.js → catalog-tZUyOfOv.js} +4 -4
- package/dist/claude-agent/index.d.ts +4 -4
- package/dist/claude-agent/index.js +4 -5
- package/dist/clerk/index.d.ts +3 -3
- package/dist/clerk/index.js +8 -33
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +3 -3
- package/dist/cloud/index.d.ts +4 -4
- package/dist/cloud/index.js +9 -15
- package/dist/{cloud-CfwZ39Rl.js → cloud-C7bSDgX4.js} +2 -2
- package/dist/{cloud-DWLXO2af.js → cloud-PeBN3IEn.js} +5 -5
- package/dist/{collect-DSTE3ml9.js → collect-2SrB7lfk.js} +6 -8
- package/dist/collect-Bw2Jdq1q.js +2 -0
- package/dist/{collect-CHB22Sx4.js → collect-CQx4hX4N.js} +3 -3
- package/dist/{commands-BeaGNj_o.js → commands-Cpip4ULx.js} +13 -13
- package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-Ba4blROR.js → compile-CcEu2ZQ1.js} +6 -5
- package/dist/{compile-BkRyBDBw.d.ts → compile-CtTP7eur.d.ts} +2 -2
- package/dist/{config-BJiM-xAm.js → config-BIjREpyE.js} +4 -4
- package/dist/{config-ir40y8m0.js → config-DLt6BvV7.js} +2 -2
- package/dist/{config-report-CdCgbRnV.js → config-report--Xb9APJS.js} +5 -6
- package/dist/{connection-CU8MQxRi.js → connection-DUsczpB5.js} +3 -2
- package/dist/constants-DcJABbLW.js +20 -0
- package/dist/{context-Bcy85rGi.js → context-DieZVJXu.js} +16 -5
- package/dist/convex/index.d.ts +4 -4
- package/dist/convex/index.js +14 -16
- package/dist/create-BBO24vj6.js +324 -0
- package/dist/{credential-8P81ff4u.js → credential-kue0qpbm.js} +4 -4
- package/dist/deciding-columns-Sq12mEA6.js +123 -0
- package/dist/delegation-input-FTxMK3ji.js +19 -0
- package/dist/denied-text-DfRk15uw.d.ts +19 -0
- package/dist/{describe-DMYiCjJV.d.ts → describe-C3ldA9os.d.ts} +11 -2
- package/dist/{describe-B_gVZEVi.js → describe-Cgkp7eP0.js} +5 -4
- package/dist/{diff-C9RDjjhs.js → diff-Cnghh_Ip.js} +2 -2
- package/dist/{diff-2VaeChzX.js → diff-rpqoe7Ag.js} +9 -9
- package/dist/{doctor-Djh6JDEk.js → doctor-CuVJvhV_.js} +2 -2
- package/dist/doctor-owlv6ddh.js +2166 -0
- package/dist/drizzle/index.d.ts +2 -2
- package/dist/drizzle/index.js +6 -14
- package/dist/elysia/index.d.ts +4 -4
- package/dist/elysia/index.js +7 -7
- package/dist/{errors-DPdqI50v.d.ts → errors-OD_4c__H.d.ts} +2 -2
- package/dist/{errors-Cz5zn0mW.js → errors-drnfxYtm.js} +3 -3
- package/dist/{evaluations-tz6D9A2G.js → evaluations-C9YJsv3H.js} +45 -7
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +5 -5
- package/dist/express/index.d.ts +4 -4
- package/dist/express/index.js +8 -7
- package/dist/fastify/index.d.ts +15 -5
- package/dist/fastify/index.js +9 -29
- package/dist/fields-fwfXN01n.js +1016 -0
- package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
- package/dist/{fixtures-ht28Cplr.js → fixtures-h2eG2OZV.js} +5 -6
- package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-7REQZe0H.js} +32 -22
- package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
- package/dist/grantee-CXY-0C87.js +254 -0
- package/dist/grantee-conditions-MQs5NIuX.js +270 -0
- package/dist/grantee-match-fxYp9Nmt.js +141 -0
- package/dist/{helpers-H_KARDVC.js → helpers-C9YmyBIa.js} +7 -6
- package/dist/hono/index.d.ts +4 -4
- package/dist/hono/index.js +8 -8
- package/dist/hosted-CBHgkzDl.js +655 -0
- package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
- package/dist/{http-BIhmLfCc.js → http-BEbdMZ05.js} +2 -24
- package/dist/{rls-import-4lLW_pbb.js → import-BFIlhetD.js} +86 -80
- package/dist/{openapi-import-cmwetbzw.js → import-UHk1N7vH.js} +6 -7
- package/dist/{index-C8z4RjQV.d.ts → index-BwMe94Xq.d.ts} +1 -1
- package/dist/{index-0nNqHspG.d.ts → index-DgM7w96K.d.ts} +2 -2
- package/dist/{index-C7Tw7MaW.d.ts → index-Dz7didpC.d.ts} +3 -3
- package/dist/{index-BNc7Wer_.d.ts → index-UdVQgq48.d.ts} +105 -38
- package/dist/index.d.ts +15 -13
- package/dist/index.js +22 -22
- package/dist/{instance-options-jARdwfPN.d.ts → instance-options-plkrsAog.d.ts} +1 -1
- package/dist/{json-schema-BsAflUGW.js → json-schema-DBkdUuCO.js} +1 -3
- package/dist/jwt/index.d.ts +4 -4
- package/dist/jwt/index.js +13 -13
- package/dist/{kernel-_RRHmnMY.js → kernel-CB3LrIJM.js} +81 -32
- package/dist/kysely/index.d.ts +2 -2
- package/dist/kysely/index.js +6 -14
- package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
- package/dist/load-B_shHrmd.js +2 -0
- package/dist/{load-DhWEB9P1.js → load-C3_oTHyO.js} +11 -3
- package/dist/{manifest-iaoBXHKc.js → manifest-BngWKryL.js} +5 -18
- package/dist/{map-claims-Z4QZOiFF.js → map-claims-AVlBaJoa.js} +7 -16
- package/dist/{map-error-pgUWNNtG.js → map-error-BE27PYkE.js} +5 -5
- package/dist/{markers-B0hYLvMD.js → markers-CiwAOZtO.js} +6 -2
- package/dist/{may-use-DndQ5tQ-.js → may-use-BOn_dR_w.js} +4 -3
- package/dist/mcp/index.d.ts +26 -9
- package/dist/mcp/index.js +456 -446
- package/dist/{rls-migrate-CN4TlJTO.js → migrate-BwlB5-Wk.js} +75 -15
- package/dist/nest/index.d.ts +4 -4
- package/dist/nest/index.js +11 -12
- package/dist/next/index.d.ts +31 -8
- package/dist/next/index.js +29 -33
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +1 -1
- package/dist/node/index.d.ts +4 -4
- package/dist/node/index.js +4 -4
- package/dist/ocsf-uYFXGVw2.js +115 -0
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.js +4 -4
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-CqqEbHsL.js → openapi-BJdBmYDG.js} +19 -23
- package/dist/{openapi-BqRBvAng.js → openapi-DBzHQbov.js} +2 -5
- package/dist/{openapi-CQTsgKoB.js → openapi-DcQ1Kg40.js} +3 -3
- package/dist/orpc/index.d.ts +5 -5
- package/dist/orpc/index.js +9 -11
- package/dist/otel/index.d.ts +2 -2
- package/dist/otel/index.js +26 -23
- package/dist/output-files-CgIsvdSa.js +140 -0
- package/dist/{rls-ownership-DcYlZ09B.js → ownership-BgnbOJFT.js} +387 -379
- package/dist/{package-root-zGkxO3oN.js → package-root-DtNnpmUd.js} +3 -2
- package/dist/{parse-BuyjxhZV.js → parse-CVFQHk6c.js} +3 -3
- package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-C-q02Pal.js} +2 -4
- package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DvYUR0QL.d.ts} +1 -1
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +8 -9
- package/dist/{permdock-CbgvLnuo.js → permdock-L26udHSk.js} +1387 -1220
- package/dist/{rls-fields-CZOMlwHl.js → permission-keys-B26jeyUv.js} +1268 -2053
- package/dist/permission-registry-BsblpB-z.js +385 -0
- package/dist/{permissions-DttnqqxC.d.ts → permission-types-Dg39z9Kv.d.ts} +59 -31
- package/dist/{permissions-RJrovw98.js → permissions-CCJ6lmxB.js} +3 -3
- package/dist/{permissions-CDgvKG5L.d.ts → permissions-COH-KRgT.d.ts} +2 -2
- package/dist/{policy-IB6yxpC3.d.ts → policy-types-sp9P1iIV.d.ts} +661 -582
- package/dist/{policy-D2W7uv19.d.ts → policy-ur0BBK_e.d.ts} +2 -2
- package/dist/{powersync-DE19b9dT.js → powersync-BFSbSElQ.js} +2 -2
- package/dist/powersync-DCDgZgfZ.js +2 -0
- package/dist/{powersync-Dv1_aWF7.js → powersync-cZe4kzhL.js} +26 -27
- package/dist/prisma/index.d.ts +1 -1
- package/dist/prisma/index.js +6 -12
- package/dist/{problem-B43ztKIm.js → problem-CnWraVhC.js} +8 -7
- package/dist/{problem-details-_AMOYz-w.js → problem-details-DECUnXAn.js} +2 -2
- package/dist/{problem-details-DX075l9V.d.ts → problem-details-rIIo8B2C.d.ts} +1 -1
- package/dist/{protected-BdnIEFkY.d.ts → protected-B3_P7xuw.d.ts} +9 -6
- package/dist/{protected-utU-SLuy.js → protected-BN7BUdY1.js} +20 -8
- package/dist/{provider-CSQd7m92.js → provider-Bi-0HNL3.js} +23 -10
- package/dist/{provider-DZY281jO.d.ts → provider-kwHC9CZD.d.ts} +1 -1
- package/dist/{provider-store-Cfbvo4DE.js → provider-store-Ctt-81Ni.js} +6 -3
- package/dist/react/index.d.ts +7 -7
- package/dist/react/index.js +5 -5
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +20 -11
- package/dist/react-native/index.js +33 -17
- package/dist/{refs-KYb4ntaz.js → refs-f3imMjNW.js} +1 -1
- package/dist/relation-walk-BXnlLtg2.js +186 -0
- package/dist/relations-CvmSpG67.js +163 -0
- package/dist/{replay-CYyiLiwO.d.ts → replay-COpmZUfx.d.ts} +1 -1
- package/dist/request-context-BVM7aWNs.js +71 -0
- package/dist/request-context-C2_1lchh.d.ts +5 -0
- package/dist/resource-relations-D1Un3pcz.js +243 -0
- package/dist/retire-BFcxL6un.js +240 -0
- package/dist/{rls-P-BeBx0n.js → rls-55DQTuf5.js} +14 -2
- package/dist/{rls-DiinlRML.js → rls-BEktUaqg.js} +884 -484
- package/dist/{policy-xyWg7QdR.js → role-builder-B4GbKM-i.js} +235 -541
- package/dist/{rpc-DL9Sqvb1.js → rpc-nYC3cR_u.js} +15 -5
- package/dist/{run-BWc9qqvE.js → run-DMcNW_8T.js} +3 -3
- package/dist/{runtime-CB8gQFvS.js → runtime-D_tMXnsq.js} +1 -1
- package/dist/{saas-DhpJjnBA.js → saas-DtaA4JuW.js} +9 -8
- package/dist/{openapi-schema--ARjyfQr.js → schema-Bol6gqXA.js} +3 -5
- package/dist/{schema-D3MnDMxa.js → schema-DjQBcAP9.js} +4 -2
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-BFGBBAqR.js → scim-vFR1O5oG.js} +6 -12
- package/dist/{scopes-CVdPrWN-.js → scopes-tvpa34ep.js} +10 -2
- package/dist/server/index.d.ts +18 -8
- package/dist/server/index.js +8 -8
- package/dist/{server-DlZ28Oz5.js → server-ZYEHtNHS.js} +4 -4
- package/dist/{create-B1d519XK.d.ts → server-types-DdqBwZN_.d.ts} +69 -33
- package/dist/shared-1uhI-e-b.js +294 -0
- package/dist/{shared-DRu8VFVM.js → shared-JhMeOsDF.js} +1 -1
- package/dist/{sink-Bd9PSyfq.js → sink-BTtmfPnE.js} +1 -1
- package/dist/{sink-B1fZzIMf.d.ts → sink-CPN4zLfs.d.ts} +1 -1
- package/dist/{skills-Biq5uup3.js → skills-BZdRQkxs.js} +4 -3
- package/dist/{skills-C5CbhhpG.js → skills-Dle4ZoLF.js} +2 -2
- package/dist/{snapshot-Enxd0n-s.js → snapshot-Dc9CHPME.js} +116 -13
- package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-Cod40D-X.js} +1 -1
- package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-J5T7t_vD.d.ts} +1 -1
- package/dist/solid/index.d.ts +22 -22
- package/dist/solid/index.js +40 -10
- package/dist/{sources-4p0-4xQm.js → sources-B1-b-R0t.js} +25 -26
- package/dist/{sources-BdOYLw9j.d.ts → sources-Dljy06zL.d.ts} +6 -2
- package/dist/{sql-xdg5f4Fi.js → sql-t8teWOfo.js} +19 -3
- package/dist/{src-BDwEZWrZ.js → src-Bb7no-hS.js} +194 -123
- package/dist/ssf/index.d.ts +3 -3
- package/dist/ssf/index.js +58 -61
- package/dist/store-9KO4BlkB.d.ts +147 -0
- package/dist/{store-DMAWwAJV.js → store-ILkLUj_o.js} +4 -4
- package/dist/{store-options-ByhZq1jV.js → store-options-CbUHDoSe.js} +16 -7
- package/dist/{stores-BT2pSCub.js → stores-BHXdSbw9.js} +7 -3
- package/dist/stores-Cxz_80g-.d.ts +31 -0
- package/dist/{stream-DDpvynSB.js → stream-BMui98KK.js} +2 -1
- package/dist/{stream-D1RXubw2.d.ts → stream-Dwsg6WwE.d.ts} +2 -2
- package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
- package/dist/supabase/index.d.ts +4 -4
- package/dist/supabase/index.js +3 -3
- package/dist/supabase/middleware.d.ts +11 -5
- package/dist/supabase/middleware.js +10 -7
- package/dist/{supabase-C0qESOa-.js → supabase-C1TWlnYt.js} +36 -52
- package/dist/{supabase-s4o9s1O1.js → supabase-Dbqr141s.js} +2 -2
- package/dist/supabase-hook-BULg7Ut_.js +2 -0
- package/dist/{supabase-hook-ntETYCQu.js → supabase-hook-Cva3ikXz.js} +730 -781
- package/dist/{supautils-u8jXTGrt.js → supautils-B6HYL9eL.js} +2 -2
- package/dist/svelte/Protected.svelte +9 -2
- package/dist/svelte/index.d.ts +6 -6
- package/dist/svelte/index.js +25 -11
- package/dist/svelte/runtime.d.ts +10 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +6 -6
- package/dist/svelte/source.js +4 -4
- package/dist/{validity-uOp8UpYn.js → tenancy-DshjqY0n.js} +3 -39
- package/dist/terminal/index.d.ts +5 -5
- package/dist/terminal/index.js +18 -34
- package/dist/testing/index.d.ts +89 -77
- package/dist/testing/index.js +1276 -1262
- package/dist/testing/saas/index.d.ts +4 -4
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/thenable-QSmvt-vZ.js +65 -0
- package/dist/{token-CIWxJOCh.js → token-Bx9qy30p.js} +1 -1
- package/dist/tool-refusal-CQ7OEia9.js +41 -0
- package/dist/trpc/index.d.ts +5 -5
- package/dist/trpc/index.js +7 -8
- package/dist/{types-DbjRN55U.d.ts → types-7rJVLWe5.d.ts} +82 -4
- package/dist/{types-aqOHTHcp.d.ts → types-Br9IXUBG.d.ts} +1 -1
- package/dist/types-ByUo56un.d.ts +88 -0
- package/dist/{types-Cq_H8N_X.d.ts → types-C0dee0Qf.d.ts} +2 -2
- package/dist/{types-DxiCGmmd.d.ts → types-CIlTojhq.d.ts} +2 -2
- package/dist/{types-QCuYJh4q.d.ts → types-CUcFZfpU.d.ts} +1 -1
- package/dist/{types-C97IskBw.d.ts → types-CqrjB9o6.d.ts} +1 -1
- package/dist/{types-BnIEz1sU.d.ts → types-DHrmBJLH.d.ts} +24 -6
- package/dist/{types-Dnemwd77.d.ts → types-DpWfQdSh.d.ts} +5 -1
- package/dist/{types-Bvc4oWT6.d.ts → types-DvupZE0w.d.ts} +1 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +1 -1
- package/dist/{usage-B0YuTQRO.js → usage-4G6t643F.js} +2 -2
- package/dist/{usage-ItS7nLcl.js → usage-CG9nOYZw.js} +7 -7
- package/dist/usage-iFLJ0-rr.js +2 -0
- package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-BSSw0l_8.d.ts} +1 -1
- package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
- package/dist/validity-dVrUEPJt.js +62 -0
- package/dist/{verifier-BxBo6I44.js → verifier-DMr_Zmb_.js} +51 -15
- package/dist/{rls-verify-CMv9H3wV.js → verify-BztUB3GX.js} +668 -239
- package/dist/{version-BvacVTZR.js → version-BEogC-rB.js} +1 -1
- package/dist/{views-Soe7bGSC.js → views-CJsrBS1f.js} +23 -1
- package/dist/{vocabulary-DA8a8zif.d.ts → vocabulary-hhEFLFoL.d.ts} +15 -6
- package/dist/vue/index.d.ts +24 -19
- package/dist/vue/index.js +24 -8
- package/dist/watch-C8TpsAl4.js +2 -0
- package/dist/{watch-DVBJ9cBq.js → watch-zeucB7eF.js} +6 -6
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +13 -37
- package/dist/wrap-Cr93qqdP.d.ts +14 -0
- package/dist/wrap-DWsU_5Bd.js +26 -0
- package/package.json +8 -8
- package/schemas/catalog-v1.json +6 -0
- package/skills/permdock-credentials/SKILL.md +1 -1
- package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
- package/skills/permdock-data/references/queries-and-rls.md +4 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
- package/skills/permdock-wire/SKILL.md +2 -2
- package/skills/permdock-wire/references/adapters.md +9 -1
- package/skills/permdock-wire/references/policy-options.md +10 -1
- package/dist/catalog-CRW5Mx_B.js +0 -2
- package/dist/collect-DSWOk19R.js +0 -2
- package/dist/create-CMwIl-w2.js +0 -253
- package/dist/delegation-D8pUEuce.js +0 -103
- package/dist/doctor-DvH0IQff.js +0 -1194
- package/dist/doctor-collect-CjdO0wQX.js +0 -665
- package/dist/evaluation-items-CfCCejQd.js +0 -40
- package/dist/fields-BMl1RxoX.js +0 -26
- package/dist/grantee-Cv5Sogfv.js +0 -602
- package/dist/hosted-DJQw46hn.js +0 -266
- package/dist/instance-options-Dhhcnm3y.js +0 -22
- package/dist/load-DwJUafYN.js +0 -2
- package/dist/map-AcxK9o_D.js +0 -147
- package/dist/permissions-DVoF6dMt.js +0 -662
- package/dist/powersync-CrBX8hY6.js +0 -2
- package/dist/protected-xAr24FEC.d.ts +0 -40
- package/dist/relations-cjOL01hZ.js +0 -505
- package/dist/rls-graph-CHwx0V8q.js +0 -435
- package/dist/rls-shared-Cewd_-zI.js +0 -495
- package/dist/store-jOBYpiXC.d.ts +0 -36
- package/dist/stores-C0IMQrGI.d.ts +0 -52
- package/dist/supabase-config-Bj3iDH2I.js +0 -472
- package/dist/supabase-hook-gd3s8bxB.js +0 -2
- package/dist/thenable-okc2xDsj.js +0 -10
- package/dist/types-CKNSyIf_.d.ts +0 -52
- package/dist/usage-DorAJiU_.js +0 -2
- package/dist/watch-BwxLTYMZ.js +0 -2
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
import { r as isRecord, t as compact } from "./compact-CAf557ZB.js";
|
|
2
|
+
import { n as freezeDeep } from "./freeze-CrU8nQeX.js";
|
|
3
|
+
import { o as isForbiddenKey, u as splitPath } from "./thenable-QSmvt-vZ.js";
|
|
4
|
+
import { a as deny, c as withGrantSchema, d as assertApprovalRelations, f as assertApprovalVersions, i as completeGrant, m as normalizeWhere, n as role, o as flattenGrants, r as allow, s as flattenPermissions } from "./role-builder-B4GbKM-i.js";
|
|
5
|
+
import { n as defineScopes, o as resolveScope } from "./scopes-tvpa34ep.js";
|
|
6
|
+
import { t as parseDuration } from "./duration-xK2jGFVc.js";
|
|
7
|
+
import { a as getResource, c as listPermissions, i as getRegistry, m as annotationsFor, n as findPermission, s as isRegistryTree } from "./permission-registry-BsblpB-z.js";
|
|
8
|
+
import { d as isSelfParented, l as isFieldRelation } from "./resource-relations-D1Un3pcz.js";
|
|
9
|
+
import { _ as listPlans, m as findRole, o as flattenGrantee, r as asGrantee, v as listRoles } from "./grantee-CXY-0C87.js";
|
|
10
|
+
import { n as normalizeValidity } from "./validity-dVrUEPJt.js";
|
|
11
|
+
import { t as canonicalJson } from "./canonical-json-CFKyFOke.js";
|
|
12
|
+
import { a as relationStart, i as relationHops, n as isGraphRelation, t as inheritCondition } from "./grantee-conditions-MQs5NIuX.js";
|
|
13
|
+
import { a as memberOfFields, p as scopeField, u as relatesTo } from "./tenancy-DshjqY0n.js";
|
|
14
|
+
import { n as sha256, t as bytesToBase64Url } from "./sha256-BO0swRXL.js";
|
|
15
|
+
//#region src/core/policy-assertions.ts
|
|
16
|
+
function assertParentGraph(resources) {
|
|
17
|
+
for (const node of resources.values()) {
|
|
18
|
+
if (node.parent === void 0) continue;
|
|
19
|
+
if (!resources.has(node.parent.resource)) throw new Error(`PermDock: resource '${node.name}' parents unknown resource '${node.parent.resource}'`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Graph relation grantees must reach their resource: `through: 'parent'` from
|
|
24
|
+
* the row to its parent resource or along its own self-parent, and never over
|
|
25
|
+
* a `memberOf` relation, which is tenancy, not the object graph.
|
|
26
|
+
*/
|
|
27
|
+
function assertRelationGrants(grants, resources) {
|
|
28
|
+
for (const grant of grants) for (const item of flattenGrantee(grant.to)) {
|
|
29
|
+
if (item.kind !== "relation" || !isGraphRelation(item, resources)) continue;
|
|
30
|
+
const label = `grant ${grant.permission.key} relation '${item.relation}'`;
|
|
31
|
+
const target = resources.get(item.resource);
|
|
32
|
+
const spec = target?.relations[item.relation];
|
|
33
|
+
if (target === void 0 || spec === void 0) throw new Error(`PermDock: ${label}: ${item.resource} does not declare it`);
|
|
34
|
+
if (isFieldRelation(spec) && spec.memberOf !== void 0) throw new Error(`PermDock: ${label} is a memberOf relation; through walks the object graph, not scopes`);
|
|
35
|
+
const row = resources.get(grant.permission.resource);
|
|
36
|
+
if (Array.isArray(item.through)) {
|
|
37
|
+
if (relationHops(item, row, resources) === void 0) throw new Error(`PermDock: ${label}: the links [${item.through.join(", ")}] from ${grant.permission.resource} do not end on ${target.name}${(item.depth ?? 0) > 0 ? ` (or ${target.name} does not parent itself for depth)` : ""}`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (item.through === "parent" && target.name === row?.name && !isSelfParented(target)) throw new Error(`PermDock: ${label}: through: 'parent' needs ${target.name} to parent itself`);
|
|
41
|
+
if (relationStart(item, row, target) === void 0) throw new Error(item.through === void 0 ? `PermDock: ${label} is on ${target.name}, not ${grant.permission.resource}; add through: 'parent'` : `PermDock: ${label}: ${grant.permission.resource} has no parent on ${target.name}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function assertInheritGrants(grants, resources, tree) {
|
|
45
|
+
const keys = new Set(listPermissions(tree).map((leaf) => leaf.key));
|
|
46
|
+
const edges = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const grant of grants) for (const item of flattenGrantee(grant.to)) {
|
|
48
|
+
if (item.kind !== "inherit") continue;
|
|
49
|
+
const label = `grant ${grant.permission.key} inherit '${item.permission}'`;
|
|
50
|
+
if (grant.effect !== "allow") throw new Error(`PermDock: ${label}: inherit() is allowed on an allow only`);
|
|
51
|
+
if (grant.permission.kind !== "instance") throw new Error(`PermDock: ${label}: inherit() needs an instance action; a collection action has no row to follow`);
|
|
52
|
+
if (!keys.has(item.permission)) throw new Error(`PermDock: ${label}: the policy does not declare it`);
|
|
53
|
+
if (inheritCondition(item, resources.get(grant.permission.resource), resources) === void 0) throw new Error(item.through === "parent" ? `PermDock: ${label}: ${grant.permission.resource} has no parent on ${item.resource}` : `PermDock: ${label}: the links [${item.through.join(", ")}] from ${grant.permission.resource} do not end on ${item.resource}`);
|
|
54
|
+
const next = edges.get(grant.permission.key) ?? /* @__PURE__ */ new Set();
|
|
55
|
+
next.add(item.permission);
|
|
56
|
+
edges.set(grant.permission.key, next);
|
|
57
|
+
}
|
|
58
|
+
const done = /* @__PURE__ */ new Set();
|
|
59
|
+
const visit = (key, path) => {
|
|
60
|
+
if (path.includes(key)) throw new Error(`PermDock: inherit() grants form a cycle: ${[...path.slice(path.indexOf(key)), key].join(" -> ")}`);
|
|
61
|
+
if (done.has(key)) return;
|
|
62
|
+
for (const target of edges.get(key) ?? []) visit(target, [...path, key]);
|
|
63
|
+
done.add(key);
|
|
64
|
+
};
|
|
65
|
+
for (const key of edges.keys()) visit(key, []);
|
|
66
|
+
}
|
|
67
|
+
function scopeOfGrant(scope, declared) {
|
|
68
|
+
if (typeof scope !== "string" || scope === "global") return scope;
|
|
69
|
+
const resolved = resolveScope(declared, scope);
|
|
70
|
+
if (resolved === void 0) throw new Error(`PermDock: definePolicy({ scopes }) must declare '${scope}' for on: '${scope}' roles`);
|
|
71
|
+
return resolved;
|
|
72
|
+
}
|
|
73
|
+
function rescopeGrantee(to, declared) {
|
|
74
|
+
const items = flattenGrantee(to);
|
|
75
|
+
if (!items.some((item) => item.kind === "role")) return to;
|
|
76
|
+
const mapped = items.map((item) => item.kind === "role" ? freezeDeep({
|
|
77
|
+
...item,
|
|
78
|
+
scope: scopeOfGrant(item.scope, declared)
|
|
79
|
+
}) : item);
|
|
80
|
+
if (Array.isArray(to)) return freezeDeep(mapped);
|
|
81
|
+
const [single] = mapped;
|
|
82
|
+
return single ?? to;
|
|
83
|
+
}
|
|
84
|
+
/** Resolves `tenant` / `team` aliases to declared names; an undeclared scope throws. */
|
|
85
|
+
function rescopeGrant(grant, declared) {
|
|
86
|
+
const scope = scopeOfGrant(grant.scope, declared);
|
|
87
|
+
const to = rescopeGrantee(grant.to, declared);
|
|
88
|
+
if (scope === grant.scope && to === grant.to) return grant;
|
|
89
|
+
return freezeDeep({
|
|
90
|
+
...grant,
|
|
91
|
+
scope,
|
|
92
|
+
to
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function rescopeBinding(binding, declared) {
|
|
96
|
+
const grants = binding.grants.map((grant) => rescopeGrant(grant, declared));
|
|
97
|
+
const on = typeof binding.on === "string" ? scopeOfGrant(binding.on, declared) : binding.on;
|
|
98
|
+
return freezeDeep(compact({
|
|
99
|
+
...binding,
|
|
100
|
+
grants,
|
|
101
|
+
on
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* No implicit cascade: a grant on scope S reaches a row only through the row's
|
|
106
|
+
* own S key, so every resource an instance grant on S touches declares a
|
|
107
|
+
* `memberOf: S` relation on that key. Without it `where()` and RLS could not
|
|
108
|
+
* narrow the rows.
|
|
109
|
+
*/
|
|
110
|
+
function assertScopeKeys(grants, scopes, resources) {
|
|
111
|
+
for (const grant of grants) {
|
|
112
|
+
if (typeof grant.scope !== "string" || grant.scope === "global" || grant.permission.kind !== "instance") continue;
|
|
113
|
+
const key = scopes.find((scope) => scope.name === grant.scope)?.key;
|
|
114
|
+
const node = resources.get(grant.permission.resource);
|
|
115
|
+
if (key === void 0 || node === void 0) continue;
|
|
116
|
+
const fields = memberOfFields(node, grant.scope, scopes);
|
|
117
|
+
if (fields.length > 1 && !fields.includes(key)) throw new Error(`PermDock: resource '${node.name}' declares several memberOf relations to '${grant.scope}' (${fields.join(", ")}); name the one holding the instance with the scope's key '${key}'`);
|
|
118
|
+
const field = scopeField(node, grant.scope, scopes);
|
|
119
|
+
if (field !== void 0 && relatesTo(node, field, grant.scope, scopes)) continue;
|
|
120
|
+
throw new Error(`PermDock: resource '${node.name}' needs relations: { <name>: { field: '${key}', memberOf: '${grant.scope}' } } for ${grant.permission.key} on '${grant.scope}' roles`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** A `requires` names a permission the policy declares. */
|
|
124
|
+
function assertRequires(grants, tree) {
|
|
125
|
+
const keys = new Set(listPermissions(tree).map((leaf) => leaf.key));
|
|
126
|
+
for (const grant of grants) for (const key of grant.requires ?? []) if (!keys.has(key)) throw new Error(`PermDock: '${grant.permission.key}' requires '${key}', which the policy does not declare`);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/core/policy-delegation.ts
|
|
130
|
+
function delegationTarget(to, index) {
|
|
131
|
+
if (typeof to === "string") return { kind: to };
|
|
132
|
+
if ("actor" in to) return { kind: to.actor };
|
|
133
|
+
const target = compact({
|
|
134
|
+
kind: to.kind,
|
|
135
|
+
id: to.id,
|
|
136
|
+
client: to.client
|
|
137
|
+
});
|
|
138
|
+
if (typeof target.kind !== "string" || target.kind === "") throw new Error(`PermDock: delegations[${index}].to needs an actor kind, such as actor('eve')`);
|
|
139
|
+
if (target.id !== void 0 && (typeof target.id !== "string" || target.id === "")) throw new Error(`PermDock: delegations[${index}].to.id must be a non-empty string`);
|
|
140
|
+
if (target.client !== void 0 && (typeof target.client !== "string" || target.client === "")) throw new Error(`PermDock: delegations[${index}].to.client must be a non-empty string`);
|
|
141
|
+
if (target.id !== void 0 && target.client !== void 0) throw new Error(`PermDock: delegations[${index}].to names both an id and a client; name one`);
|
|
142
|
+
return target;
|
|
143
|
+
}
|
|
144
|
+
function normalizeDelegation(input, index, tree) {
|
|
145
|
+
const label = `delegations[${index}]`;
|
|
146
|
+
const from = asGrantee(input.from);
|
|
147
|
+
const items = flattenGrantee(from);
|
|
148
|
+
if (items.length === 0) throw new Error(`PermDock: ${label}.from names nobody`);
|
|
149
|
+
if (items.some((item) => item.kind === "relation" || item.kind === "inherit")) throw new Error(`PermDock: ${label}.from names a relation or inherit(); a delegation is matched without a row, so name a role or another subject-only grantee`);
|
|
150
|
+
if (items.some((item) => item.kind === "actor")) throw new Error(`PermDock: ${label}.from names an actor; the principal hands over, the actor is \`to\``);
|
|
151
|
+
const leaves = /* @__PURE__ */ new Map();
|
|
152
|
+
for (const leaf of input.permissions.flatMap((item) => flattenPermissions(item))) {
|
|
153
|
+
if (findPermission(tree, leaf.key) === void 0) throw new Error(`PermDock: ${label} names unknown permission '${leaf.key}'`);
|
|
154
|
+
leaves.set(leaf.key, leaf);
|
|
155
|
+
}
|
|
156
|
+
const keys = [...leaves.keys()].toSorted();
|
|
157
|
+
if (keys.length === 0) throw new Error(`PermDock: ${label}.permissions is empty`);
|
|
158
|
+
return compact({
|
|
159
|
+
from,
|
|
160
|
+
to: delegationTarget(input.to, index),
|
|
161
|
+
permissions: keys,
|
|
162
|
+
readOnly: [...leaves.values()].filter((leaf) => annotationsFor(leaf).readOnlyHint).map((leaf) => leaf.key).toSorted(),
|
|
163
|
+
validity: normalizeValidity(input, label)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** RFC 6749 section 3.3 `scope-token`: printable ASCII without space, `"` or `\`. */
|
|
167
|
+
function isScopeToken(value) {
|
|
168
|
+
return /^[!#-[\]-~]+$/u.test(value);
|
|
169
|
+
}
|
|
170
|
+
function normalizeOAuthScopes(input, tree) {
|
|
171
|
+
if (input === void 0) return [];
|
|
172
|
+
const own = new Set(listPermissions(tree).map((leaf) => leaf.scope));
|
|
173
|
+
return Object.entries(input).map(([scope, items]) => {
|
|
174
|
+
if (!isScopeToken(scope)) throw new Error(`PermDock: oauthScopes key '${scope}' is not an OAuth scope token`);
|
|
175
|
+
if (own.has(scope)) throw new Error(`PermDock: oauthScopes key '${scope}' is a permission's own scope`);
|
|
176
|
+
const leaves = /* @__PURE__ */ new Map();
|
|
177
|
+
for (const leaf of (Array.isArray(items) ? items : []).flatMap((item) => flattenPermissions(item))) {
|
|
178
|
+
if (findPermission(tree, leaf.key) === void 0) throw new Error(`PermDock: oauthScopes '${scope}' names unknown permission '${leaf.key}'`);
|
|
179
|
+
leaves.set(leaf.key, leaf);
|
|
180
|
+
}
|
|
181
|
+
if (leaves.size === 0) throw new Error(`PermDock: oauthScopes '${scope}' covers no permission`);
|
|
182
|
+
const sorted = [...leaves.values()].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
183
|
+
return {
|
|
184
|
+
scope,
|
|
185
|
+
permissions: sorted.map((leaf) => leaf.key),
|
|
186
|
+
scopes: sorted.map((leaf) => leaf.scope)
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/core/policy-fingerprint.ts
|
|
192
|
+
function canonicalPolicy(grants, delegations, levels) {
|
|
193
|
+
const payload = canonicalGrants(grants);
|
|
194
|
+
const withDelegations = delegations.length === 0 ? payload : `${payload}\n${JSON.stringify(delegations)}`;
|
|
195
|
+
return levels === void 0 ? withDelegations : `${withDelegations}\nlevels:${canonicalJson(levels)}`;
|
|
196
|
+
}
|
|
197
|
+
function canonicalGrants(grants) {
|
|
198
|
+
const payload = grants.map((grant) => ({
|
|
199
|
+
permission: grant.permission.key,
|
|
200
|
+
effect: grant.effect,
|
|
201
|
+
to: grant.to,
|
|
202
|
+
role: grant.role,
|
|
203
|
+
where: grant.where,
|
|
204
|
+
check: grant.check,
|
|
205
|
+
approval: grant.approval,
|
|
206
|
+
portable: grant.portable,
|
|
207
|
+
fields: grant.fields,
|
|
208
|
+
scope: grant.scope,
|
|
209
|
+
name: grant.name,
|
|
210
|
+
purpose: grant.purpose,
|
|
211
|
+
breakGlass: grant.breakGlass,
|
|
212
|
+
viaOnly: grant.viaOnly,
|
|
213
|
+
validity: grant.validity,
|
|
214
|
+
requires: grant.requires
|
|
215
|
+
}));
|
|
216
|
+
return JSON.stringify(payload);
|
|
217
|
+
}
|
|
218
|
+
const POLICY_X = Symbol.for("permdock.policy-x");
|
|
219
|
+
/** The app data schemas of `policy`. */
|
|
220
|
+
function policyX(policy) {
|
|
221
|
+
if (!Object.hasOwn(policy, POLICY_X)) return;
|
|
222
|
+
return Reflect.get(policy, POLICY_X);
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/core/policy.ts
|
|
226
|
+
function indexPolicy(roles, grants, vocabulary) {
|
|
227
|
+
const declaredRoles = new Set(roles.map((item) => item.name));
|
|
228
|
+
for (const leaf of listRoles(vocabulary.roles)) declaredRoles.add(leaf.key);
|
|
229
|
+
const supports = roles.flatMap((binding) => binding.support === void 0 ? [] : [binding.support]);
|
|
230
|
+
const listed = grants.length > 0 ? grants : roles.flatMap((binding) => binding.grants);
|
|
231
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const grant of listed) {
|
|
233
|
+
const bucket = byKey.get(grant.permission.key);
|
|
234
|
+
if (bucket === void 0) byKey.set(grant.permission.key, [grant]);
|
|
235
|
+
else bucket.push(grant);
|
|
236
|
+
}
|
|
237
|
+
for (const bucket of byKey.values()) Object.freeze(bucket);
|
|
238
|
+
return freezeDeep({
|
|
239
|
+
declaredRoles,
|
|
240
|
+
supports,
|
|
241
|
+
grantsByKey: byKey
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
function normalizeLevels(resources) {
|
|
245
|
+
const out = {};
|
|
246
|
+
for (const node of resources.values()) {
|
|
247
|
+
const entries = Object.entries({ ...node.levels });
|
|
248
|
+
if (entries.length === 0) continue;
|
|
249
|
+
const levels = {};
|
|
250
|
+
for (const [name, input] of entries) try {
|
|
251
|
+
levels[name] = Object.keys(input).length === 0 ? {
|
|
252
|
+
op: "and",
|
|
253
|
+
conditions: []
|
|
254
|
+
} : normalizeWhere(input);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
throw new Error(`PermDock: level '${name}' on '${node.name}' is not a valid condition`, { cause: error });
|
|
257
|
+
}
|
|
258
|
+
out[node.name] = levels;
|
|
259
|
+
}
|
|
260
|
+
return Object.keys(out).length === 0 ? void 0 : freezeDeep(out);
|
|
261
|
+
}
|
|
262
|
+
/** Declared levels of `resource` with their conditions, in declaration order. */
|
|
263
|
+
function policyLevels(policy, resource) {
|
|
264
|
+
const levels = policy.levels;
|
|
265
|
+
return levels !== void 0 && Object.hasOwn(levels, resource) ? Object.entries({ ...levels[resource] }) : [];
|
|
266
|
+
}
|
|
267
|
+
/** The condition of `level` on `resource`, if the policy declares it. */
|
|
268
|
+
function levelCondition(policy, resource, level) {
|
|
269
|
+
return policyLevels(policy, resource).find(([name]) => name === level)?.[1];
|
|
270
|
+
}
|
|
271
|
+
/** Declared level names of `resource`, in declaration order. */
|
|
272
|
+
function levelNames(policy, resource) {
|
|
273
|
+
return policyLevels(policy, resource).map(([name]) => name);
|
|
274
|
+
}
|
|
275
|
+
function isVocabularyInput(value) {
|
|
276
|
+
return value !== null && typeof value === "object" && "permissions" in value && isRegistryTree(value.permissions);
|
|
277
|
+
}
|
|
278
|
+
function mergeBindings(items) {
|
|
279
|
+
const merged = /* @__PURE__ */ new Map();
|
|
280
|
+
for (const item of items) {
|
|
281
|
+
const existing = merged.get(item.name);
|
|
282
|
+
if (existing === void 0) {
|
|
283
|
+
merged.set(item.name, item);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
merged.set(item.name, freezeDeep(compact({
|
|
287
|
+
name: item.name,
|
|
288
|
+
grants: [...existing.grants, ...item.grants],
|
|
289
|
+
on: existing.on,
|
|
290
|
+
assignable: existing.assignable,
|
|
291
|
+
exclusiveWith: existing.exclusiveWith ?? item.exclusiveWith,
|
|
292
|
+
min: existing.min ?? item.min,
|
|
293
|
+
max: existing.max ?? item.max,
|
|
294
|
+
transferOnly: existing.transferOnly ?? item.transferOnly,
|
|
295
|
+
assigns: existing.assigns ?? item.assigns,
|
|
296
|
+
for: existing.for ?? item.for,
|
|
297
|
+
meta: existing.meta ?? item.meta,
|
|
298
|
+
activation: existing.activation ?? item.activation,
|
|
299
|
+
support: existing.support ?? item.support
|
|
300
|
+
})));
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
roles: [...merged.values()],
|
|
304
|
+
rolesByName: merged
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function definePolicy(permissions, options) {
|
|
308
|
+
const vocabulary = isVocabularyInput(permissions) ? permissions : { permissions };
|
|
309
|
+
const tree = vocabulary.permissions;
|
|
310
|
+
const mapper = options.principal ?? options.subject;
|
|
311
|
+
if (mapper === void 0) throw new Error("PermDock: definePolicy requires principal or subject");
|
|
312
|
+
const resources = getRegistry(tree);
|
|
313
|
+
assertParentGraph(resources);
|
|
314
|
+
const scopes = defineScopes(options.scopes);
|
|
315
|
+
const grantSchema = options.x?.grant;
|
|
316
|
+
const { roles, rolesByName } = mergeBindings((options.roles ?? []).map((binding) => {
|
|
317
|
+
const rescoped = rescopeBinding(binding, scopes);
|
|
318
|
+
const grants = rescoped.grants.map((grant) => withGrantSchema(grant, grantSchema));
|
|
319
|
+
return grants.every((grant, index) => grant === rescoped.grants[index]) ? rescoped : freezeDeep({
|
|
320
|
+
...rescoped,
|
|
321
|
+
grants
|
|
322
|
+
});
|
|
323
|
+
}));
|
|
324
|
+
const declared = new Set(roles.map((item) => item.name));
|
|
325
|
+
for (const leaf of listRoles(vocabulary.roles)) declared.add(leaf.key);
|
|
326
|
+
for (const binding of roles) for (const name of binding.assigns ?? []) if (!declared.has(name)) throw new Error(`PermDock: role '${binding.name}' names undeclared role '${name}'`);
|
|
327
|
+
const fromBindings = roles.flatMap((item) => item.grants);
|
|
328
|
+
const fromGrants = flattenGrants(options.grants ?? []).map(completeGrant).map((grant) => withGrantSchema(rescopeGrant(grant, scopes), grantSchema));
|
|
329
|
+
const grants = [...fromBindings, ...fromGrants];
|
|
330
|
+
assertScopeKeys(grants, scopes, resources);
|
|
331
|
+
assertApprovalVersions(grants, resources);
|
|
332
|
+
assertApprovalRelations(grants, resources);
|
|
333
|
+
assertRelationGrants(grants, resources);
|
|
334
|
+
assertRequires(grants, tree);
|
|
335
|
+
assertInheritGrants(grants, resources, tree);
|
|
336
|
+
const delegations = (options.delegations ?? []).map((item, index) => normalizeDelegation(item, index, tree));
|
|
337
|
+
const levels = normalizeLevels(resources);
|
|
338
|
+
const oauthScopes = normalizeOAuthScopes(options.oauthScopes, tree);
|
|
339
|
+
const fingerprint = bytesToBase64Url(sha256(oauthScopes.length === 0 ? canonicalPolicy(grants, delegations, levels) : `${canonicalPolicy(grants, delegations, levels)}\noauthScopes:${canonicalJson(oauthScopes)}`));
|
|
340
|
+
const hostable = [...new Set((options.hostable ?? []).flatMap((item) => flattenPermissions(item).map((leaf) => leaf.key)))].toSorted();
|
|
341
|
+
const fresh = [...new Set((options.fresh ?? []).flatMap((item) => flattenPermissions(item).map((leaf) => leaf.key)))].toSorted();
|
|
342
|
+
return freezeDeep({
|
|
343
|
+
permissions: tree,
|
|
344
|
+
roles,
|
|
345
|
+
rolesByName,
|
|
346
|
+
grants,
|
|
347
|
+
vocabulary,
|
|
348
|
+
scopes,
|
|
349
|
+
principal: mapper,
|
|
350
|
+
subject: mapper,
|
|
351
|
+
context: options.context,
|
|
352
|
+
validate: options.validate ?? "boundary",
|
|
353
|
+
onDenied: options.onDenied,
|
|
354
|
+
fingerprint,
|
|
355
|
+
resources,
|
|
356
|
+
...options.x === void 0 ? {} : { [POLICY_X]: options.x },
|
|
357
|
+
providers: options.providers,
|
|
358
|
+
hostable,
|
|
359
|
+
fresh,
|
|
360
|
+
delegations: delegations.length === 0 ? void 0 : delegations,
|
|
361
|
+
levels,
|
|
362
|
+
oauthScopes: oauthScopes.length === 0 ? void 0 : oauthScopes,
|
|
363
|
+
index: indexPolicy(roles, grants, vocabulary)
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function exclusivePairs(policy) {
|
|
367
|
+
const pairs = /* @__PURE__ */ new Map();
|
|
368
|
+
for (const binding of policy.roles) if (binding.exclusiveWith !== void 0) pairs.set(binding.name, binding.exclusiveWith);
|
|
369
|
+
return pairs;
|
|
370
|
+
}
|
|
371
|
+
function grantList(policy) {
|
|
372
|
+
if (policy.grants !== void 0 && policy.grants.length > 0) return policy.grants;
|
|
373
|
+
return policy.roles.flatMap((binding) => binding.grants);
|
|
374
|
+
}
|
|
375
|
+
function declaredRoleNames(policy) {
|
|
376
|
+
return policy.index.declaredRoles;
|
|
377
|
+
}
|
|
378
|
+
function separationConflicts(policy, memberships) {
|
|
379
|
+
const exclusive = exclusivePairs(policy);
|
|
380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
381
|
+
const conflicts = [];
|
|
382
|
+
for (const membership of memberships) {
|
|
383
|
+
const held = new Set(membership.roles);
|
|
384
|
+
for (const name of held) for (const other of exclusive.get(name) ?? []) {
|
|
385
|
+
if (!held.has(other)) continue;
|
|
386
|
+
const pair = [name, other].toSorted();
|
|
387
|
+
const key = `${membership.principal ?? ""}:${membership.tenant ?? ""}:${pair.join("+")}`;
|
|
388
|
+
if (seen.has(key)) continue;
|
|
389
|
+
seen.add(key);
|
|
390
|
+
conflicts.push(compact({
|
|
391
|
+
principal: membership.principal ?? "",
|
|
392
|
+
tenant: membership.tenant,
|
|
393
|
+
roles: pair
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return conflicts;
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/core/hosted.ts
|
|
401
|
+
function memoryPolicySource(document = null) {
|
|
402
|
+
const current = document === null ? null : parsePolicyDocument(document);
|
|
403
|
+
return Object.freeze({
|
|
404
|
+
current: () => current,
|
|
405
|
+
refresh: () => Promise.resolve()
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
function rejectUnsafe(value) {
|
|
409
|
+
if (value === null || typeof value !== "object") return;
|
|
410
|
+
if (Array.isArray(value)) {
|
|
411
|
+
for (const item of value) rejectUnsafe(item);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
for (const key of Object.keys(value)) {
|
|
415
|
+
if (isForbiddenKey(key)) throw new Error(`PermDock: unsafe policy document key '${key}'`);
|
|
416
|
+
rejectUnsafe(value[key]);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/** Validates the document envelope; per-grant checks happen at merge time. */
|
|
420
|
+
function parsePolicyDocument(json) {
|
|
421
|
+
const input = typeof json === "string" ? JSON.parse(json) : json;
|
|
422
|
+
if (!isRecord(input)) throw new TypeError("PermDock: policy document must be an object");
|
|
423
|
+
const copy = JSON.parse(JSON.stringify(input));
|
|
424
|
+
rejectUnsafe(copy);
|
|
425
|
+
if (copy["v"] !== 1) throw new Error(`PermDock: unsupported policy document version '${String(copy["v"])}'`);
|
|
426
|
+
if (typeof copy["id"] !== "string" || typeof copy["fingerprint"] !== "string" || typeof copy["catalog"] !== "string" || typeof copy["issuedAt"] !== "number" || !Array.isArray(copy["grants"])) throw new TypeError("PermDock: malformed policy document");
|
|
427
|
+
return freezeDeep(copy);
|
|
428
|
+
}
|
|
429
|
+
const COMPARISONS = /* @__PURE__ */ new Set([
|
|
430
|
+
"eq",
|
|
431
|
+
"ne",
|
|
432
|
+
"gt",
|
|
433
|
+
"gte",
|
|
434
|
+
"lt",
|
|
435
|
+
"lte",
|
|
436
|
+
"contains"
|
|
437
|
+
]);
|
|
438
|
+
function safePath(path) {
|
|
439
|
+
if (typeof path !== "string" || path.length === 0) return false;
|
|
440
|
+
return splitPath(path).every((segment) => !isForbiddenKey(segment));
|
|
441
|
+
}
|
|
442
|
+
function portableValue(value) {
|
|
443
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return true;
|
|
444
|
+
if (Array.isArray(value)) return value.every(portableValue);
|
|
445
|
+
if (!isRecord(value)) return false;
|
|
446
|
+
const keys = Object.keys(value);
|
|
447
|
+
if (keys.length !== 1) return false;
|
|
448
|
+
if (keys[0] === "ref") return safePath(value["ref"]);
|
|
449
|
+
return keys[0] === "date" && typeof value["date"] === "string";
|
|
450
|
+
}
|
|
451
|
+
/** True for the portable subset only: no `opaque`, no `sqlFunction`, no unknown op. */
|
|
452
|
+
function isPortableCondition(value) {
|
|
453
|
+
if (!isRecord(value) || typeof value["op"] !== "string") return false;
|
|
454
|
+
const op = value["op"];
|
|
455
|
+
if (COMPARISONS.has(op)) return safePath(value["field"]) && portableValue(value["value"]);
|
|
456
|
+
switch (op) {
|
|
457
|
+
case "in":
|
|
458
|
+
case "notIn": return safePath(value["field"]) && (Array.isArray(value["value"]) || isRecord(value["value"])) && portableValue(value["value"]);
|
|
459
|
+
case "isNull": return safePath(value["field"]) && typeof value["value"] === "boolean";
|
|
460
|
+
case "and":
|
|
461
|
+
case "or": return Array.isArray(value["conditions"]) && value["conditions"].every(isPortableCondition);
|
|
462
|
+
case "not": return isPortableCondition(value["condition"]);
|
|
463
|
+
case "memberOf": return (value["scope"] === "tenant" || value["scope"] === "team" || value["scope"] === "resource") && safePath(value["field"]) && Array.isArray(value["roles"]) && value["roles"].every((item) => typeof item === "string") && (value["resource"] === void 0 || typeof value["resource"] === "string") && (value["parents"] === void 0 || Array.isArray(value["parents"]) && value["parents"].every((parent) => safePath(parent) || isRecord(parent) && safePath(parent["field"]) && typeof parent["resource"] === "string"));
|
|
464
|
+
default: return false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function approvalRank(approval) {
|
|
468
|
+
if (approval === void 0) return;
|
|
469
|
+
if (approval === "human") return {
|
|
470
|
+
distinct: true,
|
|
471
|
+
stale: false,
|
|
472
|
+
quorum: 1,
|
|
473
|
+
ttl: Number.POSITIVE_INFINITY,
|
|
474
|
+
escalates: false
|
|
475
|
+
};
|
|
476
|
+
return {
|
|
477
|
+
by: approval.stages === void 0 ? JSON.stringify(approval.by) : JSON.stringify([approval.mode, approval.stages]),
|
|
478
|
+
distinct: approval.distinct !== false,
|
|
479
|
+
stale: approval.staleOn === "resource-change",
|
|
480
|
+
quorum: approval.quorum ?? 1,
|
|
481
|
+
ttl: parseDuration(approval.ttl) ?? Number.POSITIVE_INFINITY,
|
|
482
|
+
escalates: approval.escalation !== void 0
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/** A hosted approval must meet every approval a code allow requires on the permission. */
|
|
486
|
+
function approvalAtLeast(hosted, code) {
|
|
487
|
+
const mine = approvalRank(hosted);
|
|
488
|
+
for (const required of code) {
|
|
489
|
+
const theirs = approvalRank(required);
|
|
490
|
+
if (theirs === void 0) continue;
|
|
491
|
+
if (mine === void 0) return false;
|
|
492
|
+
if (theirs.distinct && !mine.distinct) return false;
|
|
493
|
+
if (theirs.stale && !mine.stale) return false;
|
|
494
|
+
if (theirs.by !== void 0 && mine.by !== theirs.by) return false;
|
|
495
|
+
if (mine.quorum < theirs.quorum || mine.ttl > theirs.ttl) return false;
|
|
496
|
+
if (mine.escalates && !theirs.escalates) return false;
|
|
497
|
+
}
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
/** A grantee kind this build does not know is never declared, so the hosted grant is dropped. */
|
|
501
|
+
function undeclaredKind(_grantee) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
function declaredGrantee(policy, grantee, permissionResource) {
|
|
505
|
+
switch (grantee.kind) {
|
|
506
|
+
case "role": return declaredRoleNames(policy).has(grantee.role);
|
|
507
|
+
case "plan": return listPlans(policy.vocabulary.plans).some((leaf) => leaf.key === grantee.plan);
|
|
508
|
+
case "relation": return grantee.resource === permissionResource && getResource(policy.permissions, grantee.resource)?.relations[grantee.relation] !== void 0 && (grantee.through === void 0 || grantee.through === "parent" && isSelfParented(getResource(policy.permissions, grantee.resource))) && (grantee.depth === void 0 || grantee.through === "parent" && Number.isInteger(grantee.depth) && grantee.depth >= 0 && grantee.depth <= 32);
|
|
509
|
+
case "anyone":
|
|
510
|
+
case "authenticated":
|
|
511
|
+
case "actor":
|
|
512
|
+
case "assurance":
|
|
513
|
+
case "inherit": return false;
|
|
514
|
+
default: return undeclaredKind(grantee);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/** Approvers are declared roles, plans or relations, or any authenticated human. */
|
|
518
|
+
function approvalAcceptable(policy, approval, permissionResource) {
|
|
519
|
+
if (approval === void 0 || approval === "human") return true;
|
|
520
|
+
if (!isRecord(approval)) return false;
|
|
521
|
+
if (approval["mode"] !== void 0 || approval["stages"] !== void 0) return false;
|
|
522
|
+
if (approval["distinct"] !== void 0 && typeof approval["distinct"] !== "boolean") return false;
|
|
523
|
+
if (approval["staleOn"] !== void 0 && (approval["staleOn"] !== "resource-change" || getResource(policy.permissions, permissionResource)?.version === void 0)) return false;
|
|
524
|
+
if (approval["quorum"] !== void 0 && (!Number.isInteger(approval["quorum"]) || approval["quorum"] < 1)) return false;
|
|
525
|
+
if (approval["ttl"] !== void 0 && (typeof approval["ttl"] !== "string" || parseDuration(approval["ttl"]) === void 0)) return false;
|
|
526
|
+
const escalation = approval["escalation"];
|
|
527
|
+
if (escalation !== void 0 && (!isRecord(escalation) || typeof escalation["after"] !== "string" || parseDuration(escalation["after"]) === void 0 || !granteesAcceptable(policy, escalation["to"], permissionResource))) return false;
|
|
528
|
+
if (approval["by"] === void 0) return true;
|
|
529
|
+
return granteesAcceptable(policy, approval["by"], permissionResource);
|
|
530
|
+
}
|
|
531
|
+
function granteesAcceptable(policy, raw, permissionResource) {
|
|
532
|
+
if (raw === void 0) return false;
|
|
533
|
+
const items = flattenGrantee(raw);
|
|
534
|
+
return items.length > 0 && items.every((item) => isRecord(item) && item.kind !== "relation" && (item.kind === "authenticated" || declaredGrantee(policy, item, permissionResource)));
|
|
535
|
+
}
|
|
536
|
+
function buildGrant(policy, document, raw) {
|
|
537
|
+
if (!isRecord(raw) || typeof raw["id"] !== "string" || typeof raw["permission"] !== "string" || raw["effect"] !== void 0 && raw["effect"] !== "allow" && raw["effect"] !== "deny" || raw["to"] === void 0) return {
|
|
538
|
+
ok: false,
|
|
539
|
+
reason: "invalid"
|
|
540
|
+
};
|
|
541
|
+
const permission = findPermission(policy.permissions, raw["permission"]);
|
|
542
|
+
if (permission === void 0) return {
|
|
543
|
+
ok: false,
|
|
544
|
+
reason: "unknown-permission"
|
|
545
|
+
};
|
|
546
|
+
if (!policy.hostable.includes(permission.key)) return {
|
|
547
|
+
ok: false,
|
|
548
|
+
reason: "not-hostable"
|
|
549
|
+
};
|
|
550
|
+
const to = flattenGrantee(raw["to"]);
|
|
551
|
+
if (to.length === 0 || !to.every((item) => isRecord(item) && declaredGrantee(policy, item, permission.resource))) return {
|
|
552
|
+
ok: false,
|
|
553
|
+
reason: "unknown-grantee"
|
|
554
|
+
};
|
|
555
|
+
for (const condition of [raw["where"], raw["check"]]) if (condition !== void 0 && !isPortableCondition(condition)) return {
|
|
556
|
+
ok: false,
|
|
557
|
+
reason: "non-portable"
|
|
558
|
+
};
|
|
559
|
+
if (!approvalAcceptable(policy, raw["approval"], permission.resource)) return {
|
|
560
|
+
ok: false,
|
|
561
|
+
reason: "invalid"
|
|
562
|
+
};
|
|
563
|
+
if (raw["fields"] !== void 0 && !(Array.isArray(raw["fields"]) && raw["fields"].every((field) => typeof field === "string"))) return {
|
|
564
|
+
ok: false,
|
|
565
|
+
reason: "invalid"
|
|
566
|
+
};
|
|
567
|
+
const effect = raw["effect"] ?? "allow";
|
|
568
|
+
const options = compact({
|
|
569
|
+
where: raw["where"] === void 0 ? void 0 : normalizeWhere(raw["where"]),
|
|
570
|
+
check: raw["check"] === void 0 ? void 0 : normalizeWhere(raw["check"]),
|
|
571
|
+
approval: raw["approval"],
|
|
572
|
+
fields: raw["fields"]
|
|
573
|
+
});
|
|
574
|
+
let built;
|
|
575
|
+
try {
|
|
576
|
+
const roleItem = to.length === 1 && to[0]?.kind === "role" ? to[0] : null;
|
|
577
|
+
if (roleItem === null) {
|
|
578
|
+
const partial = (effect === "allow" ? allow : deny)(permission, {
|
|
579
|
+
...options,
|
|
580
|
+
to
|
|
581
|
+
});
|
|
582
|
+
built = completeGrant(partial);
|
|
583
|
+
} else {
|
|
584
|
+
const binding = policy.rolesByName.get(roleItem.role);
|
|
585
|
+
const leaf = findRole(policy.vocabulary.roles, roleItem.role);
|
|
586
|
+
const [first] = role(leaf ?? roleItem.role, [(effect === "allow" ? allow : deny)(permission, options)], binding?.on === void 0 ? {} : { on: binding.on }).grants;
|
|
587
|
+
if (first === void 0) throw new TypeError("PermDock: role produced no grant");
|
|
588
|
+
built = first;
|
|
589
|
+
}
|
|
590
|
+
} catch {
|
|
591
|
+
return {
|
|
592
|
+
ok: false,
|
|
593
|
+
reason: "invalid"
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
if (effect === "allow") {
|
|
597
|
+
const codeApprovals = policy.grants.filter((grant) => grant.effect === "allow" && grant.hosted === void 0 && grant.permission.key === permission.key).map((grant) => grant.approval);
|
|
598
|
+
if (!approvalAtLeast(built.approval, codeApprovals)) return {
|
|
599
|
+
ok: false,
|
|
600
|
+
reason: "weaker-approval"
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
ok: true,
|
|
605
|
+
grant: freezeDeep({
|
|
606
|
+
...built,
|
|
607
|
+
hosted: {
|
|
608
|
+
document: document.fingerprint,
|
|
609
|
+
grant: raw["id"]
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* The code policy plus the hosted grants that pass every rule. Hosted
|
|
616
|
+
* allows OR with code allows and any deny still wins, so a hosted grant can
|
|
617
|
+
* never override a code deny; one that fails a rule is dropped, not thrown.
|
|
618
|
+
*/
|
|
619
|
+
function mergeHostedGrants(policy, document) {
|
|
620
|
+
if (document === null || document.grants.length === 0) return {
|
|
621
|
+
policy,
|
|
622
|
+
dropped: []
|
|
623
|
+
};
|
|
624
|
+
const merged = [];
|
|
625
|
+
const dropped = [];
|
|
626
|
+
for (const raw of document.grants) {
|
|
627
|
+
const result = buildGrant(policy, document, raw);
|
|
628
|
+
if (result.ok) {
|
|
629
|
+
merged.push(result.grant);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
dropped.push(freezeDeep({
|
|
633
|
+
kind: "hosted-grant-dropped",
|
|
634
|
+
document: document.fingerprint,
|
|
635
|
+
grant: isRecord(raw) && typeof raw.id === "string" ? raw.id : "",
|
|
636
|
+
reason: result.reason
|
|
637
|
+
}));
|
|
638
|
+
}
|
|
639
|
+
if (merged.length === 0) return {
|
|
640
|
+
policy,
|
|
641
|
+
dropped
|
|
642
|
+
};
|
|
643
|
+
const grants = [...policy.grants, ...merged];
|
|
644
|
+
return {
|
|
645
|
+
policy: freezeDeep({
|
|
646
|
+
...policy,
|
|
647
|
+
grants,
|
|
648
|
+
index: indexPolicy(policy.roles, grants, policy.vocabulary),
|
|
649
|
+
fingerprint: bytesToBase64Url(sha256(`${policy.fingerprint}:${document.fingerprint}`))
|
|
650
|
+
}),
|
|
651
|
+
dropped
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
//#endregion
|
|
655
|
+
export { declaredRoleNames as a, levelCondition as c, separationConflicts as d, policyX as f, parsePolicyDocument as i, levelNames as l, memoryPolicySource as n, definePolicy as o, mergeHostedGrants as r, grantList as s, isPortableCondition as t, policyLevels as u };
|