From 9044fc0234ed65056f087a86c7c117922f2a2c75 Mon Sep 17 00:00:00 2001 From: "Damien F. Katz" Date: Tue, 11 Nov 2008 19:45:50 +0000 Subject: Check in of initial validation and authorization work. This work is incomplete, as there is not yet any way of restricting who can update the design docs. git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@713132 13f79535-47bb-0310-9956-ffa450edef68 --- share/server/main.js | 14 +++++- share/www/script/couch.js | 99 +++++++++++++++++++++++------------------ share/www/script/couch_tests.js | 59 ++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 45 deletions(-) (limited to 'share') diff --git a/share/server/main.js b/share/server/main.js index df184c1c..bdb03c60 100644 --- a/share/server/main.js +++ b/share/server/main.js @@ -153,7 +153,19 @@ while (cmd = eval(readline())) { print("[true," + toJSON(reductions) + "]"); } break; - + case "validate": + var funSrc = cmd[1]; + var newDoc = cmd[2]; + var oldDoc = cmd[3]; + var userCtx = cmd[4]; + var validateFun = compileFunction(funSrc); + try { + validateFun(newDoc, oldDoc, userCtx); + print("1"); + } catch (error) { + print(toJSON(error)); + } + break; default: print(toJSON({error: "query_server_error", reason: "unknown command '" + cmd[0] + "'"})); diff --git a/share/www/script/couch.js b/share/www/script/couch.js index 87bba88a..cc50ad2e 100644 --- a/share/www/script/couch.js +++ b/share/www/script/couch.js @@ -13,18 +13,18 @@ // A simple class to represent a database. Uses XMLHttpRequest to interface with // the CouchDB server. -function CouchDB(name) { - this.name = name +function CouchDB(name, options) { + this.name = name; this.uri = "/" + encodeURIComponent(name) + "/"; - request = CouchDB.request; + request = function(method, uri, requestOptions) { + return CouchDB.request(method, uri, combine(requestOptions, options)); + } // Creates the database on the server this.createDb = function() { var req = request("PUT", this.uri); - var result = JSON.parse(req.responseText); - if (req.status != 201) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } // Deletes the database on the server @@ -32,10 +32,8 @@ function CouchDB(name) { var req = request("DELETE", this.uri); if (req.status == 404) return false; - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } // Save a document to the database @@ -47,9 +45,8 @@ function CouchDB(name) { req = request("PUT", this.uri + encodeURIComponent(doc._id) + encodeOptions(options), { body: JSON.stringify(doc) }); + maybeThrowError(req); var result = JSON.parse(req.responseText); - if (req.status != 201) - throw result; doc._rev = result.rev; return result; } @@ -59,18 +56,15 @@ function CouchDB(name) { var req = request("GET", this.uri + encodeURIComponent(docId) + encodeOptions(options)); if (req.status == 404) return null; - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } // Deletes a document from the database this.deleteDoc = function(doc) { var req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "?rev=" + doc._rev); + maybeThrowError(req); var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; doc._rev = result.rev; //record rev in input document doc._deleted = true; return result; @@ -79,9 +73,8 @@ function CouchDB(name) { // Deletes an attachment from a document this.deleteDocAttachment = function(doc, attachment_name) { var req = request("DELETE", this.uri + encodeURIComponent(doc._id) + "/" + attachment_name + "?rev=" + doc._rev); + maybeThrowError(req); var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; doc._rev = result.rev; //record rev in input document return result; } @@ -102,9 +95,8 @@ function CouchDB(name) { var req = request("POST", this.uri + "_bulk_docs" + encodeOptions(options), { body: JSON.stringify({"docs": docs}) }); + maybeThrowError(req); var result = JSON.parse(req.responseText); - if (req.status != 201) - throw result; for (var i = 0; i < docs.length; i++) { docs[i]._rev = result.new_revs[i].rev; } @@ -129,10 +121,8 @@ function CouchDB(name) { headers: {"Content-Type": "application/json"}, body: JSON.stringify(body) }); - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } this.view = function(viewname, options, keys) { @@ -147,19 +137,15 @@ function CouchDB(name) { } if (req.status == 404) return null; - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } // gets information about the database this.info = function() { var req = request("GET", this.uri); - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } this.allDocs = function(options,keys) { @@ -172,18 +158,14 @@ function CouchDB(name) { body: JSON.stringify({keys:keys}) }); } - var result = JSON.parse(req.responseText); - if (req.status != 200) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } this.compact = function() { var req = request("POST", this.uri + "_compact"); - var result = JSON.parse(req.responseText); - if (req.status != 202) - throw result; - return result; + maybeThrowError(req); + return JSON.parse(req.responseText); } // Convert a options object to an url query string. @@ -209,6 +191,35 @@ function CouchDB(name) { function toJSON(obj) { return obj !== null ? JSON.stringify(obj) : null; } + + function combine(object1, object2) { + if (!object2) { + return object1; + } + if (!object1) { + return object2; + } + for (var name in object2) { + object1[name] = object2[name]; + } + return object1; + } + + function maybeThrowError(req) { + if (req.status >= 400) { + if (req.responseText) { + try { + var result = JSON.parse(req.responseText); + } catch (ParseError) { + var result = {error:"unknown", reason:req.responseText}; + } + } else { + var result = {}; + } + result.http_status = req.status; + throw result; + } + } } CouchDB.allDbs = function() { @@ -247,7 +258,7 @@ CouchDB.request = function(method, uri, options) { } else { throw new Error("No XMLHTTPRequest support detected"); } - req.open(method, uri, false); + req.open(method, uri, false, options.username, options.password); if (options.headers) { var headers = options.headers; for (var headerName in headers) { diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index 2553df9b..bc1e83da 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -1918,6 +1918,65 @@ var tests = { // you can get a single key xhr = CouchDB.request("GET", "/_config/test/foo"); T(xhr.responseText == '"bar"'); + }, + + security : function(debug) { + var db = new CouchDB("test_suite_db"); + db.deleteDb(); + db.createDb(); + if (debug) debugger; + + var designDoc = { + _id:"_design/test", + language: "javascript", + validate_doc_update: "(" + (function (newDoc, oldDoc, userCtx) { + // docs should have an author field. + if (!newDoc.author) { + throw {error:"forbidden", + reason:"Documents must have an author field", + http_status:403}; + } + if (oldDoc && oldDoc.author != userCtx.name) { + throw {error:"unauthorized", + reason:"You are not the author of this document. You jerk.", + headers: + {"WWW-Authenticate": "Basic realm=\"" + userCtx.db + "\""}, + http_status:401}; + } + }).toString() + ")" + } + + db.save(designDoc); + + var userDb = new CouchDB("test_suite_db", {username:"test user", password:"foo"}); + + try { + userDb.save({foo:1}); + T(false && "Can't get here. Should have thrown an error"); + } catch (e) { + T(e.error == "forbidden"); + T(e.http_status == 403); + } + + userDb.save({_id:"testdoc", foo:1, author:"test user"}); + + var doc = userDb.open("testdoc"); + doc.foo=2; + userDb.save(doc); + + var user2Db = new CouchDB("test_suite_db", {username:"test user2"}); + + var doc = user2Db.open("testdoc"); + doc.foo=3; + try { + user2Db.save(doc); + T(false && "Can't get here. Should have thrown an error 2"); + } catch (e) { + T(e.error == "unauthorized"); + T(e.http_status == 401); + } + + } }; -- cgit v1.2.3