apxtrib/api/middlewares/isAuthenticated.js

200 lines
8.0 KiB
JavaScript
Raw Permalink Normal View History

2023-04-27 04:17:20 +00:00
const fs = require("fs-extra");
const dayjs = require("dayjs");
const glob = require("glob");
2023-11-05 11:03:25 +00:00
// To debug it could be easier with source code:
// const openpgp = require("/media/phil/usbfarm/apxtrib/node_modules/openpgp/dist/node/openpgp.js");
2023-05-12 05:59:32 +00:00
const openpgp = require("openpgp");
2023-04-27 04:17:20 +00:00
2023-11-05 11:03:25 +00:00
/**
2023-12-05 06:42:35 +00:00
* @api {get} http://header/istauthenticated - isAuthenticated
2023-11-19 15:34:37 +00:00
* @apiGroup Middlewares
2023-12-05 06:42:35 +00:00
* @apiName isAuthenticated
* @apiDescription - valid if exist xalias_xdays_xhash.substr(20,200) in town/tmp/tokens/
* - if not,
* - valid if xhash signature sign xalias_xdays with alias's publickey.
* - if not valid => not allowed
* - If valid =>
* - store a xalias_xdays_xhash.substr (20,200) into /tmp/tokens with xprofils array from person.
* - update header.xprofils from this token
*
* apXtrib profils are anonymous, pagans, mayor (on a node server), druid (on a tribe like smatchit).
*
* pagan identity is independant of domain (tribe), by default profils are :['anonymous','pagans']. if this alias exist in a tribe domain as a person then his profils come from /tribes/{tribeId}/objects/person/itm/{alias}.json profils:['anonymous','pagans','person','seeker'] any profils allowed to act on tribe objects.
*
* Each profil have CRUD accessright on object managed in schema in apxaccessrights:{owner,profil:{"C":[],"R":[properties],"U":[properties],"D":[]}}, see Odmdb for details.
*
* A process run once each day to clean up all xhash tmp/tokens oldest than 24 hours.
*
2023-11-19 15:34:37 +00:00
**/
2023-05-12 05:59:32 +00:00
const isAuthenticated = async (req, res, next) => {
2023-12-05 06:42:35 +00:00
const withlog = false;
2023-04-27 04:17:20 +00:00
const currentday = dayjs().date();
2023-11-05 11:03:25 +00:00
fs.ensureDirSync(`${process.env.dirtown}/tmp/tokens`);
let menagedone = fs.existsSync(
`${process.env.dirtown}/tmp/tokens/menagedone${currentday}`
2023-04-27 04:17:20 +00:00
);
2023-11-05 11:03:25 +00:00
if (withlog)
console.log(`menagedone${currentday} was it done today?:${menagedone}`);
if (!menagedone) {
2023-04-27 04:17:20 +00:00
// clean oldest
2023-05-12 05:59:32 +00:00
const tsday = dayjs().valueOf(); // now in timestamp format
2023-11-05 11:03:25 +00:00
glob.sync(`${process.env.dirtown}/tmp/tokens/menagedone*`).forEach((f) => {
2023-04-27 04:17:20 +00:00
fs.removeSync(f);
});
2023-05-16 08:31:27 +00:00
glob.sync(`${process.env.dirtown}/tmp/tokens/*.json`).forEach((f) => {
2023-11-05 11:03:25 +00:00
const fsplit = f.split("_");
const elapse = tsday - parseInt(fsplit[2]);
//24h 86400000 milliseconde 15mn 900000
if (elapse && elapse > 86400000) {
fs.remove(f);
}
2023-04-27 04:17:20 +00:00
});
2023-11-05 11:03:25 +00:00
fs.outputFile(
`${process.env.dirtown}/tmp/tokens/menagedone${currentday}`,
"done by middleware/isAUthenticated"
);
2023-04-27 04:17:20 +00:00
}
//Check register in tmp/tokens/
2023-11-05 11:03:25 +00:00
if (withlog) console.log("isAuthenticate?", req.session.header, req.body);
2023-04-27 04:17:20 +00:00
const resnotauth = {
2023-11-05 11:03:25 +00:00
ref: "middlewares",
2023-04-27 04:17:20 +00:00
msg: "notauthenticated",
data: {
xalias: req.session.header.xalias,
2023-05-12 05:59:32 +00:00
xaliasexists: true,
2023-04-27 04:17:20 +00:00
},
};
2023-11-05 11:03:25 +00:00
if (
req.session.header.xalias == "anonymous" ||
req.session.header.xhash == "anonymous"
) {
if (withlog) console.log("alias anonymous means not auth");
resnotauth.status = 401;
return res.status(resnotauth.status).json(resnotauth);
2023-05-12 05:59:32 +00:00
}
2023-04-27 04:17:20 +00:00
2023-11-05 11:03:25 +00:00
let tmpfs = `${process.env.dirtown}/tmp/tokens/${req.session.header.xalias}_${req.session.header.xtribe}_${req.session.header.xdays}`;
//max filename in ext4: 255 characters
tmpfs += `_${req.session.header.xhash.substring(
150,
150 + tmpfs.length - 249
)}.json`;
const bruteforcepenalty = async (alias, action) => {
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const failstamp = `${process.env.dirtown}/tmp/tokens/${alias}.json`;
if (action == "clean") {
//to reinit bruteforce checker
if (withlog) console.log("try to clean penalty file ", failstamp);
fs.remove(failstamp, (err) => {
if (err) console.log("Check forcebrut ", err);
});
} else if (action == "penalty") {
const stamp = fs.existsSync(failstamp)
? fs.readJSONSync(failstamp)
: { lastfail: dayjs().format(), numberfail: 0 };
stamp.lastfail = dayjs().format();
stamp.numberfail += 1;
fs.outputJSON(failstamp, stamp);
if (withlog) console.log("penalty:", stamp);
await sleep(stamp.numberfail * 100); //increase of 0,1 second the answer time per fail
if (withlog) console.log("time out penalty");
}
};
2023-05-12 05:59:32 +00:00
if (!fs.existsSync(tmpfs)) {
// need to check detached sign
2023-11-05 11:03:25 +00:00
let publickey = "";
console.log(process.cwd());
console.log(process.env.PWD);
console.log(__dirname);
const aliasinfo = `${process.env.PWD}/nationchains/pagans/itm/${req.session.header.xalias}.json`;
if (fs.existsSync(aliasinfo)) {
publickey = fs.readJsonSync(aliasinfo).publickey;
} else if (req.body.publickey) {
2023-05-12 05:59:32 +00:00
resnotauth.data.xaliasexists = false;
2023-11-05 11:03:25 +00:00
publickey = req.body.publickey;
2023-04-27 04:17:20 +00:00
}
2023-11-05 11:03:25 +00:00
if (publickey == "") {
if (withlog) console.log("alias unknown");
resnotauth.status = 404;
resnotauth.data.xaliasexists = false;
return res.status(resnotauth.status).send(resnotauth);
2023-06-02 08:21:01 +00:00
}
2023-11-05 11:03:25 +00:00
if (withlog) console.log("publickey", publickey);
if (publickey.substring(0, 31) !== "-----BEGIN PGP PUBLIC KEY BLOCK") {
if (withlog)
console.log("Publickey is not valid as armored key:", publickey);
await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 404;
return res.status(resnotauth.status).send(resnotauth);
2023-06-02 08:21:01 +00:00
}
2023-11-05 11:03:25 +00:00
const clearmsg = Buffer.from(req.session.header.xhash, "base64").toString();
if (clearmsg.substring(0, 10) !== "-----BEGIN") {
if (withlog)
console.log("xhash conv is not valid as armored key:", clearmsg);
await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 404;
return res.status(resnotauth.status).send(resnotauth);
2023-06-02 08:21:01 +00:00
}
2023-11-05 11:03:25 +00:00
if (withlog) console.log("clearmsg", clearmsg);
const pubkey = await openpgp.readKey({ armoredKey: publickey });
const signedMessage = await openpgp.readCleartextMessage({
cleartextMessage: clearmsg,
2023-05-12 05:59:32 +00:00
});
2023-11-05 11:03:25 +00:00
const verificationResult = await openpgp.verify({
message: signedMessage,
verificationKeys: pubkey,
2023-05-12 05:59:32 +00:00
});
2023-11-05 11:03:25 +00:00
if (withlog) console.log(verificationResult);
if (withlog) console.log(verificationResult.signatures[0].keyID.toHex());
2023-05-12 05:59:32 +00:00
try {
2023-11-05 11:03:25 +00:00
await verificationResult.signatures[0].verified;
if (
verificationResult.data !=
`${req.session.header.xalias}_${req.session.header.xdays}`
) {
resnotauth.msg = "signaturefailled";
if (withlog)
console.log(
`message recu:${verificationResult.data} , message attendu:${req.session.header.xalias}_${req.session.header.xdays}`
);
await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 401;
return res.status(resnotauth.status).send(resnotauth);
}
2023-05-12 05:59:32 +00:00
} catch (e) {
2023-11-05 11:03:25 +00:00
resnotauth.msg = "signaturefailled";
if (withlog) console.log("erreur", e);
await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 401;
return res.status(resnotauth.status).send(resnotauth);
}
// authenticated then get person profils (person = pagan for a xtrib)
2023-12-05 06:42:35 +00:00
const person = `${process.env.dirtown}/tribes/${req.session.header.xtribe}/objects/persons/itm/${req.session.header.xalias}.json`;
2023-11-05 11:03:25 +00:00
if (withlog) {
console.log("Profils tribe/app management");
console.log("person", person);
}
if (fs.existsSync(person)) {
const infoperson = fs.readJSONSync(person);
console.log(infoperson);
2023-12-05 06:42:35 +00:00
infoperson.profils.forEach((p) => {
if (!req.session.header.xprofils.includes(p)) req.session.header.xprofils.push(p);
})
}else{
if (!req.session.header.xprofils.includes('pagans')) req.session.header.xprofils.push("pagans");
2023-05-12 05:59:32 +00:00
}
2023-11-05 11:03:25 +00:00
fs.outputJSONSync(tmpfs, req.session.header.xprofils);
} else {
//tmpfs exist get profils from identification process
req.session.header.xprofils = fs.readJSONSync(tmpfs);
2023-04-27 04:17:20 +00:00
}
2023-11-05 11:03:25 +00:00
bruteforcepenalty(req.session.header.xalias, "clean");
console.log(`${req.session.header.xalias} Authenticated`);
2023-05-12 05:59:32 +00:00
next();
2023-04-27 04:17:20 +00:00
};
module.exports = isAuthenticated;