@murumets-ee/auth 0.1.1 → 0.1.2

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/package.json CHANGED
@@ -1,28 +1,24 @@
1
1
  {
2
2
  "name": "@murumets-ee/auth",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "Elastic-2.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./dist/index.d.ts",
9
- "import": "./dist/index.js",
10
- "require": "./dist/index.cjs"
9
+ "import": "./dist/index.js"
11
10
  },
12
11
  "./client": {
13
12
  "types": "./dist/client.d.ts",
14
- "import": "./dist/client.js",
15
- "require": "./dist/client.cjs"
13
+ "import": "./dist/client.js"
16
14
  },
17
15
  "./plugin": {
18
16
  "types": "./dist/plugin.d.ts",
19
- "import": "./dist/plugin.js",
20
- "require": "./dist/plugin.cjs"
17
+ "import": "./dist/plugin.js"
21
18
  },
22
19
  "./admin": {
23
20
  "types": "./dist/admin/index.d.ts",
24
- "import": "./dist/admin/index.js",
25
- "require": "./dist/admin/index.cjs"
21
+ "import": "./dist/admin/index.js"
26
22
  }
27
23
  },
28
24
  "files": [
@@ -32,10 +28,10 @@
32
28
  "better-auth": "^1.4.0",
33
29
  "drizzle-orm": "^0.45.0",
34
30
  "server-only": "^0.0.1",
35
- "@murumets-ee/core": "0.1.1",
36
- "@murumets-ee/db": "0.1.1",
37
- "@murumets-ee/logging": "0.1.1",
38
- "@murumets-ee/entity": "0.1.1"
31
+ "@murumets-ee/core": "0.1.3",
32
+ "@murumets-ee/entity": "0.1.2",
33
+ "@murumets-ee/db": "0.1.2",
34
+ "@murumets-ee/logging": "0.1.3"
39
35
  },
