Skip to content
Browse files

[cookies] cookie monster lands in master

  • Loading branch information...
2 parents ca2b3ad + 003127d commit d16bb87e01f5ccaaed57f8d22c8f368d8aa63771 @dscape committed May 23, 2012
Showing with 152 additions and 8 deletions.
  1. +26 −0 README.md
  2. +12 −2 nano.js
  3. +2 −1 package.json
  4. +1 −0 tests/fixtures/cfg.json
  5. +33 −0 tests/fixtures/shared/cookie.json
  6. +11 −5 tests/helpers.js
  7. +67 −0 tests/shared/cookie.js
View
26 README.md
@@ -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])
View
14 nano.js
@@ -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')
@@ -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 {
@@ -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
@@ -810,7 +820,7 @@ module.exports = exports = nano = function database_module(cfg) {
}
}
}
-
+
if(!(cfg && cfg.url)) {
throw errs.create(
{ "scope" : "init"
View
3 package.json
@@ -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)"
@@ -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"]
View
1 tests/fixtures/cfg.json
@@ -1,3 +1,4 @@
{ "timeout" : 20000
, "couch" : "http://localhost:5984"
+, "admin" : "http://admin:password@localhost:5984"
}
View
33 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 }"
+ }
+]
View
16 tests/helpers.js
@@ -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(
View
67 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.
Something went wrong with that request. Please try again.