Finished User.delete tests & fixed mysterious password update bug

This commit is contained in:
mhelander 2017-08-31 20:19:22 +03:00
parent 41c0f9ed79
commit bff2fdece9
2 changed files with 136 additions and 89 deletions

93
sea.js
View File

@ -75,38 +75,52 @@
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.
root.get('alias/'+alias).get(function(at, ev){
ev.off();
if(!at.put){
root.get('alias/'+alias).get(function(rat, rev){
rev.off();
if(!rat.put){
// if no user, don't do anything.
var err = 'No user!';
Gun.log(err);
return reject({err: err});
}
// then attempt to log into each one until we find ours!
// (if two users have the same username AND the same password... that would be bad)
Gun.obj.map(at.put, function(val, key){
// then figuring out all possible candidates having matching username
var aliases = [];
Gun.obj.map(rat.put, function(at, key){
// grab the account associated with this public key.
root.get(key).get(function(at, ev){
if(!key.slice || 'pub/' !== key.slice(0,4)){return}
key = key.slice(4);
ev.off();
if(!at.put){return} // 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.)
SEA.read(at.put.salt, key).then(function(salt){
return SEA.proof(pass, salt);
}).then(function(proof){
// 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.de(auth, proof);
}).catch(function(){reject({err: 'Failed to decrypt private key!'})});
if(!at.put){return}
aliases.push({key: key, at: at});
});
});
if (!aliases.length){return reject({err: 'Public key does not exist!'})}
// 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, key = one.key;
var remaining = (aliases.length - index) > 1;
if(!at.put){
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.)
SEA.read(at.put.salt, key).then(function(salt){
return SEA.proof(pass, salt);
}).then(function(proof){
// 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.de(auth, proof);
}).catch(function(e){
}).then(function(priv){
// 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!
return (priv && resolve({pub: key, priv: priv, at: at}))
return remaining ? undefined // Not done yet
: priv ? resolve({pub: key, priv: priv, at: at})
// Or else we failed to log in...
|| reject({err: 'Failed to decrypt private key!'});
}).catch(function(){reject({err: 'Failed to create proof!'})});
});
: reject({err: 'Failed to decrypt private key!'});
});
}).catch(function(){reject({err: 'Failed to create proof!'})});
});
});
});
@ -189,19 +203,14 @@
var newsalt = Gun.text.random(64);
SEA.proof(opts.newpass, newsalt).then(function(proof){
SEA.en(key.priv, proof).then(function(encVal){
return SEA.write(encVal, key.priv).then(function(sAuth){
return { pub: key.pub, auth: sAuth };
});
return {
alias: alias,
pub: key.pub,
auth: encVal,
salt: newsalt
};
}).then(function(user){
return SEA.write(alias, key.priv).then(function(sAlias){
user.alias = sAlias; return user;
});
}).then(function(user){
return SEA.write(newsalt, key.priv).then(function(sSalt){
user.salt = sSalt; return user;
});
}).then(function(user){
var tmp = 'pub/'+key.pub;
var tmp = 'pub/'+user.pub;
// awesome, now we can update the user using public key ID.
root.get(tmp).put(user);
// then we're done
@ -211,10 +220,9 @@
} else {
doLogin();
}
return;
}).catch(function(e){
Gun.log('Failed to sign in!');
reject({err: 'Auth attempt failed'});
reject({err: 'Auth attempt failed! Reason: '+(e && e.err) || e || ''});
});
};
if (cb){doIt(cb, cb)} else {return new Promise(doIt)}
@ -395,13 +403,11 @@
// Does enc/dec key like OpenSSL - works with CryptoJS encryption/decryption
function makeKey(p,s){
var ps = Buffer.concat([new Buffer(p, 'utf8'), s]);
var h128 = new Buffer(nodeCrypto.createHash('md5').update(ps).digest('hex'), 'hex');
var h128 = nodeCrypto.createHash('md5').update(ps).digest();
// TODO: 'md5' is insecure, do we need OpenSSL compatibility anymore ?
return Buffer.concat([
h128,
new Buffer(nodeCrypto.createHash('md5').update(
Buffer.concat([h128, ps]).toString('base64'), 'base64'
).digest('hex'), 'hex')
nodeCrypto.createHash('md5').update(Buffer.concat([h128, ps])).digest()
]);
}
@ -525,8 +531,13 @@
};
if(cb){doIt(cb, function(){cb()})} else {return new Promise(doIt)}
};
SEA.write = function(m,p,cb){
SEA.write = function(mm,p,cb){
var doIt = function(resolve, reject) {
var m = mm;
if(!m.slice || 'SEA[' !== m.slice(0,4)){m = mm}
m = m.slice(3);
try{m = JSON.parse(m);
}catch(e){m = mm}
SEA.sign(m, p).then(function(signature){
resolve('SEA'+JSON.stringify([m,signature]));
}).catch(function(e){Gun.log(e); reject(e)});
@ -536,14 +547,12 @@
SEA.read = function(m,p,cb){
var doIt = function(resolve, reject) {
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);
try{m = JSON.parse(m);
}catch(e){ return reject(e); }
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)}
};

View File

@ -19,8 +19,8 @@ var root;
//Gun.log.squelch = true;
var gleak = {globals: {}, check: function(){ // via tobyho
var leaked = []
for (var key in gleak.globe){ if (!(key in gleak.globals)){ leaked.push(key)} }
if (leaked.length > 0){ console.log("GLOBAL LEAK!", leaked); return leaked }
for (var key in gleak.globe){ if(!(key in gleak.globals)){ leaked.push(key)} }
if(leaked.length > 0){ console.log("GLOBAL LEAK!", leaked); return leaked }
}};
(function(env){
for (var key in (gleak.globe = env)){ gleak.globals[key] = true }
@ -6900,7 +6900,7 @@ describe('Gun', function(){
var now = new Date().getTime();
var cb;
for (var i = 1; i <= num; i++) {
if (i === num) {
if(i === num) {
cb = function (err, ok) {
console.log(num + 'ops: ' + (new Date().getTime() - now)/1000 + 's');
}
@ -6929,7 +6929,7 @@ describe('Gun', function(){
var now = new Date().getTime();
var cb;
for (var i = 1; i <= num; i++) {
if (i === num) {
if(i === num) {
cb = function () {
console.log(num + ' API ops: ' + (new Date().getTime() - now)/1000 + 's');
t && done();
@ -7117,7 +7117,7 @@ describe('Gun', function(){
var now = new Date().getTime();
var cb;
for (var i = 1; i <= num; i++) {
if (i === num) {
if(i === num) {
cb = function (err, ok) {
console.log(num + 'MUTANT ops: ' + (new Date().getTime() - now)/1000 + 's');
t && done();
@ -7265,7 +7265,7 @@ describe('Gun', function(){
gun.map(function (player, number) {
players[number] = player;
players[number].history = [];
if (!player.taken && !me) {
if(!player.taken && !me) {
this.put({
taken: true,
history: {
@ -7645,7 +7645,7 @@ describe('Gun', function(){
Gun.log.debug = 1; console.log("------------------");
gun.path('1.2.data').on(function (data) {
console.log("??????", data);
if (data) {
if(data) {
// gun will never receive the "true" update
done();
}
@ -8196,19 +8196,25 @@ describe('Gun', function(){
}catch(e){done(e); return};
done();
};
user.auth(alias+type, pass+' new').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.leave - performs logout for authenticated user
if(type === 'callback'){
user.leave(check);
} else {
user.leave().then(check).catch(done);
}
var usr = alias+type+'leave';
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).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.leave - performs logout for authenticated user
if(type === 'callback'){
user.leave(check);
} else {
user.leave().then(check).catch(done);
}
}).catch(done);
}).catch(done);
});
@ -8222,20 +8228,28 @@ describe('Gun', function(){
}catch(e){done(e); return};
done();
};
user.leave().then(function(ack){
expect(gun.back(-1)._.user).to.not.have.keys(['sea', 'pub']);
if(type === 'callback'){
user.leave(check);
} else {
user.leave().then(check).catch(done);
}
});
expect(gun.back(-1)._.user).to.not.have.keys(['sea', 'pub']);
if(type === 'callback'){
user.leave(check);
} else {
user.leave().then(check).catch(done);
}
});
});
describe('delete', function(){
it('existing authenticated user', function(done){
var check = function(ack){
var usr = alias+type+'del';
var createUser = function(a, p){
return user.create(a, p).then(function(ack){
expect(ack).to.not.be(undefined);
expect(ack).to.not.be('');
expect(ack).to.have.keys(['ok','pub']);
return ack;
});
};
var check = function(done){
return function(ack){
try{
expect(ack).to.not.be(undefined);
expect(ack).to.not.be('');
@ -8245,32 +8259,56 @@ describe('Gun', function(){
}catch(e){done(e); return};
done();
};
var aalias = alias+type+'del';
user.create(aalias, pass).catch(check).then(function(ack){
try{
expect(ack).to.not.be(undefined);
expect(ack).to.not.be('');
expect(ack).to.have.keys(['ok','pub']);
}catch(e){done(e); return};
user.auth(aalias, pass).then(function(usr){
};
it('existing authenticated user', function(done){
createUser(usr, pass).then(function(){
user.auth(usr, pass).then(function(ack){
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(ack).to.not.be(undefined);
expect(ack).to.not.be('');
expect(ack).to.not.have.key('err');
expect(ack).to.have.key('put');
}catch(e){done(e); return};
// Gun.user.delete - deöetes existing user account
// Gun.user.delete - deletes existing user account
if(type === 'callback'){
user.delete(alias+type, pass+' new', check);
user.delete(usr, pass, check(done));
} else {
user.delete(alias+type, pass+' new').then(check).catch(done);
user.delete(usr, pass).then(check(done)).catch(done);
}
}).catch(done);
}).catch(done);
});
it('unauthenticated existing user', function(done){
createUser(usr, pass).catch(function(){})
.then(function(){
if(type === 'callback'){
user.delete(usr, pass, check(done));
} else {
user.delete(usr, pass).then(check(done)).catch(done);
}
});
});
it.skip('unauthenticated existing user');
it.skip('unauthenticated other user');
it('non-existing user', function(done){
var notFound = function(ack){
try{
expect(ack).to.not.be(undefined);
expect(ack).to.not.be('');
expect(ack).to.not.have.key('put');
expect(ack).to.have.key('err');
}catch(e){done(e); return};
done();
};
if(type === 'callback'){
user.delete('someone', 'password guess', notFound);
} else {
user.delete('someone', 'password guess').then(function(){
done('Unexpectedly deleted guessed user!');
}).catch(notFound);
}
});
});
describe('recall', function(){