@ahmedhegazee/nestjs-telescope
Version:
Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling
713 lines • 26.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var EnterpriseSecurityService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnterpriseSecurityService = void 0;
const common_1 = require("@nestjs/common");
const rxjs_1 = require("rxjs");
const common_2 = require("@nestjs/common");
const crypto = __importStar(require("crypto"));
const jwt = __importStar(require("jsonwebtoken"));
let EnterpriseSecurityService = EnterpriseSecurityService_1 = class EnterpriseSecurityService {
constructor(telescopeConfig) {
this.telescopeConfig = telescopeConfig;
this.logger = new common_1.Logger(EnterpriseSecurityService_1.name);
this.users = new Map();
this.policies = new Map();
this.auditEvents = [];
this.encryptionKeys = new Map();
this.auditSubject = new rxjs_1.Subject();
this.authSubject = new rxjs_1.Subject();
this.complianceSubject = new rxjs_1.Subject();
this.keyRotationInterval = null;
this.config =
this.telescopeConfig.enterpriseSecurity ||
this.getDefaultSecurityConfig();
}
async onModuleInit() {
if (!this.config.enabled) {
this.logger.log('Enterprise security disabled');
return;
}
await this.initializeSecurity();
this.startKeyRotation();
this.logger.log('Enterprise security service initialized');
}
getDefaultSecurityConfig() {
return {
enabled: true,
authentication: {
enabled: true,
methods: ['jwt'],
jwt: {
secret: process.env.JWT_SECRET || 'your-secret-key',
expiresIn: '1h',
refreshExpiresIn: '7d',
},
oauth2: {
providers: {
google: undefined,
github: undefined,
azure: undefined,
okta: undefined,
},
},
saml: {
enabled: false,
entryPoint: '',
issuer: '',
cert: '',
},
ldap: {
enabled: false,
url: '',
bindDN: '',
bindCredentials: '',
searchBase: '',
searchFilter: '',
},
},
authorization: {
enabled: true,
rbac: true,
abac: true,
policies: [],
},
encryption: {
enabled: true,
algorithm: 'aes-256-gcm',
keyRotation: true,
keyRotationInterval: 30,
},
audit: {
enabled: true,
logLevel: 'detailed',
retention: 90,
compliance: ['gdpr', 'sox'],
},
compliance: {
gdpr: {
enabled: true,
dataRetention: 2555,
rightToBeForgotten: true,
dataPortability: true,
},
sox: {
enabled: true,
auditTrail: true,
accessControls: true,
},
hipaa: {
enabled: false,
phiProtection: false,
accessLogging: false,
},
pci: {
enabled: false,
cardDataEncryption: false,
tokenization: false,
},
},
};
}
async initializeSecurity() {
await this.initializeEncryptionKeys();
await this.initializeDefaultPolicies();
await this.initializeDefaultUsers();
if (this.config.audit.enabled) {
await this.initializeComplianceMonitoring();
}
}
async initializeEncryptionKeys() {
if (!this.config.encryption.enabled)
return;
const masterKey = crypto.randomBytes(32);
this.encryptionKeys.set('master', {
key: masterKey,
createdAt: new Date(),
});
this.logger.log('Encryption keys initialized');
}
async initializeDefaultPolicies() {
if (!this.config.authorization.enabled)
return;
const defaultPolicies = [
{
id: 'admin-full-access',
name: 'Administrator Full Access',
description: 'Full access for administrators',
type: 'allow',
resources: ['*'],
actions: ['*'],
conditions: [{ field: 'roles', operator: 'contains', value: 'admin' }],
priority: 100,
},
{
id: 'user-read-only',
name: 'User Read Only',
description: 'Read-only access for regular users',
type: 'allow',
resources: ['telescope:read', 'telescope:metrics'],
actions: ['read', 'view'],
conditions: [{ field: 'roles', operator: 'contains', value: 'user' }],
priority: 50,
},
{
id: 'deny-sensitive-data',
name: 'Deny Sensitive Data Access',
description: 'Deny access to sensitive data for non-admin users',
type: 'deny',
resources: ['telescope:admin', 'telescope:security'],
actions: ['*'],
conditions: [{ field: 'roles', operator: 'not_in', value: ['admin'] }],
priority: 75,
},
];
for (const policy of defaultPolicies) {
this.policies.set(policy.id, policy);
}
this.logger.log('Default security policies initialized');
}
async initializeDefaultUsers() {
const defaultUsers = [
{
id: 'admin-1',
username: 'admin',
email: 'admin@telescope.com',
firstName: 'System',
lastName: 'Administrator',
roles: ['admin'],
permissions: ['*'],
groups: ['administrators'],
lastLogin: new Date(),
isActive: true,
metadata: {},
},
{
id: 'user-1',
username: 'user',
email: 'user@telescope.com',
firstName: 'Regular',
lastName: 'User',
roles: ['user'],
permissions: ['telescope:read', 'telescope:metrics'],
groups: ['users'],
lastLogin: new Date(),
isActive: true,
metadata: {},
},
];
for (const user of defaultUsers) {
this.users.set(user.id, user);
}
this.logger.log('Default users initialized');
}
async initializeComplianceMonitoring() {
if (this.config.compliance.gdpr.enabled) {
this.logger.log('GDPR compliance monitoring initialized');
}
if (this.config.compliance.sox.enabled) {
this.logger.log('SOX compliance monitoring initialized');
}
if (this.config.compliance.hipaa.enabled) {
this.logger.log('HIPAA compliance monitoring initialized');
}
if (this.config.compliance.pci.enabled) {
this.logger.log('PCI compliance monitoring initialized');
}
}
startKeyRotation() {
if (!this.config.encryption.keyRotation)
return;
this.keyRotationInterval = (0, rxjs_1.interval)(this.config.encryption.keyRotationInterval * 24 * 60 * 60 * 1000).subscribe(async () => {
await this.rotateEncryptionKeys();
});
}
async authenticate(credentials) {
const startTime = Date.now();
try {
let result;
switch (credentials.method) {
case 'jwt':
result = await this.authenticateJwt(credentials.token);
break;
case 'oauth2':
result = await this.authenticateOAuth2(credentials.code);
break;
case 'saml':
result = await this.authenticateSaml(credentials.token);
break;
case 'ldap':
result = await this.authenticateLdap(credentials.username, credentials.password);
break;
default:
result = {
success: false,
error: 'Unsupported authentication method',
method: credentials.method,
};
}
await this.logAuditEvent({
userId: result.user?.id || 'unknown',
action: 'authentication',
resource: 'auth',
result: result.success ? 'success' : 'failure',
ipAddress: 'unknown',
userAgent: 'unknown',
metadata: {
method: credentials.method,
duration: Date.now() - startTime,
},
});
this.authSubject.next(result);
return result;
}
catch (error) {
const result = {
success: false,
error: error.message,
method: credentials.method,
};
await this.logAuditEvent({
userId: 'unknown',
action: 'authentication',
resource: 'auth',
result: 'failure',
ipAddress: 'unknown',
userAgent: 'unknown',
metadata: { method: credentials.method, error: error.message },
});
this.authSubject.next(result);
return result;
}
}
async authenticateJwt(token) {
try {
const decoded = jwt.verify(token, this.config.authentication.jwt.secret);
const user = this.users.get(decoded.userId);
if (!user || !user.isActive) {
return {
success: false,
error: 'Invalid or inactive user',
method: 'jwt',
};
}
const newToken = jwt.sign({ userId: user.id, roles: user.roles }, this.config.authentication.jwt.secret, { expiresIn: this.config.authentication.jwt.expiresIn });
return {
success: true,
user,
token: newToken,
expiresAt: new Date(Date.now() + 60 * 60 * 1000),
method: 'jwt',
};
}
catch (error) {
return {
success: false,
error: 'Invalid JWT token',
method: 'jwt',
};
}
}
async authenticateOAuth2(code) {
return {
success: false,
error: 'OAuth2 authentication not implemented',
method: 'oauth2',
};
}
async authenticateSaml(token) {
return {
success: false,
error: 'SAML authentication not implemented',
method: 'saml',
};
}
async authenticateLdap(username, password) {
return {
success: false,
error: 'LDAP authentication not implemented',
method: 'ldap',
};
}
async authorize(userId, action, resource, context = {}) {
const user = this.users.get(userId);
if (!user || !user.isActive) {
return {
allowed: false,
reason: 'User not found or inactive',
policies: [],
conditions: [],
};
}
const applicablePolicies = this.getApplicablePolicies(user, action, resource, context);
const allowPolicies = applicablePolicies.filter((p) => p.type === 'allow');
const denyPolicies = applicablePolicies.filter((p) => p.type === 'deny');
for (const policy of denyPolicies) {
if (this.evaluatePolicy(policy, user, context)) {
await this.logAuditEvent({
userId,
action,
resource,
result: 'denied',
ipAddress: context.ipAddress || 'unknown',
userAgent: context.userAgent || 'unknown',
metadata: { policy: policy.id, reason: 'Policy denied access' },
});
return {
allowed: false,
reason: `Access denied by policy: ${policy.name}`,
policies: [policy.id],
conditions: policy.conditions,
};
}
}
for (const policy of allowPolicies) {
if (this.evaluatePolicy(policy, user, context)) {
await this.logAuditEvent({
userId,
action,
resource,
result: 'success',
ipAddress: context.ipAddress || 'unknown',
userAgent: context.userAgent || 'unknown',
metadata: { policy: policy.id },
});
return {
allowed: true,
policies: [policy.id],
conditions: policy.conditions,
};
}
}
await this.logAuditEvent({
userId,
action,
resource,
result: 'denied',
ipAddress: context.ipAddress || 'unknown',
userAgent: context.userAgent || 'unknown',
metadata: { reason: 'No applicable allow policy' },
});
return {
allowed: false,
reason: 'No applicable allow policy found',
policies: [],
conditions: [],
};
}
getApplicablePolicies(user, action, resource, context) {
return Array.from(this.policies.values())
.filter((policy) => {
const resourceMatch = policy.resources.includes('*') ||
policy.resources.includes(resource) ||
policy.resources.some((r) => resource.startsWith(r));
const actionMatch = policy.actions.includes('*') || policy.actions.includes(action);
return resourceMatch && actionMatch;
})
.sort((a, b) => b.priority - a.priority);
}
evaluatePolicy(policy, user, context) {
for (const condition of policy.conditions) {
if (!this.evaluateCondition(condition, user, context)) {
return false;
}
}
return true;
}
evaluateCondition(condition, user, context) {
let fieldValue;
if (condition.field === 'roles') {
fieldValue = user.roles;
}
else if (condition.field === 'permissions') {
fieldValue = user.permissions;
}
else if (condition.field === 'groups') {
fieldValue = user.groups;
}
else if (condition.field === 'tenantId') {
fieldValue = user.tenantId;
}
else {
fieldValue = context[condition.field];
}
switch (condition.operator) {
case 'equals':
return fieldValue === condition.value;
case 'not_equals':
return fieldValue !== condition.value;
case 'contains':
return Array.isArray(fieldValue)
? fieldValue.includes(condition.value)
: fieldValue?.includes(condition.value);
case 'regex':
return new RegExp(condition.value).test(fieldValue);
case 'in':
return Array.isArray(condition.value) ? condition.value.includes(fieldValue) : false;
case 'not_in':
return Array.isArray(condition.value) ? !condition.value.includes(fieldValue) : true;
default:
return false;
}
}
async encrypt(data, keyId = 'master') {
if (!this.config.encryption.enabled) {
return data;
}
const keyData = this.encryptionKeys.get(keyId);
if (!keyData) {
throw new Error(`Encryption key not found: ${keyId}`);
}
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher(this.config.encryption.algorithm, keyData.key);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return `${keyId}:${iv.toString('hex')}:${encrypted}`;
}
async decrypt(encryptedData) {
if (!this.config.encryption.enabled) {
return encryptedData;
}
const [keyId, ivHex, encrypted] = encryptedData.split(':');
const keyData = this.encryptionKeys.get(keyId);
if (!keyData) {
throw new Error(`Encryption key not found: ${keyId}`);
}
const iv = Buffer.from(ivHex, 'hex');
const decipher = crypto.createDecipher(this.config.encryption.algorithm, keyData.key);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
async rotateEncryptionKeys() {
this.logger.log('Rotating encryption keys');
const newKey = crypto.randomBytes(32);
this.encryptionKeys.set('master', {
key: newKey,
createdAt: new Date(),
});
this.logger.log('Encryption keys rotated successfully');
}
async logAuditEvent(event) {
if (!this.config.audit.enabled)
return;
const auditEvent = {
id: crypto.randomUUID(),
timestamp: new Date(),
...event,
compliance: {
gdpr: this.config.compliance.gdpr.enabled,
sox: this.config.compliance.sox.enabled,
hipaa: this.config.compliance.hipaa.enabled,
pci: this.config.compliance.pci.enabled,
},
};
this.auditEvents.push(auditEvent);
const cutoffDate = new Date(Date.now() - this.config.audit.retention * 24 * 60 * 60 * 1000);
const recentEvents = this.auditEvents.filter((event) => event.timestamp > cutoffDate);
this.auditEvents.length = 0;
this.auditEvents.push(...recentEvents);
this.auditSubject.next(auditEvent);
}
async generateComplianceReport() {
const report = {
gdpr: {
compliant: true,
issues: [],
dataRetention: this.config.compliance.gdpr.dataRetention,
dataSubjects: this.users.size,
},
sox: {
compliant: true,
issues: [],
auditTrail: this.config.compliance.sox.auditTrail,
accessControls: this.config.compliance.sox.accessControls,
},
hipaa: {
compliant: true,
issues: [],
phiProtected: this.config.compliance.hipaa.phiProtection,
accessLogged: this.config.compliance.hipaa.accessLogging,
},
pci: {
compliant: true,
issues: [],
cardDataEncrypted: this.config.compliance.pci.cardDataEncryption,
tokenized: this.config.compliance.pci.tokenization,
},
};
if (this.config.compliance.gdpr.enabled) {
const gdprIssues = await this.checkGDPRCompliance();
report.gdpr.issues = gdprIssues;
report.gdpr.compliant = gdprIssues.length === 0;
}
if (this.config.compliance.sox.enabled) {
const soxIssues = await this.checkSOXCompliance();
report.sox.issues = soxIssues;
report.sox.compliant = soxIssues.length === 0;
}
if (this.config.compliance.hipaa.enabled) {
const hipaaIssues = await this.checkHIPAACompliance();
report.hipaa.issues = hipaaIssues;
report.hipaa.compliant = hipaaIssues.length === 0;
}
if (this.config.compliance.pci.enabled) {
const pciIssues = await this.checkPCICompliance();
report.pci.issues = pciIssues;
report.pci.compliant = pciIssues.length === 0;
}
this.complianceSubject.next(report);
return report;
}
async checkGDPRCompliance() {
const issues = [];
if (this.config.compliance.gdpr.dataRetention > 2555) {
issues.push('Data retention period exceeds GDPR requirements');
}
if (!this.config.compliance.gdpr.rightToBeForgotten) {
issues.push('Right to be forgotten not implemented');
}
if (!this.config.compliance.gdpr.dataPortability) {
issues.push('Data portability not implemented');
}
return issues;
}
async checkSOXCompliance() {
const issues = [];
if (!this.config.compliance.sox.auditTrail) {
issues.push('Audit trail not enabled');
}
if (!this.config.compliance.sox.accessControls) {
issues.push('Access controls not properly configured');
}
return issues;
}
async checkHIPAACompliance() {
const issues = [];
if (!this.config.compliance.hipaa.phiProtection) {
issues.push('PHI protection not enabled');
}
if (!this.config.compliance.hipaa.accessLogging) {
issues.push('Access logging not enabled');
}
return issues;
}
async checkPCICompliance() {
const issues = [];
if (!this.config.compliance.pci.cardDataEncryption) {
issues.push('Card data encryption not enabled');
}
if (!this.config.compliance.pci.tokenization) {
issues.push('Tokenization not enabled');
}
return issues;
}
getUsers() {
return Array.from(this.users.values());
}
getUserById(userId) {
return this.users.get(userId);
}
async createUser(userData) {
const user = {
...userData,
id: crypto.randomUUID(),
lastLogin: new Date(),
};
this.users.set(user.id, user);
return user;
}
async updateUser(userId, updates) {
const user = this.users.get(userId);
if (!user)
return null;
const updatedUser = { ...user, ...updates };
this.users.set(userId, updatedUser);
return updatedUser;
}
async deleteUser(userId) {
return this.users.delete(userId);
}
getPolicies() {
return Array.from(this.policies.values());
}
async createPolicy(policy) {
const newPolicy = {
...policy,
id: crypto.randomUUID(),
};
this.policies.set(newPolicy.id, newPolicy);
return newPolicy;
}
async updatePolicy(policyId, updates) {
const policy = this.policies.get(policyId);
if (!policy)
return null;
const updatedPolicy = { ...policy, ...updates };
this.policies.set(policyId, updatedPolicy);
return updatedPolicy;
}
async deletePolicy(policyId) {
return this.policies.delete(policyId);
}
getAuditEvents() {
return [...this.auditEvents];
}
getAuthenticationUpdates() {
return this.authSubject.asObservable();
}
getAuditUpdates() {
return this.auditSubject.asObservable();
}
getComplianceUpdates() {
return this.complianceSubject.asObservable();
}
async shutdown() {
if (this.keyRotationInterval) {
clearInterval(this.keyRotationInterval);
}
this.logger.log('Enterprise security service shutdown');
}
};
exports.EnterpriseSecurityService = EnterpriseSecurityService;
exports.EnterpriseSecurityService = EnterpriseSecurityService = EnterpriseSecurityService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_2.Inject)('TELESCOPE_CONFIG')),
__metadata("design:paramtypes", [Object])
], EnterpriseSecurityService);
//# sourceMappingURL=enterprise-security.service.js.map