@noony-serverless/core
Version:
A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript
425 lines • 14.8 kB
JavaScript
"use strict";
/**
* Permission Registry
*
* Central registry for managing available permissions in the system.
* Supports wildcard pattern expansion, permission discovery, and
* category-based organization for efficient permission management.
*
* Key Features:
* - Permission registration and discovery
* - Wildcard pattern expansion ("admin.*" -> ["admin.users", "admin.roles"])
* - Category-based organization
* - Thread-safe operations with caching
* - Auto-discovery from codebase annotations
* - Permission hierarchy validation
*
* @author Noony Framework Team
* @version 1.0.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PermissionRegistryFactory = exports.DefaultPermissionRegistry = void 0;
/**
* Default permission registry implementation
*
* Thread-safe in-memory registry with caching for pattern matching.
* In production, this could be backed by a database or external service.
*/
class DefaultPermissionRegistry {
permissions = new Map();
categoryIndex = new Map();
patternCache = new Map();
// Performance tracking
stats = {
totalLookups: 0,
patternMatchingTime: 0,
cacheHits: 0,
cacheMisses: 0,
};
constructor() {
// Initialize with common system permissions
this.initializeSystemPermissions();
}
/**
* Register a permission with metadata
*/
registerPermission(metadata) {
// Validate permission format
if (!this.isValidPermissionFormat(metadata.permission)) {
throw new Error(`Invalid permission format: ${metadata.permission}`);
}
// Check for duplicates
if (this.permissions.has(metadata.permission)) {
console.warn(`Permission ${metadata.permission} is already registered`);
return;
}
// Extract category from permission if not provided
if (!metadata.category) {
metadata.category = this.extractCategory(metadata.permission);
}
// Store permission
this.permissions.set(metadata.permission, {
...metadata,
registeredAt: new Date(),
});
// Update category index
if (!this.categoryIndex.has(metadata.category)) {
this.categoryIndex.set(metadata.category, new Set());
}
this.categoryIndex.get(metadata.category).add(metadata.permission);
// Invalidate pattern cache since new permission might affect wildcard matches
this.patternCache.clear();
console.debug(`Registered permission: ${metadata.permission} (${metadata.category})`);
}
/**
* Register multiple permissions at once
*/
registerPermissions(permissions) {
for (const permission of permissions) {
this.registerPermission(permission);
}
}
/**
* Get all permissions matching a wildcard pattern
*/
getMatchingPermissions(wildcardPattern) {
const startTime = process.hrtime.bigint();
this.stats.totalLookups++;
try {
// Check cache first
if (this.patternCache.has(wildcardPattern)) {
this.stats.cacheHits++;
return this.patternCache.get(wildcardPattern);
}
this.stats.cacheMisses++;
// Convert wildcard pattern to regex
const regex = this.wildcardToRegex(wildcardPattern);
const matchingPermissions = [];
// Find all matching permissions
for (const permission of this.permissions.keys()) {
if (regex.test(permission)) {
matchingPermissions.push(permission);
}
}
// Cache the result
this.patternCache.set(wildcardPattern, matchingPermissions);
return matchingPermissions;
}
finally {
const endTime = process.hrtime.bigint();
this.stats.patternMatchingTime += Number(endTime - startTime) / 1000; // microseconds
}
}
/**
* Get all permissions in a category
*/
getCategoryPermissions(category) {
const permissions = this.categoryIndex.get(category);
return permissions ? Array.from(permissions) : [];
}
/**
* Check if a permission exists in the registry
*/
hasPermission(permission) {
return this.permissions.has(permission);
}
/**
* Get permission metadata
*/
getPermissionMetadata(permission) {
return this.permissions.get(permission) || null;
}
/**
* Get all registered permissions
*/
getAllPermissions() {
return Array.from(this.permissions.keys());
}
/**
* Get all categories
*/
getAllCategories() {
return Array.from(this.categoryIndex.keys());
}
/**
* Get registry statistics
*/
getStats() {
const permissionsByCategory = {};
const riskLevelDistribution = {};
const registrationTimeline = [];
for (const [category, permissions] of this.categoryIndex) {
permissionsByCategory[category] = permissions.size;
}
for (const metadata of this.permissions.values()) {
riskLevelDistribution[metadata.riskLevel] =
(riskLevelDistribution[metadata.riskLevel] || 0) + 1;
registrationTimeline.push(metadata.registeredAt);
}
return {
totalPermissions: this.permissions.size,
totalCategories: this.categoryIndex.size,
permissionsByCategory,
riskLevelDistribution,
registrationTimeline: registrationTimeline.sort((a, b) => a.getTime() - b.getTime()),
};
}
/**
* Get performance statistics
*/
getPerformanceStats() {
const totalCacheRequests = this.stats.cacheHits + this.stats.cacheMisses;
return {
totalLookups: this.stats.totalLookups,
averagePatternMatchingTimeUs: this.stats.totalLookups > 0
? this.stats.patternMatchingTime / this.stats.totalLookups
: 0,
cacheHitRate: totalCacheRequests > 0
? (this.stats.cacheHits / totalCacheRequests) * 100
: 0,
cacheSize: this.patternCache.size,
};
}
/**
* Clear the pattern cache
*/
clearCache() {
this.patternCache.clear();
}
/**
* Initialize common system permissions
*/
initializeSystemPermissions() {
const now = new Date();
const systemPermissions = [
// User management permissions
{
permission: 'user.create',
description: 'Create new user accounts',
category: 'user',
action: 'create',
riskLevel: 'medium',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'user.read',
description: 'View user information',
category: 'user',
action: 'read',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'user.update',
description: 'Update user information',
category: 'user',
action: 'update',
riskLevel: 'medium',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'user.delete',
description: 'Delete user accounts',
category: 'user',
action: 'delete',
riskLevel: 'high',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'user.list',
description: 'List users with filtering',
category: 'user',
action: 'list',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
// Admin permissions
{
permission: 'admin.users',
description: 'Full administrative access to user management',
category: 'admin',
subCategory: 'users',
riskLevel: 'critical',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'admin.system',
description: 'System-level administrative access',
category: 'admin',
subCategory: 'system',
riskLevel: 'critical',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'admin.monitoring',
description: 'Access to monitoring and metrics',
category: 'admin',
subCategory: 'monitoring',
riskLevel: 'medium',
requiresValidation: false,
registeredAt: now,
},
// Organization permissions
{
permission: 'organization.view',
description: 'View organization information',
category: 'organization',
action: 'view',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'organization.manage',
description: 'Manage organization settings',
category: 'organization',
action: 'manage',
riskLevel: 'high',
requiresValidation: true,
registeredAt: now,
},
// Situation report permissions
{
permission: 'situation.reports.create',
description: 'Create situation reports',
category: 'situation',
subCategory: 'reports',
action: 'create',
riskLevel: 'medium',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'situation.reports.view',
description: 'View situation reports',
category: 'situation',
subCategory: 'reports',
action: 'view',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'situation.reports.update',
description: 'Update situation reports',
category: 'situation',
subCategory: 'reports',
action: 'update',
riskLevel: 'medium',
requiresValidation: true,
registeredAt: now,
},
{
permission: 'situation.reports.delete',
description: 'Delete situation reports',
category: 'situation',
subCategory: 'reports',
action: 'delete',
riskLevel: 'high',
requiresValidation: true,
registeredAt: now,
},
// System permissions
{
permission: 'system.health',
description: 'Access system health information',
category: 'system',
action: 'health',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'system.metrics',
description: 'Access system metrics',
category: 'system',
action: 'metrics',
riskLevel: 'low',
requiresValidation: false,
registeredAt: now,
},
{
permission: 'system.logs',
description: 'Access system logs',
category: 'system',
action: 'logs',
riskLevel: 'medium',
requiresValidation: true,
registeredAt: now,
},
];
this.registerPermissions(systemPermissions);
}
/**
* Validate permission format (2-3 levels with alphanumeric + dots)
*/
isValidPermissionFormat(permission) {
if (!permission || typeof permission !== 'string') {
return false;
}
// Allow both concrete permissions and wildcard patterns
const validPattern = /^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+){1,2}$/;
const wildcardPattern = /^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.\*$/;
return validPattern.test(permission) || wildcardPattern.test(permission);
}
/**
* Extract category from permission string
*/
extractCategory(permission) {
const parts = permission.split('.');
return parts[0] || 'unknown';
}
/**
* Convert wildcard pattern to regex
*/
wildcardToRegex(wildcardPattern) {
if (!wildcardPattern.includes('*')) {
// Exact match for non-wildcard patterns
return new RegExp(`^${wildcardPattern.replace(/\./g, '\\.')}$`);
}
// Convert wildcard pattern to regex
// "admin.*" becomes /^admin\..*$/
// "admin.users.*" becomes /^admin\.users\..*$/
const regexPattern = wildcardPattern
.replace(/\./g, '\\.') // Escape dots
.replace(/\*/g, '.*'); // Replace * with any characters
return new RegExp(`^${regexPattern}$`);
}
}
exports.DefaultPermissionRegistry = DefaultPermissionRegistry;
/**
* Factory for creating permission registries
*/
class PermissionRegistryFactory {
/**
* Create a default permission registry with system permissions
*/
static createDefault() {
return new DefaultPermissionRegistry();
}
/**
* Create an empty permission registry
*/
static createEmpty() {
const registry = new DefaultPermissionRegistry();
// Clear system permissions if needed for testing
return registry;
}
/**
* Create a registry from a permission definition file
*/
static createFromDefinitions(definitions) {
const registry = new DefaultPermissionRegistry();
registry.registerPermissions(definitions);
return registry;
}
}
exports.PermissionRegistryFactory = PermissionRegistryFactory;
//# sourceMappingURL=PermissionRegistry.js.map