mirror of
https://github.com/amark/gun.git
synced 2025-07-03 11:22:32 +00:00
Serious refactoring & structuring sea.js to handle 'remember-me'. Pegs more testing and test cases
This commit is contained in:
parent
bff2fdece9
commit
2f69bddc2a
@ -51,6 +51,7 @@
|
|||||||
"buffer": "^5.0.7",
|
"buffer": "^5.0.7",
|
||||||
"eccrypto": "^1.0.3",
|
"eccrypto": "^1.0.3",
|
||||||
"formidable": ">=1.1.1",
|
"formidable": ">=1.1.1",
|
||||||
|
"node-localstorage": "^1.3.0",
|
||||||
"subtle": "^0.1.8",
|
"subtle": "^0.1.8",
|
||||||
"text-encoding": "^0.6.4",
|
"text-encoding": "^0.6.4",
|
||||||
"ws": "~>2.2.3"
|
"ws": "~>2.2.3"
|
||||||
|
448
sea.js
448
sea.js
@ -12,19 +12,28 @@
|
|||||||
|
|
||||||
var Gun = (typeof window !== 'undefined' ? window : global).Gun || require('./gun');
|
var Gun = (typeof window !== 'undefined' ? window : global).Gun || require('./gun');
|
||||||
|
|
||||||
// Following enable Web Cryptography API use in NodeJS
|
var crypto, TextEncoder, TextDecoder, localStorage, sessionStorage;
|
||||||
var crypto = (typeof window !== 'undefined' && window.crypto)
|
|
||||||
|| { subtle: require('subtle') };
|
|
||||||
|
|
||||||
var TextEncoder = (typeof window !== 'undefined' && window.TextEncoder)
|
if (typeof window !== 'undefined') {
|
||||||
|| require('text-encoding').TextEncoder;
|
crypto = window.crypto;
|
||||||
var TextDecoder = (typeof window !== 'undefined' && window.TextDecoder)
|
TextEncoder = window.TextEncoder;
|
||||||
|| require('text-encoding').TextDecoder;
|
TextDecoder = window.TextDecoder;
|
||||||
|
localStorage = window.localStorage;
|
||||||
|
sessionStorage = window.sessionStorage;
|
||||||
|
} else {
|
||||||
|
crypto = { subtle: require('subtle') }; // Web Cryptography API for NodeJS
|
||||||
|
TextEncoder = require('text-encoding').TextEncoder;
|
||||||
|
TextDecoder = require('text-encoding').TextDecoder;
|
||||||
|
// Let's have Storage for NodeJS / testing
|
||||||
|
localStorage = new require('node-localstorage').LocalStorage('local');
|
||||||
|
sessionStorage = new require('node-localstorage').LocalStorage('session');
|
||||||
|
}
|
||||||
|
|
||||||
if(typeof Buffer === 'undefined'){
|
if(typeof Buffer === 'undefined'){
|
||||||
var Buffer = require('buffer').Buffer;
|
var Buffer = require('buffer').Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encryption parameters - TODO: maybe to be changed via init?
|
||||||
var pbkdf2 = {
|
var pbkdf2 = {
|
||||||
hash: 'SHA-256', // Was 'SHA-1'
|
hash: 'SHA-256', // Was 'SHA-1'
|
||||||
iter: 50000,
|
iter: 50000,
|
||||||
@ -37,42 +46,36 @@
|
|||||||
enc: 'aes-256-cbc'
|
enc: 'aes-256-cbc'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// These are used to persist user's authentication "session"
|
||||||
|
var authsettings = {
|
||||||
|
validity: 60 * 60 * 12, // 12 hours
|
||||||
|
session: true,
|
||||||
|
// or return new Promise(function(resolve, reject){(resolve(props))})
|
||||||
|
hook: function(props) {return props} // { iat, exp, alias, proof }
|
||||||
|
};
|
||||||
|
|
||||||
// let's extend the gun chain with a `user` function.
|
// let's extend the gun chain with a `user` function.
|
||||||
// only one user can be logged in at a time, per gun instance.
|
// only one user can be logged in at a time, per gun instance.
|
||||||
Gun.chain.user = function(){
|
Gun.chain.user = function(){
|
||||||
var root = this.back(-1); // always reference the root gun instance.
|
var root = this.back(-1); // always reference the root gun instance.
|
||||||
var user = root._.user || (root._.user = root.chain()); // create a user context.
|
var user = root._.user || (root._.user = root.chain()); // create a user context.
|
||||||
user.create = User.create; // attach a factory method to it.
|
// then methods...
|
||||||
user.auth = User.auth; // and a login method.
|
[ 'create', // factory
|
||||||
user.leave = User.leave; // and a logout method.
|
'auth', // login
|
||||||
user.delete = User.delete; // and, account delete method.
|
'leave', // logout
|
||||||
|
'delete', // account delete
|
||||||
|
'recall', // existing auth boostrap
|
||||||
|
'alive' // keep/check auth validity
|
||||||
|
].forEach(function(method){
|
||||||
|
user[method] = User[method];
|
||||||
|
});
|
||||||
return user; // return the user!
|
return user; // return the user!
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXAMPLE! Use it this way:
|
// Practical examples about usage found from ./test/common.js
|
||||||
;(function(){return;
|
|
||||||
localStorage.clear();
|
|
||||||
|
|
||||||
var gun = Gun();
|
// This is internal func queries public key(s) for alias.
|
||||||
var user = gun.user();
|
function querygunaliases(alias,root){
|
||||||
|
|
||||||
Gun.on('auth', function(at){
|
|
||||||
// do something once logged in.
|
|
||||||
});
|
|
||||||
Gun.on('secure', function(at){
|
|
||||||
// enforce some rules about shared app level data
|
|
||||||
var no;
|
|
||||||
if(no){ return }
|
|
||||||
this.to.next(at);
|
|
||||||
});
|
|
||||||
|
|
||||||
user.create("test", "password"); // create a user from a username alias and a password phrase.
|
|
||||||
user.auth("test", "password"); // authenticate and log in the user!
|
|
||||||
|
|
||||||
}());
|
|
||||||
|
|
||||||
// This internal auth func - more used in future...
|
|
||||||
function authenticate(alias,pass,root){
|
|
||||||
return new Promise(function(resolve, reject){
|
return new Promise(function(resolve, reject){
|
||||||
// load all public keys associated with the username alias we want to log in with.
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
root.get('alias/'+alias).get(function(rat, rev){
|
root.get('alias/'+alias).get(function(rat, rev){
|
||||||
@ -85,49 +88,234 @@
|
|||||||
}
|
}
|
||||||
// then figuring out all possible candidates having matching username
|
// then figuring out all possible candidates having matching username
|
||||||
var aliases = [];
|
var aliases = [];
|
||||||
Gun.obj.map(rat.put, function(at, key){
|
Gun.obj.map(rat.put, function(at, pub){
|
||||||
// grab the account associated with this public key.
|
// grab the account associated with this public key.
|
||||||
root.get(key).get(function(at, ev){
|
root.get(pub).get(function(at, ev){
|
||||||
if(!key.slice || 'pub/' !== key.slice(0,4)){return}
|
if(!pub.slice || 'pub/' !== pub.slice(0,4)){return}
|
||||||
key = key.slice(4);
|
pub = pub.slice(4);
|
||||||
ev.off();
|
ev.off();
|
||||||
if(!at.put){return}
|
if(!at.put){return}
|
||||||
aliases.push({key: key, at: at});
|
aliases.push({pub: pub, at: at});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (!aliases.length){return reject({err: 'Public key does not exist!'})}
|
return aliases.length && resolve(aliases)
|
||||||
|
|| reject({err: 'Public key does not exist!'})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is internal User authentication func.
|
||||||
|
function authenticate(alias,pass,root){
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
// load all public keys associated with the username alias we want to log in with.
|
||||||
|
querygunaliases(alias, root).then(function(aliases){
|
||||||
// then attempt to log into each one until we find ours!
|
// 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)
|
// (if two users have the same username AND the same password... that would be bad)
|
||||||
aliases.forEach(function(one, index){
|
aliases.forEach(function(one, index){
|
||||||
var at = one.at, key = one.key;
|
var at = one.at, pub = one.pub;
|
||||||
var remaining = (aliases.length - index) > 1;
|
var remaining = (aliases.length - index) > 1;
|
||||||
if(!at.put){
|
if(!at.put){
|
||||||
return (!remaining) && reject({err: 'Public key does not exist!'})
|
return (!remaining) && reject({err: 'Public key does not exist!'})
|
||||||
}
|
}
|
||||||
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
// attempt to PBKDF2 extend the password with the salt. (Verifying the signature gives us the plain text salt.)
|
||||||
SEA.read(at.put.salt, key).then(function(salt){
|
SEA.read(at.put.salt, pub).then(function(salt){
|
||||||
return SEA.proof(pass, salt);
|
return SEA.proof(pass, salt)
|
||||||
}).then(function(proof){
|
.catch(function(e){reject({err: 'Failed to create proof!'})});
|
||||||
|
}).catch(function(e){reject({err: 'Failed to create proof!'})})
|
||||||
|
.then(function(proof){
|
||||||
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
return SEA.read(at.put.auth, key).then(function(auth){
|
return SEA.read(at.put.auth, pub).then(function(auth){
|
||||||
return SEA.de(auth, proof);
|
return SEA.de(auth, proof)
|
||||||
}).catch(function(e){
|
.catch(function(e){reject({err: 'Failed to decrypt secret!'})});
|
||||||
}).then(function(priv){
|
}).then(function(priv){
|
||||||
// now we have AES decrypted the private key, from when we encrypted it with the proof at registration.
|
// 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!
|
// if we were successful, then that meanswe're logged in!
|
||||||
return remaining ? undefined // Not done yet
|
return remaining ? undefined // Not done yet
|
||||||
: priv ? resolve({pub: key, priv: priv, at: at})
|
: priv ? resolve({pub: pub, priv: priv, at: at, proof: proof})
|
||||||
// Or else we failed to log in...
|
// Or else we failed to log in...
|
||||||
: reject({err: 'Failed to decrypt private key!'});
|
: reject({err: 'Failed to decrypt private key!'});
|
||||||
});
|
}).catch(function(e){reject({err: 'Failed read secret!'})});
|
||||||
}).catch(function(){reject({err: 'Failed to create proof!'})});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}).catch(function(e){reject({err: e})});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This internal func finalizes User authentication
|
||||||
|
function finalizelogin(alias,key,root,opts){
|
||||||
|
var user = root._.user;
|
||||||
|
// add our credentials in-memory only to our root gun instance
|
||||||
|
user._ = key.at.gun._;
|
||||||
|
// so that way we can use the credentials to encrypt/decrypt data
|
||||||
|
user._.is = user.is = {};
|
||||||
|
// that is input/output through gun (see below)
|
||||||
|
user._.alias = alias;
|
||||||
|
user._.sea = key.priv;
|
||||||
|
user._.pub = key.pub;
|
||||||
|
//console.log("authorized", user._);
|
||||||
|
// persist authentication
|
||||||
|
return authpersist(user._, key.proof, opts)
|
||||||
|
.then(function(){
|
||||||
|
// emit an auth event, useful for page redirects and stuff.
|
||||||
|
Gun.on('auth', user._);
|
||||||
|
// returns success with the user data credentials.
|
||||||
|
return user._;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This internal func persists User authentication if so configured
|
||||||
|
function authpersist(user,proof,opts){
|
||||||
|
// opts = { pin: 'string' }
|
||||||
|
// authsettings.session = true // disables PIN method
|
||||||
|
// TODO: how this works:
|
||||||
|
// called when app bootstraps, with wanted options
|
||||||
|
// IF authsettings.validity === 0 THEN no remember-me, ever
|
||||||
|
// IF authsettings.session === true THEN no window.localStorage in use; nor PIN
|
||||||
|
// ELSE if no PIN then window.sessionStorage
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
var pin = Gun.obj.has(opts, 'pin') && opts.pin;
|
||||||
|
var doIt = function(props){
|
||||||
|
if (props.alias) {
|
||||||
|
if (props.proof && props.iat) {
|
||||||
|
pin = pin && new Buffer(pin, 'utf8').toString('base64');
|
||||||
|
var remember = (pin && {alias: props.alias, pin: pin }) || props;
|
||||||
|
var protected = !authsettings.session && pin && props;
|
||||||
|
|
||||||
|
return SEA.write(JSON.stringify(remember), user.sea).then(function(signed){
|
||||||
|
sessionStorage.setItem('user', props.alias);
|
||||||
|
sessionStorage.setItem('remember', signed);
|
||||||
|
if (!protected) {
|
||||||
|
localStorage.removeItem('remember');
|
||||||
|
}
|
||||||
|
return !protected || SEA.en(protected, pin).then(function(encrypted){
|
||||||
|
return encrypted && SEA.write(encrypted, user.sea)
|
||||||
|
.then(function(encsig){
|
||||||
|
localStorage.setItem('remember', encsig);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).then(function(){
|
||||||
|
resolve({ok: 0});
|
||||||
|
}).catch(function(){reject({err: 'Session persisting failed!'});});
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('remember');
|
||||||
|
sessionStorage.removeItem('user');
|
||||||
|
sessionStorage.removeItem('remember');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve({ok: 0});
|
||||||
|
};
|
||||||
|
var args = { alias: user.alias };
|
||||||
|
|
||||||
|
if(proof && authsettings.validity){
|
||||||
|
args.proof = proof;
|
||||||
|
args.iat = Math.ceil(Date.now() / 1000); // seconds
|
||||||
|
args.exp = authsettings.validity * 60; // seconds
|
||||||
|
var props = authsettings.hook(args);
|
||||||
|
if(props instanceof Promise){props.then(doIt);
|
||||||
|
} else {doIt(props)}
|
||||||
|
} else {
|
||||||
|
doIt(args);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This internal func recalls persisted User authentication if so configured
|
||||||
|
function authrecall(root){
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
var remember = sessionStorage.getItem('remember');
|
||||||
|
var alias = sessionStorage.getItem('user');
|
||||||
|
var err = 'Not authenticated';
|
||||||
|
|
||||||
|
// Already authenticated?
|
||||||
|
if(Gun.obj.has(root._.user._, 'pub')){
|
||||||
|
return resolve(root._.user._.pub);
|
||||||
|
}
|
||||||
|
// No, got alias?
|
||||||
|
if (alias && remember){
|
||||||
|
return querygunaliases(alias, root).then(function(aliases){
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
// 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)
|
||||||
|
aliases.forEach(function(one, index){
|
||||||
|
var at = one.at, pub = one.pub;
|
||||||
|
var remaining = (aliases.length - index) > 1;
|
||||||
|
if(!at.put){
|
||||||
|
return (!remaining) && reject({err: 'Public key does not exist!'})
|
||||||
|
}
|
||||||
|
// got pub, time to unwrap Storage data...
|
||||||
|
return SEA.read(remember, pub).then(function(props){
|
||||||
|
props = !props.slice ? props : JSON.parse(props);
|
||||||
|
// Got PIN ?
|
||||||
|
if(Gun.obj.has(props, 'pin')){
|
||||||
|
// Yes! We can get localStorage secret if signature is ok
|
||||||
|
return SEA.read(localStorage.getItem('remember'), pub)
|
||||||
|
.then(function(encrypted){
|
||||||
|
// And decrypt it
|
||||||
|
return SEA.de(encrypted, props.pin);
|
||||||
|
}).then(function(decr){
|
||||||
|
decr = !decr.slice ? decr : JSON.parse(decr);
|
||||||
|
// And return proof if for matching alias
|
||||||
|
return Gun.obj.has(decr, 'proof')
|
||||||
|
&& Gun.obj.has(decr, 'alias') && decr.alias === alias
|
||||||
|
&& decr.proof;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// No PIN, let's try short-term proof if for matching alias
|
||||||
|
return Gun.obj.has(props, 'proof')
|
||||||
|
&& Gun.obj.has(props, 'alias') && props.alias === alias
|
||||||
|
&& props.proof;
|
||||||
|
}).then(function(proof){
|
||||||
|
if (!proof){return reject({err: 'No secret found!'})}
|
||||||
|
// the proof of work is evidence that we've spent some time/effort trying to log in, this slows brute force.
|
||||||
|
return SEA.read(at.put.auth, pub).then(function(auth){
|
||||||
|
return SEA.de(auth, proof)
|
||||||
|
.catch(function(e){reject({err: 'Failed to decrypt secret!'})});
|
||||||
|
}).then(function(priv){
|
||||||
|
// now we have AES decrypted the private key,
|
||||||
|
// if we were successful, then that means we're logged in!
|
||||||
|
return remaining ? undefined // Not done yet
|
||||||
|
: priv ? resolve({pub: pub, priv: priv, at: at, proof: proof})
|
||||||
|
// Or else we failed to log in...
|
||||||
|
: reject({err: 'Failed to decrypt private key!'});
|
||||||
|
}).catch(function(e){reject({err: 'Failed read secret!'})});
|
||||||
|
}).catch(function(e){
|
||||||
|
reject({err: 'Failed to access stored credentials!'})})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).then(function(user){
|
||||||
|
return finalizelogin(alias, user, root).then(resolve)
|
||||||
|
.catch(function(e){
|
||||||
|
Gun.log('Failed to finalize login with new password!');
|
||||||
|
reject({err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
reject({err: 'No authentication session found!'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This internal func executes logout actions
|
||||||
|
function authleave(root, alias){
|
||||||
|
return function(resolve, reject){
|
||||||
|
// remove persisted authentication
|
||||||
|
authpersist((alias && { alias: alias }) || root._.user._).then(function(){
|
||||||
|
root._.user = root.chain();
|
||||||
|
resolve({ok: 0});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// This internal func returns hashed data for signing
|
||||||
|
function nodehash(m){
|
||||||
|
try{
|
||||||
|
m = m.slice ? m : JSON.stringify(m);
|
||||||
|
var ret = nodeCrypto.createHash(nHash).update(m, 'utf8').digest();
|
||||||
|
return ret;
|
||||||
|
}catch(e){return m}
|
||||||
|
}
|
||||||
|
|
||||||
// How does it work?
|
// How does it work?
|
||||||
function User(){};
|
function User(){}
|
||||||
// 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.create = function(alias, pass, cb){
|
User.create = function(alias, pass, cb){
|
||||||
var root = this.back(-1);
|
var root = this.back(-1);
|
||||||
@ -177,28 +365,15 @@
|
|||||||
};
|
};
|
||||||
// now that we have created a user, we want to authenticate them!
|
// now that we have created a user, we want to authenticate them!
|
||||||
User.auth = function(alias,pass,cb,opt){
|
User.auth = function(alias,pass,cb,opt){
|
||||||
var opts = opt || (typeof cb !== 'function' && cb) || {};
|
var opts = opt || (typeof cb !== 'function' && cb);
|
||||||
var root = this.back(-1);
|
var root = this.back(-1);
|
||||||
cb = typeof cb === 'function' && cb;
|
cb = typeof cb === 'function' && cb;
|
||||||
|
|
||||||
var doIt = function(resolve, reject){
|
var doIt = function(resolve, reject){
|
||||||
authenticate(alias, pass, root).then(function(key){
|
authenticate(alias, pass, root).then(function(key){
|
||||||
// we're logged in!
|
// we're logged in!
|
||||||
function doLogin(){
|
var pin = Gun.obj.has(opts, 'pin') && { pin: opts.pin };
|
||||||
var user = root._.user;
|
if(Gun.obj.has(opts, 'newpass')){
|
||||||
// add our credentials in-memory only to our root gun instance
|
|
||||||
user._ = key.at.gun._;
|
|
||||||
// so that way we can use the credentials to encrypt/decrypt data
|
|
||||||
user._.is = user.is = {};
|
|
||||||
// that is input/output through gun (see below)
|
|
||||||
user._.sea = key.priv;
|
|
||||||
user._.pub = key.pub;
|
|
||||||
//console.log("authorized", user._);
|
|
||||||
// callbacks success with the user data credentials.
|
|
||||||
resolve(user._);
|
|
||||||
// emit an auth event, useful for page redirects and stuff.
|
|
||||||
Gun.on('auth', user._);
|
|
||||||
}
|
|
||||||
if(opts.newpass) {
|
|
||||||
// password update so encrypt private key using new pwd + salt
|
// password update so encrypt private key using new pwd + salt
|
||||||
var newsalt = Gun.text.random(64);
|
var newsalt = Gun.text.random(64);
|
||||||
SEA.proof(opts.newpass, newsalt).then(function(proof){
|
SEA.proof(opts.newpass, newsalt).then(function(proof){
|
||||||
@ -212,13 +387,28 @@
|
|||||||
}).then(function(user){
|
}).then(function(user){
|
||||||
var tmp = 'pub/'+user.pub;
|
var tmp = 'pub/'+user.pub;
|
||||||
// awesome, now we can update the user using public key ID.
|
// awesome, now we can update the user using public key ID.
|
||||||
|
root.get(tmp).put(null);
|
||||||
root.get(tmp).put(user);
|
root.get(tmp).put(user);
|
||||||
// then we're done
|
// then we're done
|
||||||
doLogin();
|
finalizelogin(alias, key, root, pin).then(resolve)
|
||||||
|
.catch(function(e){
|
||||||
|
Gun.log('Failed to finalize login with new password!');
|
||||||
|
reject({err: 'Finalizing new password login failed! Reason: '+(e && e.err) || e || ''});
|
||||||
});
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
Gun.log('Failed encrypt private key using new password!');
|
||||||
|
reject({err: 'Password set attempt failed! Reason: '+(e && e.err) || e || ''});
|
||||||
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
Gun.log('Failed to set new password!');
|
||||||
|
reject({err: 'Password set attempt failed! Reason: '+(e && e.err) || e || ''});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
doLogin();
|
finalizelogin(alias, key, root, pin).then(resolve)
|
||||||
|
.catch(function(e){
|
||||||
|
Gun.log('Failed to finalize login!');
|
||||||
|
reject({err: 'Finalizing login failed! Reason: '+(e && e.err) || e || ''});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).catch(function(e){
|
}).catch(function(e){
|
||||||
Gun.log('Failed to sign in!');
|
Gun.log('Failed to sign in!');
|
||||||
@ -227,28 +417,84 @@
|
|||||||
};
|
};
|
||||||
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
||||||
};
|
};
|
||||||
// now that we authenticated a user, we want to support logout too!
|
|
||||||
User.leave = function(cb){
|
User.leave = function(cb){
|
||||||
var root = this.back(-1);
|
var root = this.back(-1);
|
||||||
var doIt = function(resolve, reject){
|
if(cb){authleave(root)(cb, cb)} else {return new Promise(authleave(root))}
|
||||||
root._.user = root.chain();
|
|
||||||
resolve({ok: 0});
|
|
||||||
}
|
|
||||||
if (cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
|
||||||
};
|
};
|
||||||
// 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.delete = function(alias,pass,cb){
|
User.delete = function(alias,pass,cb){
|
||||||
var root = this.back(-1);
|
var root = this.back(-1);
|
||||||
var doIt = function(resolve, reject){
|
var doIt = function(resolve, reject){
|
||||||
authenticate(alias, pass, root).then(function(key){
|
authenticate(alias, pass, root).then(function(key){
|
||||||
root.get(key.pub).put(null);
|
new Promise(authleave(root, alias)).catch(function(){})
|
||||||
|
.then(function(){
|
||||||
|
root.get('pub/'+key.pub).put(null);
|
||||||
root._.user = root.chain();
|
root._.user = root.chain();
|
||||||
resolve({ok: 0});
|
resolve({ok: 0});
|
||||||
}).catch(function(e){
|
}).catch(function(e){
|
||||||
Gun.log('User.delete failed! Error:', e);
|
Gun.log('User.delete failed! Error:', e);
|
||||||
reject({err: 'Delete attempt failed! Reason:'+(e && e.err) || e || ''});
|
reject({err: 'Delete attempt failed! Reason:'+(e && e.err) || e || ''});
|
||||||
});
|
});
|
||||||
|
}).catch(function(e){
|
||||||
|
Gun.log('User.delete authentication failed! Error:', e);
|
||||||
|
reject({err: 'Delete attempt failed! Reason:'+(e && e.err) || e || ''});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
||||||
|
};
|
||||||
|
// If authentication is to be remembered over reloads or browser closing,
|
||||||
|
// set validity time in seconds.
|
||||||
|
User.recall = function(validity,cb,opts){
|
||||||
|
if(!opts){
|
||||||
|
if(typeof cb !== 'function' && !Gun.val.is(cb)){
|
||||||
|
opts = cb;
|
||||||
|
cb = undefined;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if(!cb){
|
||||||
|
if(typeof validity === 'function'){
|
||||||
|
cb = validity;
|
||||||
|
validity = undefined;
|
||||||
|
} else if(!Gun.val.is(validity)){
|
||||||
|
opts = validity;
|
||||||
|
validity = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var doIt = function(resolve, reject){
|
||||||
|
// opts = { hook: function({ iat, exp, alias, proof }),
|
||||||
|
// session: false } // true disables PIN requirement/support
|
||||||
|
// iat == Date.now() when issued, exp == seconds to expire from iat
|
||||||
|
// TODO: how this works:
|
||||||
|
// called when app bootstraps, with wanted options
|
||||||
|
// IF validity === 0 THEN no remember-me, ever
|
||||||
|
// IF opt.session === true THEN no window.localStorage in use; nor PIN
|
||||||
|
if(Gun.val.is(validity)){
|
||||||
|
authsettings.validity = validity;
|
||||||
|
}
|
||||||
|
if(Gun.obj.has(opts, 'session')){
|
||||||
|
authsettings.session = opts.session;
|
||||||
|
}
|
||||||
|
if(Gun.obj.has(opts, 'hook')){
|
||||||
|
authsettings.hook = opt.hook;
|
||||||
|
}
|
||||||
|
// TODO: per authsettings, dig possibly existing auth data and
|
||||||
|
// call SEA.auth
|
||||||
|
resolve({ok: 0, pub: 'TBD'})
|
||||||
|
};
|
||||||
|
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
||||||
|
};
|
||||||
|
User.alive = function(cb){
|
||||||
|
var root = this.back(-1);
|
||||||
|
var doIt = function(resolve, reject){
|
||||||
|
authrecall(root).then(function(){
|
||||||
|
// All is good. Should we do something more with actual recalled data?
|
||||||
|
resolve(root._.user._)
|
||||||
|
}).catch(function(e){
|
||||||
|
var err = 'No session!';
|
||||||
|
Gun.log(err);
|
||||||
|
reject({ err: err });
|
||||||
|
});
|
||||||
|
};
|
||||||
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
if(cb){doIt(cb, cb)} else {return new Promise(doIt)}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -353,7 +599,6 @@
|
|||||||
});
|
});
|
||||||
} // (if we are lying about our signature, other peer's will reject our update)
|
} // (if we are lying about our signature, other peer's will reject our update)
|
||||||
}
|
}
|
||||||
// TODO: this likely isn't working as expected
|
|
||||||
SEA.read(val, tmp).then(function(data){
|
SEA.read(val, tmp).then(function(data){
|
||||||
if(u === (val = data)){ // make sure the signature matches the account it claims to be on.
|
if(u === (val = data)){ // make sure the signature matches the account it claims to be on.
|
||||||
return no = true; // reject any updates that are signed with a mismatched account.
|
return no = true; // reject any updates that are signed with a mismatched account.
|
||||||
@ -372,7 +617,6 @@
|
|||||||
if(tmp = sea.own[soul]){ // not special case, if we receive an update on an ID associated with a public key, then
|
if(tmp = sea.own[soul]){ // not special case, if we receive an update on an ID associated with a public key, then
|
||||||
Gun.obj.map(node, function(val, key){ // for each over the property/values
|
Gun.obj.map(node, function(val, key){ // for each over the property/values
|
||||||
if('_' === key){ return }
|
if('_' === key){ return }
|
||||||
// TODO: this likely isn't working as expected
|
|
||||||
SEA.read(val, tmp).then(function(data){
|
SEA.read(val, tmp).then(function(data){
|
||||||
if(u === (val = data)){ // and verify they were signed by the associated public key!
|
if(u === (val = data)){ // and verify they were signed by the associated public key!
|
||||||
return no = true; // reject the update if it fails to match.
|
return no = true; // reject the update if it fails to match.
|
||||||
@ -433,7 +677,8 @@
|
|||||||
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
||||||
}) || function(resolve, reject){ // For NodeJS crypto.pkdf2 rocks
|
}) || function(resolve, reject){ // For NodeJS crypto.pkdf2 rocks
|
||||||
nodeCrypto.pbkdf2(pass,new Buffer(salt, 'utf8'),pbkdf2.iter,pbkdf2.ks,nHash,function(err,hash){
|
nodeCrypto.pbkdf2(pass,new Buffer(salt, 'utf8'),pbkdf2.iter,pbkdf2.ks,nHash,function(err,hash){
|
||||||
resolve(!err && hash && hash.toString('base64'));
|
if(err){return reject(e)}
|
||||||
|
resolve(hash && hash.toString('base64'));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
||||||
@ -459,10 +704,7 @@
|
|||||||
};
|
};
|
||||||
SEA.sign = function(m,p,cb){
|
SEA.sign = function(m,p,cb){
|
||||||
var doIt = function(resolve, reject){
|
var doIt = function(resolve, reject){
|
||||||
ecCrypto.sign(
|
ecCrypto.sign(new Buffer(p, 'hex'), nodehash(m)).then(function(sig){
|
||||||
new Buffer(p, 'hex'),
|
|
||||||
nodeCrypto.createHash(nHash).update(JSON.stringify(m), 'utf8').digest()
|
|
||||||
).then(function(sig){
|
|
||||||
resolve(new Buffer(sig, 'binary').toString('hex'));
|
resolve(new Buffer(sig, 'binary').toString('hex'));
|
||||||
}).catch(function(e){Gun.log(e); reject(e)});
|
}).catch(function(e){Gun.log(e); reject(e)});
|
||||||
};
|
};
|
||||||
@ -470,11 +712,9 @@
|
|||||||
};
|
};
|
||||||
SEA.verify = function(m, p, s, cb){
|
SEA.verify = function(m, p, s, cb){
|
||||||
var doIt = function(resolve, reject){
|
var doIt = function(resolve, reject){
|
||||||
ecCrypto.verify(
|
ecCrypto.verify(new Buffer(p, 'hex'), nodehash(m), new Buffer(s, 'hex'))
|
||||||
new Buffer(p, 'hex'),
|
.then(function(){resolve(true)})
|
||||||
nodeCrypto.createHash(nHash).update(JSON.stringify(m), 'utf8').digest(),
|
.catch(function(e){Gun.log(e);reject(e)})
|
||||||
new Buffer(s, 'hex')
|
|
||||||
).then(function(){resolve(true)}).catch(function(e){Gun.log(e);reject(e)})
|
|
||||||
};
|
};
|
||||||
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
||||||
};
|
};
|
||||||
@ -484,12 +724,13 @@
|
|||||||
var iv = nodeCrypto.randomBytes(16);
|
var iv = nodeCrypto.randomBytes(16);
|
||||||
var r = {iv: iv.toString('hex'), s: s.toString('hex')};
|
var r = {iv: iv.toString('hex'), s: s.toString('hex')};
|
||||||
var key = makeKey(p, s);
|
var key = makeKey(p, s);
|
||||||
|
m = (m.slice && m) || JSON.stringify(m);
|
||||||
if(typeof window !== 'undefined'){ // Browser doesn't run createCipheriv
|
if(typeof window !== 'undefined'){ // Browser doesn't run createCipheriv
|
||||||
crypto.subtle.importKey('raw', key, 'AES-CBC', false, ['encrypt'])
|
crypto.subtle.importKey('raw', key, 'AES-CBC', false, ['encrypt'])
|
||||||
.then(function(aesKey){
|
.then(function(aesKey){
|
||||||
crypto.subtle.encrypt({
|
crypto.subtle.encrypt({
|
||||||
name: 'AES-CBC', iv: iv
|
name: 'AES-CBC', iv: iv
|
||||||
}, aesKey, new TextEncoder().encode(JSON.stringify(m))).then(function(ct){
|
}, aesKey, new TextEncoder().encode(m)).then(function(ct){
|
||||||
r.ct = new Buffer(ct, 'binary').toString('base64');
|
r.ct = new Buffer(ct, 'binary').toString('base64');
|
||||||
return JSON.stringify(r);
|
return JSON.stringify(r);
|
||||||
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
||||||
@ -507,7 +748,7 @@
|
|||||||
};
|
};
|
||||||
SEA.de = function(m,p,cb){
|
SEA.de = function(m,p,cb){
|
||||||
var doIt = function(resolve, reject){
|
var doIt = function(resolve, reject){
|
||||||
var d = JSON.parse(m);
|
var d = !m.slice ? m : JSON.parse(m);
|
||||||
var key = makeKey(p, new Buffer(d.s, 'hex'));
|
var key = makeKey(p, new Buffer(d.s, 'hex'));
|
||||||
var iv = new Buffer(d.iv, 'hex');
|
var iv = new Buffer(d.iv, 'hex');
|
||||||
if (typeof window !== 'undefined'){ // Browser doesn't run createDecipheriv
|
if (typeof window !== 'undefined'){ // Browser doesn't run createDecipheriv
|
||||||
@ -517,8 +758,7 @@
|
|||||||
name: 'AES-CBC', iv: iv
|
name: 'AES-CBC', iv: iv
|
||||||
}, aesKey, new Buffer(d.ct, 'base64')).then(function(ct){
|
}, aesKey, new Buffer(d.ct, 'base64')).then(function(ct){
|
||||||
var ctUtf8 = new TextDecoder('utf8').decode(ct);
|
var ctUtf8 = new TextDecoder('utf8').decode(ct);
|
||||||
var ret = JSON.parse(ctUtf8);
|
return !ctUtf8.slice ? ctUtf8 : JSON.parse(ctUtf8);
|
||||||
return ret;
|
|
||||||
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
}).then(resolve).catch(function(e){Gun.log(e); reject(e)});
|
||||||
}).catch(function(e){Gun.log(e); reject(e)});
|
}).catch(function(e){Gun.log(e); reject(e)});
|
||||||
} else { // NodeJS doesn't support crypto.subtle.importKey properly
|
} else { // NodeJS doesn't support crypto.subtle.importKey properly
|
||||||
@ -533,11 +773,18 @@
|
|||||||
};
|
};
|
||||||
SEA.write = function(mm,p,cb){
|
SEA.write = function(mm,p,cb){
|
||||||
var doIt = function(resolve, reject) {
|
var doIt = function(resolve, reject) {
|
||||||
var m = mm;
|
// TODO: something's bugging double 'SEA[]' treatment to mm...
|
||||||
if(!m.slice || 'SEA[' !== m.slice(0,4)){m = mm}
|
var m;
|
||||||
m = m.slice(3);
|
if(!mm.slice){
|
||||||
try{m = JSON.parse(m);
|
m = JSON.stringify(m);
|
||||||
|
} else if('SEA[' !== mm.slice(0,4)){
|
||||||
|
m = mm;
|
||||||
|
} else { // Needs to remove previous signature envelope
|
||||||
|
try{m = JSON.parse(mm.slice(3))[0];
|
||||||
}catch(e){m = mm}
|
}catch(e){m = mm}
|
||||||
|
while(m.slice){m = !m.slice ? m : JSON.parse(m)}
|
||||||
|
m = JSON.stringify(m);
|
||||||
|
}
|
||||||
SEA.sign(m, p).then(function(signature){
|
SEA.sign(m, p).then(function(signature){
|
||||||
resolve('SEA'+JSON.stringify([m,signature]));
|
resolve('SEA'+JSON.stringify([m,signature]));
|
||||||
}).catch(function(e){Gun.log(e); reject(e)});
|
}).catch(function(e){Gun.log(e); reject(e)});
|
||||||
@ -549,10 +796,11 @@
|
|||||||
if(!m){ return resolve(); }
|
if(!m){ return resolve(); }
|
||||||
if(!m.slice || 'SEA[' !== m.slice(0,4)){return resolve(m)}
|
if(!m.slice || 'SEA[' !== m.slice(0,4)){return resolve(m)}
|
||||||
m = m.slice(3);
|
m = m.slice(3);
|
||||||
try{m = JSON.parse(m);
|
try{m = !m.slice ? m : JSON.parse(m);}catch(e){return reject(e);}
|
||||||
}catch(e){ return reject(e); }
|
|
||||||
m = m || '';
|
m = m || '';
|
||||||
SEA.verify(m[0], p, m[1]).then(function(ok){resolve(ok && m[0])});
|
SEA.verify(m[0], p, m[1]).then(function(ok){
|
||||||
|
resolve(ok && m[0])
|
||||||
|
});
|
||||||
};
|
};
|
||||||
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
|
||||||
};
|
};
|
||||||
|
226
test/common.js
226
test/common.js
@ -1,7 +1,16 @@
|
|||||||
var root;
|
var root;
|
||||||
(function(env){
|
(function(env){
|
||||||
root = env.window ? env.window : global;
|
root = env.window ? env.window : global;
|
||||||
env.window && root.localStorage && root.localStorage.clear();
|
|
||||||
|
if(!root.sessionStorage){
|
||||||
|
root.sessionStorage = new require('node-localstorage').LocalStorage('session');
|
||||||
|
}
|
||||||
|
root.sessionStorage.clear();
|
||||||
|
if(!root.localStorage){
|
||||||
|
root.localStorage = new require('node-localstorage').LocalStorage('local');
|
||||||
|
}
|
||||||
|
root.localStorage.clear();
|
||||||
|
|
||||||
try{ require('fs').unlinkSync('data.json') }catch(e){}
|
try{ require('fs').unlinkSync('data.json') }catch(e){}
|
||||||
//root.Gun = root.Gun || require('../gun');
|
//root.Gun = root.Gun || require('../gun');
|
||||||
if(root.Gun){
|
if(root.Gun){
|
||||||
@ -8084,6 +8093,14 @@ describe('Gun', function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('auth', function(){
|
describe('auth', function(){
|
||||||
|
var checkStorage = function(done){
|
||||||
|
return function(){
|
||||||
|
console.log('auth remember:', root.sessionStorage.getItem('remember'))
|
||||||
|
console.log('auth protected:', root.localStorage.getItem('remember'))
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
it('login', function(done){
|
it('login', function(done){
|
||||||
var check = function(ack){
|
var check = function(ack){
|
||||||
try{
|
try{
|
||||||
@ -8181,7 +8198,18 @@ describe('Gun', function(){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('no recall no session storing');
|
it('without PIN auth session stored to sessionStorage', function(done){
|
||||||
|
user.auth(alias+type, pass+' new').then(checkStorage(done)).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with PIN auth session stored to sessionStorage', function(done){
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.auth(alias+type, pass+' new', checkStorage(done), {pin: 'PIN'});
|
||||||
|
} else {
|
||||||
|
user.auth(alias+type, pass+' new', {pin: 'PIN'})
|
||||||
|
.then(checkStorage(done)).catch(done);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('leave', function(){
|
describe('leave', function(){
|
||||||
@ -8312,17 +8340,201 @@ describe('Gun', function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('recall', function(){
|
describe('recall', function(){
|
||||||
it.skip('with zero validity auth skips storing');
|
var doCheck = function(done, hasPin){
|
||||||
it.skip('with validity auth stores session');
|
return function(){
|
||||||
it.skip('valid session');
|
expect(root.sessionStorage.getItem('user')).to.not.be(undefined);
|
||||||
|
expect(root.sessionStorage.getItem('user')).to.not.be('');
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be(undefined);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be('');
|
||||||
|
if(hasPin){
|
||||||
|
expect(root.localStorage.getItem('remember')).to.not.be(undefined);
|
||||||
|
expect(root.localStorage.getItem('remember')).to.not.be('');
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
it('with PIN auth session stored to localStorage', function(done){
|
||||||
|
var doAction = function(){
|
||||||
|
user.auth(alias+type, pass+' new', { pin: 'PIN' })
|
||||||
|
.then(doCheck(done, true)).catch(done);
|
||||||
|
};
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.recall(doAction, { session: false });
|
||||||
|
} else {
|
||||||
|
user.recall({ session: false }).then(doAction).catch(done)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('without PIN auth session stored to sessionStorage', function(done){
|
||||||
|
var doAction = function(){
|
||||||
|
user.auth(alias+type, pass+' new').then(doCheck(done));
|
||||||
|
};
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.recall(doAction, { session: false });
|
||||||
|
} else {
|
||||||
|
user.recall({ session: false }).then(doAction).catch(done)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('no validity no session storing', function(done){
|
||||||
|
var doAction = function(){
|
||||||
|
user.auth(alias+type, pass+' new').then(doCheck(done)).catch(done);
|
||||||
|
};
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.recall(0, doAction);
|
||||||
|
} else {
|
||||||
|
user.recall(0).then(doAction).catch(done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validity but no PIN stored to sessionStorage', function(done){
|
||||||
|
var doAction = function(){
|
||||||
|
user.auth(alias+type, pass+' new').then(doCheck(done)).catch(done);
|
||||||
|
};
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.recall(12 * 60 * 60, doAction, {session: false});
|
||||||
|
} else {
|
||||||
|
user.recall(12 * 60 * 60, {session: false}).then(doAction)
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('valid sessionStorage session', function(done){
|
||||||
|
var check = function(ack){
|
||||||
|
// TODO: check
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
user.auth(alias+type, pass+' new').then(function(usr){
|
||||||
|
var sUser;
|
||||||
|
var sRemember;
|
||||||
|
try{
|
||||||
|
expect(usr).to.not.be(undefined);
|
||||||
|
expect(usr).to.not.be('');
|
||||||
|
expect(usr).to.not.have.key('err');
|
||||||
|
expect(usr).to.have.key('put');
|
||||||
|
expect(root.sessionStorage.getItem('user')).to.be(alias+type);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be(undefined);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be('');
|
||||||
|
|
||||||
|
sUser = root.sessionStorage.getItem('user');
|
||||||
|
sRemember = root.sessionStorage.getItem('remember');
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
user.leave().then(function(ack){
|
||||||
|
try{
|
||||||
|
expect(ack).to.have.key('ok');
|
||||||
|
expect(gun.back(-1)._.user).to.not.have.keys(['sea', 'pub']);
|
||||||
|
expect(root.sessionStorage.getItem('user')).to.not.be(sUser);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be(sRemember);
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
|
||||||
|
root.sessionStorage.setItem('user', sUser);
|
||||||
|
root.sessionStorage.setItem('remember', sRemember);
|
||||||
|
|
||||||
|
user.recall(12 * 60 * 60, {session: false}).then(doCheck(done))
|
||||||
|
.catch(done);
|
||||||
|
}).catch(done);
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('valid localStorage session', function(done){
|
||||||
|
var check = function(ack){
|
||||||
|
// TODO: check
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
user.auth(alias+type, pass+' new', { pin: 'PIN' }).then(function(usr){
|
||||||
|
var sUser;
|
||||||
|
var sRemember;
|
||||||
|
var lRemember;
|
||||||
|
try{
|
||||||
|
expect(usr).to.not.be(undefined);
|
||||||
|
expect(usr).to.not.be('');
|
||||||
|
expect(usr).to.not.have.key('err');
|
||||||
|
expect(usr).to.have.key('put');
|
||||||
|
expect(root.sessionStorage.getItem('user')).to.be(alias+type);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be(undefined);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be('');
|
||||||
|
expect(root.localStorage.getItem('remember')).to.not.be(undefined);
|
||||||
|
expect(root.localStorage.getItem('remember')).to.not.be('');
|
||||||
|
|
||||||
|
sUser = root.sessionStorage.getItem('user');
|
||||||
|
sRemember = root.sessionStorage.getItem('remember');
|
||||||
|
lRemember = root.localStorage.getItem('remember');
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
user.leave().then(function(ack){
|
||||||
|
try{
|
||||||
|
expect(ack).to.have.key('ok');
|
||||||
|
expect(gun.back(-1)._.user).to.not.have.keys(['sea', 'pub']);
|
||||||
|
expect(root.sessionStorage.getItem('user')).to.not.be(sUser);
|
||||||
|
expect(root.sessionStorage.getItem('remember')).to.not.be(sRemember);
|
||||||
|
expect(root.localStorage.getItem('remember')).to.not.be(lRemember);
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
|
||||||
|
root.sessionStorage.setItem('user', sUser);
|
||||||
|
root.sessionStorage.setItem('remember', sRemember);
|
||||||
|
root.localStorage.setItem('remember', lRemember);
|
||||||
|
|
||||||
|
user.recall(12 * 60 * 60, {session: false}).then(doCheck(done))
|
||||||
|
.catch(done);
|
||||||
|
}).catch(done);
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('invalid sessionStorage session');
|
||||||
it.skip('expired session');
|
it.skip('expired session');
|
||||||
it.skip('changed password');
|
it.skip('changed password');
|
||||||
it.skip('no session');
|
it.skip('no session');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('alive', function(){
|
describe('alive', function(){
|
||||||
it.skip('valid session');
|
it('valid session', function(done){
|
||||||
it.skip('expired session');
|
var check = function(ack){
|
||||||
|
try{
|
||||||
|
expect(ack).to.not.be(undefined);
|
||||||
|
expect(ack).to.not.be('');
|
||||||
|
expect(ack).to.not.have.key('err');
|
||||||
|
expect(ack).to.have.keys(['sea', 'pub']);
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
var usr = alias+type+'alive';
|
||||||
|
user.create(usr, pass).then(function(ack){
|
||||||
|
expect(ack).to.not.be(undefined);
|
||||||
|
expect(ack).to.not.be('');
|
||||||
|
expect(ack).to.have.keys(['ok','pub']);
|
||||||
|
user.auth(usr, pass, { pin: 'PIN' }).then(function(usr){
|
||||||
|
try{
|
||||||
|
expect(usr).to.not.be(undefined);
|
||||||
|
expect(usr).to.not.be('');
|
||||||
|
expect(usr).to.not.have.key('err');
|
||||||
|
expect(usr).to.have.key('put');
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
// Gun.user.alive - keeps/checks User authentiation state
|
||||||
|
if(type === 'callback'){
|
||||||
|
user.alive(check);
|
||||||
|
} else {
|
||||||
|
user.alive().then(check).catch(done);
|
||||||
|
}
|
||||||
|
}).catch(done);
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('expired session', function(done){
|
||||||
|
var check = function(ack){
|
||||||
|
try{
|
||||||
|
expect(ack).to.not.be(undefined);
|
||||||
|
expect(ack).to.not.be('');
|
||||||
|
expect(ack).to.not.have.keys(['sea', 'pub']);
|
||||||
|
expect(ack).to.have.key('err');
|
||||||
|
}catch(e){done(e); return};
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
user.leave().catch(function(){}).then(function(){
|
||||||
|
user.alive().then(function(){
|
||||||
|
done('Unexpected alive session!');
|
||||||
|
}).catch(check);
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
it.skip('recall hook session manipulation');
|
it.skip('recall hook session manipulation');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user