update auth openpgp.js

This commit is contained in:
2023-05-12 07:59:32 +02:00
parent dc11e8235e
commit a78bd8404a
85 changed files with 47890 additions and 1042 deletions

View File

@@ -4,46 +4,61 @@
"use strict";
var app = app || {};
app.createIdentity=(alias,passphrase="")=>{
if (alias.length<3 || apx.data.object['index_pagans_alias_all'].includes(alias) ){
alert('Please chose another alias')
return;
}
const keys = apx.generateKey(alias,passphrase)
for(let tag of ['inputalias','inputpassphrase']){
document.getElementById(tag).classList.add('disabled')
}
document.getElementById('privatekey').setAttribute("key",keys.privateKey);
document.getElementById('publickey').setAttribute("key",keys.publicKey);
document.getElementById('generatekeys').classList.add('d-none');
document.getElementById('downloadkeys').classList.remove('d-none');
document.getElementById('createId').classList.remove('d-none');
}
app.registerIdentity=()=>{
const data={
alias:document.getElementById('inputalias').value,
privateKey:document.getElementById('privatekey').getAttribute('key'),
publicKey:document.getElementById('publickey').getAttribute('key'),
passphrase:document.getElementById('inputpassphrase').value
}
axios.post('api/pagans',data,apx.data.headers)
.then(rep=>{
// genere authentification headers.xalias, xhash store object.pagans_alias={privateKey, passphrase}; console.log(rep)})
// if check trust add data email recovery axios.post('api/persons,data, apx.data.headers)
app.runapirequest = (destmodalid, axiosreq, modalcontent) => {
/**
* Axios option
* {method: GET POST,...,
* url:'/relativepath'
* data:{anydata to pass}
* responseType:'json'}
*/
if (!modalcontent) {
modalcontent = {
title: "An axios request",
body: "",
actions: [],
};
}
modalcontent.body += JSON.stringify(axiosreq);
console.log(apx.data.headers);
axiosreq.headers = apx.data.headers;
axios(axiosreq)
.then((rep) => {
console.log(rep);
modalcontent.body += "<br>" + JSON.stringify(rep.data);
app.modalmanagement("reqaxios", apx.data.tpl.apxmodal, modalcontent);
})
.catch(err=>{
console.log('sorry',err);
})
}
.catch((err, rep) => {
console.log(err);
modalcontent.title += " - Status :" + err.response.status;
modalcontent.body += JSON.stringify(err.response.data);
app.modalmanagement("reqaxios", apx.data.tpl.apxmodal, modalcontent);
});
};
app.modalmanagement = (modalid, tpl, data, optionmodal) => {
/**
* Pre-request in hatml
* <div id="apxmodal" add2data tpl="static/tpl/apxmodal_en.mustache" ></div>
* data must be online with tpl
* optionmodal see https://getbootstrap.com/docs/5.0/components/modal/
*/
if (!optionmodal)
optionmodal = { backdrop: true, keyboard: true, focus: true };
if (document.getElementById(modalid)) {
document.getElementById(modalid).remove();
}
data.modalid = modalid;
document.getElementById("apxmodal").innerHTML += Mustache.render(tpl, data);
var currentmodal = new bootstrap.Modal(
document.getElementById(modalid),
optionmodal
);
//var currentmodal = bootstrap.Modal.getOrCreateInstance(modalid);
currentmodal.show();
};
app.search = (elt) => {
//@todo get search string from input then return tpldata.listofarticles={"search":[]}
//@todo get search string from input then return tpldata.articleslist={"search":[]}
console.log("A FAIRE");
};
app.downloadlink = (keys, object, prefix) => {
@@ -81,50 +96,52 @@ app.downloadlink = (keys, object, prefix) => {
};
app.setupdata = () => {
//todo generate tpldata.listofarticle get html from /static/html
apx.data.tpldata.listofarticle = {
//todo generate tpldata.articleslist get html from /static/html
apx.data.tpldata.articleslist = {
mostread: [{}],
news: [{}],
search: [],
articles: "html",
};
const list = {}
const list = {};
document.querySelectorAll("[add2data]").forEach((e) => {
/** Collect from any tag with add2data url attribute (tpl tpldata object)
* name is the filename :
* for tpl and tpldata (relativepath/filename_xx.ext) name=filename
* For public object (pagans, towns, nations, block) not tribes object are gettable (only)
* with /nationchains/objectName/idx/existingindex.json
* or for an item /itm//primarykey_value_of_item.json
*
* For tribe object get or for modification you need to use api and to have accessright setup properly:
* or for an item /itm//primarykey_value_of_item.json
*
* For tribe object get or for modification you need to use api and to have accessright setup properly:
* for object index (/api/odmdb/objectname/idx/filename.json) name =objectname_filename
* for object item (/api/odmdb/objectname/itm/primarykey.json) name =objectname_primarykey
*/
if (e.getAttribute('object')){
const url = e.getAttribute('object')
const spliturlobj=url.split("/").slice(-3);
const objectname=spliturlobj[0];
const objecttype=spliturlobj[1];
const objectkey=spliturlobj[2].substring(0,spliturlobj[2].length-5)
if (!list[objectname]) list[objectname]={};
list[objectname][`${objecttype}${objectkey}`]= url;
};
for (let k of ["tpl", "tpldata"]) {
if (e.getAttribute(k)){
const url=e.getAttribute(k);
let localname=url.split('/').slice(-1)[0];
if (url.includes('.mustache')) localname=localname.substring(0,localname.length-12);
if (url.includes('.html') || url.includes('.json') ) localname=localname.substring(0,localname.length-8);
if (!list[k]) list[k]={};
list[k][localname]=url;
}
if (e.getAttribute("object")) {
const url = e.getAttribute("object");
const spliturlobj = url.split("/").slice(-3);
const objectname = spliturlobj[0];
const objecttype = spliturlobj[1];
const objectkey = spliturlobj[2].substring(0, spliturlobj[2].length - 5);
if (!list[objectname]) list[objectname] = {};
list[objectname][`${objecttype}${objectkey}`] = url;
}
});
for (let k of ["tpl", "tpldata"]) {
if (e.getAttribute(k)) {
const url = e.getAttribute(k);
let localname = url.split("/").slice(-1)[0];
if (url.includes(".mustache"))
localname = localname.substring(0, localname.length - 12);
if (url.includes(".html") || url.includes(".json"))
localname = localname.substring(0, localname.length - 8);
if (!list[k]) list[k] = {};
list[k][localname] = url;
}
}
});
// load external template and data
for (let k of Object.keys(list) ) {
for (let k of Object.keys(list)) {
console.log(k, list[k]);
apx.loadfile(list[k], k);
}
@@ -132,21 +149,21 @@ app.setupdata = () => {
if (apx.data.firsttimeload) {
// Need to wait the first time apx.data fully load
setTimeout(() => {
app.setuppage();
}, 300);
app.setuppage();
}, 500);
delete apx.data.firsttimeload;
apx.save();
}else{
} else {
app.setuppage();
}
};
app.load = (idtoload, tplname, tpldata) => {
const tpl = apx.data.tpl[tplname]
? apx.data.tpl[tplname]
: `missing template ${tplname}`;
const data = typeof tpldata == "string" ? apx.data.tpldata[tpldata] : tpldata;
document.getElementById(idtoload).innerHTML = Mustache.render(tpl, data);
};
const tpl = apx.data.tpl[tplname]
? apx.data.tpl[tplname]
: `missing template ${tplname}`;
const data = typeof tpldata == "string" ? apx.data.tpldata[tpldata] : tpldata;
document.getElementById(idtoload).innerHTML = Mustache.render(tpl, data);
};
app.setuppage = () => {
// load partial template
document.querySelectorAll("[apptoload]").forEach((e) => {

View File

@@ -0,0 +1,232 @@
/*eslint no-undef:0*/
/*eslint-env browser*/
"use strict";
var pagans = pagans || {};
/**
* pagans.generateKey(params) create a public/private key compatible with apXtrib backend and store it in apx.data
* pagans.detachedSignature(params) generate a detachedSignature for a Message that backend can check with the publicKey of the alias
* pagans.checkdetachedSignature(params) check a detachedSignature for a Message (used on the backend as well)
*
*
*/
pagans.loadtpldata = () => {
// adapte tpldata to template tpl
// get list of tribes from nationchains/towns/idx/townId_all.json
const datacreatepagan = { tribes: [] };
apx.data.towns.idxtownId_all[apx.data.tpldata.setup.townId].tribes.forEach(
(t) => {
if (t == apx.data.tpldata.setup.tribeId) {
datacreatepagan.tribes.push({ selected: true, tribeId: t });
} else {
datacreatepagan.tribes.push({ tribeId: t });
}
}
);
return datacreatepagan;
};
pagans.generateKey = async (alias, passphrase) => {
/**
* @param {string} alias a unique alias that identify an identity
* @param {string} passphrase a string to cipher the publicKey (can be empty, less secure but simpler)
* @return {publicKey,privateKey} with userIds = [{alias}]
*/
const pgpparam = {
type: "ecc", // Type of the key, defaults to ECC
curve: "curve25519", // ECC curve name, defaults to curve25519
userIDs: [{ alias: alias }], // you can pass multiple user IDs
passphrase: passphrase, // protects the private key
format: "armored", // output key format, defaults to 'armored' (other options: 'binary' or 'object')
};
const { privateKey, publicKey } = await openpgp.generateKey(pgpparam);
// key start by '-----BEGIN PGP PRIVATE KEY BLOCK ... '
// get liste of alias:pubklickey await axios.get('api/v0/pagans')
// check alias does not exist
return { alias, privateKey, publicKey };
};
pagans.detachedSignature = async (pubK, privK, passphrase, message) => {
/**
* @pubK {string} a text public key
* @privK {string} a test priv key
* @passphrase {string} used to read privK
* @message {string} message to sign
* @Return a detached Signature of the message
*/
const publicKey = await openpgp.readKey({ armoredKey: pubK });
let privateKey;
if (passphrase == "") {
privateKey = await openpgp.readPrivateKey({ armoredKey: privK });
} else {
privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readPrivateKey({ armoredKey: privK }),
passphrase,
});
}
console.log(message);
const msg = await openpgp.createMessage({ text: message });
console.log(msg);
const sig = await openpgp.sign({
message: msg,
signingKeys: privateKey,
detached: true,
});
return btoa(sig);
};
pagans.authenticatedetachedSignature = async (
alias,
pubK,
detachedSignature,
message
) => {
/**
* Check that alias (pubkey) signe a message
* @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: atob(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
console.log("Signed by key id " + keyID.toHex());
return KeyId.toHex().alias == alias;
} catch (e) {
console.log("Signature could not be verified: " + e.message);
return false;
}
};
pagans.createIdentity = async (alias, passphrase = "") => {
console.log(Object.keys(apx.data.pagans["idxalias_all"]));
if (
alias.length < 3 ||
Object.keys(apx.data.pagans["idxalias_all"]).includes(alias)
) {
alert("Please chose another alias");
return;
}
const keys = await pagans.generateKey(alias, passphrase);
for (let tag of ["inputalias", "inputpassphrase"]) {
document.getElementById(tag).classList.add("disabled");
}
console.log(keys);
document.getElementById("privatekey").setAttribute("key", keys.privateKey);
document.getElementById("publickey").setAttribute("key", keys.publicKey);
document.getElementById("generatekeys").classList.add("d-none");
document.getElementById("trustintribe").classList.remove("d-none");
document.getElementById("downloadkeys").classList.remove("d-none");
document.getElementById("createId").classList.remove("d-none");
};
pagans.registerIdentity = async () => {
const emailregex =
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const data = {};
data.alias = document.getElementById("inputalias").value;
data.publickey = document.getElementById("publickey").getAttribute("key");
if (document.getElementById("inputemailrecovery").value != "") {
data.email = document.getElementById("inputemailrecovery").value;
}
if (data.email && !emailregex.test(data.email)) {
alert("Check your email");
return false;
}
var select = document.getElementById("trustedtribe");
if (document.getElementById("trustedcheck").checked) {
data.trustedtribe = select.options[select.selectedIndex].value;
data.privatekey = document.getElementById("privatekey").getAttribute("key");
data.passphrase = document.getElementById("inputpassphrase").value;
}
if (document.getElementById("trustedcheck").checked && !data.email) {
alert("Please provide a valid email");
return false;
}
console.log(data);
//store and create authentification by signing 'alias_timestamp'
/* const passp = data.passphrase ? data.passphrase : "";
apx.data.auth = {
alias: data.alias,
publickey: data.publickey,
privatekey: document.getElementById("privatekey").getAttribute("key"),
passphrase: passp,
};
apx.data.headers.xalias = data.alias;
apx.data.headers.xdays = dayjs().valueOf();
apx.save();
const msg = `${data.alias}_${apx.data.headers.xdays}`;
apx.data.headers.xhash = await pagans.detachedSignature(
apx.data.auth.publickey,
apx.data.auth.privatekey,
passp,
msg
);
apx.save();*/
await pagans.authentifyme(
data.alias,
data.passphrase,
document.getElementById("privatekey").getAttribute("key"),
document.getElementById("publickey").getAttribute("key")
);
console.log("header", apx.data.headers);
axios
.post("api/pagans", data, { headers: apx.data.headers })
.then((reppagan) => {
console.log(reppagan);
})
.catch((err) => {
console.log("sorry", err);
});
};
pagans.authentifyme = async (
alias,
passphrase,
privatekey,
publickeycreate
) => {
/**
* Set apx.data.auth with pub, priv, passphrase alias that allow authentification
* set headers with xdays (timestamp) and xhash of message: {alias}_{timestamp} generate with pub & priv key
*
* @Param {key} publickeycreate optional when alias does not exist
*/
console.log(alias, passphrase);
console.log(privatekey);
const passp = passphrase ? passphrase : "";
const publickey = publickeycreate
? publickeycreate
: apx.data.pagans.idxalias_all[alias].publicKey;
apx.data.auth = {
alias: alias,
publickey: publickey,
privatekey: privatekey,
passphrase: passp,
};
apx.data.headers.xalias = alias;
apx.data.headers.xdays = dayjs().valueOf();
apx.save();
const msg = `${alias}_${apx.data.headers.xdays}`;
apx.data.headers.xhash = await pagans.detachedSignature(
apx.data.auth.publickey,
apx.data.auth.privatekey,
passp,
msg
);
apx.save();
};

View File

@@ -21,93 +21,12 @@ var apx = apx || {};
},
objects:{}
};
* apx.generateKey(params) create a public/private key compatible with apXtrib backend and store it in apx.data
* apx.detachedSignature(params) generate a detachedSignature for a Message that backend can check with the publicKey of the alias
* apx.checkdetachedSignature(params) check a detachedSignature for a Message (used on the backend as well)
* apx.ready(callback) equivalent of jquery Document.ready()
* apx.save() save apx.data as xapp value in localStorage
* apx.update() get localStorage up to date
* apx.loadfile({url:name},dest,axiosoptions) async that wait to load a list of relativ url and store it in a apx.data[dest]
*/
apx.generateKey = async (alias, passphrase) => {
/**
* @param {string} alias a unique alias that identify an identity
* @param {string} passphrase a string to cipher the publicKey (can be empty, less secure but simpler)
* @return {publicKey,privateKey} with userIds = [{alias}]
*/
const { privateKey, publicKey } = await openpgp.generateKey({
type: "ecc", // Type of the key, defaults to ECC
curve: "curve25519", // ECC curve name, defaults to curve25519
userIDs: [{ alias: alias }], // you can pass multiple user IDs
passphrase: passphrase, // protects the private key
format: "armored", // output key format, defaults to 'armored' (other options: 'binary' or 'object')
});
// key start by '-----BEGIN PGP PRIVATE KEY BLOCK ... '
// get liste of alias:pubklickey await axios.get('api/v0/pagans')
// check alias does not exist
return { alias, privateKey, publicKey };
/* document.getElementById('privatekey').innerHTML=privateKey;
document.getElementById('publickey').innerHTML=publicKey;
apx.data.pagans={}
apx.data.pagans.privateKey=privateKey;
apx.data.pagans.publicKey=publicKey;
apx.save();
console.log(apx.data)
*/
};
apx.detachedSignature = async (pubK, privK, passphrase, message) => {
/**
* @pubK {string} a text public key
* @privK {string} a test priv key
* @passphrase {string} used to read privK
* @message {string} message to sign
* @Return a detached Signature of the message
*/
const publicKey = await openpgp.readKey({ armoredKey: pubK });
const privateKey = await openpgp.decryptKey({
privateKey: await openpgp.readPrivateKey({ armoredKey: privK }),
passphrase,
});
const msg = await openpgp.createMessage({ text: message });
return await openpgp.sign({ msg, signinKeys: privK, detached: true });
};
apx.checkdetachedSignature = async (
alias,
pubK,
detachedSignature,
message
) => {
/**
* @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
console.log("Signed by key id " + keyID.toHex());
return KeyId.toHex().alias == alias;
} catch (e) {
console.log("Signature could not be verified: " + e.message);
return false;
}
};
apx.ready = (callback) => {
/**
* Wait loading DOM, when ready run callback function
@@ -156,13 +75,17 @@ apx.loadfile = async (list, dest, axiosoptions) => {
};
const urls = Object.keys(invertlist);
if (!axiosoptions) {
// force no cache browser
apx.data.headers["Cache-Control"] = "no-cache";
apx.data.headers["Pragma"] = "no-cache";
apx.data.headers["Expires"] = 0;
axiosoptions = { headers: apx.data.headers };
}
console.log('axiosiptions',axiosoptions)
console.log("axiosiptions", axiosoptions);
const promiseArray = urls.map((r) => fetchURL(r, axiosoptions));
await Promise.all(promiseArray)
.then((data) => {
console.log(data)
console.log(data);
for (let pos = 0; pos < urls.length; pos++) {
//console.log( 'url', urls[ pos ] )
//console.log( data[ pos ] );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long