@murumets-ee/auth 0.1.4 → 0.1.5
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/dist/admin/{index.d.ts → index.d.mts} +31 -29
- package/dist/admin/index.d.mts.map +1 -0
- package/dist/admin/index.mjs +2 -0
- package/dist/admin/index.mjs.map +1 -0
- package/dist/client.d.mts +2066 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +2 -0
- package/dist/client.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +30 -40
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/permissions-DH0BNEtU.mjs +2 -0
- package/dist/permissions-DH0BNEtU.mjs.map +1 -0
- package/dist/plugin-Dvyj8Crv.d.mts +988 -0
- package/dist/plugin-Dvyj8Crv.d.mts.map +1 -0
- package/dist/plugin.d.mts +2 -0
- package/dist/plugin.mjs +2 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/server-DgG2m6uD.mjs +2 -0
- package/dist/server-DgG2m6uD.mjs.map +1 -0
- package/package.json +16 -16
- package/dist/admin/index.js +0 -1
- package/dist/chunk-CJL274PJ.js +0 -1
- package/dist/chunk-NH6AU5Z6.js +0 -1
- package/dist/client.d.ts +0 -2064
- package/dist/client.js +0 -1
- package/dist/index.js +0 -1
- package/dist/plugin-C-MA3A5U.d.ts +0 -993
- package/dist/plugin.d.ts +0 -6
- package/dist/plugin.js +0 -1
- package/dist/server-NWTKDUCB.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":"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"}
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{adminClient as e,organizationClient as t}from"better-auth/client/plugins";import{createAuthClient as n}from"better-auth/react";function r(r){return n({baseURL:r?.baseURL,plugins:[e(),...r?.organizations?[t()]:[]]})}function i(e){function t(e){if(e.error)throw Error(e.error.message??`Unknown error`);return e.data}return{async list(n){let r=t(await e.admin.listUsers({query:{limit:n.limit,offset:n.offset,...n.sortBy?{sortBy:n.sortBy,sortDirection:n.sortDirection}:{},...n.searchValue?{searchValue:n.searchValue,searchField:n.searchField??`email`,searchOperator:n.searchOperator??`contains`}:{}}}));return{users:(r.users??[]).map(a),total:r.total??0}},async create(n){t(await e.admin.createUser({name:n.name,email:n.email,password:n.password,role:n.role}))},async update(n,r){t(await e.admin.updateUser({userId:n,data:r}))},async remove(n){t(await e.admin.removeUser({userId:n}))},async setRole(n,r){t(await e.admin.setRole({userId:n,role:r}))},async ban(n,r){t(await e.admin.banUser({userId:n,...r?.reason?{banReason:r.reason}:{},...r?.expiresIn?{banExpiresIn:r.expiresIn}:{}}))},async unban(n){t(await e.admin.unbanUser({userId:n}))},async revokeSessions(n){t(await e.admin.revokeUserSessions({userId:n}))}}}function a(e){let t=e;return{id:t.id,name:t.name??``,email:t.email,emailVerified:t.emailVerified??!1,image:t.image??null,createdAt:String(t.createdAt??``),updatedAt:String(t.updatedAt??``),role:t.role??null,banned:t.banned??null,banReason:t.banReason??null,banExpires:t.banExpires?String(t.banExpires):null}}export{r as createClient,i as createUsersApi};
|
|
2
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["/**\n * Browser-safe auth client factory.\n *\n * This module has NO server-only imports and is safe to use in React components.\n * Import from '@murumets-ee/auth/client'.\n */\n\nimport { adminClient, organizationClient } from 'better-auth/client/plugins'\nimport { createAuthClient } from 'better-auth/react'\n\nexport interface AuthClientOptions {\n /** Base URL of the auth API. Defaults to window.location.origin. */\n baseURL?: string\n /** Enable organization client plugin (must match server config) */\n organizations?: boolean\n}\n\n/**\n * Create a typed auth client for use in React components.\n *\n * @example\n * ```typescript\n * // lib/auth-client.ts\n * import { createClient } from '@murumets-ee/auth/client'\n *\n * export const authClient = createClient()\n *\n * // In a component:\n * const { data: session } = authClient.useSession()\n * ```\n */\nexport function createClient(options?: AuthClientOptions) {\n return createAuthClient({\n baseURL: options?.baseURL,\n plugins: [adminClient(), ...(options?.organizations ? [organizationClient()] : [])],\n })\n}\n\n// ---------------------------------------------------------------------------\n// UsersApi adapter — bridges better-auth admin client → @murumets-ee/admin-ui/users\n// ---------------------------------------------------------------------------\n\ntype AuthClient = ReturnType<typeof createClient>\n\ninterface UsersListQuery {\n limit: number\n offset: number\n sortBy?: string\n sortDirection?: 'asc' | 'desc'\n searchValue?: string\n searchField?: 'email' | 'name'\n searchOperator?: 'contains' | 'starts_with' | 'ends_with'\n}\n\ninterface NormalizedUser {\n id: string\n name: string\n email: string\n emailVerified: boolean\n image: string | null\n createdAt: string\n updatedAt: string\n role: string | null\n banned: boolean | null\n banReason: string | null\n banExpires: string | null\n}\n\n/**\n * Create a users API adapter from a better-auth client that includes the\n * `adminClient()` plugin. The returned object is structurally compatible\n * with the `UsersApi` interface from `@murumets-ee/admin-ui/users`.\n *\n * @example\n * ```tsx\n * import { createClient, createUsersApi } from '@murumets-ee/auth/client'\n * import { UsersManagement } from '@murumets-ee/admin-ui/users'\n *\n * const authClient = createClient()\n * const usersApi = createUsersApi(authClient)\n *\n * <UsersManagement api={usersApi} currentUserId={session.user.id} />\n * ```\n */\nexport function createUsersApi(client: AuthClient) {\n /**\n * Unwrap better-auth's discriminated union response.\n * On error branch, `error` exists with optional `message`.\n * On success branch, `data` exists.\n */\n function unwrap<T>(res: { data?: T; error?: { message?: string } }): T {\n if (res.error) throw new Error(res.error.message ?? 'Unknown error')\n return res.data as T\n }\n\n return {\n async list(query: UsersListQuery): Promise<{ users: NormalizedUser[]; total: number }> {\n const res = await client.admin.listUsers({\n query: {\n limit: query.limit,\n offset: query.offset,\n ...(query.sortBy ? { sortBy: query.sortBy, sortDirection: query.sortDirection } : {}),\n ...(query.searchValue\n ? {\n searchValue: query.searchValue,\n searchField: query.searchField ?? 'email',\n searchOperator: query.searchOperator ?? 'contains',\n }\n : {}),\n },\n })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth's union return type doesn't match a simple generic pattern\n const data = unwrap<{ users: unknown[]; total: number }>(res as any)\n return {\n users: (data.users ?? []).map(mapUser),\n total: data.total ?? 0,\n }\n },\n\n async create(data: { name: string; email: string; password: string; role: string }) {\n const res = await client.admin.createUser({\n name: data.name,\n email: data.email,\n password: data.password,\n // better-auth admin plugin types role as a narrow union; cast is safe\n // because server validates the role against configured access control\n role: data.role as 'admin',\n })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async update(userId: string, data: { name?: string }) {\n const res = await client.admin.updateUser({ userId, data })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async remove(userId: string) {\n const res = await client.admin.removeUser({ userId })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async setRole(userId: string, role: string) {\n const res = await client.admin.setRole({\n userId,\n role: role as 'admin',\n })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async ban(userId: string, options?: { reason?: string; expiresIn?: number }) {\n const res = await client.admin.banUser({\n userId,\n ...(options?.reason ? { banReason: options.reason } : {}),\n ...(options?.expiresIn ? { banExpiresIn: options.expiresIn } : {}),\n })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async unban(userId: string) {\n const res = await client.admin.unbanUser({ userId })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n\n async revokeSessions(userId: string) {\n const res = await client.admin.revokeUserSessions({ userId })\n // biome-ignore lint/suspicious/noExplicitAny: better-auth union return type\n unwrap(res as any)\n },\n }\n}\n\n/** Normalize a better-auth user record to a consistent shape. */\nfunction mapUser(u: unknown): NormalizedUser {\n const r = u as Record<string, unknown>\n return {\n id: r.id as string,\n name: (r.name as string) ?? '',\n email: r.email as string,\n emailVerified: (r.emailVerified as boolean) ?? false,\n image: (r.image as string) ?? null,\n createdAt: String(r.createdAt ?? ''),\n updatedAt: String(r.updatedAt ?? ''),\n role: (r.role as string) ?? null,\n banned: (r.banned as boolean) ?? null,\n banReason: (r.banReason as string) ?? null,\n banExpires: r.banExpires ? String(r.banExpires) : null,\n }\n}\n"],"mappings":"sIA+BA,SAAgB,EAAa,EAA6B,CACxD,OAAO,EAAiB,CACtB,QAAS,GAAS,QAClB,QAAS,CAAC,GAAa,CAAE,GAAI,GAAS,cAAgB,CAAC,GAAoB,CAAC,CAAG,EAAE,CAAE,CACpF,CAAC,CAiDJ,SAAgB,EAAe,EAAoB,CAMjD,SAAS,EAAU,EAAoD,CACrE,GAAI,EAAI,MAAO,MAAU,MAAM,EAAI,MAAM,SAAW,gBAAgB,CACpE,OAAO,EAAI,KAGb,MAAO,CACL,MAAM,KAAK,EAA4E,CAgBrF,IAAM,EAAO,EAfD,MAAM,EAAO,MAAM,UAAU,CACvC,MAAO,CACL,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,GAAI,EAAM,OAAS,CAAE,OAAQ,EAAM,OAAQ,cAAe,EAAM,cAAe,CAAG,EAAE,CACpF,GAAI,EAAM,YACN,CACE,YAAa,EAAM,YACnB,YAAa,EAAM,aAAe,QAClC,eAAgB,EAAM,gBAAkB,WACzC,CACD,EAAE,CACP,CACF,CAAC,CAEkE,CACpE,MAAO,CACL,OAAQ,EAAK,OAAS,EAAE,EAAE,IAAI,EAAQ,CACtC,MAAO,EAAK,OAAS,EACtB,EAGH,MAAM,OAAO,EAAuE,CAUlF,EATY,MAAM,EAAO,MAAM,WAAW,CACxC,KAAM,EAAK,KACX,MAAO,EAAK,MACZ,SAAU,EAAK,SAGf,KAAM,EAAK,KACZ,CAAC,CAEgB,EAGpB,MAAM,OAAO,EAAgB,EAAyB,CAGpD,EAFY,MAAM,EAAO,MAAM,WAAW,CAAE,SAAQ,OAAM,CAAC,CAEzC,EAGpB,MAAM,OAAO,EAAgB,CAG3B,EAFY,MAAM,EAAO,MAAM,WAAW,CAAE,SAAQ,CAAC,CAEnC,EAGpB,MAAM,QAAQ,EAAgB,EAAc,CAM1C,EALY,MAAM,EAAO,MAAM,QAAQ,CACrC,SACM,OACP,CAAC,CAEgB,EAGpB,MAAM,IAAI,EAAgB,EAAmD,CAO3E,EANY,MAAM,EAAO,MAAM,QAAQ,CACrC,SACA,GAAI,GAAS,OAAS,CAAE,UAAW,EAAQ,OAAQ,CAAG,EAAE,CACxD,GAAI,GAAS,UAAY,CAAE,aAAc,EAAQ,UAAW,CAAG,EAAE,CAClE,CAAC,CAEgB,EAGpB,MAAM,MAAM,EAAgB,CAG1B,EAFY,MAAM,EAAO,MAAM,UAAU,CAAE,SAAQ,CAAC,CAElC,EAGpB,MAAM,eAAe,EAAgB,CAGnC,EAFY,MAAM,EAAO,MAAM,mBAAmB,CAAE,SAAQ,CAAC,CAE3C,EAErB,CAIH,SAAS,EAAQ,EAA4B,CAC3C,IAAM,EAAI,EACV,MAAO,CACL,GAAI,EAAE,GACN,KAAO,EAAE,MAAmB,GAC5B,MAAO,EAAE,MACT,cAAgB,EAAE,eAA6B,GAC/C,MAAQ,EAAE,OAAoB,KAC9B,UAAW,OAAO,EAAE,WAAa,GAAG,CACpC,UAAW,OAAO,EAAE,WAAa,GAAG,CACpC,KAAO,EAAE,MAAmB,KAC5B,OAAS,EAAE,QAAsB,KACjC,UAAY,EAAE,WAAwB,KACtC,WAAY,EAAE,WAAa,OAAO,EAAE,WAAW,CAAG,KACnD"}
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import { Entity } from
|
|
6
|
-
import { createAccessControl } from 'better-auth/plugins/access';
|
|
7
|
-
export { createAccessControl } from 'better-auth/plugins/access';
|
|
8
|
-
import 'better-auth';
|
|
9
|
-
import '@murumets-ee/logging';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Bridge between better-auth sessions and the toolkit's RequestContext.
|
|
13
|
-
*
|
|
14
|
-
* This is the critical integration point: it resolves a better-auth session
|
|
15
|
-
* from request headers and returns a toolkit RequestContext that the entity
|
|
16
|
-
* system's AdminClient, QueryClient, and auditable behavior can read.
|
|
17
|
-
*/
|
|
1
|
+
import { i as AuthConfig, n as getAuth, r as Auth, t as auth } from "./plugin-Dvyj8Crv.mjs";
|
|
2
|
+
import { createAccessControl } from "better-auth/plugins/access";
|
|
3
|
+
import * as _$better_auth_plugins0 from "better-auth/plugins";
|
|
4
|
+
import { PermissionChecker, RequestContext } from "@murumets-ee/core";
|
|
5
|
+
import { Entity } from "@murumets-ee/entity";
|
|
18
6
|
|
|
7
|
+
//#region src/context.d.ts
|
|
19
8
|
/**
|
|
20
9
|
* Resolve a better-auth session into a toolkit RequestContext.
|
|
21
10
|
*
|
|
@@ -36,10 +25,10 @@ import '@murumets-ee/logging';
|
|
|
36
25
|
* ```
|
|
37
26
|
*/
|
|
38
27
|
declare function resolveAuthContext(auth: Auth, headers: Headers): Promise<RequestContext>;
|
|
39
|
-
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/permissions.d.ts
|
|
40
30
|
declare const ACTIONS: readonly ["view", "create", "update", "delete"];
|
|
41
31
|
declare const ACTIONS_WITH_PUBLISH: readonly ["view", "create", "update", "delete", "publish"];
|
|
42
|
-
|
|
43
32
|
/** Built-in role names — cannot be deleted via the roles editor. */
|
|
44
33
|
declare const BUILT_IN_ROLES: readonly ["admin", "public", "authenticated"];
|
|
45
34
|
/** Maps HTTP methods to permission action names. */
|
|
@@ -72,13 +61,13 @@ declare function buildPermissionChecker(roleDefinitions: Record<string, Record<s
|
|
|
72
61
|
* Routes with `resource` and `actions` are added if not already present.
|
|
73
62
|
*/
|
|
74
63
|
declare function buildResourceCatalog(entities: {
|
|
64
|
+
name: string;
|
|
65
|
+
behaviors?: {
|
|
75
66
|
name: string;
|
|
76
|
-
|
|
77
|
-
name: string;
|
|
78
|
-
}[];
|
|
67
|
+
}[];
|
|
79
68
|
}[], routes?: {
|
|
80
|
-
|
|
81
|
-
|
|
69
|
+
resource?: string;
|
|
70
|
+
actions?: readonly string[];
|
|
82
71
|
}[]): Record<string, string[]>;
|
|
83
72
|
/**
|
|
84
73
|
* Build a permission statement object from all registered entities,
|
|
@@ -95,20 +84,21 @@ declare function buildStatements(entities: Entity[]): Record<string, readonly st
|
|
|
95
84
|
* - **authenticated**: view only (better-auth's `defaultRole`)
|
|
96
85
|
*/
|
|
97
86
|
declare function buildDefaultRoles(ac: ReturnType<typeof createAccessControl>, entities: Entity[]): {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
87
|
+
admin: {
|
|
88
|
+
authorize<K_1 extends string>(request: K_1 extends infer T extends K ? { [key in T]?: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>[key] | {
|
|
89
|
+
actions: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>[key];
|
|
90
|
+
connector: "OR" | "AND";
|
|
91
|
+
} | undefined } : never, connector?: "OR" | "AND"): _$better_auth_plugins0.AuthorizeResponse;
|
|
92
|
+
statements: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>;
|
|
93
|
+
};
|
|
94
|
+
authenticated: {
|
|
95
|
+
authorize<K_1 extends string>(request: K_1 extends infer T extends K ? { [key in T]?: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>[key] | {
|
|
96
|
+
actions: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>[key];
|
|
97
|
+
connector: "OR" | "AND";
|
|
98
|
+
} | undefined } : never, connector?: "OR" | "AND"): _$better_auth_plugins0.AuthorizeResponse;
|
|
99
|
+
statements: _$better_auth_plugins0.Subset<string, _$better_auth_plugins0.Statements>;
|
|
100
|
+
};
|
|
112
101
|
};
|
|
113
|
-
|
|
114
|
-
export { ACTIONS, ACTIONS_WITH_PUBLISH, Auth, BUILT_IN_ROLES, METHOD_TO_ACTION, buildDefaultRoles, buildInitialRoleDefinitions, buildPermissionChecker, buildResourceCatalog, buildStatements, resolveAuthContext };
|
|
102
|
+
//#endregion
|
|
103
|
+
export { ACTIONS, ACTIONS_WITH_PUBLISH, type Auth, type AuthConfig, BUILT_IN_ROLES, METHOD_TO_ACTION, auth, buildDefaultRoles, buildInitialRoleDefinitions, buildPermissionChecker, buildResourceCatalog, buildStatements, createAccessControl, getAuth, resolveAuthContext };
|
|
104
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/context.ts","../src/permissions.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;iBA8BsB,kBAAA,CAAmB,IAAA,EAAM,IAAA,EAAM,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,cAAA;;;cCf1E,OAAA;AAAA,cACA,oBAAA;;cASO,cAAA;;cAGA,gBAAA,EAAkB,MAAA;;;;;;;iBAiBf,2BAAA,CAAA,GAA+B,MAAA,SAAe,MAAA;;AAhCE;;;;;AAED;;iBA6C/C,sBAAA,CACd,eAAA,EAAiB,MAAA,SAAe,MAAA,sBAC/B,iBAAA;;;AArCH;;;;;AAGA;;;;iBAwEgB,oBAAA,CACd,QAAA;EAAY,IAAA;EAAc,SAAA;IAAc,IAAA;EAAA;AAAA,KACxC,MAAA;EAAW,QAAA;EAAmB,OAAA;AAAA,MAC7B,MAAA;;;;;;;;iBA+Ca,eAAA,CAAgB,QAAA,EAAU,MAAA,KAAQ,MAAA;;;;AAlDlD;;;iBAkEgB,iBAAA,CAAkB,EAAA,EAAI,UAAA,QAAkB,mBAAA,GAAsB,QAAA,EAAU,MAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c,u as l}from"./permissions-DH0BNEtU.mjs";import{auth as u,getAuth as d}from"./plugin.mjs";import"server-only";async function f(e,t){let n=await e.api.getSession({headers:t});return n?{user:{id:n.user.id,groups:[n.user.role??`viewer`],name:n.user.name??void 0,email:n.user.email??void 0},requestId:crypto.randomUUID()}:{requestId:crypto.randomUUID()}}export{c as ACTIONS,i as ACTIONS_WITH_PUBLISH,o as BUILT_IN_ROLES,n as METHOD_TO_ACTION,u as auth,e as buildDefaultRoles,a as buildInitialRoleDefinitions,s as buildPermissionChecker,t as buildResourceCatalog,r as buildStatements,l as createAccessControl,d as getAuth,f as resolveAuthContext};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["/**\n * Bridge between better-auth sessions and the toolkit's RequestContext.\n *\n * This is the critical integration point: it resolves a better-auth session\n * from request headers and returns a toolkit RequestContext that the entity\n * system's AdminClient, QueryClient, and auditable behavior can read.\n */\n\nimport type { RequestContext } from '@murumets-ee/core'\nimport type { Auth } from './server.js'\n\n/**\n * Resolve a better-auth session into a toolkit RequestContext.\n *\n * Call this from your Next.js middleware or server component to populate\n * the toolkit's AsyncLocalStorage context.\n *\n * @example\n * ```typescript\n * // middleware.ts (user writes this — documented copy-paste)\n * import { getAuth, resolveAuthContext } from '@murumets-ee/auth'\n * import { runWithContext } from '@murumets-ee/core'\n *\n * export async function middleware(request: NextRequest) {\n * const auth = getAuth()\n * const ctx = await resolveAuthContext(auth, request.headers)\n * return runWithContext(ctx, () => NextResponse.next())\n * }\n * ```\n */\nexport async function resolveAuthContext(auth: Auth, headers: Headers): Promise<RequestContext> {\n const session = await auth.api.getSession({ headers })\n\n if (!session) {\n return { requestId: crypto.randomUUID() }\n }\n\n return {\n user: {\n id: session.user.id,\n // The admin plugin stores role on the user object\n groups: [((session.user as Record<string, unknown>).role as string) ?? 'viewer'],\n name: session.user.name ?? undefined,\n email: session.user.email ?? undefined,\n },\n requestId: crypto.randomUUID(),\n }\n}\n"],"mappings":"8KA8BA,eAAsB,EAAmB,EAAY,EAA2C,CAC9F,IAAM,EAAU,MAAM,EAAK,IAAI,WAAW,CAAE,UAAS,CAAC,CAMtD,OAJK,EAIE,CACL,KAAM,CACJ,GAAI,EAAQ,KAAK,GAEjB,OAAQ,CAAG,EAAQ,KAAiC,MAAmB,SAAS,CAChF,KAAM,EAAQ,KAAK,MAAQ,IAAA,GAC3B,MAAO,EAAQ,KAAK,OAAS,IAAA,GAC9B,CACD,UAAW,OAAO,YAAY,CAC/B,CAZQ,CAAE,UAAW,OAAO,YAAY,CAAE"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createAccessControl as e}from"better-auth/plugins/access";const t=[`view`,`create`,`update`,`delete`],n=[`view`,`create`,`update`,`delete`,`publish`],r=[`admin`,`public`,`authenticated`],i={GET:`view`,POST:`create`,PATCH:`update`,DELETE:`delete`};function a(){return{public:{},authenticated:{}}}function o(e){let t=new Map;for(let[n,r]of Object.entries(e)){let e=new Map;for(let[t,n]of Object.entries(r))e.set(t,new Set(n));t.set(n,e)}return(e,n,r)=>e===`admin`?!0:t.get(e)?.get(n)?.has(r)??!1}function s(e){return e.behaviors?.some(e=>e.name===`publishable`)??!1}function c(e,t){let n={};for(let t of e)n[t.name]=s(t)?[`view`,`create`,`update`,`delete`,`publish`]:[`view`,`create`,`update`,`delete`];if(t)for(let e of t)e.resource&&e.actions&&!(e.resource in n)&&(n[e.resource]=[...e.actions]);return n}const l={user:[`create`,`list`,`set-role`,`ban`,`impersonate`,`delete`,`set-password`,`get`,`update`],session:[`list`,`revoke`,`delete`]};function u(e){let r={...l};for(let i of e)r[i.name]=s(i)?n:t;return r}function d(e,t){let n={},r={};n.user=[...l.user],n.session=[...l.session],r.user=[],r.session=[];for(let e of t)n[e.name]=s(e)?[`view`,`create`,`update`,`delete`,`publish`]:[`view`,`create`,`update`,`delete`],r[e.name]=[`view`];return{admin:e.newRole(n),authenticated:e.newRole(r)}}export{d as a,c,i,u as l,n,a as o,r,o as s,t,e as u};
|
|
2
|
+
//# sourceMappingURL=permissions-DH0BNEtU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions-DH0BNEtU.mjs","names":[],"sources":["../src/permissions.ts"],"sourcesContent":["/**\n * Permission system — bridges entity access definitions to better-auth's access control,\n * and provides the configurable permission checker for the admin API.\n *\n * Two layers:\n * 1. **better-auth integration**: `buildStatements` / `buildDefaultRoles` — feeds into\n * better-auth's `createAccessControl` for its internal permission system.\n * 2. **Admin API enforcement**: `buildPermissionChecker` / `buildInitialRoleDefinitions` —\n * the toolkit's own firewall-model permission system, persisted in settings.\n */\n\nimport type { PermissionChecker } from '@murumets-ee/core'\nimport type { Entity } from '@murumets-ee/entity'\nimport { createAccessControl } from 'better-auth/plugins/access'\n\nconst ACTIONS = ['view', 'create', 'update', 'delete'] as const\nconst ACTIONS_WITH_PUBLISH = ['view', 'create', 'update', 'delete', 'publish'] as const\n\nexport { ACTIONS, ACTIONS_WITH_PUBLISH }\n\n// ---------------------------------------------------------------------------\n// Built-in roles & constants\n// ---------------------------------------------------------------------------\n\n/** Built-in role names — cannot be deleted via the roles editor. */\nexport const BUILT_IN_ROLES = ['admin', 'public', 'authenticated'] as const\n\n/** Maps HTTP methods to permission action names. */\nexport const METHOD_TO_ACTION: Record<string, string> = {\n GET: 'view',\n POST: 'create',\n PATCH: 'update',\n DELETE: 'delete',\n}\n\n// ---------------------------------------------------------------------------\n// Admin API permission system (firewall model)\n// ---------------------------------------------------------------------------\n\n/**\n * Build initial role definitions for first run (no settings saved yet).\n *\n * All built-in non-admin roles start with ZERO permissions.\n * Admin is never stored — it's hardcoded in the checker.\n */\nexport function buildInitialRoleDefinitions(): Record<string, Record<string, string[]>> {\n return {\n public: {},\n authenticated: {},\n }\n}\n\n/**\n * Build a synchronous permission checker from role definitions.\n *\n * Rules:\n * - `admin` role: ALWAYS returns `true` (hardcoded safety net, ignores settings)\n * - All other roles: exact match from `roleDefinitions` (deny-by-default)\n * - Unknown role / unknown resource / unknown action → `false`\n */\nexport function buildPermissionChecker(\n roleDefinitions: Record<string, Record<string, string[]>>,\n): PermissionChecker {\n // Pre-build lookup maps for O(1) checks\n const perms = new Map<string, Map<string, Set<string>>>()\n\n for (const [role, resources] of Object.entries(roleDefinitions)) {\n const resourceMap = new Map<string, Set<string>>()\n for (const [resource, actions] of Object.entries(resources)) {\n resourceMap.set(resource, new Set(actions))\n }\n perms.set(role, resourceMap)\n }\n\n return (role: string, resource: string, action: string): boolean => {\n if (role === 'admin') return true // Safety net — admin always passes\n return perms.get(role)?.get(resource)?.has(action) ?? false\n }\n}\n\n// ---------------------------------------------------------------------------\n// Resource catalog builder\n// ---------------------------------------------------------------------------\n\n/** Check if an entity has the publishable behavior. */\nfunction isPublishable(entity: { behaviors?: { name: string }[] }): boolean {\n return entity.behaviors?.some((b) => b.name === 'publishable') ?? false\n}\n\n/**\n * Build a complete resource catalog from entities and admin routes.\n *\n * Used by:\n * - `permissionRoutes()` config (`getStatements` callback)\n * - Server-side permission page data loaders\n *\n * Entities automatically get CRUD actions. Publishable entities also get\n * the `publish` action, which gates who can set status to 'published'.\n * Routes with `resource` and `actions` are added if not already present.\n */\nexport function buildResourceCatalog(\n entities: { name: string; behaviors?: { name: string }[] }[],\n routes?: { resource?: string; actions?: readonly string[] }[],\n): Record<string, string[]> {\n const catalog: Record<string, string[]> = {}\n\n for (const entity of entities) {\n catalog[entity.name] = isPublishable(entity)\n ? ['view', 'create', 'update', 'delete', 'publish']\n : ['view', 'create', 'update', 'delete']\n }\n\n if (routes) {\n for (const route of routes) {\n if (route.resource && route.actions && !(route.resource in catalog)) {\n catalog[route.resource] = [...route.actions]\n }\n }\n }\n\n return catalog\n}\n\n// ---------------------------------------------------------------------------\n// better-auth integration (unchanged, used by createAuthServer)\n// ---------------------------------------------------------------------------\n\n/** Admin plugin's built-in resources — must be included for listUsers, ban, etc. */\nconst ADMIN_STATEMENTS = {\n user: [\n 'create',\n 'list',\n 'set-role',\n 'ban',\n 'impersonate',\n 'delete',\n 'set-password',\n 'get',\n 'update',\n ] as const,\n session: ['list', 'revoke', 'delete'] as const,\n}\n\n/**\n * Build a permission statement object from all registered entities,\n * plus the admin plugin's built-in user/session resources.\n *\n * Publishable entities get the additional `publish` action.\n * Result shape: `{ user: [...], session: [...], article: ['view', ...], category: [...] }`\n */\nexport function buildStatements(entities: Entity[]) {\n const statement: Record<string, readonly string[]> = {\n ...ADMIN_STATEMENTS,\n }\n for (const entity of entities) {\n statement[entity.name] = isPublishable(entity) ? ACTIONS_WITH_PUBLISH : ACTIONS\n }\n return statement\n}\n\n/**\n * Build the default toolkit roles for better-auth's access control.\n *\n * - **admin**: full CRUD on all entities + user/session management\n * - **authenticated**: view only (better-auth's `defaultRole`)\n */\nexport function buildDefaultRoles(ac: ReturnType<typeof createAccessControl>, entities: Entity[]) {\n const adminPerms: Record<string, string[]> = {}\n const authenticatedPerms: Record<string, string[]> = {}\n\n // Admin gets full control of user/session management\n adminPerms.user = [...ADMIN_STATEMENTS.user]\n adminPerms.session = [...ADMIN_STATEMENTS.session]\n // Authenticated gets no user/session management\n authenticatedPerms.user = []\n authenticatedPerms.session = []\n\n for (const entity of entities) {\n adminPerms[entity.name] = isPublishable(entity)\n ? ['view', 'create', 'update', 'delete', 'publish']\n : ['view', 'create', 'update', 'delete']\n authenticatedPerms[entity.name] = ['view']\n }\n\n return {\n admin: ac.newRole(adminPerms),\n authenticated: ac.newRole(authenticatedPerms),\n }\n}\n\nexport { createAccessControl }\n"],"mappings":"iEAeA,MAAM,EAAU,CAAC,OAAQ,SAAU,SAAU,SAAS,CAChD,EAAuB,CAAC,OAAQ,SAAU,SAAU,SAAU,UAAU,CASjE,EAAiB,CAAC,QAAS,SAAU,gBAAgB,CAGrD,EAA2C,CACtD,IAAK,OACL,KAAM,SACN,MAAO,SACP,OAAQ,SACT,CAYD,SAAgB,GAAwE,CACtF,MAAO,CACL,OAAQ,EAAE,CACV,cAAe,EAAE,CAClB,CAWH,SAAgB,EACd,EACmB,CAEnB,IAAM,EAAQ,IAAI,IAElB,IAAK,GAAM,CAAC,EAAM,KAAc,OAAO,QAAQ,EAAgB,CAAE,CAC/D,IAAM,EAAc,IAAI,IACxB,IAAK,GAAM,CAAC,EAAU,KAAY,OAAO,QAAQ,EAAU,CACzD,EAAY,IAAI,EAAU,IAAI,IAAI,EAAQ,CAAC,CAE7C,EAAM,IAAI,EAAM,EAAY,CAG9B,OAAQ,EAAc,EAAkB,IAClC,IAAS,QAAgB,GACtB,EAAM,IAAI,EAAK,EAAE,IAAI,EAAS,EAAE,IAAI,EAAO,EAAI,GAS1D,SAAS,EAAc,EAAqD,CAC1E,OAAO,EAAO,WAAW,KAAM,GAAM,EAAE,OAAS,cAAc,EAAI,GAcpE,SAAgB,EACd,EACA,EAC0B,CAC1B,IAAM,EAAoC,EAAE,CAE5C,IAAK,IAAM,KAAU,EACnB,EAAQ,EAAO,MAAQ,EAAc,EAAO,CACxC,CAAC,OAAQ,SAAU,SAAU,SAAU,UAAU,CACjD,CAAC,OAAQ,SAAU,SAAU,SAAS,CAG5C,GAAI,MACG,IAAM,KAAS,EACd,EAAM,UAAY,EAAM,SAAW,EAAE,EAAM,YAAY,KACzD,EAAQ,EAAM,UAAY,CAAC,GAAG,EAAM,QAAQ,EAKlD,OAAO,EAQT,MAAM,EAAmB,CACvB,KAAM,CACJ,SACA,OACA,WACA,MACA,cACA,SACA,eACA,MACA,SACD,CACD,QAAS,CAAC,OAAQ,SAAU,SAAS,CACtC,CASD,SAAgB,EAAgB,EAAoB,CAClD,IAAM,EAA+C,CACnD,GAAG,EACJ,CACD,IAAK,IAAM,KAAU,EACnB,EAAU,EAAO,MAAQ,EAAc,EAAO,CAAG,EAAuB,EAE1E,OAAO,EAST,SAAgB,EAAkB,EAA4C,EAAoB,CAChG,IAAM,EAAuC,EAAE,CACzC,EAA+C,EAAE,CAGvD,EAAW,KAAO,CAAC,GAAG,EAAiB,KAAK,CAC5C,EAAW,QAAU,CAAC,GAAG,EAAiB,QAAQ,CAElD,EAAmB,KAAO,EAAE,CAC5B,EAAmB,QAAU,EAAE,CAE/B,IAAK,IAAM,KAAU,EACnB,EAAW,EAAO,MAAQ,EAAc,EAAO,CAC3C,CAAC,OAAQ,SAAU,SAAU,SAAU,UAAU,CACjD,CAAC,OAAQ,SAAU,SAAU,SAAS,CAC1C,EAAmB,EAAO,MAAQ,CAAC,OAAO,CAG5C,MAAO,CACL,MAAO,EAAG,QAAQ,EAAW,CAC7B,cAAe,EAAG,QAAQ,EAAmB,CAC9C"}
|