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:
mimiza 2021-09-06 12:40:36 +07:00 committed by GitHub
parent 89c5286a52
commit 63b0043076
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 200 additions and 203 deletions

33
sea.js
View File

@ -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 // 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 { 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. 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. "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. "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. "authority": Key pair or priv of the certificate authority.
"cb": A callback function after all things are done. "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.') console.log('SEA.certify() is an early experimental community supported method that may change API behavior without warning in any future version.')
certificants = (() => { certificants = (() => {
var data = [] var data = []
if (certificants) { if (certificants) {
if ((typeof certificants === 'string' || Array.isArray(certificants)) && certificants.indexOf('*') !== -1) return '*' if ((typeof certificants === 'string' || Array.isArray(certificants)) && certificants.indexOf('*') > -1) return '*'
if (typeof certificants === 'string') { if (typeof certificants === 'string') return certificants
return certificants
}
if (Array.isArray(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 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 => { certificants.map(certificant => {
@ -702,7 +700,7 @@
if (typeof certificants === 'object' && certificants.pub) return certificants.pub if (typeof certificants === 'object' && certificants.pub) return certificants.pub
return data.length > 0 ? data : null return data.length > 0 ? data : null
} }
return null return
})() })()
if (!certificants) return console.log("No certificant found.") 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 expiry = opt.expiry && (typeof opt.expiry === 'number' || typeof opt.expiry === 'string') ? parseFloat(opt.expiry) : null
const readPolicy = (policy || {}).read ? policy.read : 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 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 // The "blacklist" feature is now renamed to "block". Why ? BECAUSE BLACK LIVES MATTER!
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 // 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.") if (!readPolicy && !writePolicy) return console.log("No policy found.")
@ -721,8 +722,8 @@
...(expiry ? {e: expiry} : {}), // inject expiry if possible ...(expiry ? {e: expiry} : {}), // inject expiry if possible
...(readPolicy ? {r: readPolicy } : {}), // "r" stands for read, which means read permission. ...(readPolicy ? {r: readPolicy } : {}), // "r" stands for read, which means read permission.
...(writePolicy ? {w: writePolicy} : {}), // "w" stands for write, which means write permission. ...(writePolicy ? {w: writePolicy} : {}), // "w" stands for write, which means write permission.
...(readBlacklist ? {rb: readBlacklist} : {}), // inject READ blacklist if possible ...(readBlock ? {rb: readBlock} : {}), // inject READ block if possible
...(writeBlacklist ? {wb: writeBlacklist} : {}), // inject WRITE blacklist if possible ...(writeBlock ? {wb: writeBlock} : {}), // inject WRITE block if possible
}) })
const certificate = await SEA.sign(data, authority, null, {raw:1}) const certificate = await SEA.sign(data, authority, null, {raw:1})
@ -1025,7 +1026,7 @@
at = user._ = root.get('~'+pair.pub)._; at = user._ = root.get('~'+pair.pub)._;
at.opt = upt; at.opt = upt;
// add our credentials in-memory only to our root user instance // 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; at.sea = act.pair;
cat.ing = false; 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! 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)) { 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 // 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}".`) 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 // 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 blacklist if (data.wb && (typeof data.wb === 'string' || ((data.wb || {})['#']))) { // "data.wb" = path to the WRITE block
var root = at.$.back(-1) var root = eve.as.root.$.back(-1)
if (typeof data.wb === 'string' && '~' !== data.wb.slice(0, 1)) root = root.get('~' + pub) if (typeof data.wb === 'string' && '~' !== data.wb.slice(0, 1)) root = root.get('~' + pub)
return root.get(data.wb).get(certificant).once(value => { 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) return cb(data)
}) })
} }

View File

@ -551,6 +551,14 @@ describe('SEA', function(){
}); });
}); });
describe('node', function(){
var u;
if(''+u === typeof process){ return }
console.log("REMEMBER TO RUN mocha test/sea/nodeauth !!!!");
});
});
describe('CERTIFY', function () { describe('CERTIFY', function () {
var gun = Gun() var gun = Gun()
var user = gun.user() var user = gun.user()
@ -692,57 +700,45 @@ describe('SEA', function(){
}) })
}())}) }())})
it.skip('Certify: Advanced - Blacklist', function(done){(async function(){ it.skip('Certify: Advanced - Block', function(done){(async function(){
var alice = await SEA.pair() var alice = await SEA.pair()
var dave = await SEA.pair() var dave = await SEA.pair()
var bob = await SEA.pair() var bob = await SEA.pair()
var cert = await SEA.certify(bob, {"*": "private"}, alice, null, { var cert = await SEA.certify(bob, {"*": "private"}, alice, null, {
expiry: Gun.state() + 5000, // expires in 5 seconds expiry: Gun.state() + 5000, // expires in 5 seconds
blacklist: 'blacklist' // path to blacklist in Alice's graph block: 'block' // path to block 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 // Alice points her block to Dave's graph
user.auth(dave, async () => { await user.auth(alice)
await user.get('blacklist').get(bob.pub).put(true) if (user.is) {
user.leave() await user.get('block').put({'#': '~'+dave.pub+'/block'});
console.log(333333); 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 // Bob logins and tries to hack Alice
user.auth(bob, async () => { await user.auth(bob)
console.log(4444444); if (user.is) {
var data = Gun.state().toString(36) var data = Gun.state().toString(36)
gun.get("~" + alice.pub) gun.get("~" + alice.pub)
.get("private") .get("private")
.get("asdf") .get("asdf")
.get("qwerty") .get("qwerty")
.put(data, ack => { .put(data, ack => {
console.log(555555);
expect(ack.err).to.be.ok() expect(ack.err).to.be.ok()
user.leave() user.leave()
done() done()
}, { opt: { cert } }) }, { opt: { cert } })
}) }
})
})
}())}) }())})
}); });
describe('node', function(){
var u;
if(''+u === typeof process){ return }
console.log("REMEMBER TO RUN mocha test/sea/nodeauth !!!!");
});
});
}) })
}()); }());