@volcanicminds/backend 3.6.2 → 4.0.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 (60) hide show
  1. package/README.md +11 -4
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/api/admin/routes.d.ts +1 -1
  6. package/dist/lib/api/admin/routes.d.ts.map +1 -1
  7. package/dist/lib/api/admin/routes.js +1 -1
  8. package/dist/lib/api/admin/routes.js.map +1 -1
  9. package/dist/lib/api/auth/controller/auth.d.ts.map +1 -1
  10. package/dist/lib/api/auth/controller/auth.js +5 -8
  11. package/dist/lib/api/auth/controller/auth.js.map +1 -1
  12. package/dist/lib/api/token/controller/token.d.ts +2 -2
  13. package/dist/lib/api/token/controller/token.d.ts.map +1 -1
  14. package/dist/lib/api/token/controller/token.js +13 -10
  15. package/dist/lib/api/token/controller/token.js.map +1 -1
  16. package/dist/lib/api/token/routes.d.ts +7 -7
  17. package/dist/lib/api/token/routes.js +8 -8
  18. package/dist/lib/api/token/routes.js.map +1 -1
  19. package/dist/lib/api/users/controller/user.d.ts.map +1 -1
  20. package/dist/lib/api/users/controller/user.js +65 -18
  21. package/dist/lib/api/users/controller/user.js.map +1 -1
  22. package/dist/lib/api/users/routes.d.ts +45 -8
  23. package/dist/lib/api/users/routes.d.ts.map +1 -1
  24. package/dist/lib/api/users/routes.js +10 -10
  25. package/dist/lib/api/users/routes.js.map +1 -1
  26. package/dist/lib/config/roles.d.ts.map +1 -1
  27. package/dist/lib/config/roles.js +0 -5
  28. package/dist/lib/config/roles.js.map +1 -1
  29. package/dist/lib/loader/genesis.d.ts +6 -0
  30. package/dist/lib/loader/genesis.d.ts.map +1 -0
  31. package/dist/lib/loader/genesis.js +51 -0
  32. package/dist/lib/loader/genesis.js.map +1 -0
  33. package/dist/lib/loader/roles.d.ts +3 -1
  34. package/dist/lib/loader/roles.d.ts.map +1 -1
  35. package/dist/lib/loader/roles.js +23 -4
  36. package/dist/lib/loader/roles.js.map +1 -1
  37. package/dist/lib/loader/router.d.ts +1 -1
  38. package/dist/lib/loader/router.d.ts.map +1 -1
  39. package/dist/lib/loader/router.js +50 -14
  40. package/dist/lib/loader/router.js.map +1 -1
  41. package/dist/lib/util/authz.d.ts +4 -0
  42. package/dist/lib/util/authz.d.ts.map +1 -0
  43. package/dist/lib/util/authz.js +16 -0
  44. package/dist/lib/util/authz.js.map +1 -0
  45. package/dist/types/global.d.ts +10 -1
  46. package/lib/api/admin/controller/manifest.ts +2 -2
  47. package/lib/api/admin/routes.ts +3 -4
  48. package/lib/api/auth/controller/auth.ts +7 -10
  49. package/lib/api/token/controller/token.ts +20 -12
  50. package/lib/api/token/routes.ts +8 -8
  51. package/lib/api/users/controller/user.ts +101 -22
  52. package/lib/api/users/routes.ts +10 -10
  53. package/lib/config/general.ts +1 -1
  54. package/lib/config/roles.ts +0 -5
  55. package/lib/hooks/onRequest.ts +2 -2
  56. package/lib/loader/genesis.ts +76 -0
  57. package/lib/loader/roles.ts +34 -5
  58. package/lib/loader/router.ts +75 -15
  59. package/lib/util/authz.ts +26 -0
  60. package/package.json +1 -1
@@ -58,21 +58,50 @@ async function loadMiddlewares(base, middlewares = []) {
58
58
  }
59
59
  return midds;
60
60
  }
