validate_doc_update cannot modify the document for the reasons you state.
As I said when you asked on IRC, you can write a validate_doc_update function
that refuses any update to a document that doesn’t also add information to that
document about the change being made. That is, you can insist that users
include an accurate audit trail when they update documents, and couchdb will
reject any write that doesn’t do so. A rough sketch;
validate_doc_update(oldDoc, newDoc, userCtx) {
if (newDoc.audit_trail[0].user != userCtx.name) {
throw({forbidden: "You didn’t add your name to the audit trail!"});
}
…
}
You’d obviously need to verify that the other items in doc.audit_trail are
identical to the ones in oldDoc, etc, but it’s possible.
To make adhering to these constraints easier for users, you can write a
document update handler that automatically generates an audit entry and adds it
to the start or end of the audit_trail array.
B.
On 19 Feb 2014, at 06:54, Gowtham Tamizharasan
<[email protected]> wrote:
> Hi all,
>
> We want to capture the user details of the person who creates or modifies a
> document. Now, I understand that in couch context, even a replication job
> is a 'user' that is modifying the DB.
>
> I'd ideally like to just use Changes API. It is almost perfect - except -
> we don't see the 'user' in the changes API output.
>
> So, I'm thinking of modifying validate_doc_update erlang function such that
> it will extract username from UserCtx and set a "_user" field in the
> document. However, this will surely interfere with replication. Am I
> correct?
>
> While we can "work around" this problem by putting some convention in the
> users db (if "replication user" role then don't modify _user). Is this
> approach good enough and will this work?
>
> So the modification will have be here, correct?
>
> In couch_db.erl:469: (
> https://github.com/apache/couchdb/blob/master/src/couchdb/couch_db.erl )
>
> validate_doc_update(Db, Doc, GetDiskDocFun) ->
> DiskDoc = GetDiskDocFun(),
> JsonCtx = couch_util:json_user_ctx(Db),
> SecObj = get_security(Db),
> try [case Fun(Doc, DiskDoc, JsonCtx, SecObj) of
> % INSERT HACK HERE
> ok -> ok;
> Error -> throw(Error)
> end || Fun <- Db#db.validate_doc_funs],
> ok
> catch
>
> throw:Error ->
>
> Error
> end.
>
>
> Another question: Can we use an _ field (like "_user") and will it just
> work? Or do I have to modify elsewhere too?
>
> In other words, do I have to explicitly do a transfer_fields? How do I now
> ensure _user value gets stored? From what we see, _id is written back like
> this:
>
> transfer_fields([{<<"_id">>, Id} | Rest], Doc) ->
> validate_docid(Id),
> transfer_fields(Rest, Doc#doc{id=Id});
>
> That means, I must invent a way to store the user also? like :
>
> *transfer_fields([{<<"_user">>, User} | Rest], Doc) ->*
> * %My falidation for _User field, *
> * transfer_fields(Rest, Doc#doc{user=User});*
>
>
> what about retrieval? how ll my *"_user"* field get stored, and how can i
> retrieve it?
>
>
> Regards,
> Gowtham.
>
> --
> _____________________________________________________________
> The information contained in this communication is intended solely for the
> use of the individual or entity to whom it is addressed and others
> authorized to receive it. It may contain confidential or legally privileged
> information. If you are not the intended recipient you are hereby notified
> that any disclosure, copying, distribution or taking any action in reliance
> on the contents of this information is strictly prohibited and may be
> unlawful. If you have received this communication in error, please notify
> us immediately by responding to this email and then delete it from your
> system. The firm is neither liable for the proper and complete transmission
> of the information contained in this communication nor for any delay in its
> receipt.