213 lines
4.6 KiB
JavaScript
213 lines
4.6 KiB
JavaScript
const crypto = require('crypto');
|
|
|
|
class VaultifyManager {
|
|
|
|
constructor({
|
|
vaultModel,
|
|
publicKey
|
|
}) {
|
|
this.Vault = vaultModel;
|
|
this.publicKey = publicKey;
|
|
|
|
this.cache = new Map(); // feature cache per customer
|
|
}
|
|
|
|
|
|
createMiddleware() {
|
|
|
|
return async (req, res, next) => {
|
|
|
|
const customerId = req.user?.Customer_ID;
|
|
|
|
if (!customerId) {
|
|
return res.status(403).send('No customer');
|
|
}
|
|
|
|
await this.loadCustomer(customerId);
|
|
|
|
req.vault = {
|
|
has: (f) => this.has(customerId, f),
|
|
get: (f, p) => this.get(customerId, f, p)
|
|
};
|
|
|
|
next();
|
|
};
|
|
}
|
|
|
|
// =========================================================
|
|
// LOAD ALL LICENSES FOR CUSTOMER
|
|
// =========================================================
|
|
|
|
async loadCustomer(customerId) {
|
|
|
|
const records = await this.Vault.findAll({
|
|
where: {
|
|
Customer_ID: customerId,
|
|
Active: true
|
|
}
|
|
});
|
|
|
|
const resultMap = new Map();
|
|
|
|
for (const record of records) {
|
|
|
|
const valid = this.verify(record);
|
|
if (!valid) continue;
|
|
|
|
const payload = this.parsePayload(record.Payload);
|
|
|
|
resultMap.set(record.Feature, {
|
|
payload,
|
|
expiresAt: record.ExpiresAt
|
|
});
|
|
}
|
|
|
|
this.cache.set(customerId, resultMap);
|
|
|
|
return {
|
|
customerId,
|
|
features: [...resultMap.keys()]
|
|
};
|
|
}
|
|
|
|
// =========================================================
|
|
// VERIFY SIGNATURE
|
|
// =========================================================
|
|
|
|
verify(record) {
|
|
try {
|
|
const payload = this.parsePayload(record.Payload);
|
|
|
|
const data = {
|
|
Customer_ID: record.Customer_ID,
|
|
Feature: record.Feature,
|
|
Payload: payload,
|
|
ExpiresAt: record.ExpiresAt ?? null
|
|
};
|
|
|
|
const verifier = crypto.createVerify('RSA-SHA256');
|
|
verifier.update(JSON.stringify(data));
|
|
verifier.end();
|
|
|
|
const isValid = verifier.verify(
|
|
this.publicKey,
|
|
record.Signature,
|
|
'base64'
|
|
);
|
|
|
|
if (!isValid) return false;
|
|
|
|
// 🔥 WICHTIG: Ablaufdatum HIER erzwingen
|
|
if (payload.expiresAt && record.ExpiresAt) {
|
|
if (payload.expiresAt !== record.ExpiresAt) {
|
|
return false;
|
|
}
|
|
}
|
|
if (record.ExpiresAt && new Date(record.ExpiresAt) < new Date()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// =========================================================
|
|
// SAFE JSON PARSER
|
|
// =========================================================
|
|
|
|
parsePayload(payload) {
|
|
try {
|
|
return typeof payload === 'string'
|
|
? JSON.parse(payload)
|
|
: payload;
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
// =========================================================
|
|
// FEATURE CHECK
|
|
// =========================================================
|
|
|
|
has(customerId, feature) {
|
|
|
|
const customer = this.cache.get(customerId);
|
|
if (!customer) return false;
|
|
|
|
const entry = customer.get(feature);
|
|
if (!entry) return false;
|
|
|
|
if (entry.expiresAt && new Date(entry.expiresAt) < new Date()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================
|
|
// GET FEATURE CONFIG
|
|
// =========================================================
|
|
|
|
get(customerId, feature, path = null) {
|
|
|
|
const customer = this.cache.get(customerId);
|
|
if (!customer) return undefined;
|
|
|
|
const entry = customer.get(feature);
|
|
if (!entry) return undefined;
|
|
|
|
if (!path) return entry.payload;
|
|
|
|
return path
|
|
.split('.')
|
|
.reduce((obj, key) => obj?.[key], entry.payload);
|
|
}
|
|
|
|
// =========================================================
|
|
// REFRESH SINGLE FEATURE
|
|
// =========================================================
|
|
|
|
async refreshFeature(customerId, feature) {
|
|
|
|
const record = await this.Vault.findOne({
|
|
where: {
|
|
Customer_ID: customerId,
|
|
Feature: feature,
|
|
Active: true
|
|
}
|
|
});
|
|
|
|
if (!record) return false;
|
|
|
|
if (!this.verify(record)) return false;
|
|
|
|
const customer = this.cache.get(customerId) || new Map();
|
|
|
|
customer.set(feature, {
|
|
payload: this.parsePayload(record.Payload),
|
|
expiresAt: record.ExpiresAt
|
|
});
|
|
|
|
this.cache.set(customerId, customer);
|
|
|
|
return true;
|
|
}
|
|
|
|
// =========================================================
|
|
// STATUS
|
|
// =========================================================
|
|
|
|
status(customerId) {
|
|
const customer = this.cache.get(customerId);
|
|
|
|
return {
|
|
customerId,
|
|
features: customer ? [...customer.keys()] : []
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = VaultifyManager; |