initial commit: @arbiter/core authorization engine with js-rigor hardening
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/ binary modes, condensed snapshots, value relations) with 39 rigor test campaigns. Includes fixes for snapshot binary writer/reader format mismatch (snapshot-of-snapshot corruption), possibility write-boundary validation, empty-graph snapshot serialization, relation lookup cache direction collision, config-redefinition cache invalidation, binary threshold semantics, defeasible compiled routing, and comparator reason whitelisting.
This commit is contained in:
@@ -0,0 +1,613 @@
|
||||
import { Arbiter } from '../../src/core/Arbiter.js';
|
||||
import { RuleEvaluator } from '../../src/authorization/RuleEvaluator.js';
|
||||
import { describe, it, beforeEach } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
describe.skip('Complex Policy Composition - Enterprise Authorization Scenarios', () => {
|
||||
let arbiter;
|
||||
let ruleEvaluator;
|
||||
|
||||
beforeEach(() => {
|
||||
arbiter = new Arbiter({ embeddingDimensions: 256 });
|
||||
ruleEvaluator = new RuleEvaluator(arbiter);
|
||||
|
||||
// Set up comprehensive enterprise entities
|
||||
setupEnterpriseEntities();
|
||||
setupFinancialEntities();
|
||||
setupTeamHierarchies();
|
||||
setupProjectStructures();
|
||||
});
|
||||
|
||||
function setupEnterpriseEntities() {
|
||||
// Users
|
||||
['alice', 'bob', 'charlie', 'diana', 'eve', 'frank', 'grace', 'henry'].forEach(name => {
|
||||
arbiter.addNode(`user:${name}`, 'user');
|
||||
});
|
||||
|
||||
// Teams and departments
|
||||
['engineering', 'marketing', 'finance', 'hr', 'legal', 'executive'].forEach(dept => {
|
||||
arbiter.addNode(`team:${dept}`, 'team');
|
||||
arbiter.addNode(`dept:${dept}`, 'department');
|
||||
});
|
||||
|
||||
// Roles
|
||||
['ceo', 'cto', 'vp', 'director', 'manager', 'senior', 'junior', 'intern'].forEach(role => {
|
||||
arbiter.addNode(`role:${role}`, 'role');
|
||||
});
|
||||
}
|
||||
|
||||
function setupFinancialEntities() {
|
||||
// Financial entities
|
||||
['budget:engineering', 'budget:marketing', 'budget:finance'].forEach(budget => {
|
||||
arbiter.addNode(budget, 'budget');
|
||||
});
|
||||
|
||||
// Accounts and transactions
|
||||
['account:corporate', 'account:engineering', 'account:marketing'].forEach(account => {
|
||||
arbiter.addNode(account, 'account');
|
||||
});
|
||||
|
||||
// Financial thresholds
|
||||
['threshold:low', 'threshold:medium', 'threshold:high', 'threshold:executive'].forEach(threshold => {
|
||||
arbiter.addNode(threshold, 'threshold');
|
||||
});
|
||||
}
|
||||
|
||||
function setupTeamHierarchies() {
|
||||
// Team memberships
|
||||
const memberships = [
|
||||
['user:alice', 'team:engineering'],
|
||||
['user:bob', 'team:engineering'],
|
||||
['user:charlie', 'team:marketing'],
|
||||
['user:diana', 'team:finance'],
|
||||
['user:eve', 'team:executive'],
|
||||
['user:frank', 'team:legal'],
|
||||
['user:grace', 'team:hr'],
|
||||
['user:henry', 'team:engineering']
|
||||
];
|
||||
|
||||
memberships.forEach(([user, team]) => {
|
||||
arbiter.addRelation(user, 'member_of', team, {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
});
|
||||
|
||||
// Department relationships
|
||||
const deptRelations = [
|
||||
['team:engineering', 'dept:engineering'],
|
||||
['team:marketing', 'dept:marketing'],
|
||||
['team:finance', 'dept:finance'],
|
||||
['team:executive', 'dept:executive']
|
||||
];
|
||||
|
||||
deptRelations.forEach(([team, dept]) => {
|
||||
arbiter.addRelation(team, 'belongs_to', dept, {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupProjectStructures() {
|
||||
// Projects
|
||||
['project:alpha', 'project:beta', 'project:gamma', 'project:classified'].forEach(project => {
|
||||
arbiter.addNode(project, 'project');
|
||||
});
|
||||
|
||||
// Documents and resources
|
||||
['doc:public', 'doc:internal', 'doc:confidential', 'doc:secret'].forEach(doc => {
|
||||
arbiter.addNode(doc, 'document');
|
||||
});
|
||||
|
||||
// Resources
|
||||
['server:prod', 'server:staging', 'server:dev', 'database:main'].forEach(resource => {
|
||||
arbiter.addNode(resource, 'resource');
|
||||
});
|
||||
}
|
||||
|
||||
describe('Enterprise Financial Authorization', () => {
|
||||
it('handles complex budget approval with team hierarchy and spending limits', () => {
|
||||
// Set up financial thresholds
|
||||
arbiter.addRelation('threshold:low', 'amount', 'budget:engineering', {
|
||||
value: 1000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('threshold:medium', 'amount', 'budget:engineering', {
|
||||
value: 5000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('threshold:high', 'amount', 'budget:engineering', {
|
||||
value: 25000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up user spending limits based on role
|
||||
arbiter.addRelation('user:alice', 'spending_limit', 'threshold:medium', {
|
||||
value: 5000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:bob', 'spending_limit', 'threshold:low', {
|
||||
value: 1000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up team budget
|
||||
arbiter.addRelation('team:engineering', 'budget', 'budget:engineering', {
|
||||
value: 100000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex authorization: user -> team -> budget -> spending limit
|
||||
arbiter.setRelationConfig('can_spend', {
|
||||
type: 'logical',
|
||||
union: {
|
||||
rules: [
|
||||
{ type: 'direct' },
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'budget', direction: 'out' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'spending_limit',
|
||||
rightRelation: 'amount',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.1, maxAge: 86400000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test Alice (medium limit) trying to spend $3000
|
||||
arbiter.addRelation('user:alice', 'can_spend', 'budget:engineering', {
|
||||
value: 3000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
const result = arbiter.authChecker.check('user:alice', 'can_spend', 'budget:engineering');
|
||||
|
||||
assert.ok(result.possibility > 0.8, `Expected high possibility for Alice's spending, got ${result.possibility}`);
|
||||
assert.strictEqual(result.reason, 'logical_operator_evaluation');
|
||||
});
|
||||
|
||||
it('handles multi-level approval with reputation and risk scoring', () => {
|
||||
// Set up user reputation scores
|
||||
arbiter.addRelation('user:alice', 'reputation', 'user:alice', {
|
||||
value: 0.9,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:bob', 'reputation', 'user:bob', {
|
||||
value: 0.6,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up risk thresholds
|
||||
arbiter.addRelation('threshold:risk_low', 'risk_score', 'budget:engineering', {
|
||||
value: 0.3,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('threshold:risk_high', 'risk_score', 'budget:engineering', {
|
||||
value: 0.7,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex approval: reputation + risk + team membership
|
||||
arbiter.setRelationConfig('can_approve', {
|
||||
type: 'logical',
|
||||
intersection: {
|
||||
rules: [
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'can_approve', direction: 'out' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'reputation',
|
||||
rightRelation: 'risk_score',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.05, maxAge: 3600000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test approval authorization
|
||||
const result = arbiter.authChecker.check('user:alice', 'can_approve', 'budget:engineering');
|
||||
|
||||
assert.ok(result.possibility > 0.5, `Expected reasonable possibility for approval, got ${result.possibility}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Project Access with Security Clearance', () => {
|
||||
it('handles classified project access with clearance levels and team membership', () => {
|
||||
// Set up security clearances
|
||||
arbiter.addRelation('user:alice', 'clearance', 'user:alice', {
|
||||
value: 0.9,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:bob', 'clearance', 'user:bob', {
|
||||
value: 0.4,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up project security levels
|
||||
arbiter.addRelation('project:classified', 'security_level', 'project:classified', {
|
||||
value: 0.8,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up team access to projects
|
||||
arbiter.addRelation('team:engineering', 'can_access', 'project:classified', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex access: clearance + team membership + project security
|
||||
arbiter.setRelationConfig('can_access_project', {
|
||||
type: 'logical',
|
||||
intersection: {
|
||||
rules: [
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'can_access', direction: 'out' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'clearance',
|
||||
rightRelation: 'security_level',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.02, maxAge: 7200000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test Alice (high clearance) accessing classified project
|
||||
const aliceResult = arbiter.authChecker.check('user:alice', 'can_access_project', 'project:classified');
|
||||
assert.ok(aliceResult.possibility > 0.8, `Expected high possibility for Alice, got ${aliceResult.possibility}`);
|
||||
|
||||
// Test Bob (low clearance) accessing classified project
|
||||
const bobResult = arbiter.authChecker.check('user:bob', 'can_access_project', 'project:classified');
|
||||
assert.ok(bobResult.possibility < 0.5, `Expected low possibility for Bob, got ${bobResult.possibility}`);
|
||||
});
|
||||
|
||||
it('handles document access with similarity and freshness requirements', () => {
|
||||
// Set up document similarity scores
|
||||
arbiter.addRelation('doc:confidential', 'similarity', 'doc:secret', {
|
||||
value: 0.8,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up document freshness
|
||||
arbiter.addRelation('doc:confidential', 'freshness', 'doc:confidential', {
|
||||
value: 0.9,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now() - 3600000 // 1 hour ago
|
||||
});
|
||||
|
||||
// Set up user document access
|
||||
arbiter.addRelation('user:alice', 'can_read', 'doc:confidential', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex document access: similarity + freshness + direct access
|
||||
arbiter.setRelationConfig('can_access_document', {
|
||||
type: 'logical',
|
||||
union: {
|
||||
rules: [
|
||||
{ type: 'direct' },
|
||||
{
|
||||
type: 'similarity',
|
||||
threshold: 0.7,
|
||||
relation: 'similarity'
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'freshness',
|
||||
rightRelation: 'freshness',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.1, maxAge: 1800000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const result = arbiter.authChecker.check('user:alice', 'can_access_document', 'doc:secret');
|
||||
|
||||
assert.ok(result.possibility > 0.5, `Expected reasonable possibility for document access, got ${result.possibility}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Organizational Hierarchy with Delegation', () => {
|
||||
it('handles complex delegation chains with approval workflows', () => {
|
||||
// Set up organizational hierarchy
|
||||
arbiter.addRelation('user:eve', 'reports_to', 'user:alice', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:alice', 'reports_to', 'user:charlie', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up delegation permissions
|
||||
arbiter.addRelation('user:alice', 'can_delegate', 'user:eve', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up approval authority
|
||||
arbiter.addRelation('user:charlie', 'can_approve', 'budget:engineering', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex delegation: hierarchy + delegation + approval
|
||||
arbiter.setRelationConfig('can_approve_via_delegation', {
|
||||
type: 'logical',
|
||||
union: {
|
||||
rules: [
|
||||
{ type: 'direct' },
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'reports_to', direction: 'out' },
|
||||
{ relation: 'can_approve', direction: 'out' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'can_delegate', direction: 'out' },
|
||||
{ relation: 'can_approve_via_delegation', direction: 'out' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test delegation chain: Eve -> Alice -> Charlie
|
||||
const result = arbiter.authChecker.check('user:eve', 'can_approve_via_delegation', 'budget:engineering');
|
||||
|
||||
assert.ok(result.possibility > 0.7, `Expected high possibility for delegation chain, got ${result.possibility}`);
|
||||
});
|
||||
|
||||
it('handles team-based resource access with multi-hop traversal', () => {
|
||||
// Set up resource access through teams
|
||||
arbiter.addRelation('team:engineering', 'can_access', 'server:prod', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('team:engineering', 'can_access', 'database:main', {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up cross-team access
|
||||
arbiter.addRelation('team:marketing', 'can_access', 'team:engineering', {
|
||||
value: 0.5,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure multi-hop access: user -> team -> team -> resource
|
||||
arbiter.setRelationConfig('can_access_resource', {
|
||||
type: 'multihop',
|
||||
maxHops: 3,
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'can_access', direction: 'out' },
|
||||
{ relation: 'can_access', direction: 'out' }
|
||||
]
|
||||
});
|
||||
|
||||
// Test direct team access
|
||||
const directResult = arbiter.authChecker.check('user:alice', 'can_access_resource', 'server:prod');
|
||||
assert.ok(directResult.possibility > 0.8, `Expected high possibility for direct access, got ${directResult.possibility}`);
|
||||
|
||||
// Test cross-team access
|
||||
const crossTeamResult = arbiter.authChecker.check('user:charlie', 'can_access_resource', 'server:prod');
|
||||
assert.ok(crossTeamResult.possibility > 0.4, `Expected moderate possibility for cross-team access, got ${crossTeamResult.possibility}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complex Value-Based Authorization', () => {
|
||||
it('handles financial transactions with balance, reputation, and risk scoring', () => {
|
||||
// Set up user balances
|
||||
arbiter.addRelation('user:alice', 'balance', 'account:corporate', {
|
||||
value: 50000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:bob', 'balance', 'account:corporate', {
|
||||
value: 5000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up transaction amounts
|
||||
arbiter.addRelation('transaction:large', 'amount', 'account:corporate', {
|
||||
value: 10000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('transaction:small', 'amount', 'account:corporate', {
|
||||
value: 1000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up risk scores
|
||||
arbiter.addRelation('user:alice', 'risk_score', 'user:alice', {
|
||||
value: 0.2,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
arbiter.addRelation('user:bob', 'risk_score', 'user:bob', {
|
||||
value: 0.8,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex transaction authorization: balance + risk + amount
|
||||
arbiter.setRelationConfig('can_transact', {
|
||||
type: 'logical',
|
||||
intersection: {
|
||||
rules: [
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'balance',
|
||||
rightRelation: 'amount',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.05, maxAge: 300000 }
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'risk_score',
|
||||
rightRelation: 'risk_score',
|
||||
operator: '<=',
|
||||
decay: { factor: 0.1, maxAge: 1800000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test Alice (high balance, low risk) with large transaction
|
||||
const aliceResult = arbiter.authChecker.check('user:alice', 'can_transact', 'transaction:large');
|
||||
assert.ok(aliceResult.possibility > 0.8, `Expected high possibility for Alice's large transaction, got ${aliceResult.possibility}`);
|
||||
|
||||
// Test Bob (low balance, high risk) with small transaction
|
||||
const bobResult = arbiter.authChecker.check('user:bob', 'can_transact', 'transaction:small');
|
||||
assert.ok(bobResult.possibility < 0.5, `Expected low possibility for Bob's transaction, got ${bobResult.possibility}`);
|
||||
});
|
||||
|
||||
it('handles resource allocation with team budgets and individual limits', () => {
|
||||
// Set up team budgets
|
||||
arbiter.addRelation('team:engineering', 'budget', 'budget:engineering', {
|
||||
value: 100000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up individual spending limits
|
||||
arbiter.addRelation('user:alice', 'spending_limit', 'budget:engineering', {
|
||||
value: 10000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Set up resource costs
|
||||
arbiter.addRelation('server:prod', 'cost', 'budget:engineering', {
|
||||
value: 5000,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
|
||||
// Configure complex resource allocation: team budget + individual limit + resource cost
|
||||
arbiter.setRelationConfig('can_allocate_resource', {
|
||||
type: 'logical',
|
||||
intersection: {
|
||||
rules: [
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'budget', direction: 'out' }
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'relational_comparator',
|
||||
leftRelation: 'spending_limit',
|
||||
rightRelation: 'cost',
|
||||
operator: '>=',
|
||||
decay: { factor: 0.1, maxAge: 3600000 }
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const result = arbiter.authChecker.check('user:alice', 'can_allocate_resource', 'server:prod');
|
||||
|
||||
assert.ok(result.possibility > 0.7, `Expected high possibility for resource allocation, got ${result.possibility}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Performance and Scalability', () => {
|
||||
it('handles large-scale authorization with performance optimization', () => {
|
||||
// Set up many users and teams
|
||||
for (let i = 0; i < 100; i++) {
|
||||
arbiter.addNode(`user:user${i}`, 'user');
|
||||
arbiter.addNode(`team:team${i}`, 'team');
|
||||
|
||||
arbiter.addRelation(`user:user${i}`, 'member_of', `team:team${i}`, {
|
||||
value: 1.0,
|
||||
possibility: 1.0,
|
||||
changed_last_at: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
// Configure performance-optimized authorization
|
||||
arbiter.setRelationConfig('can_access_large_scale', {
|
||||
type: 'logical',
|
||||
union: {
|
||||
rules: [
|
||||
{ type: 'direct' },
|
||||
{
|
||||
type: 'chain',
|
||||
steps: [
|
||||
{ relation: 'member_of', direction: 'out' },
|
||||
{ relation: 'can_access_large_scale', direction: 'out' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Test with performance options
|
||||
const result = arbiter.authChecker.check('user:user50', 'can_access_large_scale', 'team:team50', {
|
||||
fastPath: true,
|
||||
binary: true,
|
||||
trackEvaluation: true
|
||||
});
|
||||
|
||||
assert.ok(result.possibility > 0.8, `Expected high possibility for large-scale authorization, got ${result.possibility}`);
|
||||
assert.ok(result.binary, 'Expected binary mode result');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user