permdock 0.6.0 → 0.7.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 +1 -0
- package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
- package/dist/a2a/index.d.ts +5 -5
- package/dist/a2a/index.js +4 -4
- package/dist/{adoption-scan-DCYe60Pi.js → adoption-scan-D-feE7BE.js} +1 -1
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1 -1
- package/dist/ai-sdk/index.d.ts +21 -6
- package/dist/ai-sdk/index.js +19 -9
- package/dist/{approval-prompt-DvSSPQJX.d.ts → approval-prompt-K0faOLFp.d.ts} +2 -2
- package/dist/approvals/index.d.ts +4 -4
- package/dist/approvals/index.js +4 -4
- package/dist/{approvals-p1PGoVjb.js → approvals-CMRfsbBh.js} +74 -48
- package/dist/{arazzo-41WuzHou.js → arazzo-ClrF3PVb.js} +2 -2
- package/dist/{arazzo-CaUVBn7D.js → arazzo-rdeJ8n1J.js} +4 -4
- package/dist/{args-IIolxxKA.js → args-CC3OwyHW.js} +112 -2
- package/dist/astro/index.d.ts +6 -6
- package/dist/astro/index.js +2 -2
- package/dist/{audience-DabClFLQ.js → audience-BrMs8c4o.js} +1 -1
- package/dist/{auth-DRLEizbL.js → auth-CscUX8Sy.js} +3 -4
- package/dist/authzen/index.d.ts +162 -22
- package/dist/authzen/index.js +315 -146
- package/dist/{automation-BNM3TtDo.d.ts → automation-DOtAFWdy.d.ts} +3 -3
- package/dist/{automation-DVjOckjV.js → automation-ySsxp_Yp.js} +1 -1
- package/dist/base-url-BQbddZO5.js +9 -0
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +2 -2
- package/dist/better-supabase/index.d.ts +50 -8
- package/dist/better-supabase/index.js +94 -32
- package/dist/{bind-CAvgUuKs.js → bind-D1nO6fiC.js} +5 -5
- package/dist/{bind-DOyrv6zi.d.ts → bind-JDuvxjuW.d.ts} +7 -7
- package/dist/{build-instance-Bu_eDqYw.js → build-instance-DxE7czo9.js} +1 -1
- package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
- package/dist/{capability-Drg-1YhL.d.ts → capability-D4UC9TTW.d.ts} +2 -2
- package/dist/{capability-N1K9IpqS.js → capability-L7NnfhiE.js} +1 -1
- package/dist/{casbin-Cr7RLtA3.js → casbin-BHijLuHY.js} +2 -2
- package/dist/{casbin-Dyu2gnUD.js → casbin-BW9pS47J.js} +1 -1
- package/dist/{casl-64gpT-8x.js → casl-CmJlgziS.js} +2 -2
- package/dist/{casl-DjEDhTCP.js → casl-UbI8v-Wq.js} +1 -1
- package/dist/catalog/index.d.ts +70 -10
- package/dist/catalog/index.js +112 -6
- package/dist/catalog-BCCjSAdH.js +1 -0
- package/dist/{catalog-DZVbrF-z.js → catalog-BaMQfQ6Y.js} +3 -3
- package/dist/{catalog-HD2gf5mY.js → catalog-D69WhFii.js} +43 -13
- package/dist/{catalog-doc-Cdn7-0pQ.js → catalog-doc-CV9s6B1S.js} +28 -5
- package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-C144oW4M.js} +1 -1
- package/dist/{create-CQWIpUG-.js → challenge-nGWma08S.js} +122 -25
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +2 -2
- package/dist/chat-sdk/index.d.ts +3 -3
- package/dist/checks-BC_0mPIq.js +37 -0
- package/dist/claude-agent/index.d.ts +3 -3
- package/dist/claude-agent/index.js +2 -2
- package/dist/clerk/index.d.ts +3 -3
- package/dist/clerk/index.js +1 -1
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +3 -3
- package/dist/cloud/index.d.ts +127 -11
- package/dist/cloud/index.js +251 -3
- package/dist/{cloud-ytQ3uviA.js → cloud-CO8pYtwr.js} +5 -5
- package/dist/{codemod-ua2hHy7w.js → codemod-BP6n8N9L.js} +3 -3
- package/dist/{codemod-5mpSpnUY.js → codemod-i4N2BhDc.js} +3 -3
- package/dist/{collect-mLTY4nwb.js → collect-BaWeMjwV.js} +17 -8
- package/dist/{collect-Ci6akVwH.js → collect-DNm3oKOk.js} +6 -5
- package/dist/collect-k_Dulxr5.js +1 -0
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-CMqMx_IN.js → compile-BAEO5bOk.js} +2 -2
- package/dist/{compile-1bsAezL0.d.ts → compile-COM2VKuN.d.ts} +3 -3
- package/dist/{completion-BTFrHZhX.js → completion-BNdVf7X2.js} +3 -3
- package/dist/{config-bs_kZeYq.js → config-CoQHDrDi.js} +7 -3
- package/dist/{config-Bj2Ixq3d.js → config-DGZzMV1B.js} +2 -2
- package/dist/{config-report-DSpsLpCE.js → config-report-Cm7UITNA.js} +3 -3
- package/dist/connector-schema-_VCSsdtD.js +433 -0
- package/dist/{context-B_u3_bDJ.js → context--uM9V3Ui.js} +2 -2
- package/dist/convex/index.d.ts +5 -5
- package/dist/convex/index.js +2 -2
- package/dist/core-B5-w6Kw9.js +335 -0
- package/dist/core-CQkE8Esr.d.ts +179 -0
- package/dist/{create-B30OMUOJ.d.ts → create-B2-CQqCI.d.ts} +5 -5
- package/dist/{create-CGm28gyI.js → create-Cb86Z8Im.js} +9 -8
- package/dist/{create-DCF2SvoT.js → create-DJrlhEp9.js} +5 -5
- package/dist/{create-D_NmPOdH.d.ts → create-QHuuPw3q.d.ts} +7 -7
- package/dist/{create-CBMmFie-.d.ts → create-UTnUVETj.d.ts} +30 -10
- package/dist/{credential-BcoiejpY.js → credential-CIM7wHwA.js} +4 -4
- package/dist/{credentials-DPyHmYz5.js → credentials-BpuTwd7a.js} +1 -1
- package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-DRo2QM6E.js} +13 -2
- package/dist/{delegation-j6-rc37o.js → delegation-C5rOdTp5.js} +5 -5
- package/dist/delivery/index.d.ts +5 -5
- package/dist/delivery/index.js +4 -3
- package/dist/{delivery-DCAgNyCn.js → delivery-rwOKLWRm.js} +51 -2
- package/dist/{denied-text-CiFQNuMV.d.ts → denied-text-D4qkEtFz.d.ts} +2 -2
- package/dist/{describe-BybMRY7A.js → describe-DZlnEtLy.js} +1 -0
- package/dist/{describe-B5qgDvEW.d.ts → describe-O0SwYIK8.d.ts} +2 -2
- package/dist/{diff-CmuSD3vc.js → diff-Btr1c1Ow.js} +2 -2
- package/dist/{diff-3jbx6Rm4.js → diff-DzT-xD13.js} +10 -10
- package/dist/{doctor-BdZw53-k.js → doctor-BU1szn63.js} +241 -49
- package/dist/{doctor-3ZqNHX_G.js → doctor-BoK_y0KO.js} +4 -4
- package/dist/drizzle/index.d.ts +3 -3
- package/dist/drizzle/index.js +1 -1
- package/dist/elysia/index.d.ts +5 -5
- package/dist/elysia/index.js +3 -3
- package/dist/{errors-DH5QEGXh.d.ts → errors-B_Qg9D3e.d.ts} +2 -2
- package/dist/{errors-CWTY6uih.js → errors-DpP_U6Bf.js} +1 -0
- package/dist/{evaluate-CNClBvcj.js → evaluate-B_oqFbWC.js} +55 -12
- package/dist/{evaluations-C_1vdSZc.d.ts → evaluations-BCddEOfO.d.ts} +4 -4
- package/dist/{evaluations-beAooV0n.js → evaluations-DReoiVBw.js} +4 -4
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +3 -3
- package/dist/express/index.d.ts +5 -5
- package/dist/express/index.js +3 -3
- package/dist/fastify/index.d.ts +5 -5
- package/dist/fastify/index.js +3 -3
- package/dist/{fetch-adapter-Dw0MTS3D.d.ts → fetch-adapter-CuhyYwC1.d.ts} +4 -4
- package/dist/{fetch-adapter-DD_gfmrC.js → fetch-adapter-De-u6vhJ.js} +3 -3
- package/dist/{fields-OYG_JR78.js → fields-ClcQLrVc.js} +48 -13
- package/dist/{files-C5ZI27eV.js → files-acUbNwWt.js} +15 -2
- package/dist/{fixtures-B7XrLeY7.js → fixtures-BTMW6zgU.js} +2 -2
- package/dist/{flags-C3DktBDY.js → flags-aaeRAdiD.js} +2 -2
- package/dist/{from-snapshot-CTCMsFZk.js → from-snapshot-Bz469dIh.js} +17 -10
- package/dist/generate-3FNO5P5Q.js +159 -0
- package/dist/{grantee-hxrUsbpM.js → grantee-BrRANrpT.js} +1 -1
- package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-C6P1Tvol.js} +1 -1
- package/dist/{grantee-match-DbENBHLB.js → grantee-match-0RtHWQDx.js} +3 -3
- package/dist/h3/index.d.ts +4 -4
- package/dist/h3/index.js +3 -3
- package/dist/{helpers-CBYVMjwh.js → helpers-DY3vXuGs.js} +3 -3
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +3 -3
- package/dist/{hosted-BPk51-kX.d.ts → hosted-BmjzxQhp.d.ts} +14 -2
- package/dist/{hosted-B46-5LYE.js → hosted-XBgU6RGT.js} +34 -12
- package/dist/{http-DXJ37RDA.js → http-Ca3Sr5x1.js} +1 -1
- package/dist/{import-wyqXUyjm.js → import-Bz0GefWg.js} +5 -5
- package/dist/{import-CNV7I2Dw.js → import-DB2EG_iy.js} +3 -3
- package/dist/{index-C-6Letxg.d.ts → index-BE8y3moU.d.ts} +50 -8
- package/dist/{index-B8YjkEZ4.d.ts → index-BSfRl0hZ.d.ts} +6 -6
- package/dist/{index-DuWxc5dt.d.ts → index-BTkwrK7v.d.ts} +143 -14
- package/dist/index-CMiPH2Tv.d.ts +206 -0
- package/dist/{index-Ch8LpNDp.d.ts → index-DH9DUDEM.d.ts} +18 -3
- package/dist/{index-CueFnxQc.d.ts → index-DLwcuCN_.d.ts} +86 -7
- package/dist/index.d.ts +18 -17
- package/dist/index.js +27 -25
- package/dist/{init-GG6Jh8HD.js → init-5TXNji88.js} +5 -5
- package/dist/{init-DeMYsnYJ.js → init-B-4t1NzC.js} +4 -4
- package/dist/{instance-options-BsVpyGIk.d.ts → instance-options-CU0YQCLH.d.ts} +1 -1
- package/dist/{instance-snapshot-BOEhcqIF.js → instance-snapshot-CjcwnEiV.js} +16 -11
- package/dist/jwt/index.d.ts +83 -5
- package/dist/jwt/index.js +143 -5
- package/dist/{kernel-D0NVb1cK.js → kernel-DVExUwkp.js} +138 -12
- package/dist/kysely/index.d.ts +3 -3
- package/dist/kysely/index.js +1 -1
- package/dist/load-DrMHXzgN.js +1 -0
- package/dist/{load-DFS_ry0W.js → load-DxroEzH1.js} +2 -2
- package/dist/{login-Dfe5RlYk.js → login-ClSJh5t5.js} +4 -4
- package/dist/{login-imhuwvn3.js → login-WC9HPJpU.js} +6 -10
- package/dist/{manifest-C8IzW3Do.js → manifest-B79YuF2T.js} +6 -2
- package/dist/{manifest-mx5ghSFM.d.ts → manifest-BVhExYhw.d.ts} +23 -2
- package/dist/{manifest-dvL2XEe1.js → manifest-Dv6q4i5s.js} +54 -35
- package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-Cv492Qev.js} +37 -7
- package/dist/{map-claims-D3oRmTk0.js → map-claims-etGIS_Dv.js} +14 -11
- package/dist/{map-error-Dr70oA_3.js → map-error-BQGyDm_M.js} +1 -1
- package/dist/mcp/index.d.ts +64 -10
- package/dist/mcp/index.js +151 -22
- package/dist/{migrate-CIq4-E9F.js → migrate-Dodel0IJ.js} +77 -24
- package/dist/nest/index.d.ts +5 -5
- package/dist/nest/index.js +3 -3
- package/dist/next/index.d.ts +9 -9
- package/dist/next/index.js +5 -5
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +2 -2
- package/dist/node/index.d.ts +5 -5
- package/dist/node/index.js +2 -2
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.js +3 -3
- package/dist/openapi/index.d.ts +14 -4
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-C4dDTVEd.js → openapi-DMocl1bh.js} +4 -4
- package/dist/{openapi-B7HZ0f-r.js → openapi-Ddb4628T.js} +2 -7
- package/dist/{openapi-DD1SV-5-.js → openapi-ztVaux6j.js} +8 -8
- package/dist/{operation-findings-DgATDpvh.js → operation-findings-kRcEp8lE.js} +1 -1
- package/dist/{operations-X0VM7F3m.d.ts → operations-BNLes61x.d.ts} +1 -1
- package/dist/orpc/index.d.ts +6 -6
- package/dist/orpc/index.js +4 -4
- package/dist/otel/index.d.ts +3 -3
- package/dist/otel/index.js +1 -1
- package/dist/{output-1im__-08.js → output-DgSDLrta.js} +7 -3
- package/dist/{output-files-DhRBBE4n.js → output-files-CCMdcFWb.js} +2 -2
- package/dist/output-files-DPAY7-GX.js +1 -0
- package/dist/{ownership-DqaJNJPn.js → ownership-Z2LpxsGa.js} +443 -40
- package/dist/parse-Ddhz2xf4.js +148 -0
- package/dist/{parse-local-manifest-CVSs6J9I.js → parse-local-manifest-T1Q8X3A2.js} +24 -1
- package/dist/{parse-local-manifest-5ODwqu8b.d.ts → parse-local-manifest-e4mdwAKd.d.ts} +1 -1
- package/dist/pdp/index.d.ts +3 -3
- package/dist/pdp/index.js +7 -3
- package/dist/peer-12Q5I_RH.js +976 -0
- package/dist/{peer-CRvC7Hg1.js → peer-DOzzMfZL.js} +1 -1
- package/dist/{permdock-BrAscV28.js → permdock-BfWEeMqk.js} +177 -49
- package/dist/{permdock-SV8ZINYT.d.ts → permdock-Dco6dyCK.d.ts} +34 -5
- package/dist/{permission-registry-Br4jSNVi.js → permission-registry-DpPtSKNZ.js} +1 -1
- package/dist/{vocabulary-CN8H_9UF.d.ts → permission-types-DhNEmoRG.d.ts} +4 -64
- package/dist/{permissions-u_KHg-sN.d.ts → permissions-CoqCOfJ3.d.ts} +2 -1
- package/dist/{permissions-B-wCP5ZC.js → permissions-CyH2MV_B.js} +1 -1
- package/dist/{permissions-BPxlwVhN.js → permissions-DArULThq.js} +4 -4
- package/dist/pkce-5h8NgMQJ.js +21 -0
- package/dist/{policies-DP2nqRoF.js → policies-B2lZIurj.js} +3 -3
- package/dist/{policy-77FSAq_V.d.ts → policy-B7dg_yzu.d.ts} +2 -2
- package/dist/{policy-DbM5YYtV.js → policy-DaJy4nK4.js} +17 -8
- package/dist/{policy-from-catalog-C2gMa2Dj.js → policy-from-catalog-DFWYkNdu.js} +9 -9
- package/dist/{policy-types-n0uRTsW8.d.ts → policy-types-B-F-qAZk.d.ts} +83 -4
- package/dist/postgres-store-BL9M_CA1.js +1001 -0
- package/dist/{postgrest-CEgRbefE.d.ts → postgrest-CYacM_u7.d.ts} +12 -1
- package/dist/{postgrest-DAjUkF9w.js → postgrest-r5faDIzD.js} +37 -4
- package/dist/{powersync-DISt6SUe.js → powersync-13odBwiM.js} +3 -3
- package/dist/powersync-53ryOZip.js +1059 -0
- package/dist/powersync-CExtTLyA.js +1 -0
- package/dist/preset/index.js +1 -1
- package/dist/{preset-DZdU0tne.js → preset-CHTbd-DQ.js} +2 -2
- package/dist/prisma/index.d.ts +2 -2
- package/dist/prisma/index.js +1 -1
- package/dist/{problem-details-B45V5d2b.d.ts → problem-details-Tcfmj3bV.d.ts} +1 -1
- package/dist/prompt-DwtVemER.js +935 -0
- package/dist/{protected-RFGpakN9.d.ts → protected-BwZYL1X_.d.ts} +6 -5
- package/dist/{protected-BlF0F6wL.js → protected-DXE3cQYg.js} +2 -2
- package/dist/{provider-B1al-AZz.js → provider-BQsTYYy0.js} +2 -2
- package/dist/{provider-DILL9oPK.d.ts → provider-Csk-THAn.d.ts} +1 -1
- package/dist/{provider-store-DM6DNcta.js → provider-store-IgqQipCO.js} +2 -2
- package/dist/{pull-jEwbf80J.js → pull-CN5OLxrF.js} +7 -7
- package/dist/pull-Cty6Ovyu.js +1 -0
- package/dist/{push-BD4f15co.js → push-tVdG8dUL.js} +54 -14
- package/dist/{pushed-policy-DCW91KQU.d.ts → pushed-policy-BbcY9r_4.d.ts} +1 -1
- package/dist/{pushed-policy-CqtxkV4G.js → pushed-policy-CmYeSQqq.js} +1 -1
- package/dist/react/index.d.ts +6 -6
- package/dist/react/index.js +3 -3
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +25 -11
- package/dist/react-native/index.js +209 -21
- package/dist/{refs-K6eWUJ0H.js → refs-CNJBiTFo.js} +15 -1
- package/dist/{replay-Bkm-Ne4l.d.ts → replay-DCDD_ZfL.d.ts} +1 -1
- package/dist/{request-context-C81Pip9n.js → request-context-BykKLmyE.js} +2 -2
- package/dist/resend/index.d.ts +4 -4
- package/dist/{retire-CALjIQ7e.js → retire-Wj6dkyDE.js} +2 -2
- package/dist/{rls-BAqP4q66.js → rls-Ck_PocSp.js} +32 -8
- package/dist/{rls-DTvYd-lI.js → rls-CxBZb4rZ.js} +478 -534
- package/dist/{role-builder-uWQZ4v84.js → role-builder-C1VJzvcr.js} +3 -3
- package/dist/rolldown-runtime-C3I60J0A.js +4 -0
- package/dist/{rpc-VSl5ylaQ.js → rpc-Bk1bE-ch.js} +1 -1
- package/dist/run-Bz7DXGEv.js +1 -0
- package/dist/{run-ticket-sxFI6LAk.js → run-ticket-B5d_63Ma.js} +22 -1
- package/dist/{run-ticket-DocwaTKn.d.ts → run-ticket-BpNi7BCi.d.ts} +8 -3
- package/dist/{run-CUKuz0A8.js → run-uLD2aWuO.js} +7 -7
- package/dist/{runtime-D3VTAiCA.js → runtime-Vif3_-zf.js} +1 -1
- package/dist/{saas-BCXRuXca.js → saas-Az_Kz-4D.js} +7 -7
- package/dist/{scan-qvOe0hS3.js → scan-DSljDE1Y.js} +1 -1
- package/dist/{schema-yLuBWWsh.js → schema-gIcCx7zM.js} +1 -1
- package/dist/scim/index.d.ts +3 -2
- package/dist/scim/index.js +3 -1
- package/dist/scim-C7JYhySg.js +44 -0
- package/dist/{scim-ClHtpyV3.js → scim-YNieBO1U.js} +3 -537
- package/dist/{scopes-CUPSjo9q.js → scopes-BHsc-wp-.js} +1 -1
- package/dist/server/index.d.ts +11 -11
- package/dist/server/index.js +7 -7
- package/dist/{server-C3WXAHZj.js → server-Bnga6I50.js} +3 -3
- package/dist/{server-types-CKQiCnON.d.ts → server-types-C4gZUEZy.d.ts} +9 -9
- package/dist/{shared-DvvFzYXe.js → shared-C8LjUdVU.js} +1 -1
- package/dist/{shared-BAK8t4Uo.js → shared-fMKOv8hP.js} +44 -38
- package/dist/{sink-4-eQIRWf.d.ts → sink-Cp_Eot9W.d.ts} +4 -4
- package/dist/{skills-B6lVdD_W.js → skills-Dcjw9_1C.js} +5 -5
- package/dist/{skills-CTYwkl44.js → skills-g_3BS9NC.js} +3 -3
- package/dist/{snapshot-B7DkYg6g.js → snapshot-CbGmYiJx.js} +4 -4
- package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-DT2Kx1F8.js} +1 -1
- package/dist/{snapshot-cache-PXI-vGpY.d.ts → snapshot-cache-Pahd3IYJ.d.ts} +1 -1
- package/dist/solid/index.d.ts +6 -5
- package/dist/solid/index.js +4 -4
- package/dist/solid-start/index.d.ts +6 -6
- package/dist/solid-start/index.js +3 -3
- package/dist/{sources-vs5dGOsS.js → sources-B-zYB4GS.js} +111 -25
- package/dist/{sources-Xc4zkszI.d.ts → sources-DEEuGb8S.d.ts} +63 -14
- package/dist/sources-qoIJQrJv.js +11 -0
- package/dist/{sql-B5T1IL83.js → sql-B96movIM.js} +5 -1
- package/dist/sql-CDEPCA0g.d.ts +10 -0
- package/dist/{src-Cu2m75uo.js → src-DLFrtrrC.js} +140 -48
- package/dist/ssf/index.d.ts +4 -4
- package/dist/ssf/index.js +640 -211
- package/dist/{standard-webhooks-rbD7LS6h.d.ts → standard-webhooks-B_qRg0ws.d.ts} +2 -2
- package/dist/{store-BDutJkDL.js → store-DSP9oGvh.js} +15 -7
- package/dist/{store-options-u0ycEe8s.js → store-options-B_poyYvg.js} +6 -6
- package/dist/{stores-BUGNbJTt.d.ts → stores-BAZwSYZA.d.ts} +6 -5
- package/dist/{stores-CGhM8BN_.js → stores-BqEuHC05.js} +2 -2
- package/dist/{stream-CeCMJmBB.d.ts → stream-2Vwhl7Er.d.ts} +2 -2
- package/dist/{style-Uao76wO7.js → style-Cqo7-73x.js} +1 -0
- package/dist/{style-DiKWnAvn.js → style-Ds1gNm77.js} +1 -1
- package/dist/supabase/index.d.ts +8 -7
- package/dist/supabase/index.js +5 -5
- package/dist/supabase/middleware.d.ts +6 -6
- package/dist/supabase/middleware.js +6 -6
- package/dist/{supabase-x5JtR2tc.js → supabase--0aJdLEd.js} +8 -6
- package/dist/{supabase-By_frk3U.js → supabase-Bqp7iIY2.js} +3 -3
- package/dist/supabase-hook-DJxC4PoP.js +1 -0
- package/dist/{supabase-hook-BMoEhZir.js → supabase-hook-Dg0SrQWR.js} +1060 -119
- package/dist/supautils-MESd81bF.js +689 -0
- package/dist/{support-session-C6xJQYut.js → support-session-Cna49-3f.js} +2 -2
- package/dist/svelte/index.d.ts +5 -5
- package/dist/svelte/index.js +5 -5
- package/dist/svelte/runtime.d.ts +3 -3
- package/dist/svelte/runtime.js +3 -3
- package/dist/svelte/source.d.ts +5 -5
- package/dist/svelte/source.js +2 -2
- package/dist/sveltekit/index.d.ts +6 -6
- package/dist/sveltekit/index.js +2 -2
- package/dist/tanstack-start/index.d.ts +6 -6
- package/dist/tanstack-start/index.js +2 -2
- package/dist/{tenancy-DN2QAx9z.js → tenancy-CmeBQ6Nv.js} +12 -3
- package/dist/terminal/index.d.ts +6 -6
- package/dist/terminal/index.js +3 -3
- package/dist/testing/core.d.ts +2 -0
- package/dist/testing/core.js +1 -0
- package/dist/testing/fixtures/index.d.ts +12 -0
- package/dist/testing/fixtures/index.js +770 -0
- package/dist/testing/index.d.ts +41 -174
- package/dist/testing/index.js +241 -731
- package/dist/testing/saas/index.d.ts +5 -5
- 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/text-diff-DhITNCnD.js +444 -0
- package/dist/{token-BLZcjQgt.js → token-2qYDvXz2.js} +1 -1
- package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-reAhIzdl.js} +1 -1
- package/dist/trpc/index.d.ts +6 -6
- package/dist/trpc/index.js +3 -3
- package/dist/{types-Dfv0aTER.d.ts → types-BWZybqs9.d.ts} +110 -8
- package/dist/{types-Druu9wLM.d.ts → types-BXMYB7BY.d.ts} +2 -2
- package/dist/{types-BzzaHxt3.d.ts → types-BfVmNJO5.d.ts} +14 -3
- package/dist/{types-lUlG3TVd.d.ts → types-CIeZcG8v.d.ts} +2 -2
- package/dist/{types-B3_ooZgZ.d.ts → types-CKV71kKh.d.ts} +4 -4
- package/dist/{types-CX7AvRqP.d.ts → types-CQVEQawv.d.ts} +4 -4
- package/dist/{types-C863OQiR.d.ts → types-CeIscIKp.d.ts} +4 -4
- package/dist/{types-C_IiEs64.d.ts → types-CfiKC5aN.d.ts} +1 -1
- package/dist/{types-_WhUP7ud.d.ts → types-D3A19w4A.d.ts} +8 -4
- package/dist/{types-8RnwUAvE.d.ts → types-DBHftjgS.d.ts} +15 -8
- package/dist/{types-BdtSDbBO.d.ts → types-DC44-hqb.d.ts} +2 -2
- package/dist/{types-CVZSNocA.d.ts → types-DXmwu78Z.d.ts} +4 -4
- package/dist/{types-C791HFu9.d.ts → types-cTos-H7p.d.ts} +2 -2
- package/dist/{types-BtHtcKMr.d.ts → types-g7Oy6Zg4.d.ts} +3 -3
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/{usage-SveTRTyr.js → usage-Bybmck7f.js} +7 -7
- package/dist/{usage-CRVl9V3t.js → usage-CDaLXCfL.js} +2 -2
- package/dist/usage-y-kBNdqs.js +1 -0
- package/dist/{validation-error-DHUrAkku.d.ts → validation-error-CQpoquwj.d.ts} +1 -1
- package/dist/{verify-BaAKx7t6.js → verify-pe8rL_Qw.js} +350 -83
- package/dist/{views-DTIIO-oQ.js → views-BB8E9dTP.js} +1 -1
- package/dist/vocabulary-Bg3eeT2H.d.ts +67 -0
- package/dist/vue/index.d.ts +6 -5
- package/dist/vue/index.js +4 -4
- package/dist/{waiter-B986w1oK.d.ts → waiter-DwEyouan.d.ts} +2 -2
- package/dist/watch-Bd9mx3rd.js +1 -0
- package/dist/{watch-vea54PRw.js → watch-C-4VmFlL.js} +3 -3
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +2 -2
- package/dist/{where-scope-C9vfEphl.js → where-scope-wq2coopv.js} +41 -21
- package/dist/{who-can-DWBAcaCQ.js → who-can-BwNsI_Js.js} +6 -6
- package/dist/workflow/index.d.ts +3 -3
- package/dist/workflow/index.js +5 -5
- package/dist/workflow-sdk/index.d.ts +5 -5
- package/dist/{wrap-C5jC6sKI.d.ts → wrap-B6R0cMRL.d.ts} +2 -2
- package/dist/{wrap-gX61EtpB.js → wrap-CiSc_KCq.js} +8 -2
- package/package.json +22 -11
- package/schemas/cli-result-v1.json +1 -0
- package/schemas/cloud-api-v1.json +152 -7
- package/schemas/connector-v1.json +8 -0
- package/schemas/supabase-claims-v1.json +5 -0
- package/schemas/supabase-manifest-v1.json +276 -75
- package/skills/permdock/SKILL.md +1 -0
- package/skills/permdock-agents/references/runtimes.md +3 -1
- package/skills/permdock-approvals/references/stores-and-resume.md +2 -1
- package/skills/permdock-data/references/queries-and-rls.md +4 -1
- package/skills/permdock-tenancy/SKILL.md +1 -1
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
- package/skills/permdock-wire/references/adapters.md +14 -0
- package/skills/permdock-wire/references/policy-options.md +1 -0
- package/dist/catalog-BoWglDPI.js +0 -1
- package/dist/collect-D7eQDffE.js +0 -1
- package/dist/index-Cp_ilKi1.d.ts +0 -91
- package/dist/load-BxEtwlBS.js +0 -1
- package/dist/output-files-B3IZJksC.js +0 -1
- package/dist/parse-Cxq0WxrA.js +0 -53
- package/dist/peer-C8swJ2GH.js +0 -50
- package/dist/powersync-BkWWCS1K.js +0 -538
- package/dist/powersync-MMJ2mI2q.js +0 -1
- package/dist/prompt-DfZLJLIn.js +0 -32
- package/dist/pull-CvpINcMK.js +0 -1
- package/dist/run-DDwUaJDU.js +0 -1
- package/dist/supabase-hook-CCw5m35C.js +0 -1
- package/dist/supautils-B6HYL9eL.js +0 -91
- package/dist/text-diff-BQcUapOb.js +0 -15
- package/dist/usage-CZpSQP6L.js +0 -1
- package/dist/watch--ovlPOc0.js +0 -1
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { a as sole, n as isReadonlyArray, r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
2
|
import { n as isForbiddenKey } from "./paths-DXSHAMIa.js";
|
|
3
|
-
import {
|
|
3
|
+
import { u as isSqlFunctionField } from "./refs-CNJBiTFo.js";
|
|
4
|
+
import { c as listPermissions, h as formerKeys } from "./permission-registry-DpPtSKNZ.js";
|
|
4
5
|
import { u as isoAt } from "./time-DkWjdL98.js";
|
|
5
|
-
import {
|
|
6
|
+
import { f as scopeList, l as rootScope, s as resolveScope, u as scopeChain } from "./scopes-BHsc-wp-.js";
|
|
6
7
|
import { t as isoInstant } from "./instant-BluqDvyS.js";
|
|
7
|
-
import { m as findRole, v as listRoles } from "./grantee-
|
|
8
|
-
import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-
|
|
9
|
-
import { l as partitionsOf, p as scopeField } from "./tenancy-
|
|
10
|
-
import {
|
|
11
|
-
import { i as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
8
|
+
import { m as findRole, v as listRoles } from "./grantee-BrRANrpT.js";
|
|
9
|
+
import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-C6P1Tvol.js";
|
|
10
|
+
import { l as partitionsOf, p as scopeField } from "./tenancy-CmeBQ6Nv.js";
|
|
11
|
+
import { a as delegatedScopeMap } from "./permdock-BfWEeMqk.js";
|
|
12
|
+
import { i as configError } from "./errors-DpP_U6Bf.js";
|
|
13
|
+
import { d as sqlTextArray, i as qualifySqlTable, t as SQL_IDENT } from "./sql-B96movIM.js";
|
|
14
|
+
import { i as scopeMembershipTable, o as relatedSql, r as scopeColumn } from "./compile-BAEO5bOk.js";
|
|
15
|
+
import { d as keptKeys, l as roleColumn, u as roleManifest } from "./sources-B-zYB4GS.js";
|
|
16
|
+
import { A as quoteQualified, D as qualified, E as helperSchema, F as scopeTypeOf, I as sqlType, L as tenantTypeOf, M as textArrayLiteral, O as quoteIdent, S as CLAIM, T as declaredArray, _ as subjectClaimJsonSql, b as tenantClaimSql, d as keyTenantSql, f as kindFilterSql, h as roleKindSql, i as allowEntry, j as quoteTable, k as quoteLiteral, m as memberVia, n as GLOBAL_ROLES, o as rootName, p as liveSessionSql, r as HELPERS, s as signedIn, t as CUSTOM_ROLES, u as globalKindFilterSql, v as subjectClaimSql, w as CLI, x as userIdHelperSql, y as subjectIdSql } from "./shared-fMKOv8hP.js";
|
|
15
17
|
//#region src/cli/sql/active.ts
|
|
16
18
|
function checkActiveRow(label, row) {
|
|
17
19
|
quoteTable(row.table);
|
|
@@ -699,6 +701,10 @@ function collectGrants(policy) {
|
|
|
699
701
|
};
|
|
700
702
|
});
|
|
701
703
|
}
|
|
704
|
+
/** The declared roles a custom role may draw grants from: the custom-role ceiling. */
|
|
705
|
+
function assignableRoles(policy) {
|
|
706
|
+
return [...roleNames(policy)].toSorted().filter((name) => policy.rolesByName.get(name)?.assignable ?? listRoles(policy.vocabulary.roles).some((leaf) => leaf.key === name && leaf.assignable));
|
|
707
|
+
}
|
|
702
708
|
/** Declared role names (bindings and `defineRoles` leaves) plus any role a top-level grant names. */
|
|
703
709
|
function roleNames(policy) {
|
|
704
710
|
const names = new Set(policy.roles.map((item) => item.name));
|
|
@@ -1205,7 +1211,7 @@ end;`;
|
|
|
1205
1211
|
and ${qualified(ctx, HELPERS.has)}(m.grant_key)
|
|
1206
1212
|
)`;
|
|
1207
1213
|
const match = `c.tenant_id is not distinct from p_tenant and c.scope = p_scope and c.scope_id is not distinct from p_scope_id and c.role = p_role`;
|
|
1208
|
-
const allowEntryOf =
|
|
1214
|
+
const allowEntryOf = `c.${allowEntry(ctx)}`;
|
|
1209
1215
|
const checked = (gate) => `begin
|
|
1210
1216
|
if p_role = any(${sqlTextArray(custom.declared)}) then
|
|
1211
1217
|
return query
|
|
@@ -1248,7 +1254,7 @@ revoke execute on function ${reader}(text, text, ${tenantType}, text) from publi
|
|
|
1248
1254
|
grant execute on function ${reader}(text, text, ${tenantType}, text) to authenticated;`);
|
|
1249
1255
|
const trusted = qualified(ctx, READERS.trusted);
|
|
1250
1256
|
const signature = `text, text, ${tenantType}, text`;
|
|
1251
|
-
const grants = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\
|
|
1257
|
+
const grants = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\n${trustedReaderGrants(ctx, `${trusted}(${signature})`, trustedReaders)}`;
|
|
1252
1258
|
chunks.push(`create or replace function ${trusted}(p_role text, p_scope text, p_tenant ${tenantType} default null, p_scope_id text default null)
|
|
1253
1259
|
returns table (permission text, effect text)
|
|
1254
1260
|
language plpgsql
|
|
@@ -1301,6 +1307,16 @@ ${indent} hint = ${quoteLiteral(hint)};`;
|
|
|
1301
1307
|
function hasCustomRoleChecksFor(ctx) {
|
|
1302
1308
|
return ctx.authorize === "database" && ctx.customRoles !== void 0 && ctx.scopes.length > 0 && ctx.scopes.every((scope) => hasMemberFor(ctx, scope.name));
|
|
1303
1309
|
}
|
|
1310
|
+
/**
|
|
1311
|
+
* The grants a `rls.trustedReaders` role needs for one reader: usage on the
|
|
1312
|
+
* helper schema, which no client role reaches through these lines, and
|
|
1313
|
+
* execute on the function.
|
|
1314
|
+
*/
|
|
1315
|
+
function trustedReaderGrants(ctx, fn, readers) {
|
|
1316
|
+
const to = readers.map(quoteIdent).join(", ");
|
|
1317
|
+
return `grant usage on schema ${quoteIdent(helperSchema(ctx))} to ${to};
|
|
1318
|
+
grant execute on function ${fn} to ${to};`;
|
|
1319
|
+
}
|
|
1304
1320
|
//#endregion
|
|
1305
1321
|
//#region src/cli/sql/global-roles.ts
|
|
1306
1322
|
function qualify(name, schema) {
|
|
@@ -1328,6 +1344,7 @@ function globalRoleSource(source, schema, alias, roles) {
|
|
|
1328
1344
|
from: `${quoteTable(table)} ${alias}${role.join}${global}`,
|
|
1329
1345
|
userSql: `${alias}.${quoteIdent(user)}`,
|
|
1330
1346
|
roleSql: role.sql,
|
|
1347
|
+
nullable: role.nullable,
|
|
1331
1348
|
role: roleManifest(role),
|
|
1332
1349
|
...role.through === void 0 ? {} : { through: role.through },
|
|
1333
1350
|
...tenant === void 0 ? {} : { tenant },
|
|
@@ -1738,7 +1755,7 @@ ${rows}
|
|
|
1738
1755
|
union
|
|
1739
1756
|
select (ms.id)::${type}, ck.grant_key
|
|
1740
1757
|
${rows}
|
|
1741
|
-
${customKeysJoin(ctx, scope, entries(CUSTOM_ROLES.permissions,
|
|
1758
|
+
${customKeysJoin(ctx, scope, entries(CUSTOM_ROLES.permissions, allowEntry(ctx), " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}
|
|
1742
1759
|
where ${signedIn(ctx)}
|
|
1743
1760
|
and ms.scope = ${quoteLiteral(scope)}
|
|
1744
1761
|
and not (r.role = any(${sqlTextArray(custom.declared)}))${narrow}${sourceFilters(ctx, scope, subjectIdSql(ctx), grantPermissionSql("ck.grant_key"), sourcesKeep(sources))}`, sources, subjectIdSql(ctx));
|
|
@@ -1749,7 +1766,7 @@ ${rows}
|
|
|
1749
1766
|
where ${signedIn(ctx)}
|
|
1750
1767
|
and ms.scope = ${quoteLiteral(scope)}
|
|
1751
1768
|
and not (r.role = any(${sqlTextArray(custom.declared)}))${narrow}${sourceFilters(ctx, scope, subjectIdSql(ctx), grantPermissionSql("p_grant"), sourcesKeep(sources))}
|
|
1752
|
-
${customKeysSql(ctx, scope, entries(CUSTOM_ROLES.permissions,
|
|
1769
|
+
${customKeysSql(ctx, scope, entries(CUSTOM_ROLES.permissions, allowEntry(ctx), " and c.effect = 'allow'"), entries(CUSTOM_ROLES.permissions, "permission", " and c.effect = 'deny'"), entries(CUSTOM_ROLES.includes, "include_role", ""))}`, sources, subjectIdSql(ctx));
|
|
1753
1770
|
}
|
|
1754
1771
|
function scopedBody(ctx, scope, type) {
|
|
1755
1772
|
if (ctx.authorize !== "database") return sqlBody(claimBody(ctx, scope, type));
|
|
@@ -1799,6 +1816,137 @@ revoke execute on function ${fn}(text, text, text) from public;
|
|
|
1799
1816
|
grant execute on function ${fn}(text, text, text) to anon, authenticated;`;
|
|
1800
1817
|
}
|
|
1801
1818
|
//#endregion
|
|
1819
|
+
//#region src/cli/rls/custom-role-by-id.ts
|
|
1820
|
+
const PREFIX = "PermDock CLI";
|
|
1821
|
+
const col = (name) => `r.${quoteIdent(name)}`;
|
|
1822
|
+
/**
|
|
1823
|
+
* Custom-role functions keyed by the application's role row
|
|
1824
|
+
* (`rls.customRoleWrites.roles.primaryKey`): the tenant, scope, instance and
|
|
1825
|
+
* name come from the row, never from the caller. The writers delegate to
|
|
1826
|
+
* `permdock_replace_custom_role_grants` (or its trusted variant), so every
|
|
1827
|
+
* caller and definition check applies, and keep the stored denies and
|
|
1828
|
+
* includes. The reader returns declared allow keys only and is executable by
|
|
1829
|
+
* `rls.trustedReaders` alone.
|
|
1830
|
+
*/
|
|
1831
|
+
function customRoleByIdSql(ctx, trustedReaders) {
|
|
1832
|
+
const custom = ctx.customRoles;
|
|
1833
|
+
const roles = custom?.table;
|
|
1834
|
+
const root = ctx.scopes[0]?.name;
|
|
1835
|
+
if (custom === void 0 || roles?.primaryKey === void 0 || ctx.authorize !== "database" || root === void 0) return "";
|
|
1836
|
+
const pk = roles.primaryKey;
|
|
1837
|
+
if (pk === "") throw configError(`${PREFIX}: rls.customRoleWrites.roles.primaryKey names no column`);
|
|
1838
|
+
const leveled = custom.levels === true;
|
|
1839
|
+
const tenantType = tenantTypeOf(ctx);
|
|
1840
|
+
const table = quoteQualified(roles.table);
|
|
1841
|
+
const idType = `${table}.${quoteIdent(pk)}%type`;
|
|
1842
|
+
const perms = qualified(ctx, CUSTOM_ROLES.permissions);
|
|
1843
|
+
const includes = qualified(ctx, CUSTOM_ROLES.includes);
|
|
1844
|
+
const replace = qualified(ctx, CUSTOM_ROLES.replace);
|
|
1845
|
+
const trustedReplace = qualified(ctx, CUSTOM_ROLES.trustedReplace);
|
|
1846
|
+
const byId = qualified(ctx, CUSTOM_ROLES.replaceById);
|
|
1847
|
+
const trustedById = qualified(ctx, CUSTOM_ROLES.trustedReplaceById);
|
|
1848
|
+
const reader = qualified(ctx, CUSTOM_ROLES.trustedReadById);
|
|
1849
|
+
const renames = Object.entries(custom.renamed ?? {}).toSorted(byKey);
|
|
1850
|
+
const current = (expr) => currentKeyExpr(renames, expr);
|
|
1851
|
+
const declaredKeys = sqlTextArray(custom.permissions ?? []);
|
|
1852
|
+
const declaredRoles = sqlTextArray(custom.declared);
|
|
1853
|
+
const tenant = roles.tenant === void 0 ? `null::${tenantType}` : `${col(roles.tenant)}::${tenantType}`;
|
|
1854
|
+
const scope = rolesTableScope(roles, tenant, root, col);
|
|
1855
|
+
const scopeId = roles.id === void 0 || roles.tenant === void 0 ? "null::text" : `case when ${tenant} is null then null else ${col(roles.id)}::text end`;
|
|
1856
|
+
const skipped = roles.skip === void 0 || roles.tenant === void 0 ? "false" : `(${tenant} is not null and coalesce(${col(roles.skip)}, false))`;
|
|
1857
|
+
const match = "c.tenant_id is not distinct from v_tenant and c.scope = v_scope and c.scope_id is not distinct from v_scope_id and c.role = v_key";
|
|
1858
|
+
const audit = custom.audit;
|
|
1859
|
+
const auditCall = audit === void 0 ? "" : `
|
|
1860
|
+
perform ${quoteQualified(audit.function)}(${[
|
|
1861
|
+
"event_type => 'permdock.custom_role.replaced'",
|
|
1862
|
+
"target_type => 'role'",
|
|
1863
|
+
"record_id => p_role_id::text",
|
|
1864
|
+
"tenant => v_tenant",
|
|
1865
|
+
"metadata => jsonb_build_object('operation', 'replace', 'role', v_key, 'permissions', p_permissions)",
|
|
1866
|
+
...Object.entries(audit.args ?? {}).toSorted(byKey).map(([name, value]) => `${quoteIdent(name)} => ${quoteLiteral(value)}`)
|
|
1867
|
+
].join(", ")});`;
|
|
1868
|
+
const body = (write) => `declare
|
|
1869
|
+
v_tenant ${tenantType};
|
|
1870
|
+
v_scope text;
|
|
1871
|
+
v_scope_id text;
|
|
1872
|
+
v_key text;
|
|
1873
|
+
v_skipped boolean;
|
|
1874
|
+
v_deny text[];
|
|
1875
|
+
v_include text[];
|
|
1876
|
+
begin
|
|
1877
|
+
select ${tenant}, ${scope}, ${scopeId}, ${col(roles.key)}::text, ${skipped}
|
|
1878
|
+
into v_tenant, v_scope, v_scope_id, v_key, v_skipped
|
|
1879
|
+
from ${table} r
|
|
1880
|
+
where ${col(pk)} = p_role_id;
|
|
1881
|
+
if not found then
|
|
1882
|
+
${raiseSql(" ", "P0002", "'permdock: no role row has id ' || coalesce(p_role_id::text, 'null')", "unknown-role")}
|
|
1883
|
+
end if;
|
|
1884
|
+
if v_skipped or v_key = any(${declaredRoles}) then
|
|
1885
|
+
${raiseSql(" ", "22023", "'permdock: ' || coalesce(v_key, 'null') || ' is a declared role, not a custom role'", "declared-role")}
|
|
1886
|
+
end if;
|
|
1887
|
+
select array_agg(c.permission) into v_deny
|
|
1888
|
+
from ${perms} c
|
|
1889
|
+
where ${match} and c.effect = 'deny';
|
|
1890
|
+
select array_agg(c.include_role) into v_include
|
|
1891
|
+
from ${includes} c
|
|
1892
|
+
where ${match};
|
|
1893
|
+
perform ${write}(v_tenant, v_scope, v_scope_id, v_key, p_permissions, v_deny, v_include);${auditCall}
|
|
1894
|
+
end;`;
|
|
1895
|
+
const writer = (comment, name, write, member) => `-- ${comment}
|
|
1896
|
+
create or replace function ${name}(p_role_id ${idType}, p_permissions text[])
|
|
1897
|
+
returns void
|
|
1898
|
+
language plpgsql
|
|
1899
|
+
volatile
|
|
1900
|
+
security definer
|
|
1901
|
+
set search_path = ''
|
|
1902
|
+
as $$
|
|
1903
|
+
${body(write)}
|
|
1904
|
+
$$;
|
|
1905
|
+
${member ? `revoke execute on function ${name}(${idType}, text[]) from public, anon;
|
|
1906
|
+
grant execute on function ${name}(${idType}, text[]) to authenticated;` : `revoke execute on function ${name}(${idType}, text[]) from public, anon, authenticated;`}`;
|
|
1907
|
+
const readerEntry = leveled ? `${current("c.permission")} || coalesce('@' || c.level, '')` : current("c.permission");
|
|
1908
|
+
const readerGrant = trustedReaders === void 0 || trustedReaders.length === 0 ? "" : `\n${trustedReaderGrants(ctx, `${reader}(text[], ${tenantType})`, trustedReaders)}`;
|
|
1909
|
+
return `${writer("save a custom role by its row id: the row supplies tenant, scope, instance and name; the caller checks of permdock_replace_custom_role_grants apply, and stored denies and includes stay", byId, replace, true)}
|
|
1910
|
+
|
|
1911
|
+
${writer("save a custom role by its row id for a trusted caller: the definition checks without the caller checks", trustedById, trustedReplace, false)}
|
|
1912
|
+
|
|
1913
|
+
-- the declared allow keys of custom roles by their row ids as text, for a trusted reader; with p_tenant, an id of another tenant is refused
|
|
1914
|
+
create or replace function ${reader}(p_role_ids text[], p_tenant ${tenantType} default null)
|
|
1915
|
+
returns table (role_id text, permission text)
|
|
1916
|
+
language plpgsql
|
|
1917
|
+
stable
|
|
1918
|
+
security definer
|
|
1919
|
+
set search_path = ''
|
|
1920
|
+
as $$
|
|
1921
|
+
begin
|
|
1922
|
+
if p_tenant is not null and exists (
|
|
1923
|
+
select 1 from ${table} r
|
|
1924
|
+
where ${col(pk)}::text = any(p_role_ids) and ${tenant} is distinct from p_tenant
|
|
1925
|
+
) then
|
|
1926
|
+
${raiseSql(" ", "42501", "'permdock: a role id belongs to another tenant than ' || p_tenant::text", "not-member")}
|
|
1927
|
+
end if;
|
|
1928
|
+
return query
|
|
1929
|
+
select distinct v.role_id, ${readerEntry}
|
|
1930
|
+
from (
|
|
1931
|
+
select ${col(pk)}::text as role_id, ${tenant} as tenant_id, ${scope} as scope, ${scopeId} as scope_id, ${col(roles.key)}::text as role
|
|
1932
|
+
from ${table} r
|
|
1933
|
+
where ${col(pk)}::text = any(p_role_ids)
|
|
1934
|
+
and not ${skipped}
|
|
1935
|
+
and not (${col(roles.key)}::text = any(${declaredRoles}))
|
|
1936
|
+
) v
|
|
1937
|
+
join ${perms} c
|
|
1938
|
+
on c.tenant_id is not distinct from v.tenant_id
|
|
1939
|
+
and c.scope = v.scope
|
|
1940
|
+
and c.scope_id is not distinct from v.scope_id
|
|
1941
|
+
and c.role = v.role
|
|
1942
|
+
where c.effect = 'allow'
|
|
1943
|
+
and ${current("c.permission")} = any(${declaredKeys})
|
|
1944
|
+
order by 1, 2;
|
|
1945
|
+
end;
|
|
1946
|
+
$$;
|
|
1947
|
+
revoke execute on function ${reader}(text[], ${tenantType}) from public, anon, authenticated;${readerGrant}`;
|
|
1948
|
+
}
|
|
1949
|
+
//#endregion
|
|
1802
1950
|
//#region src/cli/rls/custom-role-cascade.ts
|
|
1803
1951
|
/** `row.column`, for a trigger's `old` or `new`. */
|
|
1804
1952
|
function rowColumn(row, name) {
|
|
@@ -1830,11 +1978,11 @@ function customRoleCascadeSql(ctx) {
|
|
|
1830
1978
|
const target = quoteTable(qualifySqlTable(table.table));
|
|
1831
1979
|
const tenant = (row) => table.tenant === void 0 ? `null::${tenantType}` : `${rowColumn(row, table.tenant)}::${tenantType}`;
|
|
1832
1980
|
const scope = (row) => rolesTableScope(table, tenant(row), root, (name) => rowColumn(row, name));
|
|
1833
|
-
const id = (row) => table.id === void 0 ? "null::text" :
|
|
1834
|
-
const skipped = (row) => table.skip === void 0 ? "false" : `coalesce(${rowColumn(row, table.skip)}, false)`;
|
|
1981
|
+
const id = (row) => table.id === void 0 || table.tenant === void 0 ? "null::text" : `case when ${tenant(row)} is null then null else ${rowColumn(row, table.id)}::text end`;
|
|
1982
|
+
const skipped = (row) => table.skip === void 0 || table.tenant === void 0 ? "false" : `(${tenant(row)} is not null and coalesce(${rowColumn(row, table.skip)}, false))`;
|
|
1835
1983
|
const guard = qualified(ctx, CUSTOM_ROLES.guard);
|
|
1836
1984
|
const entries = qualified(ctx, CUSTOM_ROLES.entries);
|
|
1837
|
-
const allowEntryOf =
|
|
1985
|
+
const allowEntryOf = `c.${allowEntry(ctx)}`;
|
|
1838
1986
|
return `-- the application's roles table: renames, moves and deletes of a custom role carry its grants and includes
|
|
1839
1987
|
create or replace function ${fn}()
|
|
1840
1988
|
returns trigger
|
|
@@ -1913,6 +2061,58 @@ create trigger "permdock_custom_role_cascade"
|
|
|
1913
2061
|
`;
|
|
1914
2062
|
}
|
|
1915
2063
|
//#endregion
|
|
2064
|
+
//#region src/cli/rls/custom-role-guard.ts
|
|
2065
|
+
/** A column of the roles-table row a trigger sees as `new`. */
|
|
2066
|
+
function column(name) {
|
|
2067
|
+
return `new.${quoteIdent(name)}`;
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* The trigger on the application's roles table (`rls.customRoleWrites.roles`)
|
|
2071
|
+
* that refuses a tenant row named like a declared role (`declared-role`) or
|
|
2072
|
+
* like a shared custom role (`name-taken`). A row the `skip` column marks is
|
|
2073
|
+
* a system role of the tenant and passes. Empty without a tenant column: then
|
|
2074
|
+
* no row is a tenant row.
|
|
2075
|
+
*/
|
|
2076
|
+
function customRoleNameGuardSql(ctx) {
|
|
2077
|
+
const custom = ctx.customRoles;
|
|
2078
|
+
const table = custom?.table;
|
|
2079
|
+
const root = ctx.scopes[0]?.name;
|
|
2080
|
+
if (custom === void 0 || table?.tenant === void 0 || root === void 0) return "";
|
|
2081
|
+
const tenant = `${column(table.tenant)}::${tenantTypeOf(ctx)}`;
|
|
2082
|
+
const skipped = table.skip === void 0 ? "false" : `coalesce(${column(table.skip)}, false)`;
|
|
2083
|
+
const fn = qualified(ctx, CUSTOM_ROLES.nameGuard);
|
|
2084
|
+
const target = quoteTable(qualifySqlTable(table.table));
|
|
2085
|
+
const key = `${column(table.key)}::text`;
|
|
2086
|
+
const scope = rolesTableScope(table, tenant, root, column);
|
|
2087
|
+
return `-- the application's roles table: a tenant row cannot take the name of a declared or a shared role
|
|
2088
|
+
create or replace function ${fn}()
|
|
2089
|
+
returns trigger
|
|
2090
|
+
language plpgsql
|
|
2091
|
+
volatile
|
|
2092
|
+
security definer
|
|
2093
|
+
set search_path = ''
|
|
2094
|
+
as $$
|
|
2095
|
+
begin
|
|
2096
|
+
if ${tenant} is null or ${skipped} then
|
|
2097
|
+
return new;
|
|
2098
|
+
end if;
|
|
2099
|
+
if ${key} = any(${sqlTextArray(custom.declared)}) then
|
|
2100
|
+
${raiseSql(" ", "22023", `'permdock: ' || ${key} || ' is a declared role'`, "declared-role")}
|
|
2101
|
+
end if;
|
|
2102
|
+
if ${qualified(ctx, CUSTOM_ROLES.shared)}(${scope}, ${key}) then
|
|
2103
|
+
${raiseSql(" ", "22023", `'permdock: ' || ${key} || ' is a shared role'`, "name-taken")}
|
|
2104
|
+
end if;
|
|
2105
|
+
return new;
|
|
2106
|
+
end;
|
|
2107
|
+
$$;
|
|
2108
|
+
revoke execute on function ${fn}() from public, anon, authenticated;
|
|
2109
|
+
drop trigger if exists "permdock_custom_role_name_guard" on ${target};
|
|
2110
|
+
create trigger "permdock_custom_role_name_guard"
|
|
2111
|
+
before insert or update on ${target}
|
|
2112
|
+
for each row execute function ${fn}();
|
|
2113
|
+
`;
|
|
2114
|
+
}
|
|
2115
|
+
//#endregion
|
|
1916
2116
|
//#region src/cli/rls/custom-role-writes.ts
|
|
1917
2117
|
function callerFunctionsSql(ctx, custom, root, levelReach, forUser) {
|
|
1918
2118
|
const leveled = custom.levels === true;
|
|
@@ -2063,6 +2263,7 @@ function customRoleWritesSql(ctx, levelReach) {
|
|
|
2063
2263
|
const guard = qualified(ctx, CUSTOM_ROLES.guard);
|
|
2064
2264
|
const shape = qualified(ctx, CUSTOM_ROLES.shape);
|
|
2065
2265
|
const entries = qualified(ctx, CUSTOM_ROLES.entries);
|
|
2266
|
+
const problem = qualified(ctx, CUSTOM_ROLES.entryProblem);
|
|
2066
2267
|
const renames = Object.entries(custom.renamed ?? {}).toSorted(byKey);
|
|
2067
2268
|
const current = (expr) => currentKeyExpr(renames, expr);
|
|
2068
2269
|
const known = sqlTextArray([...custom.permissions ?? [], ...renames.map(([former]) => former)]);
|
|
@@ -2150,6 +2351,38 @@ ${callerFunctionsSql(ctx, custom, root, levelReach, true).beyond}
|
|
|
2150
2351
|
|
|
2151
2352
|
${callerFunctionsSql(ctx, custom, root, levelReach, true).guard}
|
|
2152
2353
|
` : ""}
|
|
2354
|
+
-- null when an allow entry is a declared permission inside the ceiling of the scope${leveled ? ", at a level the resource declares" : ""}; else unknown-permission, outside-ceiling${leveled ? " or unknown-level" : ""}
|
|
2355
|
+
create or replace function ${problem}(p_scope text, p_entry text)
|
|
2356
|
+
returns text
|
|
2357
|
+
language plpgsql
|
|
2358
|
+
stable
|
|
2359
|
+
security definer
|
|
2360
|
+
set search_path = ''
|
|
2361
|
+
as $$
|
|
2362
|
+
declare
|
|
2363
|
+
v_key text := ${current(leveled ? "split_part(p_entry, '@', 1)" : "p_entry")};
|
|
2364
|
+
begin
|
|
2365
|
+
if p_entry is null or not (v_key = any(${known})) then
|
|
2366
|
+
return 'unknown-permission';
|
|
2367
|
+
end if;
|
|
2368
|
+
if not exists (
|
|
2369
|
+
select 1 from ${ceiling} c
|
|
2370
|
+
where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
|
|
2371
|
+
) then
|
|
2372
|
+
return 'outside-ceiling';
|
|
2373
|
+
end if;${leveled ? `
|
|
2374
|
+
if strpos(p_entry, '@') > 0 and not exists (
|
|
2375
|
+
select 1 from ${ceiling} c
|
|
2376
|
+
where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
|
|
2377
|
+
and split_part(c.grant_key, '@', 2) = split_part(p_entry, '@', 2)
|
|
2378
|
+
) then
|
|
2379
|
+
return 'unknown-level';
|
|
2380
|
+
end if;` : ""}
|
|
2381
|
+
return null;
|
|
2382
|
+
end;
|
|
2383
|
+
$$;
|
|
2384
|
+
revoke execute on function ${problem}(text, text) from public, anon, authenticated;
|
|
2385
|
+
|
|
2153
2386
|
-- the entries of a definition: declared keys and levels inside the ceiling of the scope, and declared includes
|
|
2154
2387
|
create or replace function ${entries}(p_scope text, p_allow text[], p_deny text[], p_include text[])
|
|
2155
2388
|
returns void
|
|
@@ -2161,23 +2394,18 @@ as $$
|
|
|
2161
2394
|
declare
|
|
2162
2395
|
v_entry text;
|
|
2163
2396
|
v_key text;
|
|
2397
|
+
v_problem text;
|
|
2164
2398
|
begin
|
|
2165
2399
|
foreach v_entry in array coalesce(p_allow, '{}'::text[]) loop
|
|
2166
2400
|
v_key := ${current(leveled ? "split_part(v_entry, '@', 1)" : "v_entry")};
|
|
2167
|
-
|
|
2401
|
+
v_problem := ${problem}(p_scope, v_entry);
|
|
2402
|
+
if v_problem = 'unknown-permission' then
|
|
2168
2403
|
${raiseSql(" ", "22023", `'permdock: ' || v_entry || ' is not a declared permission'`, "unknown-permission")}
|
|
2169
2404
|
end if;
|
|
2170
|
-
if
|
|
2171
|
-
select 1 from ${ceiling} c
|
|
2172
|
-
where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
|
|
2173
|
-
) then
|
|
2405
|
+
if v_problem = 'outside-ceiling' then
|
|
2174
2406
|
${raiseSql(" ", "22023", `'permdock: ' || v_key || ' is outside the ceiling of ' || p_scope`, "outside-ceiling")}
|
|
2175
2407
|
end if;${leveled ? `
|
|
2176
|
-
if
|
|
2177
|
-
select 1 from ${ceiling} c
|
|
2178
|
-
where c.scope = p_scope and c.effect = 'allow' and ${current("c.permission")} = v_key
|
|
2179
|
-
and split_part(c.grant_key, '@', 2) = split_part(v_entry, '@', 2)
|
|
2180
|
-
) then
|
|
2408
|
+
if v_problem = 'unknown-level' then
|
|
2181
2409
|
${raiseSql(" ", "22023", `'permdock: ' || v_entry || ' names a level the resource does not declare'`, "unknown-level")}
|
|
2182
2410
|
end if;` : ""}
|
|
2183
2411
|
end loop;
|
|
@@ -2438,17 +2666,103 @@ ${functionBody(memberRowsBody(ctx, scope, type, "p_user", memberForSources(ctx,
|
|
|
2438
2666
|
revoke execute on function ${fn}(uuid) from public, anon, authenticated;`;
|
|
2439
2667
|
}
|
|
2440
2668
|
//#endregion
|
|
2669
|
+
//#region src/cli/rls/token-scopes.ts
|
|
2670
|
+
const TOKEN_SCOPE_ALLOWS = "permdock_token_scope_allows";
|
|
2671
|
+
/** `rls.tokenScopes` as the SQL generator needs it, or `undefined` when it is off. */
|
|
2672
|
+
function tokenScopesPlan(setting, policy) {
|
|
2673
|
+
if (setting !== true) return;
|
|
2674
|
+
const permissions = listPermissions(policy.permissions).map((leaf) => ({
|
|
2675
|
+
key: leaf.key,
|
|
2676
|
+
scope: leaf.scope,
|
|
2677
|
+
renamedFrom: formerKeys(leaf)
|
|
2678
|
+
}));
|
|
2679
|
+
return { scopes: delegatedScopeMap(permissions, policy.oauthScopes) };
|
|
2680
|
+
}
|
|
2681
|
+
function tokenScopeAllowsCall(ctx, grant) {
|
|
2682
|
+
return `${qualified(ctx, TOKEN_SCOPE_ALLOWS)}(${grant})`;
|
|
2683
|
+
}
|
|
2684
|
+
/** The token's scopes as rows of `s(scope)`: a space-separated string or, outside `guc`, a JSON array. */
|
|
2685
|
+
function tokenScopeRows(ctx) {
|
|
2686
|
+
if (ctx.dialect === "guc") return `regexp_split_to_table(btrim(coalesce(${subjectClaimSql(ctx, "scope")}, '')), '\\s+') s(scope)`;
|
|
2687
|
+
const scope = subjectClaimJsonSql(ctx, "scope");
|
|
2688
|
+
return `unnest(case jsonb_typeof(${scope})
|
|
2689
|
+
when 'string' then regexp_split_to_array(btrim(${scope} #>> '{}'), '\\s+')
|
|
2690
|
+
when 'array' then array(select jsonb_array_elements_text(${scope}))
|
|
2691
|
+
else '{}'::text[]
|
|
2692
|
+
end) s(scope)`;
|
|
2693
|
+
}
|
|
2694
|
+
/**
|
|
2695
|
+
* `permdock_token_scope_allows(p_grant)`: a delegated token (a `client_id`, or
|
|
2696
|
+
* an `act` that is not a support or impersonation session) reaches a grant key
|
|
2697
|
+
* only when one of its scopes maps to the key's permission. better-supabase's
|
|
2698
|
+
* `cap_allows` reads the same tokens the same way.
|
|
2699
|
+
*/
|
|
2700
|
+
function tokenScopeAllowsSql(ctx, plan, anonExecute) {
|
|
2701
|
+
const fn = qualified(ctx, TOKEN_SCOPE_ALLOWS);
|
|
2702
|
+
const act = subjectClaimJsonSql(ctx, "act");
|
|
2703
|
+
const grants = anonExecute ? `revoke execute on function ${fn}(text) from public;
|
|
2704
|
+
grant execute on function ${fn}(text) to anon, authenticated;` : `revoke execute on function ${fn}(text) from public, anon;
|
|
2705
|
+
grant execute on function ${fn}(text) to authenticated;`;
|
|
2706
|
+
return `-- Token scopes: a delegated token holds only the permissions its scope claim maps to; denies always apply, and a token without client_id or an agent act is not limited
|
|
2707
|
+
create or replace function ${fn}(p_grant text)
|
|
2708
|
+
returns boolean
|
|
2709
|
+
language sql
|
|
2710
|
+
stable
|
|
2711
|
+
security definer
|
|
2712
|
+
set search_path = ''
|
|
2713
|
+
as $$
|
|
2714
|
+
select case
|
|
2715
|
+
when coalesce(${subjectClaimSql(ctx, "client_id")}, '') = ''
|
|
2716
|
+
and (${act} is null
|
|
2717
|
+
or ${act} = 'null'::jsonb
|
|
2718
|
+
or (jsonb_typeof(${act}) = 'object'
|
|
2719
|
+
and ${act} ->> 'kind' in ('support', 'impersonation'))) then true
|
|
2720
|
+
when exists (
|
|
2721
|
+
select 1 from ${qualified(ctx, "role_permissions")} rp
|
|
2722
|
+
where rp.grant_key = p_grant and rp.effect = 'deny'
|
|
2723
|
+
) then true
|
|
2724
|
+
else exists (
|
|
2725
|
+
select 1
|
|
2726
|
+
from ${tokenScopeRows(ctx)}
|
|
2727
|
+
where s.scope <> ''
|
|
2728
|
+
and coalesce(${quoteLiteral(JSON.stringify(plan.scopes))}::jsonb -> s.scope, '[]'::jsonb)
|
|
2729
|
+
? split_part(split_part(p_grant, '#', 1), '@', 1)
|
|
2730
|
+
)
|
|
2731
|
+
end
|
|
2732
|
+
$$;
|
|
2733
|
+
${grants}`;
|
|
2734
|
+
}
|
|
2735
|
+
//#endregion
|
|
2441
2736
|
//#region src/cli/rls/helpers.ts
|
|
2442
2737
|
/**
|
|
2443
2738
|
* The policy-side call for a grant key. Every form is uncorrelated, so
|
|
2444
2739
|
* Postgres runs the helper once per statement (an InitPlan, or a hashed
|
|
2445
2740
|
* SubPlan for `in`), never once per row.
|
|
2446
2741
|
*/
|
|
2447
|
-
function accessSql(ctx, scope, grantKey, column) {
|
|
2742
|
+
function accessSql(ctx, scope, grantKey, column, helper) {
|
|
2448
2743
|
const key = quoteLiteral(grantKey);
|
|
2449
2744
|
if (scope === "global") return `(select ${qualified(ctx, HELPERS.has)}(${key}))`;
|
|
2450
2745
|
if (column === void 0) throw configError(`PermDock CLI: ${scope}-scoped grant needs definePolicy({ scopes.${scope} })`);
|
|
2451
|
-
return `${quoteIdent(column)} in (select ${qualified(ctx, permittedIdsHelper(scope))}(${key}))`;
|
|
2746
|
+
return `${quoteIdent(column)} in (select ${qualified(ctx, helper ?? permittedIdsHelper(scope))}(${key}))`;
|
|
2747
|
+
}
|
|
2748
|
+
/**
|
|
2749
|
+
* `permitted_<parent>_ids(p_grant)` for each \`rls.tables\` \`via\`: the parent
|
|
2750
|
+
* rows whose scope instance the grant reaches. \`security definer\`, so the
|
|
2751
|
+
* caller needs no read on the parent, and called uncorrelated like
|
|
2752
|
+
* \`permitted_<scope>_ids\`.
|
|
2753
|
+
*/
|
|
2754
|
+
function viaHelpersSql(ctx, anonExecute) {
|
|
2755
|
+
const done = /* @__PURE__ */ new Set();
|
|
2756
|
+
const chunks = [];
|
|
2757
|
+
for (const via of Object.values(ctx.vias ?? {})) {
|
|
2758
|
+
if (done.has(via.helper)) continue;
|
|
2759
|
+
done.add(via.helper);
|
|
2760
|
+
chunks.push(`-- rows of ${via.table} whose ${via.column} is a ${via.scope} the grant reaches
|
|
2761
|
+
${helperFunction(ctx, via.helper, `setof ${via.type}`, sqlBody(` select p.${quoteIdent(via.id)}
|
|
2762
|
+
from ${quoteQualified(via.table)} p
|
|
2763
|
+
where p.${quoteIdent(via.column)} in (select ${qualified(ctx, permittedIdsHelper(via.scope))}(p_grant))`), anonExecute)}`);
|
|
2764
|
+
}
|
|
2765
|
+
return chunks;
|
|
2452
2766
|
}
|
|
2453
2767
|
function userIdSql(ctx, anonExecute) {
|
|
2454
2768
|
const fn = userIdHelperSql(ctx);
|
|
@@ -2536,6 +2850,40 @@ ${rows.map((row) => ` (${[
|
|
|
2536
2850
|
);`;
|
|
2537
2851
|
}
|
|
2538
2852
|
/**
|
|
2853
|
+
* `role_permissions` as a view over the compiled rows (`rls.seeds: 'view'`).
|
|
2854
|
+
* `security_invoker` makes the view read as its caller, so it adds no
|
|
2855
|
+
* privilege; the table's revoke applies to the view the same way.
|
|
2856
|
+
*/
|
|
2857
|
+
function seedViewSql(ctx, rows) {
|
|
2858
|
+
const view = qualified(ctx, "role_permissions");
|
|
2859
|
+
const columns = "role, permission, grant_key, scope, effect";
|
|
2860
|
+
return `-- rls.seeds: 'view': role_permissions is a view, so the rows travel with the schema
|
|
2861
|
+
create or replace view ${view} (${columns}) with (security_invoker = true) as
|
|
2862
|
+
${rows.length === 0 ? `select null::text, null::text, null::text, null::text, null::text where false` : `select * from (values\n${rows.map((row) => ` (${[
|
|
2863
|
+
row.role,
|
|
2864
|
+
row.permission,
|
|
2865
|
+
row.grantKey,
|
|
2866
|
+
row.scope,
|
|
2867
|
+
row.effect
|
|
2868
|
+
].map(quoteLiteral).join(", ")})`).join(",\n")}\n) as v(${columns})`};
|
|
2869
|
+
revoke all on table ${view} from anon, authenticated, public;`;
|
|
2870
|
+
}
|
|
2871
|
+
/** `<schema>.seed()`: the same upsert and delete as `seedSql`, for a declarative schema that carries no rows. */
|
|
2872
|
+
function seedFunctionSql(ctx, rows) {
|
|
2873
|
+
const fn = qualified(ctx, "seed");
|
|
2874
|
+
return `-- rls.seeds: 'function': the role_permissions rows are data, so a declarative diff leaves them out; run select ${fn}(); after each deploy
|
|
2875
|
+
create or replace function ${fn}()
|
|
2876
|
+
returns void
|
|
2877
|
+
language sql
|
|
2878
|
+
volatile
|
|
2879
|
+
security definer
|
|
2880
|
+
set search_path = ''
|
|
2881
|
+
as $permdock$
|
|
2882
|
+
${seedSql(ctx, rows)}
|
|
2883
|
+
$permdock$;
|
|
2884
|
+
revoke execute on function ${fn}() from public, anon, authenticated;`;
|
|
2885
|
+
}
|
|
2886
|
+
/**
|
|
2539
2887
|
* `role_permissions`, its seeds, and the three `security definer` helpers
|
|
2540
2888
|
* with `search_path = ''`. `execute` goes to `authenticated` only.
|
|
2541
2889
|
*/
|
|
@@ -2545,9 +2893,13 @@ function helpersSql(ctx, rows, options) {
|
|
|
2545
2893
|
const rp = qualified(ctx, "role_permissions");
|
|
2546
2894
|
const chunks = [`-- permdock helpers (${ctx.authorize === "database" ? "database: reads the membership and user_roles tables" : "jwt: reads the role and memberships claims"})
|
|
2547
2895
|
-- policies call them uncorrelated, so Postgres evaluates each once per statement`];
|
|
2548
|
-
if (schema !== "public")
|
|
2896
|
+
if (schema !== "public") {
|
|
2897
|
+
const anonUsage = options.anonExecute === true || ctx.capabilities === true;
|
|
2898
|
+
chunks.push(`create schema if not exists ${s};\nrevoke all on schema ${s} from public;\ngrant usage on schema ${s} to ${anonUsage ? "anon, authenticated" : "authenticated"};`);
|
|
2899
|
+
}
|
|
2549
2900
|
if (ctx.dialect === "supabase") chunks.push(userIdSql(ctx, options.anonExecute === true));
|
|
2550
|
-
|
|
2901
|
+
if (ctx.seeds === "view") chunks.push(seedViewSql(ctx, rows));
|
|
2902
|
+
else chunks.push(`create table if not exists ${rp} (
|
|
2551
2903
|
role text not null,
|
|
2552
2904
|
permission text not null,
|
|
2553
2905
|
grant_key text not null,
|
|
@@ -2557,7 +2909,8 @@ function helpersSql(ctx, rows, options) {
|
|
|
2557
2909
|
);
|
|
2558
2910
|
alter table ${rp} enable row level security;
|
|
2559
2911
|
revoke all on table ${rp} from anon, authenticated, public;`);
|
|
2560
|
-
if (
|
|
2912
|
+
if (ctx.seeds === "function") chunks.push(seedFunctionSql(ctx, rows));
|
|
2913
|
+
else if (options.withoutSeeds !== true && ctx.seeds !== "view") chunks.push(seedSql(ctx, rows));
|
|
2561
2914
|
if (ctx.authorize === "database" && options.userRoles && ctx.roles === void 0) {
|
|
2562
2915
|
const ur = qualified(ctx, "user_roles");
|
|
2563
2916
|
chunks.push(`create table if not exists ${ur} (
|
|
@@ -2573,6 +2926,7 @@ revoke all on table ${ur} from anon, authenticated, public;`);
|
|
|
2573
2926
|
const anon = options.anonExecute === true;
|
|
2574
2927
|
const keys = ctx.apiKeys;
|
|
2575
2928
|
if (keys !== void 0) chunks.push(apiKeyAllowsSql(ctx, keys, anon));
|
|
2929
|
+
if (ctx.tokenScopes !== void 0) chunks.push(tokenScopeAllowsSql(ctx, ctx.tokenScopes, anon));
|
|
2576
2930
|
const bodies = grantBodies(ctx);
|
|
2577
2931
|
chunks.push(helperFunction(ctx, HELPERS.has, "boolean", bodies.has, anon));
|
|
2578
2932
|
const capabilities = capabilitiesSql(ctx);
|
|
@@ -2584,6 +2938,7 @@ revoke all on table ${ur} from anon, authenticated, public;`);
|
|
|
2584
2938
|
if (ctx.roleMemberScopes?.includes(scope.name) === true) chunks.push(memberRolesFunction(ctx, scope.name, type, anon));
|
|
2585
2939
|
if (hasMemberFor(ctx, scope.name)) chunks.push(memberForFunction(ctx, scope.name, type));
|
|
2586
2940
|
}
|
|
2941
|
+
chunks.push(...viaHelpersSql(ctx, anon));
|
|
2587
2942
|
const forUser = forUserSql(ctx);
|
|
2588
2943
|
if (forUser !== "") chunks.push(forUser);
|
|
2589
2944
|
const writes = customRoleWritesSql(ctx, options.levelReach ?? []);
|
|
@@ -2591,6 +2946,12 @@ revoke all on table ${ur} from anon, authenticated, public;`);
|
|
|
2591
2946
|
chunks.push(writes);
|
|
2592
2947
|
const cascade = customRoleCascadeSql(ctx);
|
|
2593
2948
|
if (cascade !== "") chunks.push(cascade);
|
|
2949
|
+
const sync = ctx.customRoles?.sync;
|
|
2950
|
+
if (sync !== void 0) chunks.push(sync);
|
|
2951
|
+
const byId = customRoleByIdSql(ctx, options.trustedReaders);
|
|
2952
|
+
if (byId !== "") chunks.push(byId);
|
|
2953
|
+
const nameGuard = customRoleNameGuardSql(ctx);
|
|
2954
|
+
if (nameGuard !== "") chunks.push(nameGuard);
|
|
2594
2955
|
}
|
|
2595
2956
|
const globalRoles = globalRolesSql(ctx);
|
|
2596
2957
|
if (globalRoles !== "") chunks.push(globalRoles);
|
|
@@ -2600,20 +2961,26 @@ revoke all on table ${ur} from anon, authenticated, public;`);
|
|
|
2600
2961
|
}
|
|
2601
2962
|
function grantBodies(ctx) {
|
|
2602
2963
|
const keys = ctx.apiKeys;
|
|
2964
|
+
const scoped = ctx.tokenScopes !== void 0;
|
|
2603
2965
|
const set = ctx.grantSet === true;
|
|
2604
|
-
const
|
|
2605
|
-
|
|
2966
|
+
const ceiling = (grant) => {
|
|
2967
|
+
const calls = [...keys === void 0 ? [] : [apiKeyAllowsCall(ctx, grant)], ...scoped ? [tokenScopeAllowsCall(ctx, grant)] : []];
|
|
2968
|
+
return calls.length === 0 ? void 0 : calls.join(" and ");
|
|
2969
|
+
};
|
|
2970
|
+
const allows = ceiling(set ? "ids.grant_key" : "p_grant");
|
|
2971
|
+
const keyTenant = keys === void 0 ? "" : ` and ${keyTenantSql(ctx, void 0) ?? "true"}`;
|
|
2972
|
+
const hasAllows = ceiling(set ? "g.grant_key" : "p_grant");
|
|
2606
2973
|
return {
|
|
2607
|
-
has: sqlBody(
|
|
2974
|
+
has: sqlBody(hasAllows === void 0 ? hasBody(ctx) : set ? ` select g.grant_key
|
|
2608
2975
|
from (
|
|
2609
2976
|
${hasBody(ctx)}
|
|
2610
2977
|
) g(grant_key)
|
|
2611
|
-
where ${
|
|
2978
|
+
where ${hasAllows}${keyTenant}` : ceilingHasSql(hasBody(ctx), `${hasAllows}${keyTenant}`)),
|
|
2612
2979
|
ids: (scope) => {
|
|
2613
2980
|
const type = scopeTypeOf(ctx, scope);
|
|
2614
2981
|
const body = scopedBody(ctx, scope, type);
|
|
2615
|
-
if (
|
|
2616
|
-
const sql = scope === rootName(ctx) ? `${body.sql}\n union\n${serviceKeyIdsSql(ctx, keys, scope, type)}` : body.sql;
|
|
2982
|
+
if (allows === void 0) return body;
|
|
2983
|
+
const sql = keys !== void 0 && scope === rootName(ctx) ? `${body.sql}\n union\n${serviceKeyIdsSql(ctx, keys, scope, type)}` : body.sql;
|
|
2617
2984
|
return {
|
|
2618
2985
|
sql: set ? ` select ids.id, ids.grant_key
|
|
2619
2986
|
from (
|
|
@@ -2626,7 +2993,7 @@ ${sql}
|
|
|
2626
2993
|
};
|
|
2627
2994
|
}
|
|
2628
2995
|
function forUserContext(ctx) {
|
|
2629
|
-
const { apiKeys: _apiKeys, ...unkeyed } = ctx;
|
|
2996
|
+
const { apiKeys: _apiKeys, tokenScopes: _tokenScopes, ...unkeyed } = ctx;
|
|
2630
2997
|
return {
|
|
2631
2998
|
...unkeyed,
|
|
2632
2999
|
subjectId: "p_user",
|
|
@@ -3649,6 +4016,42 @@ function ownershipRules(policy, scopes) {
|
|
|
3649
4016
|
};
|
|
3650
4017
|
}
|
|
3651
4018
|
/**
|
|
4019
|
+
* The roles whose `for` lists kinds that no membership source of the role's
|
|
4020
|
+
* scope can hold: such a role resolves to no one. A `via` read from a column
|
|
4021
|
+
* can hold any kind, and so does a source with no `via` (it is not
|
|
4022
|
+
* compared) and a scope with no table or source.
|
|
4023
|
+
*/
|
|
4024
|
+
function unsatisfiedKinds(ctx, policy) {
|
|
4025
|
+
const out = [];
|
|
4026
|
+
for (const [role, kinds] of Object.entries(ctx.ownership?.kinds ?? {})) {
|
|
4027
|
+
const scope = roleScope(policy, ctx.scopes, role);
|
|
4028
|
+
if (scope === void 0) continue;
|
|
4029
|
+
const held = [];
|
|
4030
|
+
const mapped = scopeTable(ctx, scope)?.table;
|
|
4031
|
+
if (mapped !== void 0) {
|
|
4032
|
+
const via = memberVia(mapped);
|
|
4033
|
+
held.push({
|
|
4034
|
+
label: mapped.table,
|
|
4035
|
+
via: typeof via === "string" ? void 0 : via.value ?? void 0
|
|
4036
|
+
});
|
|
4037
|
+
} else for (const source of ctx.memberSources ?? ctx.sources ?? []) {
|
|
4038
|
+
if (source.sql.scope !== void 0 && source.sql.scope !== scope) continue;
|
|
4039
|
+
const via = source.sql.manifest.via;
|
|
4040
|
+
held.push({
|
|
4041
|
+
label: source.sql.table,
|
|
4042
|
+
via: via === void 0 ? void 0 : "value" in via && typeof via.value === "string" ? via.value : void 0
|
|
4043
|
+
});
|
|
4044
|
+
}
|
|
4045
|
+
if (held.length === 0 || held.some((entry) => entry.via === void 0 || kinds.includes(entry.via ?? ""))) continue;
|
|
4046
|
+
out.push({
|
|
4047
|
+
role,
|
|
4048
|
+
for: kinds,
|
|
4049
|
+
sources: held.map((entry) => `${entry.label} (via ${entry.via ?? ""})`)
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
return out;
|
|
4053
|
+
}
|
|
4054
|
+
/**
|
|
3652
4055
|
* The ownership objects: holder-count and transfer-only triggers on each
|
|
3653
4056
|
* scope's membership table, and `permdock_can_assign` for the application's
|
|
3654
4057
|
* own policies on those tables. Empty when no role declares a rule.
|
|
@@ -3694,4 +4097,4 @@ function ownershipSql(ctx) {
|
|
|
3694
4097
|
return chunks.length === 0 ? "" : `${chunks.join("\n\n")}\n`;
|
|
3695
4098
|
}
|
|
3696
4099
|
//#endregion
|
|
3697
|
-
export {
|
|
4100
|
+
export { permittedForHelper as $, apiKeyFields as A, sqlFunctionNames as B, globalRoleSource as C, byKey as D, byCodeUnit as E, collectGrants as F, hasMemberFor as G, graphHelper as H, roleNames as I, memberForHelper as J, hasPermittedUsers as K, andConditions as L, assignableRoles as M, breakGlassHolder as N, API_KEY_ALLOWS as O, breakGlassKey as P, memberRoleOf as Q, compileConditionSql as R, globalRoleRows as S, rolesTableScope as T, graphSqlName as U, CLOSURE as V, graphSqlText as W, memberForTable as X, memberForSources as Y, memberIdsHelper as Z, seedSql as _, assignmentSql as a, activeUserSql as at, tokenScopesPlan as b, OWNERSHIP as c, permissionHelpersSql as d, permittedIdsHelper as et, permissionKeysForHelper as f, helpersSql as g, accessSql as h, holderSources as i, activeRowSql as it, apiKeysPlan as j, apiKeyAllowsCall as k, PERMISSION_HELPERS as l, requiresSql as m, ownershipSql as n, scopeSources as nt, guardedTables as o, checkSuspension as ot, permittedByPermissionHelper as p, linkHelper as q, unsatisfiedKinds as r, scopeTable as rt, ASSIGNMENTS as s, ownershipRules as t, restrictedHelper as tt, checkName as u, TOKEN_SCOPE_ALLOWS as v, currentKeyExpr as w, capabilityAccessSql as x, tokenScopeAllowsCall as y, contextRefs as z };
|