1
0
forked from apxtri/apxtri

add msg in case unconsistent key

This commit is contained in:
philc 2023-12-29 13:38:47 +01:00
parent ed8a747b0b
commit c5899f21e4
15 changed files with 6548 additions and 3483 deletions

View File

@ -8,6 +8,10 @@ const cors = require("cors");
const express = require("express"); const express = require("express");
const process = require("process"); const process = require("process");
const l=require('./tools/log.js');
l.showlog= true; // force log as well in prod and dev
l.context="apxtri";
/******************************************* /*******************************************
SEE https://gitea.ndda.fr/apxtri/apxtri/wiki/Devrules SEE https://gitea.ndda.fr/apxtri/apxtri/wiki/Devrules
@ -42,9 +46,8 @@ To share configuration :
// check nginx exist // check nginx exist
if (!fs.existsSync("/etc/nginx/nginx.conf")) { if (!fs.existsSync("/etc/nginx/nginx.conf")) {
console.log( l.og(
"\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command." "\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command.");
);
process.exit(); process.exit();
} }
const param = {}; const param = {};
@ -54,13 +57,7 @@ argv.slice(2).forEach((arg) => {
param[kv[0]] = kv[1]; param[kv[0]] = kv[1];
} }
}); });
const log=(info,mode)=>{ if (!fs.existsSync('../conf/townconf.json')){
// with yarn dev => mode=dev show only log for dev
// to check other => node apxtrie mode:bug for example
// if mode is not define then it log it
if (!mode || param.mode==mode) console.log(info)
}
if (!fs.existsSync('..conf/townconf.json')){
// This is a first install // This is a first install
const nam= path.resolve('..').split('/').slice(-1)[0].split('-'); const nam= path.resolve('..').split('/').slice(-1)[0].split('-');
const town=nam[0] const town=nam[0]
@ -159,7 +156,7 @@ Object.keys(tribelist).forEach((t) => {
}); });
tribeIds.push(t); tribeIds.push(t);
}); });
console.log("Allowed DOMs to access to this apxtri server: ", doms); l.og("Allowed DOMs to access to this apxtri server:",JSON.stringify(doms));
const app = express(); const app = express();
// load express parameter from conf // load express parameter from conf
@ -173,7 +170,7 @@ app.use(express.json());
app.use(bodyParser.json(conf.api.bodyparse.json)); app.use(bodyParser.json(conf.api.bodyparse.json));
app.disable('x-powered-by');// for security app.disable('x-powered-by');// for security
app.locals.tribeids = tribeIds; app.locals.tribeids = tribeIds;
console.log("app.locals.tribeids", app.locals.tribeids); l.og("app.locals.tribeids", app.locals.tribeids);
// Cors management // Cors management
const corsOptions = { const corsOptions = {
origin: (origin, callback) => { origin: (origin, callback) => {
@ -186,16 +183,16 @@ const corsOptions = {
callback(null, true); callback(null, true);
} else { } else {
const rematch = /^https?:\/\/(.*):.*/g.exec(origin); const rematch = /^https?:\/\/(.*):.*/g.exec(origin);
//console.log( rematch )
let tmp = origin.replace(/http.?:\/\//g, "").split("."); let tmp = origin.replace(/http.?:\/\//g, "").split(".");
if (rematch && rematch.length > 1) tmp = rematch[1].split("."); if (rematch && rematch.length > 1) tmp = rematch[1].split(".");
//console.log( 'tmp', tmp ) //l.og( 'tmp', tmp );
let dom = tmp[tmp.length - 1]; let dom = tmp[tmp.length - 1];
if (tmp.length > 1) { if (tmp.length > 1) {
dom = `${tmp[tmp.length - 2]}.${tmp[tmp.length - 1]}`; dom = `${tmp[tmp.length - 2]}.${tmp[tmp.length - 1]}`;
} }
console.log( l.og(
`origin: ${origin}, dom:${dom}, CORS allowed? : ${doms.includes(dom)}` `origin: ${origin}, dom:${dom}, CORS allowed? : ${doms.includes(dom)}`
); );
if (doms.includes(dom)) { if (doms.includes(dom)) {
@ -218,19 +215,20 @@ app.use(cors(corsOptions));
let logroute = "Routes available on this apxtri instance:\n "; let logroute = "Routes available on this apxtri instance:\n ";
routes.forEach((r) => { routes.forEach((r) => {
try { try {
logroute += r.url.padEnd(30,' ') + r.route +"\n "; logroute += r.url.padEnd(30,' ') + r.route +"\n";
app.use(r.url, require(r.route)); app.use(r.url, require(r.route));
} catch (err) { } catch (err) {
logroute += " (err check it module.exports=router;? or ...)\n======\n "; logroute += " (err check it module.exports=router;? or ...)\n======\n ";
console.log("raise err-:", err); l.og("raise err-:", err);
} }
}); });
console.log(logroute); console.log(logroute)
if (param.mode && param.mode=='dev') { if (process.env.NODE_MODE=="dev") {
console.log( console.log(
`\x1b[42m############################################################################################\x1b[0m\n\x1b[42mThis is dev conf accessible in http://devfarm-ants to switch this as production, you must run:\n 1 - 'yarn dev nationId:ants townId:usbfarm dns:usbfarm-ants ' to conf your town and check it.\n 2 - 'yarn startpm2'\n Where:\n\x1b[42m * nationId have to exist in the nationchains\n * townId new or if exist must have the smae current dns,\n * dns domaine that has to redirect 80/443 into this server (example wall-ants.ndda.fr redirect to 213.32.65.213 ).\n Check README's project to learn more.\x1b[0m\n To work with apxweb for the front use http://defarm-ants/apxweb/www/tplname/src/index.html to use the api during dev process\n\x1b[42m############################################################################################\x1b[0m` `\x1b[42m############################################################################################\x1b[0m\n\x1b[42mThis is dev conf accessible in http://devfarm-ants to switch this as production, you must run:\n 1 - 'yarn dev nationId:ants townId:usbfarm dns:usbfarm-ants ' to conf your town and check it.\n 2 - 'yarn startpm2'\n Where:\n\x1b[42m * nationId have to exist in the nationchains\n * townId new or if exist must have the smae current dns,\n * dns domaine that has to redirect 80/443 into this server (example wall-ants.ndda.fr redirect to 213.32.65.213 ).\n Check README's project to learn more.\x1b[0m\n To work with apxweb for the front use http://defarm-ants/apxweb/www/tplname/src/index.html to use the api during dev process\n\x1b[42m############################################################################################\x1b[0m`
); );
} }
app.listen(conf.api.port, () => { app.listen(conf.api.port, () => {
let webaccess = `api waits request on `; let webaccess = `api waits request on `;
conf.dns.forEach((u) => { conf.dns.forEach((u) => {

View File

@ -1,4 +1,7 @@
const conf = require(`../../conf/townconf.json`); const conf = require(`../../conf/townconf.json`);
const l=require('../tools/log.js');
//l.showlog= true; // force log as well in prod and dev
l.context="apxtri";
/** /**
* @api {get} http://header/CheckHeaders - CheckHeaders * @api {get} http://header/CheckHeaders - CheckHeaders
* @apiGroup Middlewares * @apiGroup Middlewares
@ -67,13 +70,13 @@ const checkHeaders = (req, res, next) => {
// store in session the header information // store in session the header information
req.session.header = header; req.session.header = header;
// Each header have to be declared // Each header have to be declared
if (missingheader != "") { if (missingheader.length>0) {
// bad request // bad request
return res.status(400).json({ return res.status(400).json({
status:400, status:400,
ref: "middlewares", ref: "middlewares",
msg: "missingheader", msg: "missingheader",
data: missingheader, data: {missingheader},
}); });
} }
//console.log( req.app.locals.tribeids ) //console.log( req.app.locals.tribeids )
@ -92,7 +95,9 @@ const checkHeaders = (req, res, next) => {
}); });
} }
if (!conf.api.languages.includes(header.xlang)) { if (!conf.api.languages.includes(header.xlang)) {
console.log("warning language requested does not exist force to english"); const info="warning language requested does not exist force to english";
l.og(info);
l.ogprod(req.header("xtribe"),info);
header.xlang = "en"; header.xlang = "en";
} }
//set anonymous profil //set anonymous profil

View File

@ -6,6 +6,9 @@ const glob = require("glob");
// const openpgp = require("/media/phil/usbfarm/apxtri/node_modules/openpgp/dist/node/openpgp.js"); // const openpgp = require("/media/phil/usbfarm/apxtri/node_modules/openpgp/dist/node/openpgp.js");
const openpgp = require("openpgp"); const openpgp = require("openpgp");
const l=require('../tools/log.js');
l.showlog= false; // force log as well in prod and dev
l.context="isAuthenticated";
/** /**
* @api {get} http://header/istauthenticated - isAuthenticated * @api {get} http://header/istauthenticated - isAuthenticated
* @apiGroup Middlewares * @apiGroup Middlewares
@ -28,21 +31,18 @@ const openpgp = require("openpgp");
* *
**/ **/
const isAuthenticated = async (req, res, next) => { const isAuthenticated = async (req, res, next) => {
/*console.log('PASSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS') /*
console.log(__dirname) console.log(__dirname)
console.log(path.resolve('../tmp/tokens')) console.log(path.resolve('../tmp/tokens'))
if (fs.existsSync('../tmp/tokens')) console.log('pass A') if (fs.existsSync('../tmp/tokens')) console.log('pass A')
if (fs.existsSync('../tmp/tokens')) console.log('pass B') if (fs.existsSync('../tmp/tokens')) console.log('pass B')
*/ */
const withlog = true;
const currentday = dayjs().date(); const currentday = dayjs().date();
fs.ensureDirSync(`../tmp/tokens`); fs.ensureDirSync(`../tmp/tokens`);
let menagedone = fs.existsSync( let menagedone = fs.existsSync(
`../tmp/tokens/menagedone${currentday}` `../tmp/tokens/menagedone${currentday}`
); );
l.og(`menagedone${currentday} was it done today?:${menagedone}`);
if (withlog)
console.log(`menagedone${currentday} was it done today?:${menagedone}`);
if (!menagedone) { if (!menagedone) {
// clean oldest // clean oldest
const tsday = dayjs().valueOf(); // now in timestamp format const tsday = dayjs().valueOf(); // now in timestamp format
@ -57,13 +57,17 @@ const isAuthenticated = async (req, res, next) => {
fs.remove(f); fs.remove(f);
} }
}); });
//clean tmp
glob.sync(`../tmp/*.txt`).forEach((f) => {
fs.remove(f);
});
fs.outputFile( fs.outputFile(
`../tmp/tokens/menagedone${currentday}`, `../tmp/tokens/menagedone${currentday}`,
"done by middleware/isAUthenticated" "done by middleware/isAUthenticated"
); );
} }
//Check register in tmp/tokens/ //Check register in tmp/tokens/
if (withlog) console.log("isAuthenticate?", req.session.header, req.body); l.og("isAuthenticate?", req.session.header, req.body);
const resnotauth = { const resnotauth = {
ref: "middlewares", ref: "middlewares",
@ -77,7 +81,7 @@ const isAuthenticated = async (req, res, next) => {
req.session.header.xalias == "anonymous" || req.session.header.xalias == "anonymous" ||
req.session.header.xhash == "anonymous" req.session.header.xhash == "anonymous"
) { ) {
if (withlog) console.log("alias anonymous means not auth"); l.og("alias anonymous means not auth");
resnotauth.status = 401; resnotauth.status = 401;
return res.status(resnotauth.status).json(resnotauth); return res.status(resnotauth.status).json(resnotauth);
} }
@ -96,7 +100,7 @@ const isAuthenticated = async (req, res, next) => {
const failstamp = `../tmp/tokens/${alias}.json`; const failstamp = `../tmp/tokens/${alias}.json`;
if (action == "clean") { if (action == "clean") {
//to reinit bruteforce checker //to reinit bruteforce checker
if (withlog) console.log("try to clean penalty file ", failstamp); l.og("try to clean penalty file ", failstamp);
fs.remove(failstamp, (err) => { fs.remove(failstamp, (err) => {
if (err) console.log("Check forcebrut ", err); if (err) console.log("Check forcebrut ", err);
}); });
@ -107,17 +111,17 @@ const isAuthenticated = async (req, res, next) => {
stamp.lastfail = dayjs().format(); stamp.lastfail = dayjs().format();
stamp.numberfail += 1; stamp.numberfail += 1;
fs.outputJSON(failstamp, stamp); fs.outputJSON(failstamp, stamp);
if (withlog) console.log("penalty:", stamp); l.og("penalty:", stamp);
await sleep(stamp.numberfail * 100); //increase of 0,1 second the answer time per fail await sleep(stamp.numberfail * 100); //increase of 0,1 second the answer time per fail
if (withlog) console.log("time out penalty"); l.og("time out penalty");
} }
}; };
if (!fs.existsSync(tmpfs)) { if (!fs.existsSync(tmpfs)) {
// need to check detached sign // need to check detached sign
let publickey = ""; let publickey = "";
console.log(process.cwd()); l.og(process.cwd());
console.log(process.env.PWD); l.og(process.env.PWD);
console.log(__dirname); l.og(__dirname);
const aliasinfo = `../nationchains/pagans/itm/${req.session.header.xalias}.json`; const aliasinfo = `../nationchains/pagans/itm/${req.session.header.xalias}.json`;
if (fs.existsSync(aliasinfo)) { if (fs.existsSync(aliasinfo)) {
publickey = fs.readJsonSync(aliasinfo).publickey; publickey = fs.readJsonSync(aliasinfo).publickey;
@ -126,38 +130,41 @@ const isAuthenticated = async (req, res, next) => {
publickey = req.body.publickey; publickey = req.body.publickey;
} }
if (publickey == "") { if (publickey == "") {
if (withlog) console.log("alias unknown"); l.og("alias unknown");
resnotauth.status = 404; resnotauth.status = 404;
resnotauth.data.xaliasexists = false; resnotauth.data.xaliasexists = false;
return res.status(resnotauth.status).send(resnotauth); return res.status(resnotauth.status).send(resnotauth);
} }
if (withlog) console.log("publickey", publickey); l.og("publickey", publickey);
if (publickey.substring(0, 31) !== "-----BEGIN PGP PUBLIC KEY BLOCK") { if (publickey.substring(0, 31) !== "-----BEGIN PGP PUBLIC KEY BLOCK") {
if (withlog) l.og("Publickey is not valid as armored key:", publickey);
console.log("Publickey is not valid as armored key:", publickey);
await bruteforcepenalty(req.session.header.xalias, "penalty"); await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 404; resnotauth.status = 404;
return res.status(resnotauth.status).send(resnotauth); return res.status(resnotauth.status).send(resnotauth);
} }
const clearmsg = Buffer.from(req.session.header.xhash, "base64").toString(); const clearmsg = Buffer.from(req.session.header.xhash, "base64").toString();
if (clearmsg.substring(0, 10) !== "-----BEGIN") { if (clearmsg.substring(0, 10) !== "-----BEGIN") {
if (withlog) l.og("xhash conv is not valid as armored key:", clearmsg);
console.log("xhash conv is not valid as armored key:", clearmsg);
await bruteforcepenalty(req.session.header.xalias, "penalty"); await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 404; resnotauth.status = 404;
return res.status(resnotauth.status).send(resnotauth); return res.status(resnotauth.status).send(resnotauth);
} }
if (withlog) console.log("clearmsg", clearmsg); l.og("clearmsg", clearmsg);
let signedMessage=""
const pubkey = await openpgp.readKey({ armoredKey: publickey }); const pubkey = await openpgp.readKey({ armoredKey: publickey });
const signedMessage = await openpgp.readCleartextMessage({ try{
signedMessage = await openpgp.readCleartextMessage({
cleartextMessage: clearmsg, cleartextMessage: clearmsg,
}); });
}catch(err){
return res.status(422).send({status:422,ref:"Middleware",msg:"unconsistentcleartextmessage",data:{xhash:req.session.header.xhash,clearmsg}})
}
const verificationResult = await openpgp.verify({ const verificationResult = await openpgp.verify({
message: signedMessage, message: signedMessage,
verificationKeys: pubkey, verificationKeys: pubkey,
}); });
if (withlog) console.log(verificationResult); l.og(verificationResult);
if (withlog) console.log(verificationResult.signatures[0].keyID.toHex()); l.og(verificationResult.signatures[0].keyID.toHex());
try { try {
await verificationResult.signatures[0].verified; await verificationResult.signatures[0].verified;
if ( if (
@ -165,8 +172,7 @@ const isAuthenticated = async (req, res, next) => {
`${req.session.header.xalias}_${req.session.header.xdays}` `${req.session.header.xalias}_${req.session.header.xdays}`
) { ) {
resnotauth.msg = "signaturefailled"; resnotauth.msg = "signaturefailled";
if (withlog) l.og(
console.log(
`message recu:${verificationResult.data} , message attendu:${req.session.header.xalias}_${req.session.header.xdays}` `message recu:${verificationResult.data} , message attendu:${req.session.header.xalias}_${req.session.header.xdays}`
); );
await bruteforcepenalty(req.session.header.xalias, "penalty"); await bruteforcepenalty(req.session.header.xalias, "penalty");
@ -175,20 +181,18 @@ const isAuthenticated = async (req, res, next) => {
} }
} catch (e) { } catch (e) {
resnotauth.msg = "signaturefailled"; resnotauth.msg = "signaturefailled";
if (withlog) console.log("erreur", e); l.og("erreur", e);
await bruteforcepenalty(req.session.header.xalias, "penalty"); await bruteforcepenalty(req.session.header.xalias, "penalty");
resnotauth.status = 401; resnotauth.status = 401;
return res.status(resnotauth.status).send(resnotauth); return res.status(resnotauth.status).send(resnotauth);
} }
// authenticated then get person profils (person = pagan for a xtrib) // authenticated then get person profils (person = pagan for a xtrib)
const person = `${process.env.dirtown}/tribes/${req.session.header.xtribe}/objects/persons/itm/${req.session.header.xalias}.json`; const person = `${process.env.dirtown}/tribes/${req.session.header.xtribe}/objects/persons/itm/${req.session.header.xalias}.json`;
if (withlog) { l.og("Profils tribe/app management");
console.log("Profils tribe/app management"); l.og("person", person);
console.log("person", person);
}
if (fs.existsSync(person)) { if (fs.existsSync(person)) {
const infoperson = fs.readJSONSync(person); const infoperson = fs.readJSONSync(person);
console.log(infoperson); l.og(infoperson);
infoperson.profils.forEach((p) => { infoperson.profils.forEach((p) => {
if (!req.session.header.xprofils.includes(p)) req.session.header.xprofils.push(p); if (!req.session.header.xprofils.includes(p)) req.session.header.xprofils.push(p);
}) })
@ -201,7 +205,7 @@ const isAuthenticated = async (req, res, next) => {
req.session.header.xprofils = fs.readJSONSync(tmpfs); req.session.header.xprofils = fs.readJSONSync(tmpfs);
} }
bruteforcepenalty(req.session.header.xalias, "clean"); bruteforcepenalty(req.session.header.xalias, "clean");
console.log(`${req.session.header.xalias} Authenticated`); l.og(`${req.session.header.xalias} Authenticated`);
next(); next();
}; };
module.exports = isAuthenticated; module.exports = isAuthenticated;

View File

@ -24,6 +24,48 @@ Notifications.get = (alias, tribeId) => {
}; };
}; };
/**
* Register an typekey(email) or (phone) key into mailinglist for a tribe
*
*/
Notifications.registertolist = (key, typekey, tribe, mlist, srckey, uuid) => {
key = key.toLowerCase();
if (!Checkjson.testformat(key, typekey))
return {
status: 400,
ref: "Notifications",
msg: "formaterreur",
data: { fielderr: typekey, format: typekey },
};
const destin = `../../../nationchains/tribes/${tribe}/contacts/${typecontact}_${mlist}.json`;
const filestorage = fs.existsSync(destin) ? fs.readJsonSync(destin) : {};
if (filestorage[key]) {
filestorage[key].dt_update = dayjs().toISOString();
if (!filestorage[key].srckeys.includes(srckey))
filestorage[key].srckeys.push(srckey);
if (!filestorage[key].uuids.includes(uuid))
filestorage[key].uuids.push(uuid);
}else{
filestorage[key].dt_create=dayjs().toISOString();
filestorage[key].srckeys=[srckey];
filestorage[key].uuids=[uuid]
}
fs.outputJSONSync(destin, filestorage);
return {status:200,ref:"Notifications",msg:"register",data:{key, typekey, tribe, mlist, srckey, uuid}}
}
/**
* Unsubscribe an eamil or phone from a mailinglist for a tribe
*/
Notifications.unsubscribefromlist = (body, header) => {
}
/**
* Message to send to an alias from an anonymous or not
*/
Notifications.sendcontact = (body, header) => {
}
Notifications.sendsms = async (data, tribeId) => { Notifications.sendsms = async (data, tribeId) => {
/** /**
* Never use need wallet in mailjet to test * Never use need wallet in mailjet to test
@ -181,12 +223,12 @@ Notifications.sendmail = async (data, tribe) => {
const transporter = await nodemailer.createTransport(confsmtp); const transporter = await nodemailer.createTransport(confsmtp);
//@todo add attachments management //@todo add attachments management
if (data.filelist) { if (data.filelist) {
data.attachments=[]; data.attachments = [];
let missingfile = []; let missingfile = [];
data.filelist.forEach((fo) => { data.filelist.forEach((fo) => {
if (fs.existsSync(fo.pathfile)){ if (fs.existsSync(fo.pathfile)) {
}else{ missingfile.push(fo.pathfile);} } else { missingfile.push(fo.pathfile); }
}); });
if (missingfile.lenght > 0) if (missingfile.lenght > 0)
return { return {

View File

@ -299,15 +299,15 @@ Odmdb.r = (objectPathname, apxid, role) => {
return { return {
status: 403, status: 403,
ref: "Odmdb", ref: "Odmdb",
msg: "forbidden", msg: "profilnotallow",
data: { person: apxid }, data: { person: apxid, },
}; };
} }
const data = {}; const data = {};
accessright.R.forEach((p) => { accessright.R.forEach((p) => {
data[p] = itm[p]; data[p] = itm[p];
}); });
return { status: 200, ref: "Odmdb", msg: "found", data }; return { status: 200, ref: "Odmdb", msg: "profilallow", data };
}; };
/** /**
@ -336,7 +336,7 @@ Odmdb.ASUPreads = (objectPathname, apxidlist, role, propertiesfilter) => {
return { return {
status: 403, status: 403,
ref: "Odmdb", ref: "Odmdb",
msg: "accessforbidden", msg: "profilnotallow",
data: { crud: "R", accessright }, data: { crud: "R", accessright },
}; };
} }
@ -492,7 +492,7 @@ Odmdb.cud = (objectPathname, crud, itm, role, runindex = true) => {
return { return {
status: 403, status: 403,
ref: "Odmdb", ref: "Odmdb",
msg: "accessforbidden", msg: "profilnotallow",
data: { crud, accessright }, data: { crud, accessright },
}; };
} }

View File

@ -1,5 +1,20 @@
{ {
"schemanotfound":"Schema not found in {{{fullpath}}}", "alreadyexist": "An {{objectname}} object with the {{key}} key already exists with {{val}}",
"pathnamedoesnotexist":"ObjectPath or objectName does not exist {{{indexpath}}}", "doesnotexist": "The {{objectname}} object does not exist with {{key}}:{{val}}",
"objectfiledoesnotexist":"Requested index does not exist here: {{{objectpath}}}" "getschema": "Schema {{{conf.name}}}",
} "schemanotfound": "Schema not found in {{{schemaPath}}}",
"pathnamedoesnotexist": "The directory does not exist {{{indexpath}}}",
"objectfiledoesnotexist": "The file does not exist {{{objectpath}}}",
"cudsuccessfull": "Update successful",
"successfulcreatewithoutemail":"Create without sending an email",
"successfulcreatewitemail":"Create you will receive an email",
"missingprimarykey": "Missing a primary key apxid to store and identify objects",
"unconsistencyapxidx": "The index {{name}} must contain at least {{apxid}} in objkey because keyval is not unique",
"profilnotallow": "As {{person}} your profiles are not allow, this action is not authorized",
"profilallow": "You are allowed for this action",
"successreindex": "Object reindexed from items, your indexes are up to date",
"indexexist":"The index exists",
"typenotavailable":"The type: {{type}} for the propertie: {{propertie}} of the object:{{objectPathname}} is not taken into account for indexing",
"objectslist":"List of objects from apxtri and {{tribe}}",
"errordelete":"Sorry, unable to delete this account"
}

View File

@ -10,7 +10,8 @@
"successfulcreatewitemail":"Créer vous allez recevoir un email", "successfulcreatewitemail":"Créer vous allez recevoir un email",
"missingprimarykey": "Il manque une clé primaire apxid pour stocker et identifier les objects", "missingprimarykey": "Il manque une clé primaire apxid pour stocker et identifier les objects",
"unconsistencyapxidx": "L'index {{name}} doit contenir en objkey au moins {{apxid}} car keyval n'est pas unique", "unconsistencyapxidx": "L'index {{name}} doit contenir en objkey au moins {{apxid}} car keyval n'est pas unique",
"profilnotallow": "Vous n'avez pas le profil de {{profils}}, cette action n'est pas authorisée", "profilnotallow": "Vous n'avez pas le profil pour faire cette action.",
"profilallow": "Vous avez été authorisé à faire cette action",
"successreindex": "Objet reindexé à partir des items, vos index sont à jour", "successreindex": "Objet reindexé à partir des items, vos index sont à jour",
"indexexist":"L'indexe existe", "indexexist":"L'indexe existe",
"typenotavailable":"Le type: {{type}} pour la propertie : {{propertie}} de l'object :{{objectPathname}} n'est pas pris en compte pour l'indexation", "typenotavailable":"Le type: {{type}} pour la propertie : {{propertie}} de l'object :{{objectPathname}} n'est pas pris en compte pour l'indexation",

View File

@ -1,8 +1,10 @@
{ {
"errrequest": "Backend seems not available", "errrequest": "Backend seems not available",
"missingheader": "Some header miss to have a valid request: {{#data}} {{.}} {{/data}}", "unconsistentpgp": "Your keys are not consistent {{err}}",
"tribeiddoesnotexist": "Header xtribe: {{data.xtribe}} does not exist in this town you cannot access", "missingheader": "Some header miss to have a valid request: {{#missinghearder}} {{.}} {{/missingheader}}",
"authenticated": "Your alias{{{data.xalias}}} is authenticated", "tribeiddoesnotexist": "Header xtribe: {{xtribe}} does not exist in this town you cannot access",
"notauthenticated": "Your alias: {{data.xalias}} is not authenticated {{^data.aliasexists}} and this alias does not exist !{{/data.aliasexists}}", "unconsistentcleartextmessage":"Your xhash: {{xhash}} is not consistent as a clear msg {{{clearmsg}}}",
"forbiddenAccessright": "Alias {{data.xalias}} has not access right to act {{data.action}} onto object {{data.object}} for tribe {{mor.xworkon}}" "authenticated": "Your alias: {{{xalias}}} is authenticated",
"notauthenticated": "Your alias: {{xalias}} is not authenticated {{^xaliasexists}} and this alias does not exist !{{/xaliasexists}}",
"signaturefailled": "Your signature is not valid for your alias."
} }

View File

@ -1,10 +1,10 @@
{ {
"errrequest": "Le serveur ne semble pas répondre", "errrequest": "Le serveur ne semble pas répondre",
"unconsistentpgp": "Vos clés ne sont pas conforme {{err}}", "unconsistentpgp": "Vos clés ne sont pas conforme {{err}}",
"missingheader": "Certains en-têtes manquent pour avoir une requête valide : {{#data}} {{.}} {{/data}}", "missingheader": "Certains en-têtes manquent pour avoir une requête valide : {{#missinghearder}} {{.}} {{/missingheader}}",
"tribeiddoesnotexist": "L'en-tête xtribe : {{data.xtribe}} n'existe pas dans cette ville, vous ne pouvez pas y accéder", "tribeiddoesnotexist": "L'en-tête xtribe : {{xtribe}} n'existe pas dans cette ville, vous ne pouvez pas y accéder",
"authenticated": "Votre alias {{{data.xalias}}} est authentifié", "unconsistentcleartextmessage":"Votre xhash: {{xhash}} n'est pas lisible par openpgp.js avec le clearmsg: {{{clearmsg}}}",
"notauthenticated": "Votre alias : {{data.xalias}} n'est pas authentifié {{^data.aliasexists}} et cet alias n'existe pas !{{/data.aliasexists}}", "authenticated": "Votre alias {{{xalias}}} est authentifié",
"forbiddenAccessright": "L'alias {{data.xalias}} n'a pas le droit d'agir {{data.action}} sur l'objet {{data.object}} pour la tribu {{mor.xworkon}}", "notauthenticated": "Votre alias : {{xalias}} n'est pas authentifié {{^xaliasexists}} et cet alias n'existe pas !{{/xaliasexists}}",
"signaturefailled": "Desolé votre signature n'est pas valide pour cet alias." "signaturefailled": "Desolé votre signature n'est pas valide pour cet alias."
} }

6240
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,43 +12,19 @@
}, },
"scripts": { "scripts": {
"stoppm2": "pm2 stop apxtri.js", "stoppm2": "pm2 stop apxtri.js",
"startpm2": "pm2 start apxtri.js --log-date-format 'DD-MM HH:mm:ss.SSS'", "startpm2": "pm2 start apxtri.js mode:prod --log-date-format 'DD-MM HH:mm:ss.SSS'",
"deletepm2": "pm2 delete apxtri", "deletepm2": "pm2 delete apxtri",
"restartpm2": "pm2 restart apxtri.js --log-date-format 'DD-MM HH:mm:ss.SSS'", "restartpm2": "pm2 restart apxtri.js --log-date-format 'DD-MM HH:mm:ss.SSS'",
"startblockchain": "pm2 start models/Blockchains.js --log-date-format 'DD-MM HH:mm:ss:SSS'", "startblockchain": "pm2 start models/Blockchains.js --log-date-format 'DD-MM HH:mm:ss:SSS'",
"logpm2": "pm2 logs apxtri.js --lines 200", "logpm2": "pm2 logs apxtri.js --lines 200",
"dev": "node apxtri.js mode:dev", "dev": "NODE_MODE=dev node apxtri.js",
"unittest": "node unittest.js", "unittest": "node unittest.js",
"apidoc": "apidoc -i middlewares routes -o ../nationchains/tribes/adminapi/www/cdn/apidoc", "apidoc": "apidoc -c ../conf/apidoc/apidoc_$tribe.json -o ../nationchains/tribes/$tribe/www/cdn/apidoc/",
"apidocsmatchit": "apidoc -i ../nationchains/tribes/smatchit/api/middlewares ../nationchains/tribes/smatchit/api/routes -o ../nationchains/tribes/smatchit/www/apidoc" "publishtestwall": "scp -r /media/phil/usbfarm/apxtowns/dev-ants/nationchains/tribes/$space phil@wall-ants://home/phil/apxtowns/testwall-ants/nationchains/tribes/$space/..",
}, "publishwall": "scp -r /media/phil/usbfarm/apxtowns/dev-ants/nationchains/tribes/$space phil@wall-ants://home/phil/apxtowns/wall-ants/nationchains/tribes/$space/..",
"apidoc": { "publishhome": "scp -r /media/phil/usbfarm/apxtowns/dev-ants/nationchains/tribes/$space phil@wall-ants://home/phil/apxtowns/wall-ants/nationchains/tribes/$space/.."
"name": "apxtri",
"version": "1.0.0",
"title": "apiDoc for apxtri",
"description": "Core api documentation",
"url": "https://town.dns/api",
"order": [
"Middlewares",
"Odmdb",
"Nationchains"
],
"template": {
"forceLanguage": "en",
"showRequiredLabels": true,
"withCompare": true,
"withGenerator": true,
"aloneDisplay": false
},
"header": {
"title": "Introduction",
"filename": "header.md"
},
"footer": {
"title": "Best practices",
"filename": "footer.md"
}
}, },
"commentscript": " tribe=tribeid yarn apidoc to build apidoc // space=adminapi/www/cdn/apidoc yarn publishtestwall ",
"maintainers": [ "maintainers": [
{ {
"name": "Philippe Colzy", "name": "Philippe Colzy",
@ -83,7 +59,7 @@
"@editorjs/editorjs": "^2.26.5", "@editorjs/editorjs": "^2.26.5",
"apidoc": "^0.54.0", "apidoc": "^0.54.0",
"async": "^3.2.0", "async": "^3.2.0",
"axios": "^0.21.1", "axios": "^1.6.2",
"baunsu": "^0.2.3", "baunsu": "^0.2.3",
"bcrypt": "^5.0.0", "bcrypt": "^5.0.0",
"cors": "^2.8.4", "cors": "^2.8.4",
@ -102,11 +78,11 @@
"nodemailer": "^6.9.7", "nodemailer": "^6.9.7",
"openpgp": "^5.10.1", "openpgp": "^5.10.1",
"path": "^0.12.7", "path": "^0.12.7",
"pm2": "^5.3.0 ", "pm2": "^2.10.4",
"readline-sync": "^1.4.10", "readline-sync": "^1.4.10",
"smtp-client": "^0.4.0", "smtp-client": "^0.4.0",
"stripe": "^14.4.0", "stripe": "^14.4.0",
"uuid": "^9.0.0" "uuid": "^9.0.0",
}, "yarn": "^1.22.21"
"devDependencies": {} }
} }

View File

@ -8,11 +8,28 @@ const isAuthenticated = require("../middlewares/isAuthenticated");
const router = express.Router(); const router = express.Router();
/**
* wait Sagar feedback for language and label description
* @ api {post} /api/notifications/backend - Notification Backend post
* @apiName notifBackend
* @apiDescription Send an api result {status,ref,msg,data} to get personnalize notification by header.xlang abd by data
* @apiGroup Notification
*
* @apiBody {integer} status an http status
* @apiBody {string} ref an existing model name
* @apiBody {string} msg a key word existing in referentiual
* */
router.post("/backend", (req, res) => {
})
/** /**
* @api {get} /notifications/:alias/:tribeId * @api {get} /notifications/:alias/:tribeId
* @apiName notiflist * @apiName notiflist
* @apiDescription Get list of notifications for an alias and a tribe * @apiDescription Get list of notifications for an alias and a tribe
* @apiGroup Notification * @apiGroup Notifications
* *
* @apiParam {string} alias * @apiParam {string} alias
* @apiParam {string} tribeId * @apiParam {string} tribeId
@ -20,11 +37,73 @@ const router = express.Router();
* @apiSuccessExample {json} Success-Response: * @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK * HTTP/1.1 200 OK
* {status:200,ref:"Notification",msg:"Messagelist",data:{notif:[{tribeId,msg:{from(email or uuid or alias):[{dt,msg}]}}]} * {status:200,ref:"Notification",msg:"Messagelist",data:{notif:[{tribeId,msg:{from(email or uuid or alias):[{dt,msg}]}}]}
* * bouture
**/ **/
router.get("/:alias/:tribeId", (req, res) => { router.get("/:alias/:tribeId", (req, res) => {
const getnot = Notification.get(req.params.alias,req.params.tribeId); const getnot = Notification.get(req.params.alias, req.params.tribeId);
res.status(getalias.status).send(getalias); res.status(getalias.status).send(getalias);
}); });
module.exports=router; router.post("/registeranonymous", checkHeaders, (req, res) => {
['uuid','srckey','mlist'].forEach(k=>{
if (!req.body[k]){}
});
res.status().json({status:410, ref:"", msg:""})
let result;
if (req.body.email) {
result= Notifications.registertolist(
req.body.email,
"email",
req.session.header.xtribe,
req.body.mlist,
req.body.srckey,
req.body.uuid);
}
res(200).json({status:200})
})
/**
* @api {POST} /actions/contactanonymous -Contact anonymous
* @apiName contactanonymous
* @apiGroup Notifications
* @apiDescription Run action store in body.order and update mailinglist or create contact message
*
* @apiBody {string} order name of function for action in Actions.js example:registercontact ,
* @apiBody {string} srckey: where it come from and eventualy email template name to use to send email ,
* @apiBody {string} email to use
* @apiBody {string} route /actions/contactanonymous
* @apiBody {string} [mlist] filename to store email registration /contacts/mlist.json if not => filename is quest_/contacts/email.json with {email:{dt_create,dt_update, src:[list of source]}} or add message in /contacts/quest_{email}.json {timestamp:{message,name,email,dt_create,emailcontact}}
* @apiBody {string} others any other usefull key:value relevant for order action
*
* @apiSuccess {object} update/contacts/{mlist}.json successfull
* @apiSuccessExample {json} successfullmessage
* HTTP/1.1 200 OK
* {"status":200, "ref":"Contact", "msg":"success", "data":{"indexlist":[]}}
*
*/
router.post("/contactanonymous", checkHeaders, async (req, res) => {
const done = Actions[req.body.order]
? await Actions[req.body.order](req.body, req.session.header)
: { status: 406, ref: "Actions", msg: "bodyerror", data: req.body };
//console.log('routes contactanonymous ', req.body);
res.status(done.status).json(done);
});
/**
* Same as /copntactanonymous but for authenticated user => data are updated in persons/itm/alias.json
*/
router.post("/contact", checkHeaders, isAuthenticated, (req, res) => {
const done = Actions[req.body.order]
? Actions[req.body.order](req.body, req.session.header)
: { status: 406, ref: "Actions", msg: "bodyerror", data: req.body };
console.log(req.body);
res.status(done.status).json(done);
});
router.get("/contact", checkHeaders, isAuthenticated, (req, res) => {
res.status(200).json({ data: {} });
});
module.exports = router;

View File

@ -1,5 +1,6 @@
const express = require("express"); const express = require("express");
const fs = require("fs-extra"); const fs = require("fs-extra");
const dayjs=require('dayjs');
const path = require("path"); const path = require("path");
// Classes // Classes
const Pagans = require("../models/Pagans.js"); const Pagans = require("../models/Pagans.js");
@ -41,11 +42,10 @@ router.get("/alias/:alias", (req, res) => {
res.status(getalias.status).send(getalias); res.status(getalias.status).send(getalias);
}); });
/** /**
* Remove serveur token
* @api {get} /pagans/logout - pagan Logout * @api {get} /pagans/logout - pagan Logout
* @apiName Removetoken * @apiName Removetoken
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription Remove token * @apiDescription Remove server's token only the owner of the token (no one else can delete a token )
* *
* @apiSuccess {object} indexfile content * @apiSuccess {object} indexfile content
* @apiSuccessExample {json} Success-Response: * @apiSuccessExample {json} Success-Response:
@ -97,7 +97,8 @@ router.get("/isauth", checkHeaders, isAuthenticated, (req, res) => {
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription * @apiDescription
* Create a pagan account from alias, publickey, if trusted recovery => * Create a pagan account from alias, publickey, if trusted recovery =>
* Create a person in xtribe/person/xalias.json with profil.auth={email,privatekey, passphrase} * If trustedtribe is true then create a person in xtribe/person/xalias.json with profil.auth={email,privatekey, passphrase}.
*
* Middleware isAuthenticated check that: * Middleware isAuthenticated check that:
* - xhash is well signed from private key linked to the publickey of alias * - xhash is well signed from private key linked to the publickey of alias
* - check that alias does not already exist (if yes then verifiedsigne would be false) * - check that alias does not already exist (if yes then verifiedsigne would be false)
@ -107,7 +108,8 @@ router.get("/isauth", checkHeaders, isAuthenticated, (req, res) => {
* @apiBody {string} [email] if specified then an email is sent to it with public and privatekey * @apiBody {string} [email] if specified then an email is sent to it with public and privatekey
* @apiBody {string} [privatekey] * @apiBody {string} [privatekey]
* @apiBody {string} [passphrase] if not specidied => passphrase="" * @apiBody {string} [passphrase] if not specidied => passphrase=""
* @apiBody {string} [trustedtribe] the tribename if not specified then the process will only create a pagan identity, else an item person is create for trustedtribe (that must exist with profil 'person'). To create a person with an existing pagan identity use put /api/person/:alias after authenticated you (headers). In case a person is created then we use all valid other apiBody respecting rules https://smatchit.io/api/odmdb/schema/persons.json * @apiBody {string} [trustedtribe] the tribename if not specified then the process will only create a pagan identity, else an item person is create for trustedtribe (that must exist with profil 'person'). To create a person with an existing pagan identity use put /api/person/:alias after authenticated you (headers). In case a person is created then we use all valid other apiBody respecting the persons schema (see put persons)
* @apiBody {object} schema:pagans <a href='https://wall-ants.ndda.fr/nationchains/schema/pagans.json' target='_blank'>https://wall-ants.ndda.fr/nationchains/schema/pagans.json</a>
* *
* @apiError {json} objectNotfound the file does not exist * @apiError {json} objectNotfound the file does not exist
* @apiErrorExample {json} * @apiErrorExample {json}
@ -121,7 +123,7 @@ router.get("/isauth", checkHeaders, isAuthenticated, (req, res) => {
* *
*/ */
router.post("/", checkHeaders, isAuthenticated, async (req, res) => { router.post("/", checkHeaders, isAuthenticated, async (req, res) => {
console.log("pass ici", req.body); log('dev',`passici ${req.body}`);
const role = { const role = {
xalias: req.session.header.xalias, xalias: req.session.header.xalias,
xprofils: req.session.header.xprofils, xprofils: req.session.header.xprofils,
@ -191,22 +193,37 @@ router.post("/", checkHeaders, isAuthenticated, async (req, res) => {
* @apiName deletepagan * @apiName deletepagan
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription * @apiDescription
* Delete an alias and his publickey, this mean that publickey disapear as well as alias. All tribe will be inform and will delete person of this alias if they have. This alias will be availlable after 1 year. * Delete an alias and his publickey, this mean that publickey disapear as well as alias. We set dt_delete
* */ * */
router.delete("/alias/:alias", checkHeaders, isAuthenticated, (req, res) => { router.delete("/alias/:alias", checkHeaders, isAuthenticated, (req, res) => {
const personpath=`../nationchains/pagans`;
const role = {
xalias: req.session.header.xalias,
xprofils: req.session.header.xprofils,
};
req.session.header.role
const delperson = Odmdb.cud(personpath,"U",{alias:req.params.alias,dt_delete:dayjs().toISOString()},role,true);
console.log(`DELETE person ${personpath}/${req.params.alias}.json `);
console.log(delperson)
res.status(delperson.status).json(delperson);
console.log(`DELETE pagans nationchains/pagans/${req.params.alias}.json`); console.log(`DELETE pagans nationchains/pagans/${req.params.alias}.json`);
const result = Pagans.deletealias(req.params.id, req.session.header); const result = Pagans.delete(req.params.alias, req.session.header);
res.status(result.status).send(result); res.status(result.status).send(result);
}); });
/** /**
* @api {delete} /pagans/person/:alias - person Delete * @api {delete} /pagans/person/:tribe/:alias - person Delete
* @apiName deleteperson * @apiName deleteperson
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription * @apiDescription
* Unsubscribe a person to a tribe => remove a person item and all data link to this alias * Unsubscribe a person to a tribe => remove a person item and all data link to this alias
* @apiHeader {array} xprofils list of profil of authenticated user
* @apiHeader {string} xalias current user
* @apiParam {string} tribe where person alias exist
* @apiParam {string} alias to delete as person
* */ * */
router.delete("/person/:alias", checkHeaders, isAuthenticated, (req, res) => { router.delete("/person/:tribe/:alias", checkHeaders, isAuthenticated, (req, res) => {
const personpath=`../nationchains/tribes/${req.session.header.xtribe}/objects/persons`; const personpath=`../nationchains/tribes/${req.params.tribe}/objects/persons`;
const role = { const role = {
xalias: req.session.header.xalias, xalias: req.session.header.xalias,
xprofils: req.session.header.xprofils, xprofils: req.session.header.xprofils,
@ -244,17 +261,19 @@ router.get("/person/:alias", checkHeaders, isAuthenticated, (req, res) => {
}); });
/** /**
* @api {put} /pagans/person - person Put * @api {put} /pagans/person/:tribe - person Put
* @apiName updateperson * @apiName updateperson
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription add or update a person = alias in a tribe. alias authenticated must have a profil with accessright into schema person. * @apiDescription add or update a person = alias in tribe. xalias authenticated (in header) must have a profil with accessright into schema person to create a person.
* @apiHeader {string} xalias * @apiHeader {string} xhash authenthicate hash with current user keys
* @apiParam {object} in line with schema in https://smatchit.io/api/odmdb/schema/persons * @apiHeader {string} xalias current user
* @apiHeader {string} xprofils profil list
* @apiParam {object} schema:persons <a href='https://smatchit.io/smatchit/schema/persons.json' target='_blank'>https://dnstribe/tribe/schema/persons.json</a>
* *
*/ */
router.put("/person", checkHeaders, isAuthenticated, (req, res) => { router.put("/person/:tribe", checkHeaders, isAuthenticated, (req, res) => {
//console.log(req.body); //console.log(req.body);
const pathobj=`../nationchains/tribes/${req.session.header.xtribe}/objects/persons`; const pathobj=`../nationchains/tribes/${req.params.tribe}/objects/persons`;
const action = (fs.existsSync(`${pathobj}/itm/${req.body.alias}.json`))? "U":"C"; const action = (fs.existsSync(`${pathobj}/itm/${req.body.alias}.json`))? "U":"C";
//set req.body to be in line with schema //set req.body to be in line with schema
if (!req.body.profils){ if (!req.body.profils){
@ -266,7 +285,7 @@ router.put("/person", checkHeaders, isAuthenticated, (req, res) => {
}); });
/** /**
* @api {get} /pagans/keyrecovery/tribe/email - recovery keys by email * @api {get} /pagans/keyrecovery/:tribe/:email - recovery keys by email
* @apiName recoveryKey * @apiName recoveryKey
* @apiGroup Pagans * @apiGroup Pagans
* @apiDescription Send mails with all registers identities (one per alias where recoveryauth.email is register) * @apiDescription Send mails with all registers identities (one per alias where recoveryauth.email is register)

29
tools/log.js Normal file
View File

@ -0,0 +1,29 @@
const fs = require("fs-extra");
const dayjs = require("dayjs");
const l = {};
l.context="";
l.showlog=(process.env.NODE_MODE=="dev");
l.og = (...infos) => {
// by default if NODE_MODE is dev => l.showlog at true
// if l.showlog is set to false then it does not output log
// l.context is a prefixe to help understanding
// usage:
// const l=require('./tools/log.js');
// l.showlog= true; // force log as well in prod and dev
// l.context="apxtri";
// l.og(stringify)
if (l.showlog) {
console.log(l.context,'-', infos);
}
};
l.ogprod = (tribe,info) => {
//store log in file /nationchains/tribes/{tribe}/logs/apxtri/apxtri_{tribe}.log
const logf = `../../nationchains/tribes/${tribe}/logs/apxtri/apxtri_${tribe}.log`;
const msg = `${days.js().toISOString()}###${tribe}###${info}`;
fs.appendFileSync(logf, msg);
console.log(msg)
};
module.exports = l;

3345
yarn.lock

File diff suppressed because it is too large Load Diff