40
36
  "devDependencies": {
41
37
  "@types/node": "^22.10.5",
@@ -1 +0,0 @@
1
- "use strict";var T=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var A=(o,r)=>{for(var i in r)T(o,i,{get:r[i],enumerable:!0})},S=(o,r,i,m)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of x(r))!O.call(o,a)&&a!==i&&T(o,a,{get:()=>r[a],enumerable:!(m=P(r,a))||m.enumerable});return o};var C=o=>S(T({},"__esModule",{value:!0}),o);var j={};A(j,{permissionRoutes:()=>E});module.exports=C(j);var I=require("better-auth/plugins/access");var y=["admin","public","authenticated"];var N=/^[a-z][a-z0-9-]{0,49}$/;function p(o,r=200){return new Response(JSON.stringify(o),{status:r,headers:{"Content-Type":"application/json"}})}function n(o,r){return p({error:o},r)}function E(o){let{getStatements:r,loadRoles:i,saveRoles:m,onSave:a}=o;return{prefix:"permissions",resource:"permissions",actions:["view","create","update","delete"],handlers:{GET:async(f,{segments:s})=>{let c=r(),l=await i()??{};return s.length===1&&s[0]==="roles"?p({roles:Object.keys(l),builtInRoles:[...y]}):p({statements:c,roles:l,builtInRoles:[...y]})},PATCH:async(f,{user:s,audit:c})=>{let l=await f.json(),{roles:e}=l;if(!e||typeof e!="object")return n('Body must contain "roles" object',400);if("admin"in e)return n("Cannot modify admin role permissions (admin always has full access)",400);if(s.role&&s.role!=="admin"&&s.role in e)return n("Cannot modify permissions for your own role",403);let t=await i()??{};for(let u of Object.keys(e))if(!(u in t))return n(`Role '${u}' does not exist. Create it first via POST /permissions/roles`,400);let d=r();for(let[u,g]of Object.entries(e)){if(typeof u!="string"||!u)return n("Role names must be non-empty strings",400);if(typeof g!="object"||g===null||Array.isArray(g))return n(`Permissions for role '${u}' must be an object`,400);for(let[R,h]of Object.entries(g)){if(!Array.isArray(h)||!h.every(b=>typeof b=="string"))return n(`Actions for '${R}' in role '${u}' must be a string array`,400);let w=d[R];if(!w)return n(`Unknown resource: ${R}`,400);for(let b of h)if(!w.includes(b))return n(`Invalid action '${b}' for resource '${R}'. Valid: ${w.join(", ")}`,400)}}let v={...t};for(let[u,g]of Object.entries(e))v[u]=g;return await m(v),a?.(),c?.({action:"permissions.update",entityType:"permissions",userId:s.id,userName:s.name,changes:{roles:e},metadata:{rolesModified:Object.keys(e)}}),p({ok:!0})},POST:async(f,{segments:s,user:c,audit:l})=>{if(s.length!==1||s[0]!=="roles")return n("POST only supported at /permissions/roles",400);let e=await f.json(),{name:t}=e;if(!t||typeof t!="string")return n('Body must contain "name" string',400);if(!N.test(t))return n("Role name must be lowercase alphanumeric with hyphens, start with a letter, max 50 chars",400);if(y.includes(t))return n(`Cannot create role with built-in name: ${t}`,400);let d=await i()??{};return t in d?n(`Role already exists: ${t}`,409):(d[t]={},await m(d),a?.(),l?.({action:"permissions.role.create",entityType:"permissions",userId:c.id,userName:c.name,changes:{roleName:t}}),p({name:t,permissions:{}},201))},DELETE:async(f,{segments:s,user:c,audit:l})=>{if(s.length!==2||s[0]!=="roles")return n("DELETE only supported at /permissions/roles/:name",400);let e=s[1];if(y.includes(e))return n(`Cannot delete built-in role: ${e}`,400);let t=await i()??{};if(!(e in t))return n(`Role not found: ${e}`,404);let d=t[e];return delete t[e],await m(t),a?.(),l?.({action:"permissions.role.delete",entityType:"permissions",userId:c.id,userName:c.name,changes:{roleName:e,permissions:d}}),p({deleted:e})}}}}0&&(module.exports={permissionRoutes});
@@ -1,72 +0,0 @@
1
- /**
2
- * Permission management admin routes for the centralized admin API handler.
3
- *
4
- * Provides CRUD for role definitions and permission assignments.
5
- * Admin-only by default (resource: 'permissions', only admin has access).
6
- *
7
- * @example
8
- * ```typescript
9
- * import { createAdminApiHandler } from '@murumets-ee/admin-ui/server'
10
- * import { permissionRoutes } from '@murumets-ee/auth/admin'
11
- *
12
- * const handler = createAdminApiHandler({
13
- * authenticate: async (req) => { ... },
14
- * entities: [...],
15
- * routes: [
16
- * permissionRoutes({
17
- * getStatements: () => catalog,
18
- * loadRoles: () => client.get('roles'),
19
- * saveRoles: (roles) => client.set('roles', roles),
20
- * onSave: () => { cachedChecker = null },
21
- * }),
22
- * ],
23
- * })
24
- * ```
25
- */
26
- /** Fire-and-forget audit log function — structurally matches AuditLogFn from @murumets-ee/core */
27
- type AuditLogFn = (entry: {
28
- action: string;
29
- entityType?: string;
30
- entityId?: string;
31
- userId?: string;
32
- userName?: string;
33
- changes?: Record<string, unknown>;
34
- metadata?: Record<string, unknown>;
35
- }) => void;
36
- interface AdminRoute {
37
- prefix: string;
38
- resource?: string;
39
- actions?: readonly string[];
40
- handlers: Partial<Record<string, (req: Request, ctx: {
41
- segments: string[];
42
- user: {
43
- id: string;
44
- role?: string;
45
- name?: string;
46
- };
47
- audit?: AuditLogFn;
48
- checkPermission: (resource: string, action: string) => boolean;
49
- }) => Promise<Response>>>;
50
- }
51
- interface PermissionRoutesConfig {
52
- /** Returns the resource catalog: resource → available actions */
53
- getStatements: () => Record<string, readonly string[]>;
54
- /** Load saved role definitions from settings (null if first run) */
55
- loadRoles: () => Promise<Record<string, Record<string, string[]>> | null>;
56
- /** Save role definitions to settings */
57
- saveRoles: (roles: Record<string, Record<string, string[]>>) => Promise<void>;
58
- /** Called after save to invalidate cached permission checker */
59
- onSave?: () => void;
60
- }
61
- /**
62
- * Create admin API routes for permission management.
63
- *
64
- * Routes (all prefixed with `/api/admin/permissions`):
65
- * - `GET /permissions` — Get statements + roles + builtInRoles
66
- * - `PATCH /permissions` — Update role permissions
67
- * - `POST /permissions/roles` — Create a custom role
68
- * - `DELETE /permissions/roles/:name` — Delete a custom role
69
- */
70
- declare function permissionRoutes(config: PermissionRoutesConfig): AdminRoute;
71
-
72
- export { permissionRoutes };
package/dist/client.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";var i=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var c=Object.prototype.hasOwnProperty;var g=(a,n)=>{for(var e in n)i(a,e,{get:n[e],enumerable:!0})},u=(a,n,e,s)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of m(n))!c.call(a,r)&&r!==e&&i(a,r,{get:()=>n[r],enumerable:!(s=l(n,r))||s.enumerable});return a};var d=a=>u(i({},"__esModule",{value:!0}),a);var w={};g(w,{createClient:()=>p,createUsersApi:()=>b});module.exports=d(w);var t=require("better-auth/client/plugins"),o=require("better-auth/react");function p(a){return(0,o.createAuthClient)({baseURL:a?.baseURL,plugins:[(0,t.adminClient)(),...a?.organizations?[(0,t.organizationClient)()]:[]]})}function b(a){function n(e){if(e.error)throw new Error(e.error.message??"Unknown error");return e.data}return{async list(e){let s=await a.admin.listUsers({query:{limit:e.limit,offset:e.offset,...e.sortBy?{sortBy:e.sortBy,sortDirection:e.sortDirection}:{},...e.searchValue?{searchValue:e.searchValue,searchField:e.searchField??"email",searchOperator:e.searchOperator??"contains"}:{}}}),r=n(s);return{users:(r.users??[]).map(f),total:r.total??0}},async create(e){let s=await a.admin.createUser({name:e.name,email:e.email,password:e.password,role:e.role});n(s)},async update(e,s){let r=await a.admin.updateUser({userId:e,data:s});n(r)},async remove(e){let s=await a.admin.removeUser({userId:e});n(s)},async setRole(e,s){let r=await a.admin.setRole({userId:e,role:s});n(r)},async ban(e,s){let r=await a.admin.banUser({userId:e,...s?.reason?{banReason:s.reason}:{},...s?.expiresIn?{banExpiresIn:s.expiresIn}:{}});n(r)},async unban(e){let s=await a.admin.unbanUser({userId:e});n(s)},async revokeSessions(e){let s=await a.admin.revokeUserSessions({userId:e});n(s)}}}function f(a){let n=a;return{id:n.id,name:n.name??"",email:n.email,emailVerified:n.emailVerified??!1,image:n.image??null,createdAt:String(n.createdAt??""),updatedAt:String(n.updatedAt??""),role:n.role??null,banned:n.banned??null,banReason:n.banReason??null,banExpires:n.banExpires?String(n.banExpires):null}}0&&(module.exports={createClient,createUsersApi});