Versie 0.2.0

This commit is contained in:
Atreides Assets committed 2026-10-29 11:57:29 +01:00
commit e77d8fd373
495 files changed
+81026

No files matched your search

+173
View File
@@ -0,0 +1,173 @@
// Generated, do not edit. sha256:0a4af87a128e6b903b5ce63c00bf6f500061dd0feb2e18ca3a3d4791c738eff9
import { readFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { schnorr } from '@noble/curves/secp256k1.js';
import { CONFIG } from '../shared/config.js';
import { installDir } from './portfolios.mjs';
import { services } from './registers.mjs';
const L = CONFIG.LICENSE;
const HEX64 = /^[0-9a-f]{64}$/;
const HEX128 = /^[0-9a-f]{128}$/;
const DATE = /^\d{4}-\d{2}-\d{2}$/;
const FIELDS = ['buyer', 'level', 'issued', 'majorFrom', 'majorTo'];
export function licenseMessage(body) {
const lines = FIELDS.map((f) => `${f}=${body[f]}`);
return [L.MESSAGE_PREFIX, ...lines].join('\n');
}
function messageHash(body) {
return createHash('sha256').update(licenseMessage(body), 'utf-8').digest();
}
const ISSUER_FIELDS = ['key', 'validFrom', 'validTo'];
export function issuerMessage(issuer) {
return [L.ISSUER_PREFIX, ...ISSUER_FIELDS.map((f) => `${f}=${issuer[f]}`)].join('\n');
}
const sha256 = (text) => createHash('sha256').update(text, 'utf-8').digest();
const schnorrOk = (sig, hash, key) => {
try {
return schnorr.verify(Buffer.from(sig, 'hex'), hash, Buffer.from(key, 'hex'));
} catch {
return false;
}
};
class LicenseError extends Error {}
function refuse(reason) {
throw new LicenseError(reason);
}
export function parseLicense(text) {
let raw;
try {
raw = JSON.parse(text);
} catch {
refuse('The licence file is not valid JSON.');
}
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) refuse('The licence file is not an object.');
if (!L.FORMATS.includes(raw.format)) refuse(`The licence has format ${JSON.stringify(raw.format)}, this app reads format ${L.FORMATS.join(' and ')}.`);
if (typeof raw.signature !== 'string' || !HEX128.test(raw.signature)) refuse('The signature is missing or is not 128 hex characters.');
const body = raw.license;
if (!body || typeof body !== 'object' || Array.isArray(body)) refuse('The licence file has no `license` object.');
const extra = Object.keys(body).filter((k) => !FIELDS.includes(k));
if (extra.length) refuse(`The licence carries unknown fields: ${extra.join(', ')}.`);
const missing = FIELDS.filter((f) => !(f in body));
if (missing.length) refuse(`The licence is missing fields: ${missing.join(', ')}.`);
if (typeof body.buyer !== 'string' || !HEX64.test(body.buyer)) refuse('The buyer key is not 64 hex characters.');
if (!L.LEVELS.includes(body.level)) refuse(`Unknown level ${JSON.stringify(body.level)}.`);
if (typeof body.issued !== 'string' || !DATE.test(body.issued)) refuse('The issue date is not a YYYY-MM-DD date.');
if (!Number.isInteger(body.majorFrom) || body.majorFrom < 1) refuse('`majorFrom` is not a major version of 1 or higher.');
const unlimited = body.majorTo === L.UNLIMITED;
if (!unlimited && (!Number.isInteger(body.majorTo) || body.majorTo < body.majorFrom)) {
refuse(`\`majorTo\` is neither '${L.UNLIMITED}' nor a whole number from \`majorFrom\` up.`);
}
if (raw.format === 1) return { format: raw.format, license: body, signature: raw.signature };
const issuer = raw.issuer;
if (!issuer || typeof issuer !== 'object' || Array.isArray(issuer)) refuse('A format 2 licence has no `issuer` object.');
const issuerExtra = Object.keys(issuer).filter((k) => ![...ISSUER_FIELDS, 'signature'].includes(k));
if (issuerExtra.length) refuse(`The issuer carries unknown fields: ${issuerExtra.join(', ')}.`);
if (typeof issuer.key !== 'string' || !HEX64.test(issuer.key)) refuse('The issuer key is not 64 hex characters.');
if (typeof issuer.validFrom !== 'string' || !DATE.test(issuer.validFrom)) refuse('`validFrom` of the issuer is not a YYYY-MM-DD date.');
if (typeof issuer.validTo !== 'string' || !DATE.test(issuer.validTo)) refuse('`validTo` of the issuer is not a YYYY-MM-DD date.');
if (issuer.validTo < issuer.validFrom) refuse('The issuer is valid until before it is valid from.');
if (typeof issuer.signature !== 'string' || !HEX128.test(issuer.signature)) refuse('The signature of the issuer is missing or is not 128 hex characters.');
if (body.issued < issuer.validFrom || body.issued > issuer.validTo) refuse('The licence was issued outside the period of its issuer.');
return { format: raw.format, license: body, signature: raw.signature, issuer };
}
export function verifySignature(parsed, sellerKey = L.SELLER_KEY) {
if (!HEX64.test(sellerKey)) return false;
if (parsed.format === 2) {
if (!schnorrOk(parsed.issuer.signature, sha256(issuerMessage(parsed.issuer)), sellerKey)) return false;
return schnorrOk(parsed.signature, messageHash(parsed.license), parsed.issuer.key);
}
return schnorrOk(parsed.signature, messageHash(parsed.license), sellerKey);
}
export function coversVersion(license, appVersion) {
const m = /^(\d+)\.\d+\.\d+$/.exec(String(appVersion));
if (!m) return false;
const major = Number(m[1]);
if (major < 1) return false;
if (major < license.majorFrom) return false;
return license.majorTo === L.UNLIMITED ? true : major <= license.majorTo;
}
export function appVersion() {
const pad = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
return JSON.parse(readFileSync(pad, 'utf8')).version;
}
export function licenseState(version = appVersion(), dir = undefined) {
const major = Number(/^(\d+)\./.exec(String(version))?.[1]);
if (!Number.isInteger(major)) return { level: 'free', enforced: true, why: 'unknown-version' };
const forced = process.env.ASSETAPP_LICENSE_STATE;
if (forced === 'free') return { level: 'free', enforced: true, why: 'forced' };
const develop = major < 1 && services.get('develop') !== null;
if (forced === 'full' && develop) return { level: 'full', enforced: false, why: 'forced-develop' };
if (develop) return { level: 'full', enforced: false, why: 'develop' };
const uitkomst = dir === undefined ? readLicense() : readLicense(dir);
if (uitkomst.state === 'valid' && coversVersion(uitkomst.license, version)) {
return { level: uitkomst.license.level, enforced: true, why: 'licensed', license: uitkomst.license };
}
return { level: 'free', enforced: true, why: uitkomst.state === 'valid' ? 'out-of-range' : uitkomst.state };
}
export function maxFor(soort, state = licenseState()) {
const tabel = L.FREE.MAX;
if (!(soort in tabel)) throw new Error(`Unknown capacity: ${soort}. Known: ${Object.keys(tabel).join(', ')}.`);
return state.level === 'full' ? null : tabel[soort];
}
export function capacityReached(soort, huidig, state = licenseState()) {
const max = maxFor(soort, state);
if (max === null) return null;
return huidig >= max ? max : null;
}
export function allowedAssets(state = licenseState()) {
return state.level === 'full' ? null : L.FREE.ASSETS;
}
export function hasFeature(naam, state = licenseState()) {
const tabel = L.FREE.HAS;
if (!(naam in tabel)) throw new Error(`Unknown feature: ${naam}. Known: ${Object.keys(tabel).join(', ')}.`);
return state.level === 'full' ? true : tabel[naam];
}
export function readLicense(dir = installDir()) {
let text;
try {
text = readFileSync(join(dir, L.FILE_NAME), 'utf8');
} catch (err) {
if (err.code === 'ENOENT') return { state: 'none' };
return { state: 'unreadable', reason: err.message };
}
try {
const parsed = parseLicense(text);
if (!verifySignature(parsed)) return { state: 'invalid', reason: 'The signature does not belong to this licence.' };
return { state: 'valid', license: parsed.license };
} catch (err) {
if (err instanceof LicenseError) return { state: 'invalid', reason: err.message };
throw err;
}
}