cauthy-auth 0.28.1 → 0.30.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.
Files changed (45) hide show
  1. package/README.md +51 -0
  2. package/dist/{cauthy-CTVg4kvo.d.mts → cauthy-BXgsZ-Um.d.mts} +19 -1
  3. package/dist/{cauthy-BAwAJChs.d.ts → cauthy-CHENs452.d.ts} +19 -1
  4. package/dist/cli.js +155 -54
  5. package/dist/client.d.mts +47 -7
  6. package/dist/client.d.ts +47 -7
  7. package/dist/client.js +23 -1
  8. package/dist/client.js.map +1 -1
  9. package/dist/client.mjs +23 -1
  10. package/dist/client.mjs.map +1 -1
  11. package/dist/express.d.mts +2 -2
  12. package/dist/express.d.ts +2 -2
  13. package/dist/fastify.d.mts +2 -2
  14. package/dist/fastify.d.ts +2 -2
  15. package/dist/hono.d.mts +2 -2
  16. package/dist/hono.d.ts +2 -2
  17. package/dist/index.d.mts +2 -2
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.js +325 -129
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +323 -129
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/next.d.mts +2 -2
  24. package/dist/next.d.ts +2 -2
  25. package/dist/react.d.mts +214 -18
  26. package/dist/react.d.ts +214 -18
  27. package/dist/react.js +129 -32
  28. package/dist/react.js.map +1 -1
  29. package/dist/react.mjs +129 -32
  30. package/dist/react.mjs.map +1 -1
  31. package/dist/svelte.d.mts +2 -2
  32. package/dist/svelte.d.ts +2 -2
  33. package/dist/svelte.js +37 -2
  34. package/dist/svelte.js.map +1 -1
  35. package/dist/svelte.mjs +37 -2
  36. package/dist/svelte.mjs.map +1 -1
  37. package/dist/{system-CVBW_AV8.d.mts → system-0mHrdAgO.d.mts} +307 -109
  38. package/dist/{system-Bn8uckvQ.d.ts → system-BIYbS7h2.d.ts} +307 -109
  39. package/dist/vue.d.mts +2 -2
  40. package/dist/vue.d.ts +2 -2
  41. package/dist/vue.js +37 -2
  42. package/dist/vue.js.map +1 -1
  43. package/dist/vue.mjs +37 -2
  44. package/dist/vue.mjs.map +1 -1
  45. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { C as Cauthy } from './cauthy-CTVg4kvo.mjs';
3
- import { S as Session } from './system-CVBW_AV8.mjs';
2
+ import { C as Cauthy } from './cauthy-BXgsZ-Um.mjs';
3
+ import { S as Session } from './system-0mHrdAgO.mjs';
4
4
  import 'jose';
5
5
  import './theme.mjs';
6
6
 
package/dist/express.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { C as Cauthy } from './cauthy-BAwAJChs.js';
3
- import { S as Session } from './system-Bn8uckvQ.js';
2
+ import { C as Cauthy } from './cauthy-CHENs452.js';
3
+ import { S as Session } from './system-BIYbS7h2.js';
4
4
  import 'jose';
5
5
  import './theme.js';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { FastifyReply, FastifyPluginAsync } from 'fastify';
2
- import { C as Cauthy } from './cauthy-CTVg4kvo.mjs';
3
- import { S as Session } from './system-CVBW_AV8.mjs';
2
+ import { C as Cauthy } from './cauthy-BXgsZ-Um.mjs';
3
+ import { S as Session } from './system-0mHrdAgO.mjs';
4
4
  import 'jose';
5
5
  import './theme.mjs';
6
6
 
package/dist/fastify.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FastifyReply, FastifyPluginAsync } from 'fastify';
2
- import { C as Cauthy } from './cauthy-BAwAJChs.js';
3
- import { S as Session } from './system-Bn8uckvQ.js';
2
+ import { C as Cauthy } from './cauthy-CHENs452.js';
3
+ import { S as Session } from './system-BIYbS7h2.js';
4
4
  import 'jose';
5
5
  import './theme.js';
6
6
 
package/dist/hono.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MiddlewareHandler } from 'hono';
2
- import { C as Cauthy } from './cauthy-CTVg4kvo.mjs';
3
- import { S as Session } from './system-CVBW_AV8.mjs';
2
+ import { C as Cauthy } from './cauthy-BXgsZ-Um.mjs';
3
+ import { S as Session } from './system-0mHrdAgO.mjs';
4
4
  import 'jose';
5
5
  import './theme.mjs';
6
6
 
package/dist/hono.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { MiddlewareHandler } from 'hono';
2
- import { C as Cauthy } from './cauthy-BAwAJChs.js';
3
- import { S as Session } from './system-Bn8uckvQ.js';
2
+ import { C as Cauthy } from './cauthy-CHENs452.js';
3
+ import { S as Session } from './system-BIYbS7h2.js';
4
4
  import 'jose';
5
5
  import './theme.js';
