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