mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
Merge branch 'manhattan' of github.com:/amark/gun into manhattan
This commit is contained in:
commit
55ea510a8d
78
gun.js
78
gun.js
@ -62,7 +62,7 @@
|
||||
return l;
|
||||
}
|
||||
;(function(){ // max ~1ms or before stack overflow
|
||||
var u, sT = setTimeout, l = 0, c = 0, sI = (typeof setImmediate !== ''+u && setImmediate) || sT;
|
||||
var u, sT = setTimeout, l = 0, c = 0, sI = (typeof setImmediate !== ''+u && setImmediate) || sT; // queueMicrotask faster but blocks UI
|
||||
sT.poll = sT.poll || function(f){
|
||||
if((1 >= (+new Date - l)) && c++ < 3333){ f(); return }
|
||||
sI(function(){ l = +new Date; f() },c=0)
|
||||
@ -210,6 +210,7 @@
|
||||
USE('./onto'); // depends upon onto!
|
||||
module.exports = function ask(cb, as){
|
||||
if(!this.on){ return }
|
||||
var lack = (this.opt||{}).lack || 9000;
|
||||
if(!('function' == typeof cb)){
|
||||
if(!cb){ return }
|
||||
var id = cb['#'] || cb, tmp = (this.tag||'')[id];
|
||||
@ -217,16 +218,16 @@
|
||||
if(as){
|
||||
tmp = this.on(id, as);
|
||||
clearTimeout(tmp.err);
|
||||
tmp.err = setTimeout(function(){ tmp.off() }, lack);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var id = (as && as['#']) || Math.random().toString(36).slice(2);
|
||||
if(!cb){ return id }
|
||||
var to = this.on(id, cb, as);
|
||||
to.err = to.err || setTimeout(function(){
|
||||
to.err = to.err || setTimeout(function(){ to.off();
|
||||
to.next({err: "Error: No ACK yet.", lack: true});
|
||||
to.off();
|
||||
}, (this.opt||{}).lack || 9000);
|
||||
}, lack);
|
||||
return id;
|
||||
}
|
||||
})(USE, './ask');
|
||||
@ -344,7 +345,7 @@
|
||||
++ni; kl = null; pop(o);
|
||||
}());
|
||||
} Gun.on.put = put;
|
||||
console.log("BEWARE: BETA VERSION OF NEW GUN! NOT ALL FEATURES FINISHED!"); // clock below, reconnect sync. // msg put, put, say ack, hear loop...
|
||||
console.log("BEWARE: BETA VERSION OF NEW GUN! NOT ALL FEATURES FINISHED!"); // clock below, reconnect sync, SEA certify wire merge, User.auth taking multiple times, // msg put, put, say ack, hear loop...
|
||||
function ham(val, key, soul, state, msg){
|
||||
var ctx = msg._||'', root = ctx.root, graph = root.graph, lot, tmp;
|
||||
var vertex = graph[soul] || empty, was = state_is(vertex, key, 1), known = vertex[key];
|
||||
@ -359,7 +360,7 @@
|
||||
}
|
||||
if(state < was){ /*old;*/ if(!ctx.miss){ return } } // but some chains have a cache miss that need to re-fire. // TODO: Improve in future. // for AXE this would reduce rebroadcast, but GUN does it on message forwarding.
|
||||
if(!ctx.faith){ // TODO: BUG? Can this be used for cache miss as well? // Yes this was a bug, need to check cache miss for RAD tests, but should we care about the faith check now? Probably not.
|
||||
if(state === was && (val === known || L(val) <= L(known))){ /*console.log("same");*/ /*same;*/ if(true || !ctx.miss){ return } } // same
|
||||
if(state === was && (val === known || L(val) <= L(known))){ /*console.log("same");*/ /*same;*/ if(!ctx.miss){ return } } // same
|
||||
}
|
||||
ctx.stun++; // TODO: 'forget' feature in SEA tied to this, bad approach, but hacked in for now. Any changes here must update there.
|
||||
var aid = msg['#']+ctx.all++, id = {toString: function(){ return aid }, _: ctx}; // this *trick* makes it compatible between old & new versions.
|
||||
@ -395,7 +396,9 @@
|
||||
tmp.acks = (tmp.acks||0) + 1;
|
||||
if(0 == tmp.stun && tmp.acks == tmp.all){ // TODO: if ack is synchronous this may not work?
|
||||
root && root.on('in', {'@': tmp['#'], err: msg.err, ok: 'shard'});
|
||||
return;
|
||||
}
|
||||
if(msg.err){ msg['@'] = tmp['#'] }
|
||||
}
|
||||
|
||||
var ERR = "Error: Invalid graph!";
|
||||
@ -406,7 +409,7 @@
|
||||
|
||||
;(function(){
|
||||
Gun.on.get = function(msg, gun){
|
||||
var root = gun._, get = msg.get, soul = get['#'], node = root.graph[soul], has = get['.'], tmp;
|
||||
var root = gun._, get = msg.get, soul = get['#'], node = root.graph[soul], has = get['.'];
|
||||
var next = root.next || (root.next = {}), at = next[soul];
|
||||
// queue concurrent GETs?
|
||||
// TODO: consider tagging original message into dup for DAM.
|
||||
@ -448,7 +451,7 @@
|
||||
// PERF: Consider commenting this out to force disk-only reads for perf testing? // TODO: .keys( is slow
|
||||
node && (function go(){
|
||||
S = +new Date;
|
||||
var i = 0, k, put = {};
|
||||
var i = 0, k, put = {}, tmp;
|
||||
while(i < 9 && (k = keys[i++])){
|
||||
state_ify(put, k, state_is(node, k), node[k], soul);
|
||||
}
|
||||
@ -774,7 +777,7 @@
|
||||
}; Gun.on.unlink = unlink;
|
||||
|
||||
function ack(msg, ev){
|
||||
//if(!msg['%'] && (this||'').off){ this.off() } // do NOT memory leak, turn off listeners!
|
||||
//if(!msg['%'] && (this||'').off){ this.off() } // do NOT memory leak, turn off listeners! Now handled by .ask itself
|
||||
// manhattan:
|
||||
var as = this.as, at = as.$._, root = at.root, get = as.get||'', tmp = (msg.put||'')[get['#']]||'';
|
||||
if(!msg.put || ('string' == typeof get['.'] && u === tmp[get['.']])){
|
||||
@ -822,7 +825,7 @@
|
||||
var wait = {}; // can we assign this to the at instead, like in once?
|
||||
function any(msg, eve, f){
|
||||
if(any.stun){ return }
|
||||
var at = msg.$._, data = at.put, aid, tmp;
|
||||
var at = msg.$._, data = at.put, aid, test, tmp;
|
||||
if((tmp = root.pass) && !tmp[id]){ return }
|
||||
if(!at.has && !at.soul){ data = (u !== (msg.put||'')['='])? msg.put['='] : msg.put } // handles non-core messages.
|
||||
if('string' == typeof (tmp = Gun.valid(data))){ data = root.$.get(tmp)._.put } // TODO: Can we delete this line of code, because the line below (which was inspired by @rogowski) handles it anyways?
|
||||
@ -831,14 +834,20 @@
|
||||
if(u === opt.stun){
|
||||
//if(tmp = root.stun){ tmp = tmp[at.id] || at.$.back(function(back){ return tmp[back.id] || u }); if(tmp && !tmp.end && any.id > (tmp._||'').id){ // this is more thorough, but below seems to work too?
|
||||
//if((tmp = root.stun) && (tmp = tmp[at.id] || tmp[at.back.id]) && !tmp.end && any.id > (tmp._||'').id){ // if we are in the middle of a write, don't read until it is done, unless our callback was earlier than the write.
|
||||
if((tmp = root.stun) && (tmp = tmp[aid = cat.id] || tmp[aid = at.id] || (msg.$$ && tmp[aid = msg.$$._.id]) /*|| tmp[aid = at.back.id]*/) && any.id > tmp.run){
|
||||
if(tmp.stun && !tmp.stun.end){
|
||||
tmp.stun[id] = function(){any(msg,eve,1)}; // add ourself to the stun callback list that is called at end of the write.
|
||||
return;
|
||||
if((tmp = root.stun) && tmp.on){
|
||||
tmp.on(''+(aid = cat.id), test = {});
|
||||
!test.run && tmp.on(''+(aid = at.id), test);
|
||||
!test.run && msg.$$ && tmp.on(''+(aid = msg.$$._.id), test);
|
||||
if(test.run && any.id > test.run){ // what if I'm less than the last item but more than an earlier item? Don't I need to check first item but add to last item?
|
||||
if(!test.stun || test.stun.end){
|
||||
test.stun = tmp.on('stun');
|
||||
test.stun = test.stun && test.stun.last;
|
||||
}
|
||||
if(test.stun && !test.stun.end){
|
||||
(test.stun.add || (test.stun.add = {}))[id] = function(){any(msg,eve,1)} // add ourself to the stun callback list that is called at end of the write.
|
||||
return;
|
||||
}
|
||||
}
|
||||
root.stun[aid] = tmp.next;
|
||||
any(msg,eve,f);
|
||||
return;
|
||||
}
|
||||
if((tmp = root.hatch) && !tmp.end && u === opt.hatch && !f){ // quick hack! // What's going on here? Because data is streamed, we get things one by one, but a lot of developers would rather get a callback after each batch instead, so this does that by creating a wait list per chain id that is then called at the end of the batch by the hatch code in the root put listener.
|
||||
if(wait[at.$._.id]){ return } wait[at.$._.id] = 1;
|
||||
@ -878,9 +887,6 @@
|
||||
if(cb){ cb.call(gun, gun._.err) }
|
||||
return gun;
|
||||
}
|
||||
if(tmp = this._.stun){ // TODO: Refactor?
|
||||
gun._.stun = gun._.stun || tmp;
|
||||
}
|
||||
if(cb && 'function' == typeof cb){
|
||||
gun.get(cb, as);
|
||||
}
|
||||
@ -944,7 +950,6 @@
|
||||
as = as || {};
|
||||
as.root = at.root;
|
||||
as.run || (as.run = root.once);
|
||||
(as.stun || (as.stun = function(){ return as.run })).back = (root.stun || (root.stun = {}))._; (as.ta = root.stun)._ = as.stun;
|
||||
stun(as, at.id); // set a flag for reads to check if this chain is writing.
|
||||
as.ack = as.ack || cb;
|
||||
as.via = as.via || gun;
|
||||
@ -1008,9 +1013,23 @@
|
||||
|
||||
function stun(as, id){
|
||||
if(!id){ return } id = (id._||'').id||id;
|
||||
tmp = as.root.stun || (as.root.stun = as.ta);
|
||||
var it = {run: as.run, stun: as.stun};
|
||||
(tmp[id]? (tmp[id].last.next = it) : (tmp[id] = it)).last = it;
|
||||
var run = as.root.stun || (as.root.stun = {on: Gun.on}), test = {}, tmp;
|
||||
as.stun || (as.stun = run.on('stun', function(){ }));
|
||||
if(tmp = run.on(''+id)){ tmp.the.last.next(test) }
|
||||
if(test.run >= as.run){ return }
|
||||
run.on(''+id, function(test){
|
||||
if(as.stun.end){
|
||||
this.off();
|
||||
this.to.next(test);
|
||||
return;
|
||||
}
|
||||
test.run = test.run || as.run;
|
||||
if(this.to.to){
|
||||
this.the.last.next(test);
|
||||
return;
|
||||
}
|
||||
test.stun = as.stun;
|
||||
});
|
||||
}
|
||||
|
||||
function ran(as){
|
||||
@ -1025,14 +1044,9 @@
|
||||
}, as.opt), acks = 0, stun = as.stun, tmp;
|
||||
(tmp = function(){ // this is not official yet, but quick solution to hack in for now.
|
||||
if(!stun){ return } stun.end = noop; // like with the earlier id, cheaper to make this flag a function so below callbacks do not have to do an extra type check.
|
||||
if(root.stun){ delete root.stun['s'+as.run] }
|
||||
if((tmp = root.stun) && stun === tmp._){
|
||||
if(!(tmp = stun.back) || tmp.end){
|
||||
delete root.stun; // ABC, ACB, BAC, BCA, CBA, CAB;
|
||||
}
|
||||
(root.stun||{})._ = tmp;
|
||||
}
|
||||
setTimeout.each(Object.keys(stun), function(cb){ if(cb = stun[cb]){cb()} }); // resume the stunned reads // Any perf reasons to CPU schedule this .keys( ?
|
||||
if(stun.the.to === stun && stun === stun.the.last){ delete root.stun }
|
||||
stun.off();
|
||||
setTimeout.each(Object.keys(stun = stun.add||''), function(cb){ if(cb = stun[cb]){cb()} }); // resume the stunned reads // Any perf reasons to CPU schedule this .keys( ?
|
||||
}).hatch = tmp; // this is not official yet ^
|
||||
//console.log(1, "PUT", as.run, as.graph);
|
||||
(as.via._).on('out', {put: as.out = as.graph, opt: as.opt, '#': ask, _: tmp});
|
||||
|
@ -18,7 +18,7 @@
|
||||
if(files[++i]){ upload.drop(files,i) }
|
||||
return false;
|
||||
}
|
||||
reader = new FileReader();
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
cb({file: files[i], event: e, id: i}, upload);
|
||||
if(files[++i]){ upload.drop(files,i) }
|
||||
|
134
sea.js
134
sea.js
@ -278,7 +278,7 @@
|
||||
|
||||
SEA.work = SEA.work || (async (data, pair, cb, opt) => { try { // used to be named `proof`
|
||||
var salt = (pair||{}).epub || pair; // epub not recommended, salt should be random!
|
||||
var opt = opt || {};
|
||||
opt = opt || {};
|
||||
if(salt instanceof Function){
|
||||
cb = salt;
|
||||
salt = u;
|
||||
@ -495,11 +495,13 @@
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}catch(e){
|
||||
}catch(e){ try{
|
||||
buf = shim.Buffer.from(json.s, 'utf8') // AUTO BACKWARD OLD UTF8 DATA!
|
||||
sig = new Uint8Array(buf)
|
||||
check = await (shim.ossl || shim.subtle).verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, sig, new Uint8Array(hash))
|
||||
}catch(e){
|
||||
if(!check){ throw "Signature did not match." }
|
||||
}
|
||||
}
|
||||
var r = check? await S.parse(json.m) : u;
|
||||
O.fall_soul = tmp['#']; O.fall_key = tmp['.']; O.fall_val = data; O.fall_state = tmp['>'];
|
||||
@ -517,7 +519,7 @@
|
||||
|
||||
const importGen = async (key, salt, opt) => {
|
||||
//const combo = shim.Buffer.concat([shim.Buffer.from(key, 'utf8'), salt || shim.random(8)]).toString('utf8') // old
|
||||
var opt = opt || {};
|
||||
opt = opt || {};
|
||||
const combo = key + (salt || shim.random(8)).toString('utf8'); // new
|
||||
const hash = shim.Buffer.from(await sha256hash(combo), 'binary')
|
||||
|
||||
@ -1260,7 +1262,7 @@
|
||||
})(USE, './share');
|
||||
|
||||
;USE(function(module){
|
||||
var SEA = USE('./sea'), noop = function(){}, u;
|
||||
var SEA = USE('./sea'), S = USE('./settings'), noop = function() {}, u;
|
||||
var Gun = (''+u != typeof window)? (window.Gun||{on:noop}) : USE((''+u === typeof MODULE?'.':'')+'./gun', 1);
|
||||
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
|
||||
|
||||
@ -1291,7 +1293,6 @@
|
||||
function check(msg){ // REVISE / IMPROVE, NO NEED TO PASS MSG/EVE EACH SUB?
|
||||
var eve = this, at = eve.as, put = msg.put, soul = put['#'], key = put['.'], val = put[':'], state = put['>'], id = msg['#'], tmp;
|
||||
if(!soul || !key){ return }
|
||||
//console.log('check', put, msg);
|
||||
if((msg._||'').faith && (at.opt||'').faith && 'function' == typeof msg._){
|
||||
SEA.opt.pack(put, function(raw){
|
||||
SEA.verify(raw, false, function(data){ // this is synchronous if false
|
||||
@ -1343,33 +1344,106 @@
|
||||
if(key === link_is(val)){ return eve.to.next(msg) } // and the ID must be EXACTLY equal to its property
|
||||
no("Alias not same!"); // that way nobody can tamper with the list of public keys.
|
||||
};
|
||||
check.pub = function(eve, msg, val, key, soul, at, no, user, pub){ var tmp; // Example: {_:#~asdf, hello:'world'~fdsa}}
|
||||
if('pub' === key && '~'+pub === soul){
|
||||
if(val === pub){ return eve.to.next(msg) } // the account MUST match `pub` property that equals the ID of the public key.
|
||||
return no("Account not same!");
|
||||
}
|
||||
if((tmp = user.is) && pub === tmp.pub){// && (tmp = msg._.$) && (tmp = tmp._) && tmp !== tmp.root){
|
||||
SEA.opt.pack(msg.put, function(raw){
|
||||
SEA.sign(raw, (user._).sea, function(data){
|
||||
if(u === data){ return no(SEA.err || 'Signature fail.') }
|
||||
if(tmp = link_is(val)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||
JSON.stringifyAsync({':': tmp = SEA.opt.unpack(data.m), '~': data.s}, function(err,s){
|
||||
if(err){ return no(err || "Stringify error.") }
|
||||
msg.put['='] = tmp;
|
||||
msg.put[':'] = s;
|
||||
eve.to.next(msg);
|
||||
check.pub = async function(eve, msg, val, key, soul, at, no, user, pub){ var tmp // Example: {_:#~asdf, hello:'world'~fdsa}}
|
||||
const raw = await S.parse(val) || {}
|
||||
const verify = (certificate, certificant, cb) => {
|
||||
if (certificate.m && certificate.s && certificant && pub)
|
||||
// now verify certificate
|
||||
return SEA.verify(certificate, pub, data => { // check if "pub" (of the graph owner) really issued this cert
|
||||
if (u !== data && u !== data.e && msg.put['>'] && msg.put['>'] > parseFloat(data.e)) return no("Certificate expired.") // certificate expired
|
||||
// "data.c" = a list of certificants/certified users
|
||||
// "data.w" = lex WRITE permission, in the future, there will be "data.r" which means lex READ permission
|
||||
if (u !== data && data.c && data.w && (data.c === certificant || data.c.indexOf('*' || certificant) > -1)) {
|
||||
// ok, now "certificant" is in the "certificants" list, but is "path" allowed? Check path
|
||||
let path = soul.indexOf('/') > -1 ? soul.replace(soul.substring(0, soul.indexOf('/') + 1), '') : ''
|
||||
String.match = String.match || Gun.text.match
|
||||
const w = Array.isArray(data.w) ? data.w : typeof data.w === 'object' || typeof data.w === 'string' ? [data.w] : []
|
||||
for (const lex of w) {
|
||||
if ((String.match(path, lex['#']) && String.match(key, lex['.'])) || (!lex['.'] && String.match(path, lex['#'])) || (!lex['#'] && String.match(key, lex['.'])) || String.match((path ? path + '/' + key : key), lex['#'] || lex)) {
|
||||
// is Certificant forced to present in Path
|
||||
if (lex['+'] && lex['+'].indexOf('*') > -1 && path && path.indexOf(certificant) == -1 && key.indexOf(certificant) == -1) return no(`Path "${path}" or key "${key}" must contain string "${certificant}".`)
|
||||
// path is allowed, but is there any WRITE blacklist? Check it out
|
||||
if (data.wb && (typeof data.wb === 'string' || ((data.wb || {})['#']))) { // "data.wb" = path to the WRITE blacklist
|
||||
var root = at.$.back(-1)
|
||||
if (typeof data.wb === 'string' && '~' !== data.wb.slice(0, 1)) root = root.get('~' + pub)
|
||||
return root.get(data.wb).get(certificant).once(value => {
|
||||
if (value && (value === 1 || value === true)) return no("Certificant blacklisted.")
|
||||
return cb(data)
|
||||
})
|
||||
}
|
||||
return cb(data)
|
||||
}
|
||||
}
|
||||
return no("Certificate verification fail.")
|
||||
}
|
||||
})
|
||||
}, {raw: 1})});
|
||||
return
|
||||
}
|
||||
|
||||
if ('pub' === key && '~' + pub === soul) {
|
||||
if (val === pub) return eve.to.next(msg) // the account MUST match `pub` property that equals the ID of the public key.
|
||||
return no("Account not same!")
|
||||
}
|
||||
|
||||
if ((tmp = user.is) && tmp.pub && !raw['*'] && !raw['+'] && (pub === tmp.pub || (pub !== tmp.pub && ((msg._.msg || {}).opt || {}).cert))){
|
||||
SEA.opt.pack(msg.put, packed => {
|
||||
SEA.sign(packed, (user._).sea, async function(data) {
|
||||
if (u === data) return no(SEA.err || 'Signature fail.')
|
||||
msg.put[':'] = {':': tmp = SEA.opt.unpack(data.m), '~': data.s}
|
||||
msg.put['='] = tmp
|
||||
|
||||
// if writing to own graph, just allow it
|
||||
if (pub === user.is.pub) {
|
||||
if (tmp = link_is(val)) (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1
|
||||
JSON.stringifyAsync(msg.put[':'], function(err,s){
|
||||
if(err){ return no(err || "Stringify error.") }
|
||||
msg.put[':'] = s;
|
||||
return eve.to.next(msg);
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// if writing to other's graph, check if cert exists then try to inject cert into put, also inject self pub so that everyone can verify the put
|
||||
if (pub !== user.is.pub && ((msg._.msg || {}).opt || {}).cert) {
|
||||
const cert = await S.parse(msg._.msg.opt.cert)
|
||||
// even if cert exists, we must verify it
|
||||
if (cert && cert.m && cert.s)
|
||||
verify(cert, user.is.pub, _ => {
|
||||
msg.put[':']['+'] = cert // '+' is a certificate
|
||||
msg.put[':']['*'] = user.is.pub // '*' is pub of the user who puts
|
||||
JSON.stringifyAsync(msg.put[':'], function(err,s){
|
||||
if(err){ return no(err || "Stringify error.") }
|
||||
msg.put[':'] = s;
|
||||
return eve.to.next(msg);
|
||||
})
|
||||
return
|
||||
})
|
||||
}
|
||||
}, {raw: 1})
|
||||
})
|
||||
return;
|
||||
}
|
||||
SEA.opt.pack(msg.put, function(raw){
|
||||
SEA.verify(raw, pub, function(data){ var tmp;
|
||||
data = SEA.opt.unpack(data);
|
||||
if(u === data){ return no("Unverified data.") } // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
|
||||
if((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)){ (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1 }
|
||||
msg.put['='] = data;
|
||||
eve.to.next(msg);
|
||||
})});
|
||||
|
||||
SEA.opt.pack(msg.put, packed => {
|
||||
SEA.verify(packed, raw['*'] || pub, function(data){ var tmp;
|
||||
data = SEA.opt.unpack(data);
|
||||
if (u === data) return no("Unverified data.") // make sure the signature matches the account it claims to be on. // reject any updates that are signed with a mismatched account.
|
||||
if ((tmp = link_is(data)) && pub === SEA.opt.pub(tmp)) (at.sea.own[tmp] = at.sea.own[tmp] || {})[pub] = 1
|
||||
|
||||
// check if cert ('+') and putter's pub ('*') exist
|
||||
if (raw['+'] && raw['+']['m'] && raw['+']['s'] && raw['*'])
|
||||
// now verify certificate
|
||||
verify(raw['+'], raw['*'], _ => {
|
||||
msg.put['='] = data;
|
||||
return eve.to.next(msg);
|
||||
})
|
||||
else {
|
||||
msg.put['='] = data;
|
||||
return eve.to.next(msg);
|
||||
}
|
||||
});
|
||||
})
|
||||
return
|
||||
};
|
||||
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
|
||||
if(at.opt.secure){ return no("Soul missing public key at '" + key + "'.") }
|
||||
@ -1427,4 +1501,4 @@
|
||||
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
||||
|
||||
})(USE, './index');
|
||||
}());
|
||||
}());
|
||||
|
179
test/sea/sea.js
179
test/sea/sea.js
@ -541,6 +541,185 @@ describe('SEA', function(){
|
||||
gun.user().auth(alice);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CERTIFY', function () {
|
||||
var gun = Gun()
|
||||
var user = gun.user()
|
||||
|
||||
it('Certify: Simple', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var dave = await SEA.pair()
|
||||
var cert = await SEA.certify(bob, {"*": "private"}, alice)
|
||||
|
||||
user.leave()
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, () => {
|
||||
// Bob reads
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
user.leave()
|
||||
// everyone reads
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
user.auth(dave, () => {
|
||||
// Dave reads
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
user.leave()
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('Certify: Attack', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var cert = await SEA.certify(bob, {"*": "private"}, alice)
|
||||
|
||||
user.leave()
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("wrongway")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.be.ok()
|
||||
user.leave()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('Certify: Public inbox', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var cert = await SEA.certify('*', [{"*": "test", "+": "*"}, {"*": "inbox", "+": "*"}], alice)
|
||||
|
||||
user.leave()
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("inbox")
|
||||
.get(user.is.pub)
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.not.be.ok()
|
||||
user.leave()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('Certify: Expiry', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var cert = await SEA.certify(bob, {"*": "private"}, alice, null, {
|
||||
expiry: Gun.state() - 100, // expired 100 miliseconds ago
|
||||
})
|
||||
|
||||
user.leave()
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.be.ok()
|
||||
user.leave()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('Certify: Path or Key must contain Certificant Pub', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var cert = await SEA.certify(bob, {"*": "private", "+": "*"}, alice)
|
||||
|
||||
user.leave()
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get('wrongway')
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.be.ok()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get(bob.pub)
|
||||
.get('today')
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.not.be.ok()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get(bob.pub)
|
||||
.get('today')
|
||||
.once(_data => {
|
||||
expect(_data).to.be(data)
|
||||
done()
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('Certify: Advanced - Blacklist', function(done){(async function(){
|
||||
var alice = await SEA.pair()
|
||||
var dave = await SEA.pair()
|
||||
var bob = await SEA.pair()
|
||||
var cert = await SEA.certify(bob, {"*": "private"}, alice, null, {
|
||||
expiry: Gun.state() + 5000, // expires in 5 seconds
|
||||
blacklist: 'blacklist' // path to blacklist in Alice's graph
|
||||
})
|
||||
|
||||
// Alice points her blacklist to Dave's graph
|
||||
user.leave()
|
||||
user.auth(alice, async () => {
|
||||
await user.get('blacklist').put({'#': '~'+dave.pub+'/blacklist'})
|
||||
await user.leave()
|
||||
|
||||
// Dave logins, he adds Bob to his blacklist, which is connected to the certificate that Alice issued for Bob
|
||||
user.auth(dave, async () => {
|
||||
await user.get('blacklist').get(bob.pub).put(true)
|
||||
await user.leave()
|
||||
|
||||
// Bob logins and tries to hack Alice
|
||||
user.auth(bob, async () => {
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.be.ok()
|
||||
user.leave()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
})
|
||||
})
|
||||
}())})
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user