6
6
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-CTVg4kvo.mjs';
2
- export { C as CauthyApiError, a as CauthyConfigError, b as CauthyOptions, c as CauthySystem, J as JsonValue, N as NewMember, O as Organization, d as OrganizationMember, P as PermissionOf, e as Permitted, f as Policy, g as PolicyPermission, h as PolicyRole, i as PublicSession, j as PushPolicyOptions, R as RoleOf, S as Session, k as SessionOrganization, l as SessionStatus, m as SessionUser, n as SystemKey, o as SystemOptions, p as can, q as createSystem, r as definePolicy, s as policySource, t as pullPolicy, u as pushPolicy } from './system-CVBW_AV8.mjs';
1
+ export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-BXgsZ-Um.mjs';
2
+ export { A as AttributeType, C as CatalogResource, a as CauthyApiError, b as CauthyConfigError, c as CauthyOptions, d as CauthySystem, e as CheckOf, f as CheckQuestion, g as CheckResult, h as CheckRule, i as ConditionalPermission, J as JsonValue, N as NewMember, O as Organization, j as OrganizationMember, k as OrganizationRef, P as PermissionOf, l as Permitted, m as Policy, n as PolicyDefinition, o as PolicyPermission, p as PolicyRole, q as PublicSession, r as PushPolicyOptions, R as RoleOf, S as Session, s as SessionOrganization, t as SessionStatus, u as SessionUser, v as SystemKey, w as SystemOptions, x as SystemPolicy, y as can, z as createSystem, B as definePolicy, D as policySource, E as pullCatalog, F as pullPolicy, G as pushCatalog, H as pushPolicy } from './system-0mHrdAgO.mjs';
3
3
  export { CauthyTheme, ImportedTheme, PushThemeOptions, StackmythThemeOptions, StackmythTokens, ThemeFont, ThemeTexts, pushTheme, themeFromStackmyth, tokensFromStackmyth } from './theme.mjs';
