Merge pull request #103 from shekenahglory/develop

[FEATURE] vault client: updateProfile and deleteBlob
This commit is contained in:
wltsmrz
2014-06-23 11:09:30 -07:00
4 changed files with 257 additions and 75 deletions

View File

@@ -11,6 +11,14 @@ data stored using credentials originally obtained at ripple.com
## Vault Client Usage
vaultClient = new ripple.VaultClient(domain);
vaultClient.getAuthInfo(username, callback);
vaultClient.getRippleName(address, url, callback);
vaultClient.exists(username, callback);
vaultClient.login(username, password, callback);
@@ -20,20 +28,24 @@ data stored using credentials originally obtained at ripple.com
vaultClient.loginAndUnlock(username, password, callback);
vaultClient.exists(username, callback);
vaultClient.register(options, callback);
vaultClient.deleteBlob(options, callback);
vaultClient.recoverBlob(options, callback);
vaultClient.rename(options, callback);
vaultClient.changePassword(options, callback);
vaultClient.recoverBlob(options, callback);
vaultClient.verify(username, token, callback);
vaultClient.resendEmail(options, callback);
vaultClient.updateProfile(options, fn);
# Blob Methods
@@ -102,48 +114,55 @@ data stored using credentials originally obtained at ripple.com
Run `npm test` to test the high-level behavior specs
Ripple Txt
✓ should get the context of a ripple.txt file from a given domain
Ripple Txt
✓ should get the content of a ripple.txt file from a given domain
✓ should get currencies from a ripple.txt file for a given domain
✓ should get the domain from a given url
AuthInfo
✓ should get authinfo given a domain and username
AuthInfo
✓ should get auth info
Ripple Vault Client
#initialization
✓ should be initialized with a domain
✓ should default to ripple.com without a domain
#login
✓ with username and password should retrive the blob, crypt key, and id
#relogin
✓ should retrieve the decrypted blob with id and crypt key
#unlock
✓ should access the wallet secret using encryption secret, username and password
#loginAndUnlock
✓ should get the decrypted blob and decrypted secret given name and password
VaultClient
#initialization
✓ should be initialized with a domain
✓ should default to ripple.com without a domain
#exists
✓ should determine if a username exists on the domain
#login
with username and password should retrive the blob, crypt key, and id
#relogin
✓ should retrieve the decrypted blob with blob vault url, id, and crypt key
#unlock
✓ should access the wallet secret using encryption secret, username and password
#loginAndUnlock
✓ should get the decrypted blob and decrypted secret given name and password
#register
✓ should create a new blob
#deleteBlob
✓ should remove an existing blob
#updateProfile
✓ should update profile parameters associated with a blob
Blob
#set
should set a new property in the blob
#extend
should extend an object in the blob
#unset
✓ should remove a property from the blob
#unshift
✓ should prepend an item to an array in the blob
#filter
✓ should find a specific entity in an array and apply subcommands to it
#consolidate
✓ should consolidate and save changes to the blob
#identity_set
✓ should set an identity property
#identity_get
✓ should retreive an identity property given the property name and encryption key
#identity_getAll
✓ should retreive all identity properties given the encryption key
#identity_getFullAddress
✓ should retreive the address as a string
#identity_unset
✓ should remove an identity property
Blob
#set
#extend
#unset
#unshift
#filter
#consolidate
#rename
✓ should change the username of a blob
#changePassword
✓ should change the password and keys of a blob
#recoverBlob
✓ should recover the blob given a username and secret
#verifyEmail
✓ should verify an email given a username and token
#resendVerifcationEmail
✓ should resend a verification given options
identity
#identity_set
#identity_get
#identity_getAll
#identity_getFullAddress
#identity_unset

View File

