libves 2.16.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +398 -20
- package/libVES.node.js +1009 -132
- package/libves-3.0.0.tgz +0 -0
- package/package.json +4 -2
package/libVES.node.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* @title libVES
|
|
29
29
|
* @dev A JavaScript end-to-end encryption interface to VESvault REST API
|
|
30
|
-
* @version
|
|
30
|
+
* @version 3.00
|
|
31
31
|
*
|
|
32
32
|
* @dev Official source code: https://github.com/vesvault/libVES
|
|
33
33
|
*
|
|
@@ -51,6 +51,7 @@ if (typeof(libVES) != 'function') function libVES(optns) {
|
|
|
51
51
|
else if (this.user) this.type = 'primary';
|
|
52
52
|
else throw new libVES.Error('InvalidValue','Required parameters: user || domain');
|
|
53
53
|
this.unlockedKeys = {};
|
|
54
|
+
this.pendingKeys = {};
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
libVES.prototype = {
|
|
@@ -64,6 +65,7 @@ libVES.prototype = {
|
|
|
64
65
|
defaultHash: 'SHA256',
|
|
65
66
|
propagators: [],
|
|
66
67
|
request: function(method,uri,body,optns) {
|
|
68
|
+
var self = this;
|
|
67
69
|
if (!optns) optns = {};
|
|
68
70
|
return new Promise(function(resolve,reject) {
|
|
69
71
|
var xhr = new XMLHttpRequest();
|
|
@@ -80,8 +82,13 @@ libVES.prototype = {
|
|
|
80
82
|
return new libVES.Error(e.type,e.message,e);
|
|
81
83
|
});
|
|
82
84
|
if (errs.length) {
|
|
85
|
+
var retry = function(o) { return self.request(method, uri, body, (o || optns)); };
|
|
83
86
|
if (optns && optns.onerror) try {
|
|
84
|
-
resolve(optns.onerror(errs));
|
|
87
|
+
resolve(optns.onerror(errs, optns, retry));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
reject(e);
|
|
90
|
+
} else if (self.onerror) try {
|
|
91
|
+
resolve(self.onerror(errs, optns, retry));
|
|
85
92
|
} catch (e) {
|
|
86
93
|
reject(e);
|
|
87
94
|
}
|
|
@@ -95,7 +102,7 @@ libVES.prototype = {
|
|
|
95
102
|
if (body != null) xhr.setRequestHeader('Content-Type','application/json');
|
|
96
103
|
xhr.setRequestHeader('Accept','application/json');
|
|
97
104
|
if (this.user && optns.password) xhr.setRequestHeader('Authorization','Basic ' + btoa(this.user + ':' + optns.password));
|
|
98
|
-
else if (optns.token
|
|
105
|
+
else if (optns.token ?? this.token) xhr.setRequestHeader('Authorization','Bearer ' + (optns.token ? optns.token : this.token));
|
|
99
106
|
xhr.responseType = 'json';
|
|
100
107
|
xhr.send(body);
|
|
101
108
|
}.bind(this));
|
|
@@ -120,6 +127,10 @@ libVES.prototype = {
|
|
|
120
127
|
}
|
|
121
128
|
return '';
|
|
122
129
|
},
|
|
130
|
+
mergeFieldList: function(flds, flds2) {
|
|
131
|
+
for (var k in flds2) if (flds[k] instanceof Object) flds[k] = this.mergeFieldList(flds[k], flds2[k]); else flds[k] = flds2[k];
|
|
132
|
+
return flds;
|
|
133
|
+
},
|
|
123
134
|
elevateAuth: function(optns) {
|
|
124
135
|
var self = this;
|
|
125
136
|
if (optns && (optns.password || optns.token)) return Promise.resolve(optns);
|
|
@@ -236,7 +247,7 @@ libVES.prototype = {
|
|
|
236
247
|
unlock: function(veskey) {
|
|
237
248
|
var self = this;
|
|
238
249
|
return this.getVaultKey().then(function(vkey) {
|
|
239
|
-
return vkey.unlock(
|
|
250
|
+
return vkey.unlock(veskey).then(function(cryptoKey) {
|
|
240
251
|
if (self.VESkey === null) self.VESkey = veskey;
|
|
241
252
|
if (!self.token && self.type == 'secondary') return vkey.getSessionToken().then(function(tkn) {
|
|
242
253
|
self.token = tkn;
|
|
@@ -251,8 +262,13 @@ libVES.prototype = {
|
|
|
251
262
|
});
|
|
252
263
|
},
|
|
253
264
|
lock: function() {
|
|
254
|
-
|
|
255
|
-
|
|
265
|
+
var lock = [];
|
|
266
|
+
for (var kid in this.unlockedKeys) lock.push(this.unlockedKeys[kid].then(function(k) {
|
|
267
|
+
return k.lock();
|
|
268
|
+
}).catch(function() {}));
|
|
269
|
+
return Promise.all(lock).then(function() {
|
|
270
|
+
return true;
|
|
271
|
+
});
|
|
256
272
|
},
|
|
257
273
|
reset: function(val) {
|
|
258
274
|
this.userMe = undefined;
|
|
@@ -293,11 +309,11 @@ libVES.prototype = {
|
|
|
293
309
|
});
|
|
294
310
|
});
|
|
295
311
|
},
|
|
296
|
-
getItems: function() {
|
|
312
|
+
getItems: function(flds) {
|
|
297
313
|
var self = this;
|
|
298
314
|
return this.getVaultKey().then(function(k) {
|
|
299
315
|
return k.getId().then(function(kid) {
|
|
300
|
-
return k.getVaultEntries({type: true, deleted: true, file: {creator: true, externals: true}, vaultKey: {type: true, user: true}}).then(function(ves) {
|
|
316
|
+
return k.getVaultEntries(self.mergeFieldList({type: true, deleted: true, file: {creator: true, externals: true}, vaultKey: {type: true, user: true}}, flds)).then(function(ves) {
|
|
301
317
|
var vis = {};
|
|
302
318
|
var vlst = [];
|
|
303
319
|
for (var i = 0; i < ves.length; i++) {
|
|
@@ -308,6 +324,7 @@ libVES.prototype = {
|
|
|
308
324
|
if (!vi.vaultKey) vi.vaultKey = undefined;
|
|
309
325
|
vi = vis[viid] = self.getItem(vi);
|
|
310
326
|
vlst.push(vi);
|
|
327
|
+
if (!ves[i].vaultKey) ves[i].vaultKey = {id: kid};
|
|
311
328
|
vi.vaultEntryByKey[kid] = ves[i];
|
|
312
329
|
}
|
|
313
330
|
}
|
|
@@ -346,7 +363,7 @@ libVES.prototype = {
|
|
|
346
363
|
_matchUser: function(u) {
|
|
347
364
|
if (typeof(u) == 'object') {
|
|
348
365
|
if (u instanceof libVES.User) return u;
|
|
349
|
-
else return new libVES.User(u,this);
|
|
366
|
+
else return new libVES.User((u.id ? {id: u.id} : {email: u.email}), this);
|
|
350
367
|
} else if (typeof(u) == 'string' && u.match(/^\S+\@\S+$/)) return new libVES.User({email: u},this);
|
|
351
368
|
throw new libVES.Error('BadUser',"Cannot match user: " + u,{value: u});
|
|
352
369
|
},
|
|
@@ -377,11 +394,14 @@ libVES.prototype = {
|
|
|
377
394
|
}).catch(function(e) {
|
|
378
395
|
throw new libVES.Error('NotFound', 'Cannot match externalId for domain:' + ext.domain + ', user:' + user + '. Define libVES.Domain.' + ext.domain + '.userToVaultRef(user) to return a valid reference.',{error: e});
|
|
379
396
|
})).then(function(exts) {
|
|
380
|
-
|
|
397
|
+
var keyref = {externals: exts};
|
|
398
|
+
if (self.externalId && self.externalId[0] != '!') keyref.creator = self.me();
|
|
399
|
+
var vkey = new libVES.VaultKey(keyref, self);
|
|
381
400
|
return vkey.getId().then(function() {
|
|
382
401
|
return vkey;
|
|
383
402
|
}).catch(function(e) {
|
|
384
403
|
if (e.code != 'NotFound') throw e;
|
|
404
|
+
if (!keyref.creator) throw new libVES.Error.InvalidKey('Anonymous vaults are not authorized to create temp keys');
|
|
385
405
|
return Promise.resolve(user || m().then(function(dom) {
|
|
386
406
|
return dom.vaultRefToUser(exts[0]);
|
|
387
407
|
})).catch(function(e) {
|
|
@@ -434,8 +454,8 @@ libVES.prototype = {
|
|
|
434
454
|
return key.generate(veskey, optns).then(function(k) {
|
|
435
455
|
key.setField('vaultItems', veskey.then(function(v) {
|
|
436
456
|
var vi = new libVES.VaultItem({type: 'password'}, self);
|
|
437
|
-
return usr.getActiveVaultKeys().then(function() {
|
|
438
|
-
return [self.me(), self.getVaultKey()
|
|
457
|
+
return usr.getActiveVaultKeys().then(function(akeys) {
|
|
458
|
+
return [self.me(), self.getVaultKey()].concat(akeys);
|
|
439
459
|
}).catch(function(e) {
|
|
440
460
|
if (e.code != 'NotFound') throw e;
|
|
441
461
|
var sh = self.type == 'secondary' ? [self.me(), self.getVaultKey()] : [self.me()];
|
|
@@ -573,6 +593,9 @@ libVES.prototype = {
|
|
|
573
593
|
}).catch(function(e) {
|
|
574
594
|
if (!e || e.code != 'Unauthorized') throw e;
|
|
575
595
|
}).then(function() {
|
|
596
|
+
self.vaultKey = k.lock().then(function() {
|
|
597
|
+
return k;
|
|
598
|
+
});
|
|
576
599
|
return self.unlock(veskey);
|
|
577
600
|
});
|
|
578
601
|
});
|
|
@@ -807,7 +830,7 @@ libVES.prototype = {
|
|
|
807
830
|
return self.getVaultKey().then(function(vk) {
|
|
808
831
|
return vk.getField('unlockableVaultKeys').then(function(vks) {
|
|
809
832
|
var rs = {};
|
|
810
|
-
return Promise.all(vks.map(function(e, i) {
|
|
833
|
+
return Promise.all((vks || []).map(function(e, i) {
|
|
811
834
|
return e.getId().then(function(id) {
|
|
812
835
|
rs[id] = e;
|
|
813
836
|
});
|
|
@@ -917,6 +940,11 @@ libVES.Error.Redirect = function(msg, optns) {
|
|
|
917
940
|
};
|
|
918
941
|
libVES.Error.Redirect.prototype = new libVES.Error('Redirect');
|
|
919
942
|
|
|
943
|
+
libVES.Error.Unauthorized = function(msg, optns) {
|
|
944
|
+
this.init(msg, optns);
|
|
945
|
+
};
|
|
946
|
+
libVES.Error.Unauthorized.prototype = new libVES.Error('Unauthorized');
|
|
947
|
+
|
|
920
948
|
libVES.Error.Internal = function(msg, optns) {
|
|
921
949
|
this.init(msg, optns);
|
|
922
950
|
};
|
|
@@ -1024,6 +1052,14 @@ libVES.Util = {
|
|
|
1024
1052
|
}
|
|
1025
1053
|
return rs;
|
|
1026
1054
|
},
|
|
1055
|
+
fillUndefs: function(data, defs) {
|
|
1056
|
+
if (data instanceof Array) data.map((d) => libVES.Util.fillUndefs(d, defs));
|
|
1057
|
+
else if (data && defs) for (var k in defs) if (defs[k]) {
|
|
1058
|
+
if (data[k] === undefined) data[k] = undefined;
|
|
1059
|
+
else if (data[k] instanceof Object) libVES.Util.fillUndefs(data[k], defs[k]);
|
|
1060
|
+
}
|
|
1061
|
+
return data;
|
|
1062
|
+
},
|
|
1027
1063
|
loadWasm: function(src) {
|
|
1028
1064
|
return new Promise(function(resolve, reject) {
|
|
1029
1065
|
var sc = document.createElement('script');
|
|
@@ -1712,30 +1748,20 @@ libVES.Object.prototype = {
|
|
|
1712
1748
|
this.setFields(data,data.id == null);
|
|
1713
1749
|
if (refs) for (var k in refs) this[k] = Promise.resolve(refs[k]);
|
|
1714
1750
|
},
|
|
1715
|
-
setFields: function(
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
else if (v instanceof Array) return v.map(function(vv) { return clsf(vv); });
|
|
1730
|
-
else if (v) return new (self.fieldClass[k])(v,self.VES);
|
|
1731
|
-
})(v);
|
|
1732
|
-
return v;
|
|
1733
|
-
};
|
|
1734
|
-
})(k));
|
|
1735
|
-
else return Promise.reject(new libVES.Error('Internal',"Unknown field: " + k));
|
|
1736
|
-
}
|
|
1737
|
-
if (chg && self.trigger) self.trigger.resolve(self);
|
|
1738
|
-
return Promise.resolve(self);
|
|
1751
|
+
setFields: function(flds, up) {
|
|
1752
|
+
var res = Promise.resolve(null);
|
|
1753
|
+
for (var fld in flds) ((fld) => {
|
|
1754
|
+
if (up === undefined || up) this.fieldUpdate[fld] = true;
|
|
1755
|
+
var cls = this.fieldClass[fld];
|
|
1756
|
+
var cur = this[fld];
|
|
1757
|
+
res = this[fld] = res.then(() => Promise.resolve(flds[fld]).then((val) => Promise.resolve(cur).catch((er) => undefined).then((cur) => {
|
|
1758
|
+
if (!cls || !val) return val;
|
|
1759
|
+
else if (val instanceof Array) return val.map((v) => ((v instanceof libVES.Object) ? v : new cls(v, this.VES)));
|
|
1760
|
+
else if (cur instanceof cls) return cur.setFields(val, up);
|
|
1761
|
+
else return (val instanceof libVES.Object) ? val : new cls(val, this.VES);
|
|
1762
|
+
})));
|
|
1763
|
+
})(fld);
|
|
1764
|
+
return res.then(() => this);
|
|
1739
1765
|
},
|
|
1740
1766
|
setField: function(fld,val,upd) {
|
|
1741
1767
|
var flds = {};
|
|
@@ -1744,54 +1770,78 @@ libVES.Object.prototype = {
|
|
|
1744
1770
|
return self[fld];
|
|
1745
1771
|
});
|
|
1746
1772
|
},
|
|
1773
|
+
unsetField: function(fld) {
|
|
1774
|
+
delete(this[fld]);
|
|
1775
|
+
},
|
|
1776
|
+
_fieldsToLoad: function(fldlst, force, ld) {
|
|
1777
|
+
var flds = {};
|
|
1778
|
+
for (var k in fldlst) flds[k] ||= fldlst[k];
|
|
1779
|
+
for (var i = 0; i < this.fieldSets.length; i++) {
|
|
1780
|
+
for (var k in this.fieldSets[i]) if (fldlst[k]) {
|
|
1781
|
+
for (var k in this.fieldSets[i]) if ((force || !this[k]) && !(flds[k] instanceof Object)) (flds ||= {})[k] = this.fieldSets[i][k];
|
|
1782
|
+
break;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
var res = Promise.resolve(null);
|
|
1786
|
+
for (var fld in flds) ((fld) => {
|
|
1787
|
+
var cur = this[fld];
|
|
1788
|
+
var na = (cur === undefined);
|
|
1789
|
+
var cls = this.fieldClass[fld];
|
|
1790
|
+
res = res.then((res) => Promise.resolve(cur).catch((er) => null).then((cur) => {
|
|
1791
|
+
var lst = null;
|
|
1792
|
+
if (force || na) lst = flds[fld];
|
|
1793
|
+
else if (cls) {
|
|
1794
|
+
if (cur instanceof Array) lst = ((cur[0] instanceof cls) ? cur[0]._fieldsToLoad(flds[fld], force, ld.then((vals) => vals[fld])) : Promise.resolve(null)).then((lst) => (lst && flds[fld]));
|
|
1795
|
+
else if (cur instanceof cls) lst = cur._fieldsToLoad(flds[fld], force, ld.then((vals) => vals[fld]));
|
|
1796
|
+
}
|
|
1797
|
+
return Promise.resolve(lst).then((lst) => {
|
|
1798
|
+
if (lst) (res ||= {})[fld] = lst;
|
|
1799
|
+
return res;
|
|
1800
|
+
});
|
|
1801
|
+
}));
|
|
1802
|
+
if (na || (!cls && force)) (this[fld] = ld.then((vals) => (vals?.hasOwnProperty(fld) ? (cls && vals[fld] ? ((vals[fld] instanceof Array) ? vals[fld].map((e) => new cls(e, this.VES)) : new cls(vals[fld], this.VES)) : vals[fld]) : cur))).catch(() => null);
|
|
1803
|
+
})(fld);
|
|
1804
|
+
ld.then((vals) => {
|
|
1805
|
+
for (var fld in vals) if (!flds[fld] && !this[fld]) this.setField(fld, vals[fld], false);
|
|
1806
|
+
}).catch((er) => null);
|
|
1807
|
+
return res.then((res) => {
|
|
1808
|
+
if (res) for (var k in fldlst) if (res[k]) return res;
|
|
1809
|
+
return null;
|
|
1810
|
+
});
|
|
1811
|
+
},
|
|
1747
1812
|
getFields: function(fldlst, force) {
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
for (var k in this.fieldSets[i]) if (fldlst[k]) {
|
|
1752
|
-
for (var k in this.fieldSets[i]) if ((force || !this[k]) && !(flds[k] instanceof Object)) flds[k] = this.fieldSets[i][k];
|
|
1753
|
-
break;
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
for (var k in fldlst) if ((force || !this[k]) && !(flds[k] instanceof Object)) flds[k] = fldlst[k];
|
|
1757
|
-
for (var k in flds) {
|
|
1758
|
-
this.loadFields(flds);
|
|
1759
|
-
break;
|
|
1760
|
-
}
|
|
1813
|
+
var ldf;
|
|
1814
|
+
var ld = new Promise((resolve, reject) => ldf = [resolve, reject]);
|
|
1815
|
+
this._fieldsToLoad(fldlst, force, ld).then((flds) => this.loadFields(flds, force)).then((vals) => ldf[0](vals)).catch((er) => ldf[1](er));
|
|
1761
1816
|
var plst = [];
|
|
1762
|
-
for (var k in fldlst) {
|
|
1763
|
-
plst.push(
|
|
1817
|
+
for (var k in fldlst) ((k) => {
|
|
1818
|
+
plst.push(ld.then(() => this[k]).then(function rslv(v) {
|
|
1819
|
+
if (v instanceof Array) return Promise.all(v.map(function(e, i) {
|
|
1820
|
+
return rslv(e);
|
|
1821
|
+
}));
|
|
1764
1822
|
if (!(v instanceof libVES.Object)) return v;
|
|
1765
|
-
return v.getFields(fldlst[k]
|
|
1823
|
+
return v.getFields(fldlst[k]);
|
|
1766
1824
|
}));
|
|
1767
|
-
}
|
|
1825
|
+
})(k);
|
|
1768
1826
|
var rs = {};
|
|
1769
|
-
return Promise.all(plst).then(
|
|
1827
|
+
return Promise.all(plst).then((lst) => {
|
|
1770
1828
|
var i = 0;
|
|
1771
1829
|
for (var k in fldlst) rs[k] = lst[i++];
|
|
1772
1830
|
return rs;
|
|
1773
1831
|
});
|
|
1774
1832
|
},
|
|
1775
1833
|
getField: function(fld,fldlst,force) {
|
|
1776
|
-
var self = this;
|
|
1777
1834
|
if (!this[fld] || force) {
|
|
1778
1835
|
var flds = {};
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
break;
|
|
1782
|
-
}
|
|
1783
|
-
if (fldlst) flds[fld] = fldlst;
|
|
1784
|
-
if (!flds[fld]) {
|
|
1785
|
-
var cls = self.fieldClass[fld];
|
|
1786
|
-
flds[fld] = cls ? cls.prototype.fieldList : true;
|
|
1787
|
-
}
|
|
1788
|
-
this.loadFields(flds,force);
|
|
1836
|
+
flds[fld] = fldlst || true;
|
|
1837
|
+
this.getFields(flds, force).catch(() => null);
|
|
1789
1838
|
}
|
|
1790
1839
|
return this[fld];
|
|
1791
1840
|
},
|
|
1792
1841
|
loadFields: function(flds,force,optns) {
|
|
1842
|
+
if (!flds) return Promise.resolve({});
|
|
1793
1843
|
var self = this;
|
|
1794
|
-
|
|
1844
|
+
return (this.id && !flds.id ? this.id.then(function(id) { return self.VES.get(self.apiUri + '/' + id,flds,optns); }) : self.postData().then(function(data) {
|
|
1795
1845
|
data['$op'] = 'fetch';
|
|
1796
1846
|
return self.VES.post(self.apiUri,data,flds,optns);
|
|
1797
1847
|
}).then(function(data) {
|
|
@@ -1800,21 +1850,10 @@ libVES.Object.prototype = {
|
|
|
1800
1850
|
self.fieldUpdate = {id: true};
|
|
1801
1851
|
}
|
|
1802
1852
|
return data;
|
|
1803
|
-
});
|
|
1804
|
-
for (var k in flds) if (force || this[k] === undefined) {
|
|
1805
|
-
this[k] = req.then((function(fld) {
|
|
1806
|
-
var cls = self.fieldClass[fld];
|
|
1807
|
-
return function(data) {
|
|
1808
|
-
if (cls && data[fld]) return ((data[fld] instanceof Array) ? data[fld].map(function(v) {
|
|
1809
|
-
return new cls(v,self.VES);
|
|
1810
|
-
}) : new cls(data[fld],self.VES));
|
|
1811
|
-
return data[fld];
|
|
1812
|
-
};
|
|
1813
|
-
})(k));
|
|
1814
|
-
}
|
|
1853
|
+
})).then((data) => libVES.Util.fillUndefs(data, flds));
|
|
1815
1854
|
},
|
|
1816
1855
|
reset: function() {
|
|
1817
|
-
for (var k in this.fieldClass)
|
|
1856
|
+
for (var k in this.fieldClass) this.unsetField(k);
|
|
1818
1857
|
return Promise.resolve();
|
|
1819
1858
|
},
|
|
1820
1859
|
getId: function() {
|
|
@@ -1888,6 +1927,23 @@ libVES.Object.prototype = {
|
|
|
1888
1927
|
},
|
|
1889
1928
|
resolveError: function(e,field) {
|
|
1890
1929
|
throw e;
|
|
1930
|
+
},
|
|
1931
|
+
setUpdate: function(flds) {
|
|
1932
|
+
var rs = [];
|
|
1933
|
+
for (var k in flds) {
|
|
1934
|
+
if (flds[k]) this.fieldUpdate[k] = true;
|
|
1935
|
+
else delete(this.fieldUpdate[k]);
|
|
1936
|
+
if (this[k] && (flds[k] instanceof Object)) (function(p, k) {
|
|
1937
|
+
rs.push(p.then(function rslv(obj) {
|
|
1938
|
+
if (obj instanceof Array) return Promise.all(obj.map(rslv));
|
|
1939
|
+
if (obj instanceof libVES.Object) return obj.setUpdate(flds[k]);
|
|
1940
|
+
}));
|
|
1941
|
+
})(this[k], k);
|
|
1942
|
+
}
|
|
1943
|
+
return Promise.all(rs);
|
|
1944
|
+
},
|
|
1945
|
+
toJSON: function() {
|
|
1946
|
+
return String(this);
|
|
1891
1947
|
}
|
|
1892
1948
|
};
|
|
1893
1949
|
|
|
@@ -2040,10 +2096,16 @@ libVES.VaultKey.prototype = new libVES.Object({
|
|
|
2040
2096
|
case 'lost':
|
|
2041
2097
|
return self.getVaultItems().then(function(vis) {
|
|
2042
2098
|
var f = function(vis) {
|
|
2043
|
-
if (!vis.length) throw new libVES.Error('InvalidKey','Cannot unlock the secondary key');
|
|
2099
|
+
if (!vis || !vis.length) throw new libVES.Error('InvalidKey','Cannot unlock the secondary key');
|
|
2044
2100
|
return vis[0].getType().then(function(t) {
|
|
2045
2101
|
switch (t) {
|
|
2046
|
-
case 'password': return
|
|
2102
|
+
case 'password': return self.getId().then(function(kid) {
|
|
2103
|
+
if (!self.VES.pendingKeys) self.VES.pendingKeys = {};
|
|
2104
|
+
if (kid) self.VES.pendingKeys[kid] = true;
|
|
2105
|
+
return vis[0].get().finally(function() {
|
|
2106
|
+
self.VES.pendingKeys[kid] = false;
|
|
2107
|
+
});
|
|
2108
|
+
});
|
|
2047
2109
|
default: return f(vis.slice(1));
|
|
2048
2110
|
}
|
|
2049
2111
|
});
|
|
@@ -2054,26 +2116,57 @@ libVES.VaultKey.prototype = new libVES.Object({
|
|
|
2054
2116
|
}
|
|
2055
2117
|
});
|
|
2056
2118
|
},
|
|
2119
|
+
invalidVESkey: function() {
|
|
2120
|
+
var self = this;
|
|
2121
|
+
return self.getVaultItems().then(function(vis) {
|
|
2122
|
+
return Promise.all(vis.map(function(vi) {
|
|
2123
|
+
return vi.getVaultEntries().then(function(entries) {
|
|
2124
|
+
if (entries) for (let i = 0; i < entries.length; i++) if (self.VES.unlockedKeys[entries[i].vaultKey.id]) return Promise.resolve(self.VES.unlockedKeys[entries[i].vaultKey.id]).then(function(key) {
|
|
2125
|
+
return key.getFields({user: {email: true}, extrenals: {domain: true, externalId: true}}).then(function(flds) {
|
|
2126
|
+
return vi.unshareWith([flds.externals && flds.externals[0] ? flds.externals[0] : flds.user.email]);
|
|
2127
|
+
});
|
|
2128
|
+
});
|
|
2129
|
+
});
|
|
2130
|
+
}));
|
|
2131
|
+
});
|
|
2132
|
+
},
|
|
2057
2133
|
unlock: function(veskey) {
|
|
2058
2134
|
var self = this;
|
|
2059
|
-
if (self.wcPriv)
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2135
|
+
if (!self.wcPriv) self.wcPriv = self.getId().then(function(id) {
|
|
2136
|
+
return Promise.resolve(self.VES.unlockedKeys[id]).then(function(k) {
|
|
2137
|
+
return k.wcPriv;
|
|
2138
|
+
}).catch(function(e) {
|
|
2139
|
+
let un = self.engine().then(function(m) {
|
|
2140
|
+
return self.resolveVESkey(veskey).then(function(v) {
|
|
2141
|
+
return self.getPrivateKey().then(function(prk) {
|
|
2142
|
+
return m.import(prk, {password: v}).catch(function(e) {
|
|
2143
|
+
if (e && e.code == 'InvalidKey' && !veskey) {
|
|
2144
|
+
console.log('Bad key propagated for ' + id + ', unsharing...');
|
|
2145
|
+
return self.invalidVESkey().catch(function(e2) {
|
|
2146
|
+
console.log(e2);
|
|
2147
|
+
}).then(function(r) {
|
|
2148
|
+
console.log(r);
|
|
2149
|
+
throw e;
|
|
2150
|
+
});
|
|
2151
|
+
}
|
|
2152
|
+
throw e;
|
|
2153
|
+
});
|
|
2154
|
+
});
|
|
2069
2155
|
});
|
|
2070
2156
|
});
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2157
|
+
let vk = self.VES.unlockedKeys[id] = un.then(function() {
|
|
2158
|
+
return self;
|
|
2159
|
+
}).catch(function(e) {
|
|
2160
|
+
if (self.VES.unlockedKeys[id] === vk) delete(self.VES.unlockedKeys[id]);
|
|
2161
|
+
});
|
|
2162
|
+
return un;
|
|
2075
2163
|
});
|
|
2076
2164
|
});
|
|
2165
|
+
else if (veskey) self.wcPriv = self.wcPriv.catch(function(e) {
|
|
2166
|
+
delete(self.wcPriv);
|
|
2167
|
+
return self.unlock(veskey);
|
|
2168
|
+
});
|
|
2169
|
+
return self.wcPriv;
|
|
2077
2170
|
},
|
|
2078
2171
|
lock: function() {
|
|
2079
2172
|
var self = this;
|
|
@@ -2251,8 +2344,20 @@ libVES.VaultKey.prototype = new libVES.Object({
|
|
|
2251
2344
|
});
|
|
2252
2345
|
});
|
|
2253
2346
|
},
|
|
2254
|
-
matchVaults: function(
|
|
2255
|
-
|
|
2347
|
+
matchVaults: function(shares) {
|
|
2348
|
+
return this.getFields({id: true, externals: {domain: true, externalId: true}, user: {email: true}}).then((flds) => (shares || []).reduce((res, sh) => res.then((res) => Promise.resolve(sh).then((sh) => {
|
|
2349
|
+
if (res) return res;
|
|
2350
|
+
var ext = flds.externals?.[0];
|
|
2351
|
+
if (sh instanceof libVES.Object) {
|
|
2352
|
+
if (sh instanceof libVES.VaultKey) return sh.getId().then((id) => (id == flds.id ? sh : res));
|
|
2353
|
+
else if ((sh instanceof libVES.External) && ext) return sh.getFields({domain: true, externalId: true}).then((xflds) => (xflds.domain == ext.domain && xflds.externalId == ext.externalId ? sh : res));
|
|
2354
|
+
else if ((sh instanceof libVES.User) && flds.user && !ext) return sh.getEmail().then((email) => (email == flds.user.email ? sh : res));
|
|
2355
|
+
} else if (sh instanceof Object) {
|
|
2356
|
+
if (sh.externalId && sh.externalId == ext?.externalId && (sh.domain || this.VES.domain) == ext.domain) return sh;
|
|
2357
|
+
else if (sh.email && sh.email == flds.user?.email && !ext) return sh;
|
|
2358
|
+
} else if (sh && flds.user?.email == sh && !ext) return sh;
|
|
2359
|
+
return res;
|
|
2360
|
+
})), Promise.resolve(false)));
|
|
2256
2361
|
},
|
|
2257
2362
|
getKeyOptions: function() {
|
|
2258
2363
|
var self = this;
|
|
@@ -2268,7 +2373,7 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2268
2373
|
apiUri: 'vaultItems',
|
|
2269
2374
|
fieldList: {id: true, deleted: true, file: true},
|
|
2270
2375
|
fieldClass: {vaultKey: libVES.VaultKey, file: libVES.File},
|
|
2271
|
-
fieldSets: [{type: true, meta: true},{vaultEntries: {id: true, encData: true, vaultKey: {id: true, type: true, user: {id: true}, algo: true, externals: {id: true}}}},{vaultKey: true, file: true}],
|
|
2376
|
+
fieldSets: [{type: true, meta: true},{vaultEntries: {id: true, encData: true, vaultKey: {id: true, type: true, user: {id: true, email: true}, algo: true, externals: {id: true, domain: true, externalId: true}}}},{vaultKey: true, file: true}],
|
|
2272
2377
|
defaultCipher: 'AES256GCM',
|
|
2273
2378
|
getRaw: function() {
|
|
2274
2379
|
var self = this;
|
|
@@ -2277,9 +2382,11 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2277
2382
|
var fn = function() {
|
|
2278
2383
|
if (vaultEntries) for (; i < vaultEntries.length; i++) {
|
|
2279
2384
|
var k, d;
|
|
2280
|
-
if ((d = vaultEntries[i].encData) != null &&
|
|
2385
|
+
if ((d = vaultEntries[i].encData) != null && (k = vaultKeys[vaultEntries[i].vaultKey.id]) && (!self.VES.pendingKeys || !self.VES.pendingKeys[vaultEntries[i].vaultKey.id])) {
|
|
2281
2386
|
i++;
|
|
2282
|
-
return
|
|
2387
|
+
return Promise.resolve(k).then(function(k) {
|
|
2388
|
+
return k.decrypt(d).catch(fn);
|
|
2389
|
+
});
|
|
2283
2390
|
}
|
|
2284
2391
|
}
|
|
2285
2392
|
return Promise.reject(new libVES.Error('InvalidKey',"No unlocked key to decrypt the item",{vaultItem: self}));
|
|
@@ -2338,7 +2445,10 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2338
2445
|
},
|
|
2339
2446
|
build: function(data) {
|
|
2340
2447
|
var self = this;
|
|
2341
|
-
return this.getType().
|
|
2448
|
+
return this.getType().catch(function(e) {
|
|
2449
|
+
if (e && e.code == 'NotFound') return self.setField('type', libVES.VaultItem.Type._detect(data));
|
|
2450
|
+
throw e;
|
|
2451
|
+
}).then(function(type) {
|
|
2342
2452
|
return libVES.getModule(libVES.VaultItem.Type,type).then(function(m) {
|
|
2343
2453
|
return m.build.call(self,data);
|
|
2344
2454
|
});
|
|
@@ -2350,12 +2460,12 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2350
2460
|
shareWith: function(usrs,val,save) {
|
|
2351
2461
|
var self = this;
|
|
2352
2462
|
return self.VES.usersToKeys(usrs).then(function(ks) {
|
|
2353
|
-
return (val == null
|
|
2463
|
+
return (val == null ? self.getVaultEntries().then(function(ves) {
|
|
2354
2464
|
var k_ves = {};
|
|
2355
2465
|
var k_used = {};
|
|
2356
2466
|
for (var j = 0; j < ves.length; j++) k_ves[ves[j].vaultKey.id] = ves[j];
|
|
2357
2467
|
return Promise.all(ks.map(function(k,j) {
|
|
2358
|
-
return k.getId().then(function(k_id) {
|
|
2468
|
+
if (!k.fieldUpdate.privateKey) return k.getId().then(function(k_id) {
|
|
2359
2469
|
k_used[k_id] = true;
|
|
2360
2470
|
return k_ves[k_id];
|
|
2361
2471
|
}).catch(function(){});
|
|
@@ -2367,21 +2477,26 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2367
2477
|
var new_ves = [];
|
|
2368
2478
|
var set_ves = [];
|
|
2369
2479
|
var valr = null;
|
|
2480
|
+
var key_ids = {};
|
|
2370
2481
|
return Promise.all(ks.map(function(k,j) {
|
|
2371
2482
|
return new_ves[j] = (old_ves[j] || (valr != null ? valr : valr = self.resolveRaw(val)).then(function(v) {
|
|
2372
|
-
return
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2483
|
+
return (function(refs) {
|
|
2484
|
+
if (refs) for (var i in refs) if (refs[i] === k) return Promise.resolve({'$ref':i});
|
|
2485
|
+
return k.postData(null,refs);
|
|
2486
|
+
})(libVES.Object._refs).then(function(pd) {
|
|
2487
|
+
if (pd.id) {
|
|
2488
|
+
if (key_ids[pd.id]) return;
|
|
2489
|
+
key_ids[pd.id] = true;
|
|
2490
|
+
}
|
|
2491
|
+
return k.encrypt(v).then(function(ctext) {
|
|
2377
2492
|
return set_ves.push({vaultKey: pd, encData: ctext});
|
|
2378
2493
|
});
|
|
2379
2494
|
});
|
|
2380
2495
|
}));
|
|
2381
2496
|
})).then(function() {
|
|
2382
2497
|
return Promise.all(old_ves.slice(ks.length).map(function(ve,j) {
|
|
2383
|
-
return (new libVES.VaultKey(ve.vaultKey,self.VES)).matchVaults(
|
|
2384
|
-
if (f
|
|
2498
|
+
return (new libVES.VaultKey(ve.vaultKey,self.VES)).matchVaults(usrs).then(function(f) {
|
|
2499
|
+
if (!f) set_ves.push({vaultKey: {id: ve.vaultKey.id}, '$op': 'delete'});
|
|
2385
2500
|
});
|
|
2386
2501
|
}));
|
|
2387
2502
|
}).then(function() {
|
|
@@ -2390,12 +2505,20 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2390
2505
|
});
|
|
2391
2506
|
});
|
|
2392
2507
|
}).then(function() {
|
|
2393
|
-
if (
|
|
2394
|
-
|
|
2508
|
+
if (self.id && val != null) return self.setUpdate({id: false, type: true, file: {id: false, externals: {id: false, domain: true, externalId: true}}});
|
|
2509
|
+
}).then(function() {
|
|
2510
|
+
if (save || save === undefined) return self.post(undefined, undefined, ((save instanceof Object) ? save : undefined)).then(function(flds) {
|
|
2511
|
+
self.vaultEntryByKey = {};
|
|
2512
|
+
self.setFields(flds, false);
|
|
2395
2513
|
return self;
|
|
2514
|
+
}).finally(function() {
|
|
2515
|
+
delete(self.vaultEntries);
|
|
2396
2516
|
});
|
|
2397
2517
|
return self;
|
|
2398
|
-
})
|
|
2518
|
+
}).catch(function(er) {
|
|
2519
|
+
delete(self.vaultEntries);
|
|
2520
|
+
throw er;
|
|
2521
|
+
});
|
|
2399
2522
|
},
|
|
2400
2523
|
reshareWith: function(share,val,save) {
|
|
2401
2524
|
var self = this;
|
|
@@ -2418,6 +2541,15 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2418
2541
|
});
|
|
2419
2542
|
});
|
|
2420
2543
|
},
|
|
2544
|
+
unshareWith: function(share, save) {
|
|
2545
|
+
return this.getShareVaultKeys().then((vkeys) => Promise.all(vkeys.map((vkey) => vkey.matchVaults(share))).then((del) => this.shareWith(vkeys.filter((vkey, i) => !del[i]), undefined, save)));
|
|
2546
|
+
},
|
|
2547
|
+
set: function(val, save) {
|
|
2548
|
+
var self = this;
|
|
2549
|
+
return self.getShareList().then(function(sh) {
|
|
2550
|
+
return self.shareWith(sh, val, save);
|
|
2551
|
+
});
|
|
2552
|
+
},
|
|
2421
2553
|
getShareVaultKeys: function() {
|
|
2422
2554
|
var self = this;
|
|
2423
2555
|
return this.getVaultEntries().then(function(vaultEntries) {
|
|
@@ -2430,12 +2562,17 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2430
2562
|
var self = this;
|
|
2431
2563
|
return this.getShareVaultKeys().then(function(vaultKeys) {
|
|
2432
2564
|
var uids = {};
|
|
2565
|
+
var xids = {};
|
|
2433
2566
|
return Promise.all(vaultKeys.map(function(e,i) {
|
|
2434
2567
|
return e.getExternals().then(function(exts) {
|
|
2435
|
-
if (exts && exts.length) return exts[0]
|
|
2568
|
+
if (exts && exts.length) return exts[0].getId().then(function(xid) {
|
|
2569
|
+
if (!xid || xids[xid]) return null;
|
|
2570
|
+
xids[xid] = true;
|
|
2571
|
+
return exts[0];
|
|
2572
|
+
});
|
|
2436
2573
|
return e.getUser().then(function(u) {
|
|
2437
2574
|
return u.getId().then(function(uid) {
|
|
2438
|
-
if (uids[uid]) return null;
|
|
2575
|
+
if (!uid || uids[uid]) return null;
|
|
2439
2576
|
uids[uid] = true;
|
|
2440
2577
|
return u;
|
|
2441
2578
|
});
|
|
@@ -2451,7 +2588,7 @@ libVES.VaultItem.prototype = new libVES.Object({
|
|
|
2451
2588
|
delete: function() {
|
|
2452
2589
|
var self = this;
|
|
2453
2590
|
return self.getId().then(function(id) {
|
|
2454
|
-
return
|
|
2591
|
+
return self.VES.post(self.apiUri + '/' + id, {'$op': 'delete'});
|
|
2455
2592
|
});
|
|
2456
2593
|
}
|
|
2457
2594
|
});
|
|
@@ -4306,7 +4443,7 @@ libVES.Watch.prototype = {
|
|
|
4306
4443
|
}).then(function(res) {
|
|
4307
4444
|
if (!res.events) return null;
|
|
4308
4445
|
return res.events.map(function(e, i) {
|
|
4309
|
-
return new libVES.Event(e, self.VES);
|
|
4446
|
+
return new libVES.Event(libVES.Util.fillUndefs(e, self.fields), self.VES);
|
|
4310
4447
|
});
|
|
4311
4448
|
});
|
|
4312
4449
|
},
|
|
@@ -4321,8 +4458,8 @@ libVES.Watch.prototype = {
|
|
|
4321
4458
|
if (!busy && this.state == 'stop') this.setState('idle');
|
|
4322
4459
|
},
|
|
4323
4460
|
start: function(pos) {
|
|
4324
|
-
if (!this.eventfn)
|
|
4325
|
-
if (this.busy)
|
|
4461
|
+
if (!this.eventfn) return Promise.reject(new libVES.Error('InvalidValue', 'eventfn is not set'));
|
|
4462
|
+
if (this.busy) return Promise.reject(new libVES.Error('Internal', 'Watch instance is busy'));
|
|
4326
4463
|
var self = this;
|
|
4327
4464
|
self.setBusy(true);
|
|
4328
4465
|
this.setState('read');
|
|
@@ -4330,13 +4467,17 @@ libVES.Watch.prototype = {
|
|
|
4330
4467
|
this.next = 0;
|
|
4331
4468
|
return this.getEvents(0, pos).then(function(evs) {
|
|
4332
4469
|
self.setBusy(false);
|
|
4333
|
-
return self.received(evs.reverse());
|
|
4334
|
-
})
|
|
4470
|
+
return self.received(evs ? evs.reverse() : null);
|
|
4471
|
+
}).finally(function() {
|
|
4472
|
+
self.setBusy(false);
|
|
4473
|
+
});
|
|
4335
4474
|
} else {
|
|
4336
4475
|
this.next = pos;
|
|
4337
4476
|
return this.getEvents(pos, 0).then(function(evs) {
|
|
4338
4477
|
self.setBusy(false);
|
|
4339
4478
|
return self.received(evs);
|
|
4479
|
+
}).finally(function() {
|
|
4480
|
+
self.setBusy(false);
|
|
4340
4481
|
});
|
|
4341
4482
|
}
|
|
4342
4483
|
},
|
|
@@ -4346,31 +4487,30 @@ libVES.Watch.prototype = {
|
|
|
4346
4487
|
received: function(evs) {
|
|
4347
4488
|
var self = this;
|
|
4348
4489
|
var p;
|
|
4349
|
-
|
|
4490
|
+
if (!evs) return null;
|
|
4491
|
+
if (evs.length) return evs.reduce(function(v, ev) {
|
|
4350
4492
|
if (self.active()) try {
|
|
4351
|
-
self.eventfn(
|
|
4493
|
+
self.eventfn(ev, self);
|
|
4352
4494
|
} catch(e) {
|
|
4353
4495
|
console.log(e);
|
|
4354
4496
|
}
|
|
4355
|
-
return
|
|
4497
|
+
return ev;
|
|
4356
4498
|
}, null).getId().then(function(id) {
|
|
4357
4499
|
self.next = id + 1;
|
|
4500
|
+
return self.request();
|
|
4358
4501
|
});
|
|
4359
4502
|
else {
|
|
4360
|
-
p = Promise.resolve(null);
|
|
4361
4503
|
if (this.state == 'read') this.setState('poll');
|
|
4504
|
+
return self.request(), true;
|
|
4362
4505
|
}
|
|
4363
|
-
return p.then(function() {
|
|
4364
|
-
return self.request();
|
|
4365
|
-
});
|
|
4366
4506
|
},
|
|
4367
4507
|
request: function() {
|
|
4368
|
-
if (this.busy || !this.active()) return;
|
|
4508
|
+
if (this.busy || !this.active()) return true;
|
|
4369
4509
|
var self = this;
|
|
4370
4510
|
self.setBusy(true);
|
|
4371
4511
|
return self.getEvents(self.next, 0, (self.state == 'poll')).then(function(evs) {
|
|
4372
4512
|
self.setBusy(false);
|
|
4373
|
-
self.received(evs);
|
|
4513
|
+
return self.received(evs);
|
|
4374
4514
|
}).catch(function(e) {
|
|
4375
4515
|
self.setBusy(false);
|
|
4376
4516
|
if (self.errorfn) try {
|
|
@@ -4393,6 +4533,743 @@ libVES.Watch.prototype = {
|
|
|
4393
4533
|
this.setState(this.busy ? 'stop' : 'idle');
|
|
4394
4534
|
}
|
|
4395
4535
|
};
|
|
4396
|
-
|
|
4536
|
+
|
|
4537
|
+
|
|
4538
|
+
|
|
4539
|
+
libVES.subtle = function(optns) {
|
|
4540
|
+
if (!(optns instanceof Object)) {
|
|
4541
|
+
let uri = String(optns);
|
|
4542
|
+
optns = uri.includes('/') ? libVES.Vault.toRef(uri) : {domain: uri};
|
|
4543
|
+
}
|
|
4544
|
+
return new libVES(optns).subtle();
|
|
4545
|
+
};
|
|
4546
|
+
|
|
4547
|
+
libVES.prototype.subtle = function(ref) {
|
|
4548
|
+
return new libVES.Vault((ref || this), this);
|
|
4549
|
+
};
|
|
4550
|
+
|
|
4551
|
+
libVES.EventTarget = class extends EventTarget {
|
|
4552
|
+
watchFields() {
|
|
4553
|
+
return {vaultKey: {id: true, user: {id: true, email: true}, externals: {domain: true, externalId: true}}, vaultItem: {id: true, type: true, deleted: true, vaultKey: {id: true, type: true, externals: {domain: true, externalId: true}, user: {id: true, email: true}}, file: {externals: {domain: true, externalId: true}, creator: true}}, authorSession: {id: true, vaultKey: {id: true, type: true, externals: {domain: true, externalId: true}}, domain: true, user: {email: true}, remote: true, userAgent: true}, session: {id: true, vaultKey: {id: true}, user: {id: true}, remote: true, userAgent: true}};
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
dispatchEvent(ev) {
|
|
4557
|
+
if (!ev) return;
|
|
4558
|
+
super.dispatchEvent(ev);
|
|
4559
|
+
if (this['on' + ev.type]) try { this['on' + ev.type](ev) } catch (er) { console.log(er); }
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
start(optns) {
|
|
4563
|
+
if (!this.watch) {
|
|
4564
|
+
let tr = this._eventTracker();
|
|
4565
|
+
if (optns === false) optns = {replay: true};
|
|
4566
|
+
else if (optns != null && !isNaN(optns)) optns = {startEvent: Number(optns)};
|
|
4567
|
+
else if (!(optns instanceof Object)) optns = {};
|
|
4568
|
+
if (optns.startEvent == null) optns.startEvent = -1;
|
|
4569
|
+
this.watch = this.createWatch(optns);
|
|
4570
|
+
this.watch.eventfn = (ev) => (this.eventClear = this.eventClear.then(() => tr._event(ev).then((ev) => this.dispatchEvent(ev)).catch((er) => console.log(er))));
|
|
4571
|
+
this.eventClear = optns.replay ? this.replay((optns.replay instanceof Object) ? optns.replay : optns).then((evs) => Promise.all(evs.map((ev) => this.dispatchEvent(ev)))) : Promise.resolve();
|
|
4572
|
+
return this.watch.start(optns.startEvent).then((v) => (v || this.badauth().then(() => this.watch.start(startEvent)))).catch((er) => {
|
|
4573
|
+
this.watch = null;
|
|
4574
|
+
throw er;
|
|
4575
|
+
});
|
|
4576
|
+
} else throw new libVES.Error.InvalidValue('The watch is already started');
|
|
4577
|
+
}
|
|
4578
|
+
|
|
4579
|
+
stop() {
|
|
4580
|
+
if (this.watch) return Promise.resolve(this.watch.stop()).then(() => this.eventClear).then(() => true).finally(() => this.watch = null);
|
|
4581
|
+
return Promise.resolve(null);
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
events(optns) {
|
|
4585
|
+
let tr = this._eventTracker();
|
|
4586
|
+
return this.createWatch(optns).getEvents(0, 0).then((evs) => (evs ? evs.reduce((res, ev) => res.then((res) => tr._event(ev, false).then((ev) => ((ev && res.push(ev)), res))), Promise.resolve([])) : this.badauth().then(() => this.events())));
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4589
|
+
toJSON() {
|
|
4590
|
+
return this.uri();
|
|
4591
|
+
}
|
|
4592
|
+
};
|
|
4593
|
+
|
|
4594
|
+
libVES.Vault = class extends libVES.EventTarget {
|
|
4595
|
+
constructor(ref, ves, fields) {
|
|
4596
|
+
super();
|
|
4597
|
+
this.init(libVES.Vault.toRef(ref, ves), ves, fields);
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
init(ref, ves, fields) {
|
|
4601
|
+
fields ||= {};
|
|
4602
|
+
if (ref.externalId) fields.externals = [{domain: (this.domain = String(ref.domain)), externalId: (this.externalId = String(ref.externalId))}];
|
|
4603
|
+
this.email = ref.email || '';
|
|
4604
|
+
if (ref.email) fields.email = this.email;
|
|
4605
|
+
if (ref.version) this.version = fields.id = Number(ref.version);
|
|
4606
|
+
if (!fields.id) fields.type = this.externalId ? 'secondary' : 'current';
|
|
4607
|
+
this.vaultKey = new libVES.VaultKey(fields, ves);
|
|
4608
|
+
delete(this.admin);
|
|
4609
|
+
};
|
|
4610
|
+
|
|
4611
|
+
item(ref) {
|
|
4612
|
+
return new libVES.Item(ref, this);
|
|
4613
|
+
};
|
|
4614
|
+
|
|
4615
|
+
vault(ref) {
|
|
4616
|
+
if (!ref) return null;
|
|
4617
|
+
return new libVES.Vault(ref, this.vaultKey.VES);
|
|
4618
|
+
}
|
|
4619
|
+
|
|
4620
|
+
entries(optns) {
|
|
4621
|
+
const flds = {encData: true, vaultItem: {type: true, file: {externals: true, creator: {id: true, email: true}}, deleted: true, vaultKey: {type: true, externals: true, user: true}, type: true, meta: true}};
|
|
4622
|
+
const getfn = () => this.vaultKey.getField('vaultEntries', flds, true).then((data) => libVES.Util.fillUndefs(data, flds));
|
|
4623
|
+
if (!this._entriesCache) (this._entriesCache = getfn().then((entries) => (entries || this.badauth().then(getfn)))).catch(() => null).finally(() => this._entriesCache = null);
|
|
4624
|
+
return this._entriesCache;
|
|
4625
|
+
};
|
|
4626
|
+
|
|
4627
|
+
objects(optns) {
|
|
4628
|
+
let found = {items: {}, vaults: {}};
|
|
4629
|
+
return this.entries(optns).then((entries) => Promise.all((entries || []).map((entry) => {
|
|
4630
|
+
const ext = entry.vaultItem?.file?.externals?.[0];
|
|
4631
|
+
if (ext) {
|
|
4632
|
+
if (entry.vaultItem.deleted) return null;
|
|
4633
|
+
let d = (found.items[ext.domain] ||= {});
|
|
4634
|
+
return d[ext.externalId] ? null : (d[ext.externalId] = new libVES.Item(ext, this, entry.vaultItem));
|
|
4635
|
+
}
|
|
4636
|
+
const vkey = entry.vaultItem?.vaultKey;
|
|
4637
|
+
const kext = vkey?.externals?.[0];
|
|
4638
|
+
if (kext && vkey.type == 'secondary' && entry.vaultItem.type == 'password') {
|
|
4639
|
+
let d = (found.vaults[kext.domain] ||= {});
|
|
4640
|
+
return d[kext.externalId] ? null : (d[kext.externalId] = new libVES.Vault(kext, this.vaultKey.VES, vkey));
|
|
4641
|
+
}
|
|
4642
|
+
}))).then((objs) => objs.filter((obj) => obj));
|
|
4643
|
+
};
|
|
4644
|
+
|
|
4645
|
+
items(optns) {
|
|
4646
|
+
return this.objects(optns).then((objs) => objs.filter((obj) => (obj instanceof libVES.Item)));
|
|
4647
|
+
}
|
|
4648
|
+
|
|
4649
|
+
vaults(optns) {
|
|
4650
|
+
return this.objects(optns).then((objs) => objs.filter((obj) => (obj instanceof libVES.Vault)));
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
_setunlock(reauth) {
|
|
4654
|
+
this.current = true;
|
|
4655
|
+
this.owner = this.externalId[0] != '!';
|
|
4656
|
+
this.lock(600);
|
|
4657
|
+
let rf;
|
|
4658
|
+
if (reauth !== false) (rf = (tmout) => {
|
|
4659
|
+
this.refreshTmout = setTimeout(() => {
|
|
4660
|
+
this.vaultKey.loadFields({encSessionToken: true}, true, {token: ''}).then((flds) => this.vaultKey.setFields(flds)).then(() => this.vaultKey.getSessionToken()).then((tk) => {
|
|
4661
|
+
this.vaultKey.VES.token = tk;
|
|
4662
|
+
rf();
|
|
4663
|
+
}).catch((er) => {
|
|
4664
|
+
console.log(er);
|
|
4665
|
+
rf(60000);
|
|
4666
|
+
});
|
|
4667
|
+
}, (tmout || 14400000));
|
|
4668
|
+
})();
|
|
4669
|
+
return this;
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4672
|
+
unlock(optns) {
|
|
4673
|
+
if (this.lockInProgress) return this.lock().then(() => this.unlock(optns));
|
|
4674
|
+
if (optns && !(optns instanceof Object)) optns = libVES.Vault.toRef(optns, this.vaultKey.VES);
|
|
4675
|
+
if (optns?.veskey) return this.vaultKey.VES.logout().then(() => {
|
|
4676
|
+
let ves = new libVES(optns);
|
|
4677
|
+
delete(ves.veskey);
|
|
4678
|
+
return ((reauth) => ves.unlock(String(optns.veskey)).then(() => this.init(ves, ves)).then(() => this._setunlock(reauth)))(!this.vaultKey.VES.token);
|
|
4679
|
+
});
|
|
4680
|
+
return this.vaultKey.VES.unlock().catch((er) => {
|
|
4681
|
+
let mode = optns?.mode || 'delegate';
|
|
4682
|
+
switch (mode) {
|
|
4683
|
+
case 'delegate': return this.vaultKey.VES.delegate(optns).then((ves) => this.init(ves, ves));
|
|
4684
|
+
case 'flow': return this.vaultKey.VES.flow(true, optns).then((ves) => this.init(ves, ves));
|
|
4685
|
+
case 'veskey': throw new libVES.Error.InvalidValue('No veskey provided');
|
|
4686
|
+
default: throw new libVES.Error.InvalidValue('Unknown unlock mode: ' + mode + ', expected: delegate|flow');
|
|
4687
|
+
}
|
|
4688
|
+
}).then(() => this._setunlock());
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
unlocked(optns) {
|
|
4692
|
+
return this.vaultKey.VES.unlock().catch((er) => {
|
|
4693
|
+
if (optns?.mode != 'flow') throw er;
|
|
4694
|
+
return this.vaultKey.VES.flow(false, optns).then((ves) => this.init(ves, ves)).then(() => this._setunlock());
|
|
4695
|
+
}).then(() => true).catch((er) => false);
|
|
4696
|
+
}
|
|
4697
|
+
|
|
4698
|
+
anonymous(optns) {
|
|
4699
|
+
if (!(optns instanceof Object)) {
|
|
4700
|
+
if (optns && typeof(optns) == 'string') optns = {veskey: optns};
|
|
4701
|
+
else optns = {};
|
|
4702
|
+
}
|
|
4703
|
+
if (!optns.veskey) return Promise.reject(libVES.Error.InvalidValue('Required: veskey'));
|
|
4704
|
+
if (!this.vaultKey.VES.domain) return Promise.reject(libVES.Error.InvalidValue('Cannot create an anonymous vault without a VES domain'));
|
|
4705
|
+
this.vaultKey.VES.logout();
|
|
4706
|
+
return (optns.externalId ? Promise.resolve(this.vaultKey.VES.externalId = optns.externalId) : crypto.subtle.digest('sha-256', libVES.Util.StringToByteArray('libVES.subtle\0' + this.vaultKey.VES.domain + '\0' + optns.veskey)).then((hash) => {
|
|
4707
|
+
const hex = '0123456789abcdef';
|
|
4708
|
+
let xid = new Uint8Array(hash).reduce(((v, e) => v + hex[e >> 4] + hex[e & 0x0f]), '!');
|
|
4709
|
+
this.vaultKey.VES.externalId = xid;
|
|
4710
|
+
})).then(() => this.vaultKey.VES.unlock(optns.veskey).catch((er) => {
|
|
4711
|
+
if (er?.code == 'NotFound') return this.vaultKey.VES.setAnonymousKey(optns.veskey, optns);
|
|
4712
|
+
else throw er;
|
|
4713
|
+
})).then(() => this.init(this.vaultKey.VES, this.vaultKey.VES)).then(() => this._setunlock());
|
|
4714
|
+
}
|
|
4715
|
+
|
|
4716
|
+
lock(tmout) {
|
|
4717
|
+
this.lockInProgress = false;
|
|
4718
|
+
clearTimeout(this.lockTmout);
|
|
4719
|
+
this.lockTmout = null;
|
|
4720
|
+
if (tmout > 0) {
|
|
4721
|
+
if (tmout > 3600) tmout = 3600;
|
|
4722
|
+
this.lockTmout = setTimeout(() => {
|
|
4723
|
+
this.lockInProgress = true;
|
|
4724
|
+
this.dispatchEvent(new libVES.CustomEvent('authexpire', {detail: {vault: this}}));
|
|
4725
|
+
if (this.lockInProgress) this.lock();
|
|
4726
|
+
}, tmout * 1000);
|
|
4727
|
+
return Promise.resolve(true);
|
|
4728
|
+
}
|
|
4729
|
+
clearTimeout(this.refreshTmout);
|
|
4730
|
+
this.refreshTmout = null;
|
|
4731
|
+
this.stop();
|
|
4732
|
+
return this.vaultKey.VES.logout();
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
random() {
|
|
4736
|
+
return this.vaultKey.VES.generateVESkey();
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4739
|
+
static toRef(value, ves) {
|
|
4740
|
+
switch (typeof(value)) {
|
|
4741
|
+
case 'object':
|
|
4742
|
+
return value;
|
|
4743
|
+
case 'number':
|
|
4744
|
+
value = String(value);
|
|
4745
|
+
case 'string':
|
|
4746
|
+
let uri = new URL(value, "ves://" + (ves && ves.domain ? ves.domain : ''));
|
|
4747
|
+
if (uri.protocol != 'ves:') throw new libVES.Error.InvalidValue("Invalid schema: '" + uri.protocol + "', expected 'ves:'");
|
|
4748
|
+
let p = uri.pathname.split('/');
|
|
4749
|
+
let id = p.length > 1 ? decodeURIComponent(p[1]) : '';
|
|
4750
|
+
let ref = uri.host ? {domain: uri.host, externalId: id} : {version: Number(id)};
|
|
4751
|
+
if (p[2] != null) ref.email = decodeURIComponent(p[2]);
|
|
4752
|
+
if (uri.password) ref.veskey = uri.password;
|
|
4753
|
+
return ref;
|
|
4754
|
+
}
|
|
4755
|
+
};
|
|
4756
|
+
|
|
4757
|
+
static toRefs(refs, ves, owner) {
|
|
4758
|
+
if (!refs) return [];
|
|
4759
|
+
if (!(refs instanceof Array)) refs = [refs];
|
|
4760
|
+
refs = refs.map((ref) => libVES.Vault.toRef(ref, ves)).filter((ref) => ref);
|
|
4761
|
+
if (owner) {
|
|
4762
|
+
if (!ves.externalId) throw new libVES.Error.Unauthorized('The vault is locked');
|
|
4763
|
+
refs.push({domain: ves.domain, externalId: ves.externalId});
|
|
4764
|
+
}
|
|
4765
|
+
return refs;
|
|
4766
|
+
}
|
|
4767
|
+
|
|
4768
|
+
static toUri(ref, short) {
|
|
4769
|
+
if (ref.externalId && short) return (ref.domain == short ? '' : '//' + encodeURIComponent(ref.domain) + '/') + encodeURIComponent(ref.externalId).replaceAll('%40', '@');
|
|
4770
|
+
let uri = ref.externalId ? "ves://" + encodeURIComponent(ref.domain) + "/" + encodeURIComponent(ref.externalId) : "ves://" + (ref.version ? "/" + String(Number(ref.version)) : encodeURIComponent(ref.domain ?? '') + "/");
|
|
4771
|
+
if (ref.email != null) uri += '/' + encodeURIComponent(ref.email);
|
|
4772
|
+
return uri;
|
|
4773
|
+
}
|
|
4774
|
+
|
|
4775
|
+
uri() {
|
|
4776
|
+
if (!this.externalId && !this.version && !this.email) return null;
|
|
4777
|
+
return libVES.Vault.toUri(this);
|
|
4778
|
+
}
|
|
4779
|
+
|
|
4780
|
+
short() {
|
|
4781
|
+
return libVES.Vault.toUri(this, this.vaultKey.VES.domain);
|
|
4782
|
+
}
|
|
4783
|
+
|
|
4784
|
+
badauth() {
|
|
4785
|
+
const ves = this.vaultKey.VES;
|
|
4786
|
+
if (!this.externalId || (this.externalId == ves.externalId && this.domain == ves.domain)) return Promise.reject(new libVES.Error.InvalidKey('Not authorized for this key'));
|
|
4787
|
+
return this.vaultKey.getFields({encSessionToken: true, type: true, algo: true, publicKey: true, privateKey: true, vaultItems: {type: true, vaultEntries: {vaultKey : {id: true}, encData: true}, meta: true}}).then(() => {
|
|
4788
|
+
const ves2 = new libVES({domain: this.domain, externalId: this.externalId});
|
|
4789
|
+
ves2.vaultKey = Promise.resolve(this.vaultKey);
|
|
4790
|
+
ves2.unlockedKeys = ves.unlockedKeys;
|
|
4791
|
+
ves2.pendingKeys = ves.pendingKeys;
|
|
4792
|
+
this.vaultKey.VES = ves2;
|
|
4793
|
+
return ves2.unlock();
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4797
|
+
createWatch() {
|
|
4798
|
+
let w = new libVES.Watch(this.vaultKey);
|
|
4799
|
+
w.fields = {vaultItem: {id: true, type: true, deleted: true, vaultKey: {id: true, type: true, algo: true, publicKey: true, externals: {domain: true, externalId: true}, user: {id: true, email: true}}, file: {externals: {domain: true, externalId: true}, creator: true}, meta: true}, authorSession: {vaultKey: {id: true, externals: {domain: true, externalId: true}, user: {email: true}, type: true}, domain: true, user: {email: true}, remote: true, userAgent: true}, session: {id: true, remote: true, userAgent: true, vaultKey: {id: true}, user: {id: true}}};
|
|
4800
|
+
return w;
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
replay(optns) {
|
|
4804
|
+
return this.objects(optns).then((objs) => Promise.all(objs.map((obj) => {
|
|
4805
|
+
if (obj instanceof libVES.Item) return new libVES.CustomEvent('itemadd', {detail: {replay: true, item: obj, share: this}});
|
|
4806
|
+
else if (obj instanceof libVES.Vault) return new libVES.CustomEvent('vaultadd', {detail: {replay: true, vault: obj, share: this}});
|
|
4807
|
+
})));
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4810
|
+
password() {
|
|
4811
|
+
return this.vaultKey.getVaultItems().then((vitems) => {
|
|
4812
|
+
if (!vitems) return this.badauth().then(() => this.password());
|
|
4813
|
+
return vitems.reduce((p, vitem) => p.catch((e) => vitem.getType().then((type) => (type == 'password' ? vitem.getId().then((id) => this.item({version: id})) : Promise.reject(e)))), Promise.reject(new libVES.Error.NotFound('No password item found')));
|
|
4814
|
+
});
|
|
4815
|
+
}
|
|
4816
|
+
|
|
4817
|
+
verify() {
|
|
4818
|
+
if (this.current !== undefined) return Promise.resolve(this);
|
|
4819
|
+
return this.vaultKey.VES.usersToKeys([(this.externalId ? {domain: this.domain, externalId: this.externalId} : this.email)]).then((vkeys) => Promise.all(vkeys.map((vkey) => vkey.getFields(vkey.id ? {id: true, type: true} : {type: true}))).then((flds) => flds.reduce((res, fld, idx) => {
|
|
4820
|
+
if (res) return res;
|
|
4821
|
+
if (this.version) return this.version == fld.id ? vkeys[idx] : null;
|
|
4822
|
+
switch (fld.type) {
|
|
4823
|
+
case 'secondary': case 'current': case 'temp': return vkeys[idx];
|
|
4824
|
+
}
|
|
4825
|
+
}, null))).then((vkey) => {
|
|
4826
|
+
if (!vkey) throw new libVES.Error.NotFound('Cannot find or create a matching key');
|
|
4827
|
+
return vkey.getType().then((type) => {
|
|
4828
|
+
this.current = (type == 'current' || type == 'secondary');
|
|
4829
|
+
this.owner = (this.externalId != null && this.externalId == vkey.VES.externalId && this.domain == vkey.VES.domain) || (!!this.email && this.email == vkey.VES.email);
|
|
4830
|
+
this.vaultKey = vkey;
|
|
4831
|
+
return this;
|
|
4832
|
+
});
|
|
4833
|
+
});
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
_eventTracker() {
|
|
4837
|
+
return new libVES.EventTracker(this);
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
toString() {
|
|
4841
|
+
return 'libVES.Vault(' + (this.uri() ?? '[pending]') + ')';
|
|
4842
|
+
}
|
|
4843
|
+
|
|
4844
|
+
};
|
|
4845
|
+
|
|
4846
|
+
libVES.Author = class {
|
|
4847
|
+
constructor(sess, ves) {
|
|
4848
|
+
this.sessid = sess.id;
|
|
4849
|
+
if (sess.vaultKey?.externals?.[0]) this.vault = new libVES.Vault(sess.vaultKey.externals[0], ves);
|
|
4850
|
+
else if (sess.user?.email) this.vault = new libVES.Vault({email: sess.user.email}, ves);
|
|
4851
|
+
this.remote = sess.remote;
|
|
4852
|
+
this.userAgent = sess.userAgent;
|
|
4853
|
+
}
|
|
4854
|
+
toString() {
|
|
4855
|
+
return 'libVES.Author(' + (this.sessid ?? '') + ')';
|
|
4856
|
+
}
|
|
4857
|
+
toJSON() {
|
|
4858
|
+
return this.sessid;
|
|
4859
|
+
}
|
|
4860
|
+
};
|
|
4861
|
+
|
|
4862
|
+
libVES.CustomEvent = class extends CustomEvent {
|
|
4863
|
+
toString() {
|
|
4864
|
+
return 'libVES.CustomEvent(' + this.type + (this.detail?.id ? '[' + this.detail.id + ']' : '') + (this.detail?.at ? ' @' + this.detail.at : '') + ')';
|
|
4865
|
+
}
|
|
4866
|
+
toJSON() {
|
|
4867
|
+
return this.detail?.id;
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
|
|
4871
|
+
libVES.EventTracker = class {
|
|
4872
|
+
constructor(vault, share) {
|
|
4873
|
+
this.vault = vault;
|
|
4874
|
+
this.share = share;
|
|
4875
|
+
this.items = {};
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
_event(e, attn) {
|
|
4879
|
+
const event = (type, detail) => {
|
|
4880
|
+
detail.event = e;
|
|
4881
|
+
return e.getFields({id: true, recordedAt: true, authorSession: {vaultKey: {id: true, externals: {domain: true, externalId: true}, user: {email: true}}, user: {email: true}, domain: true, remote: true, userAgent: true}}).then((flds) => {
|
|
4882
|
+
detail.id = flds?.id;
|
|
4883
|
+
if (flds?.recordedAt) detail.at = new Date(flds.recordedAt);
|
|
4884
|
+
detail.author = new libVES.Author((flds?.authorSession ?? {}), this.vault.vaultKey.VES);
|
|
4885
|
+
}).catch(() => null).then(() => new libVES.CustomEvent(type, {detail: detail}));
|
|
4886
|
+
}
|
|
4887
|
+
return e.getType().then((type) => {
|
|
4888
|
+
const itemfn = (ac, attn, sh) => e.getVaultItem().then((vitem) => vitem.getType().then((itype) => {
|
|
4889
|
+
switch (itype) {
|
|
4890
|
+
case 'string': case 'file':
|
|
4891
|
+
return vitem.getFields({id: true, file: {externals: {domain: true, externalId: true}}, deleted: true, type: true, meta: true}).then((flds) => {
|
|
4892
|
+
let ext = flds?.file?.externals?.[0];
|
|
4893
|
+
if (!ext) return null;
|
|
4894
|
+
let item = new libVES.Item({domain: ext.domain, externalId: ext.externalId, version: flds.id}, this.vault, flds);
|
|
4895
|
+
let ids = (this.items[item.uri()] ||= []);
|
|
4896
|
+
let idx = ids.indexOf(flds.id);
|
|
4897
|
+
if (idx < 0 && (flds.deleted === false || (ac == 'create' || ac == 'add'))) ids.unshift(flds.id), idx = 0;
|
|
4898
|
+
return event((idx == 0 ? '' : 'old') + 'item' + ac, {item: item, share: sh});
|
|
4899
|
+
});
|
|
4900
|
+
case 'password':
|
|
4901
|
+
return vitem.getFields({vaultKey: {type: true, externals: {domain: true, externalId: true}}}).then((flds) => {
|
|
4902
|
+
switch (flds?.vaultKey?.type) {
|
|
4903
|
+
case 'secondary':
|
|
4904
|
+
return event('vault' + ac, {vault: this.vault.vault(flds.vaultKey.externals?.[0]), share: sh});
|
|
4905
|
+
default:
|
|
4906
|
+
if (attn) return vitem.getVaultKey().then((vkey) => this.vault.vaultKey.VES.attnVaultKeys([vkey])).catch((e) => console.log(e)).then(() => null);
|
|
4907
|
+
}
|
|
4908
|
+
});
|
|
4909
|
+
}
|
|
4910
|
+
}));
|
|
4911
|
+
const sharefn = () => (this.share ? e.getVaultKey().then((vkey) => vkey.getFields({id: true, type: true, externals: {domain: true, externalId: true}, user: {id: true, email: true}}).then((flds) => {
|
|
4912
|
+
let f = flds?.externals?.[0] ? flds.externals[0] : (flds?.user?.email ? {email: flds.user.email} : {});
|
|
4913
|
+
f.version = flds.id;
|
|
4914
|
+
let vault = this.vault.vault(f);
|
|
4915
|
+
vault.current = (f.externals && flds.type == 'secondary') || (f.email && flds.type == 'current');
|
|
4916
|
+
return vault;
|
|
4917
|
+
})) : Promise.resolve(this.vault));
|
|
4918
|
+
switch (type) {
|
|
4919
|
+
case 'vaultEntry.deleted':
|
|
4920
|
+
return sharefn().then((sh) => itemfn('remove', false, sh));
|
|
4921
|
+
case 'vaultEntry.created':
|
|
4922
|
+
return sharefn().then((sh) => itemfn('add', attn !== false, sh));
|
|
4923
|
+
case 'vaultItem.deleted':
|
|
4924
|
+
return itemfn('delete');
|
|
4925
|
+
case 'vaultItem.created':
|
|
4926
|
+
return itemfn('create');
|
|
4927
|
+
case 'vaultItem.listening':
|
|
4928
|
+
return itemfn('change');
|
|
4929
|
+
case 'session.created':
|
|
4930
|
+
return e.getFields({session: {id: true, remote: true, userAgent: true, vaultKey: {id: true}, user: {id: true}}}).then((flds) => {
|
|
4931
|
+
if (!flds?.session) return null;
|
|
4932
|
+
let ses = new libVES.Author(flds.session, this.vault.vaultKey.VES);
|
|
4933
|
+
ses.vault = this.vault;
|
|
4934
|
+
return event('sessioncreate', {vault: this.vault, session: ses});
|
|
4935
|
+
});
|
|
4936
|
+
}
|
|
4937
|
+
}).catch((er) => (console.log(er), event('error', {error: er})));
|
|
4938
|
+
}
|
|
4939
|
+
};
|
|
4940
|
+
|
|
4941
|
+
|
|
4942
|
+
|
|
4943
|
+
libVES.Item = class extends libVES.EventTarget {
|
|
4944
|
+
constructor(ref, vault, fields) {
|
|
4945
|
+
const ves = vault.vaultKey.VES;
|
|
4946
|
+
super();
|
|
4947
|
+
if (ref == null || typeof(ref) == 'function') {
|
|
4948
|
+
this.provisional = {share: [vault], ref: (ref || libVES.Item.newRef)};
|
|
4949
|
+
} else {
|
|
4950
|
+
ref = libVES.Vault.toRef(ref, ves);
|
|
4951
|
+
if ((!ref.domain || !ref.externalId) && !ref.version) throw new libVES.Error.InvalidValue('Bad item reference');
|
|
4952
|
+
this.domain = ref.domain;
|
|
4953
|
+
this.externalId = ref.externalId;
|
|
4954
|
+
this.version = ref.version || null;
|
|
4955
|
+
}
|
|
4956
|
+
this.vault = vault;
|
|
4957
|
+
this._vaultItem(ves, fields);
|
|
4958
|
+
};
|
|
4959
|
+
|
|
4960
|
+
static newRef() {
|
|
4961
|
+
let d = new Date();
|
|
4962
|
+
return ((d.getFullYear() * 100 + d.getMonth() + 1) * 100 + d.getDate()).toFixed(0) + '.' + (((d.getHours() + 100) * 100 + d.getMinutes()) * 100 + d.getSeconds() + d.getMilliseconds() / 1000).toFixed(3).slice(-10);
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
_vaultItem(ves, fields) {
|
|
4966
|
+
if (this.vaultItem && this._entriesCache !== null) return this.vaultItem;
|
|
4967
|
+
ves ||= this.vaultItem.VES;
|
|
4968
|
+
if (!fields) {
|
|
4969
|
+
fields = this.externalId ? {file: {externals: [{domain: this.domain, externalId: this.externalId}]}} : {};
|
|
4970
|
+
if (this.version) fields.id = this.version;
|
|
4971
|
+
} else if (fields.vaultEntries) this._entriesCache = Promise.resolve(fields).finally(() => this._entriesCache = null);
|
|
4972
|
+
return this.vaultItem = new libVES.VaultItem(fields, ves);
|
|
4973
|
+
};
|
|
4974
|
+
|
|
4975
|
+
_entries() {
|
|
4976
|
+
if (this.provisional) return Promise.reject(new libVES.Error.NotFound('Not available for a provisional item'));
|
|
4977
|
+
if (this._entriesCache) return this._entriesCache;
|
|
4978
|
+
this._entriesCache = this._vaultItem().getFields({vaultEntries: {encData: true, vaultKey: {id: true, type: true, algo: true, privateKey: true, publicKey: true, user: {id: true, email: true}, externals: {domain: true, externalId: true}}}, file: {creator: {id: true}}, vaultKey: {user: {id: true}}, deleted: true}).then((flds) => {
|
|
4979
|
+
if (!flds?.vaultEntries) return Promise.reject(new libVES.Error.InvalidKey('Not authorized to access this item'));
|
|
4980
|
+
return flds;
|
|
4981
|
+
});
|
|
4982
|
+
this._entriesCache.catch((er) => null).finally(() => this._entriesCache = null);
|
|
4983
|
+
return this._entriesCache;
|
|
4984
|
+
};
|
|
4985
|
+
|
|
4986
|
+
_get() {
|
|
4987
|
+
return this._entries().then(() => this.vaultItem.get());
|
|
4988
|
+
};
|
|
4989
|
+
|
|
4990
|
+
_reset() {
|
|
4991
|
+
this._entriesCache = null;
|
|
4992
|
+
};
|
|
4993
|
+
|
|
4994
|
+
latest() {
|
|
4995
|
+
return this.externalId && this.version ? this.vault.item({domain: this.domain, externalId: this.externalId}) : this;
|
|
4996
|
+
};
|
|
4997
|
+
|
|
4998
|
+
get() {
|
|
4999
|
+
return this._get().then((val) => {
|
|
5000
|
+
if (typeof(val) != 'string') throw new libVES.Error.InvalidValue('The item value is not a string');
|
|
5001
|
+
return val;
|
|
5002
|
+
});
|
|
5003
|
+
};
|
|
5004
|
+
|
|
5005
|
+
put(value, shares) {
|
|
5006
|
+
if (value == null) return Promise.reject(new libVES.Error.InvalidValue('The item value cannot be empty'));
|
|
5007
|
+
let prref;
|
|
5008
|
+
return (shares ? Promise.resolve(libVES.Vault.toRefs(shares, this.vaultItem.VES, true)) : this.share().then((shares) => {
|
|
5009
|
+
let owner = shares.reduce((cur, sh) => (cur || ((sh.owner && sh.externalId?.match(/@/)) ? sh : null)), null);
|
|
5010
|
+
if (owner && owner.uri() != this.vault.uri()) shares.push(this.vault.vault({domain: '.admin', externalId: owner.externalId}));
|
|
5011
|
+
return shares;
|
|
5012
|
+
}).catch((e) => {
|
|
5013
|
+
if (e && e.code == 'NotFound') return libVES.Vault.toRefs([], this.vaultItem.VES, true);
|
|
5014
|
+
throw e;
|
|
5015
|
+
})).then((shares) => {
|
|
5016
|
+
if (!this.provisional) return shares;
|
|
5017
|
+
prref = libVES.Vault.toRef(this.provisional.ref(), this.vaultItem.VES);
|
|
5018
|
+
if (!prref?.externalId) return Promise.reject(new libVES.Error.InvalidValue('Bad provisional reference generated by the supplied function'));
|
|
5019
|
+
return this.vaultItem.setFields({file: {externals: [prref]}}).then(() => shares);
|
|
5020
|
+
}).then((shares) => this.vaultItem.shareWith(shares, value)).then(() => {
|
|
5021
|
+
if (prref) {
|
|
5022
|
+
this.externalId = prref.externalId;
|
|
5023
|
+
this.domain = prref.domain;
|
|
5024
|
+
delete(this.provisional);
|
|
5025
|
+
}
|
|
5026
|
+
this.version = null;
|
|
5027
|
+
this._reset();
|
|
5028
|
+
return value;
|
|
5029
|
+
});
|
|
5030
|
+
};
|
|
5031
|
+
|
|
5032
|
+
cipher(optns, shares) {
|
|
5033
|
+
return this._get().then((val) => {
|
|
5034
|
+
if (!(val instanceof libVES.Cipher)) throw new libVES.Error.InvalidValue('The item value is not a cipher');
|
|
5035
|
+
return (shares ? this.share(shares) : Promise.resolve(null)).then(() => val);
|
|
5036
|
+
}).catch((e) => {
|
|
5037
|
+
if (e && e.code == 'NotFound') {
|
|
5038
|
+
let algo = optns?.algo || this.vaultItem.VES.cipherAlgo || 'AES256GCM1K';
|
|
5039
|
+
if (!libVES.Cipher[algo]) throw new libVES.Error.InvalidValue('Unsupported cipher: ' + algo);
|
|
5040
|
+
let ci = new libVES.Cipher[algo]();
|
|
5041
|
+
return this.put(ci, shares);
|
|
5042
|
+
}
|
|
5043
|
+
throw e;
|
|
5044
|
+
}).then((ci) => new libVES.ItemCipher(ci, this));
|
|
5045
|
+
};
|
|
5046
|
+
|
|
5047
|
+
json(val, shares) {
|
|
5048
|
+
if (val === undefined) return this.get().then((val) => JSON.parse(val));
|
|
5049
|
+
return this.put(JSON.stringify(val), shares).then(() => val);
|
|
5050
|
+
};
|
|
5051
|
+
|
|
5052
|
+
exists() {
|
|
5053
|
+
return this._entries().then((flds) => !flds?.deleted).catch((e) => {
|
|
5054
|
+
if (e?.code == 'NotFound') return false;
|
|
5055
|
+
if (e?.code == 'InvalidKey') return true;
|
|
5056
|
+
throw e;
|
|
5057
|
+
});
|
|
5058
|
+
};
|
|
5059
|
+
|
|
5060
|
+
_provShare(shares, del) {
|
|
5061
|
+
return new Promise((resolve, reject) => {
|
|
5062
|
+
let uris = libVES.Vault.toRefs(shares, this.vaultItem.VES).map((ref) => this.vault.vault(ref).uri());
|
|
5063
|
+
let prov = {};
|
|
5064
|
+
this.provisional.share.map((sh) => prov[sh.uri()] = sh);
|
|
5065
|
+
resolve(this.vaultItem.VES.usersToKeys(libVES.Vault.toRefs(uris.filter((uri) => !prov[uri]))));
|
|
5066
|
+
}).then((vkeys) => Promise.all(vkeys.map((vkey => vkey.getFields({user: {email: true}, externals: {domain: true, externalId: true}, type: true}).then((flds) => {
|
|
5067
|
+
let sh = new libVES.Vault((flds?.externals?.[0] ?? flds?.user?.email), this.vaultItem.VES, flds);
|
|
5068
|
+
if (sh) return sh.verify();
|
|
5069
|
+
}))))).then((shares) => shares.filter((sh) => sh)).then((shares) => {
|
|
5070
|
+
this.provisional.share = this.provisional.share.concat(shares);
|
|
5071
|
+
if (del) {
|
|
5072
|
+
let uris = {};
|
|
5073
|
+
shares.map((sh) => uris[sh.uri()] = sh);
|
|
5074
|
+
uris[this.vault.uri()] = this.vault;
|
|
5075
|
+
this.provisional.share = this.provisional.share.filter((sh) => uris[sh.uri()]);
|
|
5076
|
+
}
|
|
5077
|
+
return this.provisional.share;
|
|
5078
|
+
});
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
share(shares) {
|
|
5082
|
+
if (this.provisional) {
|
|
5083
|
+
if (shares) return this._provShare(shares, true);
|
|
5084
|
+
return Promise.resolve(this.provisional.share);
|
|
5085
|
+
}
|
|
5086
|
+
return (shares ? (this._reset(), this.vaultItem.shareWith(libVES.Vault.toRefs(shares, this.vaultItem.VES, true))) : Promise.resolve(null)).then(() => {
|
|
5087
|
+
let result = [];
|
|
5088
|
+
let found = {};
|
|
5089
|
+
let admins = {};
|
|
5090
|
+
return this._entries().then((flds) => flds.vaultEntries.map((entry) => {
|
|
5091
|
+
let vkey = entry?.vaultKey;
|
|
5092
|
+
let ref = vkey?.externals?.[0];
|
|
5093
|
+
if (!ref) {
|
|
5094
|
+
const email = vkey?.user?.email;
|
|
5095
|
+
ref = email ? {email: email} : {version: vkey?.id};
|
|
5096
|
+
}
|
|
5097
|
+
let vault = new libVES.Vault(ref, this.vaultItem.VES);
|
|
5098
|
+
let uri = vault.uri();
|
|
5099
|
+
if (!found[uri]) {
|
|
5100
|
+
result.push(found[uri] = vault);
|
|
5101
|
+
let uid = vkey?.user?.id;
|
|
5102
|
+
vault.owner = !!uid && uid == (flds.file?.creator?.id ?? flds.vaultKey?.user?.id);
|
|
5103
|
+
if (vault.domain == '.admin') admins[vault.externalId] = true;
|
|
5104
|
+
}
|
|
5105
|
+
switch (vkey?.type) {
|
|
5106
|
+
case 'current': case 'secondary':
|
|
5107
|
+
found[uri].current = true;
|
|
5108
|
+
break;
|
|
5109
|
+
default:
|
|
5110
|
+
vault.current = false;
|
|
5111
|
+
}
|
|
5112
|
+
})).then(() => {
|
|
5113
|
+
result.map((sh) => sh.admin = !!admins[sh.externalId]);
|
|
5114
|
+
return result;
|
|
5115
|
+
});
|
|
5116
|
+
});
|
|
5117
|
+
};
|
|
5118
|
+
|
|
5119
|
+
add(shares) {
|
|
5120
|
+
if (this.provisional) return this._provShare(shares, false).then(() => true);
|
|
5121
|
+
return this._entries().then(() => this.vaultItem.getShareVaultKeys()).then((vkeys) => {
|
|
5122
|
+
if (!vkeys) throw new libVES.Error.InvalidKey('Not authorized to share this item');
|
|
5123
|
+
if (!shares) return true;
|
|
5124
|
+
libVES.Vault.toRefs(shares, this.vaultItem.VES).map((share) => vkeys.push(share));
|
|
5125
|
+
this._reset();
|
|
5126
|
+
return this.vaultItem.shareWith(vkeys);
|
|
5127
|
+
}).then(() => true);
|
|
5128
|
+
};
|
|
5129
|
+
|
|
5130
|
+
remove(shares) {
|
|
5131
|
+
shares = libVES.Vault.toRefs(shares, this.vaultItem.VES);
|
|
5132
|
+
if (!shares || !shares.length) return Promise.resolve(true);
|
|
5133
|
+
if (this.provisional) return new Promise((resolve, reject) => {
|
|
5134
|
+
let uris = {};
|
|
5135
|
+
shares.map((sh) => uris[this.vault.vault(sh).uri()] = true);
|
|
5136
|
+
this.provisional.share = this.provisional.share.filter((sh) => !uris[sh.uri()]);
|
|
5137
|
+
resolve(true);
|
|
5138
|
+
});
|
|
5139
|
+
return this._entries().then(() => this.vaultItem.unshareWith(shares)).then(() => (this._reset(), true));
|
|
5140
|
+
};
|
|
5141
|
+
|
|
5142
|
+
refuse() {
|
|
5143
|
+
return this.remove(this.vault);
|
|
5144
|
+
};
|
|
5145
|
+
|
|
5146
|
+
delete() {
|
|
5147
|
+
this._reset();
|
|
5148
|
+
return this.vaultItem.delete();
|
|
5149
|
+
}
|
|
5150
|
+
|
|
5151
|
+
badauth() {
|
|
5152
|
+
return (this.vaultItem.VES == this.vault.vaultKey.VES ? this.vault.badauth() : Promise.resolve(null)).then(() => this.vaultItem.VES = this.vault.vaultKey.VES);
|
|
5153
|
+
}
|
|
5154
|
+
|
|
5155
|
+
shareFor(share) {
|
|
5156
|
+
let uri = this.vault.vault(share).uri();
|
|
5157
|
+
return uri ? this.share().then((shares) => shares.reduce((res, sh) => (res ?? (sh.uri() == uri ? sh : null)), null)) : null;
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
uri() {
|
|
5161
|
+
if (!this.externalId && !this.version) return null;
|
|
5162
|
+
return libVES.Vault.toUri(this);
|
|
5163
|
+
}
|
|
5164
|
+
|
|
5165
|
+
short() {
|
|
5166
|
+
if (!this.externalId) return null;
|
|
5167
|
+
return libVES.Vault.toUri(this, this.vaultItem.VES.domain);
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
_eventTracker() {
|
|
5171
|
+
return new libVES.EventTracker(this.vault, true);
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5174
|
+
createWatch() {
|
|
5175
|
+
let w = new libVES.Watch(this.vaultItem);
|
|
5176
|
+
w.fields = {vaultKey: {id: true, type: true, algo: true, publicKey: true, externals: {domain: true, externalId: true}, user: {id: true, email: true}}, vaultItem: {id: true, type: true, meta: true, deleted: true, vaultKey: {id: true, type: true, algo: true, publicKey: true, externals: {domain: true, externalId: true}, user: {id: true, email: true}}, file: {externals: {domain: true, externalId: true}, creator: true}}, authorSession: {vaultKey: {id: true, type: true, externals: {domain: true, externalId: true}}, domain: true, user: {email: true}, remote: true, userAgent: true}};
|
|
5177
|
+
return w;
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5180
|
+
replay() {
|
|
5181
|
+
return this.share().then((vaults) => Promise.all(vaults.map((vault) => {
|
|
5182
|
+
if (this.externalId) return new libVES.CustomEvent('itemadd', {detail: {replay: true, item: this, share: vault}});
|
|
5183
|
+
else return new libVES.CustomEvent('vaultadd', {detail: {replay: true, vault: this.vault, share: vault}});
|
|
5184
|
+
})));
|
|
5185
|
+
}
|
|
5186
|
+
|
|
5187
|
+
readable() {
|
|
5188
|
+
return this._get().then(() => true).catch((er) => {
|
|
5189
|
+
switch (er?.code) {
|
|
5190
|
+
case 'NotFound':
|
|
5191
|
+
case 'InvalidKey': return false;
|
|
5192
|
+
default: throw er;
|
|
5193
|
+
}
|
|
5194
|
+
});
|
|
5195
|
+
}
|
|
5196
|
+
|
|
5197
|
+
writable() {
|
|
5198
|
+
return this.shareFor(this.vault).then((sh) => sh && (sh.owner || sh.admin)).catch((er) => {
|
|
5199
|
+
switch (er?.code) {
|
|
5200
|
+
case 'NotFound': return true;
|
|
5201
|
+
case 'InvalidKey': return false;
|
|
5202
|
+
default: throw er;
|
|
5203
|
+
}
|
|
5204
|
+
});
|
|
5205
|
+
}
|
|
5206
|
+
|
|
5207
|
+
toString() {
|
|
5208
|
+
return 'libVES.Item(' + (this.uri() ?? '[provisional]') + ')';
|
|
5209
|
+
}
|
|
5210
|
+
|
|
5211
|
+
};
|
|
5212
|
+
|
|
5213
|
+
libVES.ItemCipher = class {
|
|
5214
|
+
constructor(ci, item) {
|
|
5215
|
+
this.cipher = ci;
|
|
5216
|
+
this.item = item;
|
|
5217
|
+
}
|
|
5218
|
+
|
|
5219
|
+
meta(val) {
|
|
5220
|
+
if (val == null) return Promise.resolve(this.cipher.meta);
|
|
5221
|
+
if (!(val instanceof Object)) return Promise.reject(new libVES.Error.InvalidValue('Cipher metadata must be an object'));
|
|
5222
|
+
this.cipher.meta = val;
|
|
5223
|
+
return this.item.put(this.cipher).then(() => this.cipher.meta);
|
|
5224
|
+
}
|
|
5225
|
+
|
|
5226
|
+
reset(final) {
|
|
5227
|
+
if (final && !this.final) return Promise.resolve(null);
|
|
5228
|
+
this.final = false;
|
|
5229
|
+
return this.item.vaultItem.get().then((ci) => this.cipher = ci);
|
|
5230
|
+
}
|
|
5231
|
+
|
|
5232
|
+
encrypt(data, optns) {
|
|
5233
|
+
return this.reset(true).then(() => {
|
|
5234
|
+
this.final = optns?.final !== false;
|
|
5235
|
+
if (data instanceof Blob) {
|
|
5236
|
+
return data.arrayBuffer().then((buf) => this.cipher.encrypt(buf, this.final)).then((ctext) => {
|
|
5237
|
+
const op = {type: 'application/vnd.ves.encrypted'};
|
|
5238
|
+
if (optns) optns.type = data.type;
|
|
5239
|
+
if (data instanceof File) {
|
|
5240
|
+
if (optns) optns.name = data.name;
|
|
5241
|
+
return new File([ctext], this.item.externalId, op);
|
|
5242
|
+
}
|
|
5243
|
+
return new Blob([ctext], op);
|
|
5244
|
+
});
|
|
5245
|
+
} else if (data instanceof ArrayBuffer) return this.cipher.encrypt(data, this.final).then((ctext) => ctext.buffer);
|
|
5246
|
+
else if ((data instanceof Object) && (data.buffer instanceof ArrayBuffer)) return this.cipher.encrypt(data.buffer, this.final).then((ctext) => new data.constructor(ctext.buffer));
|
|
5247
|
+
else return this.cipher.encrypt(libVES.Util.StringToByteArray(String(data)), this.final).then((ctext) => libVES.Util.ByteArrayToB64(ctext));
|
|
5248
|
+
});
|
|
5249
|
+
}
|
|
5250
|
+
|
|
5251
|
+
decrypt(data, optns) {
|
|
5252
|
+
return this.reset(true).then(() => {
|
|
5253
|
+
this.final = optns?.final !== false;
|
|
5254
|
+
if (data instanceof Blob) {
|
|
5255
|
+
return data.arrayBuffer().then((buf) => this.cipher.decrypt(buf, this.final)).then((ctext) => {
|
|
5256
|
+
if (data instanceof File) return new File([ctext], (optns?.name || ''), optns);
|
|
5257
|
+
return new Blob([ctext], optns);
|
|
5258
|
+
});
|
|
5259
|
+
} else if (data instanceof ArrayBuffer) return this.cipher.decrypt(data, this.final).then((ctext) => ctext.buffer);
|
|
5260
|
+
else if ((data instanceof Object) && (data.buffer instanceof ArrayBuffer)) return this.cipher.decrypt(data.buffer, this.final).then((ctext) => new data.constructor(ctext.buffer));
|
|
5261
|
+
else return this.cipher.decrypt(libVES.Util.B64ToByteArray(String(data)), this.final).then((ctext) => libVES.Util.ByteArrayToString(ctext));
|
|
5262
|
+
});
|
|
5263
|
+
}
|
|
5264
|
+
|
|
5265
|
+
toString() {
|
|
5266
|
+
return 'libVES.ItemCipher(' + String(this.item) + ')';
|
|
5267
|
+
}
|
|
5268
|
+
|
|
5269
|
+
toJSON() {
|
|
5270
|
+
return this.item?.uri();
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
libVES.version = '3.00';
|
|
4397
5274
|
|
|
4398
5275
|
module.exports = libVES;
|