auth bugfix + licences

This commit is contained in:
2026-04-25 17:56:06 +00:00
parent 84c3d9f9ba
commit e8b5c39a80
11 changed files with 362 additions and 67 deletions

View File

@@ -135,7 +135,7 @@ async resolvePermissions(objectGuid) {
sAMAccountName: user.sAMAccountName
},
this.SECRET_KEY,
{ expiresIn: '10s' }
{ expiresIn: '1y' }
);
user.refreshtoken = token;
@@ -197,7 +197,17 @@ async resolvePermissions(objectGuid) {
authenticate() {
return async (req, res, next) => {
try {
// 🔥 SKIP PUBLIC ROUTES
if (
req.path.startsWith('/login') ||
req.path.startsWith('/public')
) {
return next();
}
const sAMAccountName = req.cookies?.sAMAccountName;
if (!sAMAccountName) {
@@ -206,23 +216,30 @@ async resolvePermissions(objectGuid) {
const user = await this.findUser(sAMAccountName);
if (!user || !user.refreshtoken || !user.active) {
if (!user || !user.active) {
return res.redirect('/login');
}
let payload;
try {
payload = jwt.verify(user.refreshtoken, this.SECRET_KEY);
} catch {
return res.redirect('/login');
}
// jwt.verify(user.refreshtoken, this.SECRET_KEY);
this.verifyUserToken(sAMAccountName)
// 🔥 LIVE RBAC RESOLUTION (bei JEDEM REQUEST)
const rbac = await this.resolvePermissions(user.ObjectGUID);
req.user = {
...user.toJSON(),
jwt: payload,
groups: rbac.groups,
roles: rbac.roles,
permissions: rbac.permissions
};
console.log(req.user)
next();
} catch (err) {
console.error(err);
return res.redirect('/login');

View File

@@ -0,0 +1,199 @@
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 data = {
Customer_ID: record.Customer_ID,
Feature: record.Feature,
Payload: this.parsePayload(record.Payload),
ExpiresAt: record.ExpiresAt
};
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(JSON.stringify(data));
verifier.end();
return verifier.verify(
this.publicKey,
record.Signature,
'base64'
);
} 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;