UNPKG

spicedb-utils-lib

Version:

A utility wrapper for SpiceDB relationship management

534 lines (470 loc) 14.9 kB
const axios = require('axios'); const fs = require('fs'); const util = require('util'); const SPICEDB_API = 'http://172.31.100.253:8443/v1'; // Cập nhật IP nếu khác const AUTH_HEADER = { Authorization: 'Bearer foobar', 'Content-Type': 'application/json', }; async function cleanupAllRelationships(resourceTypes) { for (const resourceType of resourceTypes) { console.log(`\n🔍 Reading relationships for type: ${resourceType}`); let totalDeleted = 0; try { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: resourceType, }, limit: 100, }, { headers: AUTH_HEADER, responseType: 'text', // NDJSON hoặc JSON object } ); let lines = []; if (typeof response.data === 'string') { // NDJSON: mỗi dòng là 1 JSON object lines = response.data.trim().split('\n'); } else { // JSON object: đưa vào mảng thủ công lines = [JSON.stringify(response.data)]; } for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json.result?.relationship; if (!rel) continue; console.log(`🧩 ${rel.resource.objectType}:${rel.resource.objectId}#${rel.relation} @ ${rel.subject.object.objectType}:${rel.subject.object.objectId}`); fs.appendFileSync(`backup_${resourceType}.json`, JSON.stringify(rel) + '\n'); await axios.post( `${SPICEDB_API}/relationships/delete`, { relationship_filter: { resource_type: rel.resource.objectType, resource_id: rel.resource.objectId, relation: rel.relation, subject: { object: { object_type: rel.subject.object.objectType, object_id: rel.subject.object.objectId, }, }, }, }, { headers: AUTH_HEADER } ); console.log('🗑️ Deleted'); totalDeleted++; } } catch (err) { console.error(`❌ Error for ${resourceType}:`, JSON.stringify(err.response?.data || err.message, null, 2)); } console.log(`✅ Finished ${resourceType} — ${totalDeleted} relationships deleted`); } } const resourceTypes = [ // 'user', // 'usergroup', // 'role', 'organization', // 'case', // 'document', // 'perm_item' ]; /** * Tạo relationship: role:<roleName>#permissions@perm_item:<permName> */ async function createPermission(roleName, permName) { try { const response = await axios.post( `${SPICEDB_API}/relationships/write`, { updates: [ { operation: 'OPERATION_CREATE', relationship: { resource: { object_type: 'role', object_id: roleName, }, relation: 'permissions', subject: { object: { object_type: 'perm_item', object_id: permName, }, }, }, }, ], }, { headers: AUTH_HEADER } ); console.log(`✅ Created: role:${roleName}#permissions@perm_item:${permName}`); } catch (err) { console.error(`❌ Failed to create permission:`, err.response?.data || err.message); } } async function listPermissions() { const resourceType = 'role'; const relation = 'permissions'; console.log(`\n🔍 Reading permissions for type: ${resourceType}#${relation}`); try { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: resourceType, relation: relation, }, limit: 100, }, { headers: AUTH_HEADER, responseType: 'text', // NDJSON } ); const lines = response.data.trim().split('\n'); const results = []; for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json.result?.relationship; if (!rel) continue; const role = rel.resource.objectId; const perm = rel.subject?.object?.objectId; if (role && perm) { const lineStr = `role:${role}#permissions@perm_item:${perm}`; results.push(lineStr); console.log(`✅ ${lineStr}`); } } console.log(`\n🔢 Total permissions found: ${results.length}`); return results; } catch (err) { console.error(`❌ Error reading permissions:`, err.response?.data || err.message); return []; } } async function addUserToGroup(userId, groupId) { try { const payload = { updates: [ { operation: 'OPERATION_CREATE', relationship: { resource: { object_type: 'usergroup', object_id: groupId, }, relation: 'members', subject: { object: { object_type: 'user', object_id: userId, }, }, }, }, ], }; await axios.post(`${SPICEDB_API}/relationships/write`, payload, { headers: AUTH_HEADER, }); console.log(`✅ user:${userId} added to usergroup:${groupId}`); } catch (err) { console.error(`❌ Failed to add user:${userId} to group:${groupId}`, err.response?.data || err.message); } } async function listUsersInGroup(groupId) { const members = []; try { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: 'usergroup', relation: 'members', }, limit: 100, }, { headers: AUTH_HEADER, responseType: 'text', // NDJSON format } ); const lines = response.data.trim().split('\n'); for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json.result?.relationship; if (!rel) continue; const group = rel.resource?.objectId; const userId = rel.subject?.object?.objectId; if (group === groupId && rel.relation === "members" && userId) { members.push(userId); console.log(`👤 user:${userId} ∈ group:${group}`); } } console.log(`📋 Total members in group ${groupId}: ${members.length}`); return members; } catch (err) { console.error(`❌ Error listing users in group ${groupId}:`, err.message); return []; } } async function assignRoleToGroup(groupId, roleId) { try { const response = await axios.post( `${SPICEDB_API}/relationships/write`, { updates: [ { operation: "OPERATION_CREATE", relationship: { resource: { objectType: "usergroup", objectId: groupId, }, relation: "roles", subject: { object: { objectType: "role", objectId: roleId, }, }, }, }, ], }, { headers: AUTH_HEADER } ); console.log(`✅ Assigned role:${roleId} to group:${groupId}`); } catch (err) { console.error( `❌ Failed to assign role ${roleId} to group ${groupId}`, err.response?.data || err.message ); } } async function listRolesOfGroup(groupId) { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: "usergroup", }, limit: 100, }, { headers: AUTH_HEADER, responseType: 'text', } ); const lines = response.data.trim().split('\n'); const roles = []; for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json.result?.relationship; if (!rel) continue; const resource = rel.resource; const subject = rel.subject?.object; // Lọc đúng group và đúng role if ( resource?.objectType === "usergroup" && resource?.objectId === groupId && rel.relation === "roles" && subject?.objectType === "role" ) { roles.push(subject.objectId); console.log(`🔑 group:${groupId} ➜ role:${subject.objectId}`); } } console.log(`📋 Total roles of group ${groupId}: ${roles.length}`); } async function assignRoleToUser(userId, roleId) { try { await axios.post( `${SPICEDB_API}/relationships/write`, { updates: [ { operation: "OPERATION_CREATE", relationship: { resource: { objectType: "user", objectId: userId, }, relation: "roles", subject: { object: { objectType: "role", objectId: roleId, }, }, }, }, ], }, { headers: AUTH_HEADER } ); console.log(`✅ Assigned role:${roleId} to user:${userId}`); } catch (err) { console.error(`❌ Failed to assign role to user:`, err.response?.data || err.message); } } async function listRolesOfUser(userId) { try { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: "user", resource_id: userId, relation: "roles", }, limit: 100, }, { headers: AUTH_HEADER, responseType: "text", // nếu NDJSON thì trả text } ); let lines = []; try { lines = response.data.trim().split("\n"); } catch { lines = [JSON.stringify(response.data)]; } const roles = []; for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json?.result?.relationship ?? json?.relationship; if (!rel) continue; const roleId = rel.subject?.object?.objectId; if (roleId) { roles.push(roleId); console.log(`🔗 user:${userId} → role:${roleId}`); } } console.log(`📋 Total roles of user ${userId}: ${roles.length}`); return roles; } catch (err) { console.error(`❌ Error listing roles for user ${userId}:`, err.response?.data || err.message); return []; } } async function assignGroupToCase(caseId, groupId, roleType) { try { const response = await axios.post( `${SPICEDB_API}/relationships/write`, { updates: [ { operation: "OPERATION_CREATE", relationship: { resource: { objectType: "case", objectId: caseId, }, relation: roleType, // viewers, commenters, editors, owners subject: { object: { objectType: "usergroup", objectId: groupId, }, optionalRelation: "members", }, }, }, ], }, { headers: AUTH_HEADER } ); console.log(`✅ Assigned group:${groupId}#members to case:${caseId}#${roleType}`); } catch (err) { console.error(`❌ Failed to assign group to case:`, err.response?.data || err.message); } } async function listGroupsOfCase(caseId, roleType) { try { const response = await axios.post( `${SPICEDB_API}/relationships/read`, { relationship_filter: { resource_type: "case", resource_id: caseId, relation: roleType, // viewers, commenters, etc. }, limit: 100, }, { headers: AUTH_HEADER, responseType: "text", } ); let lines = []; try { lines = response.data.trim().split("\n"); } catch { lines = [JSON.stringify(response.data)]; } const groupIds = []; for (const line of lines) { if (!line.trim()) continue; const json = JSON.parse(line); const rel = json?.result?.relationship ?? json?.relationship; if (!rel) continue; const groupId = rel.subject?.object?.objectType === "usergroup" ? rel.subject?.object?.objectId : null; if (groupId) { groupIds.push(groupId); console.log(`🔗 case:${caseId}#${roleType} ← group:${groupId}#members`); } } console.log(`📋 Total groups in case ${caseId}#${roleType}: ${groupIds.length}`); return groupIds; } catch (err) { console.error(`❌ Error listing groups of case ${caseId}:`, err.response?.data || err.message); return []; } } async function checkPermission(resourceType, resourceId, permission, subjectType, subjectId) { try { const response = await axios.post( `${SPICEDB_API}/permissions/check`, { resource: { objectType: resourceType, objectId: resourceId, }, permission, subject: { object: { objectType: subjectType, objectId: subjectId, }, }, }, { headers: AUTH_HEADER, } ); const allowed = response?.data?.permissionship === "PERMISSIONSHIP_HAS_PERMISSION"; console.log( allowed ? `✅ ${subjectType}:${subjectId} HAS ${permission} on ${resourceType}:${resourceId}` : `❌ ${subjectType}:${subjectId} DOES NOT HAVE ${permission} on ${resourceType}:${resourceId}` ); return allowed; } catch (err) { console.error(`❌ Error checking permission:`, err.response?.data || err.message); return false; } }