UNPKG

@trap_stevo/lockline

Version:

The ultimate solution for secure, scalable, and tamper-resistant license key validation. Combine flexible validation strategies, encrypted / pluggable storage, smart fallback resilience, and real-time revocation control to lock down your software — and un

453 lines (452 loc) 15.2 kB
"use strict"; function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const Lockbox = require("./HUDManagers/Lockbox"); var _currentLicenseKeys = /*#__PURE__*/new WeakMap(); var _validators = /*#__PURE__*/new WeakMap(); var _registeredKeys = /*#__PURE__*/new WeakMap(); var _fallback = /*#__PURE__*/new WeakMap(); var _storage = /*#__PURE__*/new WeakMap(); var _defaultValidator = /*#__PURE__*/new WeakMap(); var _Lockline_brand = /*#__PURE__*/new WeakSet(); class Lockline { constructor() { _classPrivateMethodInitSpec(this, _Lockline_brand); _classPrivateFieldInitSpec(this, _currentLicenseKeys, new Map()); _classPrivateFieldInitSpec(this, _validators, {}); _classPrivateFieldInitSpec(this, _registeredKeys, new Map()); _classPrivateFieldInitSpec(this, _fallback, new Map()); _classPrivateFieldInitSpec(this, _storage, { get: async key => _classPrivateFieldGet(_fallback, this).get(key), set: async (key, value) => _classPrivateFieldGet(_fallback, this).set(key, value), delete: async key => _classPrivateFieldGet(_fallback, this).delete(key), keys: async () => Array.from(_classPrivateFieldGet(_fallback, this).keys()) }); _classPrivateFieldInitSpec(this, _defaultValidator, null); } static lockbox(path, options = {}) { return Lockbox(path, options); } setStorage(callbacks = {}, options = {}) { const rawConfig = callbacks && typeof callbacks === "object" && !callbacks.get && !callbacks.set && ("path" in callbacks || "namespace" in callbacks); if (rawConfig) { const store = Lockline.lockbox(callbacks.path || `${process.cwd()}/lockbox`, callbacks); _classPrivateFieldSet(_storage, this, { get: key => store.get(key), set: (key, val) => store.set(key, val), delete: key => store.delete(key), keys: () => store.keys() }); return; } const { get, set, delete: del, keys } = callbacks; const strict = options.strict === true; _classPrivateFieldSet(_storage, this, { get: typeof get === "function" ? get : strict ? _assertClassBrand(_Lockline_brand, this, _throw).call(this, "get") : async k => _classPrivateFieldGet(_fallback, this).get(k), set: typeof set === "function" ? set : strict ? _assertClassBrand(_Lockline_brand, this, _throw).call(this, "set") : async (k, v) => _classPrivateFieldGet(_fallback, this).set(k, v), delete: typeof del === "function" ? del : strict ? _assertClassBrand(_Lockline_brand, this, _throw).call(this, "delete") : async k => _classPrivateFieldGet(_fallback, this).delete(k), keys: typeof keys === "function" ? keys : strict ? _assertClassBrand(_Lockline_brand, this, _throw).call(this, "keys") : async () => Array.from(_classPrivateFieldGet(_fallback, this).keys()) }); } registerValidator(name, fn) { if (typeof fn !== "function") { throw new Error("Invalid validator. Must use a function"); } _classPrivateFieldGet(_validators, this)[name] = fn; } setDefaultValidator(name) { if (!_classPrivateFieldGet(_validators, this)[name]) { throw new Error(`Validator "${name}" not registered.`); } _classPrivateFieldSet(_defaultValidator, this, name); } getCurrentLicenseKey(scope = "default") { return _classPrivateFieldGet(_currentLicenseKeys, this).get(scope) || null; } async loadRecentLicenseKeys(groupBy = meta => "default") { const keys = await _classPrivateFieldGet(_storage, this).keys(); const grouped = new Map(); for (const hashedKey of keys) { const data = await _classPrivateFieldGet(_storage, this).get(hashedKey, { locked: true }); const expired = data?.meta?.expiresAt && Date.now() > data.meta.expiresAt; if (expired) { continue; } const group = groupBy(data?.meta || {}); const existing = grouped.get(group); const updatedAt = data?.meta?.updatedAt ?? 0; if (!existing || updatedAt > existing.updatedAt) { const originalKey = data?.key || data?.meta?.originalKey || null; if (originalKey) { grouped.set(group, { key: originalKey, updatedAt }); } } } for (const [group, { key }] of grouped.entries()) { _assertClassBrand(_Lockline_brand, this, _setCurrentLicenseKey).call(this, group, key); } return grouped; } async touchLicenseKey(key) { const data = await _classPrivateFieldGet(_storage, this).get(key); if (!data) { return; } const updated = { ...data, meta: { ...data.meta, lastUsedAt: Date.now() } }; await _classPrivateFieldGet(_storage, this).set(key, updated); } provisionKey(key, config = {}) { _classPrivateFieldGet(_registeredKeys, this).set(key, config); } async validate(key, options = {}) { const { recordAssumedValidation = true, assumePreviouslyValid = false, fallbackIfOffline = false, autoRemoveExpired = false } = options; const config = _classPrivateFieldGet(_registeredKeys, this).get(key) || (await _classPrivateFieldGet(_storage, this).get(key)); if (!config) { return { key, valid: false, reason: "Key not registered", raw: null, meta: null }; } const { validators = [], shortCircuit = false, validate, meta = {} } = config; const chain = validators.length > 0 ? validators : _classPrivateFieldGet(_defaultValidator, this) ? [{ name: _classPrivateFieldGet(_defaultValidator, this) }] : []; if (chain.length === 0) { return { key, valid: false, reason: "No validators defined", raw: null, meta }; } const results = []; let allOffline = true; for (const { name, input, fallback, tag } of chain) { const handler = _classPrivateFieldGet(_validators, this)[name]; if (!handler) { results.push({ valid: false, reason: `Validator "${name}" not found`, tag, fallbackUsed: false }); if (shortCircuit) { break; } continue; } try { let result = await handler({ key, input }); allOffline = false; result = { valid: !!result?.valid, reason: result?.reason, raw: result?.raw ?? null, tag, fallbackUsed: false }; if (!result.valid && fallback && _classPrivateFieldGet(_validators, this)[fallback]) { const fallbackResult = await _classPrivateFieldGet(_validators, this)[fallback]({ key, input }); result = { valid: !!fallbackResult?.valid, reason: fallbackResult?.reason || result.reason, raw: fallbackResult?.raw ?? result.raw, tag, fallbackUsed: true }; } results.push(result); if (shortCircuit && !result.valid) { break; } } catch (error) { results.push({ valid: false, reason: `Validator "${name}" did not validate ~ ${error.message}`, tag, fallbackUsed: false }); if (shortCircuit) { break; } } } let final; if (typeof validate === "function") { final = validate(results); } else { final = results.every(r => r.valid) ? { valid: true } : { valid: false, reason: "One or more validators did not validate" }; } const now = Date.now(); const expired = meta?.expiresAt && now > meta.expiresAt; const existing = await _classPrivateFieldGet(_storage, this).get(key); const stored = !!existing; if (!final.valid && assumePreviouslyValid && fallbackIfOffline && allOffline && stored) { const now = Date.now(); const storedStatus = existing?.status; const pRevoked = storedStatus === "revoked"; if (pRevoked) { return { key, valid: false, reason: "Previously revoked. Cannot assume valid offline.", assumedValid: false, raw: results, meta }; } const assumedResult = { key, valid: true, reason: "Assumed valid (offline)", assumedValid: true, raw: results, meta }; if (recordAssumedValidation === true) { const enriched = { ...config, meta: { ...(config?.meta || {}), updatedAt: now }, status: "assumed-valid", lastValidation: { valid: true, assumedValid: true, reason: "Assumed valid (offline)", timestamp: now } }; await _classPrivateFieldGet(_storage, this).set(key, enriched); assumedResult.meta = enriched.meta; } return assumedResult; } if (final.valid && expired) { const stored = !_classPrivateFieldGet(_registeredKeys, this).has(key); if (autoRemoveExpired && stored) { await _classPrivateFieldGet(_storage, this).delete(key); } return { key, valid: false, reason: "Key expired", raw: results, meta }; } return { key, ...final, assumedValid: false, raw: results, meta }; } async sealKey(key, config = {}, options = {}) { this.provisionKey(key, config); const result = await this.validate(key, options); const now = Date.now(); const defaultExcludedKeys = new Set(["valid", "reason", "assumedValid", "tag", "fallbackUsed", "timestamp"]); const customFilter = typeof options.metaFilter === "function" ? options.metaFilter : (k, v) => !defaultExcludedKeys.has(k); const extractedMetadata = result.raw.reduce((acc, res) => { if (res?.raw && typeof res.raw === "object") { for (const [k, v] of Object.entries(res.raw)) { if (customFilter(k, v) && !(k in acc)) { acc[k] = v; } } } return acc; }, {}); const existing = await _classPrivateFieldGet(_storage, this).get(key); const shouldStore = result.valid || options.storeInvalid === true; const safeToWrite = !result.assumedValid || existing?.status !== "revoked"; if (shouldStore && safeToWrite) { const enriched = { ...config, key, meta: { ...(existing?.meta || {}), ...config.meta, ...(result.assumedValid ? {} : extractedMetadata), createdAt: existing?.meta?.createdAt ?? config.meta?.createdAt ?? now, updatedAt: now }, status: result.assumedValid ? existing?.status ?? "assumed-valid" : result.valid ? "valid" : "revoked", lastValidation: { valid: result.valid, assumedValid: result.assumedValid, reason: result.reason, timestamp: now } }; await _classPrivateFieldGet(_storage, this).set(key, enriched); } _assertClassBrand(_Lockline_brand, this, _setCurrentLicenseKey).call(this, options?.scope || "default", key); return result; } async inspectKey(key) { return await _classPrivateFieldGet(_storage, this).get(key); } async revokeKey(key) { return await _classPrivateFieldGet(_storage, this).delete(key); } async scanKeys(options = {}) { const now = Date.now(); const filter = options.filter || "all"; const keys = await _classPrivateFieldGet(_storage, this).keys(); const result = []; for (const key of keys) { const data = await _classPrivateFieldGet(_storage, this).get(key); const expired = data?.meta?.expiresAt && now > data.meta.expiresAt; if (filter === "valid" && expired || filter === "expired" && !expired) { continue; } result.push(key); } return result; } withLicenseProtection(method, scope = "default", options = {}) { const { onLicenseBlocked, onMissingKey, onInvalidKey, onSuccess } = options; return async (...args) => { const key = this.getCurrentLicenseKey(scope); if (!key) { if (typeof onMissingKey === "function") { return await onMissingKey({ scope, method }); } else if (typeof onLicenseBlocked === "function") { return await onLicenseBlocked({ scope, method, reason: "missing" }); } return onLicenseBlocked ?? null; } const info = await this.inspectKey(key); if (!info || info.status === "revoked" || info.lastValidation?.valid !== true) { if (typeof onInvalidKey === "function") { await onInvalidKey({ scope, method, key }); } else if (typeof onLicenseBlocked === "function") { return await onLicenseBlocked({ scope, method, reason: "invalid", key }); } return onLicenseBlocked ?? null; } await this.touchLicenseKey(key); if (typeof onSuccess === "function") { await onSuccess({ scope, method, key }); } return await method.apply(this, args); }; } async auditExpiredKeys() { const now = Date.now(); const keys = await _classPrivateFieldGet(_storage, this).keys(); const expired = []; for (const key of keys) { const data = await _classPrivateFieldGet(_storage, this).get(key); if (data?.meta?.expiresAt && now > data.meta.expiresAt) { expired.push({ key, meta: data.meta }); } } return expired; } async purgeExpiredKeys(callback) { const expiredKeys = await this.auditExpiredKeys(); for (const { key, meta } of expiredKeys) { if (typeof callback === "function") { await callback(key, meta); } await _classPrivateFieldGet(_storage, this).delete(key); } return expiredKeys.map(k => k.key); } } function _throw(method) { throw new Error(`Storage method "${method}" required in strict mode.`); } function _setCurrentLicenseKey(scope, key) { _classPrivateFieldGet(_currentLicenseKeys, this).set(scope || "default", key); } ; module.exports = Lockline;