@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
JavaScript
"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;