mirror of
https://github.com/amark/gun.git
synced 2025-06-07 06:36:46 +00:00
MetaMask start, AXE start, SEA refactor, etc.
This commit is contained in:
parent
7f4b77a454
commit
16e64e1de5
80
axe.js
Normal file
80
axe.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
;(function(){
|
||||||
|
|
||||||
|
/* UNBUILD */
|
||||||
|
var root;
|
||||||
|
if(typeof window !== "undefined"){ root = window }
|
||||||
|
if(typeof global !== "undefined"){ root = global }
|
||||||
|
root = root || {};
|
||||||
|
var console = root.console || {log: function(){}};
|
||||||
|
function USE(arg, req){
|
||||||
|
return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
|
||||||
|
arg(mod = {exports: {}});
|
||||||
|
USE[R(path)] = mod.exports;
|
||||||
|
}
|
||||||
|
function R(p){
|
||||||
|
return p.split('/').slice(-1).toString().replace('.js','');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(typeof module !== "undefined"){ var common = module }
|
||||||
|
/* UNBUILD */
|
||||||
|
|
||||||
|
;USE(function(module){
|
||||||
|
if(typeof window !== "undefined"){ module.window = window }
|
||||||
|
var tmp = module.window || module;
|
||||||
|
var AXE = tmp.AXE || function(){};
|
||||||
|
|
||||||
|
if(AXE.window = module.window){ try{
|
||||||
|
AXE.window.AXE = AXE;
|
||||||
|
tmp = document.createEvent('CustomEvent');
|
||||||
|
tmp.initCustomEvent('extension', false, false, {type: "AXE"});
|
||||||
|
(window.dispatchEvent || window.fireEvent)(tmp);
|
||||||
|
window.postMessage({type: "AXE"}, '*');
|
||||||
|
} catch(e){} }
|
||||||
|
|
||||||
|
try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
|
||||||
|
module.exports = AXE;
|
||||||
|
})(USE, './root');
|
||||||
|
|
||||||
|
;USE(function(module){
|
||||||
|
|
||||||
|
var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
|
||||||
|
(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
|
||||||
|
|
||||||
|
Gun.on('opt', function(at){
|
||||||
|
if(!at.axe){
|
||||||
|
at.axe = {};
|
||||||
|
var p = at.opt.peers, tmp;
|
||||||
|
// 1. If any remembered peers or from last cache or extension
|
||||||
|
// 2. Fallback to use hard coded peers from dApp
|
||||||
|
// 3. Or any offered peers.
|
||||||
|
if(Gun.obj.empty(p)){
|
||||||
|
Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
|
||||||
|
p[url] = {url: url, axe: {}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Our current hypothesis is that it is most optimal
|
||||||
|
// to take peers in a common network, and align
|
||||||
|
// them in a line, where you only have left and right
|
||||||
|
// peers, so messages propagate left and right in
|
||||||
|
// a linear manner with reduced overlap, and
|
||||||
|
// with one common superpeer (with ready failovers)
|
||||||
|
// in case the p2p linear latency is high.
|
||||||
|
// Or there could be plenty of other better options.
|
||||||
|
console.log("axe", at.opt);
|
||||||
|
if(at.opt.super){
|
||||||
|
at.on('in', USE('./lib/super', 1), at);
|
||||||
|
} else {
|
||||||
|
//at.on('in', input, at);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.to.next(at); // make sure to call the "next" middleware adapter.
|
||||||
|
});
|
||||||
|
|
||||||
|
function input(msg){
|
||||||
|
var at = this.as, to = this.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AXE;
|
||||||
|
})(USE, './axe');
|
||||||
|
|
||||||
|
}());
|
@ -5,6 +5,7 @@
|
|||||||
<input id="pass" type="password" placeholder="passphrase">
|
<input id="pass" type="password" placeholder="passphrase">
|
||||||
<input id="in" type="submit" value="sign in">
|
<input id="in" type="submit" value="sign in">
|
||||||
<input id="up" type="button" value="sign up">
|
<input id="up" type="button" value="sign up">
|
||||||
|
<input id="mask" type="button" value="MetaMask Login">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul></ul>
|
<ul></ul>
|
||||||
@ -23,13 +24,14 @@ var gun = Gun(); //Gun(['http://localhost:8765/gun', 'https://guntest.herokuapp.
|
|||||||
var user = gun.user().recall({sessionStorage: true});
|
var user = gun.user().recall({sessionStorage: true});
|
||||||
|
|
||||||
$('#up').on('click', function(e){
|
$('#up').on('click', function(e){
|
||||||
user.create($('#alias').val(), $('#pass').val());
|
user.create($('#alias').val(), $('#pass').val(), login);
|
||||||
});
|
});
|
||||||
|
function login(e){
|
||||||
$('#sign').on('submit', function(e){
|
|
||||||
e.preventDefault();
|
|
||||||
user.auth($('#alias').val(), $('#pass').val());
|
user.auth($('#alias').val(), $('#pass').val());
|
||||||
});
|
return false; // e.preventDefault();
|
||||||
|
};
|
||||||
|
$('#sign').on('submit', login);
|
||||||
|
$('#mask').on('click', login);
|
||||||
|
|
||||||
gun.on('auth', function(){
|
gun.on('auth', function(){
|
||||||
$('#sign').hide();
|
$('#sign').hide();
|
||||||
@ -38,7 +40,7 @@ gun.on('auth', function(){
|
|||||||
|
|
||||||
$('#said').on('submit', function(e){
|
$('#said').on('submit', function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if(!user.is){ return }
|
//if(!user.is){ return }
|
||||||
user.get('said').set($('#say').val());
|
user.get('said').set($('#say').val());
|
||||||
$('#say').val("");
|
$('#say').val("");
|
||||||
});
|
});
|
||||||
|
203
examples/move/index.html
Normal file
203
examples/move/index.html
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
HELP WANTED!
|
||||||
|
Build decentralized Open Source Uber/Lyft!
|
||||||
|
Need people with experience on:
|
||||||
|
- Leaflet API, zoom and tile coordinates for subscribing to surrounding area.
|
||||||
|
- Creating cute little driving icons that animate with heading/direction.
|
||||||
|
-->
|
||||||
|
<title>Move by Neon ERA</title>
|
||||||
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.0.3/dist/leaflet.css"/>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.0.3/dist/leaflet.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="map"></div>
|
||||||
|
<a id="share" class="hide"><div class="stick button">Share</div></a>
|
||||||
|
<div id="link" class="hide">
|
||||||
|
<p>Copy and Paste this URL to your friends to share your location:</p>
|
||||||
|
<center>
|
||||||
|
<p id="follow">Location sharing not available!</p>
|
||||||
|
</center>
|
||||||
|
<p><b>Note</b>: Location may not sync when your device's screen is off or the tab is out of focus. You'd need to install this as an app for that to work.</p>
|
||||||
|
<center>
|
||||||
|
<a id="close"><div class="button">Close</div></a>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
<textarea style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 5em;" id="debug"></textarea>
|
||||||
|
<style>
|
||||||
|
html, body, #map {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 16pt;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.stick {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1em;
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1em;
|
||||||
|
opacity: .5;
|
||||||
|
background: blue;
|
||||||
|
color: white;
|
||||||
|
z-index: 7;
|
||||||
|
transition: .25s all;
|
||||||
|
}
|
||||||
|
.button:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#link {
|
||||||
|
position: absolute;
|
||||||
|
padding: 1em;
|
||||||
|
top: 2em;
|
||||||
|
left: 2em;
|
||||||
|
right: 2em;
|
||||||
|
bottom: 2em;
|
||||||
|
background: white;
|
||||||
|
overflow: scroll;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
#follow {
|
||||||
|
background: #EEE;
|
||||||
|
padding: .5em;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="/jquery.js"></script>
|
||||||
|
<script src="/gun.js"></script>
|
||||||
|
<script>
|
||||||
|
function Where(opt, cb){
|
||||||
|
// a small wrapper around Leaflet for map tracking.
|
||||||
|
var where = {};
|
||||||
|
where.opt = opt || {};
|
||||||
|
where.opt.zoom = where.opt.zoom || {};
|
||||||
|
where.opt.err = where.opt.err || function(){};
|
||||||
|
|
||||||
|
where.map = L.map('map', { zoom: where.opt.zoom.level });
|
||||||
|
|
||||||
|
where.opt.tile = where.opt.tile || L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: where.opt.zoom.max,
|
||||||
|
minZoom: where.opt.zoom.min,
|
||||||
|
detectRetina: true,
|
||||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
});
|
||||||
|
where.map.addLayer(where.opt.tile);
|
||||||
|
|
||||||
|
L.GridLayer.DebugCoords = L.GridLayer.extend({
|
||||||
|
createTile: function (coords) {
|
||||||
|
var tile = document.createElement('div');
|
||||||
|
tile.innerHTML = [coords.z, coords.x, coords.y].join(', ');
|
||||||
|
tile.style.outline = '1px solid black';
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
L.gridLayer.debugCoords = function(opts) {
|
||||||
|
return new L.GridLayer.DebugCoords(opts);
|
||||||
|
};
|
||||||
|
where.map.addLayer( L.gridLayer.debugCoords() );
|
||||||
|
|
||||||
|
where.opt.zoom.ing = where.opt.zoom.ing || function(){
|
||||||
|
where.opt.zoom.level = where.map.getZoom();
|
||||||
|
}
|
||||||
|
where.map.on('zoomstart', where.opt.zoom.ing, where.opt.err);
|
||||||
|
where.map.on('zoomend', where.opt.zoom.ing, where.opt.err);
|
||||||
|
where.map.on('zoomlevelschange', where.opt.zoom.ing, where.opt.err);
|
||||||
|
|
||||||
|
where.update = function(latlng){
|
||||||
|
if((+new Date) - where.update.last < 400){
|
||||||
|
clearTimeout(where.update.to);
|
||||||
|
where.update.to = setTimeout(function(){
|
||||||
|
where.update(latlng);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
where.map.setView(latlng, where.opt.zoom.level, {animate: true});
|
||||||
|
where.marker = where.marker || L.marker().setLatLng(latlng).addTo(where.map);
|
||||||
|
where.marker.setLatLng(latlng).update();
|
||||||
|
where.update.last = (+new Date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(where.opt.track){
|
||||||
|
where.map.on('locationfound', function(pos){
|
||||||
|
where.update(pos.latlng);
|
||||||
|
where.opt.track(pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
where.map.locate({
|
||||||
|
setView: true,
|
||||||
|
zoom: where.opt.zoom.level,
|
||||||
|
watch: where.opt.continuous || true,
|
||||||
|
timeout: where.opt.timeout || 10000,
|
||||||
|
maximumAge: where.opt.maximumAge || 0,
|
||||||
|
enableHighAccuracy: where.opt.enableHighAccuracy || false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
;(function(){
|
||||||
|
// the actual GPS tracking app!
|
||||||
|
var gun = Gun(location.origin + '/gun');
|
||||||
|
var gps = {};
|
||||||
|
gps.opt = {
|
||||||
|
continuous: true, // get location just once uses `getCurrentPosition()` while continuously uses `watchPosition()`
|
||||||
|
enableHighAccuracy: true, // HighAccuracy uses more resources, https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions
|
||||||
|
timeout: 5000, // have this long to get data before erring.
|
||||||
|
maximumAge: 0, // set to 0 to actually track.
|
||||||
|
zoom: {max: 18, level: 13, min: 12}
|
||||||
|
}
|
||||||
|
function start(){
|
||||||
|
gps.follow = (window.location.hash || '').slice(1);
|
||||||
|
if(gps.follow){
|
||||||
|
gps.where = gps.where || Where(gps.opt);
|
||||||
|
gps.ref = gun.get('gps/' + gps.follow);
|
||||||
|
gps.ref.on(function(latlng){
|
||||||
|
//$('#debug').value = 'track ' + JSON.stringify(latlng);
|
||||||
|
gps.where.update(latlng);
|
||||||
|
});
|
||||||
|
$('#share').addClass("hide");
|
||||||
|
} else {
|
||||||
|
document.cookie = 'gps=' + (gps.track = (document.cookie.match(/gps\=(.*?)(\&|$|\;)/i)||[])[1] || Gun.text.random(5)); // trick with cookies!
|
||||||
|
gps.ref = gun.get('gps/' + gps.track);
|
||||||
|
gps.opt.track = function(pos){
|
||||||
|
pos = pos.latlng;
|
||||||
|
if(gps.follow
|
||||||
|
|| Gun.time.is() - gps.when < 1000
|
||||||
|
|| gps.last && gps.last.lat == pos.lat && gps.last.lng == pos.lng){
|
||||||
|
return; // throttle!
|
||||||
|
}
|
||||||
|
gps.when = Gun.time.is();
|
||||||
|
gps.ref.put(gps.last = pos);
|
||||||
|
//$('#debug').value = JSON.stringify(gps.last);
|
||||||
|
}
|
||||||
|
gps.where = gps.where || Where(gps.opt);
|
||||||
|
$('#follow').text(("where.gunDB.io/" || (location.origin + location.pathname)) + '#' + gps.track);
|
||||||
|
$('#share').removeClass("hide");
|
||||||
|
$('#share').on('click', function(){
|
||||||
|
$('#link').toggleClass("hide");
|
||||||
|
});
|
||||||
|
$('#close').on('click', function(){
|
||||||
|
$('#link').toggleClass("hide");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start();
|
||||||
|
window.onhashchange = start;
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
4
gun.min.js
vendored
4
gun.min.js
vendored
File diff suppressed because one or more lines are too long
139
lib/normalize.js
Normal file
139
lib/normalize.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
;(function(){
|
||||||
|
function normalize(opt){
|
||||||
|
var el = $(this);
|
||||||
|
opt = opt || $.extend(true, normalize.opt, opt||{});
|
||||||
|
el.children().each(function(){
|
||||||
|
var a = {$: $(this), opt: opt};
|
||||||
|
a.tag = normalize.tag(a.$);
|
||||||
|
$(a.opt.mutate).each(function(i,fn){
|
||||||
|
fn && fn(a);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
var n = normalize, u;
|
||||||
|
n.get = function(o, p){
|
||||||
|
p = p.split('.');
|
||||||
|
var i = 0, l = p.length, u;
|
||||||
|
while((o = o[p[i++]]) != null && i < l){};
|
||||||
|
return i < l ? u : o;
|
||||||
|
}
|
||||||
|
n.has = function(o,p){
|
||||||
|
return Object.prototype.hasOwnProperty.call(o, p);
|
||||||
|
}
|
||||||
|
n.tag = function(e){
|
||||||
|
return (($(e)[0]||{}).nodeName||'').toLowerCase();
|
||||||
|
}
|
||||||
|
n.attrs = function(e, cb){
|
||||||
|
var attr = {};
|
||||||
|
(e = $(e)) && e.length && $(e[0].attributes||[]).each(function(v,n){
|
||||||
|
n = n.nodeName||n.name;
|
||||||
|
v = e.attr(n);
|
||||||
|
v = cb? cb(v,n,e) : v;
|
||||||
|
if(v !== u && v !== false){ attr[n] = v }
|
||||||
|
});
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
n.joint = function(e, d){
|
||||||
|
d = (d? 'next' : 'previous') + 'Sibling'
|
||||||
|
return $(($(e)[0]||{})[d]);
|
||||||
|
}
|
||||||
|
var h = {
|
||||||
|
attr: function(a$, av, al){
|
||||||
|
var l = function(i,v){
|
||||||
|
var t = v;
|
||||||
|
i = al? v : i;
|
||||||
|
v = al? av[v.toLowerCase()] : t;
|
||||||
|
a$.attr(i, v);
|
||||||
|
}
|
||||||
|
al? $(al.sort()).each(l) : $.each(av,l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.opt = { // some reasonable defaults, limited to content alone.
|
||||||
|
tags: {
|
||||||
|
'a': {attrs:{'src':1}, exclude:{'a':1}},
|
||||||
|
'b': {exclude:{'b':1}},
|
||||||
|
//'blockquote':1,
|
||||||
|
'br': {empty: 1},
|
||||||
|
'div': 1,
|
||||||
|
//'code':1,
|
||||||
|
'i': {exclude:{'i':1}},
|
||||||
|
'img': {attrs:{'src':1}, empty: 1},
|
||||||
|
'li':1, 'ol':1,
|
||||||
|
'p': {exclude:{'p':1,'div':1}},
|
||||||
|
//'pre':1,
|
||||||
|
's': {exclude:{'s':1}},
|
||||||
|
'sub':1, 'sup':1,
|
||||||
|
'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}},
|
||||||
|
'u': {exclude:{'u':1,'p':1}},
|
||||||
|
'ul':1
|
||||||
|
}
|
||||||
|
// a, audio, b, br, div, i, img, li, ol, p, s, span, sub, sup, u, ul, video
|
||||||
|
// button, canvas, embed, form, iframe, input, style, svg, table,
|
||||||
|
// Text: bold, italics, underline, align, bullet, list,
|
||||||
|
,convert: {
|
||||||
|
'em': 'i', 'strong': 'b'
|
||||||
|
}
|
||||||
|
,attrs: {
|
||||||
|
'id':1
|
||||||
|
,'class':1
|
||||||
|
,'style':1
|
||||||
|
}
|
||||||
|
,mutate: [
|
||||||
|
function(a){ // attr
|
||||||
|
a.attrs = [];
|
||||||
|
a.attr = $.extend(a.opt.attrs, n.get(a.opt,'tags.'+ a.tag +'attrs'));
|
||||||
|
a.attr = n.attrs(a.$, function(v,i){
|
||||||
|
a.$.removeAttr(i);
|
||||||
|
if(a.attr[i.toLowerCase()]){
|
||||||
|
a.attrs.push(i)
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// if this tag is gonna get converted, wait to add attr back till after the convert
|
||||||
|
if(a.attrs && !n.get(a.opt, 'convert.' + a.tag)){
|
||||||
|
h.attr(a.$, a.attr, a.attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,function(a, tmp){ // convert
|
||||||
|
if(!(tmp = n.get(a.opt,'convert.' + a.tag))){ return }
|
||||||
|
a.attr = a.attr || n.attrs(a.$);
|
||||||
|
a.$.replaceWith(a.$ = $('<'+ (a.tag = t.toLowerCase()) +'>').append(a.$.contents()));
|
||||||
|
h.attr(a.$, a.attr, a.attrs);
|
||||||
|
}
|
||||||
|
,function(a, tmp){ // lookahead
|
||||||
|
if((tmp = n.joint(a.$,1)) && (t = t.contents()).length === 1 && a.tag === n.tag(t = t.first())){
|
||||||
|
a.$.append(t.parent()); // no need to unwrap the child, since the recursion will do it for us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,function(a){ // recurse
|
||||||
|
// this needs to precede the exclusion and empty.
|
||||||
|
normalize(a);
|
||||||
|
}
|
||||||
|
,function(a){ // exclude
|
||||||
|
var t;
|
||||||
|
if(!n.get(a.opt,'tags.' + a.tag)
|
||||||
|
|| ((t = n.get(a.opt,'tags.'+ a.tag +'.exclude'))
|
||||||
|
&& a.$.parents($.map(t,function(i,v){return v})+' ').length)
|
||||||
|
){
|
||||||
|
a.$.replaceWith(a.$.contents());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,function(a){ // prior
|
||||||
|
var t;
|
||||||
|
if((t = n.joint(a.$)).length && a.tag === n.tag(t)){
|
||||||
|
t.append(a.$.contents());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,function(a){ // empty
|
||||||
|
// should always go last, since the element will be removed!
|
||||||
|
if(a.opt.empty || !n.has(a.opt,'empty')){
|
||||||
|
if(!n.get(a.opt,'tags.'+ a.tag +'.empty')
|
||||||
|
&& !a.$.contents().length){
|
||||||
|
a.$.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
$.fn.normalize = normalize;
|
||||||
|
}());
|
19
lib/reboot.js
Normal file
19
lib/reboot.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
;(function(){
|
||||||
|
var exec = require('child_process').execSync;
|
||||||
|
var dir = __dirname, tmp;
|
||||||
|
|
||||||
|
try{exec("crontab -l");
|
||||||
|
}catch(e){tmp = e}
|
||||||
|
if(0 > tmp.toString().indexOf('no')){ return }
|
||||||
|
|
||||||
|
try{tmp = exec('which node').toString();
|
||||||
|
}catch(e){console.log(e);return}
|
||||||
|
|
||||||
|
try{tmp = exec('echo "@reboot '+tmp+' '+dir+'/../examples/http.js" > '+dir+'/reboot.cron');
|
||||||
|
}catch(e){console.log(e);return}
|
||||||
|
|
||||||
|
try{tmp = exec('crontab '+dir+'/reboot.cron');
|
||||||
|
}catch(e){console.log(e);return}
|
||||||
|
console.log(tmp.toString());
|
||||||
|
|
||||||
|
}());
|
@ -3,15 +3,16 @@
|
|||||||
Gun.serve = require('./serve');
|
Gun.serve = require('./serve');
|
||||||
//process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
|
//process.env.GUN_ENV = process.env.GUN_ENV || 'debug';
|
||||||
Gun.on('opt', function(root){
|
Gun.on('opt', function(root){
|
||||||
|
if(u === root.opt.super){
|
||||||
|
root.opt.super = true;
|
||||||
|
}
|
||||||
this.to.next(root);
|
this.to.next(root);
|
||||||
if(root.once){ return }
|
|
||||||
if(u !== root.opt.super){ return }
|
|
||||||
root.opt.super = true;
|
|
||||||
})
|
})
|
||||||
require('../nts');
|
require('../nts');
|
||||||
require('./store');
|
require('./store');
|
||||||
require('./rs3');
|
require('./rs3');
|
||||||
require('./wire');
|
require('./wire');
|
||||||
|
//try{require('../axe');}catch(e){}
|
||||||
require('./file');
|
require('./file');
|
||||||
require('./evict');
|
require('./evict');
|
||||||
if('debug' === process.env.GUN_ENV){ require('./debug') }
|
if('debug' === process.env.GUN_ENV){ require('./debug') }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gun",
|
"name": "gun",
|
||||||
"version": "0.9.99997",
|
"version": "0.9.99998",
|
||||||
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"browser": "gun.min.js",
|
"browser": "gun.min.js",
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
|
|
||||||
var SEA = require('./sea');
|
|
||||||
var Gun = SEA.Gun;
|
|
||||||
const queryGunAliases = require('./query')
|
|
||||||
const parseProps = require('./parse')
|
|
||||||
// This is internal User authentication func.
|
|
||||||
const authenticate = async (alias, pass, gunRoot) => {
|
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
|
||||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
|
||||||
.filter(a => !!a.pub && !!a.put)
|
|
||||||
// Got any?
|
|
||||||
if (!aliases.length) {
|
|
||||||
throw { err: 'Public key does not exist!' }
|
|
||||||
}
|
|
||||||
let err
|
|
||||||
// then attempt to log into each one until we find ours!
|
|
||||||
// (if two users have the same username AND the same password... that would be bad)
|
|
||||||
const users = await Promise.all(aliases.map(async (a, i) => {
|
|
||||||
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
|
||||||
const auth = parseProps(a.put.auth)
|
|
||||||
// NOTE: aliasquery uses `gun.get` which internally SEA.read verifies the data for us, so we do not need to re-verify it here.
|
|
||||||
// SEA.verify(at.put.auth, pub).then(function(auth){
|
|
||||||
try {
|
|
||||||
const proof = await SEA.work(pass, auth.s)
|
|
||||||
//const props = { pub: pub, proof: proof, at: at }
|
|
||||||
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
|
||||||
/*
|
|
||||||
MARK TO @mhelander : pub vs epub!???
|
|
||||||
*/
|
|
||||||
const salt = auth.salt
|
|
||||||
const sea = await SEA.decrypt(auth.ek, proof)
|
|
||||||
if (!sea) {
|
|
||||||
err = 'Failed to decrypt secret! ' + (i+1) +'/'+aliases.length;
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
|
||||||
// if we were successful, then that meanswe're logged in!
|
|
||||||
const priv = sea.priv
|
|
||||||
const epriv = sea.epriv
|
|
||||||
const epub = a.put.epub
|
|
||||||
// TODO: 'salt' needed?
|
|
||||||
err = null
|
|
||||||
if(SEA.window){
|
|
||||||
var tmp = SEA.window.sessionStorage;
|
|
||||||
if(tmp && gunRoot._.opt.remember){
|
|
||||||
SEA.window.sessionStorage.alias = alias;
|
|
||||||
SEA.window.sessionStorage.tmp = pass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {priv: priv, pub: a.put.pub, salt: salt, epub: epub, epriv: epriv };
|
|
||||||
} catch (e) {
|
|
||||||
err = 'Failed to decrypt secret!'
|
|
||||||
throw { err }
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
var user = Gun.list.map(users, function(acc){ if(acc){ return acc } })
|
|
||||||
if (!user) {
|
|
||||||
throw { err: err || 'Public key does not exist!' }
|
|
||||||
}
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
module.exports = authenticate;
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
|||||||
}
|
}
|
||||||
return buf
|
return buf
|
||||||
}
|
}
|
||||||
const byteLength = input.byteLength
|
const byteLength = input.byteLength // what is going on here? FOR MARTTI
|
||||||
const length = input.byteLength ? input.byteLength : input.length
|
const length = input.byteLength ? input.byteLength : input.length
|
||||||
if (length) {
|
if (length) {
|
||||||
let buf
|
let buf
|
||||||
|
418
sea/create.js
418
sea/create.js
@ -2,22 +2,75 @@
|
|||||||
// TODO: This needs to be split into all separate functions.
|
// TODO: This needs to be split into all separate functions.
|
||||||
// Not just everything thrown into 'create'.
|
// Not just everything thrown into 'create'.
|
||||||
|
|
||||||
const SEA = require('./sea')
|
var SEA = require('./sea');
|
||||||
const User = require('./user')
|
var User = require('./user');
|
||||||
const authRecall = require('./recall')
|
var authsettings = require('./settings');
|
||||||
const authsettings = require('./settings')
|
var Gun = SEA.Gun;
|
||||||
const authenticate = require('./authenticate')
|
|
||||||
const finalizeLogin = require('./login')
|
var noop = function(){};
|
||||||
const authLeave = require('./leave')
|
|
||||||
const _initial_authsettings = require('./settings').recall
|
|
||||||
const Gun = SEA.Gun;
|
|
||||||
|
|
||||||
var u;
|
|
||||||
// Well first we have to actually create a user. That is what this function does.
|
// Well first we have to actually create a user. That is what this function does.
|
||||||
User.prototype.create = function(username, pass, cb, opt){
|
User.prototype.create = function(alias, pass, cb, opt){
|
||||||
// TODO: Needs to be cleaned up!!!
|
var gun = this, cat = (gun._), root = gun.back(-1);
|
||||||
const gunRoot = this.back(-1)
|
cb = cb || noop;
|
||||||
var gun = this, cat = (gun._);
|
if(cat.ing){
|
||||||
|
cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
|
||||||
|
return gun;
|
||||||
|
}
|
||||||
|
cat.ing = true;
|
||||||
|
opt = opt || {};
|
||||||
|
var act = {}, u;
|
||||||
|
act.a = function(pubs){
|
||||||
|
act.pubs = pubs;
|
||||||
|
if(pubs && !opt.already){
|
||||||
|
// If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed.
|
||||||
|
var ack = {err: Gun.log('User already created!')};
|
||||||
|
cat.ing = false;
|
||||||
|
cb(ack);
|
||||||
|
gun.leave();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
act.salt = Gun.text.random(64); // pseudo-randomly create a salt, then use PBKDF2 function to extend the password with it.
|
||||||
|
SEA.work(pass, act.salt, act.b); // this will take some short amount of time to produce a proof, which slows brute force attacks.
|
||||||
|
}
|
||||||
|
act.b = function(proof){
|
||||||
|
act.proof = proof;
|
||||||
|
SEA.pair(act.c); // now we have generated a brand new ECDSA key pair for the user account.
|
||||||
|
}
|
||||||
|
act.c = function(pair){
|
||||||
|
act.pair = pair || {};
|
||||||
|
// the user's public key doesn't need to be signed. But everything else needs to be signed with it!
|
||||||
|
act.data = {pub: pair.pub};
|
||||||
|
SEA.sign(alias, pair, act.d);
|
||||||
|
}
|
||||||
|
act.d = function(alias){
|
||||||
|
act.data.alias = alias;
|
||||||
|
SEA.sign(act.pair.epub, act.pair, act.e);
|
||||||
|
}
|
||||||
|
act.e = function(epub){
|
||||||
|
act.data.epub = epub;
|
||||||
|
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, act.proof, act.f); // to keep the private key safe, we AES encrypt it with the proof of work!
|
||||||
|
}
|
||||||
|
act.f = function(auth){
|
||||||
|
act.data.auth = auth;
|
||||||
|
SEA.sign({ek: auth, s: act.salt}, act.pair, act.g);
|
||||||
|
}
|
||||||
|
act.g = function(auth){ var tmp;
|
||||||
|
act.data.auth = auth;
|
||||||
|
root.get(tmp = '~'+act.pair.pub).put(act.data); // awesome, now we can actually save the user with their public key as their ID.
|
||||||
|
root.get('~@'+alias).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp))); // next up, we want to associate the alias with the public key. So we add it to the alias list.
|
||||||
|
setTimeout(function(){ // we should be able to delete this now, right?
|
||||||
|
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){ gun.auth(alias, pass) } // if no callback is passed, auto-login after signing up.
|
||||||
|
},10);
|
||||||
|
}
|
||||||
|
root.get('~@'+alias).once(act.a);
|
||||||
|
return gun;
|
||||||
|
}
|
||||||
|
// now that we have created a user, we want to authenticate them!
|
||||||
|
User.prototype.auth = function(alias, pass, cb, opt){
|
||||||
|
var gun = this, cat = (gun._), root = gun.back(-1);
|
||||||
cb = cb || function(){};
|
cb = cb || function(){};
|
||||||
if(cat.ing){
|
if(cat.ing){
|
||||||
cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
|
cb({err: Gun.log("User is already being created or authenticated!"), wait: true});
|
||||||
@ -25,234 +78,169 @@
|
|||||||
}
|
}
|
||||||
cat.ing = true;
|
cat.ing = true;
|
||||||
opt = opt || {};
|
opt = opt || {};
|
||||||
var resolve = function(){}, reject = resolve;
|
var pair = (alias.pub || alias.epub)? alias : (pass.pub || pass.epub)? pass : null;
|
||||||
// Because more than 1 user might have the same username, we treat the alias as a list of those users.
|
var act = {}, u;
|
||||||
if(cb){ resolve = reject = cb }
|
act.a = function(data){
|
||||||
gunRoot.get('~@'+username).get(async (at, ev) => {
|
if(!data){ return act.b() }
|
||||||
ev.off()
|
if(!data.pub){
|
||||||
if (at.put && !opt.already) {
|
var tmp = [];
|
||||||
// If we can enforce that a user name is already taken, it might be nice to try, but this is not guaranteed.
|
Gun.node.is(data, function(v){ tmp.push(v) })
|
||||||
const err = 'User already created!'
|
return act.b(tmp);
|
||||||
Gun.log(err)
|
|
||||||
cat.ing = false;
|
|
||||||
gun.leave();
|
|
||||||
return reject({ err: err })
|
|
||||||
}
|
}
|
||||||
const salt = Gun.text.random(64)
|
if(act.name){ return act.f(data) }
|
||||||
// pseudo-randomly create a salt, then use CryptoJS's PBKDF2 function to extend the password with it.
|
act.c((act.data = data).auth);
|
||||||
try {
|
|
||||||
const proof = await SEA.work(pass, salt)
|
|
||||||
// this will take some short amount of time to produce a proof, which slows brute force attacks.
|
|
||||||
const pairs = await SEA.pair()
|
|
||||||
// now we have generated a brand new ECDSA key pair for the user account.
|
|
||||||
const pub = pairs.pub
|
|
||||||
const priv = pairs.priv
|
|
||||||
const epriv = pairs.epriv
|
|
||||||
// the user's public key doesn't need to be signed. But everything else needs to be signed with it!
|
|
||||||
const alias = await SEA.sign(username, pairs)
|
|
||||||
if(u === alias){ throw SEA.err }
|
|
||||||
const epub = await SEA.sign(pairs.epub, pairs)
|
|
||||||
if(u === epub){ throw SEA.err }
|
|
||||||
// to keep the private key safe, we AES encrypt it with the proof of work!
|
|
||||||
const auth = await SEA.encrypt({ priv: priv, epriv: epriv }, proof)
|
|
||||||
.then((auth) => // TODO: So signedsalt isn't needed?
|
|
||||||
// SEA.sign(salt, pairs).then((signedsalt) =>
|
|
||||||
SEA.sign({ek: auth, s: salt}, pairs)
|
|
||||||
// )
|
|
||||||
).catch((e) => { Gun.log('SEA.en or SEA.write calls failed!'); cat.ing = false; gun.leave(); reject(e) })
|
|
||||||
const user = { alias: alias, pub: pub, epub: epub, auth: auth }
|
|
||||||
const tmp = '~'+pairs.pub;
|
|
||||||
// awesome, now we can actually save the user with their public key as their ID.
|
|
||||||
try{
|
|
||||||
|
|
||||||
gunRoot.get(tmp).put(user)
|
|
||||||
}catch(e){console.log(e)}
|
|
||||||
// next up, we want to associate the alias with the public key. So we add it to the alias list.
|
|
||||||
gunRoot.get('~@'+username).put(Gun.obj.put({}, tmp, Gun.val.link.ify(tmp)))
|
|
||||||
// callback that the user has been created. (Note: ok = 0 because we didn't wait for disk to ack)
|
|
||||||
setTimeout(() => { cat.ing = false; resolve({ ok: 0, pub: pairs.pub}) }, 10) // TODO: BUG! If `.auth` happens synchronously after `create` finishes, auth won't work. This setTimeout is a temporary hack until we can properly fix it.
|
|
||||||
} catch (e) {
|
|
||||||
Gun.log('SEA.create failed!')
|
|
||||||
cat.ing = false;
|
|
||||||
gun.leave();
|
|
||||||
reject(e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return gun; // gun chain commands must return gun chains!
|
|
||||||
}
|
|
||||||
// now that we have created a user, we want to authenticate them!
|
|
||||||
User.prototype.auth = function(alias, pass, cb, opt){
|
|
||||||
// TODO: Needs to be cleaned up!!!!
|
|
||||||
const opts = opt || (typeof cb !== 'function' && cb)
|
|
||||||
let pin = opts && opts.pin
|
|
||||||
let newpass = opts && opts.newpass
|
|
||||||
const gunRoot = this.back(-1)
|
|
||||||
cb = typeof cb === 'function' ? cb : () => {}
|
|
||||||
newpass = newpass || (opts||{}).change;
|
|
||||||
var gun = this, cat = (gun._);
|
|
||||||
if(cat.ing){
|
|
||||||
cb({err: "User is already being created or authenticated!", wait: true});
|
|
||||||
return gun;
|
|
||||||
}
|
}
|
||||||
cat.ing = true;
|
act.b = function(list){
|
||||||
|
var get = (act.list = (act.list||[]).concat(list||[])).shift();
|
||||||
if (!pass && pin) { (async function(){
|
if(u === get){
|
||||||
try {
|
if(act.name){ return act.err('Your user account is not published for dApps to access, please consider syncing it online, or allowing local access by adding your device as a peer.') }
|
||||||
var r = await authRecall(gunRoot, { alias: alias, pin: pin })
|
return act.err('Wrong user or password.')
|
||||||
return cat.ing = false, cb(r), gun;
|
|
||||||
} catch (e) {
|
|
||||||
var err = { err: 'Auth attempt failed! Reason: No session data for alias & PIN' }
|
|
||||||
return cat.ing = false, gun.leave(), cb(err), gun;
|
|
||||||
}}())
|
|
||||||
return gun;
|
|
||||||
}
|
|
||||||
|
|
||||||
const putErr = (msg) => (e) => {
|
|
||||||
const { message, err = message || '' } = e
|
|
||||||
Gun.log(msg)
|
|
||||||
var error = { err: msg+' Reason: '+err }
|
|
||||||
return cat.ing = false, gun.leave(), cb(error), gun;
|
|
||||||
}
|
|
||||||
|
|
||||||
(async function(){ try {
|
|
||||||
const keys = await authenticate(alias, pass, gunRoot)
|
|
||||||
if (!keys) {
|
|
||||||
return putErr('Auth attempt failed!')({ message: 'No keys' })
|
|
||||||
}
|
}
|
||||||
const pub = keys.pub
|
root.get(get).once(act.a);
|
||||||
const priv = keys.priv
|
}
|
||||||
const epub = keys.epub
|
act.c = function(auth){
|
||||||
const epriv = keys.epriv
|
if(u === auth){ return act.b() }
|
||||||
// we're logged in!
|
SEA.work(pass, (act.auth = auth).s, act.d); // the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
if (newpass) {
|
}
|
||||||
// password update so encrypt private key using new pwd + salt
|
act.d = function(proof){
|
||||||
try {
|
if(u === proof){ return act.b() }
|
||||||
const salt = Gun.text.random(64);
|
SEA.decrypt(act.auth.ek, proof, act.e);
|
||||||
const encSigAuth = await SEA.work(newpass, salt)
|
}
|
||||||
.then((key) =>
|
act.e = function(half){
|
||||||
SEA.encrypt({ priv: priv, epriv: epriv }, key)
|
if(u === half){ return act.b() }
|
||||||
.then((auth) => SEA.sign({ek: auth, s: salt}, keys))
|
act.half = half;
|
||||||
)
|
act.f(act.data);
|
||||||
const signedEpub = await SEA.sign(epub, keys)
|
}
|
||||||
const signedAlias = await SEA.sign(alias, keys)
|
act.f = function(data){
|
||||||
const user = {
|
if(!data || !data.pub){ return act.b() }
|
||||||
pub: pub,
|
var tmp = act.half || {};
|
||||||
alias: signedAlias,
|
act.g({pub: data.pub, epub: data.epub, priv: tmp.priv, epriv: tmp.epriv});
|
||||||
auth: encSigAuth,
|
}
|
||||||
epub: signedEpub
|
act.g = function(pair){
|
||||||
}
|
act.pair = pair;
|
||||||
// awesome, now we can update the user using public key ID.
|
var user = (root._).user, at = (user._);
|
||||||
gunRoot.get('~'+user.pub).put(user)
|
var tmp = at.tag;
|
||||||
// then we're done
|
var upt = at.opt;
|
||||||
const login = finalizeLogin(alias, keys, gunRoot, { pin })
|
at = user._ = root.get('~'+pair.pub)._;
|
||||||
login.catch(putErr('Failed to finalize login with new password!'))
|
at.opt = upt;
|
||||||
return cat.ing = false, cb(await login), gun
|
// add our credentials in-memory only to our root user instance
|
||||||
} catch (e) {
|
user.is = {pub: pair.pub, epub: pair.epub, alias: alias};
|
||||||
return putErr('Password set attempt failed!')(e)
|
at.sea = act.pair;
|
||||||
}
|
cat.ing = false;
|
||||||
} else {
|
opt.change? act.z() : cb(at);
|
||||||
const login = finalizeLogin(alias, keys, gunRoot, { pin: pin })
|
if(SEA.window && ((gun.back('user')._).opt||opt).remember){
|
||||||
login.catch(putErr('Finalizing login failed!'))
|
// TODO: this needs to be modular.
|
||||||
return cat.ing = false, cb(await login), gun;
|
var sS = {}; try{sS = window.sessionStorage}catch(e){}
|
||||||
|
sS.recall = true;
|
||||||
|
sS.alias = alias;
|
||||||
|
sS.tmp = pass;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
try{
|
||||||
return putErr('Auth attempt failed!')(e)
|
(root._).on('auth', at) // TODO: Deprecate this, emit on user instead! Update docs when you do.
|
||||||
} }());
|
//at.on('auth', at) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
|
||||||
|
}catch(e){
|
||||||
|
Gun.log("Your 'auth' callback crashed with:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
act.z = function(){
|
||||||
|
// password update so encrypt private key using new pwd + salt
|
||||||
|
act.salt = Gun.text.random(64); // pseudo-random
|
||||||
|
SEA.work(opt.change, act.salt, act.y);
|
||||||
|
}
|
||||||
|
act.y = function(proof){
|
||||||
|
SEA.encrypt({priv: act.pair.priv, epriv: act.pair.epriv}, proof, act.x);
|
||||||
|
}
|
||||||
|
act.x = function(auth){
|
||||||
|
SEA.sign({ek: auth, s: act.salt}, act.pair, act.w);
|
||||||
|
}
|
||||||
|
act.w = function(auth){
|
||||||
|
root.get('~'+act.pair.pub).get('auth').put(auth, cb);
|
||||||
|
}
|
||||||
|
act.err = function(e){
|
||||||
|
var ack = {err: Gun.log(e || 'User cannot be found!')};
|
||||||
|
cat.ing = false;
|
||||||
|
cb(ack);
|
||||||
|
}
|
||||||
|
act.plugin = function(name){
|
||||||
|
if(!(act.name = name)){ return act.err() }
|
||||||
|
var tmp = [name];
|
||||||
|
if('~' !== name[0]){
|
||||||
|
tmp[1] = '~'+name;
|
||||||
|
tmp[2] = '~@'+name;
|
||||||
|
}
|
||||||
|
act.b(tmp);
|
||||||
|
}
|
||||||
|
if(pair){
|
||||||
|
act.g(pair);
|
||||||
|
} else
|
||||||
|
if(alias){
|
||||||
|
root.get('~@'+alias).once(act.a);
|
||||||
|
} else
|
||||||
|
if(!alias && !pass){
|
||||||
|
SEA.name(act.plugin);
|
||||||
|
}
|
||||||
return gun;
|
return gun;
|
||||||
}
|
}
|
||||||
User.prototype.pair = function(){
|
User.prototype.pair = function(){
|
||||||
|
console.log("user.pair() IS DEPRECATED AND WILL BE DELETED!!!");
|
||||||
var user = this;
|
var user = this;
|
||||||
if(!user.is){ return false }
|
if(!user.is){ return false }
|
||||||
return user._.sea;
|
return user._.sea;
|
||||||
}
|
}
|
||||||
User.prototype.leave = async function(){
|
User.prototype.leave = function(opt, cb){
|
||||||
var gun = this, user = (gun.back(-1)._).user;
|
var gun = this, user = (gun.back(-1)._).user;
|
||||||
if(user){
|
if(user){
|
||||||
delete user.is;
|
delete user.is;
|
||||||
delete user._.is;
|
delete user._.is;
|
||||||
delete user._.sea;
|
delete user._.sea;
|
||||||
}
|
}
|
||||||
if(typeof window !== 'undefined'){
|
if(SEA.window){
|
||||||
var tmp = window.sessionStorage;
|
var sS = {}; try{sS = window.sessionStorage}catch(e){};
|
||||||
delete tmp.alias;
|
delete sS.alias;
|
||||||
delete tmp.tmp;
|
delete sS.tmp;
|
||||||
|
delete sS.recall;
|
||||||
}
|
}
|
||||||
return await authLeave(this.back(-1))
|
return gun;
|
||||||
}
|
}
|
||||||
// If authenticated user wants to delete his/her account, let's support it!
|
// If authenticated user wants to delete his/her account, let's support it!
|
||||||
User.prototype.delete = async function(alias, pass){
|
User.prototype.delete = async function(alias, pass, cb){
|
||||||
const gunRoot = this.back(-1)
|
var gun = this, root = gun.back(-1), user = gun.back('user');
|
||||||
try {
|
try {
|
||||||
const __gky40 = await authenticate(alias, pass, gunRoot)
|
user.auth(alias, pass, function(ack){
|
||||||
const pub = __gky40.pub
|
var pub = (user.is||{}).pub;
|
||||||
await authLeave(gunRoot, alias)
|
// Delete user data
|
||||||
// Delete user data
|
user.map().once(function(){ this.put(null) });
|
||||||
gunRoot.get('~'+pub).put(null)
|
// Wipe user data from memory
|
||||||
// Wipe user data from memory
|
user.leave();
|
||||||
const { user = { _: {} } } = gunRoot._;
|
(cb || noop)({ok: 0});
|
||||||
// TODO: is this correct way to 'logout' user from Gun.User ?
|
});
|
||||||
[ 'alias', 'sea', 'pub' ].map((key) => delete user._[key])
|
|
||||||
user._.is = user.is = {}
|
|
||||||
gunRoot.user()
|
|
||||||
return { ok: 0 } // TODO: proper return codes???
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Gun.log('User.delete failed! Error:', e)
|
Gun.log('User.delete failed! Error:', e);
|
||||||
throw e // TODO: proper error codes???
|
|
||||||
}
|
}
|
||||||
|
return gun;
|
||||||
}
|
}
|
||||||
// If authentication is to be remembered over reloads or browser closing,
|
User.prototype.recall = function(opt, cb){
|
||||||
// set validity time in minutes.
|
var gun = this, root = gun.back(-1), tmp;
|
||||||
User.prototype.recall = function(setvalidity, options){
|
opt = opt || {};
|
||||||
var gun = this;
|
if(opt && opt.sessionStorage){
|
||||||
const gunRoot = this.back(-1)
|
if(SEA.window){
|
||||||
|
var sS = {}; try{sS = window.sessionStorage}catch(e){}
|
||||||
let validity
|
if(sS){
|
||||||
let opts
|
(root._).opt.remember = true;
|
||||||
|
((gun.back('user')._).opt||opt).remember = true;
|
||||||
var o = setvalidity;
|
if(sS.recall || (sS.alias && sS.tmp)){
|
||||||
if(o && o.sessionStorage){
|
root.user().auth(sS.alias, sS.tmp, cb);
|
||||||
if(typeof window !== 'undefined'){
|
|
||||||
var tmp = window.sessionStorage;
|
|
||||||
if(tmp){
|
|
||||||
gunRoot._.opt.remember = true;
|
|
||||||
if(tmp.alias && tmp.tmp){
|
|
||||||
gunRoot.user().auth(tmp.alias, tmp.tmp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return gun;
|
return gun;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
if (!Gun.val.is(setvalidity)) {
|
TODO: copy mhelander's expiry code back in.
|
||||||
opts = setvalidity
|
Although, we should check with community,
|
||||||
validity = _initial_authsettings.validity
|
should expiry be core or a plugin?
|
||||||
} else {
|
*/
|
||||||
opts = options
|
return gun;
|
||||||
validity = setvalidity * 60 // minutes to seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// opts = { hook: function({ iat, exp, alias, proof }) }
|
|
||||||
// iat == Date.now() when issued, exp == seconds to expire from iat
|
|
||||||
// How this works:
|
|
||||||
// called when app bootstraps, with wanted options
|
|
||||||
// IF authsettings.validity === 0 THEN no remember-me, ever
|
|
||||||
// IF PIN then signed 'remember' to window.sessionStorage and 'auth' to IndexedDB
|
|
||||||
authsettings.validity = typeof validity !== 'undefined'
|
|
||||||
? validity : _initial_authsettings.validity
|
|
||||||
authsettings.hook = (Gun.obj.has(opts, 'hook') && typeof opts.hook === 'function')
|
|
||||||
? opts.hook : _initial_authsettings.hook
|
|
||||||
// All is good. Should we do something more with actual recalled data?
|
|
||||||
(async function(){ await authRecall(gunRoot) }());
|
|
||||||
return gun;
|
|
||||||
} catch (e) {
|
|
||||||
const err = 'No session!'
|
|
||||||
Gun.log(err)
|
|
||||||
// NOTE! It's fine to resolve recall with reason why not successful
|
|
||||||
// instead of rejecting...
|
|
||||||
//return { err: (e && e.err) || err }
|
|
||||||
return gun;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
User.prototype.alive = async function(){
|
User.prototype.alive = async function(){
|
||||||
const gunRoot = this.back(-1)
|
const gunRoot = this.back(-1)
|
||||||
@ -278,7 +266,7 @@
|
|||||||
User.prototype.grant = function(to, cb){
|
User.prototype.grant = function(to, cb){
|
||||||
console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
|
console.log("`.grant` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
|
||||||
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
|
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
|
||||||
gun.back(function(at){ if(at.pub){ return } path += (at.get||'') });
|
gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
|
||||||
(async function(){
|
(async function(){
|
||||||
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
|
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
|
||||||
sec = await SEA.decrypt(sec, pair);
|
sec = await SEA.decrypt(sec, pair);
|
||||||
@ -299,7 +287,7 @@
|
|||||||
User.prototype.secret = function(data, cb){
|
User.prototype.secret = function(data, cb){
|
||||||
console.log("`.secret` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
|
console.log("`.secret` API MAY BE DELETED OR CHANGED OR RENAMED, DO NOT USE!");
|
||||||
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
|
var gun = this, user = gun.back(-1).user(), pair = user.pair(), path = '';
|
||||||
gun.back(function(at){ if(at.pub){ return } path += (at.get||'') });
|
gun.back(function(at){ if(at.is){ return } path += (at.get||'') });
|
||||||
(async function(){
|
(async function(){
|
||||||
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
|
var enc, sec = await user.get('trust').get(pair.pub).get(path).then();
|
||||||
sec = await SEA.decrypt(sec, pair);
|
sec = await SEA.decrypt(sec, pair);
|
||||||
|
@ -6,8 +6,12 @@
|
|||||||
var parse = require('./parse');
|
var parse = require('./parse');
|
||||||
|
|
||||||
SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
|
SEA.decrypt = SEA.decrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
var key = (pair||opt).epriv || pair;
|
||||||
|
if(!key){
|
||||||
|
pair = await SEA.I(null, {what: data, how: 'decrypt', why: opt.why});
|
||||||
|
key = pair.epriv || pair;
|
||||||
|
}
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
const ct = await aeskey(key, shim.Buffer.from(json.s, 'utf8'), opt)
|
||||||
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
.then((aes) => (/*shim.ossl ||*/ shim.subtle).decrypt({ // Keeping aesKey scope as private as possible...
|
||||||
|
@ -5,8 +5,12 @@
|
|||||||
var aeskey = require('./aeskey');
|
var aeskey = require('./aeskey');
|
||||||
|
|
||||||
SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
|
SEA.encrypt = SEA.encrypt || (async (data, pair, cb, opt) => { try {
|
||||||
var opt = opt || {};
|
opt = opt || {};
|
||||||
const key = pair.epriv || pair;
|
var key = (pair||opt).epriv || pair;
|
||||||
|
if(!key){
|
||||||
|
pair = await SEA.I(null, {what: data, how: 'encrypt', why: opt.why});
|
||||||
|
key = pair.epriv || pair;
|
||||||
|
}
|
||||||
const msg = JSON.stringify(data)
|
const msg = JSON.stringify(data)
|
||||||
const rand = {s: shim.random(8), iv: shim.random(16)};
|
const rand = {s: shim.random(8), iv: shim.random(16)};
|
||||||
const ct = await aeskey(key, rand.s, opt)
|
const ct = await aeskey(key, rand.s, opt)
|
||||||
|
20
sea/index.js
20
sea/index.js
@ -62,7 +62,7 @@
|
|||||||
// if there is a request to read data from us, then...
|
// if there is a request to read data from us, then...
|
||||||
var soul = msg.get['#'];
|
var soul = msg.get['#'];
|
||||||
if(soul){ // for now, only allow direct IDs to be read.
|
if(soul){ // for now, only allow direct IDs to be read.
|
||||||
if(typeof soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors.
|
if(soul !== 'string'){ return to.next(msg) } // do not handle lexical cursors.
|
||||||
if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
|
if('alias' === soul){ // Allow reading the list of usernames/aliases in the system?
|
||||||
return to.next(msg); // yes.
|
return to.next(msg); // yes.
|
||||||
} else
|
} else
|
||||||
@ -111,9 +111,9 @@
|
|||||||
return each.end({err: "Account must match!"});
|
return each.end({err: "Account must match!"});
|
||||||
}
|
}
|
||||||
check['user'+soul+key] = 1;
|
check['user'+soul+key] = 1;
|
||||||
if(user && (user = user._) && user.sea && pub === user.pub){
|
if(user && user.is && pub === user.is.pub){
|
||||||
//var id = Gun.text.random(3);
|
//var id = Gun.text.random(3);
|
||||||
SEA.sign(val, user.sea, function(data){ var rel;
|
SEA.sign(val, (user._).sea, function(data){ var rel;
|
||||||
if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
|
if(u === data){ return each.end({err: SEA.err || 'Pub signature fail.'}) }
|
||||||
if(rel = Gun.val.link.is(val)){
|
if(rel = Gun.val.link.is(val)){
|
||||||
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
(at.sea.own[rel] = at.sea.own[rel] || {})[pub] = true;
|
||||||
@ -146,7 +146,7 @@
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
each.any = function(val, key, node, soul, user){ var tmp, pub;
|
each.any = function(val, key, node, soul, user){ var tmp, pub;
|
||||||
if(!user || !(user = user._) || !(user = user.sea)){
|
if(!user || !user.is){
|
||||||
if(tmp = relpub(soul)){
|
if(tmp = relpub(soul)){
|
||||||
check['any'+soul+key] = 1;
|
check['any'+soul+key] = 1;
|
||||||
SEA.verify(val, pub = tmp, function(data){ var rel;
|
SEA.verify(val, pub = tmp, function(data){ var rel;
|
||||||
@ -187,20 +187,19 @@
|
|||||||
//});
|
//});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pub = tmp;
|
if((pub = tmp) !== (user.is||noop).pub){
|
||||||
if(pub !== user.pub){
|
|
||||||
each.any(val, key, node, soul);
|
each.any(val, key, node, soul);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*var other = Gun.obj.map(at.sea.own[soul], function(v, p){
|
/*var other = Gun.obj.map(at.sea.own[soul], function(v, p){
|
||||||
if(user.pub !== p){ return p }
|
if((user.is||{}).pub !== p){ return p }
|
||||||
});
|
});
|
||||||
if(other){
|
if(other){
|
||||||
each.any(val, key, node, soul);
|
each.any(val, key, node, soul);
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
check['any'+soul+key] = 1;
|
check['any'+soul+key] = 1;
|
||||||
SEA.sign(val, user, function(data){
|
SEA.sign(val, (user._).sea, function(data){
|
||||||
if(u === data){ return each.end({err: 'My signature fail.'}) }
|
if(u === data){ return each.end({err: 'My signature fail.'}) }
|
||||||
node[key] = data;
|
node[key] = data;
|
||||||
check['any'+soul+key] = 0;
|
check['any'+soul+key] = 0;
|
||||||
@ -210,7 +209,7 @@
|
|||||||
each.end = function(ctx){ // TODO: Can't you just switch this to each.end = cb?
|
each.end = function(ctx){ // TODO: Can't you just switch this to each.end = cb?
|
||||||
if(each.err){ return }
|
if(each.err){ return }
|
||||||
if((each.err = ctx.err) || ctx.no){
|
if((each.err = ctx.err) || ctx.no){
|
||||||
console.log('NO!', each.err, msg.put);
|
console.log('NO!', each.err, msg.put); // 451 mistmached data FOR MARTTI
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!each.end.ed){ return }
|
if(!each.end.ed){ return }
|
||||||
@ -225,5 +224,6 @@
|
|||||||
}
|
}
|
||||||
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
|
to.next(msg); // pass forward any data we do not know how to handle or process (this allows custom security protocols).
|
||||||
}
|
}
|
||||||
|
var noop = {};
|
||||||
|
|
||||||
|
|
21
sea/leave.js
21
sea/leave.js
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
const authPersist = require('./persist')
|
|
||||||
const authsettings = require('./settings')
|
|
||||||
//const { scope: seaIndexedDb } = require('./indexed')
|
|
||||||
// This internal func executes logout actions
|
|
||||||
const authLeave = async (gunRoot, alias = gunRoot._.user._.alias) => {
|
|
||||||
var user = gunRoot._.user._ || {};
|
|
||||||
[ 'get', 'soul', 'ack', 'put', 'is', 'alias', 'pub', 'epub', 'sea' ].map((key) => delete user[key])
|
|
||||||
if(user.$){
|
|
||||||
delete user.$.is;
|
|
||||||
}
|
|
||||||
// Let's use default
|
|
||||||
gunRoot.user();
|
|
||||||
// Removes persisted authentication & CryptoKeys
|
|
||||||
try {
|
|
||||||
await authPersist({ alias: alias })
|
|
||||||
} catch (e) {} //eslint-disable-line no-empty
|
|
||||||
return { ok: 0 }
|
|
||||||
}
|
|
||||||
module.exports = authLeave
|
|
||||||
|
|
49
sea/login.js
49
sea/login.js
@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
const authPersist = require('./persist')
|
|
||||||
// This internal func finalizes User authentication
|
|
||||||
const finalizeLogin = async (alias, key, gunRoot, opts) => {
|
|
||||||
const user = gunRoot._.user
|
|
||||||
// add our credentials in-memory only to our root gun instance
|
|
||||||
var tmp = user._.tag;
|
|
||||||
var opt = user._.opt;
|
|
||||||
user._ = gunRoot.get('~'+key.pub)._;
|
|
||||||
user._.opt = opt;
|
|
||||||
var tags = user._.tag;
|
|
||||||
/*Object.values && Object.values(tmp).forEach(function(tag){
|
|
||||||
// TODO: This is ugly & buggy code, it needs to be refactored & tested into a event "merge" utility.
|
|
||||||
var t = tags[tag.tag];
|
|
||||||
console.log("hm??", tag, t);
|
|
||||||
if(!t){
|
|
||||||
tags[tag.tag] = tag;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(tag.last){
|
|
||||||
tag.last.to = t.to;
|
|
||||||
t.last = tag.last = t.last || tag.last;
|
|
||||||
}
|
|
||||||
t.to = tag.to;
|
|
||||||
})*/
|
|
||||||
//user._.tag = tmp || user._.tag;
|
|
||||||
// so that way we can use the credentials to encrypt/decrypt data
|
|
||||||
// that is input/output through gun (see below)
|
|
||||||
const pub = key.pub
|
|
||||||
const priv = key.priv
|
|
||||||
const epub = key.epub
|
|
||||||
const epriv = key.epriv
|
|
||||||
user._.is = user.is = {alias: alias, pub: pub};
|
|
||||||
Object.assign(user._, { alias: alias, pub: pub, epub: epub, sea: { pub: pub, priv: priv, epub: epub, epriv: epriv } })
|
|
||||||
//console.log("authorized", user._);
|
|
||||||
// persist authentication
|
|
||||||
//await authPersist(user._, key.proof, opts) // temporarily disabled
|
|
||||||
// emit an auth event, useful for page redirects and stuff.
|
|
||||||
try {
|
|
||||||
gunRoot._.on('auth', user._) // TODO: Deprecate this, emit on user instead! Update docs when you do.
|
|
||||||
//user._.on('auth', user._) // Arrgh, this doesn't work without event "merge" code, but "merge" code causes stack overflow and crashes after logging in & trying to write data.
|
|
||||||
} catch (e) {
|
|
||||||
console.log('Your \'auth\' callback crashed with:', e)
|
|
||||||
}
|
|
||||||
// returns success with the user data credentials.
|
|
||||||
return user._
|
|
||||||
}
|
|
||||||
module.exports = finalizeLogin
|
|
||||||
|
|
12
sea/pair.js
12
sea/pair.js
@ -4,8 +4,18 @@
|
|||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
var Buff = (typeof Buffer !== 'undefined')? Buffer : shim.Buffer;
|
||||||
|
|
||||||
|
SEA.name = SEA.name || (async (cb, opt) => { try {
|
||||||
|
if(cb){ try{ cb() }catch(e){console.log(e)} }
|
||||||
|
return;
|
||||||
|
} catch(e) {
|
||||||
|
console.log(e);
|
||||||
|
SEA.err = e;
|
||||||
|
if(cb){ cb() }
|
||||||
|
return;
|
||||||
|
}});
|
||||||
|
|
||||||
//SEA.pair = async (data, proof, cb) => { try {
|
//SEA.pair = async (data, proof, cb) => { try {
|
||||||
SEA.pair = SEA.pair || (async (cb) => { try {
|
SEA.pair = SEA.pair || (async (cb, opt) => { try {
|
||||||
|
|
||||||
const ecdhSubtle = shim.ossl || shim.subtle
|
const ecdhSubtle = shim.ossl || shim.subtle
|
||||||
// First: ECDSA keys for signing/verifying...
|
// First: ECDSA keys for signing/verifying...
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
|
|
||||||
const SEA = require('./sea');
|
|
||||||
const Gun = SEA.Gun;
|
|
||||||
const Buffer = require('./buffer')
|
|
||||||
const authsettings = require('./settings')
|
|
||||||
const updateStorage = require('./update')
|
|
||||||
// This internal func persists User authentication if so configured
|
|
||||||
const authPersist = async (user, proof, opts) => {
|
|
||||||
// opts = { pin: 'string' }
|
|
||||||
// no opts.pin then uses random PIN
|
|
||||||
// How this works:
|
|
||||||
// called when app bootstraps, with wanted options
|
|
||||||
// IF authsettings.validity === 0 THEN no remember-me, ever
|
|
||||||
// IF PIN then signed 'remember' to window.sessionStorage and 'auth' to IndexedDB
|
|
||||||
const pin = Buffer.from(
|
|
||||||
(Gun.obj.has(opts, 'pin') && opts.pin) || Gun.text.random(10),
|
|
||||||
'utf8'
|
|
||||||
).toString('base64')
|
|
||||||
|
|
||||||
const alias = user.alias
|
|
||||||
const exp = authsettings.validity // seconds // @mhelander what is `exp`???
|
|
||||||
|
|
||||||
if (proof && alias && exp) {
|
|
||||||
const iat = Math.ceil(Date.now() / 1000) // seconds
|
|
||||||
const remember = Gun.obj.has(opts, 'pin') || undefined // for hook - not stored
|
|
||||||
const props = authsettings.hook({ alias: alias, iat: iat, exp: exp, remember: remember })
|
|
||||||
const pub = user.pub
|
|
||||||
const epub = user.epub
|
|
||||||
const priv = user.sea.priv
|
|
||||||
const epriv = user.sea.epriv
|
|
||||||
const key = { pub: pub, priv: priv, epub: epub, epriv: epriv }
|
|
||||||
if (props instanceof Promise) {
|
|
||||||
const asyncProps = await props.then()
|
|
||||||
return await updateStorage(proof, key, pin)(asyncProps)
|
|
||||||
}
|
|
||||||
return await updateStorage(proof, key, pin)(props)
|
|
||||||
}
|
|
||||||
return await updateStorage()({ alias: 'delete' })
|
|
||||||
}
|
|
||||||
module.exports = authPersist
|
|
||||||
|
|
43
sea/query.js
43
sea/query.js
@ -1,43 +0,0 @@
|
|||||||
|
|
||||||
var SEA = require('./sea');
|
|
||||||
var Gun = SEA.Gun;
|
|
||||||
// This is internal func queries public key(s) for alias.
|
|
||||||
const queryGunAliases = (alias, gunRoot) => new Promise((resolve, reject) => {
|
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
|
||||||
gunRoot.get('~@'+alias).once((data, key) => {
|
|
||||||
//rev.off();
|
|
||||||
if (!data) {
|
|
||||||
// if no user, don't do anything.
|
|
||||||
const err = 'No user!'
|
|
||||||
Gun.log(err)
|
|
||||||
return reject({ err })
|
|
||||||
}
|
|
||||||
// then figuring out all possible candidates having matching username
|
|
||||||
const aliases = []
|
|
||||||
let c = 0
|
|
||||||
// TODO: how about having real chainable map without callback ?
|
|
||||||
Gun.obj.map(data, (at, pub) => {
|
|
||||||
if (!pub.slice || '~' !== pub.slice(0, 1)) {
|
|
||||||
// TODO: ... this would then be .filter((at, pub))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
++c
|
|
||||||
// grab the account associated with this public key.
|
|
||||||
gunRoot.get(pub).once(data => {
|
|
||||||
pub = pub.slice(1)
|
|
||||||
--c
|
|
||||||
if (data){
|
|
||||||
aliases.push({ pub, put: data })
|
|
||||||
}
|
|
||||||
if (!c && (c = -1)) {
|
|
||||||
resolve(aliases)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (!c) {
|
|
||||||
reject({ err: 'Public key does not exist!' })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
module.exports = queryGunAliases
|
|
||||||
|
|
141
sea/recall.js
141
sea/recall.js
@ -1,141 +0,0 @@
|
|||||||
|
|
||||||
const Buffer = require('./buffer')
|
|
||||||
const authsettings = require('./settings')
|
|
||||||
//const { scope: seaIndexedDb } = require('./indexed')
|
|
||||||
const queryGunAliases = require('./query')
|
|
||||||
const parseProps = require('./parse')
|
|
||||||
const updateStorage = require('./update')
|
|
||||||
const SEA = require('./sea')
|
|
||||||
const Gun = SEA.Gun;
|
|
||||||
const finalizeLogin = require('./login')
|
|
||||||
|
|
||||||
// This internal func recalls persisted User authentication if so configured
|
|
||||||
const authRecall = async (gunRoot, authprops) => {
|
|
||||||
// window.sessionStorage only holds signed { alias, pin } !!!
|
|
||||||
const remember = authprops || sessionStorage.getItem('remember')
|
|
||||||
const { alias = sessionStorage.getItem('user'), pin: pIn } = authprops || {} // @mhelander what is pIn?
|
|
||||||
const pin = pIn && Buffer.from(pIn, 'utf8').toString('base64')
|
|
||||||
// Checks for existing proof, matching alias and expiration:
|
|
||||||
const checkRememberData = async ({ proof, alias: aLias, iat, exp, remember }) => {
|
|
||||||
if (!!proof && alias === aLias) {
|
|
||||||
const checkNotExpired = (args) => {
|
|
||||||
if (Math.floor(Date.now() / 1000) < (iat + args.exp)) {
|
|
||||||
// No way hook to update 'iat'
|
|
||||||
return Object.assign(args, { iat: iat, proof: proof })
|
|
||||||
} else {
|
|
||||||
Gun.log('Authentication expired!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We're not gonna give proof to hook!
|
|
||||||
const hooked = authsettings.hook({ alias: alias, iat: iat, exp: exp, remember: remember })
|
|
||||||
return ((hooked instanceof Promise)
|
|
||||||
&& await hooked.then(checkNotExpired)) || checkNotExpired(hooked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const readAndDecrypt = async (data, pub, key) =>
|
|
||||||
parseProps(await SEA.decrypt(await SEA.verify(data, pub), key))
|
|
||||||
|
|
||||||
// Already authenticated?
|
|
||||||
if (gunRoot._.user
|
|
||||||
&& Gun.obj.has(gunRoot._.user._, 'pub')
|
|
||||||
&& Gun.obj.has(gunRoot._.user._, 'sea')) {
|
|
||||||
return gunRoot._.user._ // Yes, we're done here.
|
|
||||||
}
|
|
||||||
// No, got persisted 'alias'?
|
|
||||||
if (!alias) {
|
|
||||||
throw { err: 'No authentication session found!' }
|
|
||||||
}
|
|
||||||
// Yes, got persisted 'remember'?
|
|
||||||
if (!remember) {
|
|
||||||
throw { // And return proof if for matching alias
|
|
||||||
err: (await seaIndexedDb.get(alias, 'auth') && authsettings.validity
|
|
||||||
&& 'Missing PIN and alias!') || 'No authentication session found!'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Yes, let's get (all?) matching aliases
|
|
||||||
const aliases = (await queryGunAliases(alias, gunRoot))
|
|
||||||
.filter(({ pub } = {}) => !!pub)
|
|
||||||
// Got any?
|
|
||||||
if (!aliases.length) {
|
|
||||||
throw { err: 'Public key does not exist!' }
|
|
||||||
}
|
|
||||||
let err
|
|
||||||
// Yes, then attempt to log into each one until we find ours!
|
|
||||||
// (if two users have the same username AND the same password... that would be bad)
|
|
||||||
const [ { key, at, proof, pin: newPin } = {} ] = await Promise
|
|
||||||
.all(aliases.filter(({ at: { put } = {} }) => !!put)
|
|
||||||
.map(async ({ at: at, pub: pub }) => {
|
|
||||||
const readStorageData = async (args) => {
|
|
||||||
const props = args || parseProps(await SEA.verify(remember, pub, true))
|
|
||||||
let pin = props.pin
|
|
||||||
let aLias = props.alias
|
|
||||||
|
|
||||||
const data = (!pin && alias === aLias)
|
|
||||||
// No PIN, let's try short-term proof if for matching alias
|
|
||||||
? await checkRememberData(props)
|
|
||||||
// Got PIN so get IndexedDB secret if signature is ok
|
|
||||||
: await checkRememberData(await readAndDecrypt(await seaIndexedDb.get(alias, 'auth'), pub, pin))
|
|
||||||
pin = pin || data.pin
|
|
||||||
delete data.pin
|
|
||||||
return { pin: pin, data: data }
|
|
||||||
}
|
|
||||||
// got pub, try auth with pin & alias :: or unwrap Storage data...
|
|
||||||
const __gky20 = await readStorageData(pin && { pin, alias })
|
|
||||||
const data = __gky20.data
|
|
||||||
const newPin = __gky20.pin
|
|
||||||
const proof = data.proof
|
|
||||||
|
|
||||||
if (!proof) {
|
|
||||||
if (!data) {
|
|
||||||
err = 'No valid authentication session found!'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try { // Wipes IndexedDB silently
|
|
||||||
await updateStorage()(data)
|
|
||||||
} catch (e) {} //eslint-disable-line no-empty
|
|
||||||
err = 'Expired session!'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try { // auth parsing or decryption fails or returns empty - silently done
|
|
||||||
const auth= at.put.auth.auth
|
|
||||||
const sea = await SEA.decrypt(auth, proof)
|
|
||||||
if (!sea) {
|
|
||||||
err = 'Failed to decrypt private key!'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const priv = sea.priv
|
|
||||||
const epriv = sea.epriv
|
|
||||||
const epub = at.put.epub
|
|
||||||
// Success! we've found our private data!
|
|
||||||
err = null
|
|
||||||
return { proof: proof, at: at, pin: newPin, key: { pub: pub, priv: priv, epriv: epriv, epub: epub } }
|
|
||||||
} catch (e) {
|
|
||||||
err = 'Failed to decrypt private key!'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}).filter((props) => !!props))
|
|
||||||
|
|
||||||
if (!key) {
|
|
||||||
throw { err: err || 'Public key does not exist!' }
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we have AES decrypted the private key,
|
|
||||||
// if we were successful, then that means we're logged in!
|
|
||||||
try {
|
|
||||||
await updateStorage(proof, key, newPin || pin)(key)
|
|
||||||
|
|
||||||
const user = Object.assign(key, { at: at, proof: proof })
|
|
||||||
const pIN = newPin || pin
|
|
||||||
|
|
||||||
const pinProp = pIN && { pin: Buffer.from(pIN, 'base64').toString('utf8') }
|
|
||||||
|
|
||||||
return await finalizeLogin(alias, user, gunRoot, pinProp)
|
|
||||||
} catch (e) { // TODO: right log message ?
|
|
||||||
Gun.log('Failed to finalize login with new password!')
|
|
||||||
const { err = '' } = e || {}
|
|
||||||
throw { err: 'Finalizing new password login failed! Reason: '+err }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = authRecall
|
|
||||||
|
|
10
sea/root.js
10
sea/root.js
@ -7,15 +7,9 @@
|
|||||||
if(typeof window !== "undefined"){ module.window = window }
|
if(typeof window !== "undefined"){ module.window = window }
|
||||||
|
|
||||||
var tmp = module.window || module;
|
var tmp = module.window || module;
|
||||||
var SEA = tmp.SEA || function(){};
|
var SEA = tmp.SEA || {};
|
||||||
|
|
||||||
if(SEA.window = module.window){ try{
|
if(SEA.window = module.window){ SEA.window.SEA = SEA }
|
||||||
SEA.window.SEA = SEA;
|
|
||||||
tmp = document.createEvent('CustomEvent');
|
|
||||||
tmp.initCustomEvent('extension', false, false, {type: "SEA"});
|
|
||||||
(window.dispatchEvent || window.fireEvent)(tmp);
|
|
||||||
window.postMessage({type: "SEA"}, '*');
|
|
||||||
} catch(e){} }
|
|
||||||
|
|
||||||
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
|
try{ if(typeof common !== "undefined"){ common.exports = SEA } }catch(e){}
|
||||||
module.exports = SEA;
|
module.exports = SEA;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
// Old Code...
|
// Old Code...
|
||||||
|
try{
|
||||||
const __gky10 = require('./shim')
|
const __gky10 = require('./shim')
|
||||||
const crypto = __gky10.crypto
|
const crypto = __gky10.crypto
|
||||||
const subtle = __gky10.subtle
|
const subtle = __gky10.subtle
|
||||||
@ -18,8 +19,8 @@
|
|||||||
const keysToEcdsaJwk = __gky11.jwk
|
const keysToEcdsaJwk = __gky11.jwk
|
||||||
const sha1hash = require('./sha1')
|
const sha1hash = require('./sha1')
|
||||||
const sha256hash = require('./sha256')
|
const sha256hash = require('./sha256')
|
||||||
const recallCryptoKey = require('./remember')
|
|
||||||
const parseProps = require('./parse')
|
const parseProps = require('./parse')
|
||||||
|
}catch(e){}
|
||||||
|
|
||||||
// Practical examples about usage found from ./test/common.js
|
// Practical examples about usage found from ./test/common.js
|
||||||
const SEA = require('./root');
|
const SEA = require('./root');
|
||||||
@ -75,7 +76,7 @@
|
|||||||
// Cheers! Tell me what you think.
|
// Cheers! Tell me what you think.
|
||||||
var Gun = (SEA.window||{}).Gun || require('./gun', 1);
|
var Gun = (SEA.window||{}).Gun || require('./gun', 1);
|
||||||
Gun.SEA = SEA;
|
Gun.SEA = SEA;
|
||||||
SEA.Gun = Gun;
|
SEA.GUN = SEA.Gun = Gun;
|
||||||
|
|
||||||
module.exports = SEA
|
module.exports = SEA
|
||||||
|
|
@ -2,8 +2,12 @@
|
|||||||
var SEA = require('./root');
|
var SEA = require('./root');
|
||||||
var shim = require('./shim');
|
var shim = require('./shim');
|
||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
// Derive shared secret from other's pub and my epub/epriv
|
// Derive shared secret from other's pub and my epub/epriv
|
||||||
SEA.secret = SEA.secret || (async (key, pair, cb) => { try {
|
SEA.secret = SEA.secret || (async (key, pair, cb, opt) => { try {
|
||||||
|
opt = opt || {};
|
||||||
|
if(!pair || !pair.epriv || !pair.epub){
|
||||||
|
pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why});
|
||||||
|
}
|
||||||
const pub = key.epub || key
|
const pub = key.epub || key
|
||||||
const epub = pair.epub
|
const epub = pair.epub
|
||||||
const epriv = pair.epriv
|
const epriv = pair.epriv
|
||||||
|
57
sea/shim.js
57
sea/shim.js
@ -1,43 +1,38 @@
|
|||||||
|
|
||||||
|
const SEA = require('./root')
|
||||||
const Buffer = require('./buffer')
|
const Buffer = require('./buffer')
|
||||||
const api = {Buffer: Buffer}
|
const api = {Buffer: Buffer}
|
||||||
|
var o = {};
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if(SEA.window){
|
||||||
var crypto = window.crypto || window.msCrypto;
|
api.crypto = window.crypto || window.msCrypto;
|
||||||
var subtle = crypto.subtle || crypto.webkitSubtle;
|
api.subtle = (api.crypto||o).subtle || (api.crypto||o).webkitSubtle;
|
||||||
const TextEncoder = window.TextEncoder
|
api.TextEncoder = window.TextEncoder;
|
||||||
const TextDecoder = window.TextDecoder
|
api.TextDecoder = window.TextDecoder;
|
||||||
|
api.random = (len) => Buffer.from(api.crypto.getRandomValues(new Uint8Array(Buffer.alloc(len))))
|
||||||
|
}
|
||||||
|
if(!api.crypto){try{
|
||||||
|
var crypto = require('crypto', 1);
|
||||||
|
const { subtle } = require('@trust/webcrypto', 1) // All but ECDH
|
||||||
|
const { TextEncoder, TextDecoder } = require('text-encoding', 1)
|
||||||
Object.assign(api, {
|
Object.assign(api, {
|
||||||
crypto,
|
crypto,
|
||||||
subtle,
|
subtle,
|
||||||
TextEncoder,
|
TextEncoder,
|
||||||
TextDecoder,
|
TextDecoder,
|
||||||
random: (len) => Buffer.from(crypto.getRandomValues(new Uint8Array(Buffer.alloc(len))))
|
random: (len) => Buffer.from(crypto.randomBytes(len))
|
||||||
})
|
});
|
||||||
} else {
|
//try{
|
||||||
try{
|
const WebCrypto = require('node-webcrypto-ossl', 1)
|
||||||
var crypto = require('crypto', 1);
|
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
||||||
const { subtle } = require('@trust/webcrypto', 1) // All but ECDH
|
//}catch(e){
|
||||||
const { TextEncoder, TextDecoder } = require('text-encoding', 1)
|
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
||||||
Object.assign(api, {
|
//}
|
||||||
crypto,
|
}catch(e){
|
||||||
subtle,
|
console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!");
|
||||||
TextEncoder,
|
console.log("node-webcrypto-ossl is temporarily needed for ECDSA signature verification, and optionally needed for ECDH, please install if needed (currently necessary so add them to your package.json for now).");
|
||||||
TextDecoder,
|
TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
|
||||||
random: (len) => Buffer.from(crypto.randomBytes(len))
|
}}
|
||||||
});
|
|
||||||
//try{
|
|
||||||
const WebCrypto = require('node-webcrypto-ossl', 1)
|
|
||||||
api.ossl = new WebCrypto({directory: 'ossl'}).subtle // ECDH
|
|
||||||
//}catch(e){
|
|
||||||
//console.log("node-webcrypto-ossl is optionally needed for ECDH, please install if needed.");
|
|
||||||
//}
|
|
||||||
}catch(e){
|
|
||||||
console.log("@trust/webcrypto and text-encoding are not included by default, you must add it to your package.json!");
|
|
||||||
console.log("node-webcrypto-ossl is temporarily needed for ECDSA signature verification, and optionally needed for ECDH, please install if needed (currently necessary so add them to your package.json for now).");
|
|
||||||
TRUST_WEBCRYPTO_OR_TEXT_ENCODING_NOT_INSTALLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = api
|
module.exports = api
|
||||||
|
|
@ -4,7 +4,7 @@
|
|||||||
var S = require('./settings');
|
var S = require('./settings');
|
||||||
var sha256hash = require('./sha256');
|
var sha256hash = require('./sha256');
|
||||||
|
|
||||||
SEA.sign = SEA.sign || (async (data, pair, cb) => { try {
|
SEA.sign = SEA.sign || (async (data, pair, cb, opt) => { try {
|
||||||
if(data && data.slice
|
if(data && data.slice
|
||||||
&& 'SEA{' === data.slice(0,4)
|
&& 'SEA{' === data.slice(0,4)
|
||||||
&& '"m":' === data.slice(4,8)){
|
&& '"m":' === data.slice(4,8)){
|
||||||
@ -14,6 +14,10 @@
|
|||||||
if(cb){ try{ cb(data) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(data) }catch(e){console.log(e)} }
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
opt = opt || {};
|
||||||
|
if(!(pair||opt).priv){
|
||||||
|
pair = await SEA.I(null, {what: data, how: 'sign', why: opt.why});
|
||||||
|
}
|
||||||
const pub = pair.pub
|
const pub = pair.pub
|
||||||
const priv = pair.priv
|
const priv = pair.priv
|
||||||
const jwk = S.jwk(pub, priv)
|
const jwk = S.jwk(pub, priv)
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
|
|
||||||
const authsettings = require('./settings')
|
|
||||||
const SEA = require('./sea');
|
|
||||||
const Gun = SEA.Gun;
|
|
||||||
//const { scope: seaIndexedDb } = require('./indexed')
|
|
||||||
// This updates sessionStorage & IndexedDB to persist authenticated "session"
|
|
||||||
const updateStorage = (proof, key, pin) => async (props) => {
|
|
||||||
if (!Gun.obj.has(props, 'alias')) {
|
|
||||||
return // No 'alias' - we're done.
|
|
||||||
}
|
|
||||||
if (authsettings.validity && proof && Gun.obj.has(props, 'iat')) {
|
|
||||||
props.proof = proof
|
|
||||||
delete props.remember // Not stored if present
|
|
||||||
|
|
||||||
const alias = props.alias
|
|
||||||
const id = props.alias
|
|
||||||
const remember = { alias: alias, pin: pin }
|
|
||||||
|
|
||||||
try {
|
|
||||||
const signed = await SEA.sign(JSON.stringify(remember), key)
|
|
||||||
|
|
||||||
sessionStorage.setItem('user', alias)
|
|
||||||
sessionStorage.setItem('remember', signed)
|
|
||||||
|
|
||||||
const encrypted = await SEA.encrypt(props, pin)
|
|
||||||
|
|
||||||
if (encrypted) {
|
|
||||||
const auth = await SEA.sign(encrypted, key)
|
|
||||||
await seaIndexedDb.wipe() // NO! Do not do this. It ruins other people's sessionStorage code. This is bad/wrong, commenting it out.
|
|
||||||
await seaIndexedDb.put(id, { auth: auth })
|
|
||||||
}
|
|
||||||
|
|
||||||
return props
|
|
||||||
} catch (err) {
|
|
||||||
throw { err: 'Session persisting failed!' }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wiping IndexedDB completely when using random PIN
|
|
||||||
await seaIndexedDb.wipe() // NO! Do not do this. It ruins other people's sessionStorage code. This is bad/wrong, commenting it out.
|
|
||||||
// And remove sessionStorage data
|
|
||||||
sessionStorage.removeItem('user')
|
|
||||||
sessionStorage.removeItem('remember')
|
|
||||||
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
module.exports = updateStorage
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
|||||||
(at = (user = at.user = gun.chain(new User))._).opt = {};
|
(at = (user = at.user = gun.chain(new User))._).opt = {};
|
||||||
at.opt.uuid = function(cb){
|
at.opt.uuid = function(cb){
|
||||||
var id = uuid(), pub = root.user;
|
var id = uuid(), pub = root.user;
|
||||||
if(!pub || !(pub = (pub._).sea) || !(pub = pub.pub)){ return id }
|
if(!pub || !(pub = pub.is) || !(pub = pub.pub)){ return id }
|
||||||
id = id + '~' + pub + '.';
|
id = id + '~' + pub + '.';
|
||||||
if(cb && cb.call){ cb(null, id) }
|
if(cb && cb.call){ cb(null, id) }
|
||||||
return id;
|
return id;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
var parse = require('./parse');
|
var parse = require('./parse');
|
||||||
var u;
|
var u;
|
||||||
|
|
||||||
SEA.verify = SEA.verify || (async (data, pair, cb) => { try {
|
SEA.verify = SEA.verify || (async (data, pair, cb, opt) => { try {
|
||||||
const json = parse(data)
|
const json = parse(data)
|
||||||
if(false === pair){ // don't verify!
|
if(false === pair){ // don't verify!
|
||||||
const raw = (json !== data)?
|
const raw = (json !== data)?
|
||||||
@ -15,6 +15,9 @@
|
|||||||
if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(raw) }catch(e){console.log(e)} }
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
opt = opt || {};
|
||||||
|
// SEA.I // verify is free! Requires no user permission.
|
||||||
|
if(json === data){ throw "No signature on data." }
|
||||||
const pub = pair.pub || pair
|
const pub = pair.pub || pair
|
||||||
const jwk = S.jwk(pub)
|
const jwk = S.jwk(pub)
|
||||||
const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify'])
|
const key = await (shim.ossl || shim.subtle).importKey('jwk', jwk, S.ecdsa.pair, false, ['verify'])
|
||||||
@ -27,7 +30,7 @@
|
|||||||
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
if(cb){ try{ cb(r) }catch(e){console.log(e)} }
|
||||||
return r;
|
return r;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log(e);
|
console.log(e); // mismatched owner FOR MARTTI
|
||||||
SEA.err = e;
|
SEA.err = e;
|
||||||
if(cb){ cb() }
|
if(cb){ cb() }
|
||||||
return;
|
return;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
|
||||||
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
|
if(typeof Gun === 'undefined'){ return } // TODO: localStorage is Browser only. But it would be nice if it could somehow plugin into NodeJS compatible localStorage APIs?
|
||||||
|
|
||||||
var root, noop = function(){}, u;
|
var root, noop = function(){}, store, u;
|
||||||
if(typeof window !== 'undefined'){ root = window }
|
try{store = (Gun.window||noop).localStorage}catch(e){}
|
||||||
var store = root.localStorage || {setItem: noop, removeItem: noop, getItem: noop};
|
if(!store){
|
||||||
|
console.log("Warning: No localStorage exists to persist data to!");
|
||||||
|
store = {setItem: noop, removeItem: noop, getItem: noop};
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
|
NOTE: Both `lib/file.js` and `lib/memdisk.js` are based on this design!
|
||||||
If you update anything here, consider updating the other adapters as well.
|
If you update anything here, consider updating the other adapters as well.
|
||||||
|
@ -78,19 +78,19 @@ function val(msg, eve, to){
|
|||||||
var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
|
var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
|
||||||
if(tmp = msg.$$){
|
if(tmp = msg.$$){
|
||||||
link = tmp = (msg.$$._);
|
link = tmp = (msg.$$._);
|
||||||
if(u === tmp.put){
|
if(u !== link.put){
|
||||||
return;
|
data = link.put;
|
||||||
}
|
}
|
||||||
data = tmp.put;
|
|
||||||
}
|
}
|
||||||
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
if((tmp = eve.wait) && (tmp = tmp[at.id])){ clearTimeout(tmp) }
|
||||||
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|
if((!to && (u === data || at.soul || at.link || (link && !(0 < link.ack))))
|
||||||
|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (link||at).ack <= tmp)){
|
|| (u === data && (tmp = (obj_map(at.root.opt.peers, function(v,k,t){t(k)})||[]).length) && (!to && (link||at).ack <= tmp))){
|
||||||
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
tmp = (eve.wait = {})[at.id] = setTimeout(function(){
|
||||||
val.call({as:opt}, msg, eve, tmp || 1);
|
val.call({as:opt}, msg, eve, tmp || 1);
|
||||||
}, opt.wait || 99);
|
}, opt.wait || 99);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(link && u === link.put && (tmp = rel.is(data))){ data = Gun.node.ify({}, tmp) }
|
||||||
eve.rid(msg);
|
eve.rid(msg);
|
||||||
opt.ok.call(gun || opt.$, data, msg.get);
|
opt.ok.call(gun || opt.$, data, msg.get);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user