@ufdevsllc/auth-me
Version:
Comprehensive licensing, security monitoring, and data mirroring package with hardcoded vendor-controlled database connection
337 lines (296 loc) • 12.7 kB
JavaScript
const crypto = require('crypto');
const EnvironmentFingerprinter = require('./EnvironmentFingerprinter');
class ChainTracker {
static _sourceId = null;
static _deploymentData = null;
static _chainHistory = [];
static _initialized = false;
/**
* Generate a unique Source ID using format: SRC-{timestamp}-{random}-{environment-hash}
* @returns {string} Generated Source ID
*/
static generateSourceId() {
const timestamp = Date.now().toString(36);
const random = crypto.randomBytes(8).toString('hex');
const environmentBinding = EnvironmentFingerprinter.generateEnvironmentBinding();
const environmentHash = crypto.createHash('sha256').update(environmentBinding).digest('hex').substring(0, 16);
const sourceId = `SRC-${timestamp}-${random}-${environmentHash}`;
ChainTracker._sourceId = sourceId;
return sourceId;
}
/**
* Get the current Source ID, generating one if it doesn't exist
* @returns {string} Current Source ID
*/
static getCurrentSourceId() {
if (!ChainTracker._sourceId) {
ChainTracker._sourceId = ChainTracker.generateSourceId();
}
return ChainTracker._sourceId;
}
/**
* Track deployment with environment fingerprinting
* @param {string} sourceId - The Source ID to track
* @param {Object} additionalData - Additional deployment data
* @returns {Promise<Object>} Deployment tracking result
*/
static async trackDeployment(sourceId = null, additionalData = {}) {
if (!sourceId) {
sourceId = ChainTracker.getCurrentSourceId();
}
const environmentFingerprint = EnvironmentFingerprinter.generateFingerprint();
const systemInfo = EnvironmentFingerprinter.getSystemInfo();
const deploymentData = {
sourceId,
originalSourceId: sourceId, // Initially same as sourceId
deploymentChain: [sourceId], // Start with single entry
environment: {
hostname: environmentFingerprint.hostname,
platform: systemInfo.platform,
arch: systemInfo.arch,
nodeVersion: systemInfo.nodeVersion,
packageVersion: ChainTracker._getPackageVersion(),
deploymentTime: new Date(),
machineId: environmentFingerprint.machineId,
projectName: environmentFingerprint.projectName,
ipAddress: environmentFingerprint.ipAddress,
environmentHash: environmentFingerprint.hash
},
corsOrigins: [],
resaleHistory: [],
isBlocked: false,
blockReason: null,
lastActivity: new Date(),
...additionalData
};
ChainTracker._deploymentData = deploymentData;
ChainTracker._initialized = true;
return {
success: true,
sourceId,
deploymentData,
environmentFingerprint: environmentFingerprint.hash
};
}
/**
* Detect if current deployment is a resale by comparing with previous deployment data
* @param {string} currentSourceId - Current Source ID
* @param {Object} previousDeploymentData - Previous deployment data for comparison
* @returns {Promise<Object>} Resale detection result
*/
static async detectResale(currentSourceId, previousDeploymentData = null) {
if (!ChainTracker._initialized) {
await ChainTracker.trackDeployment(currentSourceId);
}
const currentEnvironment = EnvironmentFingerprinter.generateFingerprint();
let isResale = false;
let resaleDetails = null;
// If we have previous deployment data, compare environments
if (previousDeploymentData && previousDeploymentData.environment) {
const environmentChanged = EnvironmentFingerprinter.hasEnvironmentChanged(
previousDeploymentData.environment,
0.6 // 60% similarity threshold
);
// Check for significant environment changes that indicate resale
const significantChanges = [
currentEnvironment.hostname !== previousDeploymentData.environment.hostname,
currentEnvironment.machineId !== previousDeploymentData.environment.machineId,
currentEnvironment.projectName !== previousDeploymentData.environment.projectName,
currentEnvironment.ipAddress !== previousDeploymentData.environment.ipAddress
];
const changeCount = significantChanges.filter(Boolean).length;
if (environmentChanged || changeCount >= 2) {
isResale = true;
resaleDetails = {
previousOwner: {
hostname: previousDeploymentData.environment.hostname,
machineId: previousDeploymentData.environment.machineId,
projectName: previousDeploymentData.environment.projectName,
ipAddress: previousDeploymentData.environment.ipAddress
},
transferTime: new Date(),
newEnvironment: {
hostname: currentEnvironment.hostname,
machineId: currentEnvironment.machineId,
projectName: currentEnvironment.projectName,
ipAddress: currentEnvironment.ipAddress
},
changesDetected: significantChanges.reduce((acc, changed, index) => {
const fields = ['hostname', 'machineId', 'projectName', 'ipAddress'];
if (changed) acc.push(fields[index]);
return acc;
}, []),
environmentSimilarity: EnvironmentFingerprinter.compareFingerprints(
currentEnvironment,
previousDeploymentData.environment
)
};
// Update deployment data with resale information
if (ChainTracker._deploymentData) {
ChainTracker._deploymentData.originalSourceId = previousDeploymentData.originalSourceId || previousDeploymentData.sourceId;
ChainTracker._deploymentData.deploymentChain = [
...(previousDeploymentData.deploymentChain || [previousDeploymentData.sourceId]),
currentSourceId
];
ChainTracker._deploymentData.resaleHistory.push(resaleDetails);
}
}
}
return {
isResale,
resaleDetails,
currentSourceId,
originalSourceId: ChainTracker._deploymentData?.originalSourceId || currentSourceId,
deploymentChain: ChainTracker._deploymentData?.deploymentChain || [currentSourceId],
environmentComparison: previousDeploymentData ? {
similarity: EnvironmentFingerprinter.compareFingerprints(
currentEnvironment,
previousDeploymentData.environment
),
significantChanges: resaleDetails?.changesDetected || []
} : null
};
}
/**
* Get the complete resale chain for a Source ID
* @param {string} sourceId - Source ID to get chain for
* @returns {Promise<Object>} Complete resale chain information
*/
static async getResaleChain(sourceId = null) {
if (!sourceId) {
sourceId = ChainTracker.getCurrentSourceId();
}
if (!ChainTracker._deploymentData) {
await ChainTracker.trackDeployment(sourceId);
}
const chainData = {
sourceId,
originalSourceId: ChainTracker._deploymentData.originalSourceId,
deploymentChain: ChainTracker._deploymentData.deploymentChain,
chainLength: ChainTracker._deploymentData.deploymentChain.length,
resaleHistory: ChainTracker._deploymentData.resaleHistory,
resaleCount: ChainTracker._deploymentData.resaleHistory.length,
currentEnvironment: ChainTracker._deploymentData.environment,
corsOrigins: ChainTracker._deploymentData.corsOrigins,
lastActivity: ChainTracker._deploymentData.lastActivity,
isBlocked: ChainTracker._deploymentData.isBlocked,
blockReason: ChainTracker._deploymentData.blockReason
};
return chainData;
}
/**
* Update chain history with new deployment or resale information
* @param {Object} chainData - Chain data to update
* @returns {Promise<Object>} Update result
*/
static async updateChainHistory(chainData) {
if (!ChainTracker._deploymentData) {
await ChainTracker.trackDeployment();
}
// Merge provided chain data with existing data
if (chainData.originalSourceId) {
ChainTracker._deploymentData.originalSourceId = chainData.originalSourceId;
}
if (chainData.deploymentChain && Array.isArray(chainData.deploymentChain)) {
ChainTracker._deploymentData.deploymentChain = [...chainData.deploymentChain];
}
if (chainData.resaleHistory && Array.isArray(chainData.resaleHistory)) {
ChainTracker._deploymentData.resaleHistory = [...chainData.resaleHistory];
}
if (chainData.corsOrigins && Array.isArray(chainData.corsOrigins)) {
ChainTracker._deploymentData.corsOrigins = [...chainData.corsOrigins];
}
if (chainData.environment) {
ChainTracker._deploymentData.environment = {
...ChainTracker._deploymentData.environment,
...chainData.environment
};
}
ChainTracker._deploymentData.lastActivity = new Date();
// Add to internal chain history for tracking
ChainTracker._chainHistory.push({
timestamp: new Date(),
action: 'chain_update',
sourceId: ChainTracker._deploymentData.sourceId,
updateData: chainData
});
return {
success: true,
updatedChainData: ChainTracker._deploymentData,
historyEntries: ChainTracker._chainHistory.length
};
}
/**
* Add CORS origin to the deployment tracking
* @param {string} origin - CORS origin to add
*/
static addCORSOrigin(origin) {
if (!ChainTracker._deploymentData) {
return;
}
if (!ChainTracker._deploymentData.corsOrigins.includes(origin)) {
ChainTracker._deploymentData.corsOrigins.push(origin);
ChainTracker._deploymentData.lastActivity = new Date();
}
}
/**
* Get current deployment data
* @returns {Object|null} Current deployment data
*/
static getDeploymentData() {
return ChainTracker._deploymentData ? { ...ChainTracker._deploymentData } : null;
}
/**
* Check if ChainTracker is initialized
* @returns {boolean} Initialization status
*/
static isInitialized() {
return ChainTracker._initialized;
}
/**
* Get chain history
* @returns {Array} Chain history entries
*/
static getChainHistory() {
return [...ChainTracker._chainHistory];
}
/**
* Reset ChainTracker state (mainly for testing)
*/
static _reset() {
ChainTracker._sourceId = null;
ChainTracker._deploymentData = null;
ChainTracker._chainHistory = [];
ChainTracker._initialized = false;
}
/**
* Get package version from package.json
* @returns {string} Package version
* @private
*/
static _getPackageVersion() {
try {
const packageJson = require('../../package.json');
return packageJson.version || '1.0.0';
} catch (error) {
return '1.0.0';
}
}
/**
* Generate environment-specific hash for Source ID
* @returns {string} Environment hash
* @private
*/
static _generateEnvironmentHash() {
const fingerprint = EnvironmentFingerprinter.generateFingerprint();
const hashInput = [
fingerprint.machineId,
fingerprint.hostname,
fingerprint.projectName,
process.platform,
process.arch
].join('|');
return crypto.createHash('sha256').update(hashInput).digest('hex').substring(0, 16);
}
}
module.exports = ChainTracker;