anon-identity
Version:
Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure
313 lines • 11.9 kB
JavaScript
"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