@homeflare/alchemy 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -55
- package/dist/caddy/admin-calls.d.ts +48 -0
- package/dist/caddy/admin-calls.d.ts.map +1 -0
- package/dist/caddy/admin-guard.d.ts +40 -0
- package/dist/caddy/admin-guard.d.ts.map +1 -0
- package/dist/caddy/admin.d.ts +73 -0
- package/dist/caddy/admin.d.ts.map +1 -0
- package/dist/caddy/caddyfile-tripwire.d.ts +5 -0
- package/dist/caddy/caddyfile-tripwire.d.ts.map +1 -0
- package/dist/caddy/config-form.d.ts +27 -0
- package/dist/caddy/config-form.d.ts.map +1 -0
- package/dist/caddy/config-lifecycle.d.ts +45 -0
- package/dist/caddy/config-lifecycle.d.ts.map +1 -0
- package/dist/caddy/config.d.ts +30 -0
- package/dist/caddy/config.d.ts.map +1 -0
- package/dist/caddy/digest.d.ts +7 -0
- package/dist/caddy/digest.d.ts.map +1 -0
- package/dist/caddy/fake-caddy.d.ts +43 -0
- package/dist/caddy/fake-caddy.d.ts.map +1 -0
- package/dist/caddy/index.d.ts +21 -0
- package/dist/caddy/index.d.ts.map +1 -0
- package/dist/caddy/index.js +912 -0
- package/dist/caddy/index.js.map +28 -0
- package/dist/caddy/local-admin.d.ts +45 -0
- package/dist/caddy/local-admin.d.ts.map +1 -0
- package/dist/caddy/providers.d.ts +14 -0
- package/dist/caddy/providers.d.ts.map +1 -0
- package/dist/caddy/with-file.d.ts +47 -0
- package/dist/caddy/with-file.d.ts.map +1 -0
- package/dist/cloudflare/fake-mesh.d.ts +58 -0
- package/dist/cloudflare/fake-mesh.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +9 -3
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +218 -7
- package/dist/cloudflare/index.js.map +9 -4
- package/dist/cloudflare/mesh-node-api.d.ts +75 -0
- package/dist/cloudflare/mesh-node-api.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-form.d.ts +82 -0
- package/dist/cloudflare/mesh-node-form.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-harness.d.ts +46 -0
- package/dist/cloudflare/mesh-node-harness.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-lifecycle.d.ts +22 -0
- package/dist/cloudflare/mesh-node-lifecycle.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node-token.d.ts +23 -0
- package/dist/cloudflare/mesh-node-token.d.ts.map +1 -0
- package/dist/cloudflare/mesh-node.d.ts +48 -0
- package/dist/cloudflare/mesh-node.d.ts.map +1 -0
- package/dist/cloudflare/providers.d.ts +1 -12
- package/dist/cloudflare/providers.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/launchd/fake-runner.d.ts +36 -0
- package/dist/launchd/fake-runner.d.ts.map +1 -0
- package/dist/launchd/host-effect.d.ts +19 -0
- package/dist/launchd/host-effect.d.ts.map +1 -0
- package/dist/launchd/host-file-form.d.ts +24 -0
- package/dist/launchd/host-file-form.d.ts.map +1 -0
- package/dist/launchd/host-file-lifecycle.d.ts +29 -0
- package/dist/launchd/host-file-lifecycle.d.ts.map +1 -0
- package/dist/launchd/host-file.d.ts +25 -0
- package/dist/launchd/host-file.d.ts.map +1 -0
- package/dist/launchd/host-lookup.d.ts +27 -0
- package/dist/launchd/host-lookup.d.ts.map +1 -0
- package/dist/launchd/index.d.ts +21 -0
- package/dist/launchd/index.d.ts.map +1 -0
- package/dist/launchd/index.js +969 -0
- package/dist/launchd/index.js.map +25 -0
- package/dist/launchd/job-form.d.ts +90 -0
- package/dist/launchd/job-form.d.ts.map +1 -0
- package/dist/launchd/job-lifecycle.d.ts +34 -0
- package/dist/launchd/job-lifecycle.d.ts.map +1 -0
- package/dist/launchd/job-preflight.d.ts +50 -0
- package/dist/launchd/job-preflight.d.ts.map +1 -0
- package/dist/launchd/job-validate.d.ts +25 -0
- package/dist/launchd/job-validate.d.ts.map +1 -0
- package/dist/launchd/job.d.ts +34 -0
- package/dist/launchd/job.d.ts.map +1 -0
- package/dist/launchd/launchctl.d.ts +62 -0
- package/dist/launchd/launchctl.d.ts.map +1 -0
- package/dist/launchd/local-runner.d.ts +7 -0
- package/dist/launchd/local-runner.d.ts.map +1 -0
- package/dist/launchd/plist.d.ts +41 -0
- package/dist/launchd/plist.d.ts.map +1 -0
- package/dist/launchd/providers.d.ts +15 -0
- package/dist/launchd/providers.d.ts.map +1 -0
- package/dist/launchd/runner.d.ts +91 -0
- package/dist/launchd/runner.d.ts.map +1 -0
- package/dist/launchd/secret-tripwire.d.ts +24 -0
- package/dist/launchd/secret-tripwire.d.ts.map +1 -0
- package/dist/openbao/auth-method-form.d.ts +7 -0
- package/dist/openbao/auth-method-form.d.ts.map +1 -1
- package/dist/openbao/auth-method-reconcile.d.ts +15 -0
- package/dist/openbao/auth-method-reconcile.d.ts.map +1 -0
- package/dist/openbao/auth-method.d.ts +6 -0
- package/dist/openbao/auth-method.d.ts.map +1 -1
- package/dist/openbao/auth-role-form.d.ts +9 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -1
- package/dist/openbao/auth-role.d.ts.map +1 -1
- package/dist/openbao/auth-token-form.d.ts +48 -0
- package/dist/openbao/auth-token-form.d.ts.map +1 -0
- package/dist/openbao/bao-identity.d.ts +81 -0
- package/dist/openbao/bao-identity.d.ts.map +1 -0
- package/dist/openbao/bao-namespace.d.ts +20 -0
- package/dist/openbao/bao-namespace.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -1
- package/dist/openbao/fake-engines.d.ts +30 -0
- package/dist/openbao/fake-engines.d.ts.map +1 -0
- package/dist/openbao/fake-stack.d.ts +29 -0
- package/dist/openbao/fake-stack.d.ts.map +1 -0
- package/dist/openbao/host-approles.d.ts +62 -0
- package/dist/openbao/host-approles.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +19 -0
- package/dist/openbao/index.d.ts.map +1 -1
- package/dist/openbao/index.js +1518 -310
- package/dist/openbao/index.js.map +40 -17
- package/dist/openbao/jwt-config-form.d.ts +51 -0
- package/dist/openbao/jwt-config-form.d.ts.map +1 -0
- package/dist/openbao/jwt-config.d.ts +25 -0
- package/dist/openbao/jwt-config.d.ts.map +1 -0
- package/dist/openbao/jwt-role-form.d.ts +91 -0
- package/dist/openbao/jwt-role-form.d.ts.map +1 -0
- package/dist/openbao/jwt-role.d.ts +31 -0
- package/dist/openbao/jwt-role.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role-form.d.ts +68 -0
- package/dist/openbao/kubernetes-role-form.d.ts.map +1 -0
- package/dist/openbao/kubernetes-role.d.ts +25 -0
- package/dist/openbao/kubernetes-role.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement-form.d.ts +46 -0
- package/dist/openbao/mfa-enforcement-form.d.ts.map +1 -0
- package/dist/openbao/mfa-enforcement.d.ts +41 -0
- package/dist/openbao/mfa-enforcement.d.ts.map +1 -0
- package/dist/openbao/mfa-totp-form.d.ts +52 -0
- package/dist/openbao/mfa-totp-form.d.ts.map +1 -0
- package/dist/openbao/mfa-totp.d.ts +33 -0
- package/dist/openbao/mfa-totp.d.ts.map +1 -0
- package/dist/openbao/mfa-wire.d.ts +63 -0
- package/dist/openbao/mfa-wire.d.ts.map +1 -0
- package/dist/openbao/mount-form.d.ts +7 -0
- package/dist/openbao/mount-form.d.ts.map +1 -1
- package/dist/openbao/mount-move.d.ts +49 -0
- package/dist/openbao/mount-move.d.ts.map +1 -0
- package/dist/openbao/mount-path.d.ts +8 -4
- package/dist/openbao/mount-path.d.ts.map +1 -1
- package/dist/openbao/mount-reconcile.d.ts +16 -0
- package/dist/openbao/mount-reconcile.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +9 -0
- package/dist/openbao/mount.d.ts.map +1 -1
- package/dist/openbao/pki-role-canonical.d.ts +46 -0
- package/dist/openbao/pki-role-canonical.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +38 -30
- package/dist/openbao/pki-role-form.d.ts.map +1 -1
- package/dist/openbao/pki-role.d.ts.map +1 -1
- package/dist/openbao/policy.d.ts +2 -0
- package/dist/openbao/policy.d.ts.map +1 -1
- package/dist/openbao/proxmox-role-form.d.ts +18 -8
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role-wire.d.ts +18 -0
- package/dist/openbao/proxmox-role-wire.d.ts.map +1 -0
- package/dist/openbao/proxmox-role.d.ts.map +1 -1
- package/dist/openbao/remount-wire.d.ts +44 -0
- package/dist/openbao/remount-wire.d.ts.map +1 -0
- package/dist/openbao/rename.d.ts +106 -0
- package/dist/openbao/rename.d.ts.map +1 -0
- package/dist/openbao/role-reconcile.d.ts +40 -0
- package/dist/openbao/role-reconcile.d.ts.map +1 -0
- package/docs/caddy.md +180 -0
- package/docs/launchd.md +167 -0
- package/docs/mesh-node.md +188 -0
- package/package.json +12 -2
- package/src/caddy/admin-calls.ts +128 -0
- package/src/caddy/admin-guard.ts +170 -0
- package/src/caddy/admin.ts +74 -0
- package/src/caddy/caddyfile-tripwire.ts +133 -0
- package/src/caddy/config-form.ts +64 -0
- package/src/caddy/config-lifecycle.test.ts +218 -0
- package/src/caddy/config-lifecycle.ts +148 -0
- package/src/caddy/config.ts +116 -0
- package/src/caddy/digest.ts +44 -0
- package/src/caddy/fake-caddy.ts +160 -0
- package/src/caddy/guards.test.ts +156 -0
- package/src/caddy/index.ts +25 -0
- package/src/caddy/local-admin.test.ts +160 -0
- package/src/caddy/local-admin.ts +189 -0
- package/src/caddy/providers.test.ts +244 -0
- package/src/caddy/providers.ts +17 -0
- package/src/caddy/with-file.ts +62 -0
- package/src/cloudflare/fake-mesh.ts +181 -0
- package/src/cloudflare/index.ts +9 -3
- package/src/cloudflare/mesh-node-api.ts +127 -0
- package/src/cloudflare/mesh-node-edges.test.ts +103 -0
- package/src/cloudflare/mesh-node-form.test.ts +80 -0
- package/src/cloudflare/mesh-node-form.ts +115 -0
- package/src/cloudflare/mesh-node-harness.ts +124 -0
- package/src/cloudflare/mesh-node-lifecycle.ts +187 -0
- package/src/cloudflare/mesh-node-policy.test.ts +151 -0
- package/src/cloudflare/mesh-node-token.test.ts +117 -0
- package/src/cloudflare/mesh-node-token.ts +125 -0
- package/src/cloudflare/mesh-node.test.ts +172 -0
- package/src/cloudflare/mesh-node.ts +120 -0
- package/src/cloudflare/providers.ts +20 -2
- package/src/index.ts +2 -0
- package/src/launchd/fake-runner.ts +176 -0
- package/src/launchd/host-effect.ts +28 -0
- package/src/launchd/host-file-form.ts +71 -0
- package/src/launchd/host-file-lifecycle.test.ts +198 -0
- package/src/launchd/host-file-lifecycle.ts +164 -0
- package/src/launchd/host-file.ts +64 -0
- package/src/launchd/host-lookup.test.ts +92 -0
- package/src/launchd/host-lookup.ts +94 -0
- package/src/launchd/index.ts +26 -0
- package/src/launchd/job-form.test.ts +171 -0
- package/src/launchd/job-form.ts +180 -0
- package/src/launchd/job-identity.test.ts +147 -0
- package/src/launchd/job-lifecycle.test.ts +220 -0
- package/src/launchd/job-lifecycle.ts +194 -0
- package/src/launchd/job-preflight.ts +152 -0
- package/src/launchd/job-validate.ts +134 -0
- package/src/launchd/job.ts +96 -0
- package/src/launchd/launchctl.test.ts +181 -0
- package/src/launchd/launchctl.ts +188 -0
- package/src/launchd/local-runner.test.ts +125 -0
- package/src/launchd/local-runner.ts +143 -0
- package/src/launchd/plist.test.ts +186 -0
- package/src/launchd/plist.ts +156 -0
- package/src/launchd/providers.test.ts +222 -0
- package/src/launchd/providers.ts +21 -0
- package/src/launchd/runner.ts +95 -0
- package/src/launchd/secret-tripwire.test.ts +110 -0
- package/src/launchd/secret-tripwire.ts +81 -0
- package/src/openbao/README.md +181 -0
- package/src/openbao/REPLACE.md +163 -0
- package/src/openbao/auth-method-form.ts +7 -0
- package/src/openbao/auth-method-reconcile.ts +74 -0
- package/src/openbao/auth-method.ts +20 -41
- package/src/openbao/auth-role-form.test.ts +16 -1
- package/src/openbao/auth-role-form.ts +11 -0
- package/src/openbao/auth-role.ts +12 -0
- package/src/openbao/auth-token-form.ts +161 -0
- package/src/openbao/bao-identity.test.ts +131 -0
- package/src/openbao/bao-identity.ts +188 -0
- package/src/openbao/bao-namespace.ts +32 -0
- package/src/openbao/cloudflare-role-wire.test.ts +8 -34
- package/src/openbao/cloudflare-role.ts +30 -8
- package/src/openbao/cloudflare-roles-expand.ts +2 -5
- package/src/openbao/fake-engines.ts +119 -0
- package/src/openbao/fake-stack.ts +112 -0
- package/src/openbao/host-approles.test.ts +121 -0
- package/src/openbao/host-approles.ts +126 -0
- package/src/openbao/index.ts +19 -0
- package/src/openbao/jwt-config-form.ts +161 -0
- package/src/openbao/jwt-config.ts +85 -0
- package/src/openbao/jwt-role-form.test.ts +130 -0
- package/src/openbao/jwt-role-form.ts +231 -0
- package/src/openbao/jwt-role.ts +102 -0
- package/src/openbao/kubernetes-role-form.test.ts +82 -0
- package/src/openbao/kubernetes-role-form.ts +149 -0
- package/src/openbao/kubernetes-role.ts +94 -0
- package/src/openbao/mfa-enforcement-form.ts +147 -0
- package/src/openbao/mfa-enforcement.ts +143 -0
- package/src/openbao/mfa-totp-form.ts +155 -0
- package/src/openbao/mfa-totp.ts +118 -0
- package/src/openbao/mfa-wire.ts +117 -0
- package/src/openbao/mfa.test.ts +200 -0
- package/src/openbao/mount-form.ts +9 -1
- package/src/openbao/mount-move.test.ts +164 -0
- package/src/openbao/mount-move.ts +113 -0
- package/src/openbao/mount-path.test.ts +11 -1
- package/src/openbao/mount-path.ts +17 -11
- package/src/openbao/mount-reconcile.ts +75 -0
- package/src/openbao/mount.ts +26 -43
- package/src/openbao/pki-role-canonical.ts +163 -0
- package/src/openbao/pki-role-form.test.ts +112 -0
- package/src/openbao/pki-role-form.ts +76 -110
- package/src/openbao/pki-role.ts +12 -27
- package/src/openbao/policy-rename.test.ts +112 -0
- package/src/openbao/policy-wire.test.ts +7 -26
- package/src/openbao/policy.ts +27 -4
- package/src/openbao/proxmox-role-form.ts +27 -8
- package/src/openbao/proxmox-role-wire.ts +24 -0
- package/src/openbao/proxmox-role.ts +62 -37
- package/src/openbao/remount-wire.test.ts +87 -0
- package/src/openbao/remount-wire.ts +112 -0
- package/src/openbao/rename-occupied.test.ts +166 -0
- package/src/openbao/rename.ts +162 -0
- package/src/openbao/role-reconcile.test.ts +104 -0
- package/src/openbao/role-reconcile.ts +84 -0
- package/src/openbao/role-rename.test.ts +205 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface BaoJwtAuthConfigProps {
|
|
2
|
+
/** Auth mount path. Default `jwt`. */
|
|
3
|
+
mount?: string;
|
|
4
|
+
/** Issuer base URL, WITHOUT `/.well-known/openid-configuration` (the server refuses that). */
|
|
5
|
+
oidcDiscoveryUrl?: string;
|
|
6
|
+
/** PEM CA for the discovery URL, when its certificate is not publicly trusted. */
|
|
7
|
+
oidcDiscoveryCaPem?: string;
|
|
8
|
+
/** A JWKS URL, instead of discovery. */
|
|
9
|
+
jwksUrl?: string;
|
|
10
|
+
/** PEM CA for the JWKS URL. */
|
|
11
|
+
jwksCaPem?: string;
|
|
12
|
+
/** PEM PUBLIC keys, instead of either URL. */
|
|
13
|
+
jwtValidationPubkeys?: readonly string[];
|
|
14
|
+
/** Algorithms accepted. Default none — which the server reads as RS256. */
|
|
15
|
+
jwtSupportedAlgs?: readonly string[];
|
|
16
|
+
/** The `iss` a token must carry. */
|
|
17
|
+
boundIssuer?: string;
|
|
18
|
+
/** Role used when a login names none. Lower case. */
|
|
19
|
+
defaultRole?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface BaoJwtAuthConfigCanonical {
|
|
22
|
+
boundIssuer: string;
|
|
23
|
+
defaultRole: string;
|
|
24
|
+
jwksCaPem: string;
|
|
25
|
+
jwksUrl: string;
|
|
26
|
+
jwtSupportedAlgs: readonly string[];
|
|
27
|
+
jwtValidationPubkeys: readonly string[];
|
|
28
|
+
oidcDiscoveryCaPem: string;
|
|
29
|
+
oidcDiscoveryUrl: string;
|
|
30
|
+
}
|
|
31
|
+
/** ⛔ No secret — URLs, public keys, CA certificates, names. */
|
|
32
|
+
export interface BaoJwtAuthConfigAttributes extends BaoJwtAuthConfigCanonical {
|
|
33
|
+
mount: string;
|
|
34
|
+
digest: string;
|
|
35
|
+
}
|
|
36
|
+
export declare const canonicalFromProps: (props: BaoJwtAuthConfigProps) => BaoJwtAuthConfigCanonical;
|
|
37
|
+
export declare const canonicalFromLive: (live: Record<string, unknown>) => BaoJwtAuthConfigCanonical;
|
|
38
|
+
export declare const mountOf: (props: {
|
|
39
|
+
mount?: string;
|
|
40
|
+
}) => string;
|
|
41
|
+
export declare const configPath: (props: {
|
|
42
|
+
mount?: string;
|
|
43
|
+
}) => string;
|
|
44
|
+
export declare const attributesOf: (props: BaoJwtAuthConfigProps, live: Record<string, unknown>) => BaoJwtAuthConfigAttributes;
|
|
45
|
+
export declare const matches: (attributes: BaoJwtAuthConfigAttributes, props: BaoJwtAuthConfigProps) => boolean;
|
|
46
|
+
/** Every non-secret field — the write is a full replace, so what is left out is reset. */
|
|
47
|
+
export declare const writeBody: (props: BaoJwtAuthConfigProps) => Record<string, unknown>;
|
|
48
|
+
/** Live fields the full-replace write would reset, which this resource deliberately does not model. */
|
|
49
|
+
export declare const wouldErase: (live: Record<string, unknown>) => readonly string[];
|
|
50
|
+
export declare const problems: (props: BaoJwtAuthConfigProps) => readonly string[];
|
|
51
|
+
//# sourceMappingURL=jwt-config-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-config-form.d.ts","sourceRoot":"","sources":["../../src/openbao/jwt-config-form.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,+DAA+D;AAC/D,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAmBD,eAAO,MAAM,kBAAkB,UAAW,qBAAqB,KAAG,yBAU9D,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,yBAU9D,CAAC;AAEL,eAAO,MAAM,OAAO,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAAyC,CAAC;AAE9F,eAAO,MAAM,UAAU,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAAyC,CAAC;AAEjG,eAAO,MAAM,YAAY,UAChB,qBAAqB,QACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,0BAGF,CAAC;AAEF,eAAO,MAAM,OAAO,eAAgB,0BAA0B,SAAS,qBAAqB,YACnB,CAAC;AAE1E,0FAA0F;AAC1F,eAAO,MAAM,SAAS,UAAW,qBAAqB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAS7E,CAAC;AAEH,uGAAuG;AACvG,eAAO,MAAM,UAAU,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,SAAS,MAAM,EAazE,CAAC;AAEF,eAAO,MAAM,QAAQ,UAAW,qBAAqB,KAAG,SAAS,MAAM,EAgBtE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `auth/<mount>/config` for a JWT-validating mount — the issuer, keys and algorithms a login token
|
|
3
|
+
* is checked against. OPTIONAL, NON-SECRET, and deliberately unable to configure an OIDC browser
|
|
4
|
+
* login: read the ⛔ in jwt-config-form.ts for why `oidc_client_secret` stays out of Alchemy.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ THERE IS NO DELETE. openbao v2.6.2 path_config.go:126-141 defines read and update only; the
|
|
7
|
+
* config lives exactly as long as its mount. So `delete` here writes nothing and says so in its
|
|
8
|
+
* comment — it cannot be made to "remove" a config, and refusing would wedge every destroy. The
|
|
9
|
+
* `retain` default means it only runs when a stack opted into destroy.
|
|
10
|
+
*
|
|
11
|
+
* ★ REPLACE SEMANTICS (REPLACE.md): `mount` changed → `replace`: the new mount gets the config;
|
|
12
|
+
* the old one keeps its own until that mount is disabled. There is no identity but the mount.
|
|
13
|
+
*/
|
|
14
|
+
import { Resource } from 'alchemy';
|
|
15
|
+
import * as Provider from 'alchemy/Provider';
|
|
16
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
17
|
+
import { type BaoJwtAuthConfigAttributes, type BaoJwtAuthConfigProps } from './jwt-config-form.ts';
|
|
18
|
+
import { type RoleSpec } from './role-reconcile.ts';
|
|
19
|
+
export type { BaoJwtAuthConfigAttributes, BaoJwtAuthConfigProps } from './jwt-config-form.ts';
|
|
20
|
+
export interface BaoJwtAuthConfig extends Resource<'Bao.JwtAuthConfig', BaoJwtAuthConfigProps, BaoJwtAuthConfigAttributes, never, HttpClient.HttpClient> {
|
|
21
|
+
}
|
|
22
|
+
export declare const BaoJwtAuthConfig: import("alchemy").ResourceClass<BaoJwtAuthConfig>;
|
|
23
|
+
export declare const jwtConfigSpec: (props: BaoJwtAuthConfigProps) => RoleSpec<BaoJwtAuthConfigAttributes>;
|
|
24
|
+
export declare const BaoJwtAuthConfigProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoJwtAuthConfig>, never, HttpClient.HttpClient>;
|
|
25
|
+
//# sourceMappingURL=jwt-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-config.d.ts","sourceRoot":"","sources":["../../src/openbao/jwt-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAQ3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,QAAQ,EAAuC,MAAM,qBAAqB,CAAC;AAEzF,YAAY,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE9F,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAE3B,CAAC;AAEH,eAAO,MAAM,aAAa,UACjB,qBAAqB,KAC3B,QAAQ,CAAC,0BAA0B,CAQpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,uGA2BlC,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The form half of Bao.JwtRole — a role on a `jwt` or `oidc` auth mount (one plugin, two names).
|
|
3
|
+
*
|
|
4
|
+
* ★ READ FROM openbao v2.6.2 builtin/credential/jwt/path_role.go, NOT RECALLED:
|
|
5
|
+
* · The write MERGES (pathRoleCreateUpdate :494-720): almost every field is `GetOk`, so an
|
|
6
|
+
* omitted one keeps its stored value — EXCEPT four that are rebuilt from `data.Get` on every
|
|
7
|
+
* write and so RESET when omitted: `role_type` (to `oidc`! :520-526), `bound_claims_type`
|
|
8
|
+
* (`string`), `callback_mode` (`client`) and `oidc_disable_confirmation` (false). All four are
|
|
9
|
+
* managed here, and every managed field is sent on every write, defaults included, so a value
|
|
10
|
+
* set by hand cannot survive behind a green plan.
|
|
11
|
+
* · `name` is a lower-case string (:77): `Admins` and `admins` are one role. Refused, not folded.
|
|
12
|
+
* · A `jwt` role needs one bound constraint (audiences, subject, claims or token CIDRs) and an
|
|
13
|
+
* `oidc` role needs allowed_redirect_uris (:694-709) — refused here with the same rule.
|
|
14
|
+
* · `bound_claims` is a TypeMap and `claim_mappings` a TypeKVPairs: sent as JSON OBJECTS. A JSON
|
|
15
|
+
* string is refused for a map (the ⛔ on mapValue in ssh-role-form.ts, measured 2026-09-21).
|
|
16
|
+
* ⛔ `verbose_oidc_logging` AND `oidc_disable_confirmation` ARE ALWAYS SENT FALSE, and a live true
|
|
17
|
+
* plans `update`. The first logs received tokens and claims at debug level (:189-194); the second
|
|
18
|
+
* removes the confirmation page that guards direct-callback logins against token hijacking
|
|
19
|
+
* (RFC 8628 5.4, :205-210). Neither is a prop.
|
|
20
|
+
* ⚠️ LEFT ALONE (merged, never sent): the three leeways, `oauth2_metadata`, `max_age`,
|
|
21
|
+
* `poll_interval`, `user_claim_json_pointer`, `token_policies_template_claims`,
|
|
22
|
+
* `token_strictly_bind_ip`. A hand-set value there survives, and is not compared.
|
|
23
|
+
*/
|
|
24
|
+
import { type BaoTokenForm, type BaoTokenProps } from './auth-token-form.ts';
|
|
25
|
+
export type BaoJwtRoleType = 'jwt' | 'oidc';
|
|
26
|
+
export type BaoJwtCallbackMode = 'client' | 'device' | 'direct';
|
|
27
|
+
export interface BaoJwtRoleProps extends BaoTokenProps {
|
|
28
|
+
/** Auth mount path, no trailing slash. Default `jwt`; an OIDC mount is often `oidc`. */
|
|
29
|
+
mount?: string;
|
|
30
|
+
/** Role name — lower case, as the server stores it. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** ⛔ REQUIRED ON PURPOSE: the server's default is `oidc`, even when re-writing a `jwt` role. */
|
|
33
|
+
roleType: BaoJwtRoleType;
|
|
34
|
+
/** The claim that names the entity alias, e.g. `sub` or `email`. */
|
|
35
|
+
userClaim: string;
|
|
36
|
+
/** `aud` values; any one must match. */
|
|
37
|
+
boundAudiences?: readonly string[];
|
|
38
|
+
/** The `sub` a token must carry. */
|
|
39
|
+
boundSubject?: string;
|
|
40
|
+
/** How `boundClaims` values match. Default `string`. */
|
|
41
|
+
boundClaimsType?: 'glob' | 'string';
|
|
42
|
+
/** Claims that must match, each a value or a list of acceptable values. */
|
|
43
|
+
boundClaims?: Readonly<Record<string, string | readonly string[]>>;
|
|
44
|
+
/** Claim → alias metadata key. `role` is reserved, and two claims may not share a key. */
|
|
45
|
+
claimMappings?: Readonly<Record<string, string>>;
|
|
46
|
+
/** The claim that names identity group aliases. */
|
|
47
|
+
groupsClaim?: string;
|
|
48
|
+
/** OIDC only: the redirect URIs a login may return to. Required for `oidc`. */
|
|
49
|
+
allowedRedirectUris?: readonly string[];
|
|
50
|
+
/** OIDC only: scopes to request beyond `openid`. */
|
|
51
|
+
oidcScopes?: readonly string[];
|
|
52
|
+
/** OIDC only. Default `client`. */
|
|
53
|
+
callbackMode?: BaoJwtCallbackMode;
|
|
54
|
+
}
|
|
55
|
+
/** Every managed field, normalised. Both sides are built by `canonical`, so key order matches. */
|
|
56
|
+
export interface BaoJwtRoleCanonical extends BaoTokenForm {
|
|
57
|
+
allowedRedirectUris: readonly string[];
|
|
58
|
+
boundAudiences: readonly string[];
|
|
59
|
+
boundClaims: Readonly<Record<string, readonly string[]>>;
|
|
60
|
+
boundClaimsType: string;
|
|
61
|
+
boundSubject: string;
|
|
62
|
+
callbackMode: string;
|
|
63
|
+
claimMappings: Readonly<Record<string, string>>;
|
|
64
|
+
groupsClaim: string;
|
|
65
|
+
oidcDisableConfirmation: boolean;
|
|
66
|
+
oidcScopes: readonly string[];
|
|
67
|
+
roleType: string;
|
|
68
|
+
userClaim: string;
|
|
69
|
+
verboseOidcLogging: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** ⛔ No secret: claim names, audiences, URIs, policy names and TTLs. */
|
|
72
|
+
export interface BaoJwtRoleAttributes extends BaoJwtRoleCanonical {
|
|
73
|
+
mount: string;
|
|
74
|
+
name: string;
|
|
75
|
+
digest: string;
|
|
76
|
+
}
|
|
77
|
+
export declare const canonicalFromProps: (props: BaoJwtRoleProps) => BaoJwtRoleCanonical;
|
|
78
|
+
export declare const canonicalFromLive: (live: Record<string, unknown>) => BaoJwtRoleCanonical;
|
|
79
|
+
export declare const mountOf: (props: {
|
|
80
|
+
mount?: string;
|
|
81
|
+
}) => string;
|
|
82
|
+
export declare const rolePath: (props: {
|
|
83
|
+
mount?: string;
|
|
84
|
+
name: string;
|
|
85
|
+
}) => string;
|
|
86
|
+
export declare const attributesOf: (props: BaoJwtRoleProps, live: Record<string, unknown>) => BaoJwtRoleAttributes;
|
|
87
|
+
export declare const matches: (attributes: BaoJwtRoleAttributes, props: BaoJwtRoleProps) => boolean;
|
|
88
|
+
/** The PUT body — every managed field, the two fixed-false ones included (the ⛔ above). */
|
|
89
|
+
export declare const writeBody: (props: BaoJwtRoleProps) => Record<string, unknown>;
|
|
90
|
+
export declare const problems: (props: BaoJwtRoleProps) => readonly string[];
|
|
91
|
+
//# sourceMappingURL=jwt-role-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/jwt-role-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAMnB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,CAAC;AAC5C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,QAAQ,EAAE,cAAc,CAAC;IACzB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACpC,2EAA2E;IAC3E,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACnE,0FAA0F;IAC1F,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,oDAAoD;IACpD,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,mCAAmC;IACnC,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED,kGAAkG;AAClG,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACzD,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,OAAO,CAAC;IACjC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,wEAAwE;AACxE,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AA8CD,eAAO,MAAM,kBAAkB,UAAW,eAAe,KAAG,mBAgBxD,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,mBAgB9D,CAAC;AAEL,eAAO,MAAM,OAAO,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAAyC,CAAC;AAE9F,eAAO,MAAM,QAAQ,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,MACtB,CAAC;AAE9C,eAAO,MAAM,YAAY,UAChB,eAAe,QAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,oBAGF,CAAC;AAEF,eAAO,MAAM,OAAO,eAAgB,oBAAoB,SAAS,eAAe,KAAG,OACV,CAAC;AAE1E,2FAA2F;AAC3F,eAAO,MAAM,SAAS,UAAW,eAAe,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAevE,CAAC;AAIH,eAAO,MAAM,QAAQ,UAAW,eAAe,KAAG,SAAS,MAAM,EAuBhE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A role on a `jwt` or `oidc` auth mount — `auth/<mount>/role/<name>`: which tokens may log in,
|
|
3
|
+
* how the entity is named, and what the resulting OpenBao token carries. METADATA ONLY.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ NO SECRET IN PROPS OR ATTRIBUTES — see policy.ts. A role holds claim names, audiences, redirect
|
|
6
|
+
* URIs and policy names. The mount's OIDC client secret is config, not role, and stays out of
|
|
7
|
+
* Alchemy entirely (the ⛔ in jwt-config.ts).
|
|
8
|
+
* ⛔ HUMANS GET A NON-ADMIN DEFAULT (machine-access plan, "Admin boundary"): a human role's
|
|
9
|
+
* `tokenPolicies` should not include an admin policy, and admin needs login MFA
|
|
10
|
+
* (mfa-enforcement.ts). This resource does not know which policy is admin, so it cannot refuse —
|
|
11
|
+
* the stack that declares the role owns that line.
|
|
12
|
+
* ⚠️ DELETING A ROLE DOES NOT REVOKE THE TOKENS IT ISSUED. path_role.go:478 pathRoleDelete removes the
|
|
13
|
+
* storage entry and nothing else; those tokens live to their TTL.
|
|
14
|
+
*
|
|
15
|
+
* ★ REPLACE SEMANTICS (REPLACE.md): `mount` or `name` changed → `replace`, create-first. The new
|
|
16
|
+
* path cannot collide with the old, so no `deleteFirst`. Under the default `retain` the old role
|
|
17
|
+
* stays live, and still admits logins, until removed by hand.
|
|
18
|
+
* ★ `defaultRemovalPolicy: 'retain'`, like every Bao.* family.
|
|
19
|
+
*/
|
|
20
|
+
import { Resource } from 'alchemy';
|
|
21
|
+
import * as Provider from 'alchemy/Provider';
|
|
22
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
23
|
+
import { type BaoJwtRoleAttributes, type BaoJwtRoleProps } from './jwt-role-form.ts';
|
|
24
|
+
import { type RoleSpec } from './role-reconcile.ts';
|
|
25
|
+
export type { BaoJwtCallbackMode, BaoJwtRoleAttributes, BaoJwtRoleProps, BaoJwtRoleType, } from './jwt-role-form.ts';
|
|
26
|
+
export interface BaoJwtRole extends Resource<'Bao.JwtRole', BaoJwtRoleProps, BaoJwtRoleAttributes, never, HttpClient.HttpClient> {
|
|
27
|
+
}
|
|
28
|
+
export declare const BaoJwtRole: import("alchemy").ResourceClass<BaoJwtRole>;
|
|
29
|
+
export declare const jwtRoleSpec: (props: BaoJwtRoleProps) => RoleSpec<BaoJwtRoleAttributes>;
|
|
30
|
+
export declare const BaoJwtRoleProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoJwtRole>, never, HttpClient.HttpClient>;
|
|
31
|
+
//# sourceMappingURL=jwt-role.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-role.d.ts","sourceRoot":"","sources":["../../src/openbao/jwt-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EAOrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,QAAQ,EAAuC,MAAM,qBAAqB,CAAC;AAEzF,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAErB,CAAC;AAEH,eAAO,MAAM,WAAW,UAAW,eAAe,KAAG,QAAQ,CAAC,oBAAoB,CAOhF,CAAC;AAEH,eAAO,MAAM,kBAAkB,iGAoC5B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The form half of Bao.KubernetesRole — `auth/<mount>/role/<name>` on a `kubernetes` auth mount:
|
|
3
|
+
* which service accounts, in which namespaces, may log in, and what their token carries.
|
|
4
|
+
*
|
|
5
|
+
* ★ READ FROM openbao v2.6.2 builtin/credential/kubernetes/path_role.go, NOT RECALLED:
|
|
6
|
+
* · The write MERGES for names, namespaces, audience and alias source (`GetOk`, :335-392), but
|
|
7
|
+
* `bound_service_account_namespace_selector` is `data.Get` — RESET when omitted (:353). Every
|
|
8
|
+
* field is therefore sent on every write, defaults included.
|
|
9
|
+
* · Names may not be empty, and `*` may not be mixed with other names; namespaces may be empty
|
|
10
|
+
* only when a selector is set, with the same `*` rule (:335-372). Refused here first.
|
|
11
|
+
* · Roles are stored under the LOWER-CASED name (:396, backend.go:361), so a mixed-case name
|
|
12
|
+
* would read back as a different string. Refused, not folded.
|
|
13
|
+
* · `audience` is omitted from a read when empty (:206-208); read as `''`.
|
|
14
|
+
* ⚠️ `aliasNameSource` DECIDES WHO A POD IS. `serviceaccount_uid` (OpenBao's default) names the
|
|
15
|
+
* entity alias by the service account's UID; `serviceaccount_name` by `<namespace>/<name>`
|
|
16
|
+
* (backend.go:30-34). Changing it on a live role re-names every pod's alias, so their next logins
|
|
17
|
+
* land on NEW entities and attribution splits.
|
|
18
|
+
* ⛔ SO IT IS REQUIRED, NOT DEFAULTED (review 2026-09-21). Every field is sent on every write, so a
|
|
19
|
+
* default here would be WRITTEN: adopting a hand-made `serviceaccount_name` role with the prop
|
|
20
|
+
* left out would plan a bare `update` and silently re-key every pod onto new entities. The
|
|
21
|
+
* machine-access plan chose `serviceaccount_name`; the server's default is the other one — a
|
|
22
|
+
* choice this important is made in the declaration, the way Bao.JwtRole's `roleType` is.
|
|
23
|
+
*/
|
|
24
|
+
import { type BaoTokenForm, type BaoTokenProps } from './auth-token-form.ts';
|
|
25
|
+
export type BaoKubernetesAliasSource = 'serviceaccount_name' | 'serviceaccount_uid';
|
|
26
|
+
export interface BaoKubernetesRoleProps extends BaoTokenProps {
|
|
27
|
+
/** Auth mount path. Default `kubernetes`. */
|
|
28
|
+
mount?: string;
|
|
29
|
+
/** Role name — lower case, as the server stores it. */
|
|
30
|
+
name: string;
|
|
31
|
+
/** Service account names; `['*']` for any. Never empty. */
|
|
32
|
+
boundServiceAccountNames: readonly string[];
|
|
33
|
+
/** Namespaces; `['*']` for any. May be empty only with a selector. */
|
|
34
|
+
boundServiceAccountNamespaces?: readonly string[];
|
|
35
|
+
/** A label selector (JSON or YAML) for namespaces, ORed with the list. */
|
|
36
|
+
boundServiceAccountNamespaceSelector?: string;
|
|
37
|
+
/** The `aud` a service account token must carry. Default none. */
|
|
38
|
+
audience?: string;
|
|
39
|
+
/** ⛔ Required — read the ⚠️ and ⛔ above before changing it on a live role. */
|
|
40
|
+
aliasNameSource: BaoKubernetesAliasSource;
|
|
41
|
+
}
|
|
42
|
+
export interface BaoKubernetesRoleCanonical extends BaoTokenForm {
|
|
43
|
+
aliasNameSource: string;
|
|
44
|
+
audience: string;
|
|
45
|
+
boundServiceAccountNames: readonly string[];
|
|
46
|
+
boundServiceAccountNamespaceSelector: string;
|
|
47
|
+
boundServiceAccountNamespaces: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
/** ⛔ No secret: account names, namespaces, policy names, TTLs. */
|
|
50
|
+
export interface BaoKubernetesRoleAttributes extends BaoKubernetesRoleCanonical {
|
|
51
|
+
mount: string;
|
|
52
|
+
name: string;
|
|
53
|
+
digest: string;
|
|
54
|
+
}
|
|
55
|
+
export declare const canonicalFromProps: (props: BaoKubernetesRoleProps) => BaoKubernetesRoleCanonical;
|
|
56
|
+
export declare const canonicalFromLive: (live: Record<string, unknown>) => BaoKubernetesRoleCanonical;
|
|
57
|
+
export declare const mountOf: (props: {
|
|
58
|
+
mount?: string;
|
|
59
|
+
}) => string;
|
|
60
|
+
export declare const rolePath: (props: {
|
|
61
|
+
mount?: string;
|
|
62
|
+
name: string;
|
|
63
|
+
}) => string;
|
|
64
|
+
export declare const attributesOf: (props: BaoKubernetesRoleProps, live: Record<string, unknown>) => BaoKubernetesRoleAttributes;
|
|
65
|
+
export declare const matches: (attributes: BaoKubernetesRoleAttributes, props: BaoKubernetesRoleProps) => boolean;
|
|
66
|
+
export declare const writeBody: (props: BaoKubernetesRoleProps) => Record<string, unknown>;
|
|
67
|
+
export declare const problems: (props: BaoKubernetesRoleProps) => readonly string[];
|
|
68
|
+
//# sourceMappingURL=kubernetes-role-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubernetes-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/kubernetes-role-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAMnB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,wBAAwB,GAAG,qBAAqB,GAAG,oBAAoB,CAAC;AAEpF,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,wBAAwB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,sEAAsE;IACtE,6BAA6B,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClD,0EAA0E;IAC1E,oCAAoC,CAAC,EAAE,MAAM,CAAC;IAC9C,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,wBAAwB,CAAC;CAC3C;AAED,MAAM,WAAW,0BAA2B,SAAQ,YAAY;IAC9D,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,oCAAoC,EAAE,MAAM,CAAC;IAC7C,6BAA6B,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED,kEAAkE;AAClE,MAAM,WAAW,2BAA4B,SAAQ,0BAA0B;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAgBD,eAAO,MAAM,kBAAkB,UAAW,sBAAsB,KAAG,0BAQ/D,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,0BAQ9D,CAAC;AAEL,eAAO,MAAM,OAAO,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MACZ,CAAC;AAEzC,eAAO,MAAM,QAAQ,UAAW;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAG,MACtB,CAAC;AAE9C,eAAO,MAAM,YAAY,UAChB,sBAAsB,QACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,2BAGF,CAAC;AAEF,eAAO,MAAM,OAAO,eACN,2BAA2B,SAChC,sBAAsB,KAC5B,OAAkF,CAAC;AAEtF,eAAO,MAAM,SAAS,UAAW,sBAAsB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAO9E,CAAC;AAMH,eAAO,MAAM,QAAQ,UAAW,sBAAsB,KAAG,SAAS,MAAM,EAavE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A role on a `kubernetes` auth mount — `auth/<mount>/role/<name>`: which service accounts may log
|
|
3
|
+
* in and what their token carries. METADATA ONLY. The machine-access plan gives k8s pods this
|
|
4
|
+
* instead of AppRole: one role per namespace/service account.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ NO SECRET IN PROPS OR ATTRIBUTES — see policy.ts. The mount's own config (the cluster's CA and
|
|
7
|
+
* the token reviewer JWT) is not declared here, and the reviewer JWT must never be.
|
|
8
|
+
* ⚠️ DELETING A ROLE DOES NOT REVOKE THE TOKENS IT ISSUED — pathRoleDelete removes the storage entry
|
|
9
|
+
* and nothing else (path_role.go:239-256).
|
|
10
|
+
*
|
|
11
|
+
* ★ REPLACE SEMANTICS (REPLACE.md): `mount` or `name` changed → `replace`, create-first (the new
|
|
12
|
+
* path cannot collide). Under the default `retain` the old role stays live until removed by hand.
|
|
13
|
+
*/
|
|
14
|
+
import { Resource } from 'alchemy';
|
|
15
|
+
import * as Provider from 'alchemy/Provider';
|
|
16
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
17
|
+
import { type BaoKubernetesRoleAttributes, type BaoKubernetesRoleProps } from './kubernetes-role-form.ts';
|
|
18
|
+
import { type RoleSpec } from './role-reconcile.ts';
|
|
19
|
+
export type { BaoKubernetesAliasSource, BaoKubernetesRoleAttributes, BaoKubernetesRoleProps, } from './kubernetes-role-form.ts';
|
|
20
|
+
export interface BaoKubernetesRole extends Resource<'Bao.KubernetesRole', BaoKubernetesRoleProps, BaoKubernetesRoleAttributes, never, HttpClient.HttpClient> {
|
|
21
|
+
}
|
|
22
|
+
export declare const BaoKubernetesRole: import("alchemy").ResourceClass<BaoKubernetesRole>;
|
|
23
|
+
export declare const kubernetesRoleSpec: (props: BaoKubernetesRoleProps) => RoleSpec<BaoKubernetesRoleAttributes>;
|
|
24
|
+
export declare const BaoKubernetesRoleProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoKubernetesRole>, never, HttpClient.HttpClient>;
|
|
25
|
+
//# sourceMappingURL=kubernetes-role.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubernetes-role.d.ts","sourceRoot":"","sources":["../../src/openbao/kubernetes-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAO5B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,QAAQ,EAAuC,MAAM,qBAAqB,CAAC;AAEzF,YAAY,EACV,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,UACtB,sBAAsB,KAC5B,QAAQ,CAAC,2BAA2B,CAOrC,CAAC;AAEH,eAAO,MAAM,yBAAyB,wGAiCnC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface BaoMfaLoginEnforcementProps {
|
|
2
|
+
/** Enforcement name, unique per namespace. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Method ids — typically `[totp.methodId]`. Any ONE listed method passing is enough. */
|
|
5
|
+
mfaMethodIds: readonly string[];
|
|
6
|
+
/**
|
|
7
|
+
* Auth mount PATHS (`oidc`, `jwt`), resolved to their accessors at plan and deploy time.
|
|
8
|
+
* ⚠️ A plain string makes no graph edge. When the mount is a BaoAuthMethod in the same stack, pass
|
|
9
|
+
* its output (`[oidc.path]`) so it exists before this resolves — otherwise the deploy can reach
|
|
10
|
+
* this first and refuse with "no auth mount at …".
|
|
11
|
+
*/
|
|
12
|
+
authMethodPaths?: readonly string[];
|
|
13
|
+
/** Raw accessors (`auth_jwt_…`), for a mount outside this stack. */
|
|
14
|
+
authMethodAccessors?: readonly string[];
|
|
15
|
+
/** Auth mount TYPES — read the ⚠️ above first. */
|
|
16
|
+
authMethodTypes?: readonly string[];
|
|
17
|
+
/** Identity group ids whose members must pass MFA, on any mount. */
|
|
18
|
+
identityGroupIds?: readonly string[];
|
|
19
|
+
/** Identity entity ids, likewise. */
|
|
20
|
+
identityEntityIds?: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** The live shape, and the declared one once paths are resolved to accessors. */
|
|
23
|
+
export interface BaoMfaEnforcementCanonical {
|
|
24
|
+
authMethodAccessors: readonly string[];
|
|
25
|
+
authMethodTypes: readonly string[];
|
|
26
|
+
identityEntityIds: readonly string[];
|
|
27
|
+
identityGroupIds: readonly string[];
|
|
28
|
+
mfaMethodIds: readonly string[];
|
|
29
|
+
}
|
|
30
|
+
/** ⛔ No secret — ids, accessors and names. */
|
|
31
|
+
export interface BaoMfaLoginEnforcementAttributes extends BaoMfaEnforcementCanonical {
|
|
32
|
+
name: string;
|
|
33
|
+
/** The server-made id. */
|
|
34
|
+
enforcementId: string;
|
|
35
|
+
namespacePath: string;
|
|
36
|
+
digest: string;
|
|
37
|
+
}
|
|
38
|
+
/** The declaration, with `authMethodPaths` already resolved into `resolved` accessors. */
|
|
39
|
+
export declare const canonicalFromProps: (props: BaoMfaLoginEnforcementProps, resolved: readonly string[]) => BaoMfaEnforcementCanonical;
|
|
40
|
+
export declare const canonicalFromLive: (live: Record<string, unknown>) => BaoMfaEnforcementCanonical;
|
|
41
|
+
export declare const attributesOf: (name: string, live: Record<string, unknown>) => BaoMfaLoginEnforcementAttributes;
|
|
42
|
+
export declare const matches: (attributes: BaoMfaLoginEnforcementAttributes, props: BaoMfaLoginEnforcementProps, resolved: readonly string[]) => boolean;
|
|
43
|
+
/** Every list, as a JSON array, every time — the ⚠️ on GetOk above. */
|
|
44
|
+
export declare const writeBody: (form: BaoMfaEnforcementCanonical) => Record<string, unknown>;
|
|
45
|
+
export declare const problems: (props: BaoMfaLoginEnforcementProps, missingPaths: readonly string[]) => readonly string[];
|
|
46
|
+
//# sourceMappingURL=mfa-enforcement-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-enforcement-form.d.ts","sourceRoot":"","sources":["../../src/openbao/mfa-enforcement-form.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,2BAA2B;IAC1C,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,kDAAkD;IAClD,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,qCAAqC;IACrC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,iFAAiF;AACjF,MAAM,WAAW,0BAA0B;IACzC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED,8CAA8C;AAC9C,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAgBD,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB,UACtB,2BAA2B,YACxB,SAAS,MAAM,EAAE,KAC1B,0BAOC,CAAC;AAEL,eAAO,MAAM,iBAAiB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,0BAO9D,CAAC;AAEL,eAAO,MAAM,YAAY,SACjB,MAAM,QACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,gCASF,CAAC;AAEF,eAAO,MAAM,OAAO,eACN,gCAAgC,SACrC,2BAA2B,YACxB,SAAS,MAAM,EAAE,KAC1B,OAA4F,CAAC;AAEhG,uEAAuE;AACvE,eAAO,MAAM,SAAS,SAAU,0BAA0B,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAMjF,CAAC;AAIH,eAAO,MAAM,QAAQ,UACZ,2BAA2B,gBACpB,SAAS,MAAM,EAAE,KAC9B,SAAS,MAAM,EAgBjB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A login-MFA enforcement — `identity/mfa/login-enforcement/<name>`: logins through these mounts
|
|
3
|
+
* (or by these groups/entities) must also pass one of these MFA methods. The machine-access plan's
|
|
4
|
+
* "admin requires OpenBao login MFA" is this resource.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ ENROL BEFORE YOU ENFORCE. A login the enforcement matches, by an entity with no secret for any
|
|
7
|
+
* listed method, FAILS (login_mfa.go validateLoginMFAInternal: "MFA secret … not present"). Enrol
|
|
8
|
+
* every admin (`identity/mfa/method/totp/admin-generate`, which RETURNS the secret — never through
|
|
9
|
+
* Alchemy) before the first deploy of this, or the admin lane locks itself out.
|
|
10
|
+
* ⚠️ IT MATCHES MOUNTS, GROUPS AND ENTITIES — NEVER ROLES. An enforcement on the `jwt/` accessor
|
|
11
|
+
* covers every role on that mount (login_mfa.go buildMFAEnforcementConfigList :932-1017). To put
|
|
12
|
+
* MFA on admin only, give admin its own auth mount, or target an identity group of admins.
|
|
13
|
+
* ⛔ THE DELETE IS REFUSED: IN 2.6.2 IT DOES NOT STICK. DeleteMFALoginEnforcementConfigByNameAndNamespace
|
|
14
|
+
* (login_mfa.go:1879-1902) removes the entry from memory and never from storage, so it RETURNS on the
|
|
15
|
+
* next unseal — tracked upstream as openbao/openbao#4030, open on 2026-09-21. Worse, a method
|
|
16
|
+
* deleted in between leaves the resurrected enforcement naming an id that no longer exists, and
|
|
17
|
+
* every login it matches then fails ("MFA method configuration not present", :871-877). A delete
|
|
18
|
+
* that reports success and quietly comes back is the lie this package refuses to tell. Under the
|
|
19
|
+
* default `retain` it never runs; with `destroy`, set `retain` to drop it from state and leave it
|
|
20
|
+
* live — which is what the server would do anyway.
|
|
21
|
+
*
|
|
22
|
+
* ★ REPLACE SEMANTICS (REPLACE.md): `name` changed → `replace`, create-first (names are per
|
|
23
|
+
* namespace; the new one cannot collide). Under `retain` BOTH enforcements then apply — fail
|
|
24
|
+
* closed. Under `destroy` the old one's delete is refused, so the deploy stops after creating.
|
|
25
|
+
*/
|
|
26
|
+
import { Resource } from 'alchemy';
|
|
27
|
+
import * as Provider from 'alchemy/Provider';
|
|
28
|
+
import * as Effect from 'effect/Effect';
|
|
29
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
30
|
+
import type { BaoError } from './bao-status.ts';
|
|
31
|
+
import { type BaoMfaLoginEnforcementAttributes, type BaoMfaLoginEnforcementProps } from './mfa-enforcement-form.ts';
|
|
32
|
+
export type { BaoMfaLoginEnforcementAttributes, BaoMfaLoginEnforcementProps, } from './mfa-enforcement-form.ts';
|
|
33
|
+
export interface BaoMfaLoginEnforcement extends Resource<'Bao.MfaLoginEnforcement', BaoMfaLoginEnforcementProps, BaoMfaLoginEnforcementAttributes, never, HttpClient.HttpClient> {
|
|
34
|
+
}
|
|
35
|
+
export declare const BaoMfaLoginEnforcement: import("alchemy").ResourceClass<BaoMfaLoginEnforcement>;
|
|
36
|
+
type Env = HttpClient.HttpClient;
|
|
37
|
+
export declare const planEnforcement: (props: BaoMfaLoginEnforcementProps) => Effect.Effect<'noop' | 'update', BaoError, Env>;
|
|
38
|
+
/** Write when live differs, then prove it by reading back. */
|
|
39
|
+
export declare const reconcileEnforcement: (props: BaoMfaLoginEnforcementProps) => Effect.Effect<BaoMfaLoginEnforcementAttributes, BaoError, Env>;
|
|
40
|
+
export declare const BaoMfaLoginEnforcementProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoMfaLoginEnforcement>, never, HttpClient.HttpClient>;
|
|
41
|
+
//# sourceMappingURL=mfa-enforcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-enforcement.d.ts","sourceRoot":"","sources":["../../src/openbao/mfa-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,KAAK,gCAAgC,EACrC,KAAK,2BAA2B,EAMjC,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,sBAAuB,SAAQ,QAAQ,CACtD,yBAAyB,EACzB,2BAA2B,EAC3B,gCAAgC,EAChC,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,sBAAsB,yDAEjC,CAAC;AAEH,KAAK,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC;AAmBjC,eAAO,MAAM,eAAe,UACnB,2BAA2B,KACjC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAM7C,CAAC;AAEL,8DAA8D;AAC9D,eAAO,MAAM,oBAAoB,UACxB,2BAA2B,KACjC,MAAM,CAAC,MAAM,CAAC,gCAAgC,EAAE,QAAQ,EAAE,GAAG,CAe5D,CAAC;AAEL,eAAO,MAAM,8BAA8B,6GAkCxC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type BaoTotpAlgorithm = 'SHA1' | 'SHA256' | 'SHA512';
|
|
2
|
+
export interface BaoMfaTotpMethodProps {
|
|
3
|
+
/** `method_name` — the declarative key, unique per namespace. ⛔ A rename is refused (mfa-totp.ts). */
|
|
4
|
+
name: string;
|
|
5
|
+
/** The issuer an authenticator app shows, e.g. `vault-example`. */
|
|
6
|
+
issuer: string;
|
|
7
|
+
/** Code lifetime. Default `30s`. */
|
|
8
|
+
period?: string;
|
|
9
|
+
/** Secret size in bytes. Default 20. */
|
|
10
|
+
keySize?: number;
|
|
11
|
+
/** QR code size in pixels; `0` for none. Default 200. */
|
|
12
|
+
qrSize?: number;
|
|
13
|
+
/** Default `SHA1` — the one every authenticator app supports. */
|
|
14
|
+
algorithm?: BaoTotpAlgorithm;
|
|
15
|
+
/** Default 6. */
|
|
16
|
+
digits?: 6 | 8;
|
|
17
|
+
/** Periods of clock skew accepted. Default 1. */
|
|
18
|
+
skew?: 0 | 1;
|
|
19
|
+
/** Failed codes before the entity is locked out of this method. Default 5. */
|
|
20
|
+
maxValidationAttempts?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface BaoMfaTotpCanonical {
|
|
23
|
+
algorithm: string;
|
|
24
|
+
digits: number;
|
|
25
|
+
issuer: string;
|
|
26
|
+
keySize: number;
|
|
27
|
+
maxValidationAttempts: number;
|
|
28
|
+
periodSeconds: number;
|
|
29
|
+
qrSize: number;
|
|
30
|
+
skew: number;
|
|
31
|
+
}
|
|
32
|
+
export interface BaoMfaTotpMethodAttributes extends BaoMfaTotpCanonical {
|
|
33
|
+
/** The server-made UUID — what a Bao.MfaLoginEnforcement names in `mfaMethodIds`. */
|
|
34
|
+
methodId: string;
|
|
35
|
+
name: string;
|
|
36
|
+
/** `''` for root, `team-a/` below it. */
|
|
37
|
+
namespacePath: string;
|
|
38
|
+
digest: string;
|
|
39
|
+
}
|
|
40
|
+
export declare const canonicalFromProps: (props: BaoMfaTotpMethodProps) => BaoMfaTotpCanonical;
|
|
41
|
+
export declare const canonicalFromLive: (live: Record<string, unknown>) => BaoMfaTotpCanonical;
|
|
42
|
+
export declare const attributesOf: (methodId: string, live: Record<string, unknown>) => BaoMfaTotpMethodAttributes;
|
|
43
|
+
export declare const matches: (attributes: BaoMfaTotpMethodAttributes, props: BaoMfaTotpMethodProps) => boolean;
|
|
44
|
+
/** The upsert body — by `method_name`, never `method_id` (mfa-wire.ts). */
|
|
45
|
+
export declare const writeBody: (props: BaoMfaTotpMethodProps) => Record<string, unknown>;
|
|
46
|
+
/**
|
|
47
|
+
* The diff's refusal for a changed name, or undefined when the name is unchanged. Never a
|
|
48
|
+
* `replace`: the ⛔ on a rename in mfa-totp.ts says why.
|
|
49
|
+
*/
|
|
50
|
+
export declare const renameProblem: (from: string, to: string) => string | undefined;
|
|
51
|
+
export declare const problems: (props: BaoMfaTotpMethodProps) => readonly string[];
|
|
52
|
+
//# sourceMappingURL=mfa-totp-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-totp-form.d.ts","sourceRoot":"","sources":["../../src/openbao/mfa-totp-form.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,sGAAsG;IACtG,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,iBAAiB;IACjB,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACf,iDAAiD;IACjD,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,8EAA8E;IAC9E,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,0BAA2B,SAAQ,mBAAmB;IACrE,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAaD,eAAO,MAAM,kBAAkB,UAAW,qBAAqB,KAAG,mBAU9D,CAAC;AAKL,eAAO,MAAM,iBAAiB,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,mBAU9D,CAAC;AAEL,eAAO,MAAM,YAAY,aACb,MAAM,QACV,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,0BASF,CAAC;AAEF,eAAO,MAAM,OAAO,eAAgB,0BAA0B,SAAS,qBAAqB,YACnB,CAAC;AAE1E,2EAA2E;AAC3E,eAAO,MAAM,SAAS,UAAW,qBAAqB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAa9E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,SAAU,MAAM,MAAM,MAAM,KAAG,MAAM,GAAG,SAK5C,CAAC;AAEvB,eAAO,MAAM,QAAQ,UAAW,qBAAqB,KAAG,SAAS,MAAM,EAWtE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A TOTP login-MFA method — `identity/mfa/method/totp`, found by `method_name`. The method is
|
|
3
|
+
* policy only: issuer, period, digits. Its `methodId` is what a Bao.MfaLoginEnforcement names.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ A RENAME IS REFUSED, NOT REPLACED. A new name is a new method with a new id, and every entity's
|
|
6
|
+
* enrolled secret belongs to the OLD id (login_mfa.go validateTOTP reads `entity.MFASecrets[id]`).
|
|
7
|
+
* The enforcement would follow the new id in the same deploy, and every enrolled admin would then
|
|
8
|
+
* fail MFA — with nobody left able to log in and re-enrol them. The safe move is additive: declare
|
|
9
|
+
* the new method as a SECOND resource, list both ids on the enforcement (any one method passes,
|
|
10
|
+
* login_mfa.go:831-858), enrol everyone, then drop the old one.
|
|
11
|
+
* ⛔ DELETING THE METHOD DESTROYS EVERY ENROLLED SECRET FOR IT (mfa-wire.ts deleteTotp) — and with
|
|
12
|
+
* openbao/openbao#4030, a deleted enforcement can come back after a restart still naming this id,
|
|
13
|
+
* failing every login it matches. `retain` is the default for exactly that reason.
|
|
14
|
+
*
|
|
15
|
+
* ★ REPLACE SEMANTICS (REPLACE.md): never `replace`. `name` changed → the plan FAILS (above); any
|
|
16
|
+
* other field is an in-place `update`, because the upsert keeps the id.
|
|
17
|
+
*/
|
|
18
|
+
import { Resource } from 'alchemy';
|
|
19
|
+
import * as Provider from 'alchemy/Provider';
|
|
20
|
+
import * as Effect from 'effect/Effect';
|
|
21
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
22
|
+
import type { BaoError } from './bao-status.ts';
|
|
23
|
+
import { type BaoMfaTotpMethodAttributes, type BaoMfaTotpMethodProps } from './mfa-totp-form.ts';
|
|
24
|
+
export type { BaoMfaTotpMethodAttributes, BaoMfaTotpMethodProps, BaoTotpAlgorithm, } from './mfa-totp-form.ts';
|
|
25
|
+
export interface BaoMfaTotpMethod extends Resource<'Bao.MfaTotpMethod', BaoMfaTotpMethodProps, BaoMfaTotpMethodAttributes, never, HttpClient.HttpClient> {
|
|
26
|
+
}
|
|
27
|
+
export declare const BaoMfaTotpMethod: import("alchemy").ResourceClass<BaoMfaTotpMethod>;
|
|
28
|
+
export declare const readTotp: (props: BaoMfaTotpMethodProps) => Effect.Effect<BaoMfaTotpMethodAttributes | undefined, BaoError, HttpClient.HttpClient>;
|
|
29
|
+
export declare const planTotp: (props: BaoMfaTotpMethodProps) => Effect.Effect<'noop' | 'update', BaoError, HttpClient.HttpClient>;
|
|
30
|
+
/** Upsert by name when live differs, then prove it by reading back. */
|
|
31
|
+
export declare const reconcileTotp: (props: BaoMfaTotpMethodProps) => Effect.Effect<BaoMfaTotpMethodAttributes, BaoError, HttpClient.HttpClient>;
|
|
32
|
+
export declare const BaoMfaTotpMethodProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoMfaTotpMethod>, never, HttpClient.HttpClient>;
|
|
33
|
+
//# sourceMappingURL=mfa-totp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-totp.d.ts","sourceRoot":"","sources":["../../src/openbao/mfa-totp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAM3B,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EACV,0BAA0B,EAC1B,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAE3B,CAAC;AAKH,eAAO,MAAM,QAAQ,UACZ,qBAAqB,KAC3B,MAAM,CAAC,MAAM,CAAC,0BAA0B,GAAG,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAGrF,CAAC;AAEJ,eAAO,MAAM,QAAQ,UACZ,qBAAqB,KAC3B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAK/D,CAAC;AAEL,uEAAuE;AACvE,eAAO,MAAM,aAAa,UACjB,qBAAqB,KAC3B,MAAM,CAAC,MAAM,CAAC,0BAA0B,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAWxE,CAAC;AAEL,eAAO,MAAM,wBAAwB,uGA8BlC,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OpenBao calls behind login MFA: TOTP methods under identity/mfa/method/totp, login
|
|
3
|
+
* enforcements under identity/mfa/login-enforcement/<name>, and the auth table that turns a mount
|
|
4
|
+
* path into the accessor an enforcement names.
|
|
5
|
+
*
|
|
6
|
+
* ★ OpenBao 2.6.2 SHIPS LOGIN MFA — confirmed in source before any of this was written:
|
|
7
|
+
* vault/identity/store.go:312-800 (mfaPaths) registers `mfa/method/totp`, `mfa/login-enforcement/
|
|
8
|
+
* <name>` and their listings on the identity backend; vault/identity/mfa.go holds the handlers
|
|
9
|
+
* and vault/login_mfa.go the storage.
|
|
10
|
+
* ★ A TOTP METHOD IS FOUND BY NAME, NOT ID. The id is a server-made UUID; `method_name` is the only
|
|
11
|
+
* thing a declaration can know. An update without `method_id` UPSERTS by name within the request's
|
|
12
|
+
* namespace (identity/mfa.go:159-230, MemDBMFAConfigByName keys on namespace + name), so the same
|
|
13
|
+
* write both creates and updates. The listing (`key_info`, one entry per id, :63-69) answers the
|
|
14
|
+
* methods of this namespace AND its parents (login_mfa.go:652-713), so the match also requires
|
|
15
|
+
* this namespace's `namespace_path`.
|
|
16
|
+
* ⛔ THE NAME INDEX IGNORES THE TYPE, SO THIS LISTS EVERY TYPE. login_mfa.go's `name` index is
|
|
17
|
+
* (namespace_id, name) across all methods (:1642-1655, :1818), and handleMFAMethodUpdateCommon
|
|
18
|
+
* then sets `mConfig.Type = "totp"` on whatever it found (identity/mfa.go:228). A TOTP write under
|
|
19
|
+
* the name of a Duo or Okta method would silently CONVERT that method — and every enforcement
|
|
20
|
+
* naming its id would start demanding a TOTP code nobody is enrolled for. The per-type listing
|
|
21
|
+
* (`.../method/totp?list=true`) cannot see the clash, so this reads the global one
|
|
22
|
+
* (`identity/mfa/method?list=true`, store.go:335) and refuses a same-named method of
|
|
23
|
+
* another type. ⚠️ The deploying token needs `list` on `identity/mfa/method`.
|
|
24
|
+
* ⚠️ AN UPDATE BY A STALE `method_id` IS A SILENT NO-OP: the handler returns nil for an unknown id
|
|
25
|
+
* (identity/mfa.go:172-183), which HTTP answers 204. So this never writes by id.
|
|
26
|
+
*/
|
|
27
|
+
import * as Effect from 'effect/Effect';
|
|
28
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
29
|
+
import type { BaoError } from './bao-status.ts';
|
|
30
|
+
export declare const TOTP_PATH = "identity/mfa/method/totp";
|
|
31
|
+
/** Every MFA method of every type — the only listing that can see a name clash across types. */
|
|
32
|
+
export declare const METHODS_PATH = "identity/mfa/method";
|
|
33
|
+
export declare const enforcementPath: (name: string) => string;
|
|
34
|
+
/** This call's namespace as OpenBao writes `namespace_path`: `''` or `team-a/`. */
|
|
35
|
+
export declare const ownNamespacePath: Effect.Effect<string>;
|
|
36
|
+
export type TotpEntry = {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly live: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The TOTP method named `name` in this namespace, or undefined. Dies when the name belongs to a
|
|
42
|
+
* method of ANOTHER type here — writing would convert it (the ⛔ above).
|
|
43
|
+
*/
|
|
44
|
+
export declare const findTotp: (name: string) => Effect.Effect<TotpEntry | undefined, BaoError, HttpClient.HttpClient>;
|
|
45
|
+
/** Create-or-update by `method_name` — never by id (the ⚠️ above). */
|
|
46
|
+
export declare const writeTotp: (body: Readonly<Record<string, unknown>>) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
|
|
47
|
+
/**
|
|
48
|
+
* ⛔ DESTROYS EVERY ENTITY'S ENROLLED SECRET FOR THIS METHOD (login_mfa.go:1965-1973 clears the TOTP
|
|
49
|
+
* keys under the method id). OpenBao refuses it while an enforcement in memory still names the id
|
|
50
|
+
* (:1914-1924) — a refusal this passes on.
|
|
51
|
+
*/
|
|
52
|
+
export declare const deleteTotp: (id: string) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
|
|
53
|
+
export declare const readEnforcement: (name: string) => Effect.Effect<Record<string, unknown> | undefined, BaoError, HttpClient.HttpClient>;
|
|
54
|
+
export declare const writeEnforcement: (name: string, body: Readonly<Record<string, unknown>>) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
|
|
55
|
+
/**
|
|
56
|
+
* Accessors for auth mount paths, from `GET sys/auth` (keys are `<path>/`). A path with no mount
|
|
57
|
+
* comes back in `missing`, never as a guess.
|
|
58
|
+
*/
|
|
59
|
+
export declare const authAccessors: (paths: readonly string[]) => Effect.Effect<{
|
|
60
|
+
readonly accessors: readonly string[];
|
|
61
|
+
readonly missing: readonly string[];
|
|
62
|
+
}, BaoError, HttpClient.HttpClient>;
|
|
63
|
+
//# sourceMappingURL=mfa-wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mfa-wire.d.ts","sourceRoot":"","sources":["../../src/openbao/mfa-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAGnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,eAAO,MAAM,SAAS,6BAA6B,CAAC;AACpD,gGAAgG;AAChG,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAClD,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,MAAkD,CAAC;AAOlG,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAGjD,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,QAAQ,SACb,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAmBnE,CAAC;AAEL,sEAAsE;AACtE,eAAO,MAAM,SAAS,SAAU,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,yDACP,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,UAAU,OAAQ,MAAM,yDAAoC,CAAC;AAE1E,eAAO,MAAM,eAAe,SAAU,MAAM,wFAAmC,CAAC;AAEhF,eAAO,MAAM,gBAAgB,SAAU,MAAM,QAAQ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,yDAChB,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,aAAa,UACjB,SAAS,MAAM,EAAE,KACvB,MAAM,CAAC,MAAM,CACd;IAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAC9E,QAAQ,EACR,UAAU,CAAC,UAAU,CAanB,CAAC"}
|
|
@@ -17,6 +17,13 @@ export interface BaoMountProps {
|
|
|
17
17
|
maxLeaseTtl?: string;
|
|
18
18
|
/** KV only — sent as `options.version` on enable, as `-version=2` was. Immutable after create. */
|
|
19
19
|
version?: 1 | 2;
|
|
20
|
+
/**
|
|
21
|
+
* The path this mount lives at NOW, when `path` is where it should move to. Moves it with
|
|
22
|
+
* `sys/remount`, keeping every secret. Without it, a changed `path` fails the plan — see
|
|
23
|
+
* mount-move.ts. ⛔ Every lease under the old path is revoked, and policies are not rewritten.
|
|
24
|
+
* Harmless to leave declared after the move: once state holds `path`, it is ignored.
|
|
25
|
+
*/
|
|
26
|
+
remountFrom?: string;
|
|
20
27
|
}
|
|
21
28
|
export interface BaoMountAttributes {
|
|
22
29
|
path: string;
|