4
4
  export { A as AuthField, D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, b as PasswordPolicy, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.mjs';
5
5
  import { JWTVerifyGetKey } from 'jose';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-BAwAJChs.js';
2
- export { C as CauthyApiError, a as CauthyConfigError, b as CauthyOptions, c as CauthySystem, J as JsonValue, N as NewMember, O as Organization, d as OrganizationMember, P as PermissionOf, e as Permitted, f as Policy, g as PolicyPermission, h as PolicyRole, i as PublicSession, j as PushPolicyOptions, R as RoleOf, S as Session, k as SessionOrganization, l as SessionStatus, m as SessionUser, n as SystemKey, o as SystemOptions, p as can, q as createSystem, r as definePolicy, s as policySource, t as pullPolicy, u as pushPolicy } from './system-Bn8uckvQ.js';
1
+ export { C as Cauthy, a as CauthyEvent, b as CauthyWebhookError, R as Resolved, V as VerifyWebhookOptions, c as createCauthy, v as verifyWebhook } from './cauthy-CHENs452.js';
2
+ export { A as AttributeType, C as CatalogResource, a as CauthyApiError, b as CauthyConfigError, c as CauthyOptions, d as CauthySystem, e as CheckOf, f as CheckQuestion, g as CheckResult, h as CheckRule, i as ConditionalPermission, J as JsonValue, N as NewMember, O as Organization, j as OrganizationMember, k as OrganizationRef, P as PermissionOf, l as Permitted, m as Policy, n as PolicyDefinition, o as PolicyPermission, p as PolicyRole, q as PublicSession, r as PushPolicyOptions, R as RoleOf, S as Session, s as SessionOrganization, t as SessionStatus, u as SessionUser, v as SystemKey, w as SystemOptions, x as SystemPolicy, y as can, z as createSystem, B as definePolicy, D as policySource, E as pullCatalog, F as pullPolicy, G as pushCatalog, H as pushPolicy } from './system-BIYbS7h2.js';
3
3
  export { CauthyTheme, ImportedTheme, PushThemeOptions, StackmythThemeOptions, StackmythTokens, ThemeFont, ThemeTexts, pushTheme, themeFromStackmyth, tokensFromStackmyth } from './theme.js';
4
4
  export { A as AuthField, D as DEFAULT_PASSWORD_POLICY, F as FieldProblem, M as MIN_PASSWORD, P as PasswordClass, a as PasswordLocale, b as PasswordPolicy, c as PasswordReason, d as checkCode, e as checkEmail, f as checkName, g as checkNewPassword, h as checkPassword, i as describePasswordPolicy, j as explainPasswordProblems, p as passwordPolicyOf, k as passwordProblems } from './validate-sEXG82po.js';
5
5
  import { JWTVerifyGetKey } from 'jose';
package/dist/index.js CHANGED
@@ -41,7 +41,9 @@ __export(src_exports, {
41
41
  passwordPolicyOf: () => passwordPolicyOf,
42
42
  passwordProblems: () => passwordProblems,
43
43
  policySource: () => policySource,
44
+ pullCatalog: () => pullCatalog,
44
45
  pullPolicy: () => pullPolicy,
46
+ pushCatalog: () => pushCatalog,
45
47
  pushPolicy: () => pushPolicy,
46
48
  pushTheme: () => pushTheme,
47
49
  themeFromStackmyth: () => themeFromStackmyth,
@@ -107,9 +109,6 @@ async function unseal(sealed, key) {
107
109
  }
108
110
  }
109
111
 
110
- // src/system.ts
111
- var import_jose2 = require("jose");
112
-
113
112
  // src/url.ts
114
113
  function withoutTrailingSlashes(s) {
115
114
  let end = s.length;
@@ -117,6 +116,218 @@ function withoutTrailingSlashes(s) {
117
116
  return s.slice(0, end);
118
117
  }
119
118
 
119
+ // src/permissions.ts
120
+ function matches(entry, resource, action) {
121
+ const i = entry.lastIndexOf(":");
122
+ if (i < 0) return false;
123
+ const r = entry.slice(0, i);
124
+ const a = entry.slice(i + 1);
125
+ return (r === "*" || r === resource) && (a === "*" || a === action);
126
+ }
127
+ function can(organization, resource, action) {
128
+ if (!organization?.permissions) return false;
129
+ if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
130
+ return organization.permissions.some((p) => matches(p, resource, action));
131
+ }
132
+ function definePolicy(given) {
133
+ const roles = Array.isArray(given) ? given : given.roles;
134
+ const resources = Array.isArray(given) ? [] : given.catalog ?? [];
135
+ const entry = (p) => `${p.resource}:${p.action}`;
136
+ const byName = new Map(roles.map((r) => [r.role, r]));
137
+ const split = (permission) => {
138
+ const i = permission.lastIndexOf(":");
139
+ return i < 0 ? [permission, ""] : [permission.slice(0, i), permission.slice(i + 1)];
140
+ };
141
+ return {
142
+ roles: roles.map((r) => ({
143
+ role: r.role,
144
+ ...r.name !== void 0 ? { name: r.name } : {},
145
+ ...r.description !== void 0 ? { description: r.description } : {},
146
+ ...r.inherits ? { inherits: [...r.inherits] } : {},
147
+ ...r.permissions ? { permissions: r.permissions.map((p) => ({ ...p })) } : {}
148
+ })),
149
+ catalog: resources.map((c) => ({
150
+ resource: c.resource,
151
+ ...c.description !== void 0 ? { description: c.description } : {},
152
+ actions: [...c.actions],
153
+ ...c.attributes ? { attributes: { ...c.attributes } } : {}
154
+ })),
155
+ roleNames: roles.map((r) => r.role),
156
+ permissions: [...new Set(roles.flatMap((r) => (r.permissions ?? []).map(entry)))],
157
+ can(organization, permission) {
158
+ const [resource, action] = split(permission);
159
+ return can(organization, resource, action);
160
+ },
161
+ hasRole(organization, ...wanted) {
162
+ return organization != null && wanted.includes(organization.role);
163
+ },
164
+ permissionsOf(role) {
165
+ const seen = /* @__PURE__ */ new Set();
166
+ const out2 = /* @__PURE__ */ new Set();
167
+ const walk = (name) => {
168
+ if (seen.has(name)) return;
169
+ seen.add(name);
170
+ const r = byName.get(name);
171
+ for (const p of r?.permissions ?? []) out2.add(entry(p));
172
+ for (const parent of r?.inherits ?? []) walk(parent);
173
+ };
174
+ walk(role);
175
+ return [...out2];
176
+ },
177
+ // The types did the work; this only hands the question on, as a method of
178
+ // the asker so it keeps its `this`.
179
+ check(asker, ...args) {
180
+ return asker.check(...args);
181
+ }
182
+ };
183
+ }
184
+ var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
185
+ function policySource(system, read) {
186
+ const roles = Array.isArray(read) ? read : read.roles;
187
+ const catalog = Array.isArray(read) ? [] : read.catalog ?? [];
188
+ const text = (v) => JSON.stringify(v);
189
+ const key = (k) => IDENT.test(k) ? k : text(k);
190
+ const one = (r, pad) => {
191
+ const parts = [`role: ${text(r.role)}`];
192
+ if (r.name) parts.push(`name: ${text(r.name)}`);
193
+ if (r.description) parts.push(`description: ${text(r.description)}`);
194
+ if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text).join(", ")}]`);
195
+ if (r.permissions?.length) {
196
+ const list = r.permissions.map((p) => `${pad} { ${[
197
+ ...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text(p.kind)}`] : [],
198
+ `resource: ${text(p.resource)}`,
199
+ `action: ${text(p.action)}`,
200
+ ...p.condition ? [`condition: ${text(p.condition)}`] : []
201
+ ].join(", ")} },`);
202
+ parts.push(`permissions: [
203
+ ${list.join("\n")}
204
+ ${pad} ]`);
205
+ }
206
+ return `${pad} {
207
+ ${pad} ${parts.join(`,
208
+ ${pad} `)},
209
+ ${pad} },`;
210
+ };
211
+ const resource = (c) => {
212
+ const parts = [`resource: ${text(c.resource)}`];
213
+ if (c.description) parts.push(`description: ${text(c.description)}`);
214
+ parts.push(`actions: [${c.actions.map(text).join(", ")}]`);
215
+ const attrs = Object.entries(c.attributes ?? {});
216
+ if (attrs.length) parts.push(`attributes: { ${attrs.map(([k, v]) => `${key(k)}: ${text(v)}`).join(", ")} }`);
217
+ return ` { ${parts.join(", ")} },`;
218
+ };
219
+ const head = `// The catalog of roles of ${text(system)}, as Cauthy has it.
220
+ // Written by \`cauthy-auth policy --pull\`: run it again after the catalog
221
+ // changes, and do not edit this file by hand.
222
+ `;
223
+ if (catalog.length === 0) {
224
+ return `${head}
225
+ import { definePolicy, type PermissionOf, type RoleOf } from 'cauthy-auth'
226
+
227
+ export const policy = definePolicy([
228
+ ${roles.map((r) => one(r, "")).join("\n")}
229
+ ])
230
+
231
+ export const { can, hasRole } = policy
232
+ export type Role = RoleOf<typeof policy>
233
+ export type Permission = PermissionOf<typeof policy>
234
+ `;
235
+ }
236
+ return `${head}
237
+ import { definePolicy, type CheckOf, type PermissionOf, type RoleOf } from 'cauthy-auth'
238
+
239
+ export const policy = definePolicy({
240
+ catalog: [
241
+ ${catalog.map(resource).join("\n")}
242
+ ],
243
+ roles: [
244
+ ${roles.map((r) => one(r, " ")).join("\n")}
245
+ ],
246
+ })
247
+
248
+ export const { can, hasRole } = policy
249
+ export type Role = RoleOf<typeof policy>
250
+ export type Permission = PermissionOf<typeof policy>
251
+ /** What a check may ask: \`policy.check(auth, request, { organization, ...question })\`. */
252
+ export type Check = CheckOf<typeof policy>
253
+ `;
254
+ }
255
+ function reach(o, doing) {
256
+ const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
257
+ const r = {
258
+ issuer: withoutTrailingSlashes(o.issuer ?? env4("CAUTHY_ISSUER") ?? ""),
259
+ system: o.system ?? env4("CAUTHY_SYSTEM") ?? "",
260
+ tenant: o.tenant ?? env4("CAUTHY_TENANT") ?? "",
261
+ token: o.token ?? env4("CAUTHY_TOKEN") ?? ""
262
+ };
263
+ for (const [name, v] of Object.entries({ CAUTHY_ISSUER: r.issuer, CAUTHY_SYSTEM: r.system, CAUTHY_TENANT: r.tenant, CAUTHY_TOKEN: r.token })) {
264
+ if (v === "") throw new Error(`cauthy-auth: ${name} is required to ${doing}`);
265
+ }
266
+ return { ...r, fetch: o.fetch ?? fetch };
267
+ }
268
+ async function systemCall(r, method, what, body) {
269
+ const res = await r.fetch(`${r.issuer}/v1/systems/${encodeURIComponent(r.system)}/${what}`, {
270
+ ...method === "PUT" ? { method } : {},
271
+ headers: {
272
+ authorization: `Bearer ${r.token}`,
273
+ "x-tenant-key": r.tenant,
274
+ ...body === void 0 ? { accept: "application/json" } : { "content-type": "application/json" }
275
+ },
276
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
277
+ });
278
+ const answer = await res.json().catch(() => null);
279
+ if (!res.ok) {
280
+ const verb = method === "GET" ? `refused to give the ${what}` : `refused the ${what}`;
281
+ throw new Error(`cauthy-auth: Cauthy ${verb} (${res.status}): ${answer?.errors?.[0]?.message ?? res.statusText}`);
282
+ }
283
+ return answer?.data;
284
+ }
285
+ async function pullPolicy(o = {}) {
286
+ const data = await systemCall(reach(o, "read a policy"), "GET", "policy");
287
+ return data?.roles ?? [];
288
+ }
289
+ async function pullCatalog(o = {}) {
290
+ const data = await systemCall(reach(o, "read a catalog"), "GET", "catalog");
291
+ return data?.resources ?? [];
292
+ }
293
+ async function pushPolicy(o) {
294
+ const r = reach(o, "push a policy");
295
+ if (o.catalog !== void 0) await systemCall(r, "PUT", "catalog", { resources: o.catalog });
296
+ await systemCall(r, "PUT", "policy", { roles: o.roles });
297
+ }
298
+ async function pushCatalog(o) {
299
+ const data = await systemCall(reach(o, "push a catalog"), "PUT", "catalog", { resources: o.resources });
300
+ return data?.resources ?? [];
301
+ }
302
+ function checkWire(q) {
303
+ return {
304
+ resource: q.resource,
305
+ action: q.action,
306
+ ...q.resourceId !== void 0 ? { resource_id: q.resourceId } : {},
307
+ ...q.ownerId !== void 0 ? { owner_id: q.ownerId } : {},
308
+ ...q.attributes !== void 0 ? { attributes: q.attributes } : {}
309
+ };
310
+ }
311
+ function checkResultOf(d) {
312
+ const r = d?.rule;
313
+ const rule = r && typeof r === "object" ? {
314
+ source: r.source === "exception" ? "exception" : "role",
315
+ ...r.role ? { role: String(r.role) } : {},
316
+ kind: String(r.kind ?? "*"),
317
+ resource: String(r.resource ?? ""),
318
+ action: String(r.action ?? ""),
319
+ effect: r.effect === "allow" ? "allow" : "deny",
320
+ ...r.condition ? { condition: String(r.condition) } : {}
321
+ } : void 0;
322
+ return { allowed: d?.allowed === true, reason: String(d?.reason ?? ""), ...rule ? { rule } : {} };
323
+ }
324
+ function organizationIdOf(o) {
325
+ return typeof o === "string" ? o : typeof o?.id === "string" ? o.id : "";
326
+ }
327
+
328
+ // src/system.ts
329
+ var import_jose2 = require("jose");
330
+
120
331
  // src/theme.ts
