diff --git a/docs/VAULTCLIENT.md b/docs/VAULTCLIENT.md index 179e3f32..5bc5fbe9 100644 --- a/docs/VAULTCLIENT.md +++ b/docs/VAULTCLIENT.md @@ -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 diff --git a/src/js/ripple/blob.js b/src/js/ripple/blob.js index e00e2bb9..656819f9 100644 --- a/src/js/ripple/blob.js +++ b/src/js/ripple/blob.js @@ -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; diff --git a/src/js/ripple/vaultclient.js b/src/js/ripple/vaultclient.js index 9e971238..430d5934 100644 --- a/src/js/ripple/vaultclient.js +++ b/src/js/ripple/vaultclient.js @@ -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 diff --git a/test/vault-test.js b/test/vault-test.js index cfe9a2d6..737c4a97 100644 --- a/test/vault-test.js +++ b/test/vault-test.js @@ -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(); + }); + }); + }); + + });