@@ -1,9 +1,9 @@
var crypt = require('./crypt').Crypt;
var SignedRequest = require('./signedrequest').SignedRequest;
var request = require('superagent');
var extend = require("extend");
var async = require("async");
var request = require('superagent');
var extend = require("extend");
var async = require("async");
var log = require('./log').sub('blob');
var BlobClient = {};
//Blob object class
@@ -865,12 +865,12 @@ BlobClient.resendEmail = function (opts, fn) {
.send(signed.data)
.end(function(err, resp) {
if (err) {
console.log("blob: could not resend the token:", err);
log.error("resendEmail:", err);
fn(new Error("Failed to resend the token"));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body && resp.body.result === 'error') {
console.log("blob: could not resend the token:", resp.body.message);
log.error("resendEmail:", resp.body.message);
fn(new Error("Failed to resend the token"));
} else {
fn(new Error("Failed to resend the token"));
@@ -944,6 +944,39 @@ BlobClient.recoverBlob = function (opts, fn) {
};
};
/**
* updateProfile
* update information stored outside the blob
*/
BlobClient.updateProfile = function (opts, fn) {
var config = {
method: 'POST',
url: opts.url + '/v1/user/' + opts.username + '/profile',
dataType: 'json',
data: opts.profile
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signHmac(opts.auth_secret, opts.blob_id);
request.post(signed.url)
.send(signed.data)
.end(function(err, resp) {
if (err) {
log.error('updateProfile:', err);
fn(new Error('Failed to update profile - XHR error'));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body) {
log.error('updateProfile:', resp.body);
} else {
fn(new Error('Failed to update profile'));
}
});
};
/**
* updateKeys
* Change the blob encryption keys
@@ -979,10 +1012,10 @@ BlobClient.updateKeys = function (opts, fn) {
.send(signed.data)
.end(function(err, resp) {
if (err) {
console.log("blob: could not update blob:", err);
log.error("updateKeys:", err);
fn(new Error('Failed to update blob - XHR error'));
} else if (!resp.body || resp.body.result !== 'success') {
console.log("blob: could not update blob:", resp.body ? resp.body.message : null);
log.error("updateKeys:", resp.body ? resp.body.message : null);
fn(new Error('Failed to update blob - bad result'));
} else {
fn(null, resp.body);
@@ -1027,12 +1060,12 @@ BlobClient.rename = function (opts, fn) {
.send(signed.data)
.end(function(err, resp) {
if (err) {
console.log("blob: could not rename:", err);
log.error("rename:", err);
fn(new Error("Failed to rename"));
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body && resp.body.result === 'error') {
console.log("blob: could not rename:", resp.body.message);
log.error("rename:", resp.body.message);
fn(new Error("Failed to rename"));
} else {
fn(new Error("Failed to rename"));
@@ -1110,4 +1143,32 @@ BlobClient.create = function(options, fn) {
});
};
/**
* deleteBlob
*/
BlobClient.deleteBlob = function(options, fn) {
var config = {
method : 'DELETE',
url : options.url + '/v1/user/' + options.username,
};
var signedRequest = new SignedRequest(config);
var signed = signedRequest.signAsymmetric(options.masterkey, options.account_id, options.blob_id);
request.del(signed.url)
.end(function(err, resp) {
if (err) {
fn(err);
} else if (resp.body && resp.body.result === 'success') {
fn(null, resp.body);
} else if (resp.body && resp.body.result === 'error') {
fn(new Error(resp.body.message));
} else {
fn(new Error('Could not delete blob'));
}
});
};
exports.BlobClient = BlobClient;

View File

@@ -105,6 +105,23 @@ VaultClient.prototype.getRippleName = function(address, url, 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
*
@@ -334,23 +351,6 @@ VaultClient.prototype.loginAndUnlock = function(username, password, fn) {
};
};
/**
* 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);
}
});
};
/**
* Verify an email address for an existing user
*
@@ -387,6 +387,39 @@ VaultClient.prototype.resendEmail = function (options, fn) {
blobClient.resendEmail(options, fn);
};
/**
* deleteBlob
* @param {object} options
* @param {string} options.url
* @param {string} options.username
* @param {string} options.blob_id
* @param {string} options.account_id
* @param {string} options.masterkey
*/
VaultClient.prototype.deleteBlob = function (options, fn) {
blobClient.deleteBlob(options, fn);
};
/**
* updateProfile
* update information stored outside the blob
* @param {object}
* @param {string} options.url
* @param {string} options.username
* @param {string} options.auth_secret
* @param {srring} options.blob_id
* @param {object} options.profile
* @param {string} options.profile.phone - optional
* @param {string} options.profile.country - optional
* @param {string} options.profile.region - optional
* @param {string} options.profile.city - optional
*/
VaultClient.prototype.updateProfile = function (options, fn) {
blobClient.updateProfile(options, fn);
};
/**
* recoverBlob
* recover blob with account secret

View File

@@ -125,7 +125,8 @@ var mockUpdate;
var mockRecover;
var mockVerify;
var mockEmail;
var mockProfile;
var mockDelete;
if (!online) {
mockRippleTxt = nock('https://' + exampleData.domain)
@@ -148,7 +149,14 @@ if (!online) {
.reply(200, { result: 'error', message: 'User already exists' }, {
'Content-Type': 'application/json'
});
mockDelete = nock('https://id.staging.ripple.com').persist();
mockDelete.filteringPath(/(v1\/user\/(.+))/g, 'delete/')
.delete('/delete/')
.reply(200, { result: 'success' }, {
'Content-Type': 'application/json'
});
mockBlob = nock('https://id.staging.ripple.com').persist();
mockBlob.get('/v1/authinfo?domain=' + exampleData.domain + '&username=' + exampleData.username.toLowerCase())
.reply(200, JSON.stringify(authInfoRes.body), {
@@ -200,6 +208,13 @@ if (!online) {
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
mockProfile = nock('https://id.staging.ripple.com/v1/user').persist();
mockProfile.filteringPath(/((.+)\/profile(.+))/g, 'profile/')
.post('profile/')
.reply(200, {result:'success'}, {
'Content-Type': 'application/json'
});
}
describe('Ripple Txt', function () {
@@ -364,6 +379,60 @@ describe('VaultClient', function () {
});
});
});
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();
});
});
});
});