mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-18 19:25:48 +00:00
Merge pull request #252 from ripple/remove-vault-client
Remove blobvault client
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
"lodash": "^2.4.1",
|
||||
"lru-cache": "~2.5.0",
|
||||
"ripple-wallet-generator": "1.0.1",
|
||||
"superagent": "^0.18.0",
|
||||
"ws": "~0.4.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
var async = require('async');
|
||||
var superagent = require('superagent');
|
||||
var RippleTxt = require('./rippletxt').RippleTxt;
|
||||
|
||||
var AuthInfo = { };
|
||||
|
||||
AuthInfo._getRippleTxt = function(domain, callback) {
|
||||
RippleTxt.get(domain, callback);
|
||||
};
|
||||
|
||||
AuthInfo._getUser = function(url, callback) {
|
||||
superagent.get(url, callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get auth info for a given username
|
||||
*
|
||||
* @param {string} domain - Domain which hosts the user's info
|
||||
* @param {string} username - Username who's info we are retreiving
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
AuthInfo.get = function(domain, username, callback) {
|
||||
var self = this;
|
||||
username = username.toLowerCase();
|
||||
|
||||
function getRippleTxt(callback) {
|
||||
self._getRippleTxt(domain, function(err, txt) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!txt.authinfo_url) {
|
||||
return callback(new Error('Authentication is not supported on ' + domain));
|
||||
}
|
||||
|
||||
var url = Array.isArray(txt.authinfo_url) ? txt.authinfo_url[0] : txt.authinfo_url;
|
||||
|
||||
url += '?domain=' + domain + '&username=' + username;
|
||||
|
||||
callback(null, url);
|
||||
});
|
||||
};
|
||||
|
||||
function getUser(url, callback) {
|
||||
self._getUser(url, function(err, res) {
|
||||
if (err || res.error) {
|
||||
callback(new Error('Authentication info server unreachable'));
|
||||
} else {
|
||||
callback(null, res.body);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.waterfall([ getRippleTxt, getUser ], callback);
|
||||
};
|
||||
|
||||
exports.AuthInfo = AuthInfo;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,17 +13,12 @@ exports.Meta = require('./meta').Meta;
|
||||
exports.SerializedObject = require('./serializedobject').SerializedObject;
|
||||
exports.RippleError = require('./rippleerror').RippleError;
|
||||
exports.Message = require('./message').Message;
|
||||
exports.VaultClient = require('./vaultclient').VaultClient;
|
||||
exports.AuthInfo = require('./authinfo').AuthInfo;
|
||||
exports.RippleTxt = require('./rippletxt').RippleTxt;
|
||||
exports.binformat = require('./binformat');
|
||||
exports.utils = require('./utils');
|
||||
exports.Server = require('./server').Server;
|
||||
exports.Wallet = require('./wallet');
|
||||
exports.Ledger = require('./ledger').Ledger;
|
||||
exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
|
||||
exports.VaultClient = require('./vaultclient').VaultClient;
|
||||
exports.Blob = require('./blob').Blob;
|
||||
exports.RangeSet = require('./rangeset').RangeSet;
|
||||
|
||||
// Important: We do not guarantee any specific version of SJCL or for any
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
var request = require('superagent');
|
||||
var Currency = require('./currency').Currency;
|
||||
|
||||
var RippleTxt = {
|
||||
txts : { }
|
||||
};
|
||||
|
||||
RippleTxt.urlTemplates = [
|
||||
'https://{{domain}}/ripple.txt',
|
||||
'https://www.{{domain}}/ripple.txt',
|
||||
'https://ripple.{{domain}}/ripple.txt',
|
||||
'http://{{domain}}/ripple.txt',
|
||||
'http://www.{{domain}}/ripple.txt',
|
||||
'http://ripple.{{domain}}/ripple.txt'
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets the ripple.txt file for the given domain
|
||||
* @param {string} domain - Domain to retrieve file from
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
RippleTxt.get = function(domain, fn) {
|
||||
var self = this;
|
||||
|
||||
if (self.txts[domain]) {
|
||||
return fn(null, self.txts[domain]);
|
||||
}
|
||||
|
||||
;(function nextUrl(i) {
|
||||
var url = RippleTxt.urlTemplates[i];
|
||||
|
||||
if (!url) {
|
||||
return fn(new Error('No ripple.txt found'));
|
||||
}
|
||||
|
||||
url = url.replace('{{domain}}', domain);
|
||||
|
||||
request.get(url, function(err, resp) {
|
||||
if (err || !resp.text) {
|
||||
return nextUrl(++i);
|
||||
}
|
||||
|
||||
var sections = self.parse(resp.text);
|
||||
self.txts[domain] = sections;
|
||||
|
||||
fn(null, sections);
|
||||
});
|
||||
})(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a ripple.txt file
|
||||
* @param {string} txt - Unparsed ripple.txt data
|
||||
*/
|
||||
|
||||
RippleTxt.parse = function(txt) {
|
||||
var currentSection = '';
|
||||
var sections = { };
|
||||
|
||||
txt = txt.replace(/\r?\n/g, '\n').split('\n');
|
||||
|
||||
for (var i = 0, l = txt.length; i < l; i++) {
|
||||
var line = txt[i];
|
||||
|
||||
if (!line.length || line[0] === '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] === '[' && line[line.length - 1] === ']') {
|
||||
currentSection = line.slice(1, line.length - 1);
|
||||
sections[currentSection] = [];
|
||||
} else {
|
||||
line = line.replace(/^\s+|\s+$/g, '');
|
||||
if (sections[currentSection]) {
|
||||
sections[currentSection].push(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
/**
|
||||
* extractDomain
|
||||
* attempt to extract the domain from a given url
|
||||
* returns the url if unsuccessful
|
||||
* @param {Object} url
|
||||
*/
|
||||
|
||||
RippleTxt.extractDomain = function (url) {
|
||||
match = /[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?([^.\?][^\?.]+?)?$/.exec(url);
|
||||
return match && match[0] ? match[0] : url;
|
||||
};
|
||||
|
||||
/**
|
||||
* getCurrencies
|
||||
* returns domain, issuer account and currency object
|
||||
* for each currency found in the domain's ripple.txt file
|
||||
* @param {Object} domain
|
||||
* @param {Object} fn
|
||||
*/
|
||||
|
||||
RippleTxt.getCurrencies = function(domain, fn) {
|
||||
domain = RippleTxt.extractDomain(domain);
|
||||
this.get(domain, function(err, txt) {
|
||||
if (err) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
if (err || !txt.currencies || !txt.accounts) {
|
||||
return fn(null, []);
|
||||
}
|
||||
|
||||
//NOTE: this won't be accurate if there are
|
||||
//multiple issuer accounts with different
|
||||
//currencies associated with each.
|
||||
var issuer = txt.accounts[0];
|
||||
var currencies = [];
|
||||
|
||||
txt.currencies.forEach(function(currency) {
|
||||
currencies.push({
|
||||
issuer : issuer,
|
||||
currency : Currency.from_json(currency),
|
||||
domain : domain
|
||||
});
|
||||
});
|
||||
|
||||
fn(null, currencies);
|
||||
});
|
||||
};
|
||||
|
||||
exports.RippleTxt = RippleTxt;
|
||||
@@ -1,203 +0,0 @@
|
||||
var Crypt = require('./crypt').Crypt;
|
||||
var Message = require('./message').Message;
|
||||
var parser = require("url");
|
||||
var querystring = require('querystring');
|
||||
var extend = require("extend");
|
||||
|
||||
var SignedRequest = function (config) {
|
||||
// XXX Constructor should be generalized and constructing from an Angular.js
|
||||
// $http config should be a SignedRequest.from... utility method.
|
||||
this.config = extend(true, {}, config);
|
||||
if (!this.config.data) this.config.data = {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a string from request parameters that
|
||||
* will be used to sign a request
|
||||
* @param {Object} parsed - parsed url
|
||||
* @param {Object} date
|
||||
* @param {Object} mechanism - type of signing
|
||||
*/
|
||||
SignedRequest.prototype.getStringToSign = function (parsed, date, mechanism) {
|
||||
// XXX This method doesn't handle signing GET requests correctly. The data
|
||||
// field will be merged into the search string, not the request body.
|
||||
|
||||
// Sort the properties of the JSON object into canonical form
|
||||
var canonicalData = JSON.stringify(copyObjectWithSortedKeys(this.config.data));
|
||||
|
||||
// Canonical request using Amazon's v4 signature format
|
||||
// See: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
var canonicalRequest = [
|
||||
this.config.method || 'GET',
|
||||
parsed.pathname || '',
|
||||
parsed.search || '',
|
||||
// XXX Headers signing not supported
|
||||
'',
|
||||
'',
|
||||
Crypt.hashSha512(canonicalData).toLowerCase()
|
||||
].join('\n');
|
||||
|
||||
// String to sign inspired by Amazon's v4 signature format
|
||||
// See: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||
//
|
||||
// We don't have a credential scope, so we skip it.
|
||||
//
|
||||
// But that modifies the format, so the format ID is RIPPLE1, instead of AWS4.
|
||||
return [
|
||||
mechanism,
|
||||
date,
|
||||
Crypt.hashSha512(canonicalRequest).toLowerCase()
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
//prepare for signing
|
||||
function copyObjectWithSortedKeys(object) {
|
||||
if (isPlainObject(object)) {
|
||||
var newObj = {};
|
||||
var keysSorted = Object.keys(object).sort();
|
||||
var key;
|
||||
for (var i in keysSorted) {
|
||||
key = keysSorted[i];
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
newObj[key] = copyObjectWithSortedKeys(object[key]);
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
} else if (Array.isArray(object)) {
|
||||
return object.map(copyObjectWithSortedKeys);
|
||||
} else {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
||||
//from npm extend
|
||||
function isPlainObject(obj) {
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval)
|
||||
return false;
|
||||
|
||||
var has_own_constructor = hasOwn.call(obj, 'constructor');
|
||||
var has_is_property_of_method = hasOwn.call(obj.constructor.prototype, 'isPrototypeOf');
|
||||
// Not own constructor property must be Object
|
||||
if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
|
||||
return false;
|
||||
|
||||
// Own properties are enumerated firstly, so to speed up,
|
||||
// if last one is own, then all properties are own.
|
||||
var key;
|
||||
for ( key in obj ) {}
|
||||
|
||||
return key === undefined || hasOwn.call( obj, key );
|
||||
};
|
||||
|
||||
/**
|
||||
* HMAC signed request
|
||||
* @param {Object} config
|
||||
* @param {Object} auth_secret
|
||||
* @param {Object} blob_id
|
||||
*/
|
||||
SignedRequest.prototype.signHmac = function (auth_secret, blob_id) {
|
||||
var config = extend(true, {}, this.config);
|
||||
|
||||
// Parse URL
|
||||
var parsed = parser.parse(config.url);
|
||||
var date = dateAsIso8601();
|
||||
var signatureType = 'RIPPLE1-HMAC-SHA512';
|
||||
var stringToSign = this.getStringToSign(parsed, date, signatureType);
|
||||
var signature = Crypt.signString(auth_secret, stringToSign);
|
||||
|
||||
var query = querystring.stringify({
|
||||
signature: Crypt.base64ToBase64Url(signature),
|
||||
signature_date: date,
|
||||
signature_blob_id: blob_id,
|
||||
signature_type: signatureType
|
||||
});
|
||||
|
||||
config.url += (parsed.search ? '&' : '?') + query;
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Asymmetric signed request
|
||||
* @param {Object} config
|
||||
* @param {Object} secretKey
|
||||
* @param {Object} account
|
||||
* @param {Object} blob_id
|
||||
*/
|
||||
SignedRequest.prototype.signAsymmetric = function (secretKey, account, blob_id) {
|
||||
var config = extend(true, {}, this.config);
|
||||
|
||||
// Parse URL
|
||||
var parsed = parser.parse(config.url);
|
||||
var date = dateAsIso8601();
|
||||
var signatureType = 'RIPPLE1-ECDSA-SHA512';
|
||||
var stringToSign = this.getStringToSign(parsed, date, signatureType);
|
||||
var signature = Message.signMessage(stringToSign, secretKey);
|
||||
|
||||
var query = querystring.stringify({
|
||||
signature: Crypt.base64ToBase64Url(signature),
|
||||
signature_date: date,
|
||||
signature_blob_id: blob_id,
|
||||
signature_account: account,
|
||||
signature_type: signatureType
|
||||
});
|
||||
|
||||
config.url += (parsed.search ? '&' : '?') + query;
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Asymmetric signed request for vault recovery
|
||||
* @param {Object} config
|
||||
* @param {Object} secretKey
|
||||
* @param {Object} username
|
||||
*/
|
||||
SignedRequest.prototype.signAsymmetricRecovery = function (secretKey, username) {
|
||||
var config = extend(true, {}, this.config);
|
||||
|
||||
// Parse URL
|
||||
var parsed = parser.parse(config.url);
|
||||
var date = dateAsIso8601();
|
||||
var signatureType = 'RIPPLE1-ECDSA-SHA512';
|
||||
var stringToSign = this.getStringToSign(parsed, date, signatureType);
|
||||
var signature = Message.signMessage(stringToSign, secretKey);
|
||||
|
||||
var query = querystring.stringify({
|
||||
signature: Crypt.base64ToBase64Url(signature),
|
||||
signature_date: date,
|
||||
signature_username: username,
|
||||
signature_type: signatureType
|
||||
});
|
||||
|
||||
config.url += (parsed.search ? '&' : '?') + query;
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
var dateAsIso8601 = (function () {
|
||||
function pad(n) {
|
||||
return (n < 0 || n > 9 ? "" : "0") + n;
|
||||
}
|
||||
|
||||
return function dateAsIso8601() {
|
||||
var date = new Date();
|
||||
return date.getUTCFullYear() + "-" +
|
||||
pad(date.getUTCMonth() + 1) + "-" +
|
||||
pad(date.getUTCDate()) + "T" +
|
||||
pad(date.getUTCHours()) + ":" +
|
||||
pad(date.getUTCMinutes()) + ":" +
|
||||
pad(date.getUTCSeconds()) + ".000Z";
|
||||
};
|
||||
})();
|
||||
|
||||
// XXX Add methods for verifying requests
|
||||
// SignedRequest.prototype.verifySignatureHmac
|
||||
// SignedRequest.prototype.verifySignatureAsymetric
|
||||
|
||||
exports.SignedRequest = SignedRequest;
|
||||
|
||||
@@ -1,593 +0,0 @@
|
||||
var async = require('async');
|
||||
var blobClient = require('./blob').BlobClient;
|
||||
var AuthInfo = require('./authinfo').AuthInfo;
|
||||
var crypt = require('./crypt').Crypt;
|
||||
var log = require('./log').sub('vault');
|
||||
function VaultClient(opts) {
|
||||
|
||||
var self = this;
|
||||
|
||||
if (!opts) {
|
||||
opts = { };
|
||||
}
|
||||
|
||||
if (typeof opts === 'string') {
|
||||
opts = { domain: opts };
|
||||
}
|
||||
|
||||
this.domain = opts.domain || 'ripple.com';
|
||||
this.infos = { };
|
||||
};
|
||||
|
||||
/**
|
||||
* getAuthInfo
|
||||
* gets auth info for a username. returns authinfo
|
||||
* even if user does not exists (with exist set to false)
|
||||
* @param {string} username
|
||||
* @param {function} callback
|
||||
*/
|
||||
VaultClient.prototype.getAuthInfo = function (username, callback) {
|
||||
|
||||
AuthInfo.get(this.domain, username, function(err, authInfo) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (authInfo.version !== 3) {
|
||||
return callback(new Error('This wallet is incompatible with this version of the vault-client.'));
|
||||
}
|
||||
|
||||
if (!authInfo.pakdf) {
|
||||
return callback(new Error('No settings for PAKDF in auth packet.'));
|
||||
}
|
||||
|
||||
if (typeof authInfo.blobvault !== 'string') {
|
||||
return callback(new Error('No blobvault specified in the authinfo.'));
|
||||
}
|
||||
|
||||
callback(null, authInfo);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* _deriveLoginKeys
|
||||
* method designed for asnyc waterfall
|
||||
*/
|
||||
|
||||
VaultClient.prototype._deriveLoginKeys = function (authInfo, password, callback) {
|
||||
var normalizedUsername = authInfo.username.toLowerCase().replace(/-/g, '');
|
||||
|
||||
//derive login keys
|
||||
crypt.derive(authInfo.pakdf, 'login', normalizedUsername, password, function(err, keys) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, authInfo, password, keys);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* _deriveUnlockKey
|
||||
* method designed for asnyc waterfall
|
||||
*/
|
||||
|
||||
VaultClient.prototype._deriveUnlockKey = function (authInfo, password, keys, callback) {
|
||||
var normalizedUsername = authInfo.username.toLowerCase().replace(/-/g, '');
|
||||
|
||||
//derive unlock key
|
||||
crypt.derive(authInfo.pakdf, 'unlock', normalizedUsername, password, function(err, unlock) {
|
||||
if (err) {
|
||||
log.error('derive:', err);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!keys) {
|
||||
keys = { };
|
||||
}
|
||||
|
||||
keys.unlock = unlock.unlock;
|
||||
callback(null, authInfo, keys);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a ripple name from a given account address, if it has one
|
||||
* @param {string} address - Account address to query
|
||||
* @param {string} url - Url of blob vault
|
||||
*/
|
||||
|
||||
VaultClient.prototype.getRippleName = function(address, url, callback) {
|
||||
//use the url from previously retrieved authInfo, if necessary
|
||||
if (!url) {
|
||||
callback(new Error('Blob vault URL is required'));
|
||||
} else {
|
||||
blobClient.getRippleName(url, address, callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check blobvault for existance of username
|
||||
*
|
||||
* @param {string} username
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.exists = function(username, callback) {
|
||||
AuthInfo.get(this.domain, username.toLowerCase(), function(err, authInfo) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, !!authInfo.exists);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Authenticate and retrieve a decrypted blob using a ripple name and password
|
||||
*
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.login = function(username, password, device_id, callback) {
|
||||
var self = this;
|
||||
|
||||
var steps = [
|
||||
getAuthInfo,
|
||||
self._deriveLoginKeys,
|
||||
getBlob
|
||||
];
|
||||
|
||||
async.waterfall(steps, callback);
|
||||
|
||||
function getAuthInfo(callback) {
|
||||
self.getAuthInfo(username, function(err, authInfo){
|
||||
|
||||
if (authInfo && !authInfo.exists) {
|
||||
return callback(new Error('User does not exist.'));
|
||||
}
|
||||
|
||||
return callback (err, authInfo, password);
|
||||
});
|
||||
}
|
||||
|
||||
function getBlob(authInfo, password, keys, callback) {
|
||||
var options = {
|
||||
url : authInfo.blobvault,
|
||||
blob_id : keys.id,
|
||||
key : keys.crypt,
|
||||
device_id : device_id
|
||||
};
|
||||
|
||||
blobClient.get(options, function(err, blob) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
//save for relogin
|
||||
self.infos[keys.id] = authInfo;
|
||||
|
||||
//migrate missing fields
|
||||
if (blob.missing_fields) {
|
||||
if (blob.missing_fields.encrypted_blobdecrypt_key) {
|
||||
log.info('migration: saving encrypted blob decrypt key');
|
||||
authInfo.blob = blob;
|
||||
//get the key to unlock the secret, then update the blob keys
|
||||
self._deriveUnlockKey(authInfo, password, keys, updateKeys);
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
blob : blob,
|
||||
username : authInfo.username,
|
||||
verified : authInfo.emailVerified
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function updateKeys (err, params, keys) {
|
||||
if (err || !keys.unlock) {
|
||||
return; //unable to unlock
|
||||
}
|
||||
|
||||
var secret;
|
||||
try {
|
||||
secret = crypt.decrypt(keys.unlock, params.blob.encrypted_secret);
|
||||
} catch (error) {
|
||||
return log.error('decrypt:', error);
|
||||
}
|
||||
|
||||
options = {
|
||||
username : params.username,
|
||||
blob : params.blob,
|
||||
masterkey : secret,
|
||||
keys : keys
|
||||
};
|
||||
|
||||
blobClient.updateKeys(options, function(err, resp){
|
||||
if (err) {
|
||||
log.error('updateKeys:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retreive and decrypt blob using a blob url, id and crypt derived previously.
|
||||
*
|
||||
* @param {string} url - Blob vault url
|
||||
* @param {string} id - Blob id from previously retreived blob
|
||||
* @param {string} key - Blob decryption key
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.relogin = function(url, id, key, device_id, callback) {
|
||||
//use the url from previously retrieved authInfo, if necessary
|
||||
if (!url && this.infos[id]) {
|
||||
url = this.infos[id].blobvault;
|
||||
}
|
||||
|
||||
if (!url) {
|
||||
return callback(new Error('Blob vault URL is required'));
|
||||
}
|
||||
|
||||
var options = {
|
||||
url : url,
|
||||
blob_id : id,
|
||||
key : key,
|
||||
device_id : device_id
|
||||
};
|
||||
|
||||
blobClient.get(options, function(err, blob) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback (null, { blob: blob });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt the secret key using a username and password
|
||||
*
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {string} encryptSecret
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.unlock = function(username, password, encryptSecret, fn) {
|
||||
var self = this;
|
||||
|
||||
var steps = [
|
||||
getAuthInfo,
|
||||
self._deriveUnlockKey,
|
||||
unlockSecret
|
||||
];
|
||||
|
||||
async.waterfall(steps, fn);
|
||||
|
||||
function getAuthInfo(callback) {
|
||||
self.getAuthInfo(username, function(err, authInfo){
|
||||
|
||||
if (authInfo && !authInfo.exists) {
|
||||
return callback(new Error('User does not exist.'));
|
||||
}
|
||||
|
||||
return callback (err, authInfo, password, {});
|
||||
});
|
||||
}
|
||||
|
||||
function unlockSecret (authinfo, keys, callback) {
|
||||
|
||||
var secret;
|
||||
try {
|
||||
secret = crypt.decrypt(keys.unlock, encryptSecret);
|
||||
} catch (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
keys : keys,
|
||||
secret : secret
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the decrypted blob and secret key in one step using
|
||||
* the username and password
|
||||
*
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.loginAndUnlock = function(username, password, device_id, fn) {
|
||||
var self = this;
|
||||
|
||||
var steps = [
|
||||
login,
|
||||
deriveUnlockKey,
|
||||
unlockSecret
|
||||
];
|
||||
|
||||
async.waterfall(steps, fn);
|
||||
|
||||
function login (callback) {
|
||||
self.login(username, password, device_id, function(err, resp) {
|
||||
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!resp.blob || !resp.blob.encrypted_secret) {
|
||||
return callback(new Error('Unable to retrieve blob and secret.'));
|
||||
}
|
||||
|
||||
if (!resp.blob.id || !resp.blob.key) {
|
||||
return callback(new Error('Unable to retrieve keys.'));
|
||||
}
|
||||
|
||||
//get authInfo via id - would have been saved from login
|
||||
var authInfo = self.infos[resp.blob.id];
|
||||
|
||||
if (!authInfo) {
|
||||
return callback(new Error('Unable to find authInfo'));
|
||||
}
|
||||
|
||||
callback(null, authInfo, password, resp.blob);
|
||||
});
|
||||
};
|
||||
|
||||
function deriveUnlockKey (authInfo, password, blob, callback) {
|
||||
self._deriveUnlockKey(authInfo, password, null, function(err, authInfo, keys){
|
||||
callback(err, keys.unlock, authInfo, blob);
|
||||
});
|
||||
};
|
||||
|
||||
function unlockSecret (unlock, authInfo, blob, callback) {
|
||||
var secret;
|
||||
try {
|
||||
secret = crypt.decrypt(unlock, blob.encrypted_secret);
|
||||
} catch (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
blob : blob,
|
||||
unlock : unlock,
|
||||
secret : secret,
|
||||
username : authInfo.username,
|
||||
verified : authInfo.emailVerified
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify an email address for an existing user
|
||||
*
|
||||
* @param {string} username
|
||||
* @param {string} token - Verification token
|
||||
* @param {function} fn - Callback function
|
||||
*/
|
||||
|
||||
VaultClient.prototype.verify = function(username, token, callback) {
|
||||
var self = this;
|
||||
|
||||
self.getAuthInfo(username, function (err, authInfo){
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
blobClient.verify(authInfo.blobvault, username.toLowerCase(), token, callback);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* changePassword
|
||||
* @param {object} options
|
||||
* @param {string} options.username
|
||||
* @param {string} options.password
|
||||
* @param {string} options.masterkey
|
||||
* @param {object} options.blob
|
||||
*/
|
||||
|
||||
VaultClient.prototype.changePassword = function (options, fn) {
|
||||
var self = this;
|
||||
var password = String(options.password).trim();
|
||||
|
||||
var steps = [
|
||||
getAuthInfo,
|
||||
self._deriveLoginKeys,
|
||||
self._deriveUnlockKey,
|
||||
changePassword
|
||||
];
|
||||
|
||||
async.waterfall(steps, fn);
|
||||
|
||||
function getAuthInfo(callback) {
|
||||
self.getAuthInfo(options.username, function(err, authInfo) {
|
||||
return callback (err, authInfo, password);
|
||||
});
|
||||
};
|
||||
|
||||
function changePassword (authInfo, keys, callback) {
|
||||
options.keys = keys;
|
||||
blobClient.updateKeys(options, callback);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* rename
|
||||
* rename a ripple account
|
||||
* @param {object} options
|
||||
* @param {string} options.username
|
||||
* @param {string} options.new_username
|
||||
* @param {string} options.password
|
||||
* @param {string} options.masterkey
|
||||
* @param {object} options.blob
|
||||
* @param {function} fn
|
||||
*/
|
||||
|
||||
VaultClient.prototype.rename = function (options, fn) {
|
||||
var self = this;
|
||||
var new_username = String(options.new_username).trim();
|
||||
var password = String(options.password).trim();
|
||||
|
||||
var steps = [
|
||||
getAuthInfo,
|
||||
self._deriveLoginKeys,
|
||||
self._deriveUnlockKey,
|
||||
renameBlob
|
||||
];
|
||||
|
||||
async.waterfall(steps, fn);
|
||||
|
||||
function getAuthInfo(callback) {
|
||||
self.getAuthInfo(new_username, function(err, authInfo){
|
||||
|
||||
if (authInfo && authInfo.exists) {
|
||||
return callback(new Error('username already taken.'));
|
||||
} else {
|
||||
authInfo.username = new_username;
|
||||
}
|
||||
|
||||
return callback (err, authInfo, password);
|
||||
});
|
||||
};
|
||||
|
||||
function renameBlob (authInfo, keys, callback) {
|
||||
options.keys = keys;
|
||||
blobClient.rename(options, callback);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a new user and save to the blob vault
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.username
|
||||
* @param {string} options.password
|
||||
* @param {string} options.masterkey //optional, will create if absent
|
||||
* @param {string} options.email
|
||||
* @param {string} options.activateLink
|
||||
* @param {object} options.oldUserBlob //optional
|
||||
* @param {function} fn
|
||||
*/
|
||||
|
||||
VaultClient.prototype.register = function(options, fn) {
|
||||
var self = this;
|
||||
var username = String(options.username).trim();
|
||||
var password = String(options.password).trim();
|
||||
var result = self.validateUsername(username);
|
||||
|
||||
if (!result.valid) {
|
||||
return fn(new Error('invalid username.'));
|
||||
}
|
||||
|
||||
var steps = [
|
||||
getAuthInfo,
|
||||
self._deriveLoginKeys,
|
||||
self._deriveUnlockKey,
|
||||
create
|
||||
];
|
||||
|
||||
async.waterfall(steps, fn);
|
||||
|
||||
function getAuthInfo(callback) {
|
||||
self.getAuthInfo(username, function(err, authInfo){
|
||||
return callback (err, authInfo, password);
|
||||
});
|
||||
};
|
||||
|
||||
function create(authInfo, keys, callback) {
|
||||
var params = {
|
||||
url : authInfo.blobvault,
|
||||
id : keys.id,
|
||||
crypt : keys.crypt,
|
||||
unlock : keys.unlock,
|
||||
username : username,
|
||||
email : options.email,
|
||||
masterkey : options.masterkey || crypt.createMaster(),
|
||||
activateLink : options.activateLink,
|
||||
oldUserBlob : options.oldUserBlob,
|
||||
domain : options.domain
|
||||
};
|
||||
|
||||
blobClient.create(params, function(err, blob) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, {
|
||||
blob : blob,
|
||||
username : username
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* validateUsername
|
||||
* check username for validity
|
||||
*/
|
||||
|
||||
VaultClient.prototype.validateUsername = function (username) {
|
||||
username = String(username).trim();
|
||||
var result = {
|
||||
valid : false,
|
||||
reason : ''
|
||||
};
|
||||
|
||||
if (username.length < 2) {
|
||||
result.reason = 'tooshort';
|
||||
} else if (username.length > 20) {
|
||||
result.reason = 'toolong';
|
||||
} else if (!/^[a-zA-Z0-9\-]+$/.exec(username)) {
|
||||
result.reason = 'charset';
|
||||
} else if (/^-/.exec(username)) {
|
||||
result.reason = 'starthyphen';
|
||||
} else if (/-$/.exec(username)) {
|
||||
result.reason = 'endhyphen';
|
||||
} else if (/--/.exec(username)) {
|
||||
result.reason = 'multhyphen';
|
||||
} else {
|
||||
result.valid = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* generateDeviceID
|
||||
* create a new random device ID for 2FA
|
||||
*/
|
||||
VaultClient.prototype.generateDeviceID = function () {
|
||||
return crypt.createSecret(4);
|
||||
};
|
||||
|
||||
/*** pass thru some blob client function ***/
|
||||
|
||||
VaultClient.prototype.resendEmail = blobClient.resendEmail;
|
||||
|
||||
VaultClient.prototype.recoverBlob = blobClient.recoverBlob;
|
||||
|
||||
VaultClient.prototype.deleteBlob = blobClient.deleteBlob;
|
||||
|
||||
VaultClient.prototype.requestToken = blobClient.requestToken;
|
||||
|
||||
VaultClient.prototype.verifyToken = blobClient.verifyToken;
|
||||
|
||||
VaultClient.prototype.getAttestation = blobClient.getAttestation;
|
||||
|
||||
VaultClient.prototype.updateAttestation = blobClient.updateAttestation;
|
||||
|
||||
VaultClient.prototype.getAttestationSummary = blobClient.getAttestationSummary;
|
||||
|
||||
//export by name
|
||||
exports.VaultClient = VaultClient;
|
||||
@@ -1,851 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var nock = require('nock');
|
||||
var RippleTxt = require('ripple-lib').RippleTxt;
|
||||
var AuthInfo = require('ripple-lib').AuthInfo;
|
||||
var VaultClient = require('ripple-lib').VaultClient;
|
||||
var Blob = require('ripple-lib').Blob;
|
||||
var UInt256 = require('ripple-lib').UInt256;
|
||||
var sjcl = require('ripple-lib').sjcl;
|
||||
var online = process.argv.indexOf('--online-blobvault') !== -1 ? true : false;
|
||||
|
||||
var exampleData = {
|
||||
id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
|
||||
crypt: 'f0cc91a7c1091682c245cd8e13c246cc150b2cf98b17dd6ef092019c99dc9d82',
|
||||
unlock: '3e15fe3218a9c664835a6f585582e14480112110ddbe50e5028d05fc5bd9b5f4',
|
||||
username: 'exampleUser',
|
||||
new_username : 'exampleUser-rename',
|
||||
password: 'pass word',
|
||||
domain: 'staging.ripple.com',
|
||||
masterkey : 'ssize4HrSYZShMWBtK6BhALGEk8VH',
|
||||
email_token : '77825040-9096-4695-9cbc-76720f6a8649',
|
||||
activateLink : 'https://staging.ripple.com/client/#/register/activate/',
|
||||
device_id : "ac1b6f6dbca98190eb9687ba06f0e066",
|
||||
identity_id : "17fddb71-a5c2-44ce-8b50-4b381339d4f2",
|
||||
blob: {
|
||||
url: 'https://id.staging.ripple.com',
|
||||
id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
|
||||
key: 'f0cc91a7c1091682c245cd8e13c246cc150b2cf98b17dd6ef092019c99dc9d82',
|
||||
data: {
|
||||
auth_secret: 'd0aa918e693080a6a8d0ddc7f4dcf4bc0eecc3c3e3235f16a98661ee9c2e7a58',
|
||||
account_id: 'raVUps4RghLYkVBcpMaRbVKRTTzhesPXd',
|
||||
email: 'example@example.com',
|
||||
contacts: [ ],
|
||||
created: '2014-05-20T23:39:52.538Z',
|
||||
apps: [ ],
|
||||
lastSeenTxDate: 1401925490000,
|
||||
identityVault: { },
|
||||
revision: 2199,
|
||||
encrypted_secret: 'APYqtqvjJk/J324rx2BGGzUiQ3mtmMMhMsbrUmgxb00W2aFVQzCC2mqd58Z17gzeUUcjtjAm'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var rippleTxtRes = "[authinfo_url]\r\nhttps://id.staging.ripple.com/v1/authinfo";
|
||||
|
||||
var authInfoRes = {
|
||||
body : {
|
||||
version: 3,
|
||||
blobvault: 'https://id.staging.ripple.com',
|
||||
pakdf: {
|
||||
modulus: 'c7f1bc1dfb1be82d244aef01228c1409c1988943ca9e21431f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd',
|
||||
alpha: '7283d19e784f48a96062271a5fa6e2c3addf14e6ezf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa',
|
||||
url: 'https://auth1.ripple.com/api/sign',
|
||||
exponent: '010001',
|
||||
host: 'auth1.ripple.com'
|
||||
},
|
||||
exists: true,
|
||||
username: 'exampleUser',
|
||||
address: 'raVUps4RghLYkVBcpMaRbVKRTTzhesPXd',
|
||||
emailVerified: true,
|
||||
reserved: false
|
||||
}
|
||||
};
|
||||
|
||||
var authInfoNewUsernameRes = {
|
||||
body : {
|
||||
version: 3,
|
||||
blobvault: 'https://id.staging.ripple.com',
|
||||
pakdf: {
|
||||
modulus: 'c7f1bc1dfb1be82d244aef01228c1409c1988943ca9e21431f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd',
|
||||
alpha: '7283d19e784f48a96062271a5fa6e2c3addf14e6ezf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa',
|
||||
url: 'https://auth1.ripple.com/api/sign',
|
||||
exponent: '010001',
|
||||
host: 'auth1.ripple.com'
|
||||
},
|
||||
exists: false,
|
||||
username: exampleData.new_username,
|
||||
emailVerified: false,
|
||||
reserved: false
|
||||
}
|
||||
};
|
||||
|
||||
var signRes = '{"result":"success","signres":"64e9e46618fff0b720b8162e6caa209e046af128b929b766d3be421d3f048ba523453dad42597dcec01f23a5080c16695f6209d39a03668d46b782409e4a53821f70b5e6f7c8fd28eb641c504f9f9b2f378bf2ea7f19950790ac6a8832e2659800f5bb06b735bd450fa47b499fbcebeb3b0fc327619dd2171fa40fb0a41d9bcd69dd29567fa94e9466d4674b908f1cfc43822b38b94534cb37eead183b11b33761a73d78be6ba6f3a53291d4154ca0891fa59da58380e05a1e85b15a24d12406795385bcc5a6360a24ecbf068ff6f02097cd917281972d4895769f3a8668b852ea5d4232050200bcd03934f49ea0693d832980614dff1ead67ca2e0ce9073c25","modulus":"c7f1bc1dfb1be82d244aef01228c1409c198894eca9e21430f1669b4aa3864c9f37f3d51b2b4ba1ab9e80f59d267fda1521e88b05117993175e004543c6e3611242f24432ce8efa3b81f0ff660b4f91c5d52f2511a6f38181a7bf9abeef72db056508bbb4eeb5f65f161dd2d5b439655d2ae7081fcc62fdcb281520911d96700c85cdaf12e7d1f15b55ade867240722425198d4ce39019550c4c8a921fc231d3e94297688c2d77cd68ee8fdeda38b7f9a274701fef23b4eaa6c1a9c15b2d77f37634930386fc20ec291be95aed9956801e1c76601b09c413ad915ff03bfdc0b6b233686ae59e8caf11750b509ab4e57ee09202239baee3d6e392d1640185e1cd","alpha":"7283d19e784f48a96062271a4fa6e2c3addf14e6edf78a4bb61364856d580f13552008d7b9e3b60ebd9555e9f6c7778ec69f976757d206134e54d61ba9d588a7e37a77cf48060522478352d76db000366ef669a1b1ca93c5e3e05bc344afa1e8ccb15d3343da94180dccf590c2c32408c3f3f176c8885e95d988f1565ee9b80c12f72503ab49917792f907bbb9037487b0afed967fefc9ab090164597fcd391c43fab33029b38e66ff4af96cbf6d90a01b891f856ddd3d94e9c9b307fe01e1353a8c30edd5a94a0ebba5fe7161569000ad3b0d3568872d52b6fbdfce987a687e4b346ea702e8986b03b6b1b85536c813e46052a31ed64ec490d3ba38029544aa","exponent":"010001"}';
|
||||
|
||||
var blobRes = {
|
||||
body : {
|
||||
result: 'success',
|
||||
encrypted_secret: 'APYqtqvjJk/J324rx2BGGzUiQ3mtmMMhMsbrUmgxb00W2aFVQzCC2mqd58Z17gzeUUcjtjAm',
|
||||
blob: 'ALXga/k8mgvPpZCY0zJZdaqlptHUBL0E4V/90p4edvb7eCucU2M7aFsHIl3Z3UDu9MdlDnDU42/C+YKL1spkSTPb3rGWr0kXIFmRu8xDAd+OA3Ot7u3OBq0sN2BUHbEc47WiCue84XQHTgBh9tdeiRTqm90LJ7hZ1pD0oqr823YpFguwcC1inxFbSTNxIdWSoC3XCqZtRFM2Y5ALleWhaWKc3OwaFU6yPRcW05IBvTY/7a2SfZyklvXnJh7Bg+vfvz7ms8UCybmBgHlBPY/UqGOdZI6iFGrEQrDMFHgbxwf7bTTiaOM7Su3OsqhM1k90LvQgk3b1olb1VIMZ5J1UuTtOVTpLSsIlzgMvxxdUUyN2zMkeDE3t8kHOThhwWbLG6O+s9F9fktIv4NtoAm0dG9LtkSE1YXajk0qIYr/zrblJy7pEvNv+EzdSr+dpvssmPshgwxoHwvCwae0vL7UTmrCxIWLlHbsbU2uAzgvudJL0WOpX4W+R43U3sgMD2XysKgX783Sa7DLUWCk3Rk9eGp7c3k/XpI0IWvuKzMxID8VzdyMmXP0RE77uUufisBDtwr8gPGzS5kzU3Z/FG/dHJkfBLZdbHOffQTPKO9DKUjztWpx7CTAkN9O21XrdLK1FRAtWFTuvlA66sDYtHRaqglzFjt9DsJk7PAKi1odHeLBmYob/Bs5eK9yNnlLwu3JpHLH/jKxkuxcZ3NEdTm1WPjTdNlvT7kdGAIG9c0vIywEABkQh1kPDOe39h3GRGcUqVWJcMjJmdVDrQH7BBVV+VptCVtMOo1LviaD0MIWMiYdZyGeH5x+FkpAMjKDB3cCUkmxmis8lrDiMlnTZ5Czj+bDPp62Looc7cr2pTR2niFZRosYNgUPx6cAh7tn64RDaa/spAyv0mWyD1qRA8H0sEPmC7m7EPaBIQpODh1NFg/Bxunh+QGSmy9deINB78b9A9zLS6qWljrzg5fMDUN66xRUUKJMSD9+QJePsM4pb60vbnBBtbe04JzY7iOc/CxiT0Px6/1jlSmnY6SCtaFqtDgmQ5MLGTm1tA+aj6caT6FWsXrBboXt3eXRDPHTN+ciKELx7M3dpd4mKVWhBu7nnnVMEu1rSUrmtUStXQHod/C7vVRF2EU1hhTW7ou0hvLn+7xs9B76QeVG7iYFLiZH1qgs+upqnLCnmY3ug9yd9GQ6YwbVL1hbXJLadaOg7qhKst0KXjjjcE4G9AIEyI+UCxGdc/0PNPOCCeYEPshvonCgElGo/fAbaIuSSOFfusiovYffJ0rCkq1RagH0R/llWtUEFEDR5YFVlD3DqK6B22fQK',
|
||||
revision: 2191,
|
||||
email: 'example@example.com',
|
||||
quota: -2975,
|
||||
patches: []
|
||||
}
|
||||
};
|
||||
|
||||
var recoverRes = {
|
||||
body: {
|
||||
encrypted_secret: 'AAd69B9En2OF4O4LsjD+pFNeJHEGIuLh2hbla58zGvN7qU/16bDfy0QlFj8/Gu++AdFwH5U6',
|
||||
revision: 2403,
|
||||
blob_id: 'ef203d3e76552c0592384f909e6f61f1d1f02f61f07643ce015d8b0c9710dd2f',
|
||||
blob: 'AFfW9vuHJ2J5UMnEl4WrVIT9z2d+PPVNNHkqzN64b3pKDQcRPFp8vVEqL9B+YVs/KHhFVFNxxCNyVXwO/yGg4BAslYl8Ioo11IODmOltJmb94oKR/JVyfaY4bDWaOzAoa5N/c9LHpmd0L+9igK1o260MK5OZW4BQ6EG7I+8cYi5uM2CLguiddySu2yTEnyHW47zspWP33y2deh6p5mHtLdii/tmlm7b2rKpzrRVuLN/J09jqilhMxlCEr4X065YZLlQapJ45UWvpifejEw/6Qgl1WngZxwifHa504aR/QYhb1XCNeYbkjQ1MmkTmTef47Al4r/Irzoe//pDbAFA70XXkBUVUMAXWiOxU5V6gHO4yhXbTFEn7922JZlY7PIjo2Q+BxLkozMzuh8MZdoeadqffZX1fOuyTRWfPlqi7vIYgnUyTmThKe2EZv1LsB5ZUaX3KSArKDv1xPTKS0nexGNZoFckwEfVr6B2PGbMx8LPLYEEEmd95kh8NAKN1wkOPuBehLAtbMtcnLpTsotY6diqWdW4V9BSst0KDMTxZVfeesWD7/7ga9hzNvAWO1MN3aAvDCiQVufb44i4Qfu6fLS7+nxtcDCN2PqPHcANcW0cUhUNB50ajzNwRXN8B92CiY0zkS61CzWeooHOslGp0Acau1CJy8iHGyjzbPS4ui8F2h2TbDUuInOoMqiRjXFvRTxA=',
|
||||
encrypted_blobdecrypt_key: 'AA9vUokfQ1WXEOArl2DUwY3cxgXGKj9uNEqrJQzUu0hqXIWRu1V+6l1qqxXKPnm9BNscMpm0BMSbxUz++lfV50c1B4akvrzIBH+MUUgNyyPcHR7JBgjEYt0=',
|
||||
patches: [],
|
||||
result: 'success'
|
||||
}
|
||||
}
|
||||
|
||||
var getProfileRes = {
|
||||
"result":"success",
|
||||
"addresses":[],
|
||||
"attributes":[{
|
||||
"attribute_id":"4034e477-ffc9-48c4-bcbc-058293f081d8",
|
||||
"identity_id":"17fddb71-a5c2-44ce-8b50-4b381339d4f2",
|
||||
"name":"email",
|
||||
"type":"default",
|
||||
"domain":null,
|
||||
"value":"example@example.com",
|
||||
"visibility":"public",
|
||||
"updated":null
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var blob = new Blob();
|
||||
blob.url = exampleData.blob.url;
|
||||
blob.id = exampleData.blob.id;
|
||||
blob.device_id = exampleData.device_id;
|
||||
blob.key = exampleData.blob.key;
|
||||
blob.identity_id = exampleData.blob.identity_id;
|
||||
blob.data = exampleData.blob.data;
|
||||
blob.revision = exampleData.blob.data.revision;
|
||||
|
||||
//must be set for self signed certs
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
while(!sjcl.random.isReady()) {
|
||||
sjcl.random.addEntropy(require('crypto').randomBytes(128).toString('base64')); //add entropy to seed the generator
|
||||
}
|
||||
|
||||
var mockRippleTxt;
|
||||
var mockRippleTxt2;
|
||||
var mockAuthSign;
|
||||
var mockRegister;
|
||||
var mockBlob;
|
||||
var mockRename;
|
||||
var mockUpdate;
|
||||
var mockRecover;
|
||||
var mockVerify;
|
||||
var mockEmail;
|
||||
var mockProfile;
|
||||
var mockDelete;
|
||||
|
||||
if (!online) {
|
||||
mockRippleTxt = nock('https://ripple.com')
|
||||
.get('/ripple.txt')
|
||||
.reply(200, rippleTxtRes, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
|
||||
mockRippleTxt2 = nock('https://' + exampleData.domain)
|
||||
.get('/ripple.txt')
|
||||
.reply(200, rippleTxtRes, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
|
||||
mockAuthSign = nock('https://auth1.ripple.com')
|
||||
.persist()
|
||||
.post('/api/sign')
|
||||
.reply(200, signRes, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
|
||||
mockRegister = nock('https://id.staging.ripple.com');
|
||||
mockRegister.filteringPath(/(v1\/user\?signature(.+))/g, 'register/')
|
||||
.post('/register/')
|
||||
.reply(200, { result: 'error', message: 'User already exists' }, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockDelete = nock('https://id.staging.ripple.com');
|
||||
mockDelete.filteringPath(/(v1\/user\/(.+))/g, 'delete/')
|
||||
.delete('/delete/')
|
||||
.reply(200, { result: 'success' }, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockBlob = nock('https://id.staging.ripple.com');
|
||||
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.username.toLowerCase())
|
||||
.reply(200, JSON.stringify(authInfoRes.body), {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.new_username.toLowerCase())
|
||||
.reply(200, JSON.stringify(authInfoNewUsernameRes.body), {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockBlob.filteringPath(/(blob\/.+)/g, 'blob/')
|
||||
.persist()
|
||||
.get('/v1/blob/')
|
||||
.reply(200, JSON.stringify(blobRes.body), {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockRename = nock('https://id.staging.ripple.com/v1/user/');
|
||||
mockRename.filteringPath(/((.+)\/rename(.+))/g, 'rename/')
|
||||
.post('rename/')
|
||||
.reply(200, {result:'success',message:'rename'}, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockUpdate = nock('https://id.staging.ripple.com/v1/user/');
|
||||
mockUpdate.filteringPath(/((.+)\/updatekeys(.+))/g, 'update/')
|
||||
.post('update/')
|
||||
.reply(200, {result:'success',message:'updateKeys'}, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockRecover = nock('https://id.staging.ripple.com/')
|
||||
mockRecover.filteringPath(/((.+)user\/recov\/(.+))/g, 'recov/')
|
||||
.get('recov/')
|
||||
.reply(200, recoverRes.body, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockVerify = nock('https://id.staging.ripple.com/v1/user/');
|
||||
mockVerify.filteringPath(/((.+)\/verify(.+))/g, 'verify/')
|
||||
.get('verify/')
|
||||
.reply(200, {result:'error', message:'invalid token'}, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
mockEmail = nock('https://id.staging.ripple.com/v1/user');
|
||||
mockEmail.filteringPath(/((.+)\/email(.+))/g, 'email/')
|
||||
.post('email/')
|
||||
.reply(200, {result:'success'}, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
describe('Ripple Txt', function () {
|
||||
it('should get the content of a ripple.txt file from a given domain', function(done) {
|
||||
RippleTxt.get(exampleData.domain, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get currencies from a ripple.txt file for a given domain', function(done) {
|
||||
RippleTxt.getCurrencies(exampleData.domain, function(err, currencies) {
|
||||
assert.ifError(err);
|
||||
assert(Array.isArray(currencies));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get the domain from a given url', function() {
|
||||
var domain = RippleTxt.extractDomain("http://www.example.com");
|
||||
assert.strictEqual(typeof domain, 'string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('AuthInfo', function() {
|
||||
it('should get auth info', function(done) {
|
||||
AuthInfo.get(exampleData.domain, exampleData.username, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
Object.keys(authInfoRes.body).forEach(function(prop) {
|
||||
assert(resp.hasOwnProperty(prop));
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('VaultClient', function () {
|
||||
var client = new VaultClient(exampleData.domain);
|
||||
|
||||
describe('#initialization', function() {
|
||||
it('should be initialized with a domain', function() {
|
||||
var client = new VaultClient({ domain: exampleData.domain });
|
||||
assert.strictEqual(client.domain, exampleData.domain);
|
||||
});
|
||||
|
||||
it('should default to ripple.com without a domain', function () {
|
||||
var client = new VaultClient();
|
||||
assert.strictEqual(client.domain, 'ripple.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#exists', function() {
|
||||
it('should determine if a username exists on the domain', function(done) {
|
||||
this.timeout(10000);
|
||||
client.exists(exampleData.username, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'boolean');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#login', function() {
|
||||
it('with username and password should retrive the blob, crypt key, and id', function(done) {
|
||||
this.timeout(10000);
|
||||
client.login(exampleData.username, exampleData.password, exampleData.device_id, function(err, resp) {
|
||||
if (online) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert(resp.blob instanceof Blob);
|
||||
assert.strictEqual(typeof resp.blob.id, 'string');
|
||||
assert(UInt256.from_json(resp.blob.id).is_valid());
|
||||
assert.strictEqual(typeof resp.blob.key, 'string');
|
||||
assert(UInt256.from_json(resp.blob.key).is_valid());
|
||||
assert.strictEqual(typeof resp.username, 'string');
|
||||
assert.strictEqual(typeof resp.verified, 'boolean');
|
||||
} else {
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(resp, void(0));
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#relogin', function() {
|
||||
it('should retrieve the decrypted blob with blob vault url, id, and crypt key', function(done) {
|
||||
this.timeout(10000);
|
||||
client.relogin(exampleData.blob.url, exampleData.id, exampleData.crypt, exampleData.device_id, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert(resp.blob instanceof Blob);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#unlock', function() {
|
||||
it('should access the wallet secret using encryption secret, username and password', function (done) {
|
||||
this.timeout(10000);
|
||||
client.unlock(exampleData.username, exampleData.password, exampleData.blob.data.encrypted_secret, function(err, resp) {
|
||||
if (online) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.keys, 'object');
|
||||
assert.strictEqual(typeof resp.keys.unlock, 'string');
|
||||
assert(UInt256.from_json(resp.keys.unlock).is_valid());
|
||||
} else {
|
||||
assert.strictEqual(err.toString(), 'CORRUPT: ccm: tag doesn\'t match');
|
||||
assert.strictEqual(resp, void(0));
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#loginAndUnlock', function () {
|
||||
it('should get the decrypted blob and decrypted secret given name and password', function (done) {
|
||||
this.timeout(10000);
|
||||
client.loginAndUnlock(exampleData.username, exampleData.password, exampleData.device_id, function(err, resp) {
|
||||
if (online) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert(resp.blob instanceof Blob);
|
||||
assert.strictEqual(typeof resp.blob.id, 'string');
|
||||
assert(UInt256.from_json(resp.blob.id).is_valid());
|
||||
assert.strictEqual(typeof resp.blob.key, 'string');
|
||||
assert(UInt256.from_json(resp.blob.key).is_valid());
|
||||
assert.strictEqual(typeof resp.unlock, 'string');
|
||||
assert(UInt256.from_json(resp.unlock).is_valid());
|
||||
assert.strictEqual(typeof resp.secret, 'string');
|
||||
assert.strictEqual(typeof resp.username, 'string');
|
||||
assert.strictEqual(typeof resp.verified, 'boolean');
|
||||
} else {
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(resp, void(0));
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#register', function () {
|
||||
it('should create a new blob', function (done) {
|
||||
this.timeout(10000);
|
||||
var options = {
|
||||
username : exampleData.username,
|
||||
password : exampleData.password,
|
||||
email : exampleData.blob.data.email,
|
||||
activateLink : exampleData.activateLink
|
||||
}
|
||||
|
||||
client.register(options, function(err, resp) {
|
||||
|
||||
//fails, user already exists
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(resp, void(0));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#deleteBlob', function () {
|
||||
it('should remove an existing blob', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
var options = {
|
||||
url : exampleData.blob.url,
|
||||
blob_id : exampleData.blob.id,
|
||||
username : online ? "" : exampleData.username,
|
||||
account_id : exampleData.blob.data.account_id,
|
||||
masterkey : exampleData.masterkey
|
||||
}
|
||||
|
||||
client.deleteBlob(options, function(err, resp) {
|
||||
if (online) {
|
||||
//removing the username will result in an error from the server
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(resp, void(0));
|
||||
} else {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
describe('#updateProfile', function () {
|
||||
it('should update profile parameters associated with a blob', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
var options = {
|
||||
url : exampleData.blob.url,
|
||||
blob_id : exampleData.blob.id,
|
||||
username : exampleData.username,
|
||||
auth_secret : exampleData.blob.data.auth_secret,
|
||||
profile : {
|
||||
city : "San Francisco",
|
||||
phone : "555-555-5555"
|
||||
}
|
||||
}
|
||||
|
||||
client.updateProfile(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('Blob', function () {
|
||||
var client;
|
||||
var resp;
|
||||
|
||||
client = new VaultClient({ domain: exampleData.domain });
|
||||
|
||||
before(function(done) {
|
||||
if (online) {
|
||||
this.timeout(10000);
|
||||
|
||||
client.login(exampleData.username, exampleData.password, exampleData.device_id, function(err, res) {
|
||||
resp = res;
|
||||
blob = res.blob;
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
|
||||
mockBlob.filteringPath(/(blob\/.+)/g, 'blob/')
|
||||
.persist()
|
||||
.post('/v1/blob/')
|
||||
.reply(200, {result:'success'}, {
|
||||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
describe('#rename', function () {
|
||||
it('should change the username of a blob', function (done) {
|
||||
this.timeout(20000);
|
||||
|
||||
var options = {
|
||||
username : exampleData.username,
|
||||
new_username : exampleData.new_username,
|
||||
password : exampleData.password,
|
||||
masterkey : exampleData.masterkey,
|
||||
blob : blob
|
||||
}
|
||||
|
||||
|
||||
client.rename(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
assert.strictEqual(typeof resp.message, 'string');
|
||||
|
||||
if (online) {
|
||||
options.username = exampleData.new_username;
|
||||
options.new_username = exampleData.username;
|
||||
|
||||
//change it back
|
||||
client.rename(options, function(err,resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
assert.strictEqual(typeof resp.message, 'string');
|
||||
done();
|
||||
});
|
||||
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#changePassword', function () {
|
||||
it('should change the password and keys of a blob', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
var options = {
|
||||
username : exampleData.username,
|
||||
password : exampleData.password,
|
||||
masterkey : exampleData.masterkey,
|
||||
blob : blob
|
||||
}
|
||||
|
||||
client.changePassword(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
assert.strictEqual(typeof resp.message, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#recoverBlob', function () {
|
||||
it('should recover the blob given a username and secret', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
var options = {
|
||||
url : exampleData.blob.url,
|
||||
username : exampleData.username,
|
||||
masterkey : exampleData.masterkey,
|
||||
}
|
||||
|
||||
client.recoverBlob(options, function(err, blob) {
|
||||
assert.ifError(err);
|
||||
assert(blob instanceof Blob);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#verifyEmail', function () {
|
||||
it('should verify an email given a username and token', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
client.verify(exampleData.username, exampleData.email_token, function(err, resp) {
|
||||
//result will be error, because of invalid token
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(resp, void(0));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resendVerifcationEmail', function () {
|
||||
it('should resend a verification given options', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
var options = {
|
||||
url : exampleData.blob.url,
|
||||
id : exampleData.blob.id,
|
||||
username : exampleData.username,
|
||||
account_id : exampleData.blob.data.account_id,
|
||||
email : exampleData.blob.data.email,
|
||||
activateLink : exampleData.activateLink,
|
||||
masterkey : exampleData.masterkey
|
||||
}
|
||||
client.resendEmail(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('#set', function(done) {
|
||||
this.timeout(10000)
|
||||
blob.extend('/testObject', {
|
||||
foo: [],
|
||||
}, function(err, resp) {
|
||||
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#extend', function(done) {
|
||||
this.timeout(10000)
|
||||
blob.extend('/testObject', {
|
||||
foobar: 'baz',
|
||||
}, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#unset', function(done) {
|
||||
this.timeout(10000)
|
||||
blob.unset('/testObject', function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#unshift', function(done) {
|
||||
this.timeout(10000)
|
||||
blob.unshift('/testArray', {
|
||||
name: 'bob',
|
||||
address: '1234'
|
||||
}, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#filter', function(done) {
|
||||
this.timeout(10000)
|
||||
|
||||
blob.filter('/testArray', 'name', 'bob', 'extend', '', {description:'Alice'}, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#consolidate', function(done) {
|
||||
this.timeout(10000)
|
||||
blob.unset('/testArray', function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
blob.consolidate(function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('identity', function() {
|
||||
it('#identity_set', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
blob.identity.set('address', exampleData.unlock, {city:"San Francisco", region:"CA"}, function (err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#identity_get', function () {
|
||||
var property = blob.identity.get('address', exampleData.unlock);
|
||||
assert.ifError(property.error);
|
||||
assert.strictEqual(typeof property.encrypted, 'boolean');
|
||||
assert.notEqual(typeof property.value, 'undefined');
|
||||
});
|
||||
|
||||
it('#identity_getAll', function () {
|
||||
var obj = blob.identity.getAll(exampleData.unlock);
|
||||
assert.strictEqual(typeof obj, 'object');
|
||||
});
|
||||
|
||||
it('#identity_getFullAddress', function () {
|
||||
var address = blob.identity.getFullAddress(exampleData.unlock);
|
||||
assert.strictEqual(typeof address, 'string');
|
||||
});
|
||||
|
||||
it('#identity_unset', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
blob.identity.unset('name', exampleData.unlock, function (err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('identityVault', function() {
|
||||
it('#identity - Get Attestation', function (done) {
|
||||
var options = {
|
||||
url : blob.url,
|
||||
auth_secret : blob.data.auth_secret,
|
||||
blob_id : blob.id,
|
||||
};
|
||||
|
||||
options.type = 'identity';
|
||||
|
||||
nock('https://id.staging.ripple.com')
|
||||
.filteringPath(/(v1\/attestation\/identity(.+))/g, '')
|
||||
.post('/')
|
||||
.reply(200, {
|
||||
result: 'success',
|
||||
status: 'verified',
|
||||
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
|
||||
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
|
||||
}, {'Content-Type': 'application/json'});
|
||||
|
||||
client.getAttestation(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
assert.strictEqual(typeof resp.attestation, 'string');
|
||||
assert.strictEqual(typeof resp.blinded, 'string');
|
||||
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#identity - Update Attestation', function (done) {
|
||||
|
||||
var options = {
|
||||
url : blob.url,
|
||||
auth_secret : blob.data.auth_secret,
|
||||
blob_id : blob.id,
|
||||
};
|
||||
|
||||
options.type = 'identity';
|
||||
|
||||
nock('https://id.staging.ripple.com')
|
||||
.filteringPath(/(v1\/attestation\/identity\/update(.+))/g, '')
|
||||
.post('/')
|
||||
.reply(200, {
|
||||
result: 'success',
|
||||
status: 'verified',
|
||||
attestation: 'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig',
|
||||
blinded:'eyJ6IjoieiJ9.eyJ6IjoieiJ9.sig'
|
||||
}, {'Content-Type': 'application/json'});
|
||||
|
||||
client.updateAttestation(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
assert.strictEqual(typeof resp.attestation, 'string');
|
||||
assert.strictEqual(typeof resp.blinded, 'string');
|
||||
assert.deepEqual(resp.decoded, {"header":{"z":"z"},"payload":{"z":"z"},"signature":"sig"})
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#identity - Get Attestation Summary', function (done) {
|
||||
|
||||
var options = {
|
||||
url : blob.url,
|
||||
auth_secret : blob.data.auth_secret,
|
||||
blob_id : blob.id,
|
||||
};
|
||||
|
||||
nock('https://id.staging.ripple.com')
|
||||
.filteringPath(/(v1\/attestation\/summary(.+))/g, '')
|
||||
.get('/')
|
||||
.reply(200, {
|
||||
result: 'success',
|
||||
attestation: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IjY2ZGI3MzgxIn0%3D.eyJwcm9maWxlX3ZlcmlmaWVkIjpmYWxzZSwiaWRlbnRpdHlfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2lkLnJpcHBsZS5jb20iLCJzdWIiOiIwNDMzNTA0ZS0yYTRmLTQ1NjktODQwMi1lYWI2YTU0YTgzYjUiLCJleHAiOjE0MTI4MTc2NjksImlhdCI6MTQxMjgxNTgwOX0%3D.Jt14Y2TsM7fKqGWn0j16cPldlYqRr7%2F2dptBsdZuZhRGRTREO4TSpZZhBaU95WL3M9eXIfaoSs8f2pTOa%2BBGAYHZSZK4%2FLqeWdDH8zz8Bx9YFqGije1KmHQR%2FeoWSp1GTEfcq5Oho4nSHozHhGNN8IrDkl8woMvWb%2FE1938Y5Zl2vyv7wjlNUF4ND33XWzJkvQjzIK15uYfaB%2FUIsNW32udfHAdkigesdMDNm%2BRGBqHMDZeAMdVxzrDzE3m8oWKDMJXbcaLmk75COfJrLWYiZCHd7VcReyPEZegwEucetZJ9uDnoBcvw0%2B6hIRmjTN6Gy1eeBoJaiDYsWuOwInbIlw%3D%3D',
|
||||
}, {'Content-Type': 'application/json'});
|
||||
|
||||
client.getAttestationSummary(options, function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resp.result, 'success');
|
||||
assert.strictEqual(typeof resp.attestation, 'string');
|
||||
assert.strictEqual(typeof resp.decoded.header, 'object');
|
||||
assert.strictEqual(typeof resp.decoded.payload, 'object');
|
||||
assert.strictEqual(typeof resp.decoded.signature, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//only do these offline
|
||||
if (!online) {
|
||||
|
||||
describe('2FA', function() {
|
||||
|
||||
it('#2FA_set2FA', function (done) {
|
||||
blob.set2FA({masterkey:exampleData.masterkey}, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#2FA_get2FA', function (done) {
|
||||
blob.get2FA(function(err, resp) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#2FA_requestToken', function (done) {
|
||||
client.requestToken(exampleData.blob.url, exampleData.blob.id, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#2FA_verifyToken', function (done) {
|
||||
var options = {
|
||||
url : exampleData.blob.url,
|
||||
id : exampleData.blob.id,
|
||||
device_id : client.generateDeviceID(),
|
||||
token : "5555",
|
||||
remember_me : true
|
||||
}
|
||||
|
||||
client.verifyToken(options, function(err, resp){
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(typeof resp, 'object');
|
||||
assert.strictEqual(typeof resp.result, 'string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!online) {
|
||||
after(function () {
|
||||
nock.restore();
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user