FYI: Gateway sync() function API change

242 views
Skip to first unread message

Jens Alfke

unread,
Jul 30, 2013, 6:47:48 PM7/30/13
to mobile-c...@googlegroups.com
We've just pushed a Sync Gateway revision that changes the way your JavaScript sync() functions work. If you're doing any document validation in your sync function, you'll need to make some changes after updating to this revision.

The Summary:
The third parameter to sync(), usually called userCtx, used to be an object with a "user" property whose value was the name of the user making the change, and a "roles" property whose value was an array of that user's roles.
This userCtx object is now opaque — you can't get the actual username or roles out of it. Instead, you call methods on it to check whether the author of the revision is a specific user or has a specific role. If these tests fail, those methods will throw an exception that terminates the sync function and returns a 403 to the client, just as if you'd thrown the exception yourself.

Example:
Let's say documents should only be modified by the user listed in their "owner" property.
The old validation:
function(doc, oldDoc, userCtx) {
if (oldDoc) {
if (userCtx.user != oldDoc.owner) throw({"forbidden", "not the owner"});
}
}

The new validation:
function(doc, oldDoc, userCtx) {
if (oldDoc) {
userCtx.requireUser(oldDoc.owner);
}
}

There's more documentation of the new API here.

Why The Change?
There are conditions in which your sync function will be called when there isn't any specific user involved. Some of these are:
  • The database's sync function is modified; this requires it to be re-run on every existing document, since channel/access assignments might be different
  • A PUT/POST request made on the gateway's admin port
  • A replication between two databases on the gateway (or from a foreign database to the gateway's admin port)
In these cases there aren't any reasonable values to put in the userCtx parameter. But if we left userCtx null, or an empty object, sync functions that didn't take this into account might break when run in those cases. For example, the function might throw an exception when dereferencing a null userCtx, or mistakenly reject a document because the current user name is null.

We decided the safest way to guard against this is to prevent sync functions from making direct comparisons. Instead, they call methods that check for particular users or roles. In the no-specific-user cases, those methods will do nothing, so the sync function will keep working.

—Jens

J. Chris Anderson

unread,
Jul 30, 2013, 7:15:54 PM7/30/13
to mobile-c...@googlegroups.com
Sometimes it takes seeing it in another medium, but after Jens posted this I did another small cleanup, eliminating the 3rd argument to the function in favor of making requireUser(), requireRole() and requireAccess() into functions at the same level as access() and channel()

I think this makes it more clear. Here is the updated example:

The new validation:
function(doc, oldDoc) {
if (oldDoc) {
requireUser(oldDoc.owner);
}
}


Now I will updated the wiki.

Chris

Raymond PasContent

unread,
Jan 22, 2014, 4:44:58 AM1/22/14
to mobile-c...@googlegroups.com
Hi,

   After this API modification, does a solution remain to get the current user name ? For instance to be able to use it as the parameter of an access() function.

Thanks for your answers

Raymond

Jens Alfke

unread,
Jan 22, 2014, 11:18:21 AM1/22/14
to mobile-c...@googlegroups.com

On Jan 22, 2014, at 1:44 AM, Raymond PasContent <pasconten...@gmail.com> wrote:

   After this API modification, does a solution remain to get the current user name ? For instance to be able to use it as the parameter of an access() function.

Nope, sorry.

This was deliberate. The reason is that the sync() function might be called again later on a doc that's already been added to the database, for instance if the developer changes the sync function and every doc needs to be run through it again to update its channels. Rather than having to keep track of which user updated every document (which gets problematic if that account's since been deleted), we just make all the access-control checks automatically pass when an already-added doc is re-scanned.

The workaround is to put the username in the document, for example as a "user" property. Just make sure you validate that it's correct by calling requireUser(doc.user). Then you can use doc.user for whatever you want in the sync function.

—Jens
Reply all
Reply to author
Forward
0 new messages