Differences between revisions 10 and 11
Revision 10 as of 2013-06-05 21:35:29
Size: 3731
Editor: 71
Comment: official docs link
Revision 11 as of 2018-04-13 05:57:57
Size: 0
Editor: JoanTouzet
Comment: We have good official docs on this now.
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
<<Include(EditTheWiki)>>

= Document Update Validation =

See also the [[http://docs.couchdb.org/en/latest/ddocs.html#validate-document-update-functions|official documentation]] for this topic.

<<TableOfContents()>>

A design document may define a member function called "validate_doc_update". Requests to create or update a document are validated against every "validate_doc_update" function defined in the database. The validation functions are executed in an unspecified order. A design document can contain only one validation function. Errors are thrown as javascript objects.

Example of a design document that validates the presence of an "address" field and returns :

{{{#!highlight javascript
{
   "_id": "_design/myview",
   "validate_doc_update": "function(newDoc, oldDoc, userCtx, secObj) {
      if (newDoc.address === undefined) {
         throw({forbidden: 'Document must have an address.'});
      }"
}
}}}
The result of a document update without the address field will look like this:

{{{
HTTP/1.1 403 Forbidden
WWW-Authenticate: Basic realm="administrator"
Server: CouchDB/0.9.0 (Erlang OTP/R12B)
Date: Tue, 21 Apr 2009 00:02:32 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 57
Cache-Control: must-revalidate

{"error":"forbidden","reason":"Document must have an address."}
}}}
The "validate_doc_update" function accepts three arguments:

 1. newDoc - The document to be created or used for update.
 1. oldDoc - The current document if document id was specified in the HTTP request
 1. userCtx - User context object, which contains three properties:
  a. db - String name of database
  a. name - String user name
  a. roles - Array of roles to which user belongs. Currently only admin role is supported.
 1. secObj - The security object of the database (introduced in CouchDB-0.11.1).

== Error Formats ==

Thrown errors must be javascript objects with a key of either "forbidden" or "unauthorized," and a value with the error message:

{{{
throw({forbidden: 'Error message here.'});
}}}
or
{{{
throw({unauthorized: 'Error message here.'});
}}}

== Toolbox ==
Some of these functions are found in http://guide.couchdb.org/draft/validation.html . Use them inside your validate_doc_update functions.

{{{
  function required(field, message /* optional */) {
    message = message || "Document must have a " + field;
    if (!newDoc[field]) throw({forbidden : message});
  }

  function unchanged(field) {
    if (oldDoc && toJSON(oldDoc[field]) != toJSON(newDoc[field]))
      throw({forbidden : "Field can't be changed: " + field});
  }

  function user_is(role) {
    return userCtx.roles.indexOf(role) >= 0;
  }
}}}
Here is a validation function I use to manage update Authorization using the roles as an ACL. A user may modify documents for which the accounts listed in his "roles" ACL are a prefix of the account specified.

{{{
  function user_match(account,message /* optional */) {
    for (var i in userCtx.roles) {
      var prefix = userCtx.roles[i];
      /* prefix-matching: "roles" will contain strings like "account:0003546" -- or define your own matching rules */
      if( ("account:"+account).substring(0,prefix.length) === prefix ) return;
    }
    throw({forbidden : message||"No access to this account"});
  }

  /* Usage */
  if(oldDoc) {
    if(newDoc._deleted) {
      user_match(newDoc.account,"You are not authorized to delete this document");
    } else {
      unchanged("account");
      user_match(newDoc.account,"You are not authorized to modify this document");
    }
  } else {
    user_match(newDoc.account,"You are not authorized to create this document");
  }
}}}