UNPKG

@interopio/desktop-cli

Version:

io.Connect Desktop Seed Repository CLI Tools

245 lines 10.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LicenseValidator = void 0; const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const utils_1 = require("../utils"); class LicenseValidator { async validate() { try { const licenseData = await this.loadLicenseData(); if (!licenseData) { utils_1.Logger.error('No license.json file found'); return false; } // Check if license data contains a JWT token let license; if (this.isJWTToken(licenseData)) { utils_1.Logger.debug('Detected JWT token in license.json, decoding...'); license = await this.decodeJWTLicense(licenseData); } else { utils_1.Logger.debug('Using plain JSON license format'); license = licenseData; } const validationResult = this.validateLicenseStructure(license); if (!validationResult.isValid) { validationResult.errors.forEach(error => utils_1.Logger.error(error)); return false; } // Check expiration if (license.expiresAt) { const expirationDate = new Date(license.expiresAt); const now = new Date(); if (expirationDate < now) { utils_1.Logger.error('License has expired'); return false; } // Warn if expiring soon (within 30 days) const daysUntilExpiration = Math.ceil((expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); if (daysUntilExpiration <= 30) { utils_1.Logger.warning(`License expires in ${daysUntilExpiration} days`); } } return true; } catch (error) { utils_1.Logger.error(`License validation error: ${error instanceof Error ? error.message : String(error)}`); return false; } } async validateComponentAccess(componentName) { try { const licenseData = await this.loadLicenseData(); if (!licenseData) return false; // Decode license if it's a JWT token let license; if (this.isJWTToken(licenseData)) { license = await this.decodeJWTLicense(licenseData); } else { license = licenseData; } // Check if component is allowed by license if (license.allowedComponents && license.allowedComponents.length > 0) { return license.allowedComponents.includes(componentName) || license.allowedComponents.includes('*'); } return true; // No restrictions specified } catch (error) { utils_1.Logger.error(`Component access validation error: ${error instanceof Error ? error.message : String(error)}`); return false; } } async getLicenseInfo() { try { const licenseData = await this.loadLicenseData(); if (!licenseData) return null; // Decode license if it's a JWT token if (this.isJWTToken(licenseData)) { return await this.decodeJWTLicense(licenseData); } else { return licenseData; } } catch (error) { utils_1.Logger.error(`Failed to get license info: ${error instanceof Error ? error.message : String(error)}`); return null; } } validateLicenseStructure(license) { const errors = []; const warnings = []; // Required fields if (!license.id) { errors.push('License ID is required'); } if (!license.type || !['development', 'production', 'enterprise', 'trial'].includes(license.type)) { errors.push('Valid license type is required (development, production, enterprise, trial)'); } if (!Array.isArray(license.features)) { errors.push('License features must be an array'); } if (!Array.isArray(license.allowedComponents)) { errors.push('Allowed components must be an array'); } // Warnings for optional fields if (license.type === 'trial' && !license.expiresAt) { warnings.push('Trial licenses should have an expiration date'); } if (license.type === 'enterprise' && !license.organization) { warnings.push('Enterprise licenses should specify an organization'); } return { isValid: errors.length === 0, errors, warnings }; } async loadLicenseData() { try { const licensePath = path_1.default.join(process.cwd(), 'license.json'); if (!(await fs_extra_1.default.pathExists(licensePath))) { return null; } const rawData = await fs_extra_1.default.readFile(licensePath, 'utf8'); return JSON.parse(rawData); } catch (error) { utils_1.Logger.error(`Failed to load license data: ${error instanceof Error ? error.message : String(error)}`); return null; } } isJWTToken(data) { // Check if the data contains a JWT token if (typeof data === 'string') { // If the entire file is just a JWT token return this.isValidJWTFormat(data); } if (typeof data === 'object' && data !== null) { // Check for common JWT token field names const tokenFields = ['token', 'jwt', 'license_token', 'licenseToken']; for (const field of tokenFields) { if (data[field] && typeof data[field] === 'string' && this.isValidJWTFormat(data[field])) { return true; } } } return false; } isValidJWTFormat(token) { // JWT tokens have 3 parts separated by dots const parts = token.split('.'); return parts.length === 3 && parts.every(part => part.length > 0); } async decodeJWTLicense(data) { try { let token; if (typeof data === 'string') { token = data; } else { // Extract token from object const tokenFields = ['token', 'jwt', 'license_token', 'licenseToken']; const tokenField = tokenFields.find(field => data[field] && this.isValidJWTFormat(data[field])); if (!tokenField) { throw new Error('No valid JWT token found in license data'); } token = data[tokenField]; } // Get public key for verification const publicKey = await this.getPublicKey(); // Verify and decode the JWT token const decoded = jsonwebtoken_1.default.verify(token, publicKey, { algorithms: ['RS256', 'ES256', 'PS256'] // Only allow asymmetric algorithms }); // Extract license information from JWT payload const license = { id: decoded.sub || decoded.licenseId || decoded.id, type: decoded.licenseType || decoded.type || 'trial', expiresAt: decoded.exp ? new Date(decoded.exp * 1000).toISOString() : decoded.expiresAt, features: decoded.features || [], allowedComponents: decoded.allowedComponents || decoded.components || ['*'], maxUsers: decoded.maxUsers, organization: decoded.org || decoded.organization || decoded.iss }; utils_1.Logger.debug('JWT license decoded successfully'); return license; } catch (error) { if (error instanceof jsonwebtoken_1.default.JsonWebTokenError) { throw new Error(`Invalid JWT token: ${error.message}`); } else if (error instanceof jsonwebtoken_1.default.TokenExpiredError) { throw new Error('License token has expired'); } else if (error instanceof jsonwebtoken_1.default.NotBeforeError) { throw new Error('License token is not yet valid'); } else { throw new Error(`Failed to decode JWT license: ${error instanceof Error ? error.message : String(error)}`); } } } async getPublicKey() { try { // Try multiple locations for the public key const possiblePaths = [ path_1.default.join(process.cwd(), 'license-public-key.pem'), path_1.default.join(process.cwd(), 'keys', 'public.pem'), path_1.default.join(process.cwd(), '.keys', 'license-public.pem'), path_1.default.join(process.cwd(), 'certs', 'license-public.pem') ]; for (const keyPath of possiblePaths) { if (await fs_extra_1.default.pathExists(keyPath)) { utils_1.Logger.debug(`Using public key from: ${keyPath}`); return await fs_extra_1.default.readFile(keyPath, 'utf8'); } } // Check environment variable if (process.env.IOCD_LICENSE_PUBLIC_KEY) { utils_1.Logger.debug('Using public key from environment variable'); return process.env.IOCD_LICENSE_PUBLIC_KEY.replace(/\\n/g, '\n'); } // For development/testing, allow embedded public key in license.json const licenseData = await this.loadLicenseData(); if (licenseData && licenseData.publicKey) { utils_1.Logger.debug('Using embedded public key from license.json'); return licenseData.publicKey; } throw new Error('No public key found for JWT verification'); } catch (error) { throw new Error(`Failed to load public key: ${error instanceof Error ? error.message : String(error)}`); } } } exports.LicenseValidator = LicenseValidator; //# sourceMappingURL=license-validator.js.map