diff --git a/models/Notifications.js b/models/Notifications.js
index 85031e9..97f3ef0 100644
--- a/models/Notifications.js
+++ b/models/Notifications.js
@@ -3,6 +3,7 @@ const path = require("path");
const fs = require("fs-extra");
const axios = require("axios");
const dayjs = require("dayjs");
+const Mustache = require('mustache');
const Checkjson = require(`./Checkjson.js`);
//const smtp = require("smtp-client");
const nodemailer = require("nodemailer");
@@ -212,18 +213,27 @@ Notifications.manageemail = (data, template, tribe) => {
* data optionaly can contain Cc,Bcc as array of emails and attachments as array of filename (into the server)
* Await the 1st email
*/
-
+ //console.log(data)
if (!data.emailsto || data.emailsto.length == 0) {
return {
status: 406,
ref: "Notifications",
- msg: "emailsmissing",
+ msg: "emailstomissing",
data: data,
};
}
if (typeof data.emailsto === "string") data.emailsto = [data.emailsto];
+
+ if (!fs.existsSync(path.resolve(template))){
+ return {
+ status: 404,
+ ref: "Notification",
+ msg: "templatenotfound",
+ data: { template:path.resolve(template) },
+ }
+ }
const tplemail = require(path.resolve(template));
- let sendit;
+ let sendit={status:200,ref:"Notifications",msg:"successfullsend"};
data.emailsto.forEach(async (e, i) => {
if (Checkjson.testformat(e, "email")) {
const dat = {};
@@ -233,16 +243,17 @@ Notifications.manageemail = (data, template, tribe) => {
dat.text = Mustache.render(tplemail.text, data);
dat.Cc = data.Cc ? tplemail.Cc.push(data.Cc) : tplemail.Cc;
dat.Bcc = data.Bcc ? tplemail.Bcc.push(data.Bcc) : tplemail.Bcc;
- dat.attachments = tplemail.attachments.push(data.attachments);
- const sendit = Notifications.sendmail(data, tribe);
+ if (data.attachments){
+ data.attachments.forEach(a=>tplemail.attachments.push(a))
+ }
if (i == 0) {
- sendit = await Notifications.sendmail(data, tribe);
- if (sendit.status != 200) return sendit;
+ sendit = await Notifications.sendmail(dat, tribe);
+ if (sendit.status != 200) return {status:406,ref:"Notifications",msg:"emailnotsent"};
} else {
- Notifications.sendmail(data, tribe);
+ Notifications.sendmail(dat, tribe);
}
} else {
- // not a well formated email
+ // not a well formated email @todo add a log file to follow it
}
});
return sendit;
@@ -250,6 +261,17 @@ Notifications.manageemail = (data, template, tribe) => {
Notifications.sendmail = async (data, tribe) => {
/**
+ * * in conf global or in /itm/{tribe}.json must have valid parameter emailcontact must be authorized by the smtp
+ * "emailcontact": "noreply@smatchit.io",
+ * "smtp": {
+ * "host": "smtp-relay.brevo.com",
+ * "port": 587,
+ * "secure": false,
+ * "auth": {
+ * "user": "xx",
+ * "pass": "yy"
+ * }
+ * }
* See https://nodemailer.com/message/ for available fields to add
* @param {string} [data.from] an email authorized by smtp used priority from header xtribe
* @param {string} data.to list of email separate by ,
@@ -265,7 +287,7 @@ Notifications.sendmail = async (data, tribe) => {
* {filename:'img.png',path:"data:text/svg;base64.aGVsbG8gd29ybGQ="}
*
* @example data
- * {"to":"wall-ants.ndda.fr",
+ * {"to":"wall-ants@ndda.fr",
* "subject":"Test",
* "html":"
test welcome
",
* "text":"test welcome",
@@ -305,13 +327,18 @@ Notifications.sendmail = async (data, tribe) => {
const conftribfile = `../../itm/${tribe}.json`;
if (fs.existsSync(conftribfile)) {
const conftrib = fs.readJSONSync(conftribfile);
+ if (!conftrib.emailcontact){
+ return {
+ status: 428,
+ ref: "Notifications",
+ msg: "missingemailcontactinconf",
+ data: { tribe },
+ };
+ }
confsmtp = conftrib.smtp;
- if (!data.from) data.from = conftrib.emailcontact;
+ if (!data.from || data.from == conf.emailcontact) data.from = conftrib.emailcontact;
}
-
- //const client = smtp.connect(confsmtp);
const transporter = await nodemailer.createTransport(confsmtp);
- //@todo add attachments management
if (data.filelist) {
data.attachments = [];
let missingfile = [];
@@ -331,6 +358,7 @@ Notifications.sendmail = async (data, tribe) => {
}
//console.log("data:", data);
const res = await transporter.sendMail(data);
+ //console.log(res)
if (
res.accepted &&
data.to.split(",").reduce((acc, m) => acc && res.accepted.includes(m), true)
diff --git a/models/PagansPeter.js b/models/PagansPeter.js
new file mode 100644
index 0000000..b8d0fcd
--- /dev/null
+++ b/models/PagansPeter.js
@@ -0,0 +1,221 @@
+const glob = require("glob");
+const path = require("path");
+const dayjs = require("dayjs");
+const fs = require("fs-extra");
+const axios = require("axios");
+const Mustache = require("mustache");
+const openpgp = require("openpgp");
+const Notifications = require("./Notifications.js");
+const Odmdb = require("./Odmdb.js");
+
+const conf = require(`../../../conf.json`);
+const currentmod = "Pagans";
+const log = conf.api.activelog.includes(currentmod);
+/**
+ * Pagan Management numeric Identity and Person (Person = Pagan Id + tribe)
+ *
+ *
+ *
+ */
+const Pagans = {};
+
+Pagans.create=()=>{
+//from router.post("/", checkHeaders, isAuthenticated, async (req, res)
+}
+
+Pagans.joinpersontotribe=()=>{
+// from router.post("/", checkHeaders, isAuthenticated, async (req, res)
+
+}
+/**
+ * Remove authentification token after a logout
+ * @param {string} alias
+ * @param {string} tribe
+ * @param {integer} xdays
+ * @param {string} xhash
+ * @returns {status:200, ref:"Pagans",msg:"logout"}
+ * tmpfs name file has to be on line with the tmpfs create by isAuthenticated
+ * tmpfs contain profils name for a tribe/
+ */
+Pagans.logout = (alias, tribe, xdays, xhash) => {
+ //console.log(alias, tribe, xdays, xhash);
+ // inline with middleware isAuthenticated.js
+ let tmpfs = `../../tmp/tokens/${alias}_${tribe}_${xdays}`;
+ //max filename in ext4: 255 characters
+ tmpfs += `_${xhash.substring(150, 150 + tmpfs.length - 249)}.json`;
+ fs.remove(tmpfs);
+ if (log) console.log(currentmod, "logout token", tmpfs);
+ return { status: 200, ref: "Pagans", msg: "logout" };
+};
+
+/**
+ * @param {string} alias a alias that exist or not
+ * @return {object} { status: 200, ref:"pagans",msg:"aliasexist",data: { alias, publicKey } }
+ * { status: 404, ref:"pagans",msg:"aliasdoesnotexist",data: { alias} }
+ *
+ **/
+Pagans.getalias = (alias) => {
+ //bypass Odmdb cause all is public save ressources
+ console.log(path.resolve(`../objects/pagans/itm/${alias}.json`));
+ if (fs.existsSync(`../objects/pagans/itm/${alias}.json`)) {
+ return {
+ status: 200,
+ ref: "Pagans",
+ msg: "aliasexist",
+ data: fs.readJSONSync(`../objects/pagans/itm/${alias}.json`),
+ };
+ } else {
+ return {
+ status: 404,
+ ref: "Pagans",
+ msg: "aliasdoesnotexist",
+ data: { alias },
+ };
+ }
+};
+
+/**
+ * Send email with alias's keys to email or person alias person.recovery.email
+ *
+ * If email or pubkey is undefined then get data from tribe/person(alias)
+ * Send email with keys
+ * @param {object} data
+ * @param {string} data.alias
+ * @param {pgpPrivate} [data.privatekey]
+ * @param {string} [data.passphrase]
+ * @param {string} data.tribe
+ * @param {pgpPublic} [data.publickey]
+ * @param {string} [data.email]
+ * @param {string} data.lg
+ */
+Pagans.sendmailkey = (data) => {
+ if (log)
+ console.log(
+ currentmod,
+ data.alias,
+ "-",
+ data.privatekey ? data.privatekey.substring(0, 10) : "",
+ "-",
+ data.tribe,
+ "-",
+ data.passphrase,
+ "-",
+ data.publickey ? data.publickey.substring(0, 10) : "",
+ "-",
+ data.email,
+ "-",
+ data.lg
+ );
+ const person = {
+ alias: data.alias,
+ privatekey: data.privatekey,
+ tribe: data.tribe,
+ };
+
+ if (!data.publickey || !data.email || !data.privatekey) {
+ const personfile = `../../${data.tribe}/objects/persons/itm/${data.alias}.json`;
+ if (!fs.existsSync(personfile)) {
+ return {
+ status: 404,
+ ref: "Pagans",
+ msg: "persondoesnotexist",
+ data: { alias: data.alias, tribe: data.tribe },
+ };
+ }
+ const persondata = fs.readJsonSync(personfile);
+ if (!persondata.recoveryauth) {
+ return {
+ status: 404,
+ ref: "Pagans",
+ msg: "personhasnorecoveryauth",
+ data: { alias: data.alias, tribe: data.tribe, email: data.email },
+ };
+ }
+ person.email = persondata.recoveryauth.email;
+ person.publickey = persondata.recoveryauth.publickey;
+ person.privatekey = persondata.recoveryauth.privatekey;
+ person.passphrase = persondata.recoveryauth.passphrase;
+ } else {
+ person.email = data.email;
+ person.passphrase = data.passphrase;
+ person.publickey = data.publickey;
+ }
+ person.avecpassphrase = person.passphrase != "";
+ let tplfile = `../../${data.tribe}/template/createidentity_${data.lg}.js`;
+ if (!fs.existsSync(tplfile)) {
+ tplfile = `../template/createidentity_${data.lg}.js`;
+ if (!fs.existsSync(tplfile)) {
+ return {
+ status: 406,
+ ref: "Pagans",
+ msg: "templatedoesnotexist",
+ data: { tplfile },
+ };
+ }
+ }
+ const tplemail = require(path.resolve(tplfile));
+ /*
+ Remove from attachments for less user confusing
+ {
+ filename:`${person.alias}_publickey.txt`,
+ content: person.publickey,
+ contentType:"text/plain"
+ },
+ */
+ const maildata = {
+ from: tplemail.sender,
+ to: person.email,
+ subject: Mustache.render(tplemail.subject, person),
+ html: Mustache.render(tplemail.html, person),
+ text: Mustache.render(tplemail.text, person),
+ attachments: [
+ {
+ filename: `${person.alias}_privatekey.txt`,
+ content: person.privatekey,
+ contentType: "text/plain",
+ },
+ ],
+ };
+ return Notifications.sendmail(maildata, data.tribe);
+};
+
+Pagans.authenticatedetachedSignature = async (
+ alias,
+ pubK,
+ detachedSignature,
+ message
+) => {
+ /**
+ * Check that a message was signed with a privateKey from a publicKey
+ * This is not necessary if isAuthenticated, but can be usefull to double check
+ * @TODO finish it and implement it also in /apxpagan.js for browser
+ * @alias {string} alias link to the publicKey
+ * @pubK {string} publiKey text format
+ * @detachedSignature {string} a detachedsignatured get from apx.detachedSignature
+ * @message {string} the message signed
+ * @return {boolean} true the message was signed by alias
+ * false the message was not signed by alias
+ */
+ const publicKey = await openpgp.readKey({ armoredKey: pubK });
+ const msg = await openpgp.createMessage({ text: message });
+ const signature = await openpgp.readSignature({
+ armoredSignature: detachedSignature, // parse detached signature
+ });
+ const verificationResult = await openpgp.verify({
+ msg, // Message object
+ signature,
+ verificationKeys: publicKey,
+ });
+ const { verified, keyID } = verificationResult.signatures[0];
+ try {
+ await verified; // throws on invalid signature
+ if (log) console.log(currentmod, "Signed by key id " + keyID.toHex());
+ return KeyId.toHex().alias == alias;
+ } catch (e) {
+ if (log)
+ console.log(currentmod, "Signature could not be verified: " + e.message);
+ return false;
+ }
+};
+
+module.exports = Pagans;
diff --git a/routes/notifications.js b/routes/notifications.js
index d0026a4..a5ecfc6 100644
--- a/routes/notifications.js
+++ b/routes/notifications.js
@@ -44,13 +44,24 @@ router.get("/messages/:alias/:tribeId", (req, res) => {
/**
* @api {POST} adminapi/notifications/sendmail/:tribe/:template -Send personnalize emails
* @apiName Sendmail
- * @apiDescription Send personnalize email with data from template store in ../../{tribe}/template/{template}.json
+ * @apiDescription Send personnalize email with data from template store in ../../{tribe}/template/{template}.json and smtp in conf global or in /itm/{tribe}.json that must have valid parameter emailcontact must be authorized by the smtp
+ * "emailcontact": "noreply@smatchit.io",
+ * "smtp": {
+ * "host": "smtp-relay.brevo.com",
+ * "port": 587,
+ * "secure": false,
+ * "auth": {
+ * "user": "xx",
+ * "pass": "yy"
+ * }
+ * }
+
* @apiGroup Notifications
*
* @apiParam {string} template
* @apiParam {string} tribe
* @apiBody {array} emails to send (array of valid email)
- * @apiBody {object} Data to personnalize template
+ * @apiBody {object} data to personnalize template
*
* @apiSuccess {object} notif content
* @apiSuccessExample {json} Success-Response:
@@ -59,24 +70,19 @@ router.get("/messages/:alias/:tribeId", (req, res) => {
* bouture
*/
router.post(
- "/sendmail/:tribe/:template/:await",
+ "/sendmail/:tribe/:template",
checkHeaders,
isAuthenticated,
- (req, res) => {
- const pathtpl = `../../${req.params.tribe}/${req.params.template}.js`;
- if (fs.existsSync(pathtpl)) {
- const sendemail = Notification.manageemail(req.body, pathtpl,req.params.tribe);
- res.status(sendemail.status).send(sendemail);
- } else {
- res
- .status(404)
- .send({
- status: 404,
- ref: "Notification",
- msg: "templatenotfound",
- data: { pathtpl },
- });
- }
+ async (req, res) => {
+ const data = req.body.data;
+ data.emailsto = req.body.emails;
+ const pathtpl = `../../${req.params.tribe}/template/${req.params.template}_${req.session.header.xlang}.js`;
+ const sendemail = await Notifications.manageemail(
+ data,
+ pathtpl,
+ req.params.tribe
+ );
+ res.status(sendemail.status).send(sendemail);
}
);
@@ -99,14 +105,12 @@ router.post(
router.post("/registeranonymous", checkHeaders, (req, res) => {
//console.log("list registration ",req.body)
if (!req.body.typekey || !["email", "telephone"].includes(req.body.typekey)) {
- return res
- .status(406)
- .json({
- status: 406,
- ref: "Notifications",
- msg: "typekeyunknown",
- data: { typekey: req.body.typekey },
- });
+ return res.status(406).json({
+ status: 406,
+ ref: "Notifications",
+ msg: "typekeyunknown",
+ data: { typekey: req.body.typekey },
+ });
}
const key = req.body.contactpoint
? req.body.contactpoint