permdock 0.7.0 → 0.9.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 -3
- package/dist/a2a/index.d.ts +7 -7
- package/dist/a2a/index.js +8 -8
- package/dist/action-result-BcudBQOF.d.ts +19 -0
- package/dist/{adapter-context-CRhqyzCk.js → adapter-context-CIQpQxbE.js} +8 -2
- package/dist/{adoption-scan-D-feE7BE.js → adoption-scan-Ch07MdxN.js} +5 -5
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/ai-sdk/index.d.ts +19 -13
- package/dist/ai-sdk/index.js +6 -6
- package/dist/{app-data-MuyzVqX8.js → app-data-UNNm07jV.js} +10 -7
- package/dist/{approval-header-CJACtHkv.js → approval-header-8QV6MU4H.js} +6 -3
- package/dist/{approval-prompt-K0faOLFp.d.ts → approval-prompt-3BqWczTt.d.ts} +13 -11
- package/dist/{approval-prompt-Dg5LeAcT.js → approval-prompt-B8hQY69x.js} +5 -6
- package/dist/approvals/index.d.ts +4 -4
- package/dist/approvals/index.js +6 -6
- package/dist/{approvals-CMRfsbBh.js → approvals-DnIBTuXE.js} +66 -42
- package/dist/{approvers-CqPOLD-o.js → approvers-B4r0N_Uz.js} +8 -2
- package/dist/{arazzo-ClrF3PVb.js → arazzo-BpS7LnV0.js} +2 -2
- package/dist/{arazzo-rdeJ8n1J.js → arazzo-CmQaGPkM.js} +6 -6
- package/dist/{args-CC3OwyHW.js → args-0FrVs5EH.js} +14 -7
- package/dist/astro/index.d.ts +30 -12
- package/dist/astro/index.js +2 -2
- package/dist/{audience-BrMs8c4o.js → audience-DTjR8xet.js} +8 -8
- package/dist/{auth-CscUX8Sy.js → auth--yPyZNgr.js} +19 -7
- package/dist/authzen/index.d.ts +34 -28
- package/dist/authzen/index.js +23 -20
- package/dist/{automation-DOtAFWdy.d.ts → automation-C6Jg75xq.d.ts} +42 -32
- package/dist/{automation-ySsxp_Yp.js → automation-x3KEUusw.js} +22 -21
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +13 -7
- package/dist/better-supabase/index.d.ts +357 -227
- package/dist/better-supabase/index.js +219 -140
- package/dist/{bind-D1nO6fiC.js → bind-BNQA9JK9.js} +26 -10
- package/dist/{bind-JDuvxjuW.d.ts → bind-YYjvfpB9.d.ts} +32 -12
- package/dist/{body-CH5of8Ne.js → body-CJQwYVa8.js} +3 -3
- package/dist/{body-text-QcXok_SI.js → body-text-DQhcmqdY.js} +8 -4
- package/dist/{boundary-BM8a_P6E.js → boundary-BV3O1eud.js} +8 -2
- package/dist/{boundary-BmHxVH1J.d.ts → boundary-C9ck-3uK.d.ts} +13 -4
- package/dist/{budget-NbEtbJC_.js → budget-BELja5A9.js} +6 -3
- package/dist/{budget-JqcRqyhK.d.ts → budget-hYsPz_yE.d.ts} +6 -3
- package/dist/{build-instance-DxE7czo9.js → build-instance-BWXqwQYU.js} +3 -3
- package/dist/{capability-L7NnfhiE.js → capability-BqX94r07.js} +31 -25
- package/dist/{capability-D4UC9TTW.d.ts → capability-Di9tiUvi.d.ts} +41 -29
- package/dist/{casbin-BHijLuHY.js → casbin-B7_yS-oo.js} +2 -2
- package/dist/{casbin-BW9pS47J.js → casbin-bHyIxTNQ.js} +16 -8
- package/dist/{casl-CmJlgziS.js → casl-C4OINZHI.js} +2 -2
- package/dist/{casl-UbI8v-Wq.js → casl-CHHoCldP.js} +19 -10
- package/dist/catalog/index.d.ts +51 -30
- package/dist/catalog/index.js +18 -15
- package/dist/{catalog-D69WhFii.js → catalog-CGlSwU6j.js} +20 -19
- package/dist/catalog-D3oS2GH3.js +1 -0
- package/dist/{catalog-BaMQfQ6Y.js → catalog-amPDr0JQ.js} +3 -3
- package/dist/{catalog-doc-CV9s6B1S.js → catalog-doc-DjMKzJp9.js} +28 -14
- package/dist/{catalog-fingerprint-C144oW4M.js → catalog-fingerprint-e6aCWosn.js} +18 -14
- package/dist/catalog-operations-DeHTrJSV.js +60 -0
- package/dist/{challenge-nGWma08S.js → challenge-DImNcR7B.js} +48 -27
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +13 -10
- package/dist/chat-sdk/index.d.ts +44 -26
- package/dist/chat-sdk/index.js +17 -17
- package/dist/{checks-BC_0mPIq.js → checks-CGkP3Z7A.js} +4 -1
- package/dist/{claims-map-7oQ3ue4X.js → claims-map-CKkoAAYF.js} +5 -2
- package/dist/{claims-map-Ca-QX8C6.d.ts → claims-map-D1Pb5oRq.d.ts} +8 -5
- package/dist/claude-agent/index.d.ts +6 -6
- package/dist/claude-agent/index.js +6 -3
- package/dist/clerk/index.d.ts +11 -7
- package/dist/clerk/index.js +6 -6
- package/dist/cli/bin.js +4 -4
- package/dist/cli/index.d.ts +82 -7
- package/dist/cli/index.js +6 -6
- package/dist/{clients-C7zLFuWT.d.ts → clients-C_NOET3z.d.ts} +5 -5
- package/dist/{clients-DjU5ABMy.js → clients-DYr3U86j.js} +3 -3
- package/dist/cloud/index.d.ts +84 -52
- package/dist/cloud/index.js +53 -39
- package/dist/{cloud-CO8pYtwr.js → cloud-3kWfjnYy.js} +4 -5
- package/dist/{codemod-BP6n8N9L.js → codemod-Bv5i-Mzo.js} +523 -201
- package/dist/{codemod-i4N2BhDc.js → codemod-D9Aiii1r.js} +3 -4
- package/dist/{collect-DNm3oKOk.js → collect-B80GYEot.js} +4 -5
- package/dist/collect-D-eroIfV.js +1 -0
- package/dist/{collect-BaWeMjwV.js → collect-WCl8cJQV.js} +26 -63
- package/dist/{compare-pfyVnBjE.js → compare-DTfyUunF.js} +2 -2
- package/dist/compile/index.d.ts +2 -2
- package/dist/compile/index.js +1 -1
- package/dist/{compile-COM2VKuN.d.ts → compile-BEklRUYo.d.ts} +36 -12
- package/dist/{compile-BAEO5bOk.js → compile-EzEMB-7v.js} +77 -33
- package/dist/{completion-BNdVf7X2.js → completion-_bc0OUQ-.js} +17 -8
- package/dist/config-BwPBfN0t.js +108 -0
- package/dist/{config-DGZzMV1B.js → config-D5XrLWpk.js} +2 -2
- package/dist/{config-report-Cm7UITNA.js → config-report-BmWIskD7.js} +9 -6
- package/dist/config-shape-ykV0Vm2n.js +134 -0
- package/dist/{connection-DjhIJSs2.js → connection-B72dg5Q2.js} +23 -22
- package/dist/{connector-schema-_VCSsdtD.js → connector-schema-CYLQNMYq.js} +3 -2
- package/dist/{context--uM9V3Ui.js → context-BnIzxTNE.js} +4 -3
- package/dist/convex/index.d.ts +13 -7
- package/dist/convex/index.js +9 -6
- package/dist/{core-CQkE8Esr.d.ts → core-CFepFiDP.d.ts} +49 -22
- package/dist/{core-B5-w6Kw9.js → core-DzVINFbU.js} +26 -20
- package/dist/{create-Cb86Z8Im.js → create-6EwRQlig.js} +10 -7
- package/dist/{create-DJrlhEp9.js → create-BLPKnJKa.js} +35 -25
- package/dist/{create-UTnUVETj.d.ts → create-BPUopdfL.d.ts} +70 -37
- package/dist/{create-QHuuPw3q.d.ts → create-BamXmeob.d.ts} +37 -29
- package/dist/{create-B2-CQqCI.d.ts → create-DBvUsgzK.d.ts} +49 -28
- package/dist/{credential-CIM7wHwA.js → credential-CyyyvCsL.js} +41 -38
- package/dist/{credentials-BpuTwd7a.js → credentials-CdDxp7jN.js} +6 -6
- package/dist/{deciding-columns-DRo2QM6E.js → deciding-columns-NB4wcyBf.js} +2 -2
- package/dist/{delegation-C5rOdTp5.js → delegation-CDB6_keF.js} +31 -22
- package/dist/delivery/index.d.ts +4 -4
- package/dist/delivery/index.js +5 -5
- package/dist/{delivery-rwOKLWRm.js → delivery-BupfvJIV.js} +34 -22
- package/dist/{delivery-event-xX4obB8F.js → delivery-event-JioK5YbS.js} +6 -6
- package/dist/denied-text-DHldhVUv.d.ts +22 -0
- package/dist/{describe-DZlnEtLy.js → describe-Bb4hGK5d.js} +5 -5
- package/dist/{describe-O0SwYIK8.d.ts → describe-BbFPBSms.d.ts} +5 -5
- package/dist/{device-BARQ8eYG.js → device-BwbJNmb2.js} +11 -8
- package/dist/{diff-Btr1c1Ow.js → diff-BwR1JdP2.js} +2 -2
- package/dist/{diff-DzT-xD13.js → diff-CQegtBAo.js} +22 -13
- package/dist/{digest-B3kc_PgQ.d.ts → digest-8Yx2CchM.d.ts} +3 -3
- package/dist/{doctor-BU1szn63.js → doctor-ClRVAtXQ.js} +287 -135
- package/dist/{doctor-BoK_y0KO.js → doctor-LPMYjSqO.js} +4 -4
- package/dist/drizzle/index.d.ts +33 -19
- package/dist/drizzle/index.js +17 -9
- package/dist/elysia/index.d.ts +17 -11
- package/dist/elysia/index.js +5 -5
- package/dist/{errors-BKy0EfhN.js → errors-B6R_lg2a.js} +13 -4
- package/dist/{errors-B_Qg9D3e.d.ts → errors-BIM4MFsJ.d.ts} +22 -6
- package/dist/{errors-BWXBMaKK.d.ts → errors-CHwoRRpB.d.ts} +4 -1
- package/dist/{errors-DpP_U6Bf.js → errors-Vpu6rKHl.js} +13 -10
- package/dist/{evaluate-B_oqFbWC.js → evaluate-BAmf_AQd.js} +153 -82
- package/dist/{evaluations-BCddEOfO.d.ts → evaluations-DOWuBpWv.d.ts} +11 -5
- package/dist/{evaluations-DReoiVBw.js → evaluations-IBceAeKT.js} +40 -19
- package/dist/eve/index.d.ts +9 -6
- package/dist/eve/index.js +4 -4
- package/dist/{event-time-CsegxXxB.js → event-time-BGy48400.js} +8 -4
- package/dist/{expiry-D4_YKRKD.js → expiry-6XByK0dm.js} +8 -5
- package/dist/express/index.d.ts +9 -6
- package/dist/express/index.js +9 -6
- package/dist/fastify/index.d.ts +16 -10
- package/dist/fastify/index.js +8 -5
- package/dist/{fetch-adapter-CuhyYwC1.d.ts → fetch-adapter-BIbBAtPZ.d.ts} +9 -6
- package/dist/{fetch-adapter-De-u6vhJ.js → fetch-adapter-dPTgJVHd.js} +14 -11
- package/dist/fields-D0vUI6dq.js +498 -0
- package/dist/{files-acUbNwWt.js → files-B3AA05f1.js} +8 -5
- package/dist/{files-D_JIzUrP.js → files-BSjrtIHw.js} +11 -4
- package/dist/{fixtures-BTMW6zgU.js → fixtures-A1_sp_tR.js} +15 -6
- package/dist/{flags-aaeRAdiD.js → flags-DXjoTVbS.js} +2 -2
- package/dist/{freeze-CrU8nQeX.js → freeze-jjan0lCv.js} +4 -1
- package/dist/{from-snapshot-Bz469dIh.js → from-snapshot-DCakWRsr.js} +18 -15
- package/dist/gen-BFfzZSLH.js +58 -0
- package/dist/gen-BtfFWGbz.js +827 -0
- package/dist/{generate-3FNO5P5Q.js → generate-CRVV5V7l.js} +14 -8
- package/dist/generate-CezbCaTI.js +275 -0
- package/dist/{grantee-BrRANrpT.js → grantee-Dke-9rd1.js} +4 -4
- package/dist/{grantee-conditions-C6P1Tvol.js → grantee-conditions-BrYPe-w0.js} +12 -12
- package/dist/{grantee-match-0RtHWQDx.js → grantee-match-7RZZvoT_.js} +12 -6
- package/dist/{graph-sql-B-dsAT6j.d.ts → graph-sql-BwFC4Qjs.d.ts} +3 -3
- package/dist/h3/index.d.ts +10 -7
- package/dist/h3/index.js +3 -3
- package/dist/headers-B9jQxjtW.d.ts +10 -0
- package/dist/{helpers-DY3vXuGs.js → helpers-DJicaG6f.js} +40 -28
- package/dist/hono/index.d.ts +16 -10
- package/dist/hono/index.js +5 -5
- package/dist/{hosted-XBgU6RGT.js → hosted-D0IzESu_.js} +42 -21
- package/dist/{hosted-BmjzxQhp.d.ts → hosted-DVxlLZMe.d.ts} +33 -21
- package/dist/{http-ujKm0JXE.js → http-DH94eh6f.js} +4 -4
- package/dist/{http-BBoKefeS.js → http-DKLXrFGk.js} +13 -7
- package/dist/{http-Ca3Sr5x1.js → http-DOL33cO0.js} +11 -8
- package/dist/{import-DB2EG_iy.js → import-ACkOrv6g.js} +6 -6
- package/dist/{import-Bz0GefWg.js → import-CST57XLp.js} +64 -51
- package/dist/{index-CMiPH2Tv.d.ts → index-BinlXeU-.d.ts} +64 -37
- package/dist/{index-BSfRl0hZ.d.ts → index-CV-7DWeD.d.ts} +64 -52
- package/dist/{index-BE8y3moU.d.ts → index-DJwEgYzA.d.ts} +127 -82
- package/dist/{index-DH9DUDEM.d.ts → index-Dj2qF9VC.d.ts} +54 -30
- package/dist/{index-BTkwrK7v.d.ts → index-vYjMrXyK.d.ts} +336 -173
- package/dist/{index-DLwcuCN_.d.ts → index-ymept53A.d.ts} +51 -30
- package/dist/index.d.ts +22 -22
- package/dist/index.js +34 -34
- package/dist/{init-B-4t1NzC.js → init-BjuFDYT1.js} +10 -10
- package/dist/{init-5TXNji88.js → init-txYTuW9u.js} +5 -5
- package/dist/{instance-options-CU0YQCLH.d.ts → instance-options-CoHn-0e8.d.ts} +4 -4
- package/dist/{instance-snapshot-CjcwnEiV.js → instance-snapshot-DuWf1cT3.js} +86 -61
- package/dist/{instant-BluqDvyS.js → instant-DK3Iz83e.js} +2 -2
- package/dist/{internals-Dk8A8fX3.js → internals-B3IgHrvo.js} +6 -3
- package/dist/{json-schema-B-vAwlde.js → json-schema-BPGa-RjQ.js} +9 -3
- package/dist/{json-schema-CN_iLFH2.d.ts → json-schema-BfXgnRNT.d.ts} +4 -4
- package/dist/jwt/index.d.ts +57 -27
- package/dist/jwt/index.js +24 -21
- package/dist/{kernel-DVExUwkp.js → kernel-qv4cr9w8.js} +32 -23
- package/dist/kysely/index.d.ts +49 -33
- package/dist/kysely/index.js +9 -7
- package/dist/{live-options-BaAFCIiu.js → live-options-BBIT68le.js} +2 -2
- package/dist/{load-DxroEzH1.js → load-DjSU5KXO.js} +6 -6
- package/dist/load-Dm0pSYH3.js +1 -0
- package/dist/{login-ClSJh5t5.js → login-CfImTJGb.js} +4 -4
- package/dist/{login-WC9HPJpU.js → login-CgfCTO3F.js} +16 -9
- package/dist/{manifest-Dv6q4i5s.js → manifest-B2QPh19C.js} +49 -38
- package/dist/{manifest-BVhExYhw.d.ts → manifest-CzzWtnAb.d.ts} +126 -31
- package/dist/{manifest-schema-Cv492Qev.js → manifest-schema-2r37XRhD.js} +44 -3
- package/dist/{manifest-B79YuF2T.js → manifest-wg8WkkpD.js} +6 -6
- package/dist/{map-claims-etGIS_Dv.js → map-claims-C-nv08_n.js} +16 -9
- package/dist/{map-error-BQGyDm_M.js → map-error-pasIGqMT.js} +7 -7
- package/dist/{markers-C6joDI3o.js → markers-BeHZ4x9Q.js} +20 -5
- package/dist/mcp/index.d.ts +114 -61
- package/dist/mcp/index.js +70 -40
- package/dist/{migrate-Dodel0IJ.js → migrate-B7oP_sQU.js} +35 -17
- package/dist/module-values-BJTBdo6y.js +107 -0
- package/dist/nest/index.d.ts +23 -13
- package/dist/nest/index.js +18 -11
- package/dist/next/client.d.ts +5 -2
- package/dist/next/client.js +1 -1
- package/dist/next/index.d.ts +79 -26
- package/dist/next/index.js +121 -10
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +4 -3
- package/dist/node/index.d.ts +5 -5
- package/dist/node/index.js +3 -3
- package/dist/{ocsf-BFp-PZZA.js → ocsf-TcjRBgIs.js} +15 -5
- package/dist/openai/index.d.ts +13 -7
- package/dist/openai/index.js +3 -3
- package/dist/openapi/index.d.ts +51 -30
- package/dist/openapi/index.js +2 -2
- package/dist/{openapi-ztVaux6j.js → openapi-BGeuunWC.js} +23 -14
- package/dist/{openapi-DMocl1bh.js → openapi-BIpw3Mb7.js} +4 -5
- package/dist/{openapi-Ddb4628T.js → openapi-BOsx0xn9.js} +33 -18
- package/dist/{operation-findings-kRcEp8lE.js → operation-findings-CXtFzlKJ.js} +3 -3
- package/dist/{operations-BNLes61x.d.ts → operations-Ci_rT6Uo.d.ts} +10 -7
- package/dist/orpc/index.d.ts +68 -14
- package/dist/orpc/index.js +62 -13
- package/dist/otel/index.d.ts +7 -4
- package/dist/otel/index.js +8 -5
- package/dist/{output-DgSDLrta.js → output-DV5dwOTs.js} +9 -5
- package/dist/output-files-BHOIlrfD.js +1 -0
- package/dist/{output-files-CCMdcFWb.js → output-files-BIQ_9pIs.js} +29 -17
- package/dist/ownership-DA5YdYTm.js +1059 -0
- package/dist/{parse-Ddhz2xf4.js → parse-1_ZheTW5.js} +16 -13
- package/dist/parse-local-manifest-CCmbhnxP.d.ts +10 -0
- package/dist/{parse-local-manifest-T1Q8X3A2.js → parse-local-manifest-DEiCF4ah.js} +19 -7
- package/dist/{parse-request-D_tUXG6M.js → parse-request-CwCZpyjV.js} +7 -8
- package/dist/{paths-DXSHAMIa.js → paths-CUvURzHp.js} +4 -1
- package/dist/pdp/index.d.ts +9 -6
- package/dist/pdp/index.js +20 -13
- package/dist/{peer-DOzzMfZL.js → peer-C9LvBCcb.js} +1 -1
- package/dist/{peer-12Q5I_RH.js → peer-Q0fuZ6hx.js} +9 -3
- package/dist/{permdock-BfWEeMqk.js → permdock-BvjivVb9.js} +200 -140
- package/dist/{permdock-Dco6dyCK.d.ts → permdock-CprAPN3w.d.ts} +260 -129
- package/dist/{permission-registry-DpPtSKNZ.js → permission-registry-B8tYxPIt.js} +23 -10
- package/dist/{permission-types-DhNEmoRG.d.ts → permission-types-CAY-GU7e.d.ts} +127 -41
- package/dist/{permissions-CoqCOfJ3.d.ts → permissions-Bx6UI8n1.d.ts} +6 -3
- package/dist/{permissions-CyH2MV_B.js → permissions-CVXmRILq.js} +3 -3
- package/dist/{permissions-DArULThq.js → permissions-CYcHj_w7.js} +8 -5
- package/dist/pg-DQTqRrou.js +60 -0
- package/dist/{pins-8ILTftC9.js → pins-nqarl3oE.js} +4 -1
- package/dist/{pkce-5h8NgMQJ.js → pkce-CnmmYqUZ.js} +1 -1
- package/dist/{policy-B7dg_yzu.d.ts → policy-BycVKnXv.d.ts} +5 -5
- package/dist/{policy-DaJy4nK4.js → policy-CLQ8__GF.js} +43 -23
- package/dist/{policy-from-catalog-DFWYkNdu.js → policy-from-catalog-CU3An8uP.js} +28 -25
- package/dist/{policy-module-CTlwMvLI.js → policy-module-xym9fnSq.js} +12 -6
- package/dist/{policy-types-B-F-qAZk.d.ts → policy-types-DGIO_gxq.d.ts} +782 -357
- package/dist/{postgres-store-BL9M_CA1.js → postgres-store-DcyrPS-r.js} +43 -16
- package/dist/{postgrest-r5faDIzD.js → postgrest-5Dr3W64L.js} +84 -86
- package/dist/{postgrest-CYacM_u7.d.ts → postgrest-D5nUGm-c.d.ts} +55 -35
- package/dist/powersync-B701tvhE.js +1 -0
- package/dist/{powersync-13odBwiM.js → powersync-Bg8u-JcR.js} +3 -3
- package/dist/{powersync-53ryOZip.js → powersync-CJrT1DSc.js} +127 -69
- package/dist/preset/index.d.ts +38 -28
- package/dist/preset/index.js +1 -1
- package/dist/{preset-CHTbd-DQ.js → preset-BjLzeFpp.js} +56 -32
- package/dist/{principal-source-pHneukrx.js → principal-source-C6N60GtM.js} +10 -10
- package/dist/prisma/index.d.ts +69 -41
- package/dist/prisma/index.js +21 -17
- package/dist/{problem-CTviyH5L.js → problem-BVLHFK33.js} +41 -21
- package/dist/{problem-details-cn5X30gZ.js → problem-details-CLDgX1KV.js} +13 -4
- package/dist/{problem-types-CJ2325SZ.js → problem-types-DSsRfONQ.js} +4 -1
- package/dist/{prompt-DwtVemER.js → prompt-BYc6drYW.js} +4 -1
- package/dist/protected-action-D8x73IGm.d.ts +58 -0
- package/dist/protected-action-DUYqb-Ma.js +80 -0
- package/dist/{protected-DXE3cQYg.js → protected-action-QEA8arRB.js} +48 -14
- package/dist/{provider-BQsTYYy0.js → provider-C55OGXJ7.js} +5 -4
- package/dist/{provider-Csk-THAn.d.ts → provider-DfLeek9O.d.ts} +1 -1
- package/dist/{provider-store-IgqQipCO.js → provider-store-CaTv00tr.js} +13 -9
- package/dist/{pull-CN5OLxrF.js → pull-2nJr0qg_.js} +12 -11
- package/dist/pull-C3EEGzSw.js +1 -0
- package/dist/{push-tVdG8dUL.js → push-Bn-lMEmI.js} +19 -13
- package/dist/{pushed-policy-CmYeSQqq.js → pushed-policy-CDuOL2de.js} +10 -7
- package/dist/{pushed-policy-BbcY9r_4.d.ts → pushed-policy-CryQheKq.d.ts} +12 -6
- package/dist/react/index.d.ts +9 -8
- package/dist/react/index.js +7 -6
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +94 -55
- package/dist/react-native/index.js +200 -61
- package/dist/{refs-CNJBiTFo.js → refs-C8BKxc_e.js} +2 -2
- package/dist/{relation-walk-3OyDBF0S.js → relation-walk-DK_gkgL7.js} +12 -9
- package/dist/{relations-BnCWnJGI.js → relations-BxvRvDHK.js} +3 -3
- package/dist/{replay-Du1tJjG7.js → replay-BU09ad4L.js} +1 -1
- package/dist/{replay-DCDD_ZfL.d.ts → replay-Bs9Df2fQ.d.ts} +1 -1
- package/dist/{request-context-BykKLmyE.js → request-context-Duzdf0cd.js} +8 -8
- package/dist/{request-schema-CMreVuFM.js → request-schema-DhX7UbfQ.js} +4 -1
- package/dist/resend/index.d.ts +34 -28
- package/dist/resend/index.js +29 -29
- package/dist/resolve-names-ConCSj1y.js +201 -0
- package/dist/{resource-relations-D_jw0BFx.js → resource-relations-8-pCScBO.js} +18 -9
- package/dist/{retire-Wj6dkyDE.js → retire-B2xffQgY.js} +15 -6
- package/dist/{revocations-R07lFBnX.d.ts → revocations-BaUXHM1N.d.ts} +6 -3
- package/dist/{rls-Ck_PocSp.js → rls-Ba9wN0SP.js} +13 -6
- package/dist/{rls-CxBZb4rZ.js → rls-Cx0dtsB7.js} +310 -128
- package/dist/{role-builder-C1VJzvcr.js → role-builder-BK2-T6oD.js} +41 -20
- package/dist/{row-conditions-h2HythXQ.js → row-conditions-BAGHOmVq.js} +4 -3
- package/dist/{rpc-Bk1bE-ch.js → rpc-vHIwyXCp.js} +8 -5
- package/dist/run-Ck22anC2.js +1 -0
- package/dist/{run-uLD2aWuO.js → run-PbRlXTfh.js} +26 -23
- package/dist/{run-ticket-BpNi7BCi.d.ts → run-ticket-B2o2Kxz8.d.ts} +29 -14
- package/dist/{run-ticket-B5d_63Ma.js → run-ticket-Dv7aF7Dk.js} +9 -9
- package/dist/{runtime-Vif3_-zf.js → runtime-BGnrOkVH.js} +5 -2
- package/dist/{saas-Az_Kz-4D.js → saas-DK4V-3Is.js} +42 -29
- package/dist/{scan-DSljDE1Y.js → scan-CRTj6fke.js} +1 -1
- package/dist/{schema-B4v0Qrac.js → schema-BOJjqlWL.js} +3 -2
- package/dist/{schema-gIcCx7zM.js → schema-BTsE1WRT.js} +8 -4
- package/dist/scim/index.d.ts +2 -2
- package/dist/scim/index.js +2 -2
- package/dist/{scim-C7JYhySg.js → scim-D8YupzX1.js} +2 -2
- package/dist/{scim-YNieBO1U.js → scim-jJBJ7g1Z.js} +33 -15
- package/dist/{scopes-BHsc-wp-.js → scopes-0Up_UA2t.js} +41 -23
- package/dist/server/index.d.ts +11 -11
- package/dist/server/index.js +12 -12
- package/dist/{server-Bnga6I50.js → server-3cVlTLde.js} +31 -31
- package/dist/{server-types-C4gZUEZy.d.ts → server-types-CyRN0-Cd.d.ts} +88 -46
- package/dist/{sha256-D9ihqmvd.js → sha256-C-4WXKIu.js} +4 -3
- package/dist/{shared-fMKOv8hP.js → shared-Afctg7B0.js} +65 -32
- package/dist/{shared-C8LjUdVU.js → shared-l0oGGetg.js} +9 -6
- package/dist/{signals-D3NcTKUS.js → signals-B1vHR3fN.js} +2 -2
- package/dist/{sink-Cp_Eot9W.d.ts → sink-B5zoUW_O.d.ts} +44 -20
- package/dist/{sink-BEZSlMS_.js → sink-D9_vh372.js} +32 -21
- package/dist/{skills-g_3BS9NC.js → skills-C9uKfSF5.js} +3 -3
- package/dist/{skills-Dcjw9_1C.js → skills-CQQcuGBi.js} +12 -8
- package/dist/{snapshot-CbGmYiJx.js → snapshot-CXlzi69u.js} +21 -18
- package/dist/{snapshot-cache-Pahd3IYJ.d.ts → snapshot-cache-BjAWkchD.d.ts} +21 -12
- package/dist/{snapshot-cache-DT2Kx1F8.js → snapshot-cache-Bx_4xC_6.js} +14 -11
- package/dist/solid/index.d.ts +55 -16
- package/dist/solid/index.js +46 -12
- package/dist/solid-start/index.d.ts +30 -12
- package/dist/solid-start/index.js +3 -3
- package/dist/{sources-B-zYB4GS.js → sources-CMAM-2A3.js} +46 -27
- package/dist/{sources-DEEuGb8S.d.ts → sources-CWG32C0G.d.ts} +126 -53
- package/dist/{sql-B96movIM.js → sql-0WjxIp9g.js} +28 -7
- package/dist/{sql-CDEPCA0g.d.ts → sql-B80DFdpG.d.ts} +2 -2
- package/dist/{src-DLFrtrrC.js → src-C_pi2Rnh.js} +292 -101
- package/dist/ssf/index.d.ts +3 -3
- package/dist/ssf/index.js +47 -35
- package/dist/{standard-webhooks-DgFFD-Ga.js → standard-webhooks-D67a3nzk.js} +5 -5
- package/dist/{standard-webhooks-B_qRg0ws.d.ts → standard-webhooks-DOYEuOLx.d.ts} +7 -7
- package/dist/{store-DSP9oGvh.js → store-RNFvaJJv.js} +32 -26
- package/dist/{store-options-B_poyYvg.js → store-options-Dil3lraY.js} +105 -15
- package/dist/stores-DZJvxFIZ.d.ts +59 -0
- package/dist/{stores-BqEuHC05.js → stores-DxZWDXCs.js} +42 -5
- package/dist/{stream-2Vwhl7Er.d.ts → stream-9y9qw51m.d.ts} +4 -3
- package/dist/{stream-CvdUS9V1.js → stream-CDvF0sJm.js} +5 -5
- package/dist/{style-Ds1gNm77.js → style-DblOtSga.js} +1 -1
- package/dist/{style-Cqo7-73x.js → style-Ds11Hmk4.js} +2 -1
- package/dist/{subject-settings-CkEwrw3W.js → subject-settings-8sH5HlII.js} +8 -4
- package/dist/{row-check-Dt35czLy.d.ts → subject-settings-vNW8qmUH.d.ts} +30 -18
- package/dist/supabase/index.d.ts +48 -38
- package/dist/supabase/index.js +5 -5
- package/dist/supabase/middleware.d.ts +19 -13
- package/dist/supabase/middleware.js +7 -7
- package/dist/{supabase--0aJdLEd.js → supabase-DnMMg5Fn.js} +79 -35
- package/dist/{supabase-Bqp7iIY2.js → supabase-SBs3MYUE.js} +3 -3
- package/dist/supabase-hook-C0FyEACZ.js +1 -0
- package/dist/{supabase-hook-Dg0SrQWR.js → supabase-hook-CrgU06WH.js} +175 -196
- package/dist/{supautils-MESd81bF.js → supautils-ByezN3b8.js} +385 -219
- package/dist/{support-session-Cna49-3f.js → support-session-BsmPCbz8.js} +21 -21
- package/dist/svelte/PermissionBoundary.svelte +2 -6
- package/dist/svelte/Protected.svelte +2 -8
- package/dist/svelte/index.d.ts +9 -8
- package/dist/svelte/index.js +10 -9
- package/dist/svelte/runtime.d.ts +4 -4
- package/dist/svelte/runtime.js +4 -4
- package/dist/svelte/source.d.ts +9 -8
- package/dist/svelte/source.js +6 -5
- package/dist/sveltekit/index.d.ts +30 -12
- package/dist/sveltekit/index.js +2 -2
- package/dist/{ownership-Z2LpxsGa.js → tables-BqrxBCir.js} +641 -1104
- package/dist/tanstack-start/index.d.ts +22 -10
- package/dist/tanstack-start/index.js +2 -2
- package/dist/{target-Bw37TFNA.js → target-qdEAnnhU.js} +6 -6
- package/dist/{tenancy-CmeBQ6Nv.js → tenancy-5d1zVBvK.js} +25 -19
- package/dist/terminal/index.d.ts +23 -14
- package/dist/terminal/index.js +17 -20
- package/dist/testing/core.d.ts +1 -1
- package/dist/testing/core.js +1 -1
- package/dist/testing/fixtures/index.d.ts +8 -5
- package/dist/testing/fixtures/index.js +78 -17
- package/dist/testing/index.d.ts +225 -141
- package/dist/testing/index.js +352 -234
- package/dist/testing/saas/index.d.ts +43 -27
- 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 → text-diff-BtfSmdUo.js} +2 -2
- package/dist/{thenable-yD7SabqG.js → thenable-BNSM1U2R.js} +12 -3
- package/dist/{time-DkWjdL98.js → time-nR1JesoH.js} +32 -8
- package/dist/{token-2qYDvXz2.js → token-DC9EQiet.js} +14 -12
- package/dist/{tool-refusal-reAhIzdl.js → tool-refusal-Qpjs_W6M.js} +5 -2
- package/dist/trpc/index.d.ts +11 -8
- package/dist/trpc/index.js +15 -9
- package/dist/type-helpers-c7Xo_2Cu.d.ts +50 -0
- package/dist/{types-CfiKC5aN.d.ts → types-B2efzoSX.d.ts} +14 -11
- package/dist/{types-CIeZcG8v.d.ts → types-B6ExllNo.d.ts} +2 -2
- package/dist/{types-DBHftjgS.d.ts → types-BB-zWDcR.d.ts} +45 -33
- package/dist/{types-BfVmNJO5.d.ts → types-BMVDHvI0.d.ts} +128 -50
- package/dist/{types-DXmwu78Z.d.ts → types-CEinc15V.d.ts} +31 -14
- package/dist/{types-g7Oy6Zg4.d.ts → types-CN0LsqbK.d.ts} +47 -24
- package/dist/{types-CeIscIKp.d.ts → types-CWBsAwqn.d.ts} +23 -12
- package/dist/{types-DqzsnlSA.js → types-CcUXciSj.js} +17 -5
- package/dist/{types-BWZybqs9.d.ts → types-CzOeVvcC.d.ts} +384 -203
- package/dist/{types-DC44-hqb.d.ts → types-DLvhVm1r.d.ts} +59 -32
- package/dist/{types-D3A19w4A.d.ts → types-DlelvQk4.d.ts} +22 -13
- package/dist/{types-cTos-H7p.d.ts → types-DsHGPtkR.d.ts} +68 -32
- package/dist/{types-BXMYB7BY.d.ts → types-ZzRGoViD.d.ts} +77 -24
- package/dist/{types-CQVEQawv.d.ts → types-rCRV4Ge-.d.ts} +24 -14
- package/dist/types-tQB2CEnY.d.ts +262 -0
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/usage-0hjZPIRX.js +1 -0
- package/dist/{usage-Bybmck7f.js → usage-B9EtHWLa.js} +50 -26
- package/dist/{usage-CDaLXCfL.js → usage-CBzgwfIs.js} +2 -2
- package/dist/{validation-error-ZfUHbKvf.js → validation-error-CYg2kJEw.js} +4 -1
- package/dist/{validation-error-CQpoquwj.d.ts → validation-error-uvp-kyfO.d.ts} +5 -2
- package/dist/{validity-CZ7-y0Cj.js → validity-Buc7w8E9.js} +13 -10
- package/dist/{verifier-DHuh-okk.js → verifier-DUV6pLqZ.js} +10 -4
- package/dist/{verify-pe8rL_Qw.js → verify-DlUcTDBz.js} +148 -487
- package/dist/version-DMQ0dnN5.js +48 -0
- package/dist/version-DizhmD72.js +1 -0
- package/dist/{views-BB8E9dTP.js → views-CD8wuL4Z.js} +19 -6
- package/dist/{vocabulary-Bg3eeT2H.d.ts → vocabulary-DEJ5j4mR.d.ts} +7 -4
- package/dist/vue/index.d.ts +71 -22
- package/dist/vue/index.js +50 -13
- package/dist/{waiter-DwEyouan.d.ts → waiter-CqUKX6CY.d.ts} +13 -10
- package/dist/{waiter-BGUtGYoU.js → waiter-aBLM63_f.js} +1 -1
- package/dist/{watch-C-4VmFlL.js → watch-BFDKqmUa.js} +8 -8
- package/dist/watch-ClLuO2TL.js +1 -0
- package/dist/webmcp/index.d.ts +11 -11
- package/dist/webmcp/index.js +8 -5
- package/dist/{where-scope-wq2coopv.js → where-scope-DlCdCM_8.js} +24 -18
- package/dist/{who-can-BwNsI_Js.js → who-can-BoDJm0Nc.js} +23 -20
- package/dist/workflow/index.d.ts +3 -3
- package/dist/workflow/index.js +13 -13
- package/dist/workflow-sdk/index.d.ts +19 -13
- package/dist/workflow-sdk/index.js +6 -6
- package/dist/{wrap-CiSc_KCq.js → wrap-B2wziJI_.js} +4 -4
- package/dist/{wrap-B6R0cMRL.d.ts → wrap-DxXB54Wu.d.ts} +8 -5
- package/package.json +8 -8
- package/schemas/catalog-v1.json +3 -0
- package/schemas/supabase-manifest-v1.json +33 -1
- package/skills/permdock-credentials/references/tokens-keys-links.md +8 -6
- package/skills/permdock-data/references/queries-and-rls.md +2 -1
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +2 -2
- package/skills/permdock-wire/references/adapters.md +16 -23
- package/dist/catalog-BCCjSAdH.js +0 -1
- package/dist/collect-k_Dulxr5.js +0 -1
- package/dist/config-CoQHDrDi.js +0 -166
- package/dist/denied-text-D4qkEtFz.d.ts +0 -19
- package/dist/fields-ClcQLrVc.js +0 -238
- package/dist/generate-CNU1pIOo.js +0 -171
- package/dist/headers-BIjfMaoC.js +0 -8
- package/dist/headers-CIbDTUTK.d.ts +0 -7
- package/dist/load-DrMHXzgN.js +0 -1
- package/dist/output-files-DPAY7-GX.js +0 -1
- package/dist/package-root-Dk_ETdkp.js +0 -31
- package/dist/parse-local-manifest-e4mdwAKd.d.ts +0 -10
- package/dist/policies-B2lZIurj.js +0 -295
- package/dist/powersync-CExtTLyA.js +0 -1
- package/dist/problem-details-Tcfmj3bV.d.ts +0 -7
- package/dist/protected-BwZYL1X_.d.ts +0 -39
- package/dist/pull-Cty6Ovyu.js +0 -1
- package/dist/run-Bz7DXGEv.js +0 -1
- package/dist/stores-BAZwSYZA.d.ts +0 -31
- package/dist/supabase-hook-DJxC4PoP.js +0 -1
- package/dist/types-CKV71kKh.d.ts +0 -172
- package/dist/usage-y-kBNdqs.js +0 -1
- package/dist/version-CEbQ5UeM.js +0 -18
- package/dist/version-DXkYUISx.js +0 -1
- package/dist/watch-Bd9mx3rd.js +0 -1
|
@@ -1,47 +1,46 @@
|
|
|
1
1
|
import { a as sole, r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
-
import { l as isConditionRef, o as hasConditionOp } from "./refs-
|
|
3
|
-
import {
|
|
4
|
-
import { c as listPermissions, h as formerKeys, n as findPermission } from "./permission-registry-
|
|
5
|
-
import {
|
|
6
|
-
import { f as nowMillis, u as isoAt } from "./time-
|
|
2
|
+
import { l as isConditionRef, o as hasConditionOp } from "./refs-C8BKxc_e.js";
|
|
3
|
+
import { j as memoryCustomRoleSource, x as memoryRelationSource } from "./src-C_pi2Rnh.js";
|
|
4
|
+
import { c as listPermissions, h as formerKeys, n as findPermission } from "./permission-registry-B8tYxPIt.js";
|
|
5
|
+
import { c as isEdgeRelation, l as isFieldRelation, u as isPrincipalRelation } from "./resource-relations-8-pCScBO.js";
|
|
6
|
+
import { f as nowMillis, u as isoAt } from "./time-nR1JesoH.js";
|
|
7
7
|
import { t as canonicalJson } from "./canonical-json-DwilZ1iB.js";
|
|
8
|
-
import { f as scopeList, l as rootScope, o as normalizeMemberships } from "./scopes-
|
|
9
|
-
import { o as requiresStandIn } from "./delegation-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { p as scopeField } from "./tenancy-CmeBQ6Nv.js";
|
|
13
|
-
import { c as policyLevels } from "./policy-DaJy4nK4.js";
|
|
8
|
+
import { f as scopeList, l as rootScope, o as normalizeMemberships } from "./scopes-0Up_UA2t.js";
|
|
9
|
+
import { o as requiresStandIn } from "./delegation-CDB6_keF.js";
|
|
10
|
+
import { p as scopeField } from "./tenancy-5d1zVBvK.js";
|
|
11
|
+
import { l as policyLevels, t as coveredLevels } from "./policy-CLQ8__GF.js";
|
|
14
12
|
import { t as requiresApproval } from "./approval-required-TzNB9oQA.js";
|
|
15
|
-
import { c as andWhere, m as customRoleScope, v as leveled, y as membershipsClaim } from "./evaluate-
|
|
16
|
-
import { t as getPermDock } from "./permdock-
|
|
17
|
-
import { a as describeError, i as configError, u as usageResult } from "./errors-
|
|
18
|
-
import { t as policyRowConditionKeys } from "./row-conditions-
|
|
19
|
-
import { s as writeFileEnsuringDir } from "./files-
|
|
20
|
-
import { c as jsonSchemaOf } from "./catalog-doc-
|
|
21
|
-
import { o as loadPolicyAt } from "./load-
|
|
22
|
-
import { i as qualifySqlTable, n as defaultSqlCommand, t as SQL_IDENT } from "./sql-
|
|
23
|
-
import {
|
|
24
|
-
import "./budget-
|
|
25
|
-
import "./sources-
|
|
13
|
+
import { c as andWhere, m as customRoleScope, v as leveled, y as membershipsClaim } from "./evaluate-BAmf_AQd.js";
|
|
14
|
+
import { t as getPermDock } from "./permdock-BvjivVb9.js";
|
|
15
|
+
import { a as describeError, i as configError, u as usageResult } from "./errors-Vpu6rKHl.js";
|
|
16
|
+
import { t as policyRowConditionKeys } from "./row-conditions-BAGHOmVq.js";
|
|
17
|
+
import { s as writeFileEnsuringDir } from "./files-BSjrtIHw.js";
|
|
18
|
+
import { c as jsonSchemaOf } from "./catalog-doc-DjMKzJp9.js";
|
|
19
|
+
import { o as loadPolicyAt } from "./load-DjSU5KXO.js";
|
|
20
|
+
import { i as qualifySqlTable, n as defaultSqlCommand, t as SQL_IDENT } from "./sql-0WjxIp9g.js";
|
|
21
|
+
import { a as scopeMembershipTable, i as scopeColumn } from "./compile-EzEMB-7v.js";
|
|
22
|
+
import "./budget-BELja5A9.js";
|
|
23
|
+
import "./sources-CMAM-2A3.js";
|
|
26
24
|
import { t as beforeFirst } from "./text-BMFRfBqc.js";
|
|
27
|
-
import { t as DOCS_BASE } from "./manifest-
|
|
28
|
-
import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-
|
|
29
|
-
import { A as quoteQualified,
|
|
30
|
-
import {
|
|
25
|
+
import { t as DOCS_BASE } from "./manifest-B2QPh19C.js";
|
|
26
|
+
import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-A1_sp_tR.js";
|
|
27
|
+
import { A as quoteQualified, N as arrayColumnsOf, O as quoteIdent, P as columnTypesOf, _ as subjectClaimJsonSql, a as customRolesOn, j as quoteTable, k as quoteLiteral, r as HELPERS, v as subjectClaimSql } from "./shared-Afctg7B0.js";
|
|
28
|
+
import { C as accessSql, D as tokenScopeAllowsCall, S as requiresSql, ht as scopeTable, k as capabilityAccessSql, n as tableNameOf, o as graphPlan, q as apiKeyAllowsCall, r as tableNames } from "./tables-BqrxBCir.js";
|
|
29
|
+
import { c as breakGlassKey, f as compileConditionSql, l as collectGrants, m as sqlFunctionNames, p as contextRefs, s as breakGlassHolder } from "./ownership-DA5YdYTm.js";
|
|
31
30
|
import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
|
|
32
|
-
import {
|
|
33
|
-
import { s as viewName, t as FIELD_VIEWS } from "./fields-
|
|
31
|
+
import { n as pgConnector, t as connectPgPool } from "./pg-DQTqRrou.js";
|
|
32
|
+
import { _ as wrapSql, g as validitySql, m as andSql, s as viewName, t as FIELD_VIEWS } from "./fields-D0vUI6dq.js";
|
|
34
33
|
import { resolve } from "node:path";
|
|
35
34
|
import { createHash, randomUUID } from "node:crypto";
|
|
36
35
|
//#region src/cli/rls/indexes.ts
|
|
37
36
|
/** Postgres truncates identifiers longer than this, in bytes. */
|
|
38
37
|
const MAX_IDENT = 63;
|
|
39
38
|
/**
|
|
40
|
-
* The row fields `condition` compares with the subject, a claim or a
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* The row fields `condition` compares with the subject, a claim or a membership
|
|
40
|
+
* (`ownerId: principal.id`, `memberOf`), through `and` and `or`: the selective
|
|
41
|
+
* comparisons an index serves. A comparison with a constant (`status: 'sent'`)
|
|
42
|
+
* narrows rows the scope key already found, a negation scans anyway, and a
|
|
43
|
+
* dotted field is a JSON path a plain index does not serve.
|
|
45
44
|
*/
|
|
46
45
|
function indexedFields(condition) {
|
|
47
46
|
if (condition === void 0) return [];
|
|
@@ -71,9 +70,9 @@ function plainField(field) {
|
|
|
71
70
|
return field.includes(".") ? [] : [field];
|
|
72
71
|
}
|
|
73
72
|
/**
|
|
74
|
-
* Every index the generated SQL reads through: the row columns policies
|
|
75
|
-
*
|
|
76
|
-
*
|
|
73
|
+
* Every index the generated SQL reads through: the row columns policies filter
|
|
74
|
+
* on (scope keys and selective condition fields), and the membership tables'
|
|
75
|
+
* user column, followed by the scope column the helpers select.
|
|
77
76
|
*/
|
|
78
77
|
function indexTargets(ctx, rowColumns) {
|
|
79
78
|
const targets = /* @__PURE__ */ new Map();
|
|
@@ -106,7 +105,10 @@ function indexName(target) {
|
|
|
106
105
|
function indexesSql(targets) {
|
|
107
106
|
return targets.map((target) => `create index if not exists ${quoteIdent(indexName(target))} on ${quoteTable(target.table)} (${target.columns.map(quoteIdent).join(", ")});`).join("\n");
|
|
108
107
|
}
|
|
109
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Every live column of the tables in `$1` (`schema.table`), as `target` and
|
|
110
|
+
* `name`.
|
|
111
|
+
*/
|
|
110
112
|
const COLUMNS_SQL = `select n.nspname || '.' || c.relname as target, a.attname as name
|
|
111
113
|
from pg_attribute a
|
|
112
114
|
join pg_class c on c.oid = a.attrelid
|
|
@@ -191,25 +193,30 @@ function levelReachOf(policy, keys) {
|
|
|
191
193
|
const { item } = entry;
|
|
192
194
|
const { grant } = item;
|
|
193
195
|
if (item.access.kind !== "role" || grant.effect !== "allow" || grant.permission.kind !== "instance" || grant.level !== void 0) continue;
|
|
194
|
-
const
|
|
196
|
+
const resource = grant.permission.resource;
|
|
197
|
+
const levels = policyLevels(policy, resource);
|
|
195
198
|
if (levels.length > 0) {
|
|
196
199
|
const set = reach.get(grantKey) ?? /* @__PURE__ */ new Set();
|
|
197
|
-
|
|
200
|
+
const reached = coveredLevels(policy, resource, reachedLevels(grant.where, levels));
|
|
201
|
+
for (const name of reached) set.add(name);
|
|
198
202
|
reach.set(grantKey, set);
|
|
199
203
|
}
|
|
200
204
|
}
|
|
205
|
+
const resourceOf = new Map([...keys].map(([entry, grantKey]) => [grantKey, entry.item.grant.permission.resource]));
|
|
201
206
|
const out = [];
|
|
202
207
|
for (const grantKey of new Set(keys.values())) {
|
|
203
208
|
const at = grantKey.lastIndexOf("@");
|
|
204
209
|
const base = at === -1 ? grantKey : grantKey.slice(0, at);
|
|
205
210
|
const level = at === -1 ? void 0 : grantKey.slice(at + 1);
|
|
206
|
-
|
|
211
|
+
const covered = level === void 0 ? void 0 : coveredLevels(policy, resourceOf.get(grantKey) ?? "", [level]);
|
|
212
|
+
for (const name of reach.get(base) ?? []) if (covered === void 0 || covered.includes(name)) out.push([grantKey, name]);
|
|
207
213
|
}
|
|
208
214
|
return out.toSorted(([a, x], [b, y]) => a === b ? x < y ? -1 : 1 : a < b ? -1 : 1);
|
|
209
215
|
}
|
|
210
216
|
/**
|
|
211
|
-
* The SQL command `action` compiles to: `rls.actions` first, then the six
|
|
212
|
-
* Any other verb is `undefined`, as `'none'` is: no policy, and
|
|
217
|
+
* The SQL command `action` compiles to: `rls.actions` first, then the six
|
|
218
|
+
* default verbs. Any other verb is `undefined`, as `'none'` is: no policy, and
|
|
219
|
+
* its grants are still seeded.
|
|
213
220
|
*/
|
|
214
221
|
function commandFor(action, actions) {
|
|
215
222
|
if (actions !== void 0 && Object.hasOwn(actions, action)) {
|
|
@@ -218,7 +225,7 @@ function commandFor(action, actions) {
|
|
|
218
225
|
}
|
|
219
226
|
return defaultSqlCommand(action);
|
|
220
227
|
}
|
|
221
|
-
function tableFor
|
|
228
|
+
function tableFor(resource, tables) {
|
|
222
229
|
return tables?.[resource] ?? resource;
|
|
223
230
|
}
|
|
224
231
|
function ancestorsOf(policy, name) {
|
|
@@ -266,8 +273,8 @@ function resourceCondition(item, policy, ctx) {
|
|
|
266
273
|
}
|
|
267
274
|
/**
|
|
268
275
|
* The access a link capability has to a resource-scoped grant, over the same
|
|
269
|
-
* holders as `resourceCondition`. It reads the claim, so ancestor hops need
|
|
270
|
-
*
|
|
276
|
+
* holders as `resourceCondition`. It reads the claim, so ancestor hops need no
|
|
277
|
+
* memberships table. `undefined` when no holder is on the row's chain.
|
|
271
278
|
*/
|
|
272
279
|
function capabilityAccess(item, policy, ctx) {
|
|
273
280
|
if (item.access.kind !== "resource") return;
|
|
@@ -310,7 +317,7 @@ function prepare(item, tables, warnings, skipClosures, sql, rowActions = false)
|
|
|
310
317
|
}
|
|
311
318
|
const tableless = sql.tableless?.has(grant.permission.resource) === true;
|
|
312
319
|
const command = tableless ? void 0 : commandFor(grant.permission.action, sql.actions);
|
|
313
|
-
const table = tableFor
|
|
320
|
+
const table = tableFor(grant.permission.resource, tables);
|
|
314
321
|
if (command === void 0) {
|
|
315
322
|
if (tableless && item.access.kind !== "role") {
|
|
316
323
|
warnings.push(`skipped ${label}/${grant.permission.key}: rls.tables.${grant.permission.resource} is false, so the resource has no table, and only role grants are seeded`);
|
|
@@ -359,9 +366,9 @@ function signature(entry, byFields) {
|
|
|
359
366
|
/**
|
|
360
367
|
* Grant keys per permission: the key is the permission, split into
|
|
361
368
|
* `permission#n` when role grants carry different portable conditions (or
|
|
362
|
-
* effects), one key per condition group; a group whose grants set `group`
|
|
363
|
-
*
|
|
364
|
-
*
|
|
369
|
+
* effects), one key per condition group; a group whose grants set `group` is
|
|
370
|
+
* `permission#<group>` instead. With field views, grants that differ only in
|
|
371
|
+
* `fields` get their own keys too, so a view can tell them apart.
|
|
365
372
|
*/
|
|
366
373
|
function assignKeys(entries, byFields) {
|
|
367
374
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -381,7 +388,7 @@ function assignKeys(entries, byFields) {
|
|
|
381
388
|
const group = groupOf(permission, list);
|
|
382
389
|
if (group !== void 0 && named.has(group)) throw configError(`PermDock CLI: grants of ${permission} with different conditions share group '${group}'; a group names one condition`);
|
|
383
390
|
if (group !== void 0) named.add(group);
|
|
384
|
-
const grantKey = group
|
|
391
|
+
const grantKey = group === void 0 ? lists.length === 1 ? permission : `${permission}#${index + 1}` : `${permission}#${group}`;
|
|
385
392
|
for (const entry of list) keys.set(entry, grantKey);
|
|
386
393
|
}
|
|
387
394
|
}
|
|
@@ -432,11 +439,14 @@ function noteConditions(entry, warnings) {
|
|
|
432
439
|
if (hasConditionOp(entry.using, "opaque") || hasConditionOp(entry.check, "opaque")) warnings.push(`opaque SQL on ${label}/${grant.permission.key} is untestable app-side`);
|
|
433
440
|
}
|
|
434
441
|
/**
|
|
435
|
-
* Compiles every grant (role and top-level) to branches whose role check is
|
|
436
|
-
*
|
|
442
|
+
* Compiles every grant (role and top-level) to branches whose role check is a
|
|
443
|
+
* helper call keyed by grant key. Nothing in a branch is per-row except the
|
|
437
444
|
* portable row condition and resource-membership joins.
|
|
438
445
|
*/
|
|
439
|
-
/**
|
|
446
|
+
/**
|
|
447
|
+
* A Supabase token from a permanent user: anonymous sign-ins carry
|
|
448
|
+
* `is_anonymous: true`.
|
|
449
|
+
*/
|
|
440
450
|
const PERMANENT_USER = `((select auth.jwt()) ->> 'is_anonymous') is distinct from 'true'`;
|
|
441
451
|
function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions = false) {
|
|
442
452
|
const items = collectGrants(policy);
|
|
@@ -501,7 +511,7 @@ function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions =
|
|
|
501
511
|
};
|
|
502
512
|
rows.set(`${row.role}\u0000${row.grantKey}\u0000${row.scope}`, row);
|
|
503
513
|
}
|
|
504
|
-
if (!rowActions || ctx.tableless?.has(grant.permission.resource)) continue;
|
|
514
|
+
if (!rowActions || ctx.tableless?.has(grant.permission.resource) === true) continue;
|
|
505
515
|
}
|
|
506
516
|
const actionOnly = entry.command === "none";
|
|
507
517
|
const command = actionOnly ? "select" : entry.command;
|
|
@@ -589,8 +599,8 @@ function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions =
|
|
|
589
599
|
}
|
|
590
600
|
/**
|
|
591
601
|
* Each row again under every key its permission was renamed from, so SQL that
|
|
592
|
-
* still calls `permdock_has('<old key>')` keeps the access the current key
|
|
593
|
-
*
|
|
602
|
+
* still calls `permdock_has('<old key>')` keeps the access the current key has.
|
|
603
|
+
* A split grant key (`key#n`) keeps its suffix; break-glass rows are not
|
|
594
604
|
* copied.
|
|
595
605
|
*/
|
|
596
606
|
function withAliasRows(policy, rows) {
|
|
@@ -609,8 +619,8 @@ function withAliasRows(policy, rows) {
|
|
|
609
619
|
}
|
|
610
620
|
/**
|
|
611
621
|
* Postgres needs SELECT access to find rows for UPDATE / DELETE and for
|
|
612
|
-
* RETURNING, so a table with only update or delete grants gets matching
|
|
613
|
-
*
|
|
622
|
+
* RETURNING, so a table with only update or delete grants gets matching SELECT
|
|
623
|
+
* branches.
|
|
614
624
|
*/
|
|
615
625
|
function ensureSelectCoverage(branches, warnings) {
|
|
616
626
|
const extra = [];
|
|
@@ -640,18 +650,21 @@ function ensureSelectCoverage(branches, warnings) {
|
|
|
640
650
|
const HELPER_TABLES = ["storage.objects", "realtime.messages"];
|
|
641
651
|
const HELPER_CALL = new RegExp(String.raw`\b(${HELPERS.has}(?:_permission)?|permitted_[a-z][a-z0-9_]*_ids(?:_by_permission)?)\s*\(\s*'((?:[^']|'')+)'(\s*,\s*(false\b)?)?`, "giu");
|
|
642
652
|
const ANY_HELPER = new RegExp(String.raw`\b(?:${HELPERS.has}(?:_permission)?(?:_for)?|permitted_[a-z][a-z0-9_]*_ids(?:_by_permission)?(?:_for)?|member_[a-z][a-z0-9_]*_ids(?:_for)?)\s*\(`, "iu");
|
|
643
|
-
/**
|
|
653
|
+
/**
|
|
654
|
+
* Whether a policy expression calls any generated helper, the key-less
|
|
655
|
+
* `member_<scope>_ids` included.
|
|
656
|
+
*/
|
|
644
657
|
function callsHelper(sql) {
|
|
645
658
|
return ANY_HELPER.test(sql);
|
|
646
659
|
}
|
|
647
660
|
/**
|
|
648
661
|
* The permission keys a policy expression passes to `permdock_has`,
|
|
649
662
|
* `permitted_<scope>_ids` or their permission-key forms, `#n` stripped. With
|
|
650
|
-
* `blind`, only the calls that ignore row conditions: the grant-key forms,
|
|
651
|
-
*
|
|
663
|
+
* `blind`, only the calls that ignore row conditions: the grant-key forms, and
|
|
664
|
+
* a permission-key form asked for conditioned allows. A plain
|
|
652
665
|
* `permdock_has_permission(key)` or `permitted_<scope>_ids_by_permission(key)`
|
|
653
|
-
* counts only allows without a row condition, so it never grants more than
|
|
654
|
-
*
|
|
666
|
+
* counts only allows without a row condition, so it never grants more than the
|
|
667
|
+
* application does.
|
|
655
668
|
*/
|
|
656
669
|
function helperCallKeys(sql, blind = false) {
|
|
657
670
|
const keys = /* @__PURE__ */ new Set();
|
|
@@ -671,7 +684,10 @@ function helperTable(name) {
|
|
|
671
684
|
const table = tableName(name);
|
|
672
685
|
return HELPER_TABLES.find((candidate) => candidate === table);
|
|
673
686
|
}
|
|
674
|
-
/**
|
|
687
|
+
/**
|
|
688
|
+
* `create policy` statements on {@link HELPER_TABLES} in migration SQL, with
|
|
689
|
+
* the keys their condition-blind helper calls pass.
|
|
690
|
+
*/
|
|
675
691
|
function helperTablePolicies(sql) {
|
|
676
692
|
const text = sql.replaceAll(/--[^\n]*/gu, "").replaceAll(/\/\*[\s\S]*?\*\//gu, "");
|
|
677
693
|
const found = [];
|
|
@@ -686,19 +702,25 @@ function helperTablePolicies(sql) {
|
|
|
686
702
|
}
|
|
687
703
|
return found;
|
|
688
704
|
}
|
|
689
|
-
/**
|
|
705
|
+
/**
|
|
706
|
+
* `pg_policies` rows on {@link HELPER_TABLES}, read by `rls verify
|
|
707
|
+
* --db-url-stdin`.
|
|
708
|
+
*/
|
|
690
709
|
const HELPER_TABLE_POLICIES_SQL = `select schemaname || '.' || tablename as target, policyname, coalesce(qual, '') || ' ' || coalesce(with_check, '') as body
|
|
691
710
|
from pg_policies
|
|
692
711
|
where (schemaname, tablename) in (('storage', 'objects'), ('realtime', 'messages'))`;
|
|
712
|
+
function textOf(value) {
|
|
713
|
+
return typeof value === "string" ? value : "";
|
|
714
|
+
}
|
|
693
715
|
/** The same rows as {@link helperTablePolicies}, from `pg_policies`. */
|
|
694
716
|
function helperTablePoliciesFromRows(rows) {
|
|
695
717
|
const found = [];
|
|
696
718
|
for (const row of rows) {
|
|
697
|
-
const table = helperTable(
|
|
698
|
-
const keys = helperCallKeys(
|
|
719
|
+
const table = helperTable(textOf(row["target"]));
|
|
720
|
+
const keys = helperCallKeys(textOf(row["body"]), true);
|
|
699
721
|
if (table !== void 0 && keys.length > 0) found.push({
|
|
700
722
|
table,
|
|
701
|
-
name:
|
|
723
|
+
name: textOf(row["policyname"]),
|
|
702
724
|
keys
|
|
703
725
|
});
|
|
704
726
|
}
|
|
@@ -710,53 +732,6 @@ function rowConditionMessage(policy, keys) {
|
|
|
710
732
|
}
|
|
711
733
|
const ROW_CONDITION_FIX = "call permitted_<scope>_ids_by_permission('<key>') or permdock_has_permission('<key>'), which count only the allows without a row condition (a relationship grant never reaches a topic or a folder), or use a permission whose catalog entry has rowConditions: false";
|
|
712
734
|
//#endregion
|
|
713
|
-
//#region src/cli/sql/tables.ts
|
|
714
|
-
/** The `schema.table` an `rls.tables` entry names, or `resource` when it names none or is `false`. */
|
|
715
|
-
function tableNameOf(entry, resource) {
|
|
716
|
-
if (entry === void 0 || entry === false) return resource;
|
|
717
|
-
return typeof entry === "string" ? entry : entry.table ?? resource;
|
|
718
|
-
}
|
|
719
|
-
/** `rls.tables` as resource to table name, the `via` objects reduced to their `table` and the `false` entries left out. */
|
|
720
|
-
function tableNames(tables) {
|
|
721
|
-
return tables === void 0 ? void 0 : Object.fromEntries(Object.entries(tables).filter(([, entry]) => entry !== false).map(([resource, entry]) => [resource, tableNameOf(entry, resource)]));
|
|
722
|
-
}
|
|
723
|
-
/** The resources `rls.tables` marks `false`: no table, so no policies. */
|
|
724
|
-
function tablelessResources(tables) {
|
|
725
|
-
return new Set(Object.entries(tables ?? {}).filter(([, entry]) => entry === false).map(([resource]) => resource));
|
|
726
|
-
}
|
|
727
|
-
function singular(name) {
|
|
728
|
-
if (name.endsWith("ies")) return `${name.slice(0, -3)}y`;
|
|
729
|
-
if (name.endsWith("sses")) return name.slice(0, -2);
|
|
730
|
-
return name.endsWith("s") && !name.endsWith("ss") ? name.slice(0, -1) : name;
|
|
731
|
-
}
|
|
732
|
-
/** The `via` entries of `rls.tables`, by resource. */
|
|
733
|
-
function resolveVias(tables, scopes, dialect) {
|
|
734
|
-
const out = {};
|
|
735
|
-
for (const [resource, entry] of Object.entries(tables ?? {})) {
|
|
736
|
-
if (typeof entry === "string" || entry === false) continue;
|
|
737
|
-
const { via } = entry;
|
|
738
|
-
const scope = via.scope ?? scopes[0]?.name;
|
|
739
|
-
if (scope === void 0 || !scopes.some((item) => item.name === scope)) throw configError(`PermDock CLI: rls.tables.${resource}.via.scope '${scope ?? ""}' is not a scope of the policy (${scopes.map((item) => item.name).join(", ")})`);
|
|
740
|
-
const name = singular(via.table.slice(via.table.lastIndexOf(".") + 1));
|
|
741
|
-
out[resource] = {
|
|
742
|
-
helper: permittedIdsHelper(name),
|
|
743
|
-
key: via.key ?? `${name}_id`,
|
|
744
|
-
scope,
|
|
745
|
-
table: via.table.includes(".") ? via.table : `public.${via.table}`,
|
|
746
|
-
column: via.column,
|
|
747
|
-
id: via.id ?? "id",
|
|
748
|
-
type: via.type ?? (dialect === "supabase" ? "uuid" : "text")
|
|
749
|
-
};
|
|
750
|
-
}
|
|
751
|
-
const helpers = /* @__PURE__ */ new Map();
|
|
752
|
-
for (const [resource, via] of Object.entries(out)) {
|
|
753
|
-
const seen = helpers.get(via.helper);
|
|
754
|
-
if (seen !== void 0 && (seen.table !== via.table || seen.column !== via.column || seen.id !== via.id || seen.scope !== via.scope || seen.type !== via.type)) throw configError(`PermDock CLI: rls.tables.${resource}.via names ${via.helper}, which another resource defines over a different parent; give the parents different table names`);
|
|
755
|
-
helpers.set(via.helper, via);
|
|
756
|
-
}
|
|
757
|
-
return Object.keys(out).length === 0 ? void 0 : out;
|
|
758
|
-
}
|
|
759
|
-
//#endregion
|
|
760
735
|
//#region src/cli/rls/verify-findings.ts
|
|
761
736
|
const VERIFY_DOCS = `${DOCS_BASE}/rls#verify`;
|
|
762
737
|
function rlsFinding(code, table, key, message, severity = "error") {
|
|
@@ -790,7 +765,10 @@ function verifyIgnores(config, flags = []) {
|
|
|
790
765
|
});
|
|
791
766
|
return out;
|
|
792
767
|
}
|
|
793
|
-
/**
|
|
768
|
+
/**
|
|
769
|
+
* Splits `findings` by `ignores`; an id listed twice counts once, with its
|
|
770
|
+
* first reason.
|
|
771
|
+
*/
|
|
794
772
|
function applyIgnores(findings, ignores) {
|
|
795
773
|
const reasons = /* @__PURE__ */ new Map();
|
|
796
774
|
for (const entry of ignores) if (!reasons.has(entry.id)) reasons.set(entry.id, entry.reason);
|
|
@@ -832,8 +810,8 @@ function prefixOf(severity) {
|
|
|
832
810
|
}
|
|
833
811
|
/**
|
|
834
812
|
* The `rls verify` result: exit 1 when an error finding is left after the
|
|
835
|
-
* ignores. Text lines carry each finding's id; `--json` data is
|
|
836
|
-
*
|
|
813
|
+
* ignores. Text lines carry each finding's id; `--json` data is `{ findings,
|
|
814
|
+
* ignored, stale }`; `--format github|sarif` renders `findings`.
|
|
837
815
|
*/
|
|
838
816
|
function verifyResult(input) {
|
|
839
817
|
const report = applyIgnores(input.findings, input.ignore);
|
|
@@ -870,7 +848,10 @@ function verifyResult(input) {
|
|
|
870
848
|
function insert(table, columns) {
|
|
871
849
|
return `insert into ${quoteTable(table)} (${columns.map(([name]) => quoteIdent(name)).join(", ")}) values (${columns.map(([, value]) => quoteLiteral(value)).join(", ")}) on conflict do nothing;`;
|
|
872
850
|
}
|
|
873
|
-
/**
|
|
851
|
+
/**
|
|
852
|
+
* The rows of a table mapped by `rls.memberships`: one per role, for a plain
|
|
853
|
+
* role column.
|
|
854
|
+
*/
|
|
874
855
|
function mappedRows(config, scopes, user, membership) {
|
|
875
856
|
const { scope, id } = membership;
|
|
876
857
|
if (scope === void 0 || id === void 0) return [];
|
|
@@ -894,7 +875,10 @@ function mappedRows(config, scopes, user, membership) {
|
|
|
894
875
|
[roleColumn, role]
|
|
895
876
|
]));
|
|
896
877
|
}
|
|
897
|
-
/**
|
|
878
|
+
/**
|
|
879
|
+
* The rows of a `membershipSources` source: only a source whose columns are
|
|
880
|
+
* plain, never a `through` or `map`.
|
|
881
|
+
*/
|
|
898
882
|
function sourceRows(source, scopes, user, membership) {
|
|
899
883
|
const { scope, id } = membership;
|
|
900
884
|
if (scope === void 0 || id === void 0) return [];
|
|
@@ -919,9 +903,9 @@ function sourceRows(source, scopes, user, membership) {
|
|
|
919
903
|
}
|
|
920
904
|
/**
|
|
921
905
|
* The statements that put a fixture's memberships into the configured
|
|
922
|
-
* membership tables and sources, so the helpers of `authorize: 'database'`
|
|
923
|
-
*
|
|
924
|
-
*
|
|
906
|
+
* membership tables and sources, so the helpers of `authorize: 'database'` find
|
|
907
|
+
* them. A membership no plain table or source can hold (a `through` or mapped
|
|
908
|
+
* role column, an `on` membership) is listed in a comment.
|
|
925
909
|
*/
|
|
926
910
|
function fixtureMembershipSql(config, scopes, fixture, sources) {
|
|
927
911
|
const lines = [];
|
|
@@ -934,7 +918,10 @@ function fixtureMembershipSql(config, scopes, fixture, sources) {
|
|
|
934
918
|
}
|
|
935
919
|
//#endregion
|
|
936
920
|
//#region src/cli/rls/verify-pgtap.ts
|
|
937
|
-
/**
|
|
921
|
+
/**
|
|
922
|
+
* A custom role's scope and pinned instance, as the `custom_role_*` tables
|
|
923
|
+
* store them.
|
|
924
|
+
*/
|
|
938
925
|
function customRoleAt(role, scopes) {
|
|
939
926
|
return {
|
|
940
927
|
scope: customRoleScope(role, scopes),
|
|
@@ -987,7 +974,10 @@ function statementFor(fixture, action, table) {
|
|
|
987
974
|
function rowId(row) {
|
|
988
975
|
if (isRecord(row) && "id" in row) return row["id"];
|
|
989
976
|
}
|
|
990
|
-
/**
|
|
977
|
+
/**
|
|
978
|
+
* A fixture value as a SQL literal; untyped, so it coerces to the column like a
|
|
979
|
+
* bound parameter.
|
|
980
|
+
*/
|
|
991
981
|
function sqlValue(value) {
|
|
992
982
|
if (value === null || value === void 0) return "null";
|
|
993
983
|
return quoteLiteral(typeof value === "string" ? value : JSON.stringify(value));
|
|
@@ -1020,7 +1010,10 @@ function subjectOf(config, customRoles, scopes) {
|
|
|
1020
1010
|
scopes
|
|
1021
1011
|
};
|
|
1022
1012
|
}
|
|
1023
|
-
/**
|
|
1013
|
+
/**
|
|
1014
|
+
* The settings that bind a fixture's subject: the GUCs, or the Supabase JWT
|
|
1015
|
+
* claims.
|
|
1016
|
+
*/
|
|
1024
1017
|
function subjectSettings(fixture, subject) {
|
|
1025
1018
|
const roles = fixture.subject.roles ?? [];
|
|
1026
1019
|
const memberships = membershipsClaim(fixture.subject.memberships ?? [], subject.customRoles, subject.scopes);
|
|
@@ -1052,7 +1045,10 @@ function subjectSettings(fixture, subject) {
|
|
|
1052
1045
|
};
|
|
1053
1046
|
return [["request.jwt.claims", JSON.stringify(claims)]];
|
|
1054
1047
|
}
|
|
1055
|
-
/**
|
|
1048
|
+
/**
|
|
1049
|
+
* The `custom_role_*` rows for the fixtures' custom roles; existing rows are
|
|
1050
|
+
* kept.
|
|
1051
|
+
*/
|
|
1056
1052
|
function customRoleRows(schema, customRoles, scopes) {
|
|
1057
1053
|
const table = (name) => `${quoteIdent(schema)}.${quoteIdent(name)}`;
|
|
1058
1054
|
const rows = [];
|
|
@@ -1095,8 +1091,8 @@ function customRoleSchema(config) {
|
|
|
1095
1091
|
return config.rls?.schema ?? config.rls?.rbac?.schema ?? "permdock";
|
|
1096
1092
|
}
|
|
1097
1093
|
/**
|
|
1098
|
-
* A pgTAP script that runs each fixture's statement under the fixture's
|
|
1099
|
-
*
|
|
1094
|
+
* A pgTAP script that runs each fixture's statement under the fixture's subject
|
|
1095
|
+
* and asserts the outcome `can()` gave in-process.
|
|
1100
1096
|
*/
|
|
1101
1097
|
function emitPgtap(input) {
|
|
1102
1098
|
const scopes = scopeList(input.policy.scopes);
|
|
@@ -1130,350 +1126,6 @@ function emitPgtap(input) {
|
|
|
1130
1126
|
return `${lines.join("\n")}\n`;
|
|
1131
1127
|
}
|
|
1132
1128
|
//#endregion
|
|
1133
|
-
//#region src/cli/rls/graph.ts
|
|
1134
|
-
function tableFor(resource, tables) {
|
|
1135
|
-
return tables?.[resource] ?? resource;
|
|
1136
|
-
}
|
|
1137
|
-
/** Every `related` condition the policy's grants compile to, with the resource graph they need, groups included. */
|
|
1138
|
-
function graphPlan(policy) {
|
|
1139
|
-
const scopes = scopeList(policy.scopes);
|
|
1140
|
-
const plan = /* @__PURE__ */ new Map();
|
|
1141
|
-
const entryFor = (node) => {
|
|
1142
|
-
const entry = plan.get(node.name) ?? {
|
|
1143
|
-
node,
|
|
1144
|
-
relations: /* @__PURE__ */ new Set(),
|
|
1145
|
-
links: /* @__PURE__ */ new Set()
|
|
1146
|
-
};
|
|
1147
|
-
plan.set(node.name, entry);
|
|
1148
|
-
return entry;
|
|
1149
|
-
};
|
|
1150
|
-
const addRelation = (node, relation) => {
|
|
1151
|
-
const entry = entryFor(node);
|
|
1152
|
-
if (entry.relations.has(relation)) return;
|
|
1153
|
-
entry.relations.add(relation);
|
|
1154
|
-
for (const name of expandRelation(node, relation)) {
|
|
1155
|
-
const spec = node.relations[name];
|
|
1156
|
-
if (!isEdgeRelation(spec) || spec.groups === void 0) continue;
|
|
1157
|
-
for (const { resource, relation: groupRelation } of groupEntries(spec)) {
|
|
1158
|
-
const target = policy.resources.get(resource);
|
|
1159
|
-
if (target !== void 0 && target.name !== node.name) addRelation(target, groupRelation);
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
};
|
|
1163
|
-
const addLinks = (hops) => {
|
|
1164
|
-
for (let index = 1; index < hops.length; index += 1) {
|
|
1165
|
-
const from = policy.resources.get(hops[index - 1]?.resource ?? "");
|
|
1166
|
-
const hop = hops[index];
|
|
1167
|
-
if (from !== void 0 && hop !== void 0) entryFor(from).links.add(hop.link);
|
|
1168
|
-
}
|
|
1169
|
-
};
|
|
1170
|
-
const addAbove = (above) => {
|
|
1171
|
-
const node = above === void 0 ? void 0 : policy.resources.get(above.resource);
|
|
1172
|
-
if (above === void 0 || node === void 0) return;
|
|
1173
|
-
const entry = entryFor(node);
|
|
1174
|
-
entry.closure = Math.max(entry.closure ?? 0, above.depth);
|
|
1175
|
-
entry.restrictedAncestors = above;
|
|
1176
|
-
};
|
|
1177
|
-
for (const grant of policy.grants) {
|
|
1178
|
-
const row = policy.resources.get(grant.permission.resource);
|
|
1179
|
-
for (const item of flattenGrantee(grant.to)) {
|
|
1180
|
-
if (item.kind === "inherit") {
|
|
1181
|
-
const inherited = inheritCondition(item, row, policy.resources);
|
|
1182
|
-
addLinks(inherited?.hops ?? []);
|
|
1183
|
-
addAbove(inherited?.restrictedAncestors);
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1186
|
-
if (item.kind !== "relation") continue;
|
|
1187
|
-
const condition = relationCondition(item, row, scopes, { resources: policy.resources });
|
|
1188
|
-
if (condition?.op !== "related") continue;
|
|
1189
|
-
const node = policy.resources.get(condition.resource);
|
|
1190
|
-
if (node === void 0) continue;
|
|
1191
|
-
addRelation(node, condition.relation);
|
|
1192
|
-
const entry = entryFor(node);
|
|
1193
|
-
if (condition.depth > 0 && isSelfParented(node)) entry.closure = Math.max(entry.closure ?? 0, condition.depth);
|
|
1194
|
-
addLinks(condition.hops ?? []);
|
|
1195
|
-
addAbove(condition.restrictedAncestors);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
return plan;
|
|
1199
|
-
}
|
|
1200
|
-
/** The closure depth per walked resource, as `RlsSqlContext.graph` carries it. */
|
|
1201
|
-
function closureDepths(plan) {
|
|
1202
|
-
const out = {};
|
|
1203
|
-
for (const [name, entry] of plan) if (entry.closure !== void 0) out[name] = entry.closure;
|
|
1204
|
-
return out;
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* The `permitted_<resource>_ids(relation)` arm for one concrete relation:
|
|
1208
|
-
* ids of `resource` the subject holds it on, directly or through a group.
|
|
1209
|
-
* `asked` are the relation names that include it, so a call for any of them
|
|
1210
|
-
* (or for all, with null) reads this arm.
|
|
1211
|
-
*/
|
|
1212
|
-
function relationArm(ctx, plan, node, relation, asked, tables) {
|
|
1213
|
-
const spec = node.relations[relation];
|
|
1214
|
-
if (!isEdgeRelation(spec) && !isPrincipalRelation(spec) && (!isFieldRelation(spec) || spec.memberOf !== void 0)) throw configError(`PermDock CLI: relation '${relation}' on ${node.name} is not a graph relation RLS can compile`);
|
|
1215
|
-
const guard = asked.length === 1 ? `(p_relation is null or p_relation = ${quoteLiteral(asked[0] ?? relation)})` : `(p_relation is null or p_relation in (${asked.map(quoteLiteral).join(", ")}))`;
|
|
1216
|
-
return ` select a.id from (${graphSqlText(relationArmSql(node.name, relation, {
|
|
1217
|
-
resources: new Map([...plan].map(([name, item]) => [name, item.node])),
|
|
1218
|
-
...tables === void 0 ? {} : { tables },
|
|
1219
|
-
qualify: qualifySqlTable,
|
|
1220
|
-
holders: (resource, groupRelation) => [{ text: `select ${qualified(ctx, graphHelper(resource))}(${quoteLiteral(groupRelation)})` }]
|
|
1221
|
-
}), ctx)}) a\n where ${guard}${activeUserSql(ctx).map((part) => ` and ${part}`).join("")}`;
|
|
1222
|
-
}
|
|
1223
|
-
function permittedSql(ctx, plan, entry, tables) {
|
|
1224
|
-
const fn = qualified(ctx, graphHelper(entry.node.name));
|
|
1225
|
-
const asked = [...entry.relations].toSorted();
|
|
1226
|
-
const arms = [...new Set(asked.flatMap((name) => expandRelation(entry.node, name)))].toSorted().map((relation) => relationArm(ctx, plan, entry.node, relation, asked.filter((name) => expandRelation(entry.node, name).includes(relation)), tables));
|
|
1227
|
-
if (arms.length === 0) arms.push(" select null::text where false");
|
|
1228
|
-
return `-- ${entry.node.name}: ids the subject holds a relation on (all relations when p_relation is null)
|
|
1229
|
-
create or replace function ${fn}(p_relation text)
|
|
1230
|
-
returns setof text
|
|
1231
|
-
language sql
|
|
1232
|
-
stable
|
|
1233
|
-
security definer
|
|
1234
|
-
set search_path = ''
|
|
1235
|
-
as $$
|
|
1236
|
-
${arms.join("\n union\n")}
|
|
1237
|
-
$$;
|
|
1238
|
-
revoke execute on function ${fn}(text) from public, anon;
|
|
1239
|
-
grant execute on function ${fn}(text) to authenticated;`;
|
|
1240
|
-
}
|
|
1241
|
-
/** `permdock_link_<resource>_<link>(ids)`: ids of `resource` whose link points into `ids`, read past the table's own policies. */
|
|
1242
|
-
function linkSql(ctx, node, link, tables) {
|
|
1243
|
-
const spec = Object.hasOwn(node.links, link) ? node.links[link] : void 0;
|
|
1244
|
-
if (spec === void 0) throw configError(`PermDock CLI: ${node.name} has no link '${link}'`);
|
|
1245
|
-
const fn = qualified(ctx, linkHelper(node.name, link));
|
|
1246
|
-
return `-- ${node.name}.${link}: rows whose ${spec.field} points at one of the given ${spec.resource} ids
|
|
1247
|
-
create or replace function ${fn}(p_ids text[])
|
|
1248
|
-
returns setof text
|
|
1249
|
-
language sql
|
|
1250
|
-
stable
|
|
1251
|
-
security definer
|
|
1252
|
-
set search_path = ''
|
|
1253
|
-
as $$
|
|
1254
|
-
select t.${quoteIdent(node.id)}::text from ${quoteQualified(tableFor(node.name, tables))} t
|
|
1255
|
-
where t.${quoteIdent(spec.field)}::text = any(p_ids)
|
|
1256
|
-
$$;
|
|
1257
|
-
revoke execute on function ${fn}(text[]) from public, anon;
|
|
1258
|
-
grant execute on function ${fn}(text[]) to authenticated;`;
|
|
1259
|
-
}
|
|
1260
|
-
function closureTableSql(ctx) {
|
|
1261
|
-
const table = qualified(ctx, CLOSURE.table);
|
|
1262
|
-
return `-- the object graph: one row per (ancestor, descendant) within each walked resource's depth,
|
|
1263
|
-
-- never crossing a restricted row; kept current by statement triggers on each self-parented table
|
|
1264
|
-
create table if not exists ${table} (
|
|
1265
|
-
resource text not null,
|
|
1266
|
-
ancestor text not null,
|
|
1267
|
-
descendant text not null,
|
|
1268
|
-
depth integer not null check (depth >= 0),
|
|
1269
|
-
primary key (resource, descendant, ancestor)
|
|
1270
|
-
);
|
|
1271
|
-
create index if not exists ${quoteIdent(`${CLOSURE.table}_ancestor`)} on ${table} (resource, ancestor);
|
|
1272
|
-
alter table ${table} enable row level security;
|
|
1273
|
-
revoke all on table ${table} from anon, authenticated, public;
|
|
1274
|
-
grant select on table ${table} to authenticated;`;
|
|
1275
|
-
}
|
|
1276
|
-
function closureObjectsSql(ctx, entry, depth, tables) {
|
|
1277
|
-
const { node } = entry;
|
|
1278
|
-
const parent = node.parent;
|
|
1279
|
-
if (parent === void 0) return "";
|
|
1280
|
-
const name = node.name;
|
|
1281
|
-
const sqlName = graphSqlName(name) ?? name;
|
|
1282
|
-
const closure = qualified(ctx, CLOSURE.table);
|
|
1283
|
-
const table = quoteQualified(tableFor(name, tables));
|
|
1284
|
-
const id = quoteIdent(node.id);
|
|
1285
|
-
const up = quoteIdent(parent.field);
|
|
1286
|
-
const restricted = restrictedFor(node, "parent");
|
|
1287
|
-
const stop = restricted === void 0 ? "false" : `coalesce(t.${quoteIdent(restricted)}, false)`;
|
|
1288
|
-
const stopParent = restricted === void 0 ? "false" : `coalesce(p.${quoteIdent(restricted)}, false)`;
|
|
1289
|
-
const refresh = qualified(ctx, `${CLOSURE.table}_${sqlName}`);
|
|
1290
|
-
const changed = qualified(ctx, `${CLOSURE.table}_${sqlName}_changed`);
|
|
1291
|
-
const literal = quoteLiteral(name);
|
|
1292
|
-
const restrictedChanged = restricted === void 0 ? "" : ` or n.${quoteIdent(restricted)} is distinct from o.${quoteIdent(restricted)}`;
|
|
1293
|
-
const trigger = (suffix) => quoteIdent(`${CLOSURE.table}_${sqlName}_${suffix}`);
|
|
1294
|
-
return `-- ${name}: ${restricted === void 0 ? `rows reach their ancestors up to depth ${String(depth)}` : `rows reach their ancestors up to depth ${String(depth)}, stopping at a restricted row`}
|
|
1295
|
-
drop policy if exists ${quoteIdent(`${CLOSURE.table}_${sqlName}`)} on ${closure};
|
|
1296
|
-
create policy ${quoteIdent(`${CLOSURE.table}_${sqlName}`)} on ${closure}
|
|
1297
|
-
for select to authenticated
|
|
1298
|
-
using (resource = ${literal} and ancestor in (select ${qualified(ctx, graphHelper(name))}(null)));
|
|
1299
|
-
create or replace function ${refresh}(p_ids text[])
|
|
1300
|
-
returns void
|
|
1301
|
-
language plpgsql
|
|
1302
|
-
security definer
|
|
1303
|
-
set search_path = ''
|
|
1304
|
-
as $$
|
|
1305
|
-
declare
|
|
1306
|
-
v_cycle text;
|
|
1307
|
-
v_affected text[];
|
|
1308
|
-
begin
|
|
1309
|
-
with recursive walk(start, next, path) as (
|
|
1310
|
-
select t.${id}, t.${up}, array[t.${id}::text]
|
|
1311
|
-
from ${table} t
|
|
1312
|
-
where t.${id}::text = any(p_ids)
|
|
1313
|
-
union all
|
|
1314
|
-
select walk.start, t.${up}, walk.path || t.${id}::text
|
|
1315
|
-
from walk join ${table} t on t.${id} = walk.next
|
|
1316
|
-
where not (walk.next::text = any(walk.path))
|
|
1317
|
-
)
|
|
1318
|
-
select walk.start::text into v_cycle
|
|
1319
|
-
from walk
|
|
1320
|
-
where walk.next::text = any(walk.path)
|
|
1321
|
-
limit 1;
|
|
1322
|
-
if v_cycle is not null then
|
|
1323
|
-
raise exception 'permdock: % % is its own ancestor', ${literal}, v_cycle
|
|
1324
|
-
using errcode = 'check_violation';
|
|
1325
|
-
end if;
|
|
1326
|
-
with recursive down(id, level) as (
|
|
1327
|
-
select t.${id}, 0
|
|
1328
|
-
from ${table} t
|
|
1329
|
-
where t.${id}::text = any(p_ids) or t.${up}::text = any(p_ids)
|
|
1330
|
-
union
|
|
1331
|
-
select t.${id}, down.level + 1
|
|
1332
|
-
from down join ${table} t on t.${up} = down.id
|
|
1333
|
-
where down.level < ${String(depth)}
|
|
1334
|
-
)
|
|
1335
|
-
select coalesce(array_agg(distinct down.id::text), '{}') into v_affected from down;
|
|
1336
|
-
delete from ${closure} c
|
|
1337
|
-
where c.resource = ${literal}
|
|
1338
|
-
and (c.descendant = any(p_ids) or c.descendant = any(v_affected));
|
|
1339
|
-
insert into ${closure} (resource, ancestor, descendant, depth)
|
|
1340
|
-
with recursive walk(descendant, ancestor, parent, depth, stop) as (
|
|
1341
|
-
select t.${id}::text, t.${id}, t.${up}, 0, ${stop}
|
|
1342
|
-
from ${table} t
|
|
1343
|
-
where t.${id}::text = any(v_affected)
|
|
1344
|
-
union all
|
|
1345
|
-
select walk.descendant, p.${id}, p.${up}, walk.depth + 1, ${stopParent}
|
|
1346
|
-
from walk join ${table} p on p.${id} = walk.parent
|
|
1347
|
-
where not walk.stop and walk.depth < ${String(depth)}
|
|
1348
|
-
)
|
|
1349
|
-
select ${literal}, walk.ancestor::text, walk.descendant, walk.depth from walk
|
|
1350
|
-
on conflict do nothing;
|
|
1351
|
-
end
|
|
1352
|
-
$$;
|
|
1353
|
-
revoke execute on function ${refresh}(text[]) from public, anon, authenticated;
|
|
1354
|
-
create or replace function ${changed}()
|
|
1355
|
-
returns trigger
|
|
1356
|
-
language plpgsql
|
|
1357
|
-
security definer
|
|
1358
|
-
set search_path = ''
|
|
1359
|
-
as $$
|
|
1360
|
-
begin
|
|
1361
|
-
if tg_op = 'TRUNCATE' then
|
|
1362
|
-
delete from ${closure} where resource = ${literal};
|
|
1363
|
-
elsif tg_op = 'INSERT' then
|
|
1364
|
-
perform ${refresh}(array(select n.${id}::text from new_rows n));
|
|
1365
|
-
elsif tg_op = 'DELETE' then
|
|
1366
|
-
perform ${refresh}(array(select o.${id}::text from old_rows o));
|
|
1367
|
-
else
|
|
1368
|
-
perform ${refresh}(array(
|
|
1369
|
-
select n.${id}::text
|
|
1370
|
-
from new_rows n left join old_rows o on o.${id} = n.${id}
|
|
1371
|
-
where o.${id} is null or n.${up} is distinct from o.${up}${restrictedChanged}
|
|
1372
|
-
union
|
|
1373
|
-
select o.${id}::text
|
|
1374
|
-
from old_rows o left join new_rows n on n.${id} = o.${id}
|
|
1375
|
-
where n.${id} is null
|
|
1376
|
-
));
|
|
1377
|
-
end if;
|
|
1378
|
-
return null;
|
|
1379
|
-
end
|
|
1380
|
-
$$;
|
|
1381
|
-
revoke execute on function ${changed}() from public, anon, authenticated;
|
|
1382
|
-
drop trigger if exists ${trigger("insert")} on ${table};
|
|
1383
|
-
create trigger ${trigger("insert")} after insert on ${table}
|
|
1384
|
-
referencing new table as new_rows
|
|
1385
|
-
for each statement execute function ${changed}();
|
|
1386
|
-
drop trigger if exists ${trigger("update")} on ${table};
|
|
1387
|
-
create trigger ${trigger("update")} after update on ${table}
|
|
1388
|
-
referencing old table as old_rows new table as new_rows
|
|
1389
|
-
for each statement execute function ${changed}();
|
|
1390
|
-
drop trigger if exists ${trigger("delete")} on ${table};
|
|
1391
|
-
create trigger ${trigger("delete")} after delete on ${table}
|
|
1392
|
-
referencing old table as old_rows
|
|
1393
|
-
for each statement execute function ${changed}();
|
|
1394
|
-
drop trigger if exists ${trigger("truncate")} on ${table};
|
|
1395
|
-
create trigger ${trigger("truncate")} after truncate on ${table}
|
|
1396
|
-
for each statement execute function ${changed}();
|
|
1397
|
-
select ${refresh}(array(select t.${id}::text from ${table} t));`;
|
|
1398
|
-
}
|
|
1399
|
-
function restrictedRowsHelperSql(ctx, plan, entry, tables) {
|
|
1400
|
-
const above = entry.restrictedAncestors;
|
|
1401
|
-
if (above === void 0) return "";
|
|
1402
|
-
const fn = qualified(ctx, restrictedHelper(entry.node.name));
|
|
1403
|
-
const body = graphSqlText(restrictedRowsSql(above, {
|
|
1404
|
-
resources: new Map([...plan].map(([name, item]) => [name, item.node])),
|
|
1405
|
-
...tables === void 0 ? {} : { tables },
|
|
1406
|
-
qualify: qualifySqlTable,
|
|
1407
|
-
closure: `${helperSchema(ctx)}.${CLOSURE.table}`,
|
|
1408
|
-
closureDepths: closureDepths(plan)
|
|
1409
|
-
}), ctx);
|
|
1410
|
-
return `-- ${entry.node.name}: rows a stopped link does not reach below: restricted, under a restricted row within ${String(above.depth - 1)} levels, or deeper
|
|
1411
|
-
create or replace function ${fn}()
|
|
1412
|
-
returns setof text
|
|
1413
|
-
language sql
|
|
1414
|
-
stable
|
|
1415
|
-
security definer
|
|
1416
|
-
set search_path = ''
|
|
1417
|
-
as $$
|
|
1418
|
-
${body}
|
|
1419
|
-
$$;
|
|
1420
|
-
revoke execute on function ${fn}() from public, anon;
|
|
1421
|
-
grant execute on function ${fn}() to authenticated;`;
|
|
1422
|
-
}
|
|
1423
|
-
/** Group resources a resource's asked relations name, other than itself. */
|
|
1424
|
-
function groupTargets(entry) {
|
|
1425
|
-
const out = /* @__PURE__ */ new Set();
|
|
1426
|
-
for (const asked of entry.relations) for (const name of expandRelation(entry.node, asked)) {
|
|
1427
|
-
const spec = entry.node.relations[name];
|
|
1428
|
-
const resources = isEdgeRelation(spec) ? groupEntries(spec).map((group) => group.resource) : [];
|
|
1429
|
-
for (const resource of resources) if (resource !== entry.node.name) out.add(resource);
|
|
1430
|
-
}
|
|
1431
|
-
return [...out].toSorted();
|
|
1432
|
-
}
|
|
1433
|
-
/** Plan entries by name, each after the group resources its helper calls (SQL function bodies are checked on create). */
|
|
1434
|
-
function dependencyOrder(plan) {
|
|
1435
|
-
const out = [];
|
|
1436
|
-
const done = /* @__PURE__ */ new Set();
|
|
1437
|
-
const visit = (name) => {
|
|
1438
|
-
const entry = plan.get(name);
|
|
1439
|
-
if (entry === void 0 || done.has(name)) return;
|
|
1440
|
-
done.add(name);
|
|
1441
|
-
for (const target of groupTargets(entry)) visit(target);
|
|
1442
|
-
out.push(entry);
|
|
1443
|
-
};
|
|
1444
|
-
for (const name of [...plan.keys()].toSorted()) visit(name);
|
|
1445
|
-
return out;
|
|
1446
|
-
}
|
|
1447
|
-
/**
|
|
1448
|
-
* The graph preamble: `permitted_<resource>_ids` for every resource a graph
|
|
1449
|
-
* grant reads, and the closure table with its triggers for every
|
|
1450
|
-
* self-parented resource a grant walks. Empty when no grant is a graph grant.
|
|
1451
|
-
*/
|
|
1452
|
-
function graphSql(ctx, plan, tables) {
|
|
1453
|
-
if (plan.size === 0) return "";
|
|
1454
|
-
const sqlNames = /* @__PURE__ */ new Map();
|
|
1455
|
-
for (const name of plan.keys()) {
|
|
1456
|
-
const helper = graphHelper(name);
|
|
1457
|
-
const sql = graphSqlName(name) ?? name;
|
|
1458
|
-
if (ctx.scopes.some((scope) => scope.name === sql)) throw configError(`PermDock CLI: resource '${name}' has graph relations and shares its SQL name with the ${sql} scope, so ${helper} would replace the scope helper; rename the resource (permdock doctor PD032)`);
|
|
1459
|
-
const other = sqlNames.get(sql);
|
|
1460
|
-
if (other !== void 0) throw configError(`PermDock CLI: resources '${other}' and '${name}' both name the SQL helper ${helper}; rename one (permdock doctor PD032)`);
|
|
1461
|
-
sqlNames.set(sql, name);
|
|
1462
|
-
}
|
|
1463
|
-
const entries = dependencyOrder(plan);
|
|
1464
|
-
const chunks = entries.flatMap((entry) => [...entry.relations.size === 0 && entry.closure === void 0 ? [] : [permittedSql(ctx, plan, entry, tables)], ...[...entry.links].toSorted().map((link) => linkSql(ctx, entry.node, link, tables))]);
|
|
1465
|
-
const walked = entries.filter((entry) => entry.closure !== void 0);
|
|
1466
|
-
if (walked.length > 0) {
|
|
1467
|
-
chunks.push(closureTableSql(ctx));
|
|
1468
|
-
for (const entry of walked) chunks.push(closureObjectsSql(ctx, entry, entry.closure ?? 0, tables));
|
|
1469
|
-
for (const entry of walked) {
|
|
1470
|
-
const helper = restrictedRowsHelperSql(ctx, plan, entry, tables);
|
|
1471
|
-
if (helper !== "") chunks.push(helper);
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
return `${chunks.join("\n\n")}\n`;
|
|
1475
|
-
}
|
|
1476
|
-
//#endregion
|
|
1477
1129
|
//#region src/cli/rls/verify-tree.ts
|
|
1478
1130
|
const SYSTEM_ENTROPY = Object.freeze({
|
|
1479
1131
|
now: () => nowMillis(),
|
|
@@ -1489,9 +1141,9 @@ function idFactory(node, entropy) {
|
|
|
1489
1141
|
} : () => entropy.randomUUID();
|
|
1490
1142
|
}
|
|
1491
1143
|
/**
|
|
1492
|
-
* Two spines one level deeper than the walk depth, so the depth cap is
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1144
|
+
* Two spines one level deeper than the walk depth, so the depth cap is crossed;
|
|
1145
|
+
* side branches under the first levels alternate restricted, and the second
|
|
1146
|
+
* spine is restricted two levels down.
|
|
1495
1147
|
*/
|
|
1496
1148
|
function generateTree(node, depth, entropy) {
|
|
1497
1149
|
const id = idFactory(node, entropy);
|
|
@@ -1612,9 +1264,9 @@ function asRequiredColumn(row) {
|
|
|
1612
1264
|
};
|
|
1613
1265
|
}
|
|
1614
1266
|
/**
|
|
1615
|
-
* Values for the required columns a generated row leaves out, such as a
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1267
|
+
* Values for the required columns a generated row leaves out, such as a tenant
|
|
1268
|
+
* column or a name: an existing value of the referenced table for a foreign
|
|
1269
|
+
* key, a placeholder of the column's type otherwise.
|
|
1618
1270
|
*/
|
|
1619
1271
|
async function requiredValues(query, table, set, entropy) {
|
|
1620
1272
|
const columns = (await query(REQUIRED_COLUMNS_SQL, [table])).rows.map(asRequiredColumn).filter((column) => column !== void 0 && !set.has(column.name));
|
|
@@ -1644,10 +1296,10 @@ async function seedRows(query, table, rows, given, entropy) {
|
|
|
1644
1296
|
}
|
|
1645
1297
|
/**
|
|
1646
1298
|
* Parity between `decide` and RLS on a generated object graph: for every
|
|
1647
|
-
* self-parented resource a graph grant walks, a tree with restricted
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
*
|
|
1299
|
+
* self-parented resource a graph grant walks, a tree with restricted branches
|
|
1300
|
+
* and a chain past the walk depth, child rows under it, and edges for a few
|
|
1301
|
+
* generated principals. Everything runs in one transaction that is rolled
|
|
1302
|
+
* back.
|
|
1651
1303
|
*/
|
|
1652
1304
|
async function verifyTree(input) {
|
|
1653
1305
|
const { policy, query } = input;
|
|
@@ -1797,7 +1449,10 @@ function fieldsMismatch(fixture, want, seen) {
|
|
|
1797
1449
|
if (typeof seen === "string") return [mismatch(`${fixture.action}: field view read failed (${seen}), pick keeps [${kept}]`)];
|
|
1798
1450
|
return seen.join(", ") === kept ? [] : [mismatch(`${fixture.action} ${String(rowId(fixture.row))}: field view returns [${seen.join(", ")}], pick keeps [${kept}]`)];
|
|
1799
1451
|
}
|
|
1800
|
-
/**
|
|
1452
|
+
/**
|
|
1453
|
+
* The field view's columns for the fixture's row: the view, or the table itself
|
|
1454
|
+
* when no grant limits its fields.
|
|
1455
|
+
*/
|
|
1801
1456
|
async function viewFields(query, table, key) {
|
|
1802
1457
|
const view = quoteTable(viewName(table, FIELD_VIEWS.view));
|
|
1803
1458
|
const id = quoteIdent("id");
|
|
@@ -1809,7 +1464,10 @@ async function viewFields(query, table, key) {
|
|
|
1809
1464
|
const base = await query(`select * from ${quoteTable(table)} where ${id} = $1`, [key]);
|
|
1810
1465
|
return base.code ?? valuedColumns(base.rows[0]);
|
|
1811
1466
|
}
|
|
1812
|
-
/**
|
|
1467
|
+
/**
|
|
1468
|
+
* Writes the custom roles into the `database`-mode tables; the fixture
|
|
1469
|
+
* transaction rolls them back.
|
|
1470
|
+
*/
|
|
1813
1471
|
async function seedCustomRoles(query, schema, customRoles, scopes) {
|
|
1814
1472
|
for (const row of customRoleRows(schema, customRoles, scopes)) {
|
|
1815
1473
|
const result = await query(row.sql, row.values);
|
|
@@ -1886,7 +1544,10 @@ async function verifyAgainstDatabase(input) {
|
|
|
1886
1544
|
notes
|
|
1887
1545
|
};
|
|
1888
1546
|
}
|
|
1889
|
-
/**
|
|
1547
|
+
/**
|
|
1548
|
+
* `--tree`: seeds a generated object graph in a rolled-back transaction and
|
|
1549
|
+
* compares `can()` with RLS.
|
|
1550
|
+
*/
|
|
1890
1551
|
async function verifyTreeAgainstDatabase(db, policy, config, connect, ignore) {
|
|
1891
1552
|
const client = await connect(db);
|
|
1892
1553
|
const scopes = scopeList(policy.scopes);
|
|
@@ -2034,4 +1695,4 @@ async function runRlsVerify(input) {
|
|
|
2034
1695
|
});
|
|
2035
1696
|
}
|
|
2036
1697
|
//#endregion
|
|
2037
|
-
export {
|
|
1698
|
+
export { indexTargets as _, verifyResult as a, readTableIndexFacts as b, callsHelper as c, rowConditionMessage as d, commandFor as f, INDEX_COLUMNS_SQL as g, COLUMNS_SQL as h, verifyIgnores as i, helperCallKeys as l, tableFor as m, runRlsVerify as n, HELPER_TABLES as o, compileGrants as p, rlsFinding as r, ROW_CONDITION_FIX as s, canFixture as t, helperTablePolicies as u, indexesSql as v, pruneIndexTargets as y };
|