anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
334 lines • 13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DelegationPolicyEngine = void 0;
class DelegationPolicyEngine {
constructor(agentManager) {
this.agentManager = agentManager;
this.policies = new Map();
this.globalMaxDepth = 5;
this.defaultPolicy = this.createDefaultPolicy();
this.initializeBuiltInPolicies();
}
/**
* Sets the global maximum delegation depth
*/
setGlobalMaxDepth(depth) {
if (depth < 1 || depth > 10) {
throw new Error('Global max depth must be between 1 and 10');
}
this.globalMaxDepth = depth;
}
/**
* Registers a delegation policy
*/
registerPolicy(policy) {
// Validate policy
if (policy.maxDepth > this.globalMaxDepth) {
throw new Error(`Policy max depth (${policy.maxDepth}) exceeds global max (${this.globalMaxDepth})`);
}
this.policies.set(policy.id, policy);
}
/**
* Evaluates whether a delegation is allowed based on policies
*/
async evaluatePolicy(context, policyId) {
const violations = [];
const warnings = [];
const appliedConstraints = [];
// Get the policy to evaluate
const policy = policyId
? this.policies.get(policyId) || this.defaultPolicy
: this.findApplicablePolicy(context) || this.defaultPolicy;
if (!policy.enabled) {
return {
allowed: false,
policy,
violations: ['Policy is disabled'],
warnings,
appliedConstraints
};
}
// Check global max depth
const currentDepth = context.parentAgent.delegationDepth;
if (currentDepth >= this.globalMaxDepth) {
violations.push(`Global max delegation depth (${this.globalMaxDepth}) reached`);
}
// Check policy max depth
if (currentDepth >= policy.maxDepth) {
violations.push(`Policy max delegation depth (${policy.maxDepth}) reached`);
}
// Apply scope reduction
const parentScopes = await this.getParentScopes(context.parentAgent);
const allowedScopes = this.agentManager.reduceScopesForDelegation(parentScopes, context.requestedScopes, policy.scopeReduction);
if (allowedScopes.length === 0) {
violations.push('No scopes would be granted after reduction');
}
else if (allowedScopes.length < context.requestedScopes.length) {
warnings.push(`Scope reduction applied: ${context.requestedScopes.length - allowedScopes.length} scopes removed`);
appliedConstraints.push('scope-reduction');
}
// Apply constraints
if (policy.constraints) {
const constraintResults = this.evaluateConstraints(policy.constraints, context);
violations.push(...constraintResults.violations);
warnings.push(...constraintResults.warnings);
appliedConstraints.push(...constraintResults.applied);
}
return {
allowed: violations.length === 0,
policy,
violations,
warnings,
appliedConstraints
};
}
/**
* Creates a delegation options object based on policy
*/
createDelegationOptions(policy, parentAgent) {
const remainingDepth = Math.min(policy.maxDepth - parentAgent.delegationDepth - 1, this.globalMaxDepth - parentAgent.delegationDepth - 1);
return {
maxDepth: remainingDepth,
scopeReduction: policy.scopeReduction,
expirationPolicy: policy.expirationPolicy,
auditLevel: 'detailed'
};
}
/**
* Applies expiration policy to determine credential expiration
*/
calculateExpiration(policy, parentExpiration) {
const now = new Date();
const { strategy, fixedDuration, reductionFactor } = policy.expirationPolicy;
switch (strategy) {
case 'inherit':
return parentExpiration;
case 'fixed':
const duration = fixedDuration || 24 * 60 * 60 * 1000; // Default 24 hours
return new Date(now.getTime() + duration);
case 'reduced':
const parentRemaining = parentExpiration.getTime() - now.getTime();
const factor = reductionFactor || 0.5;
return new Date(now.getTime() + parentRemaining * factor);
default:
return parentExpiration;
}
}
/**
* Validates that a delegation request complies with all policies
*/
async validateDelegationRequest(parentAgentDID, requestedScopes, serviceDID) {
const parentAgent = this.agentManager.getAgent(parentAgentDID);
if (!parentAgent) {
return { valid: false, errors: ['Parent agent not found'] };
}
const context = {
parentAgent,
requestedScopes,
serviceDID
};
const evaluation = await this.evaluatePolicy(context);
if (!evaluation.allowed) {
return { valid: false, errors: evaluation.violations };
}
const maxDepth = Math.min(evaluation.policy?.maxDepth || this.globalMaxDepth, this.globalMaxDepth - parentAgent.delegationDepth - 1);
return { valid: true, errors: [], maxDepth };
}
// Private helper methods
createDefaultPolicy() {
return {
id: 'default',
name: 'Default Delegation Policy',
description: 'Default policy for agent delegations',
maxDepth: 3,
scopeReduction: { strategy: 'intersection' },
expirationPolicy: { strategy: 'reduced', reductionFactor: 0.8 },
enabled: true
};
}
initializeBuiltInPolicies() {
// Restrictive policy for high-security scenarios
this.registerPolicy({
id: 'high-security',
name: 'High Security Policy',
description: 'Restrictive policy for sensitive operations',
maxDepth: 1,
scopeReduction: { strategy: 'subset' },
expirationPolicy: { strategy: 'fixed', fixedDuration: 60 * 60 * 1000 }, // 1 hour
constraints: {
maxScopes: 3,
requiredScopes: ['agent:audit']
},
enabled: true
});
// Permissive policy for development
this.registerPolicy({
id: 'development',
name: 'Development Policy',
description: 'Permissive policy for development environments',
maxDepth: 5,
scopeReduction: { strategy: 'intersection' },
expirationPolicy: { strategy: 'inherit' },
enabled: true
});
// Time-restricted policy
this.registerPolicy({
id: 'business-hours',
name: 'Business Hours Policy',
description: 'Allows delegation only during business hours',
maxDepth: 2,
scopeReduction: { strategy: 'intersection' },
expirationPolicy: { strategy: 'fixed', fixedDuration: 8 * 60 * 60 * 1000 }, // 8 hours
constraints: {
timeOfDayRestrictions: [{
startHour: 9,
endHour: 17,
timezone: 'UTC',
daysOfWeek: [1, 2, 3, 4, 5] // Monday to Friday
}]
},
enabled: true
});
}
findApplicablePolicy(context) {
// For now, return the default policy
// In a full implementation, this would match based on context
return this.defaultPolicy;
}
async getParentScopes(parentAgent) {
// Get the parent's delegation credentials
const credentials = this.agentManager.getDelegationCredentials(parentAgent.did);
if (credentials.length === 0)
return [];
// Extract all unique scopes from all credentials
const allScopes = new Set();
credentials.forEach(cred => {
cred.credentialSubject.scopes.forEach(scope => allScopes.add(scope));
});
return Array.from(allScopes);
}
evaluateConstraints(constraints, context) {
const violations = [];
const warnings = [];
const applied = [];
// Check allowed services
if (constraints.allowedServices && context.serviceDID) {
if (!constraints.allowedServices.includes(context.serviceDID)) {
violations.push(`Service ${context.serviceDID} is not in allowed list`);
}
applied.push('allowed-services');
}
// Check denied services
if (constraints.deniedServices && context.serviceDID) {
if (constraints.deniedServices.includes(context.serviceDID)) {
violations.push(`Service ${context.serviceDID} is explicitly denied`);
}
applied.push('denied-services');
}
// Check max scopes
if (constraints.maxScopes) {
if (context.requestedScopes.length > constraints.maxScopes) {
violations.push(`Requested scopes (${context.requestedScopes.length}) exceed maximum (${constraints.maxScopes})`);
}
applied.push('max-scopes');
}
// Check required scopes
if (constraints.requiredScopes) {
const missingRequired = constraints.requiredScopes.filter(req => !context.requestedScopes.includes(req));
if (missingRequired.length > 0) {
violations.push(`Missing required scopes: ${missingRequired.join(', ')}`);
}
applied.push('required-scopes');
}
// Check time restrictions
if (constraints.timeOfDayRestrictions) {
const now = new Date();
const allowed = constraints.timeOfDayRestrictions.some(restriction => {
return this.isTimeAllowed(now, restriction);
});
if (!allowed) {
violations.push('Current time is outside allowed delegation hours');
}
applied.push('time-restrictions');
}
// Check custom constraints
if (constraints.customConstraints) {
constraints.customConstraints.forEach((constraint, index) => {
try {
if (!constraint(context)) {
violations.push(`Custom constraint ${index + 1} failed`);
}
applied.push(`custom-constraint-${index + 1}`);
}
catch (error) {
warnings.push(`Custom constraint ${index + 1} threw error: ${error}`);
}
});
}
return { violations, warnings, applied };
}
isTimeAllowed(date, restriction) {
// Convert to timezone (simplified - in production would use proper timezone library)
const hour = date.getUTCHours();
const dayOfWeek = date.getUTCDay();
// Check day of week
if (restriction.daysOfWeek && !restriction.daysOfWeek.includes(dayOfWeek)) {
return false;
}
// Check hour range
return hour >= restriction.startHour && hour < restriction.endHour;
}
/**
* Exports all registered policies
*/
exportPolicies() {
return Array.from(this.policies.values());
}
/**
* Imports policies from an array
*/
importPolicies(policies) {
policies.forEach(policy => {
try {
this.registerPolicy(policy);
}
catch (error) {
// Log error but continue importing other policies
console.error(`Failed to import policy ${policy.id}:`, error);
}
});
}
/**
* Gets a specific policy by ID
*/
getPolicy(policyId) {
return this.policies.get(policyId);
}
/**
* Updates an existing policy
*/
updatePolicy(policyId, updates) {
const existing = this.policies.get(policyId);
if (!existing) {
throw new Error(`Policy ${policyId} not found`);
}
const updated = { ...existing, ...updates };
// Validate updated policy
if (updated.maxDepth > this.globalMaxDepth) {
throw new Error(`Updated max depth exceeds global maximum`);
}
this.policies.set(policyId, updated);
}
/**
* Enables or disables a policy
*/
setPolicyEnabled(policyId, enabled) {
const policy = this.policies.get(policyId);
if (!policy) {
throw new Error(`Policy ${policyId} not found`);
}
policy.enabled = enabled;
}
}
exports.DelegationPolicyEngine = DelegationPolicyEngine;
//# sourceMappingURL=delegation-policy-engine.js.map