Skip to content
This repository has been archived by the owner on Nov 5, 2018. It is now read-only.

Commit

Permalink
[cookies] cookie monster lands in master
Browse files Browse the repository at this point in the history
  • Loading branch information
dscape committed May 23, 2012
2 parents ca2b3ad + 003127d commit d16bb87
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 8 deletions.
26 changes: 26 additions & 0 deletions README.md
Expand Up @@ -123,6 +123,32 @@ a very important configuration parameter if you have a high traffic website and

you can increase the size using `request_options` if this is problematic, and refer to the [request] documentation and examples for further clarification

### using cookie authentication

nano supports making requests using couchdb [cookie authentication](http://guide.couchdb.org/editions/1/en/security.html#cookies). Once authenticated, all nano operations can be performed as usual, but with the use of cookie authentication - simply by adding the cookie value to your configuration options:

``` js
var db = require('nano')({
"url" : "http://localhost:5984/foo"
, "cookie" : "couchdb_cookie_value_here"
});
```

because couchdb cookies have a sliding expiry, it is important that you check for any updated 'set-cookie' values on response. For example:

``` js
db.insert(doc, null,
function (err, body, headers) {
if (headers && headers['set-cookie']) {
res.cookie('cookie_name', JSON.stringify(headers['set-cookie']));
}
if (!err) {
res.send('ok');
}
}
);
```

## database functions

### nano.db.create(name, [callback])
Expand Down
14 changes: 12 additions & 2 deletions nano.js
Expand Up @@ -14,7 +14,7 @@
* see the license for the specific language governing permissions and
* limitations under the license.
*/
var request = require('request')
var request = require('request').defaults({ jar: false })
, fs = require('fs')
, qs = require('querystring')
, u = require('url')
Expand Down Expand Up @@ -162,6 +162,11 @@ module.exports = exports = nano = function database_module(cfg) {
delete req.headers.accept; // undo headers set
}

if(cfg.cookie) {
req.headers["X-CouchDB-WWW-Authenticate"] = "Cookie";
req.headers.cookie = cfg.cookie;
}

// these need to be encoded
if(!isEmpty(params)) {
try {
Expand Down Expand Up @@ -222,6 +227,11 @@ module.exports = exports = nano = function database_module(cfg) {
} // json data
}

if(opts.form) {
req.headers['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8';
req.body = qs.stringify(opts.form).toString('utf8');
}

log(req);

// streaming mode
Expand Down Expand Up @@ -810,7 +820,7 @@ module.exports = exports = nano = function database_module(cfg) {
}
}
}

if(!(cfg && cfg.url)) {
throw errs.create(
{ "scope" : "init"
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -2,7 +2,7 @@
, "description" : "minimalistic couchdb driver for node.js"
, "homepage" : "http://github.com/dscape/nano"
, "repository" : "git://github.com/dscape/nano"
, "version" : "3.0.3"
, "version" : "3.0.4"
, "author" : "Nuno Job <nunojobpinto@gmail.com> (http://nunojob.com)"
, "contributors" :
[ "Thiago Arrais <thiago.arrais@gmail.com> (http://thiagoarrais.com)"
Expand All @@ -19,6 +19,7 @@
, "Mark Hahn (http://hahnca.com)"
, "Francesco Ceccon <francesco@ceccon.me> (http://francesco-cek.com/)"
, "Oliver Leics (http://oleics.github.com)"
, "Jonathan Mahoney (http://mahoney.eu)"
]
, "keywords" :
["couchdb", "data", "request", "json", "nosql", "micro", "nano", "database"]
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/cfg.json
@@ -1,3 +1,4 @@
{ "timeout" : 20000
, "couch" : "http://localhost:5984"
, "admin" : "http://admin:password@localhost:5984"
}
33 changes: 33 additions & 0 deletions tests/fixtures/shared/cookie.json
@@ -0,0 +1,33 @@
[
{ "method" : "put"
, "path" : "/shared_cookie"
, "status" : 201
, "response" : "{ \"ok\": true }"
}
, { "path" : "/_config/admins/admin"
, "method" : "put"
, "body" : "*"
, "response" : "{\"ok\": true}"
}
, { "method" : "post"
, "path" : "/_session"
, "body" : "name=admin&password=password"
, "headers" : {"set-cookie" : ["AuthSession=YWRtaW46NEZCMzFERDk6BJyusEcy3SgfI9xD4Mptl0N0gh0;Version=1;Path=/;HttpOnly"]}
, "status" : 201
, "response" : "{ \"ok\": true,\"name\": null,\"roles\": [ \"_admin\" ] }"
}
, { "method" : "post"
, "status" : 201
, "path" : "/shared_cookie"
, "body" : "{\"foo\":\"baz\"}"
, "response" : "{\"ok\":true,\"id\":\"234\",\"rev\":\"1-333231\"}"
}
, { "path" : "/_config/admins/admin"
, "method" : "delete"
, "response" : "{\"ok\": true}"
}
, { "method" : "delete"
, "path" : "/shared_cookie"
, "response" : "{ \"ok\": true }"
}
]
16 changes: 11 additions & 5 deletions tests/helpers.js
Expand Up @@ -31,11 +31,17 @@ function fake_chain() {
}

helpers.timeout = cfg.timeout;
helpers.nano = nano(cfg.couch);
helpers.Nano = nano;
helpers.couch = cfg.couch;
helpers.pixel = "Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BABgAAAAA" +
"AAAAAAATCwAAEwsAAAAAAAAAAAAAWm2CAA==";
helpers.nano = nano(cfg.couch);
helpers.Nano = nano;
helpers.couch = cfg.couch;
helpers.admin = cfg.admin;
helpers.pixel = "Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BABgAAAAA" +
"AAAAAAATCwAAEwsAAAAAAAAAAAAAWm2CAA==";

var auth = require("url").parse(cfg.admin).auth.split(":");

helpers.username = auth[0];
helpers.password = auth[1];

helpers.loadFixture = function helpersLoadFixture(filename, json) {
var contents = fs.readFileSync(
Expand Down
67 changes: 67 additions & 0 deletions tests/shared/cookie.js
@@ -0,0 +1,67 @@
var specify = require("specify")
, helpers = require("../helpers")
, timeout = helpers.timeout
, nano = helpers.nano
, Nano = helpers.Nano
, nock = helpers.nock
;

var mock = nock(helpers.couch, "shared/cookie")
, admin = Nano(helpers.admin)
, cookie
, cookie_nano
;

specify("shared_cookie:setup", timeout, function (assert) {
// creates a db in admin party mode
nano.db.create("shared_cookie", function (err, response) {
assert.equal(err, undefined, "Failed to create database");
// creates a admin user, leaves admin party mode
nano.relax(
{ method : "PUT"
, path : "_config/admins/" + helpers.username
, body : helpers.password
}, function (err, response, headers) {
assert.equal(err, undefined, "Failed to create admin user");
// authenticate
nano.relax(
{ method : "POST"
, db : "_session"
, form : { "name" : "admin", "password" : "password" }
, content_type : "application/x-www-form-urlencoded; charset=utf-8"
}, function (err, response, headers) {
assert.equal(err, undefined, "Should have logged in successfully");
assert.ok(headers['set-cookie'],
"Response should have a set-cookie header");
cookie = headers['set-cookie'];
});
});
});
});

specify("shared_cookie:test", timeout, function (assert) {
var server = Nano({ url : helpers.couch, cookie: cookie });
var db = server.use("shared_cookie");
// insert with a shared cookie
db.insert({"foo": "baz"}, null, function (error, response) {
assert.equal(error, undefined, "Should have stored value");
assert.equal(response.ok, true, "Response should be ok");
assert.ok(response.rev, "Response should have rev");
});
});

specify("shared_cookie:teardown", timeout, function (assert) {
// back to admin party mode
admin.relax(
{ method : "DELETE"
, path : "_config/admins/" + helpers.username
}, function (err, response, headers) {
// delete the database that we created
nano.db.destroy("shared_cookie", function (err) {
assert.equal(err, undefined, "Failed to destroy database");
assert.ok(mock.isDone(), "Some mocks didn't run");
});
});
});

specify.run(process.argv.slice(2));

0 comments on commit d16bb87

Please sign in to comment.