local-auth-role.mjs
62 lines 2.1 KB
Raw
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor ⚠ breaking 11 days ago
1 /**
2 * Phase 8 P1b-b — role resolution for offline-locked auth (§5.2, §5.3).
3 */
4
5 import { loadRoleMap, getRole } from '../roles.mjs';
6
7 const VALID_ROLES = new Set(['admin', 'editor', 'viewer', 'evaluator']);
8
9 /**
10 * Resolve role for a local or OAuth sub.
11 * When offlineLockedActive and role store is empty → denied (member), not default-admin.
12 * @param {string} dataDir
13 * @param {string} sub
14 * @param {{ offlineLockedActive?: boolean, adminUserIdsSet?: Set<string> }} [opts]
15 * @returns {string}
16 */
17 export function resolveLocalAuthRole(dataDir, sub, opts = {}) {
18 const { offlineLockedActive = false, adminUserIdsSet = new Set() } = opts;
19 const roleMap = loadRoleMap(dataDir);
20 if (roleMap.size === 0) {
21 if (offlineLockedActive) return 'member';
22 return 'admin';
23 }
24 const stored = getRole(roleMap, sub);
25 if (stored && stored !== 'member' && VALID_ROLES.has(stored)) return stored;
26 if (adminUserIdsSet.has(sub)) return 'admin';
27 return stored === 'member' || !stored ? 'member' : stored;
28 }
29
30 /**
31 * Whether the credential store has at least one admin role entry.
32 * @param {string} dataDir
33 * @returns {boolean}
34 */
35 export function hasBootstrappedAdmin(dataDir, credentialStore) {
36 const roleMap = loadRoleMap(dataDir);
37 for (const role of roleMap.values()) {
38 if (role === 'admin') return true;
39 }
40 if (credentialStore && credentialStore.credentials) {
41 for (const cred of Object.values(credentialStore.credentials)) {
42 const sub = `local:${cred.userId}`;
43 if (roleMap.get(sub) === 'admin') return true;
44 }
45 }
46 return false;
47 }
48
49 /**
50 * Self-hosted effectiveRole helper when offline-locked is active.
51 * @param {import('../roles.mjs').loadRoleMap extends Function ? ReturnType<typeof loadRoleMap> : never} roleMap
52 * @param {string} sub
53 * @param {boolean} offlineLockedActive
54 * @returns {string}
55 */
56 export function effectiveRoleForHub(roleMap, sub, offlineLockedActive) {
57 if (roleMap.size === 0) {
58 return offlineLockedActive ? 'member' : 'admin';
59 }
60 const gr = getRole(roleMap, sub);
61 return gr === 'member' || !gr ? 'editor' : gr;
62 }
File History 1 commit
sha256:b5f647cb9c409f563d4671fe3fc05ddea01fabfed9b41fc11cb923588e1c1baf mirror: GitHub Phase A durable MCP OAuth (#270) Human minor 11 days ago