mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
Improve SEA: .auth, .certify, check.pub (#1130)
* Improve SEA.certify - Improve certify() logic - "Blacklist" renamed to "block". BLACK LIVES MATTER! - Fix user.is.alias, now it doesn't contain full pair any more. * SEA.certify wire logic tests The "block" (former "blacklist") feature is not working yet (due to a bug in gun) * skip certify block feature unit test
This commit is contained in:
parent
89c5286a52
commit
63b0043076
37
sea.js
37
sea.js
@ -674,23 +674,21 @@
|
||||
// This is to certify that a group of "certificants" can "put" anything at a group of matched "paths" to the certificate authority's graph
|
||||
SEA.certify = SEA.certify || (async (certificants, policy = {}, authority, cb, opt = {}) => { try {
|
||||
/*
|
||||
The Certify Protocol was made out of love by a Vietnamese code enthusiast. Vietnamese people around the world deserve respect!
|
||||
IMPORTANT: A Certificate is like a Signature. No one knows who (authority) created/signed a cert until you put it into their graph.
|
||||
"certificants": '*' or a String (Bob.pub) || an Object that contains "pub" as a key || an array of [object || string]. These people will have the rights.
|
||||
"policy": A string ('inbox'), or a RAD/LEX object {'*': 'inbox'}, or an Array of RAD/LEX objects or strings. RAD/LEX object can contain key "?" with indexOf("*") > -1 to force key equals certificant pub. This rule is used to check against soul+'/'+key using Gun.text.match or String.match.
|
||||
"authority": Key pair or priv of the certificate authority.
|
||||
"cb": A callback function after all things are done.
|
||||
"opt": If opt.expiry (a timestamp) is set, SEA won't sync data after opt.expiry. If opt.blacklist is set, SEA will look for blacklist before syncing.
|
||||
"opt": If opt.expiry (a timestamp) is set, SEA won't sync data after opt.expiry. If opt.block is set, SEA will look for block before syncing.
|
||||
*/
|
||||
console.log('SEA.certify() is an early experimental community supported method that may change API behavior without warning in any future version.')
|
||||
|
||||
certificants = (() => {
|
||||
var data = []
|
||||
if (certificants) {
|
||||
if ((typeof certificants === 'string' || Array.isArray(certificants)) && certificants.indexOf('*') !== -1) return '*'
|
||||
if (typeof certificants === 'string') {
|
||||
return certificants
|
||||
}
|
||||
|
||||
if ((typeof certificants === 'string' || Array.isArray(certificants)) && certificants.indexOf('*') > -1) return '*'
|
||||
if (typeof certificants === 'string') return certificants
|
||||
if (Array.isArray(certificants)) {
|
||||
if (certificants.length === 1 && certificants[0]) return typeof certificants[0] === 'object' && certificants[0].pub ? certificants[0].pub : typeof certificants[0] === 'string' ? certificants[0] : null
|
||||
certificants.map(certificant => {
|
||||
@ -702,7 +700,7 @@
|
||||
if (typeof certificants === 'object' && certificants.pub) return certificants.pub
|
||||
return data.length > 0 ? data : null
|
||||
}
|
||||
return null
|
||||
return
|
||||
})()
|
||||
|
||||
if (!certificants) return console.log("No certificant found.")
|
||||
@ -710,8 +708,11 @@
|
||||
const expiry = opt.expiry && (typeof opt.expiry === 'number' || typeof opt.expiry === 'string') ? parseFloat(opt.expiry) : null
|
||||
const readPolicy = (policy || {}).read ? policy.read : null
|
||||
const writePolicy = (policy || {}).write ? policy.write : typeof policy === 'string' || Array.isArray(policy) || policy["+"] || policy["#"] || policy["."] || policy["="] || policy["*"] || policy[">"] || policy["<"] ? policy : null
|
||||
const readBlacklist = ((opt || {}).blacklist || {}).read && (typeof opt.blacklist.read === 'string' || opt.blacklist.read['#']) ? opt.blacklist.read : null
|
||||
const writeBlacklist = typeof (opt || {}).blacklist === 'string' || (((opt || {}).blacklist || {}).write || {})['#'] ? opt.blacklist : ((opt || {}).blacklist || {}).write && (typeof opt.blacklist.write === 'string' || opt.blacklist.write['#']) ? opt.blacklist.write : null
|
||||
// The "blacklist" feature is now renamed to "block". Why ? BECAUSE BLACK LIVES MATTER!
|
||||
// We can now use 3 keys: block, blacklist, ban
|
||||
const block = (opt || {}).block || (opt || {}).blacklist || (opt || {}).ban || {}
|
||||
const readBlock = block.read && (typeof block.read === 'string' || (block.read || {})['#']) ? block.read : null
|
||||
const writeBlock = typeof block === 'string' ? block : block.write && (typeof block.write === 'string' || block.write['#']) ? block.write : null
|
||||
|
||||
if (!readPolicy && !writePolicy) return console.log("No policy found.")
|
||||
|
||||
@ -721,8 +722,8 @@
|
||||
...(expiry ? {e: expiry} : {}), // inject expiry if possible
|
||||
...(readPolicy ? {r: readPolicy } : {}), // "r" stands for read, which means read permission.
|
||||
...(writePolicy ? {w: writePolicy} : {}), // "w" stands for write, which means write permission.
|
||||
...(readBlacklist ? {rb: readBlacklist} : {}), // inject READ blacklist if possible
|
||||
...(writeBlacklist ? {wb: writeBlacklist} : {}), // inject WRITE blacklist if possible
|
||||
...(readBlock ? {rb: readBlock} : {}), // inject READ block if possible
|
||||
...(writeBlock ? {wb: writeBlock} : {}), // inject WRITE block if possible
|
||||
})
|
||||
|
||||
const certificate = await SEA.sign(data, authority, null, {raw:1})
|
||||
@ -934,7 +935,7 @@
|
||||
if(!act.h.ok || !act.i.ok){ return }
|
||||
cat.ing = false;
|
||||
cb({ok: 0, pub: act.pair.pub}); // callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
|
||||
if(noop === cb){ pair? gun.auth(pair) : gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
|
||||
if(noop === cb){ pair ? gun.auth(pair) : gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
|
||||
}
|
||||
root.get('~@'+alias).once(act.a);
|
||||
return gun;
|
||||
@ -1025,7 +1026,7 @@
|
||||
at = user._ = root.get('~'+pair.pub)._;
|
||||
at.opt = upt;
|
||||
// add our credentials in-memory only to our root user instance
|
||||
user.is = {pub: pair.pub, epub: pair.epub, alias: alias || pair};
|
||||
user.is = {pub: pair.pub, epub: pair.epub, alias: alias || pair.pub};
|
||||
at.sea = act.pair;
|
||||
cat.ing = false;
|
||||
try{if(pass && u == (obj_ify(cat.root.graph['~'+pair.pub].auth)||'')[':']){ opt.shuffle = opt.change = pass; } }catch(e){} // migrate UTF8 & Shuffle!
|
||||
@ -1368,12 +1369,12 @@
|
||||
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)
|
||||
// path is allowed, but is there any WRITE block? Check it out
|
||||
if (data.wb && (typeof data.wb === 'string' || ((data.wb || {})['#']))) { // "data.wb" = path to the WRITE block
|
||||
var root = eve.as.root.$.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.")
|
||||
if (value && (value === 1 || value === true)) return no(`Certificant ${certificant} blocked.`)
|
||||
return cb(data)
|
||||
})
|
||||
}
|
||||
@ -1507,4 +1508,4 @@
|
||||
// TODO: Potential bug? If pub/priv key starts with `-`? IDK how possible.
|
||||
|
||||
})(USE, './index');
|
||||
}());
|
||||
}());
|
||||
|
366
test/sea/sea.js
366
test/sea/sea.js
@ -551,191 +551,6 @@ describe('SEA', function(){
|
||||
});
|
||||
});
|
||||
|
||||
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)
|
||||
user.leave();
|
||||
done()
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it.skip('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
|
||||
})
|
||||
console.log(111111);
|
||||
// Alice points her blacklist to Dave's graph
|
||||
user.leave()
|
||||
user.auth(alice, async () => {
|
||||
console.log("meeeeoooooow");
|
||||
var ref = gun.get('~'+dave.pub+'/blacklist');
|
||||
await user.get('blacklist').put(ref);
|
||||
user.leave()
|
||||
console.log(2222222);
|
||||
|
||||
// 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)
|
||||
user.leave()
|
||||
console.log(333333);
|
||||
|
||||
// Bob logins and tries to hack Alice
|
||||
user.auth(bob, async () => {
|
||||
console.log(4444444);
|
||||
var data = Gun.state().toString(36)
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, ack => {
|
||||
console.log(555555);
|
||||
expect(ack.err).to.be.ok()
|
||||
user.leave()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
})
|
||||
})
|
||||
}())})
|
||||
});
|
||||
|
||||
describe('node', function(){
|
||||
var u;
|
||||
if(''+u === typeof process){ return }
|
||||
@ -743,6 +558,187 @@ describe('SEA', function(){
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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)
|
||||
user.leave();
|
||||
done()
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it.skip('Certify: Advanced - Block', 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
|
||||
block: 'block' // path to block in Alice's graph
|
||||
})
|
||||
|
||||
// Alice points her block to Dave's graph
|
||||
await user.auth(alice)
|
||||
if (user.is) {
|
||||
await user.get('block').put({'#': '~'+dave.pub+'/block'});
|
||||
await user.leave()
|
||||
}
|
||||
|
||||
// Dave logins, he adds Bob to his block, which is connected to the certificate that Alice issued for Bob
|
||||
await user.auth(dave)
|
||||
if (user.is) {
|
||||
await user.get('block').get(bob.pub).put(true)
|
||||
await user.leave()
|
||||
}
|
||||
|
||||
// Bob logins and tries to hack Alice
|
||||
await user.auth(bob)
|
||||
if (user.is) {
|
||||
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