permdock 0.0.0 → 0.1.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 +268 -1
- package/dist/a2a/index.d.ts +178 -0
- package/dist/a2a/index.js +271 -0
- package/dist/ai-sdk/index.d.ts +74 -0
- package/dist/ai-sdk/index.js +137 -0
- package/dist/approval-required-TzNB9oQA.js +6 -0
- package/dist/approvals/index.d.ts +3 -0
- package/dist/approvals/index.js +4 -0
- package/dist/approvals-DaxY-Bdg.js +218 -0
- package/dist/approvers-aH0PJkmf.js +20 -0
- package/dist/arazzo-CTeVuXr5.js +70 -0
- package/dist/arazzo-aoTkqYsJ.js +54 -0
- package/dist/authzen/index.d.ts +48 -0
- package/dist/authzen/index.js +336 -0
- package/dist/better-auth/index.d.ts +92 -0
- package/dist/better-auth/index.js +324 -0
- package/dist/budget-JqcRqyhK.d.ts +11 -0
- package/dist/budget-NbEtbJC_.js +11 -0
- package/dist/capability-B68OFnZx.js +171 -0
- package/dist/capability-BDG_nkxt.d.ts +99 -0
- package/dist/catalog/index.d.ts +12 -0
- package/dist/catalog/index.js +2 -0
- package/dist/catalog-B0E6bggc.js +48 -0
- package/dist/catalog-H-vFGwWK.js +55 -0
- package/dist/catalog-doc-BSo_Q_-6.js +298 -0
- package/dist/catalog-idsHc0vo.js +2 -0
- package/dist/catalog-path-CnI0XTFK.js +12 -0
- package/dist/claude-agent/index.d.ts +81 -0
- package/dist/claude-agent/index.js +83 -0
- package/dist/clerk/index.d.ts +56 -0
- package/dist/clerk/index.js +280 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +20 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.js +5 -0
- package/dist/cloud/index.d.ts +91 -0
- package/dist/cloud/index.js +390 -0
- package/dist/cloud-BNQTrmrr.js +50 -0
- package/dist/cloud-Cuxr6Czw.js +117 -0
- package/dist/collect-B7_TIfmg.js +49 -0
- package/dist/collect-O-tOtbnk.js +350 -0
- package/dist/collect-fRPAQIWb.js +2 -0
- package/dist/commands-BMh_Etm2.js +93 -0
- package/dist/compact-y6vfu2_f.js +19 -0
- package/dist/compare-pfyVnBjE.js +12 -0
- package/dist/compile-CQbvyIBr.d.ts +30 -0
- package/dist/compile-D22iwvuV.js +558 -0
- package/dist/config-BPYmjTOp.js +26 -0
- package/dist/connection-JvqwHODC.js +194 -0
- package/dist/context-CKEAabd7.js +73 -0
- package/dist/context-gtAwAILx.js +6 -0
- package/dist/convex/index.d.ts +40 -0
- package/dist/convex/index.js +55 -0
- package/dist/create-BKF3dgFk.d.ts +162 -0
- package/dist/create-CSkhDU9T.js +222 -0
- package/dist/credential-B_rkUNlY.js +326 -0
- package/dist/credentials-BgRX3WEg.js +268 -0
- package/dist/describe-Bt1YD5uS.js +99 -0
- package/dist/describe-DernyItf.d.ts +37 -0
- package/dist/diff-BUzf5Poi.js +48 -0
- package/dist/diff-x5Il8LVa.js +418 -0
- package/dist/digest-B3kc_PgQ.d.ts +19 -0
- package/dist/digest-BI-6aa6d.js +27 -0
- package/dist/doctor-BGle8Gqi.js +1314 -0
- package/dist/doctor-Bqo6FwUa.js +53 -0
- package/dist/drizzle/index.d.ts +72 -0
- package/dist/drizzle/index.js +160 -0
- package/dist/duration-xK2jGFVc.js +25 -0
- package/dist/elysia/index.d.ts +71 -0
- package/dist/elysia/index.js +103 -0
- package/dist/errors-DSMnAVNa.js +43 -0
- package/dist/errors-DbZ1YOnb.d.ts +138 -0
- package/dist/errors-Iy71s7Oj.js +177 -0
- package/dist/errors-m3u9TmDu.js +27 -0
- package/dist/evaluations-DsAU9ppl.js +479 -0
- package/dist/eve/index.d.ts +95 -0
- package/dist/eve/index.js +181 -0
- package/dist/express/index.d.ts +46 -0
- package/dist/express/index.js +108 -0
- package/dist/fastify/index.d.ts +56 -0
- package/dist/fastify/index.js +141 -0
- package/dist/files-B_wAGYmt.js +121 -0
- package/dist/fixtures-Cvaw0gDG.js +64 -0
- package/dist/freeze-y1_jQL4v.js +31 -0
- package/dist/from-snapshot-BMGnhmgv.js +552 -0
- package/dist/generate-CH7ThJ14.js +173 -0
- package/dist/grantee-Bv04_Vzf.js +521 -0
- package/dist/headers-B5RRv3Xm.js +6 -0
- package/dist/headers-C8SHQccD.d.ts +6 -0
- package/dist/helpers-DnSOrhB9.js +682 -0
- package/dist/hono/index.d.ts +70 -0
- package/dist/hono/index.js +131 -0
- package/dist/hosted-BwKQ1BTA.js +265 -0
- package/dist/http-DPuDaChb.d.ts +13 -0
- package/dist/http-GgVApX1k.js +111 -0
- package/dist/index-CltcQ4Bf.d.ts +138 -0
- package/dist/index-LF_hS-yn.d.ts +177 -0
- package/dist/index-fG6NSXlc.d.ts +9 -0
- package/dist/index-xIxJLwOQ.d.ts +415 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +21 -0
- package/dist/jwt/index.d.ts +87 -0
- package/dist/jwt/index.js +571 -0
- package/dist/kernel-YOGCWNLf.js +211 -0
- package/dist/kysely/index.d.ts +77 -0
- package/dist/kysely/index.js +129 -0
- package/dist/load-BZ24CpZP.js +69 -0
- package/dist/load-QOOm1z70.js +2 -0
- package/dist/load-jose-BL6LH4bL.js +11 -0
- package/dist/lru-8q4WZjBV.js +28 -0
- package/dist/manifest-DDQbC6K0.d.ts +118 -0
- package/dist/map-DowSO96j.js +158 -0
- package/dist/map-claims-B_6I1Rn-.js +360 -0
- package/dist/map-error-RJ3ZsUyA.js +16 -0
- package/dist/markers-B0hYLvMD.js +60 -0
- package/dist/may-use-CnFEjVEp.js +37 -0
- package/dist/mcp/index.d.ts +127 -0
- package/dist/mcp/index.js +563 -0
- package/dist/nest/index.d.ts +80 -0
- package/dist/nest/index.js +270 -0
- package/dist/next/client.d.ts +24 -0
- package/dist/next/client.js +31 -0
- package/dist/next/index.d.ts +110 -0
- package/dist/next/index.js +195 -0
- package/dist/next/plugin.d.ts +14 -0
- package/dist/next/plugin.js +2 -0
- package/dist/node/index.d.ts +43 -0
- package/dist/node/index.js +66 -0
- package/dist/openai/index.d.ts +71 -0
- package/dist/openai/index.js +105 -0
- package/dist/openapi/index.d.ts +93 -0
- package/dist/openapi/index.js +3 -0
- package/dist/openapi-CwePaF7C.js +164 -0
- package/dist/openapi-DWD0Uc_4.js +250 -0
- package/dist/openapi-DcqM8nU7.js +306 -0
- package/dist/openapi-import-BWRUNNeW.js +5959 -0
- package/dist/openapi-schema-D8j_T9tO.js +6902 -0
- package/dist/orpc/index.d.ts +64 -0
- package/dist/orpc/index.js +188 -0
- package/dist/otel/index.d.ts +13 -0
- package/dist/otel/index.js +202 -0
- package/dist/package-root-zGkxO3oN.js +30 -0
- package/dist/parse-DPfIvFMU.js +135 -0
- package/dist/pdp/index.d.ts +22 -0
- package/dist/pdp/index.js +795 -0
- package/dist/peer-BOr_kJE1.js +31 -0
- package/dist/permdock-yoZ7EjxX.js +2956 -0
- package/dist/permissions-B4apnahQ.d.ts +185 -0
- package/dist/permissions-B8vCg_bI.d.ts +52 -0
- package/dist/permissions-CofRqesP.js +142 -0
- package/dist/permissions-DC7GROPA.js +600 -0
- package/dist/pg-4GJWDUxG.js +953 -0
- package/dist/pins-CqE1jZuF.js +12 -0
- package/dist/plugin-4gr7IxFJ.js +65 -0
- package/dist/policy-ANp36PnI.js +971 -0
- package/dist/policy-CInmNGkM.d.ts +1959 -0
- package/dist/policy-Cq2fB8Q5.d.ts +20 -0
- package/dist/prisma/index.d.ts +135 -0
- package/dist/prisma/index.js +430 -0
- package/dist/problem-Cgp9-KZA.js +226 -0
- package/dist/prompt-CXYhLfsS.js +32 -0
- package/dist/protected-B4K3BKoX.js +138 -0
- package/dist/protected-BuPAycJT.d.ts +26 -0
- package/dist/protected-wY7nXhY9.d.ts +23 -0
- package/dist/provider-_wx46HpB.js +48 -0
- package/dist/provider-wTAUda2v.d.ts +6 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/dist/react/provider-client.d.ts +2 -0
- package/dist/react/provider-client.js +3 -0
- package/dist/react-native/index.d.ts +42 -0
- package/dist/react-native/index.js +174 -0
- package/dist/refs-ChLSxbST.js +90 -0
- package/dist/relations-r4soIFZB.js +426 -0
- package/dist/revocations-Cy06JNx2.d.ts +21 -0
- package/dist/rls-BOFU3chG.js +233 -0
- package/dist/rls-CJVja9Ww.js +2119 -0
- package/dist/rls-graph-DxQ39PJj.js +371 -0
- package/dist/rls-helpers-BxRuif0l.js +1346 -0
- package/dist/rls-import-CXV6LBlS.js +942 -0
- package/dist/rls-migrate-DrFPbf8p.js +366 -0
- package/dist/row-check-BajTgQZ-.d.ts +43 -0
- package/dist/run-CJ6UvS3a.js +155 -0
- package/dist/runtime-DP9ttFh1.js +27 -0
- package/dist/saas-BRrZzJkM.js +1193 -0
- package/dist/schema-P8yoma__.js +296 -0
- package/dist/scim/index.d.ts +2 -0
- package/dist/scim/index.js +2 -0
- package/dist/scim-9NFa0XVZ.js +1445 -0
- package/dist/scopes-cKVp_WTw.js +212 -0
- package/dist/server/index.d.ts +123 -0
- package/dist/server/index.js +8 -0
- package/dist/sha256-BO0swRXL.js +150 -0
- package/dist/sink-Bd9PSyfq.js +141 -0
- package/dist/sink-Cfr9-7YC.d.ts +104 -0
- package/dist/skills-C_lpV4Jx.js +50 -0
- package/dist/skills-Dx3QLnMY.js +110 -0
- package/dist/snapshot-DE8dE_hz.js +1051 -0
- package/dist/solid/index.d.ts +63 -0
- package/dist/solid/index.js +197 -0
- package/dist/source-DoBBYJi2.js +6 -0
- package/dist/sources-DOpW5Ila.d.ts +347 -0
- package/dist/sources-DVHM3Mwq.js +284 -0
- package/dist/sql-Bll1d7y0.js +22 -0
- package/dist/src-CTNT-PBW.js +430 -0
- package/dist/ssf/index.d.ts +3 -0
- package/dist/ssf/index.js +622 -0
- package/dist/store-Bq90395i.d.ts +27 -0
- package/dist/store-C6hFW6ta.js +508 -0
- package/dist/stores-CkG2HrSg.js +140 -0
- package/dist/stores-CvOnu_2T.d.ts +41 -0
- package/dist/stream-BhMfZOwr.d.ts +12 -0
- package/dist/stream-Q_xlB6Ko.js +52 -0
- package/dist/style-CTm1AXDT.js +22 -0
- package/dist/subject-C6dLxqHU.js +24 -0
- package/dist/subject-settings-xt7V845g.js +82 -0
- package/dist/supabase/index.d.ts +67 -0
- package/dist/supabase/index.js +4 -0
- package/dist/supabase/middleware.d.ts +77 -0
- package/dist/supabase/middleware.js +68 -0
- package/dist/supabase-D8UlBA7Z.js +83 -0
- package/dist/supabase-DTxGtYdi.js +796 -0
- package/dist/supabase-hook-DW-xP4hU.js +2 -0
- package/dist/supabase-hook-D_OqGb_V.js +1528 -0
- package/dist/svelte/Protected.svelte +34 -0
- package/dist/svelte/index.d.ts +10 -0
- package/dist/svelte/index.js +53 -0
- package/dist/svelte/runtime.d.ts +6 -0
- package/dist/svelte/runtime.js +3 -0
- package/dist/svelte/source.d.ts +10 -0
- package/dist/svelte/source.js +8 -0
- package/dist/terminal/index.d.ts +171 -0
- package/dist/terminal/index.js +821 -0
- package/dist/testing/index.d.ts +656 -0
- package/dist/testing/index.js +3208 -0
- package/dist/testing/saas/index.d.ts +194 -0
- package/dist/testing/saas/index.js +3 -0
- package/dist/testing/saas/permissions.d.ts +2 -0
- package/dist/testing/saas/permissions.js +2 -0
- package/dist/text-diff-BdkkYSbi.js +15 -0
- package/dist/thenable-okc2xDsj.js +10 -0
- package/dist/timeout-D51niuQP.js +8 -0
- package/dist/trpc/index.d.ts +80 -0
- package/dist/trpc/index.js +205 -0
- package/dist/types-B8w5PFtS.d.ts +141 -0
- package/dist/types-B9dmiiab.d.ts +66 -0
- package/dist/types-BBK-exvJ.d.ts +177 -0
- package/dist/types-BvBk1wgn.d.ts +118 -0
- package/dist/types-CxquUByI.d.ts +9 -0
- package/dist/types-D6Z6-rKJ.d.ts +312 -0
- package/dist/types-DbWdEfcm.d.ts +87 -0
- package/dist/types-bva3i4Gf.d.ts +98 -0
- package/dist/types-i4BAOvdb.d.ts +97 -0
- package/dist/unplugin/index.d.ts +7 -0
- package/dist/unplugin/index.js +30 -0
- package/dist/usage-1ozMubLY.js +42 -0
- package/dist/usage-CUnK6Ou9.js +530 -0
- package/dist/usage-PyEor6GA.js +2 -0
- package/dist/validity-D0uRmJKJ.js +224 -0
- package/dist/verifier-B90agnhl.js +461 -0
- package/dist/version-CnSh33Fj.js +16 -0
- package/dist/vocabulary-Dk0fs-Jk.d.ts +56 -0
- package/dist/vue/index.d.ts +57 -0
- package/dist/vue/index.js +203 -0
- package/dist/webmcp/index.d.ts +75 -0
- package/dist/webmcp/index.js +313 -0
- package/package.json +423 -4
- package/rulesets/permdock.yaml +29 -0
- package/schemas/catalog-v1.json +507 -0
- package/schemas/openapi/oas-3.1.json +1298 -0
- package/schemas/openapi/oas-3.2.json +1516 -0
- package/schemas/openapi/overlay-1.1.json +79 -0
- package/schemas/supabase-claims-v1.json +139 -0
- package/schemas/supabase-manifest-v1.json +217 -0
- package/skills/permdock/SKILL.md +66 -0
- package/skills/permdock-agents/SKILL.md +77 -0
- package/skills/permdock-agents/references/runtimes.md +211 -0
- package/skills/permdock-approvals/SKILL.md +80 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +83 -0
- package/skills/permdock-audit/SKILL.md +67 -0
- package/skills/permdock-credentials/SKILL.md +55 -0
- package/skills/permdock-credentials/references/tokens-keys-links.md +127 -0
- package/skills/permdock-data/SKILL.md +57 -0
- package/skills/permdock-data/references/queries-and-rls.md +54 -0
- package/skills/permdock-data/references/relationships.md +65 -0
- package/skills/permdock-tenancy/SKILL.md +77 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +83 -0
- package/skills/permdock-wire/SKILL.md +116 -0
- package/skills/permdock-wire/references/adapters.md +480 -0
- package/skills/permdock-wire/references/policy-options.md +43 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { t as compact } from "./compact-y6vfu2_f.js";
|
|
2
|
+
import { i as credentialTenant, n as credentialPolicyViolation, o as parseCredential, r as credentialSubject } from "./credential-B_rkUNlY.js";
|
|
3
|
+
import { t as bytesToBase64Url } from "./sha256-BO0swRXL.js";
|
|
4
|
+
import { n as isThenable } from "./thenable-okc2xDsj.js";
|
|
5
|
+
import { t as anonymousSubject } from "./subject-C6dLxqHU.js";
|
|
6
|
+
import { r as credentialEvent } from "./sink-Bd9PSyfq.js";
|
|
7
|
+
//#region src/server/credentials.ts
|
|
8
|
+
const PREFIX = "pdk_";
|
|
9
|
+
const ID = /^[\w-]{1,128}$/u;
|
|
10
|
+
const TAIL = /^[A-Za-z\d]{49}$/u;
|
|
11
|
+
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
12
|
+
const SECRET_LENGTH = 43;
|
|
13
|
+
const CHECKSUM_LENGTH = 6;
|
|
14
|
+
const MAX_KEY = 182;
|
|
15
|
+
const CRC_TABLE = (() => {
|
|
16
|
+
const table = /* @__PURE__ */ new Uint32Array(256);
|
|
17
|
+
for (let index = 0; index < 256; index += 1) {
|
|
18
|
+
let value = index;
|
|
19
|
+
for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
20
|
+
table[index] = value >>> 0;
|
|
21
|
+
}
|
|
22
|
+
return table;
|
|
23
|
+
})();
|
|
24
|
+
/** CRC-32 (IEEE 802.3) of the key's ASCII bytes, as 6 base62 characters. */
|
|
25
|
+
function checksum(text) {
|
|
26
|
+
let crc = 4294967295;
|
|
27
|
+
for (let index = 0; index < text.length; index += 1) crc = (CRC_TABLE[(crc ^ (text.codePointAt(index) ?? 0)) & 255] ?? 0) ^ crc >>> 8;
|
|
28
|
+
let value = (crc ^ 4294967295) >>> 0;
|
|
29
|
+
let out = "";
|
|
30
|
+
for (let index = 0; index < CHECKSUM_LENGTH; index += 1) {
|
|
31
|
+
out = (ALPHABET[value % 62] ?? "") + out;
|
|
32
|
+
value = Math.floor(value / 62);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Splits `pdk_<id>_<secret><checksum>`: `id` is 1 to 128 of `A-Z a-z 0-9 _ -`,
|
|
38
|
+
* the secret 43 base62 characters and the checksum the 6 base62 characters of
|
|
39
|
+
* the CRC-32 of everything before it. Anything else, including a checksum
|
|
40
|
+
* that does not match, is `undefined`.
|
|
41
|
+
*/
|
|
42
|
+
function parseApiKey(key) {
|
|
43
|
+
if (typeof key !== "string" || key.length > MAX_KEY || !key.startsWith(PREFIX)) return;
|
|
44
|
+
const body = key.slice(4);
|
|
45
|
+
const cut = body.lastIndexOf("_");
|
|
46
|
+
if (cut <= 0) return;
|
|
47
|
+
const id = body.slice(0, cut);
|
|
48
|
+
const tail = body.slice(cut + 1);
|
|
49
|
+
if (!ID.test(id) || !TAIL.test(tail)) return;
|
|
50
|
+
const secret = tail.slice(0, SECRET_LENGTH);
|
|
51
|
+
return tail.slice(SECRET_LENGTH) === checksum(`${PREFIX}${id}_${secret}`) ? {
|
|
52
|
+
id,
|
|
53
|
+
secret
|
|
54
|
+
} : void 0;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A fresh `pdk_<id>_<secret><checksum>`: a 43-character base62 secret (about
|
|
58
|
+
* 256 bits) and a 6-character CRC-32 checksum, so a secret scanner can tell a
|
|
59
|
+
* real key from a lookalike without calling the issuer.
|
|
60
|
+
*/
|
|
61
|
+
function generateApiKey(id) {
|
|
62
|
+
if (!ID.test(id)) throw new TypeError("PermDock: an API key id is 1 to 128 of A-Z, a-z, 0-9, _ and -");
|
|
63
|
+
let secret = "";
|
|
64
|
+
const bytes = /* @__PURE__ */ new Uint8Array(64);
|
|
65
|
+
while (secret.length < SECRET_LENGTH) {
|
|
66
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
67
|
+
for (const byte of bytes) if (byte < 248 && secret.length < SECRET_LENGTH) secret += ALPHABET[byte % 62];
|
|
68
|
+
}
|
|
69
|
+
const key = `${PREFIX}${id}_${secret}`;
|
|
70
|
+
return `${key}${checksum(key)}`;
|
|
71
|
+
}
|
|
72
|
+
/** base64url SHA-256 of the whole key, the value to store and compare. */
|
|
73
|
+
async function hashApiKey(key) {
|
|
74
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
|
|
75
|
+
return bytesToBase64Url(new Uint8Array(digest));
|
|
76
|
+
}
|
|
77
|
+
function sameHash(left, right) {
|
|
78
|
+
if (left.length !== right.length) return false;
|
|
79
|
+
let difference = 0;
|
|
80
|
+
for (let index = 0; index < left.length; index += 1) difference |= (left.codePointAt(index) ?? 0) ^ (right.codePointAt(index) ?? 0);
|
|
81
|
+
return difference === 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A `CredentialVerifier` over the application's own table: `find` looks a
|
|
85
|
+
* key up by the id in it, and the key's hash is compared in constant time.
|
|
86
|
+
* A record that is not a valid v1 credential, or whose id is not the key's,
|
|
87
|
+
* verifies to `null`. A throwing `find` propagates, so the resolver denies
|
|
88
|
+
* with `source-threw`.
|
|
89
|
+
*/
|
|
90
|
+
function apiKeyVerifier(options) {
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
...options.touch === void 0 ? {} : { touch: options.touch },
|
|
93
|
+
async verify(secret) {
|
|
94
|
+
const parts = parseApiKey(secret);
|
|
95
|
+
if (parts === void 0) return null;
|
|
96
|
+
const stored = await options.find(parts.id);
|
|
97
|
+
if (stored === null || stored === void 0) return null;
|
|
98
|
+
const credential = parseCredential(stored.credential);
|
|
99
|
+
if (credential === void 0 || credential.id !== parts.id || typeof stored.hash !== "string" || !sameHash(await hashApiKey(secret), stored.hash)) return null;
|
|
100
|
+
return credential;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function write(sink, event) {
|
|
105
|
+
try {
|
|
106
|
+
const written = sink.write(event);
|
|
107
|
+
if (isThenable(written)) Promise.resolve(written).catch(() => void 0);
|
|
108
|
+
} catch {}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* In-process credentials for tests and single-replica apps. `issue`, `rotate`
|
|
112
|
+
* and `revoke` write `credential` events to `sink` when one is given.
|
|
113
|
+
*/
|
|
114
|
+
function memoryCredentials(options = {}) {
|
|
115
|
+
const records = /* @__PURE__ */ new Map();
|
|
116
|
+
const used = /* @__PURE__ */ new Map();
|
|
117
|
+
const verifier = apiKeyVerifier({ find: (id) => records.get(id) });
|
|
118
|
+
const emit = (operation, credential) => {
|
|
119
|
+
if (options.sink !== void 0) write(options.sink, [credentialEvent(compact({
|
|
120
|
+
operation,
|
|
121
|
+
credential,
|
|
122
|
+
source: options.source
|
|
123
|
+
}))]);
|
|
124
|
+
};
|
|
125
|
+
const store = async (credential) => {
|
|
126
|
+
const key = generateApiKey(credential.id);
|
|
127
|
+
records.set(credential.id, {
|
|
128
|
+
credential,
|
|
129
|
+
hash: await hashApiKey(key)
|
|
130
|
+
});
|
|
131
|
+
return key;
|
|
132
|
+
};
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
verify: (secret) => verifier.verify(secret),
|
|
135
|
+
touch(id, at) {
|
|
136
|
+
if (records.has(id)) used.set(id, at);
|
|
137
|
+
},
|
|
138
|
+
lastUsedAt(id) {
|
|
139
|
+
return used.get(id);
|
|
140
|
+
},
|
|
141
|
+
async issue(input) {
|
|
142
|
+
const credential = parseCredential(input);
|
|
143
|
+
if (credential === void 0) throw new TypeError("PermDock: not a valid v1 credential");
|
|
144
|
+
if (records.has(credential.id)) throw new Error(`PermDock: credential '${credential.id}' exists`);
|
|
145
|
+
const key = await store(credential);
|
|
146
|
+
emit("created", credential);
|
|
147
|
+
return key;
|
|
148
|
+
},
|
|
149
|
+
async rotate(id) {
|
|
150
|
+
const current = records.get(id);
|
|
151
|
+
if (current === void 0) return;
|
|
152
|
+
const key = await store(current.credential);
|
|
153
|
+
emit("rotated", current.credential);
|
|
154
|
+
return key;
|
|
155
|
+
},
|
|
156
|
+
revoke(id) {
|
|
157
|
+
const current = records.get(id);
|
|
158
|
+
if (current === void 0) return false;
|
|
159
|
+
records.delete(id);
|
|
160
|
+
used.delete(id);
|
|
161
|
+
emit("revoked", current.credential);
|
|
162
|
+
return true;
|
|
163
|
+
},
|
|
164
|
+
list() {
|
|
165
|
+
return [...records.values()].map((record) => record.credential);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function checkStores(options, credential, active) {
|
|
170
|
+
try {
|
|
171
|
+
const tenant = credentialTenant(credential, active);
|
|
172
|
+
const policy = options.settings === void 0 || tenant === void 0 ? void 0 : (await options.settings.settingsFor(tenant))?.credentials;
|
|
173
|
+
if (policy !== void 0 && credentialPolicyViolation(credential, policy) !== void 0) return {
|
|
174
|
+
ok: false,
|
|
175
|
+
cause: "credential-policy"
|
|
176
|
+
};
|
|
177
|
+
if (options.revoked !== void 0 && await options.revoked(credential.id)) return {
|
|
178
|
+
ok: false,
|
|
179
|
+
cause: "credential-revoked"
|
|
180
|
+
};
|
|
181
|
+
if (credential.kind !== "user" || options.owner === void 0) return { ok: true };
|
|
182
|
+
const owner = await options.owner(credential.principal);
|
|
183
|
+
if (owner === null || owner === void 0) return {
|
|
184
|
+
ok: false,
|
|
185
|
+
cause: "owner-unavailable"
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
ok: true,
|
|
189
|
+
owner
|
|
190
|
+
};
|
|
191
|
+
} catch {
|
|
192
|
+
return { ok: false };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function touch(verifier, id) {
|
|
196
|
+
if (verifier.touch === void 0) return;
|
|
197
|
+
try {
|
|
198
|
+
const touched = verifier.touch(id, Math.floor(Date.now() / 1e3));
|
|
199
|
+
if (isThenable(touched)) Promise.resolve(touched).catch(() => void 0);
|
|
200
|
+
} catch {}
|
|
201
|
+
}
|
|
202
|
+
function sampled(sample) {
|
|
203
|
+
if (sample === 1) return true;
|
|
204
|
+
const draw = /* @__PURE__ */ new Uint32Array(1);
|
|
205
|
+
globalThis.crypto.getRandomValues(draw);
|
|
206
|
+
return (draw[0] ?? 0) / 4294967296 < sample;
|
|
207
|
+
}
|
|
208
|
+
function reportUse(options, credential) {
|
|
209
|
+
const sample = options.sample ?? 1;
|
|
210
|
+
if (options.sink === void 0 || !Number.isFinite(sample) || sample <= 0 || sample > 1 || !sampled(sample)) return;
|
|
211
|
+
write(options.sink, [credentialEvent(compact({
|
|
212
|
+
operation: "used",
|
|
213
|
+
credential,
|
|
214
|
+
source: options.source,
|
|
215
|
+
sample
|
|
216
|
+
}))]);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* A `SubjectResolver` for opaque API keys (`pdk_<id>_<secret>`): each call
|
|
220
|
+
* resolves a key into the subject its credential acts as. Every failure (a
|
|
221
|
+
* malformed or unknown key, a record that is not a valid credential, expiry,
|
|
222
|
+
* a tenant policy the key breaks, a revoked id, a missing owner, a throwing
|
|
223
|
+
* verifier or callback) is the anonymous subject, never a throw.
|
|
224
|
+
*/
|
|
225
|
+
function subjectFromApiKey(options) {
|
|
226
|
+
return (key, resolve) => resolveApiKey(key, options, resolve?.tenant);
|
|
227
|
+
}
|
|
228
|
+
function anonymous() {
|
|
229
|
+
return anonymousSubject();
|
|
230
|
+
}
|
|
231
|
+
async function resolveApiKey(key, options, tenant) {
|
|
232
|
+
if (typeof key !== "string" || key.length === 0) return anonymous();
|
|
233
|
+
const deny = (cause, reason = "invalid-token") => {
|
|
234
|
+
try {
|
|
235
|
+
options.onAuth?.(compact({
|
|
236
|
+
reason,
|
|
237
|
+
cause,
|
|
238
|
+
source: "api-key",
|
|
239
|
+
requestId: options.requestId
|
|
240
|
+
}));
|
|
241
|
+
} catch {}
|
|
242
|
+
return anonymous();
|
|
243
|
+
};
|
|
244
|
+
const parts = parseApiKey(key);
|
|
245
|
+
if (parts === void 0) return deny("malformed");
|
|
246
|
+
let verified;
|
|
247
|
+
try {
|
|
248
|
+
verified = await options.verifier.verify(key);
|
|
249
|
+
} catch {
|
|
250
|
+
return deny(void 0, "source-threw");
|
|
251
|
+
}
|
|
252
|
+
if (verified === null || verified === void 0) return deny("unknown-credential");
|
|
253
|
+
const credential = parseCredential(verified);
|
|
254
|
+
if (credential === void 0 || credential.id !== parts.id) return deny("invalid-claims");
|
|
255
|
+
if (credential.expiresAt !== void 0 && credential.expiresAt <= Date.now() / 1e3) return deny("expired");
|
|
256
|
+
const checked = await checkStores(options, credential, tenant);
|
|
257
|
+
if (!checked.ok) return checked.cause === void 0 ? deny(void 0, "source-threw") : deny(checked.cause);
|
|
258
|
+
const subject = credentialSubject(credential, compact({
|
|
259
|
+
permissions: options.permissions,
|
|
260
|
+
owner: checked.owner
|
|
261
|
+
}));
|
|
262
|
+
if (subject.principal === null) return deny("owner-unavailable");
|
|
263
|
+
reportUse(options, credential);
|
|
264
|
+
touch(options.verifier, credential.id);
|
|
265
|
+
return subject;
|
|
266
|
+
}
|
|
267
|
+
//#endregion
|
|
268
|
+
export { parseApiKey as a, memoryCredentials as i, generateApiKey as n, subjectFromApiKey as o, hashApiKey as r, apiKeyVerifier as t };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { t as flattenApprovers } from "./approvers-aH0PJkmf.js";
|
|
2
|
+
import { s as flattenGrantee } from "./grantee-Bv04_Vzf.js";
|
|
3
|
+
//#region src/core/describe.ts
|
|
4
|
+
const TENANT_REASONS = /* @__PURE__ */ new Set([
|
|
5
|
+
"tenant-mismatch",
|
|
6
|
+
"no-membership",
|
|
7
|
+
"expired-membership",
|
|
8
|
+
"scope"
|
|
9
|
+
]);
|
|
10
|
+
const DELEGATION_REASONS = /* @__PURE__ */ new Set(["not-delegated", "no-delegation"]);
|
|
11
|
+
function labelGrantee(grantee) {
|
|
12
|
+
switch (grantee.kind) {
|
|
13
|
+
case "role": return grantee.role;
|
|
14
|
+
case "plan": return grantee.plan;
|
|
15
|
+
case "actor": return grantee.actor;
|
|
16
|
+
case "authenticated": return "an authenticated user";
|
|
17
|
+
case "anyone": return "anyone";
|
|
18
|
+
case "relation": return grantee.relation;
|
|
19
|
+
case "assurance": {
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (grantee.acr !== void 0 && grantee.acr.length > 0) parts.push(`acr ${grantee.acr.join("/")}`);
|
|
22
|
+
if (grantee.amr !== void 0 && grantee.amr.length > 0) parts.push(`amr ${grantee.amr.join("/")}`);
|
|
23
|
+
return parts.length > 0 ? parts.join(" ") : "step-up";
|
|
24
|
+
}
|
|
25
|
+
default: return grantee;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The plans named by the `not-entitled` denials of a decision, when every
|
|
30
|
+
* denial is `not-entitled`; otherwise empty, since a plan alone would not grant.
|
|
31
|
+
*/
|
|
32
|
+
function requiredPlans(decision) {
|
|
33
|
+
if (decision.outcome !== "denied" || decision.denials.length === 0 || decision.denials.some((denial) => denial.reason !== "not-entitled")) return [];
|
|
34
|
+
const plans = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const denial of decision.denials) for (const item of flattenGrantee(denial.to)) if (item.kind === "plan") plans.add(item.plan);
|
|
36
|
+
return [...plans];
|
|
37
|
+
}
|
|
38
|
+
const TITLES = {
|
|
39
|
+
granted: "Granted",
|
|
40
|
+
denied: "Denied",
|
|
41
|
+
approval: "Approval required",
|
|
42
|
+
tenant: "Wrong tenant",
|
|
43
|
+
delegation: "Not delegated",
|
|
44
|
+
"server-only": "Server only",
|
|
45
|
+
upgrade: "Upgrade required"
|
|
46
|
+
};
|
|
47
|
+
function labelApprover(approver) {
|
|
48
|
+
return approver.kind === "user" ? `user ${approver.id}` : labelGrantee(approver);
|
|
49
|
+
}
|
|
50
|
+
function approvers(approval) {
|
|
51
|
+
if (approval === void 0 || approval === "human") return [];
|
|
52
|
+
return [...flattenApprovers(approval.by), ...(approval.stages ?? []).flatMap((stage) => flattenApprovers(stage.by))].map(labelApprover);
|
|
53
|
+
}
|
|
54
|
+
function approvalDetail(permission, labels) {
|
|
55
|
+
if (labels.length === 0) return `${permission} requires human approval.`;
|
|
56
|
+
return `${permission} requires approval from ${labels.join(" and ")}.`;
|
|
57
|
+
}
|
|
58
|
+
function describe(decision, options) {
|
|
59
|
+
const messages = options?.messages;
|
|
60
|
+
const titleOf = (kind) => messages?.titles?.[kind] ?? TITLES[kind];
|
|
61
|
+
if (decision.outcome === "granted") {
|
|
62
|
+
const { permission } = decision.matched;
|
|
63
|
+
return {
|
|
64
|
+
kind: "granted",
|
|
65
|
+
title: titleOf("granted"),
|
|
66
|
+
detail: messages?.granted?.(permission) ?? `${permission} granted.`,
|
|
67
|
+
alternatives: []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (decision.outcome === "approval-required") {
|
|
71
|
+
const { permission } = decision.grant;
|
|
72
|
+
const labels = approvers(decision.grant.approval);
|
|
73
|
+
return {
|
|
74
|
+
kind: "approval",
|
|
75
|
+
title: titleOf("approval"),
|
|
76
|
+
detail: messages?.approval?.(permission, labels) ?? approvalDetail(permission, labels),
|
|
77
|
+
alternatives: []
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const plans = requiredPlans(decision);
|
|
81
|
+
if (plans.length > 0) return {
|
|
82
|
+
kind: "upgrade",
|
|
83
|
+
title: titleOf("upgrade"),
|
|
84
|
+
detail: messages?.upgrade?.(plans) ?? `${plans.join(" or ")} plan required.`,
|
|
85
|
+
alternatives: decision.alternatives,
|
|
86
|
+
plans
|
|
87
|
+
};
|
|
88
|
+
const reasons = decision.denials.map((denial) => denial.reason);
|
|
89
|
+
const kind = reasons.some((reason) => TENANT_REASONS.has(reason)) ? "tenant" : reasons.some((reason) => DELEGATION_REASONS.has(reason)) ? "delegation" : reasons.includes("opaque-condition") || reasons.includes("server-only") ? "server-only" : "denied";
|
|
90
|
+
const detail = reasons.map((reason) => messages?.reasons?.[reason] ?? reason).join(messages?.separator ?? ", ");
|
|
91
|
+
return {
|
|
92
|
+
kind,
|
|
93
|
+
title: titleOf(kind),
|
|
94
|
+
detail,
|
|
95
|
+
alternatives: decision.alternatives
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { requiredPlans as n, describe as t };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Mn as Decision, Pn as DenialReason } from "./policy-CInmNGkM.js";
|
|
2
|
+
import { d as Permission } from "./permissions-B4apnahQ.js";
|
|
3
|
+
//#region src/core/describe.d.ts
|
|
4
|
+
type DecisionDescription = {
|
|
5
|
+
readonly kind: "granted" | "denied" | "approval" | "tenant" | "delegation" | "server-only" | "upgrade";
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly detail: string;
|
|
8
|
+
readonly alternatives: readonly Permission[];
|
|
9
|
+
/** On `upgrade`: the plans any of which would grant the permission. */
|
|
10
|
+
readonly plans?: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The plans named by the `not-entitled` denials of a decision, when every
|
|
14
|
+
* denial is `not-entitled`; otherwise empty, since a plan alone would not grant.
|
|
15
|
+
*/
|
|
16
|
+
declare function requiredPlans(decision: Decision): readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Localised text for `describe`. Every entry is optional and falls back to the
|
|
19
|
+
* English default; `kind` and `alternatives` never change.
|
|
20
|
+
*/
|
|
21
|
+
type DescribeMessages = {
|
|
22
|
+
readonly titles?: Partial<Record<DecisionDescription["kind"], string>>;
|
|
23
|
+
/** Text per denial reason; a denied detail joins the texts of its reasons with `separator`. */
|
|
24
|
+
readonly reasons?: Partial<Record<DenialReason, string>>;
|
|
25
|
+
/** Joins denial reason texts. Default `', '`. */
|
|
26
|
+
readonly separator?: string;
|
|
27
|
+
readonly granted?: (permission: string) => string;
|
|
28
|
+
/** `approvers` is empty for `'human'` approval. */
|
|
29
|
+
readonly approval?: (permission: string, approvers: readonly string[]) => string;
|
|
30
|
+
readonly upgrade?: (plans: readonly string[]) => string;
|
|
31
|
+
};
|
|
32
|
+
type DescribeOptions = {
|
|
33
|
+
readonly messages?: DescribeMessages;
|
|
34
|
+
};
|
|
35
|
+
declare function describe(decision: Decision, options?: DescribeOptions): DecisionDescription;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { requiredPlans as a, describe as i, DescribeMessages as n, DescribeOptions as r, DecisionDescription as t };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { i as globalArgs, s as stringArg, t as COMMAND_DESCRIPTIONS } from "./commands-BMh_Etm2.js";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
//#region src/cli/commands/diff.ts
|
|
4
|
+
function diff(ctx) {
|
|
5
|
+
return defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "diff",
|
|
8
|
+
description: COMMAND_DESCRIPTIONS.diff
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
...globalArgs,
|
|
12
|
+
before: {
|
|
13
|
+
type: "positional",
|
|
14
|
+
required: false,
|
|
15
|
+
description: "A permissions.catalog.json or a module exporting policy"
|
|
16
|
+
},
|
|
17
|
+
after: {
|
|
18
|
+
type: "positional",
|
|
19
|
+
required: false,
|
|
20
|
+
description: "The same, for the new side"
|
|
21
|
+
},
|
|
22
|
+
impact: {
|
|
23
|
+
type: "boolean",
|
|
24
|
+
description: "Run the fixtures through both sides and report changed outcomes"
|
|
25
|
+
},
|
|
26
|
+
fixtures: {
|
|
27
|
+
type: "string",
|
|
28
|
+
description: "Fixtures file for --impact",
|
|
29
|
+
valueHint: "file"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
async run({ args: parsed }) {
|
|
33
|
+
const result = await (await import("./diff-x5Il8LVa.js")).runDiff({
|
|
34
|
+
cwd: ctx.cwd,
|
|
35
|
+
config: ctx.config,
|
|
36
|
+
sources: parsed._,
|
|
37
|
+
impact: parsed.impact === true,
|
|
38
|
+
fixtures: stringArg(parsed.fixtures),
|
|
39
|
+
json: ctx.json,
|
|
40
|
+
now: ctx.now,
|
|
41
|
+
io: ctx.io
|
|
42
|
+
});
|
|
43
|
+
ctx.report(result);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { diff };
|