import { Arbiter } from '../../src/core/Arbiter.js'; /** * Big Graph Generator for Enterprise Authorization Testing * Generates realistic authorization graphs with complex multi-hop relationships */ export class BigGraphGenerator { constructor(config = {}) { this.config = { scale: 'small', seed: 12345, ...config }; // Scale configurations - reduced for testing this.scaleConfigs = { small: { users: 100, documents: 500, relations: 1000, complexChains: 200, // Multi-hop authorization chains targetQPS: 100, maxMemoryMB: 128, maxLatencyMs: 50 }, medium: { users: 500, documents: 2000, relations: 5000, complexChains: 1000, targetQPS: 200, maxMemoryMB: 256, maxLatencyMs: 100 }, large: { users: 1000, documents: 5000, relations: 10000, complexChains: 2000, targetQPS: 500, maxMemoryMB: 512, maxLatencyMs: 200 }, enterprise: { users: 2000, documents: 10000, relations: 20000, complexChains: 5000, targetQPS: 1000, maxMemoryMB: 1024, maxLatencyMs: 500 }, million: { users: 100000, documents: 900000, relations: 2000000, complexChains: 10000, // Reduced complexity to avoid stack overflow targetQPS: 100, maxMemoryMB: 8192, maxLatencyMs: 1000 } }; // Initialize random number generator with seed this.rng = this._createSeededRNG(this.config.seed); } /** * Create seeded random number generator for reproducible results */ _createSeededRNG(seed) { let state = seed; return { next: () => { state = (state * 1664525 + 1013904223) % 4294967296; return state / 4294967296; } }; } /** * Generate authorization graph for specified model */ generateGraph(model = 'enterprise') { const scale = this.scaleConfigs[this.config.scale]; const graphData = { users: [], documents: [], relations: [], enterprises: [], saasTenants: [], metadata: { model, scale: this.config.scale, generatedAt: new Date().toISOString(), seed: this.config.seed } }; // Generate users graphData.users = this._generateUsers(scale); // Generate documents graphData.documents = this._generateDocuments(scale); // Generate basic user-document relationships const basicRelations = this._generateBasicRelations(scale, graphData.users, graphData.documents); graphData.relations.push(...basicRelations); // Generate complex authorization chains (multi-hop relationships) const { complexChains, roleNodes, departmentNodes } = this._generateComplexAuthorizationChains(scale, graphData.users, graphData.documents); graphData.relations.push(...complexChains); // Add the generated nodes to the graph data graphData.roles = roleNodes; graphData.departments = departmentNodes; // Generate organizational hierarchies const orgRelations = this._generateOrganizationalHierarchies(scale, graphData.users); graphData.relations.push(...orgRelations); // Generate long organizational chains for multi-hop testing const longChains = this._generateLongOrganizationalChains(scale, graphData.users, graphData.documents); graphData.relations.push(...longChains); // Generate role-based access chains const roleChains = this._generateRoleBasedChains(scale, graphData.users, graphData.documents); graphData.relations.push(...roleChains); return graphData; } /** * Load graph data into Arbiter instance */ loadIntoArbiter(graphData, options = {}) { const arbiter = new Arbiter({ fastConstructionMode: true, ...options }); // Add all nodes graphData.users.forEach(user => { arbiter.addNode(user.id, 'user'); }); graphData.documents.forEach(doc => { arbiter.addNode(doc.id, 'document'); }); // Add role nodes if they exist if (graphData.roles) { graphData.roles.forEach(role => { arbiter.addNode(role.id, 'role'); }); } // Add department nodes if they exist if (graphData.departments) { graphData.departments.forEach(dept => { arbiter.addNode(dept.id, 'department'); }); } // Add intermediate nodes for long chains const longChainRelations = graphData.relations.filter(r => r.metadata?.type === 'long_chain'); const intermediateNodes = new Set(); longChainRelations.forEach(relation => { // Add all intermediate nodes (teams, departments, divisions, companies) if (relation.src.startsWith('team:') || relation.src.startsWith('dept:') || relation.src.startsWith('division:') || relation.src.startsWith('company:')) { intermediateNodes.add(relation.src); } if (relation.dst.startsWith('team:') || relation.dst.startsWith('division:') || relation.dst.startsWith('company:')) { intermediateNodes.add(relation.dst); } }); intermediateNodes.forEach(nodeId => { const nodeType = nodeId.startsWith('team:') ? 'team' : nodeId.startsWith('dept:') ? 'department' : nodeId.startsWith('division:') ? 'division' : 'company'; arbiter.addNode(nodeId, nodeType); }); // Add all relations graphData.relations.forEach(relation => { arbiter.addRelation(relation.src, relation.relation, relation.dst, relation.possibility); }); // Disable fast construction mode and build indices now that loading is complete arbiter.setFastConstructionMode(false); // Configure relation types with proper ReBAC patterns const relationTypes = ['can_read', 'can_write', 'can_delete', 'can_share', 'can_admin']; relationTypes.forEach(relType => { // Configure direct relations arbiter.setRelationConfig(relType, { type: 'direct' }); // Configure role-based access using chain rule for proper role-based access const roleBasedRel = `${relType}_via_role`; arbiter.setRelationConfig(roleBasedRel, { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: relType, direction: 'out' } ] }); }); // Configure membership relations as direct const membershipTypes = ['member_of', 'manager_of', 'reports_to']; membershipTypes.forEach(relType => { arbiter.setRelationConfig(relType, { type: 'direct' }); }); // Configure department-based access using chain rule const departmentBasedRel = 'can_write_via_department'; arbiter.setRelationConfig(departmentBasedRel, { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'can_write', direction: 'out' } ] }); // Configure multi-hop access for long chain traversal (computationally intensive) // Use chain rule for complex multi-step authorization arbiter.setRelationConfig('can_access_multi_hop', { type: 'chain', steps: [ { relation: 'member_of', direction: 'out' }, { relation: 'member_of', direction: 'out' }, { relation: 'member_of', direction: 'out' }, { relation: 'member_of', direction: 'out' }, { relation: 'can_read', direction: 'out' } ], collectValues: true, valueAggregation: 'sum' }); return arbiter; } /** * Generate users */ _generateUsers(scale) { const users = []; const names = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack']; const surnames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez']; for (let i = 0; i < scale.users; i++) { const firstName = names[Math.floor(this.rng.next() * names.length)]; const lastName = surnames[Math.floor(this.rng.next() * surnames.length)]; users.push({ id: `user:${firstName} ${lastName}-${i}`, type: 'employee', name: `${firstName} ${lastName}`, email: `${firstName.toLowerCase()} ${lastName.toLowerCase()}@company.com`, role: ['manager', 'employee', 'contractor', 'intern'][Math.floor(this.rng.next() * 4)], clearance: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)], department: `dept:${this._generateDepartmentName()}-${i}`, organization: `org:company-${Math.floor(i / 50)}`, manager: null, startDate: new Date(Date.now() - Math.floor(this.rng.next() * 365 * 24 * 60 * 60 * 1000)).toISOString(), isActive: true }); } return users; } /** * Generate documents */ _generateDocuments(scale) { const documents = []; const docTypes = ['report', 'analysis', 'proposal', 'contract', 'policy', 'spec', 'budget']; for (let i = 0; i < scale.documents; i++) { const docType = docTypes[Math.floor(this.rng.next() * docTypes.length)]; documents.push({ id: `doc:${docType}-${i}`, type: 'document', name: `${docType}_${i}`, classification: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)], department: `dept:${this._generateDepartmentName()}-${i}`, organization: `org:company-${Math.floor(i / 100)}`, requiredClearance: ['public', 'internal', 'confidential', 'secret', 'top_secret'][Math.floor(this.rng.next() * 5)], cost: Math.floor(this.rng.next() * 10000), createdDate: new Date(Date.now() - Math.floor(this.rng.next() * 365 * 24 * 60 * 60 * 1000)).toISOString(), lastModified: new Date(Date.now() - Math.floor(this.rng.next() * 30 * 24 * 60 * 60 * 1000)).toISOString(), size: Math.floor(this.rng.next() * 1000000), tags: this._generateTags() }); } return documents; } /** * Generate basic user-document relationships * FIXED: Reduce direct relations to avoid conflicts with chain relations */ _generateBasicRelations(scale, users, documents) { const relations = []; const operations = ['can_read', 'can_write', 'can_delete', 'can_share']; // Reduce the number of direct relations to avoid conflicts with chain relations const reducedRelations = Math.floor(scale.relations * 0.3); // Only 30% of original for (let i = 0; i < reducedRelations; i++) { const user = users[Math.floor(this.rng.next() * users.length)]; const document = documents[Math.floor(this.rng.next() * documents.length)]; const operation = operations[Math.floor(this.rng.next() * operations.length)]; const possibility = 0.5 + (this.rng.next() * 0.5); // 0.5 to 1.0 relations.push({ src: user.id, relation: operation, dst: document.id, possibility, metadata: { role: user.role, clearance: user.clearance, department: user.department, grantedDate: new Date().toISOString(), expiresDate: null } }); } return relations; } /** * Generate complex authorization chains (multi-hop relationships) * These create realistic enterprise authorization scenarios */ _generateComplexAuthorizationChains(scale, users, documents) { const relations = []; const roleNodes = []; const departmentNodes = []; const chainCount = scale.complexChains || Math.floor(scale.relations * 0.2); // Create role nodes first const roleTypes = ['admin', 'manager', 'employee', 'contractor', 'intern']; const createdRoles = new Set(); for (let i = 0; i < chainCount; i++) { const user = users[Math.floor(this.rng.next() * users.length)]; const document = documents[Math.floor(this.rng.next() * documents.length)]; // Chain 1: User -> Role -> Document (Role-based access) const roleType = roleTypes[Math.floor(this.rng.next() * roleTypes.length)]; const roleId = `role:${roleType}-${Math.floor(this.rng.next() * 10)}`; // Create role node if it doesn't exist if (!createdRoles.has(roleId)) { roleNodes.push({ id: roleId, type: 'role', name: `${roleType} role`, roleType: roleType, permissions: ['can_read', 'can_write', 'can_share'] }); createdRoles.add(roleId); } relations.push({ src: user.id, relation: 'member_of', dst: roleId, possibility: 0.9, metadata: { type: 'role_membership' } }); relations.push({ src: roleId, relation: 'can_read', dst: document.id, possibility: 0.8, metadata: { type: 'role_permission' } }); // Chain 2: User -> Department -> Document (Department-based access) const deptName = this._generateDepartmentName(); const deptId = `dept:${deptName}-${Math.floor(this.rng.next() * 20)}`; // Create department node if it doesn't exist if (!createdRoles.has(deptId)) { departmentNodes.push({ id: deptId, type: 'department', name: `${deptName} department`, departmentName: deptName, permissions: ['can_read', 'can_write'] }); createdRoles.add(deptId); } relations.push({ src: user.id, relation: 'member_of', dst: deptId, possibility: 0.95, metadata: { type: 'department_membership' } }); relations.push({ src: deptId, relation: 'can_write', dst: document.id, possibility: 0.7, metadata: { type: 'department_permission' } }); // Chain 3: User -> Manager -> Document (Manager approval chain) const manager = users[Math.floor(this.rng.next() * users.length)]; relations.push({ src: user.id, relation: 'reports_to', dst: manager.id, possibility: 0.9, metadata: { type: 'reporting_chain' } }); relations.push({ src: manager.id, relation: 'can_share', dst: document.id, possibility: 0.6, metadata: { type: 'manager_permission' } }); } return { complexChains: relations, roleNodes, departmentNodes }; } /** * Generate long organizational chains for multi-hop testing * These create deep hierarchies that will be computationally intensive to traverse */ _generateLongOrganizationalChains(scale, users, documents) { const relations = []; const chainCount = Math.floor(scale.users * 0.1); // 10% of users get long chains for (let i = 0; i < chainCount; i++) { const user = users[Math.floor(this.rng.next() * users.length)]; const document = documents[Math.floor(this.rng.next() * documents.length)]; // Create a long chain: User -> Team -> Department -> Division -> Company -> Document const teamId = `team:${this._generateTeamName()}-${Math.floor(this.rng.next() * 50)}`; const deptId = `dept:${this._generateDepartmentName()}-${Math.floor(this.rng.next() * 100)}`; const divisionId = `division:${this._generateDivisionName()}-${Math.floor(this.rng.next() * 20)}`; const companyId = `company:${this._generateCompanyName()}-${Math.floor(this.rng.next() * 10)}`; // Create the long chain with decreasing possibility (realistic for deep hierarchies) relations.push({ src: user.id, relation: 'member_of', dst: teamId, possibility: 0.95, metadata: { type: 'long_chain', step: 1, chainId: `chain-${i}` } }); relations.push({ src: teamId, relation: 'member_of', dst: deptId, possibility: 0.9, metadata: { type: 'long_chain', step: 2, chainId: `chain-${i}` } }); relations.push({ src: deptId, relation: 'member_of', dst: divisionId, possibility: 0.85, metadata: { type: 'long_chain', step: 3, chainId: `chain-${i}` } }); relations.push({ src: divisionId, relation: 'member_of', dst: companyId, possibility: 0.8, metadata: { type: 'long_chain', step: 4, chainId: `chain-${i}` } }); relations.push({ src: companyId, relation: 'can_read', dst: document.id, possibility: 0.75, metadata: { type: 'long_chain', step: 5, chainId: `chain-${i}` } }); } return relations; } /** * Generate organizational hierarchies */ _generateOrganizationalHierarchies(scale, users) { const relations = []; // Create department hierarchies for (let i = 0; i < Math.floor(scale.users / 10); i++) { const deptId = `dept:${this._generateDepartmentName()}-${i}`; const parentDeptId = `dept:${this._generateDepartmentName()}-${Math.floor(i / 3)}`; if (i > 0) { relations.push({ src: deptId, relation: 'reports_to', dst: parentDeptId, possibility: 0.9, metadata: { type: 'department_hierarchy' } }); } } return relations; } /** * Generate role-based access chains */ _generateRoleBasedChains(scale, users, documents) { const relations = []; // Create role hierarchies const roles = ['admin', 'manager', 'senior', 'employee', 'contractor', 'intern']; for (let i = 0; i < roles.length - 1; i++) { const currentRole = `role:${roles[i]}`; const parentRole = `role:${roles[i + 1]}`; relations.push({ src: currentRole, relation: 'inherits_from', dst: parentRole, possibility: 0.8, metadata: { type: 'role_inheritance' } }); } return relations; } /** * Generate department name */ _generateDepartmentName() { const departments = ['engineering', 'finance', 'hr', 'legal', 'operations', 'marketing', 'sales']; return departments[Math.floor(this.rng.next() * departments.length)]; } _generateTeamName() { const teams = ['Frontend', 'Backend', 'DevOps', 'QA', 'Design', 'Analytics', 'Security', 'Mobile']; return teams[Math.floor(this.rng.next() * teams.length)]; } _generateDivisionName() { const divisions = ['Product', 'Engineering', 'Sales', 'Marketing', 'Operations', 'Finance', 'Legal', 'HR']; return divisions[Math.floor(this.rng.next() * divisions.length)]; } _generateCompanyName() { const companies = ['AcmeCorp', 'TechGiant', 'InnovateLabs', 'DataFlow', 'CloudSystems', 'NextGen', 'FutureTech', 'SmartSolutions']; return companies[Math.floor(this.rng.next() * companies.length)]; } /** * Generate document tags */ _generateTags() { const allTags = ['important', 'urgent', 'confidential', 'draft', 'final', 'reviewed', 'approved']; const tagCount = Math.floor(this.rng.next() * 3) + 1; const tags = []; for (let i = 0; i < tagCount; i++) { const tag = allTags[Math.floor(this.rng.next() * allTags.length)]; if (!tags.includes(tag)) { tags.push(tag); } } return tags; } }