permdock 0.1.0 → 0.3.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 +106 -47
- package/dist/a2a/index.d.ts +7 -16
- package/dist/a2a/index.js +59 -98
- package/dist/adapter-context-mlWsVGGg.js +23 -0
- package/dist/ai-sdk/index.d.ts +37 -16
- package/dist/ai-sdk/index.js +30 -13
- package/dist/approvals/index.d.ts +3 -3
- package/dist/approvals/index.js +5 -4
- package/dist/{approvals-DaxY-Bdg.js → approvals-DbYAuNkG.js} +17 -11
- package/dist/approvers-DT4AVyIg.js +52 -0
- package/dist/{arazzo-CTeVuXr5.js → arazzo-BAgw9ap6.js} +2 -2
- package/dist/{arazzo-aoTkqYsJ.js → arazzo-CKwwqFWz.js} +2 -2
- package/dist/authzen/index.d.ts +4 -13
- package/dist/authzen/index.js +16 -50
- package/dist/better-auth/index.d.ts +20 -3
- package/dist/better-auth/index.js +27 -7
- package/dist/better-supabase/index.d.ts +223 -0
- package/dist/better-supabase/index.js +648 -0
- package/dist/bind-C_ob3LKa.d.ts +25 -0
- package/dist/bind-CuzGytRf.js +97 -0
- package/dist/boundary-BM8a_P6E.js +13 -0
- package/dist/boundary-BmHxVH1J.d.ts +15 -0
- package/dist/canonical-json-DemiK4Vp.js +26 -0
- package/dist/{capability-BDG_nkxt.d.ts → capability-2kYTMWDv.d.ts} +2 -2
- package/dist/{capability-B68OFnZx.js → capability-4qPdO9gk.js} +7 -5
- package/dist/catalog/index.d.ts +1 -1
- package/dist/catalog/index.js +1 -1
- package/dist/catalog-CRW5Mx_B.js +2 -0
- package/dist/{catalog-H-vFGwWK.js → catalog-DWF5cYqB.js} +4 -4
- package/dist/{catalog-B0E6bggc.js → catalog-dFZdWCGN.js} +2 -2
- package/dist/{catalog-doc-BSo_Q_-6.js → catalog-doc-DdGm82XT.js} +20 -32
- package/dist/claude-agent/index.d.ts +5 -14
- package/dist/claude-agent/index.js +4 -10
- package/dist/clerk/index.d.ts +12 -3
- package/dist/clerk/index.js +26 -5
- package/dist/cli/bin.js +1 -1
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +2 -2
- package/dist/clients-C7zLFuWT.d.ts +12 -0
- package/dist/clients-DjU5ABMy.js +23 -0
- package/dist/cloud/index.d.ts +4 -5
- package/dist/cloud/index.js +20 -6
- package/dist/{cloud-BNQTrmrr.js → cloud-CB_ELYuX.js} +2 -2
- package/dist/{cloud-Cuxr6Czw.js → cloud-DWLXO2af.js} +7 -6
- package/dist/collect-DClJsheJ.js +83 -0
- package/dist/{collect-O-tOtbnk.js → collect-DSTE3ml9.js} +194 -42
- package/dist/collect-DSWOk19R.js +2 -0
- package/dist/{commands-BMh_Etm2.js → commands-DSxCqG9A.js} +16 -12
- package/dist/compile/index.d.ts +3 -0
- package/dist/compile/index.js +2 -0
- package/dist/{compile-D22iwvuV.js → compile-Ba4blROR.js} +85 -30
- package/dist/compile-BkRyBDBw.d.ts +94 -0
- package/dist/config-BJiM-xAm.js +157 -0
- package/dist/config-Dnszbv-6.js +36 -0
- package/dist/config-report-DMvWKUxL.js +44 -0
- package/dist/{connection-JvqwHODC.js → connection-CU8MQxRi.js} +1 -1
- package/dist/{context-CKEAabd7.js → context-Bcy85rGi.js} +36 -27
- package/dist/convex/index.d.ts +9 -4
- package/dist/convex/index.js +14 -4
- package/dist/create-B1d519XK.d.ts +245 -0
- package/dist/create-CMwIl-w2.js +253 -0
- package/dist/{credential-B_rkUNlY.js → credential-8P81ff4u.js} +26 -46
- package/dist/delegation-D8pUEuce.js +103 -0
- package/dist/{describe-Bt1YD5uS.js → describe-B_gVZEVi.js} +10 -15
- package/dist/{describe-DernyItf.d.ts → describe-DMYiCjJV.d.ts} +9 -10
- package/dist/{diff-x5Il8LVa.js → diff-2VaeChzX.js} +44 -11
- package/dist/{diff-BUzf5Poi.js → diff-DBIhz_f2.js} +2 -2
- package/dist/{doctor-Bqo6FwUa.js → doctor-BwCAMLx_.js} +2 -2
- package/dist/doctor-collect-CHk6XWOA.js +665 -0
- package/dist/doctor-igIiM_Hl.js +1194 -0
- package/dist/drizzle/index.d.ts +4 -3
- package/dist/drizzle/index.js +3 -3
- package/dist/elysia/index.d.ts +6 -28
- package/dist/elysia/index.js +16 -60
- package/dist/{errors-DSMnAVNa.js → errors-CWfoNg9N.js} +8 -4
- package/dist/{errors-Iy71s7Oj.js → errors-Cz5zn0mW.js} +17 -71
- package/dist/{errors-DbZ1YOnb.d.ts → errors-DPdqI50v.d.ts} +8 -21
- package/dist/{errors-m3u9TmDu.js → errors-DRnGZHzF.js} +4 -1
- package/dist/evaluation-items-CfCCejQd.js +40 -0
- package/dist/{evaluations-DsAU9ppl.js → evaluations-tz6D9A2G.js} +28 -46
- package/dist/eve/index.d.ts +6 -15
- package/dist/eve/index.js +7 -12
- package/dist/expiry-BoBJ8Tvc.js +7 -0
- package/dist/express/index.d.ts +6 -29
- package/dist/express/index.js +28 -58
- package/dist/fastify/index.d.ts +6 -29
- package/dist/fastify/index.js +16 -65
- package/dist/fields-BMl1RxoX.js +26 -0
- package/dist/{files-B_wAGYmt.js → files-DWUN9PzI.js} +5 -1
- package/dist/{fixtures-Cvaw0gDG.js → fixtures-ht28Cplr.js} +5 -2
- package/dist/{freeze-y1_jQL4v.js → freeze-CrU8nQeX.js} +31 -2
- package/dist/{from-snapshot-BMGnhmgv.js → from-snapshot-CTLijvdm.js} +66 -48
- package/dist/{grantee-Bv04_Vzf.js → grantee-Cv5Sogfv.js} +88 -7
- package/dist/graph-sql-B-dsAT6j.d.ts +29 -0
- package/dist/{headers-B5RRv3Xm.js → headers-BZSDieNT.js} +1 -1
- package/dist/{headers-C8SHQccD.d.ts → headers-Cf-zWvsO.d.ts} +1 -1
- package/dist/helpers-H_KARDVC.js +421 -0
- package/dist/hono/index.d.ts +6 -28
- package/dist/hono/index.js +11 -43
- package/dist/{hosted-BwKQ1BTA.js → hosted-DJQw46hn.js} +6 -5
- package/dist/{http-GgVApX1k.js → http-BIhmLfCc.js} +4 -20
- package/dist/http-SO7qGttf.js +44 -0
- package/dist/ids-DC4V4CFu.js +18 -0
- package/dist/index-0nNqHspG.d.ts +6 -0
- package/dist/{index-xIxJLwOQ.d.ts → index-BNc7Wer_.d.ts} +75 -7
- package/dist/{index-CltcQ4Bf.d.ts → index-C7Tw7MaW.d.ts} +68 -5
- package/dist/{index-LF_hS-yn.d.ts → index-C8z4RjQV.d.ts} +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +22 -17
- package/dist/instance-options-Dhhcnm3y.js +22 -0
- package/dist/instance-options-jARdwfPN.d.ts +11 -0
- package/dist/{parse-DPfIvFMU.js → json-schema-BsAflUGW.js} +28 -57
- package/dist/jwt/index.d.ts +10 -6
- package/dist/jwt/index.js +32 -13
- package/dist/kernel-_RRHmnMY.js +208 -0
- package/dist/kysely/index.d.ts +4 -3
- package/dist/kysely/index.js +3 -3
- package/dist/live-options-H2eG8dfJ.js +60 -0
- package/dist/{load-BZ24CpZP.js → load-DhWEB9P1.js} +16 -10
- package/dist/load-DwJUafYN.js +2 -0
- package/dist/manifest-iaoBXHKc.js +783 -0
- package/dist/manifest-uDS9y5de.d.ts +206 -0
- package/dist/{map-DowSO96j.js → map-AcxK9o_D.js} +4 -15
- package/dist/{map-claims-B_6I1Rn-.js → map-claims-Z4QZOiFF.js} +13 -4
- package/dist/map-error-pgUWNNtG.js +23 -0
- package/dist/may-use-DndQ5tQ-.js +77 -0
- package/dist/mcp/index.d.ts +54 -22
- package/dist/mcp/index.js +218 -178
- package/dist/nest/index.d.ts +26 -31
- package/dist/nest/index.js +75 -84
- package/dist/next/client.d.ts +7 -8
- package/dist/next/client.js +11 -9
- package/dist/next/index.d.ts +30 -47
- package/dist/next/index.js +45 -60
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +32 -1
- package/dist/node/index.d.ts +9 -28
- package/dist/node/index.js +11 -43
- package/dist/openai/index.d.ts +6 -15
- package/dist/openai/index.js +7 -20
- package/dist/openapi/index.d.ts +87 -4
- package/dist/openapi/index.js +3 -3
- package/dist/{openapi-DcqM8nU7.js → openapi-BqRBvAng.js} +237 -5
- package/dist/{openapi-DWD0Uc_4.js → openapi-CqqEbHsL.js} +5 -5
- package/dist/{openapi-import-BWRUNNeW.js → openapi-import-cmwetbzw.js} +1 -1
- package/dist/{openapi-CwePaF7C.js → openapi-l0xYHkcv.js} +3 -3
- package/dist/{openapi-schema-D8j_T9tO.js → openapi-schema--ARjyfQr.js} +2 -2
- package/dist/orpc/index.d.ts +15 -28
- package/dist/orpc/index.js +74 -117
- package/dist/otel/index.d.ts +2 -2
- package/dist/parse-BuyjxhZV.js +53 -0
- package/dist/parse-local-manifest-BrMozJCo.js +36 -0
- package/dist/parse-local-manifest-JLnaBH0S.d.ts +10 -0
- package/dist/pdp/index.d.ts +2 -2
- package/dist/pdp/index.js +14 -127
- package/dist/{permdock-yoZ7EjxX.js → permdock-CbgvLnuo.js} +672 -149
- package/dist/{permissions-B8vCg_bI.d.ts → permissions-CDgvKG5L.d.ts} +2 -2
- package/dist/{permissions-DC7GROPA.js → permissions-DVoF6dMt.js} +77 -15
- package/dist/{permissions-B4apnahQ.d.ts → permissions-DttnqqxC.d.ts} +34 -5
- package/dist/{permissions-CofRqesP.js → permissions-RJrovw98.js} +3 -3
- package/dist/{pins-CqE1jZuF.js → pins-8ILTftC9.js} +2 -2
- package/dist/{policy-Cq2fB8Q5.d.ts → policy-D2W7uv19.d.ts} +2 -2
- package/dist/{policy-CInmNGkM.d.ts → policy-IB6yxpC3.d.ts} +351 -42
- package/dist/{policy-ANp36PnI.js → policy-xyWg7QdR.js} +173 -22
- package/dist/powersync-BylawD_U.js +540 -0
- package/dist/powersync-DliZK_-y.js +62 -0
- package/dist/powersync-e_AZrKbg.js +2 -0
- package/dist/prisma/index.d.ts +3 -2
- package/dist/prisma/index.js +45 -20
- package/dist/{problem-Cgp9-KZA.js → problem-B43ztKIm.js} +74 -109
- package/dist/problem-details-DX075l9V.d.ts +7 -0
- package/dist/problem-details-_AMOYz-w.js +144 -0
- package/dist/{protected-BuPAycJT.d.ts → protected-BdnIEFkY.d.ts} +5 -4
- package/dist/protected-utU-SLuy.js +100 -0
- package/dist/protected-xAr24FEC.d.ts +40 -0
- package/dist/provider-CSQd7m92.js +52 -0
- package/dist/{provider-wTAUda2v.d.ts → provider-DZY281jO.d.ts} +1 -1
- package/dist/provider-store-Cfbvo4DE.js +43 -0
- package/dist/react/index.d.ts +7 -5
- package/dist/react/index.js +5 -4
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +166 -10
- package/dist/react-native/index.js +607 -85
- package/dist/{refs-ChLSxbST.js → refs-KYb4ntaz.js} +4 -3
- package/dist/{relations-r4soIFZB.js → relations-cjOL01hZ.js} +110 -31
- package/dist/replay-BsOyTrdV.js +28 -0
- package/dist/replay-CYyiLiwO.d.ts +5 -0
- package/dist/resource-ref-CaS9yVPi.js +20 -0
- package/dist/revocations-Bhr0aMIC.js +27 -0
- package/dist/{rls-BOFU3chG.js → rls-B4ur8nKQ.js} +8 -3
- package/dist/rls-DQtdgTHB.js +2521 -0
- package/dist/rls-fields-74oZPXWL.js +3582 -0
- package/dist/{rls-graph-DxQ39PJj.js → rls-graph-CHwx0V8q.js} +100 -36
- package/dist/{rls-import-CXV6LBlS.js → rls-import-BTFVk3jH.js} +7 -6
- package/dist/{rls-migrate-DrFPbf8p.js → rls-migrate-CN4TlJTO.js} +6 -5
- package/dist/rls-ownership-RuFqaqGz.js +843 -0
- package/dist/rls-shared-Cewd_-zI.js +495 -0
- package/dist/rls-verify-CVQTHTh5.js +746 -0
- package/dist/{row-check-BajTgQZ-.d.ts → row-check-Dt35czLy.d.ts} +12 -15
- package/dist/row-conditions-h2HythXQ.js +27 -0
- package/dist/rpc-DL9Sqvb1.js +89 -0
- package/dist/{run-CJ6UvS3a.js → run-DKDbM4Du.js} +31 -12
- package/dist/runtime-CB8gQFvS.js +11 -0
- package/dist/{saas-BRrZzJkM.js → saas-DhpJjnBA.js} +17 -7
- package/dist/{schema-P8yoma__.js → schema-D3MnDMxa.js} +8 -4
- package/dist/scim/index.d.ts +1 -1
- package/dist/scim/index.js +1 -1
- package/dist/{scim-9NFa0XVZ.js → scim-BFGBBAqR.js} +1 -1
- package/dist/{scopes-cKVp_WTw.js → scopes-CVdPrWN-.js} +35 -8
- package/dist/server/index.d.ts +22 -11
- package/dist/server/index.js +11 -8
- package/dist/{credentials-BgRX3WEg.js → server-DlZ28Oz5.js} +1 -1
- package/dist/shared-DRu8VFVM.js +123 -0
- package/dist/{sink-Cfr9-7YC.d.ts → sink-B1fZzIMf.d.ts} +1 -1
- package/dist/skills-Biq5uup3.js +192 -0
- package/dist/{skills-C_lpV4Jx.js → skills-Bx-U0irG.js} +10 -4
- package/dist/{snapshot-DE8dE_hz.js → snapshot-Enxd0n-s.js} +138 -214
- package/dist/snapshot-cache-B-LxUahj.d.ts +45 -0
- package/dist/snapshot-cache-D6jLelln.js +44 -0
- package/dist/solid/index.d.ts +39 -9
- package/dist/solid/index.js +76 -85
- package/dist/sources-4p0-4xQm.js +545 -0
- package/dist/sources-BdOYLw9j.d.ts +192 -0
- package/dist/{sql-Bll1d7y0.js → sql-xdg5f4Fi.js} +6 -1
- package/dist/{src-CTNT-PBW.js → src-BDwEZWrZ.js} +149 -11
- package/dist/ssf/index.d.ts +3 -2
- package/dist/ssf/index.js +3 -29
- package/dist/store-DMAWwAJV.js +371 -0
- package/dist/{store-Bq90395i.d.ts → store-jOBYpiXC.d.ts} +13 -4
- package/dist/store-options-ByhZq1jV.js +716 -0
- package/dist/{stores-CkG2HrSg.js → stores-BT2pSCub.js} +10 -63
- package/dist/{stores-CvOnu_2T.d.ts → stores-C0IMQrGI.d.ts} +20 -9
- package/dist/{stream-BhMfZOwr.d.ts → stream-D1RXubw2.d.ts} +2 -2
- package/dist/{stream-Q_xlB6Ko.js → stream-DDpvynSB.js} +16 -2
- package/dist/subject-settings-BwBmCdTR.js +128 -0
- package/dist/supabase/index.d.ts +133 -6
- package/dist/supabase/index.js +4 -3
- package/dist/supabase/middleware.d.ts +32 -27
- package/dist/supabase/middleware.js +56 -22
- package/dist/{supabase-D8UlBA7Z.js → supabase-Boqb9a_Y.js} +3 -3
- package/dist/{supabase-DTxGtYdi.js → supabase-C0qESOa-.js} +347 -367
- package/dist/supabase-config-BpPXt3x1.js +472 -0
- package/dist/supabase-hook-6sYLXg_u.js +2 -0
- package/dist/{supabase-hook-D_OqGb_V.js → supabase-hook-CSLQ4zJ3.js} +1037 -705
- package/dist/supautils-Cn4R4cJ4.js +91 -0
- package/dist/svelte/PermissionBoundary.svelte +37 -0
- package/dist/svelte/Protected.svelte +8 -2
- package/dist/svelte/index.d.ts +9 -6
- package/dist/svelte/index.js +61 -7
- package/dist/svelte/runtime.d.ts +4 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +9 -6
- package/dist/svelte/source.js +7 -4
- package/dist/terminal/index.d.ts +6 -14
- package/dist/terminal/index.js +44 -32
- package/dist/testing/index.d.ts +105 -16
- package/dist/testing/index.js +504 -40
- package/dist/testing/saas/index.d.ts +4 -4
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/token-CIWxJOCh.js +50 -0
- package/dist/trpc/index.d.ts +7 -26
- package/dist/trpc/index.js +17 -100
- package/dist/types-BnIEz1sU.d.ts +48 -0
- package/dist/{types-CxquUByI.d.ts → types-Bvc4oWT6.d.ts} +1 -1
- package/dist/{sources-DOpW5Ila.d.ts → types-C97IskBw.d.ts} +86 -127
- package/dist/types-CKNSyIf_.d.ts +52 -0
- package/dist/types-Cq_H8N_X.d.ts +197 -0
- package/dist/{types-D6Z6-rKJ.d.ts → types-DbjRN55U.d.ts} +273 -22
- package/dist/{types-BBK-exvJ.d.ts → types-Dnemwd77.d.ts} +7 -1
- package/dist/{types-BvBk1wgn.d.ts → types-DxiCGmmd.d.ts} +5 -3
- package/dist/{types-B9dmiiab.d.ts → types-QCuYJh4q.d.ts} +1 -1
- package/dist/{types-B8w5PFtS.d.ts → types-aqOHTHcp.d.ts} +17 -1
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +10 -9
- package/dist/usage-DorAJiU_.js +2 -0
- package/dist/{usage-1ozMubLY.js → usage-GTixvDDU.js} +2 -2
- package/dist/{usage-CUnK6Ou9.js → usage-ItS7nLcl.js} +225 -62
- package/dist/validation-error-Ba264OaJ.d.ts +26 -0
- package/dist/validation-error-CnjZ4t92.js +33 -0
- package/dist/{validity-D0uRmJKJ.js → validity-uOp8UpYn.js} +57 -36
- package/dist/{verifier-B90agnhl.js → verifier-BxBo6I44.js} +10 -5
- package/dist/version-BvacVTZR.js +17 -0
- package/dist/views-Soe7bGSC.js +95 -0
- package/dist/{vocabulary-Dk0fs-Jk.d.ts → vocabulary-DA8a8zif.d.ts} +1 -1
- package/dist/vue/index.d.ts +35 -13
- package/dist/vue/index.js +86 -93
- package/dist/watch-BwxLTYMZ.js +2 -0
- package/dist/watch-DVBJ9cBq.js +128 -0
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +7 -13
- package/package.json +62 -36
- package/schemas/approval-request-v1.json +175 -0
- package/schemas/catalog-v1.json +29 -1
- package/schemas/supabase-claims-v1.json +6 -1
- package/schemas/supabase-manifest-v1.json +150 -6
- package/skills/permdock/SKILL.md +7 -7
- package/skills/permdock-agents/SKILL.md +5 -5
- package/skills/permdock-agents/references/runtimes.md +12 -10
- package/skills/permdock-approvals/SKILL.md +4 -4
- package/skills/permdock-approvals/references/stores-and-resume.md +15 -4
- package/skills/permdock-audit/SKILL.md +3 -3
- package/skills/permdock-credentials/SKILL.md +4 -4
- package/skills/permdock-credentials/references/tokens-keys-links.md +7 -2
- package/skills/permdock-data/SKILL.md +4 -4
- package/skills/permdock-data/references/queries-and-rls.md +14 -6
- package/skills/permdock-data/references/relationships.md +5 -3
- package/skills/permdock-tenancy/SKILL.md +9 -6
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +14 -8
- package/skills/permdock-wire/SKILL.md +4 -4
- package/skills/permdock-wire/references/adapters.md +42 -30
- package/skills/permdock-wire/references/policy-options.md +5 -3
- package/dist/approvers-aH0PJkmf.js +0 -20
- package/dist/catalog-idsHc0vo.js +0 -2
- package/dist/collect-B7_TIfmg.js +0 -49
- package/dist/collect-fRPAQIWb.js +0 -2
- package/dist/compile-CQbvyIBr.d.ts +0 -30
- package/dist/config-BPYmjTOp.js +0 -26
- package/dist/context-gtAwAILx.js +0 -6
- package/dist/create-BKF3dgFk.d.ts +0 -162
- package/dist/create-CSkhDU9T.js +0 -222
- package/dist/doctor-BGle8Gqi.js +0 -1314
- package/dist/helpers-DnSOrhB9.js +0 -682
- package/dist/index-fG6NSXlc.d.ts +0 -9
- package/dist/kernel-YOGCWNLf.js +0 -211
- package/dist/load-QOOm1z70.js +0 -2
- package/dist/manifest-DDQbC6K0.d.ts +0 -118
- package/dist/map-error-RJ3ZsUyA.js +0 -16
- package/dist/may-use-CnFEjVEp.js +0 -37
- package/dist/pg-4GJWDUxG.js +0 -953
- package/dist/plugin-4gr7IxFJ.js +0 -65
- package/dist/protected-B4K3BKoX.js +0 -138
- package/dist/protected-wY7nXhY9.d.ts +0 -23
- package/dist/provider-_wx46HpB.js +0 -48
- package/dist/rls-CJVja9Ww.js +0 -2119
- package/dist/rls-helpers-BxRuif0l.js +0 -1346
- package/dist/runtime-DP9ttFh1.js +0 -27
- package/dist/skills-Dx3QLnMY.js +0 -110
- package/dist/source-DoBBYJi2.js +0 -6
- package/dist/sources-DVHM3Mwq.js +0 -284
- package/dist/store-C6hFW6ta.js +0 -508
- package/dist/subject-settings-xt7V845g.js +0 -82
- package/dist/supabase-hook-DW-xP4hU.js +0 -2
- package/dist/types-DbWdEfcm.d.ts +0 -87
- package/dist/types-bva3i4Gf.d.ts +0 -98
- package/dist/types-i4BAOvdb.d.ts +0 -97
- package/dist/usage-PyEor6GA.js +0 -2
- package/dist/version-CnSh33Fj.js +0 -16
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import { n as isReadonlyArray, r as sole, t as compact } from "./compact-y6vfu2_f.js";
|
|
2
|
-
import { n as freezeDeep, t as freezeCopy } from "./freeze-
|
|
3
|
-
import {
|
|
4
|
-
import { a as hasConditionOp } from "./refs-
|
|
5
|
-
import { d as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { a as
|
|
9
|
-
import {
|
|
2
|
+
import { n as freezeDeep, t as freezeCopy } from "./freeze-CrU8nQeX.js";
|
|
3
|
+
import { E as ownGet, T as isForbiddenKey, _ as listPermissions, a as formerKeys, c as getResource, d as isEdgeRelation, f as isFieldRelation, i as findPermission, m as isPrincipalRelation, o as formerScopes, r as expandRelation } from "./permissions-DVoF6dMt.js";
|
|
4
|
+
import { a as hasConditionOp } from "./refs-KYb4ntaz.js";
|
|
5
|
+
import { d as normalizeApproval, f as normalizeAssurance, g as applyQuota, i as declaredRoleNames$1, l as levelCondition, m as role, n as assertApprovalRelations, s as grantList, u as levelNames, v as normalizeWhere } from "./policy-xyWg7QdR.js";
|
|
6
|
+
import { n as PermDockValidationError } from "./validation-error-CnjZ4t92.js";
|
|
7
|
+
import { a as wireDenials } from "./problem-details-_AMOYz-w.js";
|
|
8
|
+
import { a as deniedMessage, i as approvalMessage, n as PermDockDeniedError, t as PermDockApprovalRequiredError } from "./errors-Cz5zn0mW.js";
|
|
9
|
+
import { n as sameId } from "./ids-DC4V4CFu.js";
|
|
10
|
+
import { a as normalizeMemberships, c as rootScope, d as scopeList, f as subjectForPermission, i as keepsPermission, l as scopeChain, m as tenantOf, o as resolveScope, s as rootMembershipId, u as scopeIdOf } from "./scopes-CVdPrWN-.js";
|
|
11
|
+
import { a as whereFromGrants, c as rowIdOf, d as isArazzoSimulateInput, f as simulateArazzo, i as signSnapshot, l as rowValues, p as evaluateCondition, r as snapshotGrant, s as isRowPair, t as buildSnapshot } from "./snapshot-Enxd0n-s.js";
|
|
10
12
|
import { t as parseDuration } from "./duration-xK2jGFVc.js";
|
|
11
|
-
import {
|
|
13
|
+
import { D as synthesiseRole, E as listRoles, S as findRole, _ as resourceRoleCondition, a as authenticated, d as matchGrantee, i as assurance, m as relationCondition, o as combineWhere, r as asGrantee, s as flattenGrantee, w as isRole } from "./grantee-Cv5Sogfv.js";
|
|
14
|
+
import { t as canonicalJson } from "./canonical-json-DemiK4Vp.js";
|
|
15
|
+
import { n as pickVisible, r as sanitizeContext, t as grantCoversField } from "./fields-BMl1RxoX.js";
|
|
16
|
+
import { a as isMembershipExpired, d as partitionsOf, f as relatesTo, g as tenantsOf, h as scopeField, i as inTeam, o as matchScopedMembership, p as resolveActiveTenant, r as heldRoleNamesIn, t as isActive, u as nowSeconds } from "./validity-uOp8UpYn.js";
|
|
12
17
|
import { t as requiresApproval } from "./approval-required-TzNB9oQA.js";
|
|
13
|
-
import { r as mergeHostedGrants, t as isPortableCondition } from "./hosted-
|
|
18
|
+
import { r as mergeHostedGrants, t as isPortableCondition } from "./hosted-DJQw46hn.js";
|
|
14
19
|
import { n as isThenable, t as ignoreRejection } from "./thenable-okc2xDsj.js";
|
|
15
20
|
import { t as byCodePoint } from "./compare-pfyVnBjE.js";
|
|
16
|
-
import { a as relationWalk, i as relationReader, n as pendingRelations, o as resolveRelated, r as relationId } from "./relations-
|
|
21
|
+
import { a as relationWalk, i as relationReader, n as pendingRelations, o as resolveRelated, r as relationId } from "./relations-cjOL01hZ.js";
|
|
22
|
+
import { i as requiresStandIn, n as coversRequirements, r as delegatedPermissions, t as coveredByDelegation } from "./delegation-D8pUEuce.js";
|
|
23
|
+
import { t as resourceIdOf } from "./resource-ref-CaS9yVPi.js";
|
|
24
|
+
import { n as payloadDigest, r as versionOf, t as decisionToken } from "./token-CIWxJOCh.js";
|
|
17
25
|
import { i as isSubject, r as isPrincipal, t as anonymousSubject } from "./subject-C6dLxqHU.js";
|
|
18
26
|
//#region src/core/approval-policies.ts
|
|
27
|
+
/** The denial detail when an `ApprovalPolicySource` fails: every call an allow would grant denies with reason `approval`. */
|
|
28
|
+
const APPROVAL_POLICY_UNAVAILABLE = "approval-policy-unavailable";
|
|
19
29
|
function memoryApprovalPolicies(policies) {
|
|
20
30
|
const entries = [...policies];
|
|
21
31
|
return Object.freeze({ approvalPoliciesFor(query) {
|
|
@@ -28,17 +38,13 @@ function isRecord(value) {
|
|
|
28
38
|
function isStringList(value) {
|
|
29
39
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
30
40
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* permission the policy declares (it applies to nothing), `undefined` when
|
|
34
|
-
* it is invalid.
|
|
35
|
-
*/
|
|
36
|
-
function loadOne(policy, raw) {
|
|
37
|
-
if (!isRecord(raw) || typeof raw["permission"] !== "string") return;
|
|
41
|
+
function checkOne(policy, raw) {
|
|
42
|
+
if (!isRecord(raw) || typeof raw["permission"] !== "string") return { problem: "invalid" };
|
|
38
43
|
const permission = findPermission(policy.permissions, raw["permission"]);
|
|
39
|
-
if (permission === void 0) return
|
|
44
|
+
if (permission === void 0) return { problem: "unknown-permission" };
|
|
40
45
|
const { tenant, actors, where, check, approval } = raw;
|
|
41
|
-
if (tenant !== void 0 && typeof tenant !== "string" || actors !== void 0 && !isStringList(actors) || where !== void 0 && !isPortableCondition(where) || check !== void 0 && !isPortableCondition(check) ||
|
|
46
|
+
if (tenant !== void 0 && typeof tenant !== "string" || actors !== void 0 && !isStringList(actors) || where !== void 0 && !isPortableCondition(where) || check !== void 0 && !isPortableCondition(check) || approval !== "human" && !isRecord(approval)) return { problem: "invalid" };
|
|
47
|
+
if (where !== void 0 && permission.kind !== "instance") return { problem: "where-on-collection" };
|
|
42
48
|
let normalized;
|
|
43
49
|
try {
|
|
44
50
|
normalized = normalizeApproval(approval, permission.key);
|
|
@@ -47,18 +53,47 @@ function loadOne(policy, raw) {
|
|
|
47
53
|
approval: normalized
|
|
48
54
|
}], policy.resources);
|
|
49
55
|
} catch {
|
|
50
|
-
return;
|
|
56
|
+
return { problem: "invalid" };
|
|
51
57
|
}
|
|
52
|
-
if (normalized === void 0) return;
|
|
53
|
-
if (normalized !== "human" && normalized.staleOn !== void 0 && (permission.kind !== "instance" || policy.resources.get(permission.resource)?.version === void 0)) return;
|
|
54
|
-
return compact({
|
|
58
|
+
if (normalized === void 0) return { problem: "invalid" };
|
|
59
|
+
if (normalized !== "human" && normalized.staleOn !== void 0 && (permission.kind !== "instance" || policy.resources.get(permission.resource)?.version === void 0)) return { problem: "stale-on-without-version" };
|
|
60
|
+
return { entry: compact({
|
|
55
61
|
permission: permission.key,
|
|
56
62
|
tenant,
|
|
57
63
|
actors: actors === void 0 ? void 0 : new Set(actors),
|
|
58
64
|
where: where === void 0 ? void 0 : normalizeWhere(where),
|
|
59
65
|
check: check === void 0 ? void 0 : normalizeWhere(check),
|
|
60
66
|
approval: normalized
|
|
61
|
-
});
|
|
67
|
+
}) };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Checks one entry against the policy the way `approvalPoliciesFor` loads
|
|
71
|
+
* it, for an application to refuse a bad entry when it is saved instead of
|
|
72
|
+
* finding every call denied. Never throws.
|
|
73
|
+
*/
|
|
74
|
+
function validateApprovalPolicy(policy, entry) {
|
|
75
|
+
try {
|
|
76
|
+
const checked = checkOne(policy, entry);
|
|
77
|
+
return "problem" in checked ? {
|
|
78
|
+
ok: false,
|
|
79
|
+
problem: checked.problem
|
|
80
|
+
} : { ok: true };
|
|
81
|
+
} catch {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
problem: "invalid"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* One raw entry checked against the policy: `null` when it names no
|
|
90
|
+
* permission the policy declares (it applies to nothing), `undefined` when
|
|
91
|
+
* it does not load.
|
|
92
|
+
*/
|
|
93
|
+
function loadOne(policy, raw) {
|
|
94
|
+
const checked = checkOne(policy, raw);
|
|
95
|
+
if ("entry" in checked) return checked.entry;
|
|
96
|
+
return checked.problem === "unknown-permission" ? null : void 0;
|
|
62
97
|
}
|
|
63
98
|
function loadAll(policy, raw) {
|
|
64
99
|
if (!Array.isArray(raw)) return "failed";
|
|
@@ -103,6 +138,17 @@ function asStages(approval) {
|
|
|
103
138
|
quorum: approval.quorum
|
|
104
139
|
})];
|
|
105
140
|
}
|
|
141
|
+
/** An entry's stages, each carrying the entry's escalation unless it has its own. */
|
|
142
|
+
function entryStages(approval) {
|
|
143
|
+
const stages = asStages(approval);
|
|
144
|
+
if (approval === "human" || approval.escalation === void 0) return stages;
|
|
145
|
+
const escalation = approval.escalation;
|
|
146
|
+
return stages.map((stage) => stage.escalation === void 0 ? compact({
|
|
147
|
+
by: stage.by,
|
|
148
|
+
quorum: stage.quorum,
|
|
149
|
+
escalation
|
|
150
|
+
}) : stage);
|
|
151
|
+
}
|
|
106
152
|
function shorter(a, b) {
|
|
107
153
|
if (a === void 0) return b;
|
|
108
154
|
if (b === void 0) return a;
|
|
@@ -124,8 +170,9 @@ function tightenApproval(code, entries, input) {
|
|
|
124
170
|
let distinct = false;
|
|
125
171
|
let staleOn;
|
|
126
172
|
let ttl;
|
|
127
|
-
for (const part of parts) {
|
|
128
|
-
|
|
173
|
+
for (const [index, part] of parts.entries()) {
|
|
174
|
+
const fromCode = code !== void 0 && index === 0;
|
|
175
|
+
for (const stage of fromCode ? asStages(part) : entryStages(part)) {
|
|
129
176
|
const key = JSON.stringify(stage);
|
|
130
177
|
if (!seen.has(key)) {
|
|
131
178
|
seen.add(key);
|
|
@@ -173,7 +220,8 @@ function identity(membership) {
|
|
|
173
220
|
membership.member?.group ?? null,
|
|
174
221
|
[...membership.eligible ?? []].toSorted(),
|
|
175
222
|
membership.managedBy ?? null,
|
|
176
|
-
[...membership.entitlements ?? []].toSorted()
|
|
223
|
+
[...membership.entitlements ?? []].toSorted(),
|
|
224
|
+
membership.keep === void 0 ? null : [...membership.keep].toSorted()
|
|
177
225
|
]);
|
|
178
226
|
}
|
|
179
227
|
/**
|
|
@@ -245,7 +293,8 @@ function asMembershipSource(input) {
|
|
|
245
293
|
/**
|
|
246
294
|
* Trust the memberships the verified token carries, and read `source` only
|
|
247
295
|
* when the token marks them truncated. With `version`, a token minted before
|
|
248
|
-
* the latest membership change is stale for the policy's `fresh` permissions
|
|
296
|
+
* the latest membership change is stale for the policy's `fresh` permissions,
|
|
297
|
+
* or with `onStale: 'reread'` has its memberships read from `source` instead.
|
|
249
298
|
*/
|
|
250
299
|
function claimsFirst(source, options = {}) {
|
|
251
300
|
const inner = asMembershipSource(source);
|
|
@@ -254,6 +303,7 @@ function claimsFirst(source, options = {}) {
|
|
|
254
303
|
membershipsFor: (principal, query) => inner.membershipsFor(principal, query),
|
|
255
304
|
...inner.list === void 0 ? {} : { list: (query) => inner.list?.(query) ?? [] },
|
|
256
305
|
...version === void 0 ? {} : { version },
|
|
306
|
+
...options.onStale === "reread" ? { onStale: "reread" } : {},
|
|
257
307
|
claimsFirst: true
|
|
258
308
|
};
|
|
259
309
|
}
|
|
@@ -261,6 +311,33 @@ function claimsFirst(source, options = {}) {
|
|
|
261
311
|
function isExternallyManaged(membership) {
|
|
262
312
|
return membership.managedBy === "idp";
|
|
263
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* How many principals hold `role` in one scope instance now, from the
|
|
316
|
+
* source's member list (`MembershipSource.list`): live memberships of exactly
|
|
317
|
+
* that scope and instance, each principal counted once. The `holders` a
|
|
318
|
+
* `decideRoleChange` with `min`, `max` or `transferOnly` needs. `undefined`
|
|
319
|
+
* when the source cannot list or the read fails, which that check denies.
|
|
320
|
+
*/
|
|
321
|
+
async function countHolders(source, query) {
|
|
322
|
+
const composed = asMembershipSource(source);
|
|
323
|
+
if (composed.list === void 0) return;
|
|
324
|
+
const role = typeof query.role === "string" ? query.role : query.role.key;
|
|
325
|
+
try {
|
|
326
|
+
const entries = await composed.list({
|
|
327
|
+
scope: query.scope,
|
|
328
|
+
id: query.id
|
|
329
|
+
});
|
|
330
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
331
|
+
const holders = /* @__PURE__ */ new Set();
|
|
332
|
+
for (const entry of entries) {
|
|
333
|
+
const membership = entry.membership;
|
|
334
|
+
if (membership.scope === query.scope && membership.id === query.id && membership.roles.includes(role) && (membership.expiresAt === void 0 || membership.expiresAt > now)) holders.add(entry.principal.id);
|
|
335
|
+
}
|
|
336
|
+
return holders.size;
|
|
337
|
+
} catch {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
264
341
|
//#endregion
|
|
265
342
|
//#region src/core/custom-roles.ts
|
|
266
343
|
/** The current key for a stored key, current or former; `undefined` when neither. */
|
|
@@ -269,7 +346,30 @@ function currentKey(policy, raw) {
|
|
|
269
346
|
if (leaf === void 0) return;
|
|
270
347
|
return leaf.key === raw || formerKeys(leaf).includes(raw) ? leaf.key : void 0;
|
|
271
348
|
}
|
|
272
|
-
const GRANT_KEYS = /* @__PURE__ */ new Set([
|
|
349
|
+
const GRANT_KEYS = /* @__PURE__ */ new Set([
|
|
350
|
+
"permission",
|
|
351
|
+
"effect",
|
|
352
|
+
"level"
|
|
353
|
+
]);
|
|
354
|
+
/** `where` narrowed by a level; the empty level (`{}`) leaves it as is. */
|
|
355
|
+
function andWhere(where, level) {
|
|
356
|
+
if (level.op === "and" && level.conditions.length === 0) return where;
|
|
357
|
+
return where === void 0 ? level : {
|
|
358
|
+
op: "and",
|
|
359
|
+
conditions: [where, level]
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
/** A ceiling grant narrowed by a level's condition and marked with the level. */
|
|
363
|
+
function leveled(grant, level, condition) {
|
|
364
|
+
const where = andWhere(grant.where, condition);
|
|
365
|
+
const check = grant.check === void 0 ? void 0 : andWhere(grant.check, condition);
|
|
366
|
+
return freezeDeep({
|
|
367
|
+
...grant,
|
|
368
|
+
level,
|
|
369
|
+
...where === void 0 ? {} : { where },
|
|
370
|
+
...check === void 0 ? {} : { check }
|
|
371
|
+
});
|
|
372
|
+
}
|
|
273
373
|
/**
|
|
274
374
|
* The scope a custom role is held at: `scope` (a name or alias), the second
|
|
275
375
|
* scope for the `team` input shape, else the first scope. `undefined` when
|
|
@@ -372,8 +472,11 @@ function resolveCustomRole(policy, role) {
|
|
|
372
472
|
};
|
|
373
473
|
const included = [];
|
|
374
474
|
const includedKeys = /* @__PURE__ */ new Set();
|
|
375
|
-
const allowKeys = /* @__PURE__ */ new
|
|
475
|
+
const allowKeys = /* @__PURE__ */ new Map();
|
|
376
476
|
const denyKeys = /* @__PURE__ */ new Set();
|
|
477
|
+
const allowAll = (key) => {
|
|
478
|
+
allowKeys.set(key, "all");
|
|
479
|
+
};
|
|
377
480
|
for (const name of Array.isArray(role.includes) ? role.includes : []) {
|
|
378
481
|
if (typeof name !== "string" || !declared.has(name)) {
|
|
379
482
|
drop({
|
|
@@ -390,7 +493,7 @@ function resolveCustomRole(policy, role) {
|
|
|
390
493
|
} else if (ceilingSet.has(grant)) {
|
|
391
494
|
included.push(grant);
|
|
392
495
|
includedKeys.add(key);
|
|
393
|
-
} else if (ceilingKeys.has(key))
|
|
496
|
+
} else if (ceilingKeys.has(key)) allowAll(key);
|
|
394
497
|
else drop({
|
|
395
498
|
permission: key,
|
|
396
499
|
reason: "outside-ceiling"
|
|
@@ -421,7 +524,9 @@ function resolveCustomRole(policy, role) {
|
|
|
421
524
|
to: key
|
|
422
525
|
});
|
|
423
526
|
const effect = entry.effect ?? "allow";
|
|
424
|
-
|
|
527
|
+
const extra = Object.keys(entry).some((name) => !GRANT_KEYS.has(name));
|
|
528
|
+
const level = entry.level;
|
|
529
|
+
if (extra || effect !== "allow" && effect !== "deny" || effect === "deny" && level !== void 0) {
|
|
425
530
|
drop({
|
|
426
531
|
permission: key,
|
|
427
532
|
reason: "condition-not-allowed"
|
|
@@ -433,6 +538,21 @@ function resolveCustomRole(policy, role) {
|
|
|
433
538
|
denyKeys.add(key);
|
|
434
539
|
continue;
|
|
435
540
|
}
|
|
541
|
+
let narrowing;
|
|
542
|
+
if (level !== void 0) {
|
|
543
|
+
const leaf = findPermission(policy.permissions, key);
|
|
544
|
+
const condition = typeof level === "string" && leaf?.kind === "instance" ? levelCondition(policy, leaf.resource, level) : void 0;
|
|
545
|
+
if (condition === void 0) {
|
|
546
|
+
drop({
|
|
547
|
+
permission: key,
|
|
548
|
+
reason: "unknown-level",
|
|
549
|
+
level: String(level)
|
|
550
|
+
});
|
|
551
|
+
denyKeys.add(key);
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
narrowing = [String(level), condition];
|
|
555
|
+
}
|
|
436
556
|
if (!ceilingKeys.has(key)) {
|
|
437
557
|
drop({
|
|
438
558
|
permission: key,
|
|
@@ -440,19 +560,27 @@ function resolveCustomRole(policy, role) {
|
|
|
440
560
|
});
|
|
441
561
|
continue;
|
|
442
562
|
}
|
|
443
|
-
allowKeys.
|
|
563
|
+
const held = allowKeys.get(key);
|
|
564
|
+
if (narrowing === void 0) allowAll(key);
|
|
565
|
+
else if (held === void 0) allowKeys.set(key, new Map([narrowing]));
|
|
566
|
+
else if (held !== "all") held.set(...narrowing);
|
|
444
567
|
}
|
|
445
568
|
const out = [];
|
|
446
569
|
const push = (grant) => {
|
|
447
570
|
if (!out.includes(grant)) out.push(grant);
|
|
448
571
|
};
|
|
449
572
|
for (const grant of included) if (grant.effect === "deny" || !denyKeys.has(grant.permission.key)) push(grant);
|
|
450
|
-
for (const key of allowKeys) {
|
|
573
|
+
for (const [key, levels] of allowKeys) {
|
|
451
574
|
if (denyKeys.has(key) || includedKeys.has(key)) continue;
|
|
452
575
|
const sources = /* @__PURE__ */ new Set();
|
|
453
|
-
for (const grant of ceiling)
|
|
454
|
-
|
|
576
|
+
for (const grant of ceiling) {
|
|
577
|
+
if (grant.permission.key !== key) continue;
|
|
455
578
|
sources.add(soleRole(grant) ?? "");
|
|
579
|
+
if (levels === "all") {
|
|
580
|
+
push(grant);
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
for (const [level, condition] of levels) push(leveled(grant, level, condition));
|
|
456
584
|
}
|
|
457
585
|
for (const grant of all) if (grant.effect === "deny" && grant.permission.key === key && sources.has(soleRole(grant) ?? "")) push(grant);
|
|
458
586
|
}
|
|
@@ -490,12 +618,17 @@ function customGrantsFor(policy, roles) {
|
|
|
490
618
|
}
|
|
491
619
|
return out;
|
|
492
620
|
}
|
|
621
|
+
/** The well-formed tenant (not platform) custom roles; a declared role name is never one. */
|
|
622
|
+
function tenantCustomRoles(policy, roles) {
|
|
623
|
+
const declared = declaredRoleNames$1(policy);
|
|
624
|
+
return roles.filter((role) => wellFormed(role) && !isGlobalCustomRole(role) && !declared.has(role.name));
|
|
625
|
+
}
|
|
493
626
|
/** A membership at the role's scope, inside its tenant (and instance, when pinned), naming it. */
|
|
494
627
|
function holdsCustomRole(membership, role, scopes) {
|
|
495
628
|
if (isGlobalCustomRole(role)) return false;
|
|
496
629
|
const scope = customRoleScope(role, scopes);
|
|
497
630
|
const id = customRoleId(role);
|
|
498
|
-
return scope !== void 0 && membership.scope === scope && tenantOf(membership, scopes)
|
|
631
|
+
return scope !== void 0 && membership.scope === scope && sameId(tenantOf(membership, scopes), role.tenant) && (id === void 0 || sameId(membership.id, id)) && membership.roles.includes(role.name);
|
|
499
632
|
}
|
|
500
633
|
/** Whether `principal.roles` names the platform custom role. */
|
|
501
634
|
function holdsGlobalCustomRole(roles, role) {
|
|
@@ -503,7 +636,7 @@ function holdsGlobalCustomRole(roles, role) {
|
|
|
503
636
|
}
|
|
504
637
|
/** A tenant's custom role inside that tenant, or a platform custom role anywhere. */
|
|
505
638
|
function isCustomRoleName(name, roles, tenant) {
|
|
506
|
-
return roles.some((item) => wellFormed(item) && item.name === name && (isGlobalCustomRole(item) || tenant !== void 0 && item.tenant
|
|
639
|
+
return roles.some((item) => wellFormed(item) && item.name === name && (isGlobalCustomRole(item) || tenant !== void 0 && sameId(item.tenant, tenant)));
|
|
507
640
|
}
|
|
508
641
|
/**
|
|
509
642
|
* The compact JWT form of custom roles for the `memberships[].grants` claim
|
|
@@ -520,7 +653,14 @@ function customRoleClaim(roles, policy) {
|
|
|
520
653
|
const includes = Array.isArray(role.includes) ? role.includes : [];
|
|
521
654
|
const grants = Array.isArray(role.grants) ? role.grants : [];
|
|
522
655
|
for (const name of includes) entries.push(`@${name}`);
|
|
523
|
-
for (const grant of grants)
|
|
656
|
+
for (const grant of grants) {
|
|
657
|
+
if (grant.effect === "deny") {
|
|
658
|
+
entries.push(`-${keyOf(grant.permission)}`);
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const level = grant.level;
|
|
662
|
+
entries.push(typeof level === "string" && level !== "" ? `${keyOf(grant.permission)}@${level}` : keyOf(grant.permission));
|
|
663
|
+
}
|
|
524
664
|
claim[role.name] = entries;
|
|
525
665
|
}
|
|
526
666
|
return freezeDeep(claim);
|
|
@@ -704,7 +844,7 @@ function activate(policy, subject, input, now = nowSeconds()) {
|
|
|
704
844
|
if (binding === void 0 || activation === void 0) return activationDenied("unknown-role");
|
|
705
845
|
const scopes = scopeList(policy.scopes);
|
|
706
846
|
const scope = resolveScope(scopes, input.scope) ?? input.scope;
|
|
707
|
-
const eligible = (principal.memberships ?? []).find((membership) => membership.scope === scope && membership.id
|
|
847
|
+
const eligible = (principal.memberships ?? []).find((membership) => membership.scope === scope && sameId(membership.id, input.id) && !isMembershipExpired(membership, now) && (membership.eligible ?? []).includes(roleName) && withinAgrees(scopes, membership.within, input.within));
|
|
708
848
|
if (eligible === void 0) return activationDenied("no-membership");
|
|
709
849
|
const within = eligible.within;
|
|
710
850
|
if (activation.justification === "required") {
|
|
@@ -903,20 +1043,30 @@ function matchWriteScope(match, instance, current, next) {
|
|
|
903
1043
|
const moved = match(next);
|
|
904
1044
|
return moved.ok ? first : moved;
|
|
905
1045
|
}
|
|
906
|
-
function isRowPair(value) {
|
|
907
|
-
return value !== null && typeof value === "object" && "current" in value && "next" in value;
|
|
908
|
-
}
|
|
909
1046
|
function isGlobalRole(value) {
|
|
910
1047
|
return value !== null && typeof value === "object" && Reflect.get(value, "scope") === "global";
|
|
911
1048
|
}
|
|
912
1049
|
function onlyGlobal(roles) {
|
|
913
1050
|
return Array.isArray(roles) ? roles.filter(isGlobalRole) : [];
|
|
914
1051
|
}
|
|
915
|
-
|
|
1052
|
+
/** The `customRoles` option as a source: a factory is called with the resolved subject, and a throw reads none. */
|
|
1053
|
+
function roleSourceFor(option, subject, auth) {
|
|
1054
|
+
if (typeof option !== "function") return option;
|
|
1055
|
+
try {
|
|
1056
|
+
return option(subject);
|
|
1057
|
+
} catch {
|
|
1058
|
+
auth.push({
|
|
1059
|
+
reason: "source-threw",
|
|
1060
|
+
source: "customRoles"
|
|
1061
|
+
});
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function customRolesFor(source, tenants, auth, globalHeld, heldIn) {
|
|
916
1066
|
if (source === void 0) return [];
|
|
917
1067
|
const loaded = [];
|
|
918
1068
|
for (const tenant of tenants) try {
|
|
919
|
-
loaded.push(source.rolesFor(tenant));
|
|
1069
|
+
loaded.push(source.rolesFor(tenant, { held: heldIn(tenant) }));
|
|
920
1070
|
} catch {
|
|
921
1071
|
auth.push({
|
|
922
1072
|
reason: "source-threw",
|
|
@@ -924,8 +1074,8 @@ function customRolesFor(source, tenants, auth, signedIn = false) {
|
|
|
924
1074
|
});
|
|
925
1075
|
loaded.push([]);
|
|
926
1076
|
}
|
|
927
|
-
if (
|
|
928
|
-
const roles = source.globalRoles();
|
|
1077
|
+
if (globalHeld !== void 0 && source.globalRoles !== void 0) try {
|
|
1078
|
+
const roles = source.globalRoles({ held: globalHeld });
|
|
929
1079
|
loaded.push(isThenable(roles) ? roles.then(onlyGlobal) : onlyGlobal(roles));
|
|
930
1080
|
} catch {
|
|
931
1081
|
auth.push({
|
|
@@ -989,6 +1139,87 @@ function expandRoleNames(names, declared, custom, tenant) {
|
|
|
989
1139
|
function declaredRoleNames(policy) {
|
|
990
1140
|
return policy.index.declaredRoles;
|
|
991
1141
|
}
|
|
1142
|
+
const NEVER = {
|
|
1143
|
+
op: "or",
|
|
1144
|
+
conditions: []
|
|
1145
|
+
};
|
|
1146
|
+
function isUnconditionedAllow(grant, now) {
|
|
1147
|
+
return grant.where === void 0 && grant.check === void 0 && grant.closure === void 0 && grant.portable && grant.purpose === void 0 && grant.viaOnly === void 0 && grant.requires === void 0 && isActive(grant.validity, now);
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* The row condition a grant's `requires` adds: the row's instance of some
|
|
1151
|
+
* scope is one where the subject holds `key` through a role grant without a
|
|
1152
|
+
* row condition, minus the instances a deny of `key` reaches at that scope.
|
|
1153
|
+
* `undefined` when the subject holds `key` globally that way, so every row
|
|
1154
|
+
* passes. The in-process mirror of the SQL `permdock_has_permission` and
|
|
1155
|
+
* `permitted_<scope>_ids_by_permission`.
|
|
1156
|
+
*/
|
|
1157
|
+
function requirementCondition(policy, subject, keys, resource, now, customRoles, customGrants) {
|
|
1158
|
+
let where;
|
|
1159
|
+
for (const key of keys) where = combineWhere(where, keyRequirement(policy, subject, key, resource, now, customRoles, customGrants));
|
|
1160
|
+
return where;
|
|
1161
|
+
}
|
|
1162
|
+
function keyRequirement(policy, subject, key, resource, now, customRoles, customGrants) {
|
|
1163
|
+
const principal = subject.principal;
|
|
1164
|
+
if (principal === null) return NEVER;
|
|
1165
|
+
const scopes = scopeList(policy.scopes);
|
|
1166
|
+
const declared = declaredRoleNames(policy);
|
|
1167
|
+
const global = new Set(expandRoleNames(principal.roles ?? [], declared, customRoles, principal.tenant).roles);
|
|
1168
|
+
const held = (principal.memberships ?? []).filter((membership) => !isMembershipExpired(membership, now) && keepsPermission(membership, key)).map((membership) => ({
|
|
1169
|
+
membership,
|
|
1170
|
+
roles: new Set(expandRoleNames(membership.roles, declared, customRoles, tenantOf(membership, scopes)).roles)
|
|
1171
|
+
}));
|
|
1172
|
+
const allowed = /* @__PURE__ */ new Map();
|
|
1173
|
+
const denied = /* @__PURE__ */ new Map();
|
|
1174
|
+
let globalAllow = false;
|
|
1175
|
+
let globalDeny = false;
|
|
1176
|
+
const note = (effect, membership) => {
|
|
1177
|
+
if (membership === void 0) {
|
|
1178
|
+
if (effect === "allow") globalAllow = true;
|
|
1179
|
+
else globalDeny = true;
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (membership.scope === void 0 || membership.id === void 0) return;
|
|
1183
|
+
const target = effect === "allow" ? allowed : denied;
|
|
1184
|
+
const ids = target.get(membership.scope) ?? /* @__PURE__ */ new Set();
|
|
1185
|
+
ids.add(membership.id);
|
|
1186
|
+
target.set(membership.scope, ids);
|
|
1187
|
+
};
|
|
1188
|
+
const applies = (grant) => grant.effect === "allow" ? isUnconditionedAllow(grant, now) : isActive(grant.validity, now);
|
|
1189
|
+
for (const grant of policy.index.grantsByKey.get(key) ?? []) {
|
|
1190
|
+
const items = flattenGrantee(grant.to);
|
|
1191
|
+
if (grant.breakGlass !== void 0 || !applies(grant) || items.length === 0 || !items.every((item) => item.kind === "role")) continue;
|
|
1192
|
+
const roles = items.flatMap((item) => item.kind === "role" ? [item] : []);
|
|
1193
|
+
if (!roles.every((item) => item.scope !== "global" || global.has(item.role))) continue;
|
|
1194
|
+
const scoped = roles.filter((item) => item.scope !== "global");
|
|
1195
|
+
if (scoped.length === 0) {
|
|
1196
|
+
note(grant.effect);
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
for (const entry of held) if (scoped.every((item) => typeof item.scope === "string" && entry.membership.scope === item.scope && entry.roles.has(item.role))) note(grant.effect, entry.membership);
|
|
1200
|
+
}
|
|
1201
|
+
for (const { grant, role } of customGrants) {
|
|
1202
|
+
if (grant.permission.key !== key || !applies(grant)) continue;
|
|
1203
|
+
if (holdsGlobalCustomRole(principal.roles, role)) note(grant.effect);
|
|
1204
|
+
for (const entry of held) if (holdsCustomRole(entry.membership, role, scopes)) note(grant.effect, entry.membership);
|
|
1205
|
+
}
|
|
1206
|
+
if (globalAllow && !globalDeny) return;
|
|
1207
|
+
const parts = [];
|
|
1208
|
+
for (const name of resource === void 0 ? [] : partitionsOf(resource, scopes)) {
|
|
1209
|
+
const field = scopeField(resource, name, scopes);
|
|
1210
|
+
const deniedIds = denied.get(name);
|
|
1211
|
+
const ids = [...allowed.get(name) ?? []].filter((id) => deniedIds?.has(id) !== true).toSorted();
|
|
1212
|
+
if (field !== void 0 && ids.length > 0) parts.push({
|
|
1213
|
+
op: "in",
|
|
1214
|
+
field,
|
|
1215
|
+
value: ids
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
return sole(parts) ?? {
|
|
1219
|
+
op: "or",
|
|
1220
|
+
conditions: parts
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
992
1223
|
function alternativesFor(policy, permission, subject, now, env) {
|
|
993
1224
|
return listPermissions(policy.permissions).filter((leaf) => leaf.resource === permission.resource && leaf.key !== permission.key).filter((leaf) => {
|
|
994
1225
|
return evaluate(policy, subject, leaf, void 0, {
|
|
@@ -998,11 +1229,35 @@ function alternativesFor(policy, permission, subject, now, env) {
|
|
|
998
1229
|
}, {
|
|
999
1230
|
...env,
|
|
1000
1231
|
emit: false,
|
|
1001
|
-
skipAlternatives: true
|
|
1232
|
+
skipAlternatives: true,
|
|
1233
|
+
outcomeOnly: true
|
|
1002
1234
|
}).outcome === "granted";
|
|
1003
1235
|
});
|
|
1004
1236
|
}
|
|
1005
|
-
function
|
|
1237
|
+
function inheritedVerdict(policy, subject, resource, id, key, now, env) {
|
|
1238
|
+
const leaf = findPermission(policy.permissions, key);
|
|
1239
|
+
const loaded = env.relations?.row({
|
|
1240
|
+
resource,
|
|
1241
|
+
id
|
|
1242
|
+
});
|
|
1243
|
+
if (leaf === void 0 || loaded === void 0) return "relation-unavailable";
|
|
1244
|
+
if (loaded === "pending" || loaded === "failed") return "relation-unavailable";
|
|
1245
|
+
if (loaded.row === null) return false;
|
|
1246
|
+
const decision = evaluate(policy, subject, leaf, loaded.row, {
|
|
1247
|
+
trusted: true,
|
|
1248
|
+
source: "simulate",
|
|
1249
|
+
now
|
|
1250
|
+
}, {
|
|
1251
|
+
...env,
|
|
1252
|
+
emit: false,
|
|
1253
|
+
skipAlternatives: true,
|
|
1254
|
+
outcomeOnly: true
|
|
1255
|
+
});
|
|
1256
|
+
if (decision.outcome === "granted") return true;
|
|
1257
|
+
const reasons = decision.outcome === "denied" ? decision.denials.map((denial) => denial.reason) : [];
|
|
1258
|
+
return reasons.includes("relation-unavailable") ? "relation-unavailable" : reasons.includes("relation-depth") ? "relation-depth" : false;
|
|
1259
|
+
}
|
|
1260
|
+
function evaluateGrantCondition(grant, permission, current, next, subject, now, scopes, relations, permitted) {
|
|
1006
1261
|
let unknown;
|
|
1007
1262
|
const onOpaque = () => {
|
|
1008
1263
|
unknown ??= "opaque-condition";
|
|
@@ -1012,7 +1267,7 @@ function evaluateGrantCondition(grant, permission, current, next, subject, now,
|
|
|
1012
1267
|
unknown ??= "relation-unavailable";
|
|
1013
1268
|
return false;
|
|
1014
1269
|
}
|
|
1015
|
-
const verdict = resolveRelated(condition, row, subject, now, relations);
|
|
1270
|
+
const verdict = resolveRelated(condition, row, subject, now, relations, permitted);
|
|
1016
1271
|
if (verdict === "relation-depth" || verdict === "relation-unavailable") {
|
|
1017
1272
|
unknown ??= verdict;
|
|
1018
1273
|
return false;
|
|
@@ -1140,12 +1395,12 @@ function traceOf(tracer) {
|
|
|
1140
1395
|
skipped: tracer.skipped
|
|
1141
1396
|
};
|
|
1142
1397
|
}
|
|
1143
|
-
function evaluate(policy,
|
|
1398
|
+
function evaluate(policy, given, permission, data, options, env) {
|
|
1399
|
+
const subject = subjectForPermission(given, permission.key);
|
|
1144
1400
|
const now = options.now ?? nowSeconds();
|
|
1145
1401
|
const trusted = options.trusted === true;
|
|
1146
1402
|
const resource = getResource(policy.permissions, permission.resource);
|
|
1147
|
-
let current = data;
|
|
1148
|
-
let next = data;
|
|
1403
|
+
let { current, next } = rowValues(permission, data);
|
|
1149
1404
|
const tracer = options.explain === true ? {
|
|
1150
1405
|
evaluated: 0,
|
|
1151
1406
|
allows: [],
|
|
@@ -1153,21 +1408,13 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1153
1408
|
skipped: []
|
|
1154
1409
|
} : void 0;
|
|
1155
1410
|
const complete = (decision, membership) => {
|
|
1156
|
-
const final = freezeDeep(tracer === void 0 ? decision : {
|
|
1411
|
+
const final = env.outcomeOnly === true ? decision : freezeDeep(tracer === void 0 ? decision : {
|
|
1157
1412
|
...decision,
|
|
1158
1413
|
trace: traceOf(tracer)
|
|
1159
1414
|
});
|
|
1160
|
-
finish(policy,
|
|
1415
|
+
finish(policy, given, permission, current, final, options, env, trusted, membership);
|
|
1161
1416
|
return final;
|
|
1162
1417
|
};
|
|
1163
|
-
if (permission.kind === "instance" && isRowPair(data)) {
|
|
1164
|
-
current = data.current;
|
|
1165
|
-
next = data.next;
|
|
1166
|
-
}
|
|
1167
|
-
if (permission.kind === "collection") {
|
|
1168
|
-
current = void 0;
|
|
1169
|
-
next = data;
|
|
1170
|
-
}
|
|
1171
1418
|
try {
|
|
1172
1419
|
if (data !== void 0 || permission.kind === "instance" && policy.validate === "always") {
|
|
1173
1420
|
const validated = validateBoundary(permission, resource, permission.kind === "instance" && isRowPair(data) ? data.current : data, policy.validate, trusted, options.boundary ?? "manual");
|
|
@@ -1323,7 +1570,7 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1323
1570
|
const matchRow = (row) => matchScopedMembership(subject, roleItem.scope, roleItem.role, row, scopes, resource, policy.resources, now, (membership) => {
|
|
1324
1571
|
if (custom === void 0) return expandRoleNames(membership.roles, declared, env.customRoles, tenantOf(membership, scopes)).roles;
|
|
1325
1572
|
return holdsCustomRole(membership, custom, scopes) ? [custom.name] : [];
|
|
1326
|
-
}, env.team, walkRole);
|
|
1573
|
+
}, env.team, walkRole, permission.kind === "instance", options.scope);
|
|
1327
1574
|
const scopeMatch = matchWriteScope(matchRow, permission.kind === "instance", current, next);
|
|
1328
1575
|
if (!scopeMatch.ok) {
|
|
1329
1576
|
denials.push({
|
|
@@ -1347,11 +1594,12 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1347
1594
|
else skip(tracer, grant, permission.key, "validity");
|
|
1348
1595
|
continue;
|
|
1349
1596
|
}
|
|
1597
|
+
const requirement = grant.requires === void 0 || grant.effect !== "allow" ? void 0 : requirementCondition(policy, subject, grant.requires, resource, now, env.customRoles, env.customGrants);
|
|
1350
1598
|
const merged = freezeDeep(compact({
|
|
1351
1599
|
...grant,
|
|
1352
|
-
where: combineWhere(grant.where, granteeMatch.where)
|
|
1600
|
+
where: combineWhere(combineWhere(grant.where, granteeMatch.where), requirement)
|
|
1353
1601
|
}));
|
|
1354
|
-
const condition = evaluateGrantCondition(merged, permission, current, next, subject, now, scopes, env.relations);
|
|
1602
|
+
const condition = evaluateGrantCondition(merged, permission, current, next, subject, now, scopes, env.relations, (target, id, key) => inheritedVerdict(policy, given, target, id, key, now, env));
|
|
1355
1603
|
if (!condition.matched && grant.effect === "deny" && isUnevaluable(condition.reason) && grantCoversField(grant.fields, options.field, grant.effect)) {
|
|
1356
1604
|
if (condition.reason === "closure-error") emitSafe(env.listeners.error, condition.cause ?? /* @__PURE__ */ new Error("closure-error"), env.listeners);
|
|
1357
1605
|
tracer?.denies.push(matchedOf(merged, permission.key));
|
|
@@ -1442,7 +1690,12 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1442
1690
|
}],
|
|
1443
1691
|
alternatives: env.skipAlternatives ? [] : alternativesFor(policy, permission, subject, now, env)
|
|
1444
1692
|
});
|
|
1445
|
-
const
|
|
1693
|
+
const unscoped = subject.actor !== void 0 && ceiling === void 0;
|
|
1694
|
+
let delegationMiss = coveredByDelegation(permission, subject.delegation, resourceIdOf(current), unscoped);
|
|
1695
|
+
const requirementsCovered = allows.filter((candidate) => coversRequirements(policy.permissions, candidate.grant.requires, subject.delegation, resourceIdOf(current), unscoped));
|
|
1696
|
+
let usable = delegationMiss === void 0 ? requirementsCovered : requirementsCovered.filter((candidate) => requiresStandIn(candidate.grant));
|
|
1697
|
+
if (delegationMiss === "not-delegated" && usable.length > 0) delegationMiss = void 0;
|
|
1698
|
+
if (delegationMiss === void 0 && usable.length === 0) delegationMiss = "not-delegated";
|
|
1446
1699
|
if (delegationMiss !== void 0) return complete({
|
|
1447
1700
|
outcome: "denied",
|
|
1448
1701
|
denials: [{
|
|
@@ -1452,12 +1705,12 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1452
1705
|
alternatives: env.skipAlternatives ? [] : alternativesFor(policy, permission, subject, now, env)
|
|
1453
1706
|
});
|
|
1454
1707
|
const approvalPolicies = env.approvalPolicies;
|
|
1455
|
-
if (approvalPolicies === "failed" &&
|
|
1708
|
+
if (approvalPolicies === "failed" && usable.length > 0) return complete({
|
|
1456
1709
|
outcome: "denied",
|
|
1457
1710
|
denials: [{
|
|
1458
1711
|
role: null,
|
|
1459
1712
|
reason: "approval",
|
|
1460
|
-
detail:
|
|
1713
|
+
detail: APPROVAL_POLICY_UNAVAILABLE
|
|
1461
1714
|
}],
|
|
1462
1715
|
alternatives: []
|
|
1463
1716
|
});
|
|
@@ -1474,7 +1727,7 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1474
1727
|
let matchedAllow;
|
|
1475
1728
|
let approval;
|
|
1476
1729
|
let quotaState = {};
|
|
1477
|
-
for (const candidate of
|
|
1730
|
+
for (const candidate of usable) {
|
|
1478
1731
|
approval = approvalOf(candidate);
|
|
1479
1732
|
const consume = !requiresApproval(approval) && shouldConsumeQuota(options.source, env.simulated);
|
|
1480
1733
|
const quota = applyQuota({
|
|
@@ -1509,9 +1762,9 @@ function evaluate(policy, subject, permission, data, options, env) {
|
|
|
1509
1762
|
denials: quotaDenials.length > 0 ? quotaDenials : denials,
|
|
1510
1763
|
alternatives: env.skipAlternatives ? [] : alternativesFor(policy, permission, subject, now, env)
|
|
1511
1764
|
});
|
|
1512
|
-
const resourceId = permission.kind === "collection" ? "*" : current
|
|
1765
|
+
const resourceId = permission.kind === "collection" ? "*" : rowIdOf(current, resource?.id);
|
|
1513
1766
|
const version = approval !== void 0 && approval !== "human" && approval.staleOn === "resource-change" && resource?.version !== void 0 ? versionOf(current, resource.version) : void 0;
|
|
1514
|
-
const token = env.simulated ? "pd1.simulated" : decisionToken({
|
|
1767
|
+
const token = env.simulated ? "pd1.simulated" : env.outcomeOnly === true ? "pd1.outcome" : decisionToken({
|
|
1515
1768
|
key: permission.key,
|
|
1516
1769
|
resourceId,
|
|
1517
1770
|
principal: subject.principal,
|
|
@@ -1647,7 +1900,7 @@ function audiencesOf(policy, names) {
|
|
|
1647
1900
|
/** Whether `membership` is the instance itself or one of its ancestors named in `within`. */
|
|
1648
1901
|
function coversInstance(membership, scopes, scope, id, within) {
|
|
1649
1902
|
if (membership.scope === void 0 || membership.id === void 0) return false;
|
|
1650
|
-
if (membership.scope === scope) return membership.id
|
|
1903
|
+
if (membership.scope === scope) return sameId(membership.id, id);
|
|
1651
1904
|
if (!scopeChain(scopes, scope).slice(1).includes(membership.scope)) return false;
|
|
1652
1905
|
const wanted = within === void 0 ? void 0 : Object.entries(within).find(([key]) => resolveScope(scopes, key) === membership.scope)?.[1] ?? void 0;
|
|
1653
1906
|
return wanted !== void 0 && scopeIdOf(membership, membership.scope) === wanted;
|
|
@@ -1661,7 +1914,22 @@ function rolesAtInstance(policy, principal, scopes, scope, id, within, now) {
|
|
|
1661
1914
|
}
|
|
1662
1915
|
/** Whether the principal holds `role` on a live membership of exactly this instance. */
|
|
1663
1916
|
function holdsAt(principal, role, scope, id, now) {
|
|
1664
|
-
return (principal.memberships ?? []).some((membership) => membership.scope === scope && membership.id
|
|
1917
|
+
return (principal.memberships ?? []).some((membership) => membership.scope === scope && sameId(membership.id, id) && !isMembershipExpired(membership, now) && membership.roles.includes(role));
|
|
1918
|
+
}
|
|
1919
|
+
function includedRoles(role) {
|
|
1920
|
+
return isReadonlyArray(role.includes) ? role.includes.filter((name) => typeof name === "string") : [];
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* The membership kinds a custom role may be held through: those every
|
|
1924
|
+
* included declared role with `for` allows. `undefined` when none restricts.
|
|
1925
|
+
*/
|
|
1926
|
+
function customForKinds(policy, role) {
|
|
1927
|
+
let kinds;
|
|
1928
|
+
for (const name of includedRoles(role)) {
|
|
1929
|
+
const listed = bindingOf(policy, name)?.for;
|
|
1930
|
+
if (listed !== void 0) kinds = kinds === void 0 ? listed : kinds.filter((kind) => listed.includes(kind));
|
|
1931
|
+
}
|
|
1932
|
+
return kinds;
|
|
1665
1933
|
}
|
|
1666
1934
|
function conflictsWith(policy, role) {
|
|
1667
1935
|
const out = new Set(bindingOf(policy, role)?.exclusiveWith ?? []);
|
|
@@ -1707,7 +1975,7 @@ function heldAnchor(principal, scopes, scope, id, now) {
|
|
|
1707
1975
|
* The one rule check behind `permdock.decideRoleChange`. The actor is always
|
|
1708
1976
|
* the instance's own principal; nothing in `change` can stand in for it.
|
|
1709
1977
|
*/
|
|
1710
|
-
function decideRoleChange(policy, principal, scopes, change, authority, now, options = {}) {
|
|
1978
|
+
function decideRoleChange(policy, principal, scopes, change, customRoles, authority, now, options = {}) {
|
|
1711
1979
|
const name = typeof change?.role === "string" ? change.role : change?.role?.key;
|
|
1712
1980
|
const denials = [];
|
|
1713
1981
|
const deny = (reason, detail) => {
|
|
@@ -1732,40 +2000,54 @@ function decideRoleChange(policy, principal, scopes, change, authority, now, opt
|
|
|
1732
2000
|
}
|
|
1733
2001
|
const kind = change?.kind;
|
|
1734
2002
|
const target = change?.target;
|
|
1735
|
-
|
|
2003
|
+
const global = change?.scope === "global";
|
|
2004
|
+
if (typeof name !== "string" || kind !== "assign" && kind !== "revoke" && kind !== "transfer" || (global ? change.id !== void 0 : typeof change.id !== "string" || change.id === "") || target === null || typeof target !== "object" || typeof target.id !== "string" || target.id === "") {
|
|
1736
2005
|
deny("validation");
|
|
1737
2006
|
return done(null);
|
|
1738
2007
|
}
|
|
2008
|
+
const id = change.id ?? "";
|
|
1739
2009
|
const binding = bindingOf(policy, name);
|
|
1740
|
-
|
|
2010
|
+
const declared = declaredRoleNames$1(policy).has(name);
|
|
2011
|
+
const named = declared || global ? [] : tenantCustomRoles(policy, customRoles).filter((role) => role.name === name);
|
|
2012
|
+
if (!declared && named.length === 0) {
|
|
1741
2013
|
deny("unknown-role");
|
|
1742
2014
|
return done(null);
|
|
1743
2015
|
}
|
|
1744
|
-
const scope = resolveScope(scopes, change.scope);
|
|
1745
|
-
const
|
|
1746
|
-
const
|
|
2016
|
+
const scope = global ? "global" : resolveScope(scopes, change.scope);
|
|
2017
|
+
const customAt = named.map((role) => customRoleScope(role, scopes));
|
|
2018
|
+
const on = declared ? binding?.on ?? findRole(policy.vocabulary?.roles, name)?.on : customAt.includes(scope) ? scope : customAt[0];
|
|
2019
|
+
const heldAt = typeof on === "string" ? resolveScope(scopes, on) : global ? "global" : void 0;
|
|
1747
2020
|
if (scope === void 0 || heldAt === void 0 || heldAt !== scope) {
|
|
1748
2021
|
deny("scope", { expected: heldAt ?? null });
|
|
1749
2022
|
return done(null);
|
|
1750
2023
|
}
|
|
1751
|
-
const root = scopes[0]?.name;
|
|
1752
|
-
const supplied = change.within === void 0 ? void 0 : Object.entries(change.within).find(([key]) => resolveScope(scopes, key) === root)?.[1];
|
|
1753
2024
|
let tenant;
|
|
1754
2025
|
let within = change.within;
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
const
|
|
1758
|
-
const
|
|
1759
|
-
if (
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2026
|
+
let custom;
|
|
2027
|
+
if (!global) {
|
|
2028
|
+
const root = scopes[0]?.name;
|
|
2029
|
+
const supplied = change.within === void 0 ? void 0 : Object.entries(change.within).find(([key]) => resolveScope(scopes, key) === root)?.[1];
|
|
2030
|
+
if (scope === root) tenant = id;
|
|
2031
|
+
else {
|
|
2032
|
+
const anchor = heldAnchor(principal, scopes, scope, id, now);
|
|
2033
|
+
const held = anchor === void 0 ? void 0 : tenantOf(anchor, scopes);
|
|
2034
|
+
if (held !== void 0) {
|
|
2035
|
+
tenant = supplied === void 0 || supplied === held ? held : void 0;
|
|
2036
|
+
within = anchor?.within;
|
|
2037
|
+
} else if (options.trusted === true) tenant = supplied;
|
|
2038
|
+
}
|
|
2039
|
+
if (tenant === void 0) {
|
|
2040
|
+
deny("no-membership", {
|
|
2041
|
+
scope,
|
|
2042
|
+
id
|
|
2043
|
+
});
|
|
2044
|
+
return done(null);
|
|
2045
|
+
}
|
|
2046
|
+
const found = tenant;
|
|
2047
|
+
custom = named.find((role) => sameId(role.tenant, found) && customRoleScope(role, scopes) === scope && sameId(customRoleId(role) ?? id, id));
|
|
2048
|
+
}
|
|
2049
|
+
if (!declared && custom === void 0) {
|
|
2050
|
+
deny("unknown-role");
|
|
1769
2051
|
return done(null);
|
|
1770
2052
|
}
|
|
1771
2053
|
if (target.managedBy === "idp") {
|
|
@@ -1778,24 +2060,26 @@ function decideRoleChange(policy, principal, scopes, change, authority, now, opt
|
|
|
1778
2060
|
let by = null;
|
|
1779
2061
|
if (kind === "transfer") {
|
|
1780
2062
|
if (self) deny("self-demotion");
|
|
1781
|
-
if (holdsAt(principal, name, scope,
|
|
2063
|
+
if (global ? (principal.roles ?? []).includes(name) : holdsAt(principal, name, scope, id, now)) by = name;
|
|
1782
2064
|
else deny("not-assignable-by", { holds: false });
|
|
1783
2065
|
} else {
|
|
1784
2066
|
if (self) deny(kind === "revoke" ? "self-demotion" : "not-assignable-by", { self: true });
|
|
1785
2067
|
const allowed = authority(tenant);
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
2068
|
+
const revocable = kind === "revoke" && custom !== void 0 && allowed.inert?.has(name);
|
|
2069
|
+
if (!allowed.assignable.has(name) && revocable !== true) deny("not-assignable-by");
|
|
2070
|
+
else if (custom === void 0 && usesAssigns(policy) && !allowed.manage) {
|
|
2071
|
+
const held = global ? rankRoles(policy, (principal.roles ?? []).filter((role) => declaredRoleNames$1(policy).has(role))) : rolesAtInstance(policy, principal, scopes, scope, id, within, now);
|
|
1789
2072
|
by = held.find((role) => bindingOf(policy, role)?.assigns?.includes(name) === true) ?? null;
|
|
1790
2073
|
if (by === null) deny("not-assignable-by", { held });
|
|
1791
2074
|
}
|
|
1792
2075
|
}
|
|
1793
2076
|
if (kind !== "revoke" && !already) {
|
|
1794
|
-
|
|
2077
|
+
const kinds = custom === void 0 ? binding?.for : customForKinds(policy, custom);
|
|
2078
|
+
if (kinds !== void 0 && (target.via === void 0 || !kinds.includes(target.via))) deny("not-allowed-for-membership", {
|
|
1795
2079
|
via: target.via ?? null,
|
|
1796
|
-
for:
|
|
2080
|
+
for: kinds
|
|
1797
2081
|
});
|
|
1798
|
-
const conflicts = conflictsWith(policy, name);
|
|
2082
|
+
const conflicts = custom === void 0 ? conflictsWith(policy, name) : new Set(includedRoles(custom).flatMap((include) => Array.from(conflictsWith(policy, include))));
|
|
1799
2083
|
const clash = targetRoles.filter((role) => conflicts.has(role));
|
|
1800
2084
|
if (clash.length > 0) deny("conflicting-role", { with: clash });
|
|
1801
2085
|
}
|
|
@@ -1849,7 +2133,7 @@ async function discoverRole(item, allow, ctx) {
|
|
|
1849
2133
|
ctx.incomplete();
|
|
1850
2134
|
return;
|
|
1851
2135
|
}
|
|
1852
|
-
const key =
|
|
2136
|
+
const key = scopeField(ctx.resource, item.scope, ctx.scopes);
|
|
1853
2137
|
const id = key === void 0 ? void 0 : relationId(ownGet(ctx.row, key));
|
|
1854
2138
|
if (id === void 0) return;
|
|
1855
2139
|
const members = await ctx.list(item.scope, id);
|
|
@@ -2002,6 +2286,7 @@ function discover(grant, item, ctx) {
|
|
|
2002
2286
|
case "plan":
|
|
2003
2287
|
case "actor":
|
|
2004
2288
|
case "assurance":
|
|
2289
|
+
case "inherit":
|
|
2005
2290
|
ctx.incomplete();
|
|
2006
2291
|
return Promise.resolve();
|
|
2007
2292
|
default: return unknownKind(item, ctx);
|
|
@@ -2069,7 +2354,7 @@ async function whoCan(input) {
|
|
|
2069
2354
|
};
|
|
2070
2355
|
await Promise.all((policy.index.grantsByKey.get(permission.key) ?? []).flatMap((grant) => flattenGrantee(grant.to).map((item) => discover(grant, item, ctx))));
|
|
2071
2356
|
const root = rootScope(scopes);
|
|
2072
|
-
const tenantKey = root === void 0 ? void 0 :
|
|
2357
|
+
const tenantKey = root === void 0 ? void 0 : scopeField(getResource(policy.permissions, permission.resource), root, scopes);
|
|
2073
2358
|
const tenant = tenantKey === void 0 ? void 0 : relationId(ownGet(row, tenantKey));
|
|
2074
2359
|
const env = {
|
|
2075
2360
|
emit: false,
|
|
@@ -2161,8 +2446,8 @@ function heldRoleNames(policy, subject, tenant, only, now) {
|
|
|
2161
2446
|
if (only?.scope !== void 0 && scope === void 0) return [];
|
|
2162
2447
|
const names = new Set(scope === void 0 ? subject.principal.roles ?? [] : []);
|
|
2163
2448
|
for (const membership of subject.principal.memberships ?? []) {
|
|
2164
|
-
if (isMembershipExpired(membership, now)) continue;
|
|
2165
|
-
if (!(scope === void 0 ? tenantOf(membership, scopes) === tenant : membership.scope === scope && (only?.id === void 0 || membership.id
|
|
2449
|
+
if (isMembershipExpired(membership, now) || membership.keep !== void 0) continue;
|
|
2450
|
+
if (!(scope === void 0 ? tenantOf(membership, scopes) === tenant : membership.scope === scope && (only?.id === void 0 || sameId(membership.id, only.id)))) continue;
|
|
2166
2451
|
for (const role of membership.roles) names.add(role);
|
|
2167
2452
|
}
|
|
2168
2453
|
return rankRoles(policy, [...names]);
|
|
@@ -2183,11 +2468,12 @@ function roleLeaf(policy, name) {
|
|
|
2183
2468
|
function graphAware(grant, where) {
|
|
2184
2469
|
const combined = combineWhere(grant.where, where);
|
|
2185
2470
|
const graph = hasConditionOp(combined, "related");
|
|
2471
|
+
const inherits = flattenGrantee(grant.to).some((item) => item.kind === "inherit");
|
|
2186
2472
|
return freezeDeep(compact({
|
|
2187
2473
|
...grant,
|
|
2188
2474
|
where: combined,
|
|
2189
2475
|
portable: graph ? false : grant.portable,
|
|
2190
|
-
graph: graph && grant.portable ? true : void 0
|
|
2476
|
+
graph: graph && grant.portable && !inherits ? true : void 0
|
|
2191
2477
|
}));
|
|
2192
2478
|
}
|
|
2193
2479
|
/**
|
|
@@ -2215,33 +2501,35 @@ function collectSnapshotGrants(policy, subject, customRoles, now = nowSeconds(),
|
|
|
2215
2501
|
roles: new Set(expandRoleNames(membership.roles, declared, customRoles, tenantOf(membership, scopes)).roles)
|
|
2216
2502
|
}));
|
|
2217
2503
|
const holds = (entry, item) => entry.roles.has(item.role) && (typeof item.scope === "string" ? entry.membership.scope === item.scope : entry.membership.on !== void 0);
|
|
2504
|
+
const requirementOf = (grant, resource) => grant.requires === void 0 || grant.effect !== "allow" ? void 0 : requirementCondition(policy, subject, grant.requires, resource, now, customRoles, customGrants);
|
|
2218
2505
|
const out = [];
|
|
2219
2506
|
for (const grant of grantList(policy)) {
|
|
2507
|
+
if (mode === "held" && !coversRequirements(policy.permissions, grant.requires, subject.delegation)) continue;
|
|
2220
2508
|
const resource = getResource(policy.permissions, grant.permission.resource);
|
|
2221
2509
|
const match = matchGrantee(grant.to, subject, now, resource, scopes, policy.resources);
|
|
2222
2510
|
if (skip(grant, match)) continue;
|
|
2223
2511
|
const roleItems = flattenGrantee(grant.to).filter((item) => item.kind === "role");
|
|
2224
2512
|
if (roleItems.length > 0 && subject.principal === null) continue;
|
|
2225
2513
|
if (!roleItems.every((item) => item.scope !== "global" || global.roles.includes(item.role))) continue;
|
|
2226
|
-
const merged = graphAware(grant, match.where);
|
|
2514
|
+
const merged = graphAware(grant, combineWhere(match.where, requirementOf(grant, resource)));
|
|
2227
2515
|
const scoped = roleItems.filter((item) => item.scope !== "global");
|
|
2228
2516
|
if (scoped.length === 0) {
|
|
2229
2517
|
out.push({ grant: merged });
|
|
2230
2518
|
continue;
|
|
2231
2519
|
}
|
|
2232
|
-
for (const entry of held) if (scoped.every((item) => item.kind === "role" && holds(entry, item))) out.push({
|
|
2520
|
+
for (const entry of held) if (keepsPermission(entry.membership, grant.permission.key) && scoped.every((item) => item.kind === "role" && holds(entry, item))) out.push({
|
|
2233
2521
|
grant: merged,
|
|
2234
2522
|
membership: entry.membership
|
|
2235
2523
|
});
|
|
2236
2524
|
}
|
|
2237
2525
|
for (const { grant, role } of customGrants) {
|
|
2238
2526
|
const globalHeld = holdsGlobalCustomRole(subject.principal?.roles, role);
|
|
2239
|
-
const holders = held.filter((entry) => holdsCustomRole(entry.membership, role, scopes));
|
|
2527
|
+
const holders = held.filter((entry) => keepsPermission(entry.membership, grant.permission.key) && holdsCustomRole(entry.membership, role, scopes));
|
|
2240
2528
|
if (holders.length === 0 && !globalHeld) continue;
|
|
2241
2529
|
const resource = getResource(policy.permissions, grant.permission.resource);
|
|
2242
2530
|
const match = matchGrantee(grant.to, subject, now, resource, scopes, policy.resources);
|
|
2243
2531
|
if (skip(grant, match)) continue;
|
|
2244
|
-
const merged = graphAware(grant, match.where);
|
|
2532
|
+
const merged = graphAware(grant, combineWhere(match.where, requirementOf(grant, resource)));
|
|
2245
2533
|
if (globalHeld) out.push({ grant: merged });
|
|
2246
2534
|
for (const entry of holders) out.push({
|
|
2247
2535
|
grant: merged,
|
|
@@ -2262,6 +2550,20 @@ function assignableCandidates(policy) {
|
|
|
2262
2550
|
return rankRoles(policy, [...new Set(names)]).map((name) => roleLeaf(policy, name));
|
|
2263
2551
|
}
|
|
2264
2552
|
/**
|
|
2553
|
+
* The levels a held allow of a permission covers: its own `level`, every
|
|
2554
|
+
* level when it has no row condition, or the levels whose condition equals
|
|
2555
|
+
* its condition.
|
|
2556
|
+
*/
|
|
2557
|
+
function heldLevels(policy, grant, names) {
|
|
2558
|
+
if (grant.level !== void 0) return [grant.level];
|
|
2559
|
+
if (grant.where === void 0) return names;
|
|
2560
|
+
const held = canonicalJson(grant.where);
|
|
2561
|
+
return names.filter((name) => {
|
|
2562
|
+
const condition = levelCondition(policy, grant.permission.resource, name);
|
|
2563
|
+
return condition !== void 0 && canonicalJson(condition) === held;
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
/**
|
|
2265
2567
|
* What the subject may hand out in `tenant`: declared assignable roles and the
|
|
2266
2568
|
* tenant custom-role ceiling, narrowed by `allowed` (`RoleSource.assignable`),
|
|
2267
2569
|
* then intersected with the permissions the subject holds there. Holding a
|
|
@@ -2272,7 +2574,9 @@ function assignableIn(policy, subject, customRoles, customGrants, tenant, allowe
|
|
|
2272
2574
|
if (principal === null) return {
|
|
2273
2575
|
roles: [],
|
|
2274
2576
|
permissions: [],
|
|
2275
|
-
|
|
2577
|
+
levels: {},
|
|
2578
|
+
manage: false,
|
|
2579
|
+
inert: []
|
|
2276
2580
|
};
|
|
2277
2581
|
const scopes = scopeList(policy.scopes);
|
|
2278
2582
|
const scoped = tenant === void 0 ? subject : freezeDeep(compact({
|
|
@@ -2282,7 +2586,8 @@ function assignableIn(policy, subject, customRoles, customGrants, tenant, allowe
|
|
|
2282
2586
|
tenant: resolveActiveTenant(principal, tenant, scopes, now)
|
|
2283
2587
|
})
|
|
2284
2588
|
}));
|
|
2285
|
-
const
|
|
2589
|
+
const heldGrants = collectSnapshotGrants(policy, scoped, customRoles, now, customGrants).filter((item) => item.grant.effect === "allow" && (item.membership === void 0 || !global && tenantOf(item.membership, scopes) === tenant && !isMembershipExpired(item.membership, now))).map((item) => item.grant);
|
|
2590
|
+
const heldKeys = new Set(heldGrants.map((grant) => grant.permission.key));
|
|
2286
2591
|
const heldNames = heldRoleNames(policy, subject, tenant, void 0, now);
|
|
2287
2592
|
const quiet = {
|
|
2288
2593
|
emit: false,
|
|
@@ -2313,10 +2618,72 @@ function assignableIn(policy, subject, customRoles, customGrants, tenant, allowe
|
|
|
2313
2618
|
});
|
|
2314
2619
|
const root = global ? "global" : rootScope(scopes);
|
|
2315
2620
|
const ceiling = new Set((root === void 0 ? [] : ceilingGrants(policy, root, allowed)).map((grant) => grant.permission.key));
|
|
2621
|
+
const permissions = listPermissions(policy.permissions).filter((leaf) => ceiling.has(leaf.key) && (manage || heldKeys.has(leaf.key)));
|
|
2622
|
+
const offeredLevels = (key, names) => {
|
|
2623
|
+
if (manage) return names;
|
|
2624
|
+
const reached = new Set(heldGrants.filter((grant) => grant.permission.key === key).flatMap((grant) => heldLevels(policy, grant, names)));
|
|
2625
|
+
return names.filter((name) => reached.has(name));
|
|
2626
|
+
};
|
|
2627
|
+
const levels = {};
|
|
2628
|
+
for (const leaf of permissions) {
|
|
2629
|
+
const names = leaf.kind === "instance" ? levelNames(policy, leaf.resource) : [];
|
|
2630
|
+
if (names.length > 0) levels[leaf.key] = offeredLevels(leaf.key, names);
|
|
2631
|
+
}
|
|
2632
|
+
const ceilingKeys = /* @__PURE__ */ new Map();
|
|
2633
|
+
const mayHandOut = (scope, grant) => {
|
|
2634
|
+
const key = grant.permission.key;
|
|
2635
|
+
let keys = ceilingKeys.get(scope);
|
|
2636
|
+
if (keys === void 0) {
|
|
2637
|
+
keys = new Set(ceilingGrants(policy, scope, allowed).map((item) => item.permission.key));
|
|
2638
|
+
ceilingKeys.set(scope, keys);
|
|
2639
|
+
}
|
|
2640
|
+
if (!keys.has(key) || !(manage || heldKeys.has(key))) return false;
|
|
2641
|
+
const names = grant.permission.kind === "instance" ? levelNames(policy, grant.permission.resource) : [];
|
|
2642
|
+
if (names.length === 0) return true;
|
|
2643
|
+
const offered = offeredLevels(key, names);
|
|
2644
|
+
return heldLevels(policy, grant, names).every((name) => offered.includes(name));
|
|
2645
|
+
};
|
|
2646
|
+
const custom = global || tenant === void 0 ? {
|
|
2647
|
+
roles: [],
|
|
2648
|
+
inert: []
|
|
2649
|
+
} : assignableCustomRoles(policy, customRoles, tenant, manage ? void 0 : roles, mayHandOut);
|
|
2316
2650
|
return {
|
|
2317
|
-
roles,
|
|
2318
|
-
permissions
|
|
2319
|
-
|
|
2651
|
+
roles: [...roles, ...custom.roles],
|
|
2652
|
+
permissions,
|
|
2653
|
+
levels,
|
|
2654
|
+
manage,
|
|
2655
|
+
inert: custom.inert
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
/**
|
|
2659
|
+
* The tenant's custom roles the subject may hand out: held at a scope it may
|
|
2660
|
+
* assign declared roles at (any scope with `meta.manageRoles`), allowing at
|
|
2661
|
+
* least one permission after the ceiling, and only permissions and levels it
|
|
2662
|
+
* may hand out at that scope. A role that allows nothing is `inert`.
|
|
2663
|
+
*/
|
|
2664
|
+
function assignableCustomRoles(policy, customRoles, tenant, declared, mayHandOut) {
|
|
2665
|
+
const scopes = scopeList(policy.scopes);
|
|
2666
|
+
const root = rootScope(scopes);
|
|
2667
|
+
const assignsAt = declared === void 0 ? void 0 : new Set(declared.map((leaf) => leaf.on === void 0 ? root : resolveScope(scopes, leaf.on)));
|
|
2668
|
+
const out = /* @__PURE__ */ new Map();
|
|
2669
|
+
const inert = /* @__PURE__ */ new Set();
|
|
2670
|
+
for (const role of tenantCustomRoles(policy, customRoles)) {
|
|
2671
|
+
if (role.tenant !== tenant || out.has(role.name)) continue;
|
|
2672
|
+
const at = customRoleScope(role, scopes);
|
|
2673
|
+
if (at === void 0 || assignsAt !== void 0 && !assignsAt.has(at)) continue;
|
|
2674
|
+
const { grants } = resolveCustomRole(policy, role);
|
|
2675
|
+
if (!grants.some((grant) => grant.effect === "allow")) {
|
|
2676
|
+
inert.add(role.name);
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
if (grants.every((grant) => grant.effect === "deny" || mayHandOut(at, grant))) out.set(role.name, synthesiseRole(role.name, {
|
|
2680
|
+
on: at,
|
|
2681
|
+
assignable: true
|
|
2682
|
+
}));
|
|
2683
|
+
}
|
|
2684
|
+
return {
|
|
2685
|
+
roles: [...out.values()].toSorted((a, b) => a.key.localeCompare(b.key)),
|
|
2686
|
+
inert: [...inert].filter((name) => !out.has(name)).toSorted()
|
|
2320
2687
|
};
|
|
2321
2688
|
}
|
|
2322
2689
|
/** The one path from a resolved subject to a `Snapshot`; pure apart from the default clock. */
|
|
@@ -2340,14 +2707,16 @@ function snapshotOf(policy, subject, options) {
|
|
|
2340
2707
|
now: Math.floor(now),
|
|
2341
2708
|
vocabulary: policy.vocabulary,
|
|
2342
2709
|
scopes: snapshotScopes(policy),
|
|
2710
|
+
idOf: (name) => getResource(policy.permissions, name)?.id,
|
|
2343
2711
|
delegated: delegatedPermissions(policy.delegations, subject, new Set(roles), now),
|
|
2344
2712
|
assignable: (tenant) => {
|
|
2345
2713
|
const found = assignableIn(policy, subject, options.customRoles, customGrants, tenant, options.assignable?.get(tenant), now);
|
|
2346
|
-
return {
|
|
2714
|
+
return compact({
|
|
2347
2715
|
tenant,
|
|
2348
2716
|
roles: found.roles.map((leaf) => leaf.key),
|
|
2349
|
-
permissions: found.permissions
|
|
2350
|
-
|
|
2717
|
+
permissions: found.permissions,
|
|
2718
|
+
levels: Object.keys(found.levels).length === 0 ? void 0 : found.levels
|
|
2719
|
+
});
|
|
2351
2720
|
}
|
|
2352
2721
|
}));
|
|
2353
2722
|
}
|
|
@@ -2356,12 +2725,18 @@ function snapshotScopes(policy) {
|
|
|
2356
2725
|
if (policy.scopes.length === 0) return;
|
|
2357
2726
|
const resources = [...policy.resources.values()];
|
|
2358
2727
|
return policy.scopes.map((scope) => {
|
|
2359
|
-
const
|
|
2728
|
+
const nodes = resources.filter((node) => partitionsOf(node, policy.scopes).includes(scope.name));
|
|
2729
|
+
const partitioned = nodes.map((node) => node.name);
|
|
2730
|
+
const fields = Object.fromEntries(nodes.flatMap((node) => {
|
|
2731
|
+
const field = scopeField(node, scope.name, policy.scopes);
|
|
2732
|
+
return field === void 0 || field === scope.key ? [] : [[node.name, field]];
|
|
2733
|
+
}));
|
|
2360
2734
|
return compact({
|
|
2361
2735
|
name: scope.name,
|
|
2362
2736
|
key: scope.key ?? "",
|
|
2363
2737
|
within: scope.within,
|
|
2364
|
-
resources: partitioned.length === 0 ? void 0 : partitioned
|
|
2738
|
+
resources: partitioned.length === 0 ? void 0 : partitioned,
|
|
2739
|
+
fields: Object.keys(fields).length === 0 ? void 0 : fields
|
|
2365
2740
|
});
|
|
2366
2741
|
});
|
|
2367
2742
|
}
|
|
@@ -2373,6 +2748,7 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2373
2748
|
const relationCache = envBase.relationCache ?? /* @__PURE__ */ new Map();
|
|
2374
2749
|
const relations = relationReader(envBase.relations, relationCache, policy.resources);
|
|
2375
2750
|
const assignableAt = (tenant) => assignableIn(policy, subject, envBase.customRoles, customGrants, tenant, tenant === void 0 ? void 0 : envBase.assignable?.get(tenant));
|
|
2751
|
+
const assignableGlobal = () => assignableIn(policy, subject, envBase.customRoles, customGrants, void 0, void 0, nowSeconds(), true);
|
|
2376
2752
|
const envFor = (emit, skipAlternatives = false) => ({
|
|
2377
2753
|
emit,
|
|
2378
2754
|
simulated: envBase.simulated,
|
|
@@ -2457,7 +2833,8 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2457
2833
|
scope: permission.scope,
|
|
2458
2834
|
resource: resourceRef,
|
|
2459
2835
|
subject,
|
|
2460
|
-
message: deniedMessage(permission.key, subject.principal?.id, decision.denials, decision.alternatives.map((leaf) => leaf.key))
|
|
2836
|
+
message: deniedMessage(permission.key, subject.principal?.id, decision.denials, decision.alternatives.map((leaf) => leaf.key)),
|
|
2837
|
+
...data === void 0 || resource === void 0 ? {} : { disclosure: resource.disclosure }
|
|
2461
2838
|
});
|
|
2462
2839
|
};
|
|
2463
2840
|
const instance = {
|
|
@@ -2473,7 +2850,10 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2473
2850
|
let granted = 0;
|
|
2474
2851
|
let denied = 0;
|
|
2475
2852
|
let approvalRequired = 0;
|
|
2476
|
-
const quiet =
|
|
2853
|
+
const quiet = {
|
|
2854
|
+
...envFor(false, true),
|
|
2855
|
+
outcomeOnly: true
|
|
2856
|
+
};
|
|
2477
2857
|
const trusted = options?.trusted ?? true;
|
|
2478
2858
|
const decideOptions = {
|
|
2479
2859
|
...options,
|
|
@@ -2533,6 +2913,7 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2533
2913
|
graph: envBase.relations !== void 0,
|
|
2534
2914
|
scopes: scopeList(policy.scopes),
|
|
2535
2915
|
partitioned: (name, key) => relatesTo(getResource(policy.permissions, permission.resource), key, name, scopeList(policy.scopes)),
|
|
2916
|
+
fieldOf: (name) => scopeField(getResource(policy.permissions, permission.resource), name, scopeList(policy.scopes)),
|
|
2536
2917
|
tenant: subject.principal?.tenant,
|
|
2537
2918
|
team,
|
|
2538
2919
|
now: nowSeconds(),
|
|
@@ -2615,7 +2996,7 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2615
2996
|
relationCache
|
|
2616
2997
|
}, team);
|
|
2617
2998
|
}),
|
|
2618
|
-
snapshot(options) {
|
|
2999
|
+
snapshot: ((options) => {
|
|
2619
3000
|
const snapshot = snapshotOf(policy, subject, compact({
|
|
2620
3001
|
customRoles: envBase.customRoles,
|
|
2621
3002
|
customGrants,
|
|
@@ -2626,7 +3007,7 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2626
3007
|
}));
|
|
2627
3008
|
if (options?.signer !== void 0) return signSnapshot(snapshot, options.signer, options.audience);
|
|
2628
3009
|
return snapshot;
|
|
2629
|
-
},
|
|
3010
|
+
}),
|
|
2630
3011
|
on(event, handler) {
|
|
2631
3012
|
const set = listeners[event];
|
|
2632
3013
|
set.add(handler);
|
|
@@ -2680,31 +3061,119 @@ function buildInstance(policy, subject, envBase, team) {
|
|
|
2680
3061
|
return audiencesOf(policy, heldRoleNames(policy, subject, subject.principal?.tenant));
|
|
2681
3062
|
},
|
|
2682
3063
|
assignableRoles(options) {
|
|
3064
|
+
if (options?.scope === "global") return assignableGlobal().roles.filter((leaf) => leaf.on === void 0);
|
|
2683
3065
|
return assignableAt(options?.tenant ?? subject.principal?.tenant).roles;
|
|
2684
3066
|
},
|
|
2685
3067
|
assignablePermissions(options) {
|
|
2686
|
-
if (options?.scope === "global") return
|
|
3068
|
+
if (options?.scope === "global") return assignableGlobal().permissions;
|
|
2687
3069
|
const tenant = options?.tenant ?? subject.principal?.tenant;
|
|
2688
3070
|
return tenant === void 0 ? [] : assignableAt(tenant).permissions;
|
|
2689
3071
|
},
|
|
3072
|
+
assignableLevels(permission, options) {
|
|
3073
|
+
const of = (found) => (Object.hasOwn(found.levels, permission.key) ? found.levels[permission.key] : void 0) ?? [];
|
|
3074
|
+
if (options?.scope === "global") return of(assignableGlobal());
|
|
3075
|
+
const tenant = options?.tenant ?? subject.principal?.tenant;
|
|
3076
|
+
return tenant === void 0 ? [] : of(assignableAt(tenant));
|
|
3077
|
+
},
|
|
2690
3078
|
decideRoleChange(change, options) {
|
|
2691
|
-
return decideRoleChange(policy, subject.principal, scopeList(policy.scopes), change, (tenant) => {
|
|
2692
|
-
const found = assignableAt(tenant);
|
|
3079
|
+
return decideRoleChange(policy, subject.principal, scopeList(policy.scopes), change, envBase.customRoles, (tenant) => {
|
|
3080
|
+
const found = tenant === void 0 ? assignableGlobal() : assignableAt(tenant);
|
|
2693
3081
|
return {
|
|
2694
3082
|
assignable: new Set(found.roles.map((leaf) => leaf.key)),
|
|
2695
|
-
manage: found.manage
|
|
3083
|
+
manage: found.manage,
|
|
3084
|
+
inert: new Set(found.inert)
|
|
2696
3085
|
};
|
|
2697
3086
|
}, nowSeconds(), options);
|
|
2698
3087
|
},
|
|
2699
3088
|
activate(input) {
|
|
2700
3089
|
return activate(policy, subject, input, nowSeconds());
|
|
2701
3090
|
},
|
|
3091
|
+
derive(options) {
|
|
3092
|
+
const scopes = scopeList(policy.scopes);
|
|
3093
|
+
const tenants = tenantsOf(subject.principal, scopes);
|
|
3094
|
+
const auth = [];
|
|
3095
|
+
const named = options.customRoles === void 0 ? void 0 : roleSourceFor(options.customRoles, subject, auth);
|
|
3096
|
+
const roleSource = options.customRoles === void 0 ? envBase.roleSource : named;
|
|
3097
|
+
const roles = options.customRoles === void 0 ? envBase.customRoles : customRolesFor(named, tenants, auth, subject.principal === null ? void 0 : subject.principal.roles ?? [], (tenant) => heldRoleNamesIn(subject.principal, scopes, tenant));
|
|
3098
|
+
const names = options.customRoles === void 0 ? envBase.assignable : assignableNamesFor(named, tenants, auth);
|
|
3099
|
+
const approvals = options.approvalPolicies === void 0 ? envBase.approvalPolicies : approvalPoliciesFor(policy, options.approvalPolicies, tenants, auth);
|
|
3100
|
+
const relationSource = options.relations ?? envBase.relations;
|
|
3101
|
+
const build = (loadedRoles, loadedNames, loadedApprovals) => buildInstance(policy, subject, compact({
|
|
3102
|
+
...envBase,
|
|
3103
|
+
customRoles: loadedRoles,
|
|
3104
|
+
roleSource,
|
|
3105
|
+
assignable: loadedNames,
|
|
3106
|
+
approvalPolicies: loadedApprovals,
|
|
3107
|
+
relations: relationSource,
|
|
3108
|
+
relationCache: options.relations === void 0 ? relationCache : void 0,
|
|
3109
|
+
queuedAuth: auth,
|
|
3110
|
+
queuedErrors: []
|
|
3111
|
+
}), team);
|
|
3112
|
+
if (isThenable(roles) || isThenable(names) || isThenable(approvals)) return Promise.all([
|
|
3113
|
+
roles,
|
|
3114
|
+
names,
|
|
3115
|
+
approvals
|
|
3116
|
+
]).then(([loadedRoles, loadedNames, loadedApprovals]) => build(loadedRoles, loadedNames, loadedApprovals));
|
|
3117
|
+
return build(roles, names, approvals);
|
|
3118
|
+
},
|
|
2702
3119
|
subject
|
|
2703
3120
|
};
|
|
2704
3121
|
return Object.freeze(instance);
|
|
2705
3122
|
}
|
|
2706
3123
|
//#endregion
|
|
3124
|
+
//#region src/core/oauth-scopes.ts
|
|
3125
|
+
/**
|
|
3126
|
+
* `delegation` with every coarse OAuth scope the policy declares in
|
|
3127
|
+
* `oauthScopes` followed by the scopes of the permissions it covers, so the
|
|
3128
|
+
* delegation checks downstream match them. The token's scopes stay first and
|
|
3129
|
+
* unchanged; an undeclared scope expands to nothing.
|
|
3130
|
+
*/
|
|
3131
|
+
function expandOAuthScopes(policy, delegation) {
|
|
3132
|
+
const declared = policy.oauthScopes;
|
|
3133
|
+
const scopes = delegation?.scopes;
|
|
3134
|
+
if (declared === void 0 || delegation === void 0 || scopes === void 0) return delegation;
|
|
3135
|
+
const out = new Set(scopes);
|
|
3136
|
+
for (const entry of declared) if (scopes.includes(entry.scope)) for (const scope of entry.scopes) out.add(scope);
|
|
3137
|
+
return out.size === scopes.length ? delegation : {
|
|
3138
|
+
...delegation,
|
|
3139
|
+
scopes: [...out]
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
/** The coarse scopes the policy declares that cover `permission`, in declaration order. */
|
|
3143
|
+
function coarseScopesFor(policy, permission) {
|
|
3144
|
+
return (policy.oauthScopes ?? []).filter((entry) => entry.permissions.includes(permission.key)).map((entry) => entry.scope);
|
|
3145
|
+
}
|
|
3146
|
+
/** Every token scope that reaches `permission`: its own, any it was renamed from, and the coarse scopes covering it. */
|
|
3147
|
+
function scopesReaching(policy, permission) {
|
|
3148
|
+
return [
|
|
3149
|
+
permission.scope,
|
|
3150
|
+
...formerScopes(permission),
|
|
3151
|
+
...coarseScopesFor(policy, permission)
|
|
3152
|
+
];
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* The scope an `insufficient_scope` challenge names for `permission`: the
|
|
3156
|
+
* first coarse scope that covers it, which the authorization server issues,
|
|
3157
|
+
* else the permission's own scope.
|
|
3158
|
+
*/
|
|
3159
|
+
function challengeScope(policy, permission) {
|
|
3160
|
+
return coarseScopesFor(policy, permission)[0] ?? permission.scope;
|
|
3161
|
+
}
|
|
3162
|
+
//#endregion
|
|
2707
3163
|
//#region src/core/resolve-subject.ts
|
|
3164
|
+
/**
|
|
3165
|
+
* The tenant a user API key holds its owner to (`Credential.tenant`): only
|
|
3166
|
+
* the memberships inside it count, and no global role.
|
|
3167
|
+
*/
|
|
3168
|
+
function heldTenantOf(principal) {
|
|
3169
|
+
const credential = principal["credential"];
|
|
3170
|
+
if (credential === null || typeof credential !== "object" || !Object.hasOwn(credential, "tenant") || !Object.hasOwn(credential, "kind")) return;
|
|
3171
|
+
const record = credential;
|
|
3172
|
+
return record.kind === "user" && typeof record.tenant === "string" && record.tenant !== "" ? record.tenant : void 0;
|
|
3173
|
+
}
|
|
3174
|
+
function outsideHeld(held, options) {
|
|
3175
|
+
return held !== void 0 && options.tenant !== void 0 && options.tenant !== held;
|
|
3176
|
+
}
|
|
2708
3177
|
function assemblePrincipal(policy, user, options, auth) {
|
|
2709
3178
|
let principal;
|
|
2710
3179
|
let context = {};
|
|
@@ -2712,6 +3181,7 @@ function assemblePrincipal(policy, user, options, auth) {
|
|
|
2712
3181
|
let delegation = options.delegation;
|
|
2713
3182
|
let session = options.session;
|
|
2714
3183
|
let expiresAt = options.expiresAt;
|
|
3184
|
+
let liveSession;
|
|
2715
3185
|
try {
|
|
2716
3186
|
if (user === null || user === void 0) principal = policy.subject(user);
|
|
2717
3187
|
else if (isSubject(user)) {
|
|
@@ -2721,6 +3191,7 @@ function assemblePrincipal(policy, user, options, auth) {
|
|
|
2721
3191
|
delegation = user.delegation ?? delegation;
|
|
2722
3192
|
session = user.session ?? session;
|
|
2723
3193
|
expiresAt = user.expiresAt ?? expiresAt;
|
|
3194
|
+
liveSession = user.liveSession === true ? true : void 0;
|
|
2724
3195
|
} else if (isPrincipal(user)) principal = user;
|
|
2725
3196
|
else principal = policy.subject(user);
|
|
2726
3197
|
} catch {
|
|
@@ -2731,30 +3202,73 @@ function assemblePrincipal(policy, user, options, auth) {
|
|
|
2731
3202
|
let live = false;
|
|
2732
3203
|
const source = options.memberships === void 0 ? void 0 : asMembershipSource(options.memberships);
|
|
2733
3204
|
const fromClaims = source?.claimsFirst === true && principal?.memberships !== void 0 && principal.membershipsTruncated !== true;
|
|
2734
|
-
|
|
2735
|
-
live = true;
|
|
3205
|
+
const read = (from, of) => {
|
|
2736
3206
|
try {
|
|
2737
|
-
|
|
2738
|
-
id:
|
|
2739
|
-
kind:
|
|
3207
|
+
return from.membershipsFor(compact({
|
|
3208
|
+
id: of.id,
|
|
3209
|
+
kind: of.kind
|
|
2740
3210
|
}), compact({ tenant: options.tenant }));
|
|
2741
3211
|
} catch {
|
|
2742
3212
|
auth.push({
|
|
2743
3213
|
reason: "source-threw",
|
|
2744
3214
|
source: "memberships"
|
|
2745
3215
|
});
|
|
2746
|
-
|
|
3216
|
+
return [];
|
|
2747
3217
|
}
|
|
3218
|
+
};
|
|
3219
|
+
let stale;
|
|
3220
|
+
if (principal !== null && source !== void 0 && !fromClaims) {
|
|
3221
|
+
live = true;
|
|
3222
|
+
memberships = read(source, principal);
|
|
3223
|
+
} else if (principal?.memberships !== void 0 && fromClaims && source.onStale === "reread" && source.version !== void 0) {
|
|
3224
|
+
const reread = rereadStale(source, principal, principal.memberships, () => read(source, principal), auth);
|
|
3225
|
+
memberships = reread.memberships;
|
|
3226
|
+
stale = reread.stale;
|
|
2748
3227
|
}
|
|
2749
|
-
return {
|
|
3228
|
+
return compact({
|
|
2750
3229
|
principal,
|
|
2751
3230
|
context: contextResult,
|
|
2752
3231
|
actor,
|
|
2753
|
-
delegation,
|
|
3232
|
+
delegation: expandOAuthScopes(policy, delegation),
|
|
2754
3233
|
session,
|
|
2755
3234
|
expiresAt,
|
|
2756
3235
|
memberships,
|
|
2757
|
-
live
|
|
3236
|
+
live,
|
|
3237
|
+
stale,
|
|
3238
|
+
liveSession: principal === null ? void 0 : liveSession
|
|
3239
|
+
});
|
|
3240
|
+
}
|
|
3241
|
+
function versionQuery(principal) {
|
|
3242
|
+
return compact({
|
|
3243
|
+
id: principal.id,
|
|
3244
|
+
roles: principal.roles,
|
|
3245
|
+
memberships: principal.memberships
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3248
|
+
function rereadStale(source, principal, claims, read, auth) {
|
|
3249
|
+
const claimed = principal.authzVersion;
|
|
3250
|
+
const judge = (current) => typeof claimed === "number" && current !== void 0 && claimed >= current ? "fresh" : "reread";
|
|
3251
|
+
const unknown = () => {
|
|
3252
|
+
auth.push({
|
|
3253
|
+
reason: "source-threw",
|
|
3254
|
+
source: "memberships"
|
|
3255
|
+
});
|
|
3256
|
+
return "unknown";
|
|
3257
|
+
};
|
|
3258
|
+
let freshness;
|
|
3259
|
+
try {
|
|
3260
|
+
const current = source.version?.(versionQuery(principal));
|
|
3261
|
+
freshness = isThenable(current) ? Promise.resolve(current).then(judge, unknown) : judge(current);
|
|
3262
|
+
} catch {
|
|
3263
|
+
freshness = unknown();
|
|
3264
|
+
}
|
|
3265
|
+
if (isThenable(freshness)) return {
|
|
3266
|
+
memberships: freshness.then((found) => found === "reread" ? read() : claims),
|
|
3267
|
+
stale: freshness.then((found) => found === "unknown")
|
|
3268
|
+
};
|
|
3269
|
+
return {
|
|
3270
|
+
memberships: freshness === "reread" ? read() : claims,
|
|
3271
|
+
stale: freshness === "unknown"
|
|
2758
3272
|
};
|
|
2759
3273
|
}
|
|
2760
3274
|
function finishSubject(policy, assembled, context, input, options, extra) {
|
|
@@ -2766,7 +3280,10 @@ function finishSubject(policy, assembled, context, input, options, extra) {
|
|
|
2766
3280
|
expiresAt: assembled.expiresAt
|
|
2767
3281
|
}));
|
|
2768
3282
|
const scopes = scopeList(policy.scopes);
|
|
2769
|
-
const
|
|
3283
|
+
const held = heldTenantOf(assembled.principal);
|
|
3284
|
+
const kinds = applyRoleKinds(policy, assembled.principal.roles, normalizeMemberships(input, scopes));
|
|
3285
|
+
const roles = held === void 0 ? kinds.roles : [];
|
|
3286
|
+
const memberships = held === void 0 ? kinds.memberships : outsideHeld(held, options) ? [] : kinds.memberships.filter((membership) => tenantOf(membership, scopes) === held);
|
|
2770
3287
|
const plans = [.../* @__PURE__ */ new Set([...assembled.principal.plans ?? [], ...extra.plans])];
|
|
2771
3288
|
const withMemberships = freezeCopy(compact({
|
|
2772
3289
|
...assembled.principal,
|
|
@@ -2782,11 +3299,14 @@ function finishSubject(policy, assembled, context, input, options, extra) {
|
|
|
2782
3299
|
context: freezeCopy(context),
|
|
2783
3300
|
session: assembled.session,
|
|
2784
3301
|
expiresAt: assembled.expiresAt,
|
|
2785
|
-
stale: extra.stale ? true : void 0
|
|
3302
|
+
stale: extra.stale ? true : void 0,
|
|
3303
|
+
liveSession: assembled.liveSession
|
|
2786
3304
|
}));
|
|
2787
3305
|
}
|
|
2788
3306
|
function activeTenantOf(policy, assembled, memberships, options) {
|
|
2789
3307
|
if (assembled.principal === null) return;
|
|
3308
|
+
const held = heldTenantOf(assembled.principal);
|
|
3309
|
+
if (held !== void 0) return outsideHeld(held, options) ? void 0 : held;
|
|
2790
3310
|
return resolveActiveTenant({
|
|
2791
3311
|
...assembled.principal,
|
|
2792
3312
|
memberships
|
|
@@ -2807,12 +3327,13 @@ function settle(value, fallback, onError) {
|
|
|
2807
3327
|
function staleness(policy, assembled, options, auth) {
|
|
2808
3328
|
const principal = assembled.principal;
|
|
2809
3329
|
if ((policy.fresh ?? []).length === 0 || principal === null || assembled.live) return false;
|
|
3330
|
+
if (assembled.stale !== void 0) return assembled.stale;
|
|
2810
3331
|
const source = options.memberships === void 0 ? void 0 : asMembershipSource(options.memberships);
|
|
2811
3332
|
const claimed = principal.authzVersion;
|
|
2812
3333
|
if (source?.version === void 0 || typeof claimed !== "number") return true;
|
|
2813
3334
|
const compare = (current) => current === void 0 || claimed < current;
|
|
2814
3335
|
try {
|
|
2815
|
-
const current = source.version(
|
|
3336
|
+
const current = source.version(versionQuery(principal));
|
|
2816
3337
|
return isThenable(current) ? settle(Promise.resolve(current).then(compare), true, () => {
|
|
2817
3338
|
auth.push({
|
|
2818
3339
|
reason: "source-threw",
|
|
@@ -2832,7 +3353,7 @@ function cleanNames(list) {
|
|
|
2832
3353
|
}
|
|
2833
3354
|
function entitlementsOf(assembled, tenant, options, auth) {
|
|
2834
3355
|
const source = options.entitlements;
|
|
2835
|
-
if (source === void 0 || assembled.principal === null) return [];
|
|
3356
|
+
if (source === void 0 || assembled.principal === null || outsideHeld(heldTenantOf(assembled.principal), options)) return [];
|
|
2836
3357
|
try {
|
|
2837
3358
|
const found = source.entitlementsFor({ id: assembled.principal.id }, compact({ tenant }));
|
|
2838
3359
|
return isThenable(found) ? settle(Promise.resolve(found).then(cleanNames), [], () => {
|
|
@@ -2921,9 +3442,11 @@ function hostedPolicy(policy, source) {
|
|
|
2921
3442
|
}
|
|
2922
3443
|
function instantiate(codePolicy, subject, options, auth) {
|
|
2923
3444
|
const { policy, errors } = hostedPolicy(codePolicy, options.policies);
|
|
2924
|
-
const
|
|
2925
|
-
const
|
|
2926
|
-
const
|
|
3445
|
+
const scopes = scopeList(policy.scopes);
|
|
3446
|
+
const tenants = tenantsOf(subject.principal, scopes);
|
|
3447
|
+
const roleSource = roleSourceFor(options.customRoles, subject, auth);
|
|
3448
|
+
const customRoles = customRolesFor(roleSource, tenants, auth, subject.principal === null ? void 0 : subject.principal.roles ?? [], (tenant) => heldRoleNamesIn(subject.principal, scopes, tenant));
|
|
3449
|
+
const assignable = assignableNamesFor(roleSource, tenants, auth);
|
|
2927
3450
|
const approvals = approvalPoliciesFor(policy, options.approvalPolicies, tenants, auth);
|
|
2928
3451
|
const build = (roles, names, approvalPolicies) => buildInstance(policy, subject, compact({
|
|
2929
3452
|
customRoles: roles,
|
|
@@ -2931,7 +3454,7 @@ function instantiate(codePolicy, subject, options, auth) {
|
|
|
2931
3454
|
limits: options.limits,
|
|
2932
3455
|
limitCache: /* @__PURE__ */ new Map(),
|
|
2933
3456
|
simulated: false,
|
|
2934
|
-
roleSource
|
|
3457
|
+
roleSource,
|
|
2935
3458
|
assignable: names,
|
|
2936
3459
|
queuedAuth: auth,
|
|
2937
3460
|
queuedErrors: errors,
|
|
@@ -2953,4 +3476,4 @@ function createPermDock(policy, user, options = {}) {
|
|
|
2953
3476
|
return instantiate(policy, subject, options, auth);
|
|
2954
3477
|
}
|
|
2955
3478
|
//#endregion
|
|
2956
|
-
export { approvalPoliciesFor as _,
|
|
3479
|
+
export { countHolders as C, memoryApprovalPolicies as D, approvalPoliciesFor as E, validateApprovalPolicy as O, composeMemberships as S, APPROVAL_POLICY_UNAVAILABLE as T, leveled as _, collectSnapshotGrants as a, validateCustomRole as b, rankRoles as c, breakGlass as d, supportAccess as f, customRoleScope as g, customRoleClaim as h, scopesReaching as i, usesAssigns as l, ceilingGrants as m, resolveSubject as n, snapshotOf as o, andWhere as p, challengeScope as r, snapshotScopes as s, createPermDock as t, declaredRoleNames as u, membershipsClaim as v, isExternallyManaged as w, claimsFirst as x, resolveCustomRole as y };
|