61
- export function processRoute(route, index, file, dir, base, defaultConfig, authMiddlewares, validRoutes) {
62
- const errors = [];
63
- const { method: methodCase, path: pathName = '/', handler, roles: rs = [], config = {}, middlewares = [], rateLimit, cache: cacheInput } = route;
64
- const rsp = !rs.length ? [roles.public] : rs;
65
- let requiredRoles = [];
66
- try {
67
- requiredRoles = rsp.some((r) => r.code === roles.admin.code) ? rsp : [...rsp, roles.admin];
61
+ function resolveRequiredRoles(rs, capability, where, roleErrors) {
62
+ const declared = [];
63
+ for (const ref of rs) {
64
+ if (ref == null) {
65
+ roleErrors.push(`${where} references an undefined role`);
66
+ }
67
+ else if (typeof ref === 'string') {
68
+ const resolved = roles[ref];
69
+ if (resolved)
70
+ declared.push(resolved);
71
+ else
72
+ roleErrors.push(`${where} → unknown role '${ref}' (not declared in config/roles.ts)`);
73
+ }
74
+ else if (ref.code) {
75
+ declared.push(ref);
76
+ }
77
+ else {
78
+ roleErrors.push(`${where} → a declared role has no code`);
79
+ }
68
80
  }
69
- catch (err) {
70
- if (log.e)
71
- log.error(`Error in loading roles for ${methodCase} ${pathName} (${handler})`);
72
- if (log.t)
73
- log.trace(err);
74
- config.enable = false;
81
+ const capRoles = capability
82
+ ? Object.values(roles).filter((r) => Array.isArray(r.capabilities) && r.capabilities.includes(capability))
83
+ : [];
84
+ if (capability && capRoles.length === 0 && log?.w) {
85
+ log.warn(`Route ${where} requires capability '${capability}' held by no role — admin-only`);
86
+ }
87
+ const seen = new Set();
88
+ const out = [];
89
+ for (const r of [...declared, ...capRoles]) {
90
+ if (r?.code && !seen.has(r.code)) {
91
+ seen.add(r.code);
92
+ out.push(r);
93
+ }
75
94
  }
95
+ if (out.length === 0 && !capability)
96
+ out.push(roles.public);
97
+ if (!out.some((r) => r.code === roles.admin.code))
98
+ out.push(roles.admin);
99
+ return out;
100
+ }
101
+ export function processRoute(route, index, file, dir, base, defaultConfig, authMiddlewares, validRoutes, roleErrors = []) {
102
+ const errors = [];
103
+ const { method: methodCase, path: pathName = '/', handler, roles: rs = [], requireCapability, config = {}, middlewares = [], rateLimit, cache: cacheInput } = route;
104
+ const requiredRoles = resolveRequiredRoles(rs, requireCapability, `${methodCase} ${pathName} (${handler})`, roleErrors);
76
105
  const reqAuth = middlewares.some((m) => authMiddlewares.includes(m)) ||
77
106
  requiredRoles.every((r) => r.code !== roles.public.code);
78
107
  if (!config?.security && reqAuth) {
@@ -149,6 +178,7 @@ export function processRoute(route, index, file, dir, base, defaultConfig, authM
149
178
  }
150
179
  async function load() {
151
180
  const validRoutes = [];
181
+ const roleErrors = [];
152
182
  const patterns = normalizePatterns(['..', 'api', '**', 'routes.{ts,js}'], ['src', 'api', '**', 'routes.{ts,js}']);
153
183
  const authMiddlewares = ['global.isAuthenticated', 'global.isAdmin'];
154
184
  for (const pattern of patterns) {
@@ -167,13 +197,19 @@ async function load() {
167
197
  if (log.t)
168
198
  log.trace(`* Add ${routes.length} routes from ${file}`);
169
199
  routes.forEach((route, index) => {
170
- const configuredRoute = processRoute(route, index, file, dir, base, defaultConfig, authMiddlewares, validRoutes);
200
+ const configuredRoute = processRoute(route, index, file, dir, base, defaultConfig, authMiddlewares, validRoutes, roleErrors);
171
201
  if (configuredRoute) {
172
202
  validRoutes.push(configuredRoute);
173
203
  }
174
204
  });
175
205
  }
176
206
  }
207
+ if (roleErrors.length) {
208
+ const message = `Route/role integrity check failed — every route role must be declared in config/roles.ts:\n - ${roleErrors.join('\n - ')}`;
209
+ if (log?.f)
210
+ log.fatal(message);
211
+ throw new Error(message);
212
+ }
177
213
  return validRoutes;
178
214
  }
179
215
  async function applyRoutes(server, routes) {
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../lib/loader/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../../lib/loader/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function roleCodes(value: unknown): string[];
2
+ export declare function includesRole(value: unknown, code: string): boolean;
3
+ export declare function isFounderEmail(email: unknown): boolean;
4
+ //# sourceMappingURL=authz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../../lib/util/authz.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAMlD;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAElE;AAMD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGtD"}
@@ -0,0 +1,16 @@
1
+ export function roleCodes(value) {
2
+ if (!Array.isArray(value))
3
+ return [];
4
+ const codes = value
5
+ .map((r) => (typeof r === 'string' ? r : r?.code))
6
+ .filter((c) => !!c);
7
+ return [...new Set(codes)];
8
+ }
9
+ export function includesRole(value, code) {
10
+ return roleCodes(value).includes(code);
11
+ }
12
+ export function isFounderEmail(email) {
13
+ const founder = process.env.ADMIN_EMAIL?.trim().toLowerCase();
14
+ return !!founder && typeof email === 'string' && email.trim().toLowerCase() === founder;
15
+ }
16
+ //# sourceMappingURL=authz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.js","sourceRoot":"","sources":["../../../lib/util/authz.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACpC,MAAM,KAAK,GAAG,KAAK;SAChB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA8B,EAAE,IAAI,CAAC,CAAC;SAC/E,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5B,CAAC;AAGD,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,IAAY;IACvD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AACxC,CAAC;AAMD,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC7D,OAAO,CAAC,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,CAAA;AACzF,CAAC"}
@@ -27,6 +27,10 @@ export interface Role {
27
27
  code: string
28
28
  name: string
29
29
  description: string
30
+ // Capabilities granted to this role (see docs/AUTHORIZATION_MODEL.md §3). Named only
31
+ // in config, resolved at boot, never at runtime. `admin`/`public` are protected
32
+ // built-ins and never carry capabilities (locked by the roles loader).
33
+ capabilities?: string[]
30
34
  }
31
35
 
32
36
  export interface Roles {
@@ -94,7 +98,12 @@ export interface Route {
94
98
  method: string
95
99
  path: string
96
100
  handler: string
97
- roles: Role[]
101
+ // Role objects (from the global `roles` catalog) or bare string codes; string codes
102
+ // are resolved and validated against the catalog at load (unknown code → fail-fast).
103
+ roles: (Role | string)[]
104
+ // Gate on a capability: the allowed set becomes admin + every role that declares it.
105
+ // A capability held by no role leaves the route admin-only. See docs/AUTHORIZATION_MODEL.md §3.
106
+ requireCapability?: string
98
107
  middlewares: string[]
99
108
  config?: RouteConfig
100
109
  rateLimit?: any
@@ -2,8 +2,8 @@ import { FastifyReply, FastifyRequest } from 'fastify'
2
2
  import { generateManifest } from '../../../manifest/generator.js'
3
3
 
4
4
  // GET /admin/manifest — full Manifest v2 (declared roles). Gating is enforced by the
5
- // route (roles:[admin, backoffice] + isAuthenticated); the manifest itself is not
6
- // per-user — clients filter capabilities against the declared per-capability roles.
5
+ // route (`manifest` capability + isAuthenticated); the manifest itself is not per-user
6
+ // — clients filter capabilities against the declared per-capability roles.
7
7
  export function get(req: FastifyRequest, reply: FastifyReply) {
8
8
  const server = (req.server as any) || global.server
9
9
  return reply.send(generateManifest(server))
@@ -14,10 +14,9 @@ export default {
14
14
  {
15
15
  method: 'GET',
16
16
  path: '/manifest',
17
- // `backoffice` is allowed too: operators with limited roles still need the
18
- // manifest to boot the admin UI the client hides what their roles cannot
19
- // reach (declared per-capability `roles`), and every API route still enforces.
20
- roles: [roles.admin, roles.backoffice],
17
+ // A role granted the `manifest` capability can load the admin console; the client
18
+ // hides what the caller's roles cannot reach and every API route still enforces.
19
+ requireCapability: 'manifest',
21
20
  handler: 'manifest.get',
22
21
  middlewares: ['global.isAuthenticated'],
23
22
  config: {
@@ -50,21 +50,18 @@ export async function register(req: FastifyRequest, reply: FastifyReply) {
50
50
  return reply.status(400).send({ statusCode: 400, error: 'Bad Request', message: 'Repeated password not match' })
51
51
  }
52
52
 
53
- let existings = await req.server['userManager'].retrieveUserByEmail(data.email, req.runner)
53
+ const existings = await req.server['userManager'].retrieveUserByEmail(data.email, req.runner)
54
54
  if (existings) {
55
55
  return reply.status(400).send({ statusCode: 400, error: 'Bad Request', message: 'Email already registered' })
56
56
  }
57
57
 
58
- if ((data.requiredRoles || []).includes('admin')) {
59
- existings = await req.server['userManager'].findQuery({ 'roles:in': 'admin' }, req.runner)
60
- if (existings?.records?.length) {
61
- return reply.status(400).send({ statusCode: 400, error: 'Bad Request', message: 'User admin already registered' })
62
- }
63
- }
64
-
65
- // public is the default
58
+ // Registration never grants the admin role — the admin apex is provisioned only at boot
59
+ // from ADMIN_EMAIL (see docs/AUTHORIZATION_MODEL.md §6). `public` is the default.
66
60
  const publicRole = global.roles?.public?.code || 'public'
67
- data.roles = (data.requiredRoles || []).map((r) => global.roles[r]?.code).filter((r) => !!r)
61
+ const adminRole = global.roles?.admin?.code || 'admin'
62
+ data.roles = (data.requiredRoles || [])
63
+ .map((r) => global.roles[r]?.code)
64
+ .filter((r) => !!r && r !== adminRole)
68
65
  if (!data.roles.includes(publicRole)) {
69
66
  data.roles.push(publicRole)
70
67
  }
@@ -1,4 +1,15 @@
1
1
  import { FastifyReply, FastifyRequest } from 'fastify'
2
+ import { includesRole } from '../../../util/authz.js'
3
+
4
+ // Rule A: only an admin may grant a token the admin role, and only with
5
+ // allow_multiple_admin — otherwise a `tokens` capability holder could mint an admin
6
+ // token and escalate. Tokens carry roles used for authorization (see onRequest).
7
+ function assignsAdmin(req: FastifyRequest, roleValue: unknown): boolean {
8
+ return (
9
+ includesRole(roleValue, roles.admin.code) &&
10
+ (!req.hasRole(roles.admin) || config.options?.allow_multiple_admin !== true)
11
+ )
12
+ }
2
13
 
3
14
  export async function count(req: FastifyRequest, _reply: FastifyReply) {
4
15
  return await req.server['tokenManager'].countQuery(req.data(), req.runner)
@@ -30,6 +41,10 @@ export async function create(req: FastifyRequest, reply: FastifyReply) {
30
41
  data.roles.push(publicRole)
31
42
  }
32
43
 
44
+ if (assignsAdmin(req, data.roles)) {
45
+ return reply.status(403).send({ statusCode: 403, error: 'Forbidden', message: 'Cannot assign the admin role to a token' })
46
+ }
47
+
33
48
  let token = await req.server['tokenManager'].createToken(data, req.runner)
34
49
  if (!token || !token.getId() || !token.externalId) {
35
50
  return reply.status(400).send({ statusCode: 400, error: 'Bad Request', message: 'Token not registered' })
@@ -76,14 +91,13 @@ export async function update(req: FastifyRequest, reply: FastifyReply) {
76
91
  }
77
92
 
78
93
  const data = req.data() || {}
94
+ if (assignsAdmin(req, data.roles)) {
95
+ return reply.status(403).send({ statusCode: 403, error: 'Forbidden', message: 'Cannot assign the admin role to a token' })
96
+ }
79
97
  return req.server['tokenManager'].updateTokenById(token.getId(), data, req.runner)
80
98
  }
81
99
 
82
- export async function block(req: FastifyRequest, reply: FastifyReply) {
83
- if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
84
- return reply.status(403).send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to block a user' })
85
- }
86
-
100
+ export async function block(req: FastifyRequest, _reply: FastifyReply) {
87
101
  const { id: userId } = req.parameters()
88
102
  const { reason } = req.data()
89
103
 
@@ -92,13 +106,7 @@ export async function block(req: FastifyRequest, reply: FastifyReply) {
92
106
  return { ok: !!token.getId() }
93
107
  }
94
108
 
95
- export async function unblock(req: FastifyRequest, reply: FastifyReply) {
96
- if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
97
- return reply
98
- .status(403)
99
- .send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to unblock a user' })
100
- }
101
-
109
+ export async function unblock(req: FastifyRequest, _reply: FastifyReply) {
102
110
  const { id: userId } = req.parameters()
103
111
  await req.server['tokenManager'].unblockTokenById(userId, req.runner)
104
112
  const token = await req.server['tokenManager'].retrieveTokenById(userId, req.runner)
@@ -12,7 +12,7 @@ export default {
12
12
  {
13
13
  method: 'GET',
14
14
  path: '/',
15
- roles: [roles.admin, roles.backoffice],
15
+ requireCapability: 'tokens',
16
16
  handler: 'token.find',
17
17
  middlewares: [],
18
18
  config: {
@@ -31,7 +31,7 @@ export default {
31
31
  {
32
32
  method: 'GET',
33
33
  path: '/count',
34
- roles: [roles.admin, roles.backoffice],
34
+ requireCapability: 'tokens',
35
35
  handler: 'token.count',
36
36
  middlewares: [],
37
37
  config: {
@@ -48,7 +48,7 @@ export default {
48
48
  {
49
49
  method: 'GET',
50
50
  path: '/:id',
51
- roles: [roles.admin, roles.backoffice],
51
+ requireCapability: 'tokens',
52
52
  handler: 'token.findOne',
53
53
  middlewares: [],
54
54
  config: {
@@ -66,7 +66,7 @@ export default {
66
66
  {
67
67
  method: 'POST',
68
68
  path: '/',
69
- roles: [roles.admin, roles.backoffice],
69
+ requireCapability: 'tokens',
70
70
  handler: 'token.create',
71
71
  middlewares: ['global.isAuthenticated'],
72
72
  config: {
@@ -84,7 +84,7 @@ export default {
84
84
  {
85
85
  method: 'PUT',
86
86
  path: '/:id',
87
- roles: [roles.admin, roles.backoffice],
87
+ requireCapability: 'tokens',
88
88
  handler: 'token.update',
89
89
  middlewares: ['global.isAuthenticated'],
90
90
  config: {
@@ -103,7 +103,7 @@ export default {
103
103
  {
104
104
  method: 'DELETE',
105
105
  path: '/:id',
106
- roles: [roles.admin, roles.backoffice],
106
+ requireCapability: 'tokens',
107
107
  handler: 'token.remove',
108
108
  middlewares: ['global.isAuthenticated'],
109
109
  config: {
@@ -118,7 +118,7 @@ export default {
118
118
  {
119
119
  method: 'POST',
120
120
  path: '/block/:id',
121
- roles: [roles.admin, roles.backoffice],
121
+ requireCapability: 'tokens',
122
122
  handler: 'token.block',
123
123
  middlewares: ['global.isAuthenticated'],
124
124
  config: {
@@ -134,7 +134,7 @@ export default {
134
134
  {
135
135
  method: 'POST',
136
136
  path: '/unblock/:id',
137
- roles: [roles.admin, roles.backoffice],
137
+ requireCapability: 'tokens',
138
138
  handler: 'token.unblock',
139
139
  middlewares: ['global.isAuthenticated'],
140
140
  config: {
@@ -1,6 +1,17 @@
1
1
  import { FastifyReply, FastifyRequest } from 'fastify'
2
2
  import type { AuthenticatedUser } from '../../../../types/global.js'
3
3
  import { MfaPolicy } from '../../../config/constants.js'
4
+ import { includesRole, isFounderEmail } from '../../../util/authz.js'
5
+
6
+ const forbidden = (reply: FastifyReply, message: string) =>
7
+ reply.status(403).send({ statusCode: 403, error: 'Forbidden', message })
8
+
9
+ // The admin role is a restricted apex (default single, see allow_multiple_admin). This
10
+ // guards against dropping to zero admins, which would lock the instance out.
11
+ async function isLastAdmin(req: FastifyRequest): Promise<boolean> {
12
+ const total = await req.server['userManager'].countQuery({ 'roles:in': roles.admin.code }, req.runner)
13
+ return Number(total) <= 1
14
+ }
4
15
 
5
16
  export async function getRoles(_req: FastifyRequest, reply: FastifyReply) {
6
17
  const allRoles = Object.keys(roles).map((key) => roles[key])
@@ -23,21 +34,15 @@ export async function findOne(req: FastifyRequest, reply: FastifyReply) {
23
34
  }
24
35
 
25
36
  export async function create(req: FastifyRequest, reply: FastifyReply) {
26
- if (!req.hasRole(roles.admin)) {
27
- return reply.status(403).send({ statusCode: 403, error: 'Forbidden', message: 'Only admins can create users' })
28
- }
29
-
30
37
  const { id: _id, ...data } = req.data()
31
38
 
32
- // Roles may arrive as string codes (JSON body: ['admin']) or as role objects
33
- // ({ code: 'admin', ... }); normalize to codes before checking.
34
- const roleCodes = (data.roles || []).map((r: string | { code?: string }) => (typeof r === 'string' ? r : r?.code))
35
- if (roleCodes.includes(roles.admin.code)) {
36
- if (config.options?.allow_multiple_admin !== true) {
37
- return reply
38
- .status(403)
39
- .send({ statusCode: 403, error: 'Forbidden', message: 'Cannot assign admin role to a user' })
40
- }
39
+ // Rule A: assigning the admin role requires an admin caller AND allow_multiple_admin.
40
+ // The `users` capability alone can manage users but never mint an admin.
41
+ if (
42
+ includesRole(data.roles, roles.admin.code) &&
43
+ (!req.hasRole(roles.admin) || config.options?.allow_multiple_admin !== true)
44
+ ) {
45
+ return forbidden(reply, 'Cannot assign the admin role')
41
46
  }
42
47
 
43
48
  // Pass req.runner so creation lands in the resolved tenant schema (multi-tenant);
@@ -68,6 +73,42 @@ export async function update(req: FastifyRequest, reply: FastifyReply) {
68
73
  }
69
74
 
70
75
  const { id: _id, ...userData } = req.data()
76
+
77
+ const target = await req.server['userManager'].retrieveUserById(id, req.runner)
78
+ const targetIsAdmin = includesRole(target?.roles, roles.admin.code)
79
+ const changingRoles = Object.prototype.hasOwnProperty.call(userData, 'roles')
80
+
81
+ // Sovereign founder: cannot be demoted or have its email changed via the API
82
+ // (transfer happens via env + redeploy).
83
+ if (isFounderEmail(target?.email)) {
84
+ if (changingRoles && !includesRole(userData.roles, roles.admin.code)) {
85
+ return forbidden(reply, 'Cannot demote the sovereign admin')
86
+ }
87
+ if (
88
+ typeof userData.email === 'string' &&
89
+ userData.email.trim().toLowerCase() !== (target.email || '').trim().toLowerCase()
90
+ ) {
91
+ return forbidden(reply, 'Cannot change the sovereign admin email')
92
+ }
93
+ }
94
+
95
+ // Rule B: only an admin may modify an admin subject.
96
+ if (targetIsAdmin && !req.hasRole(roles.admin)) {
97
+ return forbidden(reply, 'Cannot modify an admin user')
98
+ }
99
+ // Rule A: assigning the admin role requires an admin caller AND allow_multiple_admin.
100
+ if (
101
+ changingRoles &&
102
+ includesRole(userData.roles, roles.admin.code) &&
103
+ (!req.hasRole(roles.admin) || config.options?.allow_multiple_admin !== true)
104
+ ) {
105
+ return forbidden(reply, 'Cannot assign the admin role')
106
+ }
107
+ // Never drop to zero admins: refuse demoting the last admin.
108
+ if (targetIsAdmin && changingRoles && !includesRole(userData.roles, roles.admin.code) && (await isLastAdmin(req))) {
109
+ return forbidden(reply, 'Cannot demote the last admin')
110
+ }
111
+
71
112
  return await req.server['userManager'].updateUserById(id, userData, req.runner)
72
113
  }
73
114
 
@@ -76,6 +117,23 @@ export async function remove(req: FastifyRequest, reply: FastifyReply) {
76
117
  if (!id) {
77
118
  return reply.status(404).send()
78
119
  }
120
+
121
+ const target = await req.server['userManager'].retrieveUserById(id, req.runner)
122
+ const targetIsAdmin = includesRole(target?.roles, roles.admin.code)
123
+
124
+ // Sovereign founder: cannot be deleted.
125
+ if (isFounderEmail(target?.email)) {
126
+ return forbidden(reply, 'Cannot delete the sovereign admin')
127
+ }
128
+ // Rule B: only an admin may delete an admin subject.
129
+ if (targetIsAdmin && !req.hasRole(roles.admin)) {
130
+ return forbidden(reply, 'Cannot delete an admin user')
131
+ }
132
+ // Never drop to zero admins.
133
+ if (targetIsAdmin && (await isLastAdmin(req))) {
134
+ return forbidden(reply, 'Cannot delete the last admin')
135
+ }
136
+
79
137
  return await req.server['userManager'].deleteUser(id, req.runner)
80
138
  }
81
139
 
@@ -126,13 +184,24 @@ export async function block(req: FastifyRequest, reply: FastifyReply) {
126
184
  throw new Error('Not implemented')
127
185
  }
128
186
 
129
- if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
130
- return reply.status(403).send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to block a user' })
131
- }
132
-
133
187
  const { id: userId } = req.parameters()
134
188
  const { reason } = req.data()
135
189
 
190
+ const target = await req.server['userManager'].retrieveUserById(userId, req.runner)
191
+ const targetIsAdmin = includesRole(target?.roles, roles.admin.code)
192
+ // Sovereign founder: cannot be blocked.
193
+ if (isFounderEmail(target?.email)) {
194
+ return forbidden(reply, 'Cannot block the sovereign admin')
195
+ }
196
+ // Rule B: only an admin may block an admin subject.
197
+ if (targetIsAdmin && !req.hasRole(roles.admin)) {
198
+ return forbidden(reply, 'Cannot block an admin user')
199
+ }
200
+ // Never drop to zero admins (blocking the last admin locks the instance out).
201
+ if (targetIsAdmin && (await isLastAdmin(req))) {
202
+ return forbidden(reply, 'Cannot block the last admin')
203
+ }
204
+
136
205
  let user = await req.server['userManager'].blockUserById(userId, reason, req.runner)
137
206
  user = await req.server['userManager'].resetExternalId(user.getId(), req.runner)
138
207
  return { ok: !!user.getId() }
@@ -143,13 +212,14 @@ export async function unblock(req: FastifyRequest, reply: FastifyReply) {
143
212
  throw new Error('Not implemented')
144
213
  }
145
214
 
146
- if (!req.hasRole(roles.admin) && !req.hasRole(roles.backoffice)) {
147
- return reply
148
- .status(403)
149
- .send({ statusCode: 403, code: 'ROLE_NOT_ALLOWED', message: 'Not allowed to unblock a user' })
215
+ const { id: userId } = req.parameters()
216
+
217
+ const target = await req.server['userManager'].retrieveUserById(userId, req.runner)
218
+ // Rule B: only an admin may unblock an admin subject.
219
+ if (includesRole(target?.roles, roles.admin.code) && !req.hasRole(roles.admin)) {
220
+ return forbidden(reply, 'Cannot unblock an admin user')
150
221
  }
151
222
 
152
- const { id: userId } = req.parameters()
153
223
  const user = await req.server['userManager'].unblockUserById(userId, req.runner)
154
224
  return { ok: !!user.getId() }
155
225
  }
@@ -165,6 +235,11 @@ export async function resetMfaByAdmin(req: FastifyRequest, reply: FastifyReply)
165
235
  return reply.status(400).send({ statusCode: 400, error: 'Bad Request', message: 'Missing user id' })
166
236
  }
167
237
 
238
+ const mfaTarget = await req.server['userManager'].retrieveUserById(id, req.runner)
239
+ if (isFounderEmail(mfaTarget?.email) && !isFounderEmail(req.user?.email)) {
240
+ return forbidden(reply, 'Cannot reset the sovereign admin MFA')
241
+ }
242
+
168
243
  try {
169
244
  await req.server['userManager'].disableMfa(id, req.runner)
170
245
  return { ok: true }
@@ -200,6 +275,10 @@ export async function resetPasswordByAdmin(req: FastifyRequest, reply: FastifyRe
200
275
  return reply.status(404).send({ statusCode: 404, error: 'Not Found', message: 'User not found' })
201
276
  }
202
277
 
278
+ if (isFounderEmail(user.email) && !isFounderEmail(req.user?.email)) {
279
+ return forbidden(reply, 'Cannot reset the sovereign admin password')
280
+ }
281
+
203
282
  await req.server['userManager'].resetPassword(user, password, req.runner)
204
283
  return { ok: true }
205
284
  } catch (error) {