UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

313 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ScopeReductionStrategies = void 0; const scope_registry_1 = require("./scope-registry"); class ScopeReductionStrategies { /** * Intersection strategy - only grant scopes that both parent has and child requests */ static intersection(parentScopes, requestedScopes) { const grantedScopes = requestedScopes.filter(scope => parentScopes.includes(scope)); const deniedScopes = requestedScopes.filter(scope => !parentScopes.includes(scope)); return { grantedScopes, deniedScopes, reason: deniedScopes.length > 0 ? 'Some requested scopes not available in parent' : undefined }; } /** * Subset strategy - grant all requested scopes only if they form a subset of parent scopes */ static subset(parentScopes, requestedScopes) { const isSubset = requestedScopes.every(scope => parentScopes.includes(scope)); if (isSubset) { return { grantedScopes: requestedScopes, deniedScopes: [], reason: undefined }; } return { grantedScopes: [], deniedScopes: requestedScopes, reason: 'Requested scopes must be a complete subset of parent scopes' }; } /** * Hierarchical reduction - considers scope dependencies and implications */ static hierarchical(parentScopes, requestedScopes) { const grantedScopes = new Set(); const deniedScopes = []; const scopeHierarchy = this.buildScopeHierarchy(parentScopes); for (const requested of requestedScopes) { if (this.canGrantScope(requested, parentScopes, scopeHierarchy)) { grantedScopes.add(requested); // Also add any scopes implied by this scope const scope = this.scopeRegistry.getScope(requested); if (scope?.dependencies) { scope.dependencies.forEach(dep => { if (parentScopes.includes(dep)) { grantedScopes.add(dep); } }); } } else { deniedScopes.push(requested); } } return { grantedScopes: Array.from(grantedScopes), deniedScopes, reason: deniedScopes.length > 0 ? 'Some scopes cannot be granted based on hierarchy' : undefined, metadata: { hierarchyApplied: true, totalDependencies: grantedScopes.size - requestedScopes.filter(s => grantedScopes.has(s)).length } }; } /** * Category-based reduction - allows scopes within certain categories */ static categoryBased(parentScopes, requestedScopes, allowedCategories) { const grantedScopes = []; const deniedScopes = []; for (const requested of requestedScopes) { const scope = this.scopeRegistry.getScope(requested); if (!scope) { deniedScopes.push(requested); continue; } if (parentScopes.includes(requested) && allowedCategories.includes(scope.category)) { grantedScopes.push(requested); } else { deniedScopes.push(requested); } } return { grantedScopes, deniedScopes, reason: deniedScopes.length > 0 ? `Some scopes denied due to category restrictions (allowed: ${allowedCategories.join(', ')})` : undefined, metadata: { allowedCategories } }; } /** * Risk-based reduction - filters scopes based on risk level */ static riskBased(parentScopes, requestedScopes, maxRiskLevel) { const riskLevels = { low: 1, medium: 2, high: 3 }; const maxRisk = riskLevels[maxRiskLevel]; const grantedScopes = []; const deniedScopes = []; for (const requested of requestedScopes) { if (!parentScopes.includes(requested)) { deniedScopes.push(requested); continue; } const scope = this.scopeRegistry.getScope(requested); if (!scope) { deniedScopes.push(requested); continue; } const scopeRisk = riskLevels[scope.riskLevel]; if (scopeRisk <= maxRisk) { grantedScopes.push(requested); } else { deniedScopes.push(requested); } } return { grantedScopes, deniedScopes, reason: deniedScopes.length > 0 ? `Some scopes exceed maximum risk level (${maxRiskLevel})` : undefined, metadata: { maxRiskLevel } }; } /** * Time-based reduction - grants different scopes based on time/duration */ static timeBased(parentScopes, requestedScopes, duration // milliseconds ) { const shortTermThreshold = 60 * 60 * 1000; // 1 hour const mediumTermThreshold = 24 * 60 * 60 * 1000; // 24 hours if (duration <= shortTermThreshold) { // Short-term: allow all low-risk scopes return this.riskBased(parentScopes, requestedScopes, 'low'); } else if (duration <= mediumTermThreshold) { // Medium-term: allow low and medium risk return this.riskBased(parentScopes, requestedScopes, 'medium'); } else { // Long-term: standard intersection return this.intersection(parentScopes, requestedScopes); } } /** * Composite strategy - combines multiple strategies */ static composite(parentScopes, requestedScopes, strategies) { const results = []; for (const strategy of strategies) { let result; switch (strategy.type) { case 'intersection': result = this.intersection(parentScopes, requestedScopes); break; case 'subset': result = this.subset(parentScopes, requestedScopes); break; case 'hierarchical': result = this.hierarchical(parentScopes, requestedScopes); break; case 'category': result = this.categoryBased(parentScopes, requestedScopes, strategy.params?.allowedCategories || []); break; case 'risk': result = this.riskBased(parentScopes, requestedScopes, strategy.params?.maxRiskLevel || 'medium'); break; case 'time': result = this.timeBased(parentScopes, requestedScopes, strategy.params?.duration || Infinity); break; default: result = this.intersection(parentScopes, requestedScopes); } results.push(result); } // Combine results - take intersection of all granted scopes const finalGranted = requestedScopes.filter(scope => results.every(r => r.grantedScopes.includes(scope))); const finalDenied = requestedScopes.filter(scope => !finalGranted.includes(scope)); return { grantedScopes: finalGranted, deniedScopes: finalDenied, reason: finalDenied.length > 0 ? 'Composite strategy denied some scopes' : undefined, metadata: { strategiesApplied: strategies.length, individualResults: results } }; } /** * Creates a custom reducer that applies a specific policy */ static createCustomReducer(policy) { return (parentScopes, requestedScopes) => { let result; switch (policy.strategy) { case 'intersection': result = this.intersection(parentScopes, requestedScopes); break; case 'subset': result = this.subset(parentScopes, requestedScopes); break; case 'custom': if (policy.customReducer) { return policy.customReducer(parentScopes, requestedScopes); } result = this.intersection(parentScopes, requestedScopes); break; default: result = this.intersection(parentScopes, requestedScopes); } return result.grantedScopes; }; } // Helper methods static buildScopeHierarchy(scopes) { const hierarchy = new Map(); for (const scope of scopes) { const scopeDef = this.scopeRegistry.getScope(scope); if (!scopeDef) continue; hierarchy.set(scope, { scope, implies: scopeDef.dependencies || [], requiredBy: this.findRequiredBy(scope, scopes) }); } return hierarchy; } static findRequiredBy(targetScope, allScopes) { const requiredBy = []; for (const scope of allScopes) { const scopeDef = this.scopeRegistry.getScope(scope); if (scopeDef?.dependencies?.includes(targetScope)) { requiredBy.push(scope); } } return requiredBy; } static canGrantScope(scope, parentScopes, hierarchy) { // Direct match if (parentScopes.includes(scope)) { return true; } // Check if any parent scope implies this scope for (const parentScope of parentScopes) { const parentHierarchy = hierarchy.get(parentScope); if (parentHierarchy?.implies.includes(scope)) { return true; } } return false; } /** * Analyzes the impact of scope reduction */ static analyzeReduction(original, reduced) { const removed = original.filter(s => !reduced.includes(s)); const retained = reduced; const reductionPercentage = (removed.length / original.length) * 100; // Analyze category impact const categoryImpact = {}; const riskImpact = {}; for (const scope of original) { const scopeDef = this.scopeRegistry.getScope(scope); if (!scopeDef) continue; // Category analysis if (!categoryImpact[scopeDef.category]) { categoryImpact[scopeDef.category] = { removed: 0, retained: 0 }; } if (removed.includes(scope)) { categoryImpact[scopeDef.category].removed++; } else { categoryImpact[scopeDef.category].retained++; } // Risk analysis if (!riskImpact[scopeDef.riskLevel]) { riskImpact[scopeDef.riskLevel] = { removed: 0, retained: 0 }; } if (removed.includes(scope)) { riskImpact[scopeDef.riskLevel].removed++; } else { riskImpact[scopeDef.riskLevel].retained++; } } return { removed, retained, reductionPercentage, categoryImpact, riskImpact }; } } exports.ScopeReductionStrategies = ScopeReductionStrategies; ScopeReductionStrategies.scopeRegistry = scope_registry_1.ScopeRegistry.getInstance(); //# sourceMappingURL=scope-reduction-strategies.js.map