On Nov 21, 2008, at 5:12 PM, Damien Katz wrote:

-Authorization/Validation-

Each design document can contain a document update validation function. Like views, these can be in any language. The language is set at the design document level.

On document update -- either via interactive update or via replication -- each is run in succession. These validation functions are responsible for enforcing document constraints or schemas, and to enforce security.

Validation functions are provided the new document, previous revision of the document on disk, and the user context. The user context has the users name and roles.

If the document values don't pass validation, a forbidden exception is returned If the user isn't allow to update the document, a unauthorized exception is returned.

This scheme can be used for signature based security (is the update signed by an authorized user) or user based security (is the user currently writing the update an authorized user).

This user based example ensures that every document has an authors field and when updating an existing document, you are an allowed author, or a moderator:

function(newDocument, previousDocument, userCtx) {
 if (!newDocument._deleted && !newDocument.author) {
        throw {forbidden, "All documents must have an author specified."}
 }
 if (previousDocument &&
        previousDocument.author != userCtx.name &&
        userCtx.roles.indexOf("Discussion Moderator") == -1) {
        throw {unauthorized, "You are not the author of this document."}
 }
}

The validation works with replication too, all updates, whether interactive or replicated are validated. But the replication still needs testing and might be buggy on update failures.

That's all for now. Question and comments please.

-Damien

Hi Damien, cool stuff. Could we have it so that the user context passed to the validation function includes a role of "_admin" if the user's name is in the DB admins list? Maybe something as simple as

diff --git a/trunk/src/couchdb/couch_db.erl b/trunk/src/couchdb/ couch_db.erl
index 7de5b8d..4eed8bc 100644
--- a/trunk/src/couchdb/couch_db.erl
+++ b/trunk/src/couchdb/couch_db.erl
@@ -231,9 +231,14 @@ validate_doc_update(_Db, #doc{id= <<"_local/",_/ binary>>}=Doc, _GetDiskDocFun) -
     Doc;
validate_doc_update(#db{name=DbName,user_ctx=Ctx}=Db, Doc, GetDiskDocFun) ->
     DiskDoc = GetDiskDocFun(),
+    UserName = Ctx#user_ctx.name,
+ Roles = Ctx#user_ctx.roles ++ case lists:member(UserName, Db#db.admins) of
+        true -> [<<"_admin">>];
+        false -> []
+    end,
     JsonCtx =  {[{<<"db">>, DbName},
-            {<<"name">>,Ctx#user_ctx.name},
-            {<<"roles">>,Ctx#user_ctx.roles}]},
+            {<<"name">>,UserName},
+            {<<"roles">>,Roles}]},
     [case Fun(Doc, DiskDoc, JsonCtx) of
         ok -> ok;
         Error -> throw(Error)

Best, Adam

Reply via email to