mirror of
https://github.com/amark/gun.git
synced 2025-11-27 15:54:04 +00:00
Merge branch 'master' of github.com:amark/gun
This commit is contained in:
commit
227685cedf
75
.github/workflows/ci.yml
vendored
Normal file
75
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
name: ci
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
project: 'release-node'
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x] # [12.x, 14.x]
|
||||
os: [ubuntu-latest] #, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# verify the version in package.json matches the release tag
|
||||
- name: Version
|
||||
uses: tcurdt/action-verify-version-npm@master
|
||||
|
||||
- name: Setup Node ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Cache
|
||||
id: cache-modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ matrix.node-version }}-${{ runner.OS }}-build-${{ hashFiles('package.json') }}
|
||||
|
||||
- name: Install
|
||||
if: steps.cache-modules.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
# create release artifacts to publish as github release
|
||||
# - name: Upload
|
||||
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: ${{ env.project }}_${{ matrix.os }}_${{ matrix.node-version }}
|
||||
# path: |
|
||||
# !.git
|
||||
# !.github
|
||||
# !node_modules
|
||||
# .
|
||||
|
||||
release:
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
# - name: Download
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# path: artifacts
|
||||
# - name: Archives
|
||||
# run: find artifacts -mindepth 1 -maxdepth 1 -exec tar -C {} -cvzf {}.tgz . \;
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
# with:
|
||||
# files: |
|
||||
# artifacts/*.tgz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
17
.github/workflows/cleanup.yml
vendored
Normal file
17
.github/workflows/cleanup.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: cleanup
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '1 1 * * 1' # once a week clean out old artifacts
|
||||
|
||||
jobs:
|
||||
|
||||
expire:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Expire Artifacts
|
||||
uses: kolpav/purge-artifacts-action@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
expire-in: 1hour
|
||||
35
.github/workflows/dockerhub.yml
vendored
Normal file
35
.github/workflows/dockerhub.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
name: dockerhub
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
env:
|
||||
project: 'release-node'
|
||||
|
||||
jobs:
|
||||
|
||||
dockerhub:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
docker build -t ${{ env.project }} .
|
||||
|
||||
- name: Login
|
||||
env:
|
||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
run: echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
|
||||
|
||||
- name: Tag
|
||||
run: |
|
||||
docker tag ${{ env.project }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.project }}:${GITHUB_REF/refs\/tags\/v/}
|
||||
docker tag ${{ env.project }} ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.project }}:latest
|
||||
|
||||
- name: Push
|
||||
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.project }}
|
||||
22
.github/workflows/npm.yml
vendored
Normal file
22
.github/workflows/npm.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
name: npm
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
|
||||
npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
||||
run: |
|
||||
npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN
|
||||
npm install
|
||||
npm publish --access=public
|
||||
15
lib/meta.js
15
lib/meta.js
@ -168,7 +168,8 @@
|
||||
}
|
||||
}
|
||||
var $m = $('<div>').attr('id', 'meta');
|
||||
$m.append($('<span>').html('☰').addClass('meta-start'));
|
||||
//$m.append($('<span>').html('☰').addClass('meta-start'));
|
||||
$m.append($('<span>').html('+').addClass('meta-start'));
|
||||
$m.append($('<div>').addClass('meta-menu meta-none').append('<ul>'));
|
||||
$m.on('mouseenter', function(){
|
||||
if (meta.flip.active || meta.flip.is()) return;
|
||||
@ -186,7 +187,6 @@
|
||||
position: 'fixed',
|
||||
bottom: '2em',
|
||||
right: '2em',
|
||||
background: 'white',
|
||||
'font-size': '18pt',
|
||||
'font-family': 'Tahoma, arial',
|
||||
'border-radius': '1em',
|
||||
@ -197,15 +197,15 @@
|
||||
width: '2em',
|
||||
height: '2em',
|
||||
outline: 'none',
|
||||
color: '#000044',
|
||||
overflow: 'visible',
|
||||
background: 'rgba(0,0,0,0.5)', color: 'white',
|
||||
transition: 'all 0.2s ease-in'
|
||||
},
|
||||
'#meta *': {outline: 'none'},
|
||||
'#meta .meta-none': {display: 'none'},
|
||||
'#meta span': {'line-height': '2em'},
|
||||
'#meta .meta-menu': {
|
||||
background: 'rgba(0,0,0,0.1)',
|
||||
background: 'rgba(0,0,0,0.2)',
|
||||
width: '12em',
|
||||
right: '-2em',
|
||||
bottom: '-2em',
|
||||
@ -224,16 +224,15 @@
|
||||
'#meta .meta-menu ul li': {
|
||||
display: 'block',
|
||||
'float': 'right',
|
||||
background: 'white',
|
||||
opacity: 0.7,
|
||||
padding: '0.5em 1em',
|
||||
'border-radius': '1em',
|
||||
'margin-left': '0.25em',
|
||||
'margin-top': '0.25em',
|
||||
background: 'rgba(0,0,0,0.2)', 'backdrop-filter': 'blur(10px)', color: 'white',
|
||||
'cursor': 'pointer'
|
||||
},
|
||||
'#meta .meta-menu ul li:hover': {
|
||||
opacity: 1
|
||||
background: 'rgba(0,0,0,0.5)'
|
||||
},
|
||||
'#meta a': {color: 'black'},
|
||||
'#meta .meta-menu ul:before': {
|
||||
@ -289,4 +288,4 @@
|
||||
meta.flip()
|
||||
})
|
||||
})(USE, './metaEvents');
|
||||
}());
|
||||
}());
|
||||
|
||||
146
sea.js
146
sea.js
@ -650,6 +650,67 @@
|
||||
module.exports = SEA.secret;
|
||||
})(USE, './secret');
|
||||
|
||||
;USE(function(module){
|
||||
var SEA = USE('./root');
|
||||
|
||||
// This is to certify that a group of "certificants" can "put" anything at a group of matched "paths" to the certificate issuer's graph
|
||||
SEA.certify = SEA.certify || (async (certificants, patterns, issuer, cb, opt = {}) => { try {
|
||||
/*
|
||||
IMPORTANT: A Certificate is like a Signature. No one knows who (issuer) created/signed a cert until you put it into their graph.
|
||||
"certificants": A string (~Bobpub) || a pair || an array of pubs/pairs. These people will have the rights.
|
||||
"patterns": A string (^inbox.*), or an array of strings [^inbox.*, ^secret\-group.*]. These patterns will be used to check against soul+'/'+key
|
||||
"issuer": Key pair or priv of the certificate issuer
|
||||
"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
|
||||
*/
|
||||
|
||||
// We need some logic here to verify that all params are valid
|
||||
|
||||
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') {
|
||||
data.push(certificants)
|
||||
}
|
||||
|
||||
if (Array.isArray(certificants)) {
|
||||
certificants.map(person => {
|
||||
if (typeof person ==='string') data.push(person)
|
||||
else if (typeof person === 'object' && person.pub) data.push(person.pub)
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof certificants === 'object' && certificants.pub) data.push(certificants.pub)
|
||||
}
|
||||
return data
|
||||
})()
|
||||
|
||||
patterns = patterns ? typeof patterns === 'string' ? [patterns] : Array.isArray(patterns) ? patterns : null : null
|
||||
|
||||
const data = JSON.stringify({
|
||||
c: certificants,
|
||||
p: patterns,
|
||||
...(opt.expiry && typeof opt.expiry === 'number' ? {e: opt.expiry} : {}) // inject expiry if possible
|
||||
})
|
||||
|
||||
const certificate = await SEA.sign(data, issuer, null, {raw:1})
|
||||
|
||||
var r = certificate
|
||||
if(!opt.raw){ r = 'SEA'+JSON.stringify(r) }
|
||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||
return r;
|
||||
} catch(e) {
|
||||
SEA.err = e;
|
||||
if(SEA.throw){ throw e }
|
||||
if(cb){ cb() }
|
||||
return;
|
||||
}});
|
||||
|
||||
module.exports = SEA.certify;
|
||||
})(USE, './certify');
|
||||
|
||||
;USE(function(module){
|
||||
var shim = USE('./shim');
|
||||
// Practical examples about usage found in tests.
|
||||
@ -659,6 +720,7 @@
|
||||
SEA.verify = USE('./verify');
|
||||
SEA.encrypt = USE('./encrypt');
|
||||
SEA.decrypt = USE('./decrypt');
|
||||
SEA.certify = USE('./certify');
|
||||
//SEA.opt.aeskey = USE('./aeskey'); // not official! // this causes problems in latest WebCrypto.
|
||||
|
||||
SEA.random = SEA.random || shim.random;
|
||||
@ -1137,6 +1199,7 @@
|
||||
|
||||
;USE(function(module){
|
||||
var SEA = USE('./sea')
|
||||
var S = USE('./settings')
|
||||
var Gun = SEA.Gun;
|
||||
// After we have a GUN extension to make user registration/login easy, we then need to handle everything else.
|
||||
|
||||
@ -1217,6 +1280,7 @@
|
||||
return; // omit!
|
||||
}
|
||||
}
|
||||
|
||||
if('~@' === soul){ // special case for shared system data, the list of aliases.
|
||||
check.alias(eve, msg, val, key, soul, at, no); return;
|
||||
}
|
||||
@ -1249,27 +1313,83 @@
|
||||
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!");
|
||||
check.pub = function(eve, msg, val, key, soul, at, no, user, pub){ var tmp // Example: {_:#~asdf, hello:'world'~fdsa}}
|
||||
const raw = 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.")
|
||||
// "data.c" = a list of certificants/certified users, "data.p" = a list of allowed patterns
|
||||
if (u !== data && data.c && data.p && (data.c.indexOf('*') || data.c.indexOf(certificant))) {
|
||||
// ok, now "certificant" is in the "certificants" list, but is "path" allowed? Check path
|
||||
let path = soul + '/' + key
|
||||
path = path.replace(path.substring(0, path.indexOf('/') + 1), '')
|
||||
for (p of data.p) {
|
||||
if (new RegExp(p).test(path)) {
|
||||
return cb(data)
|
||||
}
|
||||
}
|
||||
return no("Certificate verification fail.")
|
||||
}
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
if((tmp = user.is) && pub === tmp.pub){
|
||||
|
||||
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._.out || {}).opt || {}).cert))){
|
||||
SEA.sign(SEA.opt.pack(msg.put), (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 }
|
||||
msg.put[':'] = JSON.stringify({':': tmp = SEA.opt.unpack(data.m), '~': data.s});
|
||||
msg.put['='] = tmp;
|
||||
eve.to.next(msg);
|
||||
}, {raw: 1});
|
||||
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 }
|
||||
msg.put[':'] = JSON.stringify(msg.put[':'])
|
||||
return eve.to.next(msg)
|
||||
}
|
||||
|
||||
// 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._.out || {}).opt || {}).cert) {
|
||||
const cert = S.parse(msg._.out.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
|
||||
msg.put[':'] = JSON.stringify(msg.put[':'])
|
||||
return eve.to.next(msg)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, {raw: 1})
|
||||
return;
|
||||
}
|
||||
SEA.verify(SEA.opt.pack(msg.put), pub, function(data){ var tmp;
|
||||
|
||||
SEA.verify(SEA.opt.pack(msg.put), 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
};
|
||||
check.any = function(eve, msg, val, key, soul, at, no, user){ var tmp, pub;
|
||||
|
||||
115
test/sea/sea.js
115
test/sea/sea.js
@ -1,3 +1,5 @@
|
||||
const expect = require('../expect')
|
||||
|
||||
var root;
|
||||
var Gun;
|
||||
(function(){
|
||||
@ -523,6 +525,119 @@ describe('SEA', function(){
|
||||
gun.user().auth(alice);
|
||||
});
|
||||
});
|
||||
|
||||
var alice = {
|
||||
epriv: "Odtnqn-gng-NCLAULCdhxcG7KE26WSWdnNTBSYf8Dsw",
|
||||
epub:
|
||||
"rOWulaGGaNOKhrS9XtZUbdWjcIfTM5k5pImolyNwLe0.9Ks7JRrOQl3e401dSgCGlNWgvIC_DQm0EA9jGKXBDg0",
|
||||
priv: "ijke9inZcbIpNUy5p3wiMRxUvqM12xU8WLewGzUXj8E",
|
||||
pub:
|
||||
"Zpf4KFmDmxNnHbRcTkZcAvPnke8_4hLv_FtNhBLcSps.ICAIjzky_T0ENNFIC5cjE-dN87dWp7cb88y0Rb3Nbvo"
|
||||
}
|
||||
|
||||
var bob = {
|
||||
epriv: "z5OC6iWYPVZO-sNqxd20t_qAPsA5nn9d-_yg5uW2mZM",
|
||||
epub:
|
||||
"bHUUjC-xP9QoTEyY5rubZJwft_szXgvetGOGUPOT8Mw.5J2j9SBZ8lqSHKgeFRbMZDs0EuNgM-VVWgMHE3YMFSI",
|
||||
priv: "dWDbrbKUinmSxrlilKmyPzKAgmZCzm9i14bTydUf0kQ",
|
||||
pub:
|
||||
"naP2o7Ebn5tFF2V-z8pDFwOgOazduoiKogWnZ0cTtEE.K-sa7v6DXkb_saMFlCepqPUH--C-6rv6cO1t3wEo6-M"
|
||||
}
|
||||
|
||||
var dave = {
|
||||
epriv: "1eBCIIk30bzfTN50uqSTIN10TWP2AqkqExioV5P-oCE",
|
||||
epub:
|
||||
"PneRg1oMRw3mrrnjbRq8YSADLrdbg8BGEBoC0_P6It4.Vhv8QIkLhurxU5-LhHctaaNn5u3LNujqMNRdh6JbzvU",
|
||||
priv: "XKJrGFWoERdHfhXfhc-VY0nIWeI2eAIDAfkX0nu_O0A",
|
||||
pub:
|
||||
"AXEGD2GABu1lnzTKML_wXHSlznwI5ZFebF-MLPKxI8Y.0Sx-Sd5GpF_1kzrUcliWRdhppq7FFehQi41oZ-WOJmI"
|
||||
}
|
||||
|
||||
it('certify + the good', function(done){(async function(){
|
||||
var cert = await SEA.certify(bob, ["^private.*"], alice)
|
||||
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state.lex()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, () => {
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
user.leave()
|
||||
done()
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('certify + the public', function(done){(async function(){
|
||||
var cert = await SEA.certify(bob, ["^private.*"], alice)
|
||||
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state.lex()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, () => {
|
||||
user.leave()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
done()
|
||||
})
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('certify + someone', function(done){(async function(){
|
||||
var cert = await SEA.certify(bob, ["^private.*"], alice)
|
||||
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state.lex()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, () => {
|
||||
user.leave()
|
||||
user.auth(dave, () => {
|
||||
gun.get("~" + alice.pub)
|
||||
.get("private")
|
||||
.get("asdf")
|
||||
.get("qwerty").once(_data=>{
|
||||
expect(_data).to.be(data)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
it('certify + the evil', function(done){(async function(){
|
||||
var cert = await SEA.certify(bob, ["^private.*"], alice)
|
||||
|
||||
user.auth(bob, () => {
|
||||
var data = Gun.state.lex()
|
||||
gun.get("~" + alice.pub)
|
||||
.get("wrongway")
|
||||
.get("asdf")
|
||||
.get("qwerty")
|
||||
.put(data, ack => {
|
||||
expect(ack.err).to.be.ok()
|
||||
done()
|
||||
}, { opt: { cert } })
|
||||
})
|
||||
}())})
|
||||
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user