Sync Function API Reference
Use Sync Functions to implement effective data routing and access control in the cloud-to-edge synchronization of enterprise data.
The Sync Function is crucial to the security of data sync and replication. It is in charge of data validation, access control and routing. This topic provides a reference resource on Sync and its helper functions.
Related Topics: Concepts | How-to | Sync Function | Use XATTRs for Access Grants
Other Topics: Sync Function Overview
access()
- Function
-
access(username, channelname)
Arguments
Argument | Description |
---|---|
username |
Must be a string identifying a user, or an array of strings identifying multiple users; the function is applied to each user in the array. If the value resolves to null the function result is a no-op. |
|
Must be a string identifying a channel name, or an array of strings to specify multiple channel names (for example: If the value resolves to null the function result is a no-op. |
As a convenience, the resolved value of either argument may be null or undefined , in which case nothing happens.
|
Context
You can invoke this function multiple times from within your Sync Function.
Prefix the username argument value with role: to apply this function to a role rather than a user.
This grants access to the specified channel(s) for all users assigned that role.
|
The effects of all access calls by all active documents are effectively combined in a union, so if any document grants a user access to a channel, that user has access to the channel.
You can use the all channels wildcard ('*') to grant the user access to all documents in all channels.
Use
This example shows some valid ways to call access()
:
access ("jchris", "mtv"); (1)
access ("jchris", ["mtv", "mtv2", "vh1"]); (2)
access (["snej", "jchris", "role:admin"], "vh1"); (3)
access (["snej", "jchris"], ["mtv", "mtv2", "vh1"]); (4)
access (null, "hbo"); (5)
access ("snej", null);
1 | Allow access of single channel to single user |
2 | Allow access of multiple channels to single user |
3 | Allow access of single channel to multiple users |
4 | Allow access of multiple channels to multiple users |
5 | The null arguments mean these are treated as no-ops |
If you invoke the access() function multiple times to grant the same user access to the same channel, you could see negative performance effects, such as large fetches or request timeouts.
|
channel()
Arguments
Argument | Description |
---|---|
|
Must be a string identifying a channel name, or an array of strings to specify multiple channel names (for example: If the value resolves to null the function result is a no-op. |
Context
The channel function can be called zero or more times from the sync function, for any document.
Channels don’t have to be predefined. A channel implicitly comes into existence when a document is routed to it. |
Routing changes have no effect until the document is actually saved in the database, so if the sync function first calls channel()
or access()
, but then rejects the update, the channel and access changes will not occur.
As a convenience, it is legal to call channel with a null or undefined argument; it simply does nothing.This allows you to do something like channel(doc.channels) without having to first check whether doc.channels exists.
|
expiry()
- Function
-
expiry(value)
Arguments
Argument | Description |
---|---|
|
The
|
1 Couchbase Server expiries are specified as Unix time, and if the desired TTL is below 30 days then it can also represent an interval in seconds from the current time (for example, a value of 5 will remove the document 5 seconds after it is written to Couchbase Server).
Context
Under the hood, the expiration time is set and managed on the Couchbase Server document (TTL is not supported for databases in walrus mode).
Impact
The impact on the resulting document when the expiry value is reached depends on the setting of shared-bucket-access:
- Enabled
-
The active revision of the document is tombstoned.
If there is another non-tombstoned revision for this document (i.e a conflict) it will become the active revision.
The tombstoned revision will be purged when the server’s metadata purge interval is reached.
- Disabled
-
The document will be purged from the database.
As with the existing explicit purge mechanism, this applies only to the local database; it has nothing to do with replication.
This expiration time is not propagated when the document is replicated.
The purge of the document does not cause it to be deleted on any other database.
Inspect a Document Expiry Value
You can retrieve a document’s expiration time, as it is returned in the response of GET +/\{db/{doc} use show_exp=true
as the querystring.
curl -X GET "http://localhost:4985/ourdb/ourdoc?show_exp=true" -H "accept: application/json"
requireAccess()
- Function
-
requireAccess(channels)
Purpose
Use the requireAccess()
function to reject document updates that are not made by the a user with access to at least one of the given channels, as shown in Example 4
Arguments
Argument | Description |
---|---|
|
Must be a string identifying a channel name, or an array of strings to specify multiple channel names (for example: If the value resolves to null the function result is a no-op. |
Context
The function signals rejection by throwing an exception, so the rest of the sync function will not be run.
Note that requireAccess()
will only recognize grants made explicitly using a channel name (not by a wildcard).
So, if a user was granted access using only the all channels wildcard] (*
), then requireAccess('anychannelname')'
will fail because the user wasn’t granted access to that channel (only to the *
channel).
Use
requireAccess("events"); (1)
if (oldDoc) {
requireAccess(oldDoc.channels); (2)
}
1 | Throw an exception unless the user has access to read the "events" channel: |
2 | Throw an exception unless the user can read one of the channels in the previous revision’s channels property: |
requireRole()
- Function
-
requireRole(rolename)
Purpose
Use the requireRole()
function to reject document updates that are not made by user with the specified role or roles, as shown in Example 6.
Arguments
Argument | Description |
---|---|
|
Must be a string identifying a role, or an array of strings identifying multiple roles; the function is applied to each role in the array. If the value resolves to null the function result is a no-op. Note — Role names must always be prefixed with |
requireUser()
- Function
-
requireUser(username)
Purpose
Use the requireUser()
function to reject document updates that are not made by the specified user or users.
Arguments
Argument | Description |
---|---|
username |
Must be a string identifying a user, or an array of strings identifying multiple users; the function is applied to each user in the array. If the value resolves to null the function result is a no-op. |
Context
The function signals rejection by throwing an exception, so the rest of the sync function will not be run.
When validating a document, you should treat all properties of the doc
parameter as untrusted. That is because it is the object that you’re validating.
This may sound obvious, but it can be easy to make mistakes, like calling requireUser(doc.owners)
instead of requireUser(oldDoc.owners)
.
When using one document property to validate another, look up that property in oldDoc
, not doc
!
role()
- Function
-
role(username, rolename)
Purpose
Use the role()
function to add a role to a user.
This indirectly gives them access to any channels assigned to that role.
Roles, like users, have to be explicitly created by an administrator. |
Arguments
Argument | Description |
---|---|
|
Must be a string identifying a role, or an array of strings identifying multiple roles; the function is applied to each role in the array. If the value resolves to null the function result is a no-op. Note — Role names must always be prefixed with |
username |
Must be a string identifying a user, or an array of strings identifying multiple users; the function is applied to each user in the array. If the value resolves to null the function result is a no-op. |
Context
This function affects the user’s ability to revise documents, if the access function requires role membership to validate certain types of changes.
Its use is similar to access
.
Nonexistent roles don’t cause an error, but have no effect on the user’s access privileges.
You can create roles retrospectively. As soon as a role is created, any pre-existing references to it take effect. |
Use
role ("jchris", "role:admin"); (1)
role ("jchris", ["role:portlandians", "role:portlandians-owners"]); (2)
role (["snej", "jchris", "traun"], "role:mobile"); (3)
role ("ed", null); (4)
1 | The role admin is assigned to the user |
2 | Both the named roles are assigned to the user |
3 | The role mobile is assigned to all the named users |
4 | No op |
throw()
- Function
-
throw()
Context
You enforce the validity of document structure by checking the necessary constraints and throwing an exception if they’re not met.
In validating a document, you’ll often need to compare the new revision to the old one, to check for illegal changes in state.
For example, some properties may be immutable after the document is created, or may be changeable only by certain users, or may only be allowed to change in certain ways.
That’s why the current document contents are given to the sync function, as the oldDoc
parameter.
We recommend that you not create invalid documents in the first place. As much as possible, your app logic and validation function should prevent invalid documents from being created locally. The server-side sync function validation should be seen as a fail-safe and a guard against malicious access.
Use
In this example the sync function disallows all writes to the database it is in.
function(doc) {
throw({forbidden: "read only!"}) (1)
}
1 | The document update will be rejected with an HTTP 403 "Forbidden" error code, with the value of the forbidden: property being the HTTP status message.This is the preferred way to reject an update. |