hub_scope.mjs
74 lines 2.7 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Multi-vault (Phase 15) Option B: per-user per-vault scope (projects/folders allowlist).
3 * Format: { "user_id": { "vault_id": { "projects": string[], "folders": string[] } }, ... }
4 * Omitted or empty = no scope (user sees full vault).
5 */
6
7 import fs from 'fs';
8 import path from 'path';
9
10 const SCOPE_FILE = 'hub_scope.json';
11
12 /**
13 * @param {string} dataDir - e.g. config.data_dir
14 * @returns {{ [userId: string]: { [vaultId: string]: { projects?: string[], folders?: string[] } } }}
15 */
16 export function readScope(dataDir) {
17 if (!dataDir) return {};
18 const filePath = path.join(dataDir, SCOPE_FILE);
19 try {
20 if (!fs.existsSync(filePath)) return {};
21 const raw = fs.readFileSync(filePath, 'utf8');
22 const data = JSON.parse(raw);
23 if (!data || typeof data !== 'object') return {};
24 return data;
25 } catch (_) {
26 return {};
27 }
28 }
29
30 /**
31 * @param {string} dataDir
32 * @param {{ [userId: string]: { [vaultId: string]: { projects?: string[], folders?: string[] } } }} scope
33 */
34 export function writeScope(dataDir, scope) {
35 if (!dataDir) throw new Error('data_dir required');
36 const filePath = path.join(dataDir, SCOPE_FILE);
37 const obj = {};
38 for (const [uid, vaultMap] of Object.entries(scope)) {
39 if (typeof uid !== 'string' || !uid.trim() || !vaultMap || typeof vaultMap !== 'object') continue;
40 obj[uid.trim()] = {};
41 for (const [vaultId, rules] of Object.entries(vaultMap)) {
42 if (typeof vaultId !== 'string' || !vaultId.trim() || !rules) continue;
43 const projects = Array.isArray(rules.projects)
44 ? rules.projects.filter((p) => typeof p === 'string' && p.trim()).map((p) => p.trim())
45 : [];
46 const folders = Array.isArray(rules.folders)
47 ? rules.folders.filter((f) => typeof f === 'string' && f.trim()).map((f) => f.trim())
48 : [];
49 if (projects.length > 0 || folders.length > 0) {
50 obj[uid.trim()][vaultId.trim()] = { projects, folders };
51 }
52 }
53 }
54 fs.mkdirSync(dataDir, { recursive: true });
55 fs.writeFileSync(filePath, JSON.stringify(obj, null, 2), 'utf8');
56 }
57
58 /**
59 * Get scope for (userId, vaultId). Returns null if no scope (full access).
60 * @param {string} dataDir
61 * @param {string} userId
62 * @param {string} vaultId
63 * @returns {{ projects: string[], folders: string[] } | null}
64 */
65 export function getScopeForUserVault(dataDir, userId, vaultId) {
66 const scope = readScope(dataDir);
67 const userScope = scope[userId];
68 if (!userScope || typeof userScope[vaultId] !== 'object') return null;
69 const r = userScope[vaultId];
70 const projects = Array.isArray(r.projects) ? r.projects : [];
71 const folders = Array.isArray(r.folders) ? r.folders : [];
72 if (projects.length === 0 && folders.length === 0) return null;
73 return { projects, folders };
74 }
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago