Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cloudant

Package Overview
Dependencies
Maintainers
3
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cloudant - npm Package Compare versions

Comparing version 1.6.2 to 1.7.0

plugins/cookieauth.js

79

cloudant.js

@@ -20,2 +20,3 @@ module.exports = Cloudant;

var nanodebug = require('debug')('nano');
var async = require('async');

@@ -52,2 +53,9 @@

}
if (theurl === null) {
if (callback) {
return callback('invalid url', null);
} else {
throw(new Error('invalid url'));
}
}

@@ -247,47 +255,36 @@ // keep connections alive by default

var nano = this;
if (!callback) {
callback = login;
login = null;
}
// Only call back once.
var inner_callback = callback;
callback = function(er, result, cookie) {
inner_callback(er, result, cookie);
inner_callback = function() {};
};
var cookie = null;
var done = {welcome:false, session:false, auth:true};
nano.session( function(er, body) { returned('session', er, body); });
nano.relax({db:""}, function(er, body) { returned('welcome', er, body); });
// If credentials are supplied, authenticate to get a cookie.
if (login && login.username && login.password) {
done.auth = false;
nano.auth(login.username, login.password, function(er, body, headers) {
returned('auth', er, body, headers);
});
}
function returned(type, er, body, headers) {
if (er)
return callback(er);
debug('Pong/%s %j', type, body);
done[type] = body;
if (type == 'auth') {
if (headers['set-cookie'] && headers['set-cookie'][0]) {
cookie = headers['set-cookie'][0].replace(/;.*$/, '');
async.series([
function(done) {
if (login && login.username && login.password) {
done.auth = false;
nano.auth(login.username, login.password, function(e, b, h) {
cookie = (h && h['set-cookie']) || null;
if (cookie) {
cookie = cookie[0];
}
done(null, b);
});
} else {
done(null, null);
}
},
function(done) {
nano.session(function(e, b, h) {
done(null, b);
});
},
function(done) {
nano.relax({db:''}, function(e, b, h) {
done(null, b);
})
}
], function(err, data) {
var body = (data && data[2]) || {};
body.userCtx = (data && data[1] && data[1].userCtx) || {};
callback(null, body, cookie);
});
}
if (done.welcome && done.session && done.auth) {
// Return the CouchDB "Welcome" body but with the userCtx added in.
done.welcome.userCtx = done.session.userCtx;
callback(null, done.welcome, cookie);
}
}
}

@@ -9,2 +9,3 @@ // reconfigure deals with the various ways the credentials can be passed in

// or { url: "https://mykey:mypassword@myaccount.cloudant.com"}
// or { instanceName: "mycloudantservice", vcapServices: JSON.parse(process.env.VCAP_SERVICES)}

@@ -19,5 +20,13 @@ var url = require('url');

if (config.url) {
// parse the URL
var parsed = null;
try {
var parsed = url.parse(config.url);
} catch(e) {
parsed = null;
};
if (!config.url || !parsed || !parsed.hostname || !parsed.protocol || !parsed.slashes) {
return null;
}
// parse the URL
var parsed = url.parse(config.url);

@@ -43,3 +52,26 @@ // enforce HTTPS for *cloudant.com domains

outUrl = config.url;
} else if (config.vcapServices) {
cloudantServices = config.vcapServices.cloudantNoSQLDB;
if (!cloudantServices || cloudantServices.length == 0) {
throw new Error('Missing Cloudant service in vcapServices');
}
for (var i = 0; i < cloudantServices.length; i++) {
if (config.instanceName == undefined || cloudantServices[i].name == config.instanceName) {
var credentials = cloudantServices[i].credentials;
if (credentials && credentials.url) {
outUrl = credentials.url;
break;
} else {
throw new Error('Invalid Cloudant service in vcapServices');
}
}
}
if (!outUrl) {
throw new Error('Missing Cloudant service in vcapServices');
}
} else {

@@ -75,7 +107,7 @@ // An account can be just the username, or the full cloudant URL.

// Issue: cloudant/nodejs-cloudant#129
if (outUrl.slice(-1) == '/') {
if (outUrl && outUrl.slice(-1) == '/') {
outUrl = outUrl.slice(0, -1);
}
return outUrl;
return (outUrl || null);
};

@@ -82,0 +114,0 @@

@@ -7,3 +7,3 @@ {

"repository": "git://github.com/cloudant/nodejs-cloudant",
"version": "1.6.2",
"version": "1.7.0",
"author": "Jason Smith <jasons@us.ibm.com>",

@@ -10,0 +10,0 @@ "contributors": [

@@ -146,2 +146,11 @@ # Cloudant Node.js Client

Running on Bluemix? You can initialize Cloudant directly from the `VCAP_SERVICES` environment variable:
~~~ js
var Cloudant = require('cloudant');
var cloudant = Cloudant({instanceName: 'foo', vcapServices: JSON.parse(process.env.VCAP_SERVICES)});
~~~
Note, if you only have a single Cloudant service then specifying the `instanceName` isn't required.
You can optionally provide a callback to the Cloudant initialization function. This will make the library automatically "ping" Cloudant to confirm the connection and that your credentials work.

@@ -213,3 +222,3 @@

This library can be used with one of three `request` plugins:
This library can be used with one of these `request` plugins:

@@ -222,3 +231,5 @@ 1. `default` - the default [request](https://www.npmjs.com/package/request) library plugin. This uses Node.js's callbacks to communicate Cloudant's replies

The "retry" plugin will automatically retry your request with exponential back-off. The 'retry' plugin can be used to stream data.
4. custom plugin - you may also supply your own function which will be called to make API calls.
4. `cookieauth` - this plugin will automatically swap your Cloudant credentials for a cookie transparently for you. It will handle the authentication for you
and ensure that the cookie is refreshed. The 'cookieauth' plugin can be used to stream data.
5. custom plugin - you may also supply your own function which will be called to make API calls.

@@ -263,2 +274,19 @@ #### The 'promises' Plugins

#### The 'cookieauth' plugin
When initialising the Cloudant library, you can opt to use the 'retry' plugin:
```js
var cloudant = Cloudant({url: myurl, plugin:'cookieauth'});
var mydb = cloudant.db.use('mydb');
mydb.get('mydoc', function(err, data) {
});
```
The above code will transparently call `POST /_session` to exchange your credentials for a cookie and then call `GET /mydoc` to fetch the document.
Subsequent calls to the same `cloudant` instance will simply use cookie authentication from that point. The library will automatically ensure that the cookie remains
up-to-date by calling Cloudant on an hourly basis to refresh the cookie.
#### Custom plugin

@@ -265,0 +293,0 @@

@@ -126,1 +126,129 @@ /**

});
describe('cookieauth plugin', function() {
it('should return a stream', function(done) {
var mocks = nock(SERVER)
.post('/_session').reply(200, { ok: true})
.get('/' + MYDB).reply(200, { ok: true});
var cloudant = Cloudant({plugin:'cookieauth', account:ME, password: PASSWORD});
var db = cloudant.db.use(MYDB);
var p = db.info(function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
// check that we use all the nocked API calls
assert.equal(mocks.isDone(), true);
done();
});
assert.equal(p instanceof require('stream').PassThrough, true);
});
it('should authenticate before attempting API call', function(done) {
var mocks = nock(SERVER)
.post('/_session', {name: ME, password: PASSWORD}).reply(200, { ok: true, info: { }, userCtx: { name: ME, roles: ['_admin']}})
.get('/' + MYDB + '/mydoc').reply(200, { _id: 'mydoc', _rev: '1-123', ok: true});
var cloudant = Cloudant({plugin:'cookieauth', account:ME, password: PASSWORD});
var db = cloudant.db.use(MYDB);
var p = db.get('mydoc', function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property._id;
data.should.have.property._rev;
data.should.have.property.ok;
// check that we use all the nocked API calls
assert.equal(mocks.isDone(), true);
done();
});
});
it('should fail with incorrect authentication', function(done) {
var mocks = nock(SERVER)
.post('/_session', {name: ME, password: PASSWORD}).reply(401, {error:'unauthorized', reason: 'Name or password is incorrect.'});
var cloudant = Cloudant({plugin:'cookieauth', account:ME, password: PASSWORD});
var db = cloudant.db.use(MYDB);
var p = db.get('mydoc', function(err, data) {
assert.equal(data, null);
err.should.be.an.Object;
err.should.have.property.error;
err.should.have.property.reason;
// check that we use all the nocked API calls
assert.equal(mocks.isDone(), true);
done();
});
});
it('should only authenticate once', function(done) {
var mocks = nock(SERVER)
.post('/_session', {name: ME, password: PASSWORD}).reply(200, { ok: true, info: { }, userCtx: { name: ME, roles: ['_admin']}}, { 'Set-Cookie': 'AuthSession=xyz; Version=1; Path=/; HttpOnly' })
.get('/' + MYDB + '/mydoc').reply(200, { _id: 'mydoc', _rev: '1-123', ok: true})
.get('/' + MYDB + '/mydoc').reply(200, { _id: 'mydoc', _rev: '1-123', ok: true});
var cloudant = Cloudant({plugin:'cookieauth', account:ME, password: PASSWORD});
var db = cloudant.db.use(MYDB);
var p = db.get('mydoc', function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property._id;
data.should.have.property._rev;
data.should.have.property.ok;
db.get('mydoc', function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property._id;
data.should.have.property._rev;
data.should.have.property.ok;
// check that we use all the nocked API calls
assert.equal(mocks.isDone(), true);
done();
});
});
});
it('should not authenticate without credentials', function(done) {
var mocks = nock(SERVER)
.get('/' + MYDB + '/mydoc').reply(200, { _id: 'mydoc', _rev: '1-123', ok: true});
var cloudant = Cloudant({plugin:'cookieauth', url: SERVER});
var db = cloudant.db.use(MYDB);
var p = db.get('mydoc', function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property._id;
data.should.have.property._rev;
data.should.have.property.ok;
// check that we use all the nocked API calls
assert.equal(mocks.isDone(), true);
done();
});
});
it('should work with asynchronous instantiation', function(done) {
var mocks = nock(SERVER)
.post('/_session', {name: ME, password: PASSWORD}).reply(200, { ok: true, info: { }, userCtx: { name: ME, roles: ['_admin']}}, { 'Set-Cookie': 'AuthSession=xyz; Version=1; Path=/; HttpOnly' })
.get('/_session').reply(200, { ok: true, info: { }, userCtx: { name: ME, roles: ['_admin']}})
.get('*').reply(200, {"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"IBM Cloudant","version":"5662","variant":"paas"},"features":["geo"]})
var cloudant = Cloudant({plugin:'cookieauth', account:ME, password: PASSWORD}, function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property.userCtx;
done();
});
});
it('should work with asynchronous instantiation with no credentials', function(done) {
var mocks = nock(SERVER)
.get('/_session').reply(200, { ok: true, info: { }, userCtx: { name: ME, roles: ['_admin']}})
.get('*').reply(200, {"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"IBM Cloudant","version":"5662","variant":"paas"},"features":["geo"]})
var cloudant = Cloudant({plugin:'cookieauth', url: SERVER}, function(err, data) {
assert.equal(err, null);
data.should.be.an.Object;
data.should.have.property.userCtx;
done();
});
});
});
var should = require('should');
var reconfigure = require('../lib/reconfigure.js');
var assert = require('assert');

@@ -106,2 +107,103 @@ describe('Reconfigure', function() {

it('gets first URL from vcap containing single service', function(done) {
var config = {vcapServices: {cloudantNoSQLDB: [
{credentials: {url: "http://mykey:mypassword@mydomain.cloudant.com"}}
]}};
var url = reconfigure(config);
url.should.be.a.String;
url.should.equal("http://mykey:mypassword@mydomain.cloudant.com");
done();
});
it('gets URL by instance name from vcap containing single service', function(done) {
var config = {instanceName: "serviceA", vcapServices: {cloudantNoSQLDB: [
{name: "serviceA", credentials: {url: "http://mykey:mypassword@mydomain.cloudant.com"}}
]}};
var url = reconfigure(config);
url.should.be.a.String;
url.should.equal("http://mykey:mypassword@mydomain.cloudant.com");
done();
});
it('gets first URL from vcap containing multiple services', function(done) {
var config = {vcapServices: {cloudantNoSQLDB: [
{credentials: {url: "http://mykey:mypassword@mydomain.cloudant.com"}},
{credentials: {url: "http://foo.bar"}},
{credentials: {url: "http://foo.bar"}}
]}};
var url = reconfigure(config);
url.should.be.a.String;
url.should.equal("http://mykey:mypassword@mydomain.cloudant.com");
done();
});
it('gets URL by instance name from vcap containing multiple services', function(done) {
var config = {instanceName: 'serviceC', vcapServices: {cloudantNoSQLDB: [
{name: "serviceA", credentials: {url: "http://foo.bar"}},
{name: "serviceB", credentials: {url: "http://foo.bar"}},
{name: "serviceC", credentials: {url: "http://mykey:mypassword@mydomain.cloudant.com"}}
]}};
var url = reconfigure(config);
url.should.be.a.String;
url.should.equal("http://mykey:mypassword@mydomain.cloudant.com");
done();
});
it('errors for empty vcap', function(done) {
var config = {vcapServices: {}}
should(function () { reconfigure(config); }).throw("Missing Cloudant service in vcapServices");
done();
});
it('errors for no services in vcap', function(done) {
var config = {vcapServices: {cloudantNoSQLDB: []}}
should(function () { reconfigure(config); }).throw("Missing Cloudant service in vcapServices");
done();
});
it('errors for missing service in vcap', function(done) {
var config = {instanceName: 'serviceC', vcapServices: {cloudantNoSQLDB: [
{name: "serviceA", credentials: {url: "http://foo.bar"}},
{name: "serviceB", credentials: {url: "http://foo.bar"}} // missing "serviceC"
]}};
should(function () { reconfigure(config); }).throw("Missing Cloudant service in vcapServices");
done();
});
it('errors for invalid service in vcap - missing credentials', function(done) {
var config = {vcapServices: {cloudantNoSQLDB: [
{name: "serviceA"} // invalid service, missing credentials
]}};
should(function () { reconfigure(config); }).throw('Invalid Cloudant service in vcapServices');
done();
});
it('errors for invalid service in vcap - missing url', function(done) {
var config = {vcapServices: {cloudantNoSQLDB: [
{name: "serviceA", credentials: {}} // invalid service, missing url
]}};
should(function () { reconfigure(config); }).throw("Invalid Cloudant service in vcapServices");
done();
});
it('detects bad urls', function(done) {
var credentials = { url: 'invalid' };
var url = reconfigure(credentials);
assert.equal(url, null);
var credentials = { url: '' };
var url = reconfigure(credentials);
assert.equal(url, null);
var credentials = { url: 'http://' };
var url = reconfigure(credentials);
assert.equal(url, null);
var credentials = { };
var url = reconfigure(credentials);
assert.equal(url, null);
var credentials = 'invalid';
var url = reconfigure(credentials);
assert.equal(url, null);
done();
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc