|
|
|
/*jshint esversion: 6 */
|
|
|
|
/* globals process */
|
|
|
|
const Core = module.exports;
|
|
|
|
const Util = require("../common-util");
|
|
|
|
const escapeKeyCharacters = Util.escapeKeyCharacters;
|
|
|
|
|
|
|
|
/* Use Nacl for checking signatures of messages */
|
|
|
|
const Nacl = require("tweetnacl/nacl-fast");
|
|
|
|
|
|
|
|
|
|
|
|
Core.DEFAULT_LIMIT = 50 * 1024 * 1024;
|
|
|
|
Core.SESSION_EXPIRATION_TIME = 60 * 1000;
|
|
|
|
|
|
|
|
Core.isValidId = function (chan) {
|
|
|
|
return chan && chan.length && /^[a-zA-Z0-9=+-]*$/.test(chan) &&
|
|
|
|
[32, 48].indexOf(chan.length) > -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
var makeToken = Core.makeToken = function () {
|
|
|
|
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
|
|
|
|
.toString(16);
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.makeCookie = function (token) {
|
|
|
|
var time = (+new Date());
|
|
|
|
time -= time % 5000;
|
|
|
|
|
|
|
|
return [
|
|
|
|
time,
|
|
|
|
process.pid,
|
|
|
|
token
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
var parseCookie = function (cookie) {
|
|
|
|
if (!(cookie && cookie.split)) { return null; }
|
|
|
|
|
|
|
|
var parts = cookie.split('|');
|
|
|
|
if (parts.length !== 3) { return null; }
|
|
|
|
|
|
|
|
var c = {};
|
|
|
|
c.time = new Date(parts[0]);
|
|
|
|
c.pid = Number(parts[1]);
|
|
|
|
c.seq = parts[2];
|
|
|
|
return c;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.getSession = function (Sessions, key) {
|
|
|
|
var safeKey = escapeKeyCharacters(key);
|
|
|
|
if (Sessions[safeKey]) {
|
|
|
|
Sessions[safeKey].atime = +new Date();
|
|
|
|
return Sessions[safeKey];
|
|
|
|
}
|
|
|
|
var user = Sessions[safeKey] = {};
|
|
|
|
user.atime = +new Date();
|
|
|
|
user.tokens = [
|
|
|
|
makeToken()
|
|
|
|
];
|
|
|
|
return user;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.expireSession = function (Sessions, safeKey) {
|
|
|
|
var session = Sessions[safeKey];
|
|
|
|
if (!session) { return; }
|
|
|
|
if (session.blobstage) {
|
|
|
|
session.blobstage.close();
|
|
|
|
}
|
|
|
|
delete Sessions[safeKey];
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.expireSessionAsync = function (Env, safeKey, cb) {
|
|
|
|
setTimeout(function () {
|
|
|
|
Core.expireSession(Env.Sessions, safeKey);
|
|
|
|
cb(void 0, 'OK');
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
var isTooOld = function (time, now) {
|
|
|
|
return (now - time) > 300000;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.expireSessions = function (Sessions) {
|
|
|
|
var now = +new Date();
|
|
|
|
Object.keys(Sessions).forEach(function (safeKey) {
|
|
|
|
var session = Sessions[safeKey];
|
|
|
|
if (session && isTooOld(session.atime, now)) {
|
|
|
|
Core.expireSession(Sessions, safeKey);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
var addTokenForKey = function (Sessions, publicKey, token) {
|
|
|
|
if (!Sessions[publicKey]) { throw new Error('undefined user'); }
|
|
|
|
|
|
|
|
var user = Core.getSession(Sessions, publicKey);
|
|
|
|
user.tokens.push(token);
|
|
|
|
user.atime = +new Date();
|
|
|
|
if (user.tokens.length > 2) { user.tokens.shift(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.isValidCookie = function (Sessions, publicKey, cookie) {
|
|
|
|
var parsed = parseCookie(cookie);
|
|
|
|
if (!parsed) { return false; }
|
|
|
|
|
|
|
|
var now = +new Date();
|
|
|
|
|
|
|
|
if (!parsed.time) { return false; }
|
|
|
|
if (isTooOld(parsed.time, now)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// different process. try harder
|
|
|
|
if (process.pid !== parsed.pid) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var user = Core.getSession(Sessions, publicKey);
|
|
|
|
if (!user) { return false; }
|
|
|
|
|
|
|
|
var idx = user.tokens.indexOf(parsed.seq);
|
|
|
|
if (idx === -1) { return false; }
|
|
|
|
|
|
|
|
if (idx > 0) {
|
|
|
|
// make a new token
|
|
|
|
addTokenForKey(Sessions, publicKey, Core.makeToken());
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.checkSignature = function (Env, signedMsg, signature, publicKey) {
|
|
|
|
if (!(signedMsg && publicKey)) { return false; }
|
|
|
|
|
|
|
|
var signedBuffer;
|
|
|
|
var pubBuffer;
|
|
|
|
var signatureBuffer;
|
|
|
|
|
|
|
|
try {
|
|
|
|
signedBuffer = Nacl.util.decodeUTF8(signedMsg);
|
|
|
|
} catch (e) {
|
|
|
|
Env.Log.error('INVALID_SIGNED_BUFFER', signedMsg);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
pubBuffer = Nacl.util.decodeBase64(publicKey);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
signatureBuffer = Nacl.util.decodeBase64(signature);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pubBuffer.length !== 32) {
|
|
|
|
Env.Log.error('PUBLIC_KEY_LENGTH', publicKey);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signatureBuffer.length !== 64) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Nacl.sign.detached.verify(signedBuffer, signatureBuffer, pubBuffer);
|
|
|
|
};
|
|
|
|
|
|
|
|
// E_NO_OWNERS
|
|
|
|
Core.hasOwners = function (metadata) {
|
|
|
|
return Boolean(metadata && Array.isArray(metadata.owners));
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.hasPendingOwners = function (metadata) {
|
|
|
|
return Boolean(metadata && Array.isArray(metadata.pending_owners));
|
|
|
|
};
|
|
|
|
|
|
|
|
// INSUFFICIENT_PERMISSIONS
|
|
|
|
Core.isOwner = function (metadata, unsafeKey) {
|
|
|
|
return metadata.owners.indexOf(unsafeKey) !== -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
Core.isPendingOwner = function (metadata, unsafeKey) {
|
|
|
|
return metadata.pending_owners.indexOf(unsafeKey) !== -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|