121
332
  var MAX_RADIUS = 40;
122
333
  function withoutComments(css) {
@@ -534,6 +745,7 @@ function createSystem(given = {}) {
534
745
  const clientSecret = given.clientSecret ?? env("CAUTHY_CLIENT_SECRET") ?? "";
535
746
  const envKey = env("CAUTHY_SYSTEM_KEY");
536
747
  const key = given.key ?? (envKey ? { privateKey: envKey, keyId: env("CAUTHY_SYSTEM_KEY_ID") ?? "" } : void 0);
748
+ const ownSystem = given.system ?? env("CAUTHY_SYSTEM") ?? "";
537
749
  const doFetch = (...args) => (given.fetch ?? fetch)(...args);
538
750
  const tokenEndpoint = `${issuer}/v1/oauth/token`;
539
751
  function configured() {
@@ -651,6 +863,19 @@ function createSystem(given = {}) {
651
863
  return (await data("GET", `/v1/systems/${id(system)}/policy`)).roles ?? [];
652
864
  }
653
865
  },
866
+ catalog: {
867
+ async get(system) {
868
+ return (await data("GET", `/v1/systems/${id(system)}/catalog`)).resources ?? [];
869
+ },
870
+ async set(system, resources) {
871
+ return (await data("PUT", `/v1/systems/${id(system)}/catalog`, { resources })).resources ?? [];
872
+ }
873
+ },
874
+ async check(q) {
875
+ const system = q.system ?? ownSystem;
876
+ if (system === "") throw new CauthyConfigError("name the system to check in (system, or CAUTHY_SYSTEM)");
877
+ return checkResultOf(await data("POST", `/v1/systems/${id(system)}/check`, { organization_id: q.organizationId, user_id: q.userId, ...checkWire(q) }));
878
+ },
654
879
  async publicJwks() {
655
880
  if (!key) return { keys: [] };
656
881
  const { kty, n, e } = asJwk(key) ?? await (0, import_jose2.exportJWK)(await signingKey());
@@ -798,6 +1023,12 @@ function checkOrganization(value, required = false) {
798
1023
  if (v.length > MAX_ORGANIZATION) return { field: "organization", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
799
1024
  return null;
800
1025
  }
1026
+ function checkOrganizationName(value) {
1027
+ const v = typeof value === "string" ? value.trim() : "";
1028
+ if (v === "") return { field: "name", code: "required", message: "Enter the organization's name." };
1029
+ if (v.length > MAX_ORGANIZATION) return { field: "name", code: "too_long", message: `Use at most ${MAX_ORGANIZATION} characters.` };
1030
+ return null;
1031
+ }
801
1032
  function checkCode(value) {
802
1033
  const v = typeof value === "string" ? value.trim() : "";
803
1034
  if (v === "") return { field: "code", code: "required", message: "Enter the code." };
@@ -929,6 +1160,9 @@ function redirect(location, cookies = []) {
929
1160
  for (const c of cookies) h.append("set-cookie", c);
930
1161
  return new Response(null, { status: 302, headers: h });
931
1162
  }
1163
+ function conditionalOf(c) {
1164
+ return { resource: String(c?.resource ?? ""), action: String(c?.action ?? ""), effect: c?.effect === "deny" ? "deny" : "allow" };
1165
+ }
932
1166
  function createCauthy(given = {}) {
933
1167
  const issuer = withoutTrailingSlashes(given.issuer ?? env2("CAUTHY_ISSUER") ?? "");
934
1168
  const clientId = given.clientId ?? env2("CAUTHY_CLIENT_ID") ?? "";
@@ -957,7 +1191,7 @@ function createCauthy(given = {}) {
957
1191
  resetPassword: given.pages?.resetPassword ? localPath(given.pages.resetPassword, "/") : void 0,
958
1192
  verifyEmail: given.pages?.verifyEmail ? localPath(given.pages.verifyEmail, "/") : void 0
959
1193
  };
960
- const ownSystem = createSystem({ issuer, clientId, clientSecret, key: given.systemKey, fetch: given.fetch });
1194
+ const ownSystem = createSystem({ issuer, clientId, clientSecret, key: given.systemKey, system: system || void 0, fetch: given.fetch });
961
1195
  const originOf = (request) => {
962
1196
  if (baseURL) return withoutTrailingSlashes(baseURL);
963
1197
  const url = new URL(request.url);
@@ -1092,13 +1326,35 @@ function createCauthy(given = {}) {
1092
1326
  role: String(o.role ?? ""),
1093
1327
  metadata: o.metadata ?? {},
1094
1328
  ...o.external_id ? { externalId: String(o.external_id) } : {},
1095
- ...Array.isArray(o.permissions) ? { permissions: o.permissions.map(String), denied: Array.isArray(o.denied) ? o.denied.map(String) : [] } : {}
1329
+ ...Array.isArray(o.permissions) ? { permissions: o.permissions.map(String), denied: Array.isArray(o.denied) ? o.denied.map(String) : [], conditional: Array.isArray(o.conditional) ? o.conditional.map(conditionalOf) : [] } : {}
1096
1330
  })
1097
1331
  );
1098
1332
  if (orgCache.size > 5e3) orgCache.clear();
1099
1333
  orgCache.set(session.accessToken, { at: Date.now(), value });
1100
1334
  return value;
1101
1335
  }
1336
+ async function decide(token2, question) {
1337
+ const org = organizationIdOf(question.organization);
1338
+ if (org === "") return { allowed: false, reason: "no organization named" };
1339
+ const res = await doFetch(`${issuer}/v1/oauth/organizations/${encodeURIComponent(org)}/check`, {
1340
+ method: "POST",
1341
+ headers: { authorization: `Bearer ${token2}`, "content-type": "application/json", accept: "application/json" },
1342
+ body: JSON.stringify(checkWire(question))
1343
+ }).catch(() => null);
1344
+ if (res === null) throw new CauthyApiError(503, "unavailable", "Cauthy could not be reached to check a permission");
1345
+ const body2 = await res.json().catch(() => null);
1346
+ if (res.ok) return checkResultOf(body2?.data);
1347
+ const first = body2?.errors?.[0];
1348
+ if (res.status === 401 || res.status === 403 || res.status === 404) {
1349
+ return { allowed: false, reason: first?.message ?? (res.status === 401 ? "not signed in" : res.status === 404 ? "not a member of the organization" : "refused") };
1350
+ }
1351
+ throw new CauthyApiError(res.status, first?.code ?? "error", `Cauthy refused to check a permission (${res.status}): ${first?.message ?? res.statusText}`);
1352
+ }
1353
+ async function check(who, question) {
1354
+ const session = "accessToken" in who ? who : await getSession(who);
1355
+ if (!session) return { allowed: false, reason: "not signed in" };
1356
+ return decide(session.accessToken, question);
1357
+ }
1102
1358
  const forgetting = (fn) => async (...args) => {
1103
1359
  const out2 = await fn(...args);
1104
1360
  orgCache.clear();
@@ -1231,7 +1487,15 @@ function createCauthy(given = {}) {
1231
1487
  name: String(d?.name ?? ""),
1232
1488
  kind: String(d?.kind ?? ""),
1233
1489
  role: String(d?.role ?? ""),
1234
- can: { readMembers: d?.can?.read_members === true, manageMembers: d?.can?.manage_members === true }
1490
+ // A Cauthy from before a power says nothing of it: then it is not given.
1491
+ can: {
1492
+ readMembers: d?.can?.read_members === true,
1493
+ manageMembers: d?.can?.manage_members === true,
1494
+ removeMembers: d?.can?.remove_members === true,
1495
+ update: d?.can?.update === true,
1496
+ archive: d?.can?.archive === true,
1497
+ delete: d?.can?.delete === true
1498
+ }
1235
1499
  });
1236
1500
  const orgMemberOf = (d) => ({
1237
1501
  userId: String(d?.user_id ?? ""),
@@ -1424,7 +1688,10 @@ function createCauthy(given = {}) {
1424
1688
  "/me/organizations/members/add",
1425
1689
  "/me/organizations/members/role",
1426
1690
  "/me/organizations/members/remove",
1427
- "/me/organizations/members/invite"
1691
+ "/me/organizations/members/invite",
1692
+ "/me/organizations/rename",
1693
+ "/me/organizations/archive",
1694
+ "/me/organizations/delete"
1428
1695
  ].includes(route) || route.startsWith("/sign-in/social/") || route.startsWith("/callback/") || route.startsWith("/me/identities/link/") || route.startsWith("/me/upgrade/social/") || route === "/sign-in/sso" || route === "/sign-in/sso/discover" || route === "/sso/callback";
1429
1696
  if (!own) return null;
1430
1697
  if (tenant === "") {
@@ -1895,6 +2162,23 @@ function createCauthy(given = {}) {
1895
2162
  if (res.error) return refused(res, res.error.code === "weak_password" ? "password" : "email");
1896
2163
  return signedIn(request, res.data);
1897
2164
  }
2165
+ if (route === "/me/organizations/rename" || route === "/me/organizations/archive" || route === "/me/organizations/delete") {
2166
+ const org = typeof b.organizationId === "string" ? b.organizationId : "";
2167
+ if (!UUID.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
2168
+ const path = `/v1/auth/me/organizations/${org}`;
2169
+ if (route === "/me/organizations/rename") {
2170
+ const bad = checkOrganizationName(b.name);
2171
+ if (bad) return invalid(bad);
2172
+ const res2 = await as(path, { name: String(b.name).trim() }, "PATCH");
2173
+ if (res2.error) return refused(res2, res2.error.code === "validation_error" ? "name" : void 0);
2174
+ orgCache.delete(mine.token);
2175
+ return json({ status: "renamed", organization: seatOf(res2.data) }, 200, mine.headers);
2176
+ }
2177
+ const res = route === "/me/organizations/archive" ? await as(`${path}/archive`) : await as(path, void 0, "DELETE");
2178
+ if (res.error) return refused(res);
2179
+ orgCache.delete(mine.token);
2180
+ return json({ status: route === "/me/organizations/archive" ? "archived" : "deleted", organizationId: org }, 200, mine.headers);
2181
+ }
1898
2182
  if (route.startsWith("/me/organizations/members/")) {
1899
2183
  const org = typeof b.organizationId === "string" ? b.organizationId : "";
1900
2184
  if (!UUID.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, mine.headers);
@@ -2040,6 +2324,7 @@ function createCauthy(given = {}) {
2040
2324
  const get = request.method === "GET" || request.method === "HEAD";
2041
2325
  const mine = await ownRoute(request, url, route);
2042
2326
  if (mine !== null) return mine;
2327
+ if (route === "/me/organizations/check") return checkRoute(request);
2043
2328
  if (route === "/login" && get) return login(request, url);
2044
2329
  if (route === "/callback" && get) return callback(request, url);
2045
2330
  if (route === "/logout" && (get || request.method === "POST")) return logout(request);
@@ -2066,6 +2351,36 @@ function createCauthy(given = {}) {
2066
2351
  }
2067
2352
  return new Response("Not found", { status: 404 });
2068
2353
  }
2354
+ async function checkRoute(request) {
2355
+ if (request.method !== "POST") return new Response("Method not allowed", { status: 405, headers: { allow: "POST" } });
2356
+ if (!ownRequest(request)) return problem(403, "cross_site", "This request did not come from this application.");
2357
+ const b = await body(request);
2358
+ if (b === null) return problem(400, "bad_request", "Send the question as JSON.");
2359
+ const asker = await signedInAs(request);
2360
+ if ("response" in asker) return asker.response;
2361
+ const org = typeof b.organizationId === "string" ? b.organizationId : "";
2362
+ if (!UUID.test(org)) return problem(400, "invalid_organization", "Say which organization.", void 0, asker.headers);
2363
+ const named = (v) => typeof v === "string" && v !== "" && v.length <= 200;
2364
+ if (!named(b.resource) || !named(b.action)) return problem(400, "bad_request", "Say which resource and action.", void 0, asker.headers);
2365
+ const attributes2 = b.attributes;
2366
+ if (attributes2 !== void 0 && (typeof attributes2 !== "object" || attributes2 === null || Array.isArray(attributes2))) {
2367
+ return problem(400, "bad_request", "The attributes are an object.", void 0, asker.headers);
2368
+ }
2369
+ try {
2370
+ const r = await decide(asker.token, {
2371
+ organization: org,
2372
+ resource: b.resource,
2373
+ action: b.action,
2374
+ ...attributes2 !== void 0 ? { attributes: attributes2 } : {},
2375
+ ...typeof b.resourceId === "string" ? { resourceId: b.resourceId } : {},
2376
+ ...typeof b.ownerId === "string" ? { ownerId: b.ownerId } : {}
2377
+ });
2378
+ return json({ status: "decided", ...r }, 200, asker.headers);
2379
+ } catch (e) {
2380
+ const err = e instanceof CauthyApiError ? e : new CauthyApiError(503, "unavailable", "Cauthy could not be reached.");
2381
+ return problem(err.status, err.code, err.message.replace(/^cauthy-auth: /, ""), void 0, asker.headers);
2382
+ }
2383
+ }
2069
2384
  async function protect(request) {
2070
2385
  const { value } = await read(request);
2071
2386
  if (value !== null && fresh(value)) return { session: toSession(value) };
@@ -2089,6 +2404,7 @@ function createCauthy(given = {}) {
2089
2404
  resolve: resolve2,
2090
2405
  protect,
2091
2406
  organizations,
2407
+ check,
2092
2408
  toPublic,
2093
2409
  urls,
2094
2410
  options: { issuer, clientId, basePath, afterSignIn, afterSignOut, cookieName }
@@ -2103,128 +2419,6 @@ function legalOf(raw) {
2103
2419
  };
2104
2420
  }
2105
2421
 
2106
- // src/permissions.ts
2107
- function matches(entry, resource, action) {
2108
- const i = entry.lastIndexOf(":");
2109
- if (i < 0) return false;
2110
- const r = entry.slice(0, i);
2111
- const a = entry.slice(i + 1);
2112
- return (r === "*" || r === resource) && (a === "*" || a === action);
2113
- }
2114
- function can(organization, resource, action) {
2115
- if (!organization?.permissions) return false;
2116
- if ((organization.denied ?? []).some((d) => matches(d, resource, action))) return false;
2117
- return organization.permissions.some((p) => matches(p, resource, action));
2118
- }
2119
- function definePolicy(roles) {
2120
- const entry = (p) => `${p.resource}:${p.action}`;
2121
- const byName = new Map(roles.map((r) => [r.role, r]));
2122
- const split = (permission) => {
2123
- const i = permission.lastIndexOf(":");
2124
- return i < 0 ? [permission, ""] : [permission.slice(0, i), permission.slice(i + 1)];
2125
- };
2126
- return {
2127
- roles: roles.map((r) => ({
2128
- role: r.role,
2129
- ...r.name !== void 0 ? { name: r.name } : {},
2130
- ...r.description !== void 0 ? { description: r.description } : {},
2131
- ...r.inherits ? { inherits: [...r.inherits] } : {},
2132
- ...r.permissions ? { permissions: r.permissions.map((p) => ({ ...p })) } : {}
2133
- })),
2134
- roleNames: roles.map((r) => r.role),
2135
- permissions: [...new Set(roles.flatMap((r) => (r.permissions ?? []).map(entry)))],
2136
- can(organization, permission) {
2137
- const [resource, action] = split(permission);
2138
- return can(organization, resource, action);
2139
- },
2140
- hasRole(organization, ...wanted) {
2141
- return organization != null && wanted.includes(organization.role);
2142
- },
2143
- permissionsOf(role) {
2144
- const seen = /* @__PURE__ */ new Set();
2145
- const out2 = /* @__PURE__ */ new Set();
2146
- const walk = (name) => {
2147
- if (seen.has(name)) return;
2148
- seen.add(name);
2149
- const r = byName.get(name);
2150
- for (const p of r?.permissions ?? []) out2.add(entry(p));
2151
- for (const parent of r?.inherits ?? []) walk(parent);
2152
- };
2153
- walk(role);
2154
- return [...out2];
2155
- }
2156
- };
2157
- }
2158
- var IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2159
- function policySource(system, roles) {
2160
- const text = (v) => JSON.stringify(v);
2161
- const key = (k) => IDENT.test(k) ? k : text(k);
2162
- const one = (r) => {
2163
- const parts = [`role: ${text(r.role)}`];
2164
- if (r.name) parts.push(`name: ${text(r.name)}`);
2165
- if (r.description) parts.push(`description: ${text(r.description)}`);
2166
- if (r.inherits?.length) parts.push(`inherits: [${r.inherits.map(text).join(", ")}]`);
2167
- if (r.permissions?.length) {
2168
- const list = r.permissions.map((p) => ` { ${[...p.kind && p.kind !== "*" ? [`${key("kind")}: ${text(p.kind)}`] : [], `resource: ${text(p.resource)}`, `action: ${text(p.action)}`].join(", ")} },`);
2169
- parts.push(`permissions: [
2170
- ${list.join("\n")}
2171
- ]`);
2172
- }
2173
- return ` {
2174
- ${parts.join(",\n ")},
2175
- },`;
2176
- };
2177
- return `// The catalog of roles of ${text(system)}, as Cauthy has it.
2178
- // Written by \`cauthy-auth policy --pull\`: run it again after the catalog
2179
- // changes, and do not edit this file by hand.
2180
-
2181
- import { definePolicy, type PermissionOf, type RoleOf } from 'cauthy-auth'
2182
-
2183
- export const policy = definePolicy([
2184
- ${roles.map(one).join("\n")}
2185
- ])
2186
-
2187
- export const { can, hasRole } = policy
2188
- export type Role = RoleOf<typeof policy>
2189
- export type Permission = PermissionOf<typeof policy>
2190
- `;
2191
- }
2192
- async function pullPolicy(o = {}) {
2193
- const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
2194
- const issuer = withoutTrailingSlashes(o.issuer ?? env4("CAUTHY_ISSUER") ?? "");
2195
- const system = o.system ?? env4("CAUTHY_SYSTEM") ?? "";
2196
- const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
2197
- const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
2198
- for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
2199
- if (v === "") throw new Error(`cauthy-auth: ${name} is required to read a policy`);
2200
- }
2201
- const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
2202
- headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, accept: "application/json" }
2203
- });
2204
- const body = await res.json().catch(() => null);
2205
- if (!res.ok) throw new Error(`cauthy-auth: Cauthy refused to give the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
2206
- return body?.data?.roles ?? [];
2207
- }
2208
- async function pushPolicy(o) {
2209
- const env4 = (n) => typeof process !== "undefined" ? process.env?.[n] : void 0;
2210
- const issuer = withoutTrailingSlashes(o.issuer ?? env4("CAUTHY_ISSUER") ?? "");
2211
- const system = o.system ?? env4("CAUTHY_SYSTEM") ?? "";
2212
- const tenant = o.tenant ?? env4("CAUTHY_TENANT") ?? "";
2213
- const token = o.token ?? env4("CAUTHY_TOKEN") ?? "";
2214
- for (const [name, v] of Object.entries({ CAUTHY_ISSUER: issuer, CAUTHY_SYSTEM: system, CAUTHY_TENANT: tenant, CAUTHY_TOKEN: token })) {
2215
- if (v === "") throw new Error(`cauthy-auth: ${name} is required to push a policy`);
2216
- }
2217
- const res = await (o.fetch ?? fetch)(`${issuer}/v1/systems/${encodeURIComponent(system)}/policy`, {
2218
- method: "PUT",
2219
- headers: { authorization: `Bearer ${token}`, "x-tenant-key": tenant, "content-type": "application/json" },
2220
- body: JSON.stringify({ roles: o.roles })
2221
- });
2222
- if (!res.ok) {
2223
- const body = await res.json().catch(() => null);
2224
- throw new Error(`cauthy-auth: Cauthy refused the policy (${res.status}): ${body?.errors?.[0]?.message ?? res.statusText}`);
2225
- }
2226
- }
2227
-
2228
2422
  // src/connect.ts
2229
2423
  function sessionSecret() {
2230
2424
  const bytes = crypto.getRandomValues(new Uint8Array(32));
@@ -2350,7 +2544,9 @@ async function verifyToken(from, options = {}) {
2350
2544
  passwordPolicyOf,
2351
2545
  passwordProblems,
2352
2546
  policySource,
2547
+ pullCatalog,
2353
2548
  pullPolicy,
2549
+ pushCatalog,
2354
2550
  pushPolicy,
2355
2551
  pushTheme,
2356
2552
  themeFromStackmyth,