Multitenancy with Couchbase Sync-Gateway and Couchbase Server

Hi Team,

I am using sync gateway to sync the documents from local to couchbase server. My requirement is our application would be multitenant where information should not be shared between tenants. Tenant1 should have import and sync tenant1 docs only and tenant2 should import and sync tenant2’s documents only.

I am going to follow following approach, please let me know if I am on correct path or not ??

Following is sample document which would have one field with name tenantID.

{
“collectionType”: “contactsCollection”,
“createdDate”: “28-12-2022 11:46:10”,
“firstName”: “Dattu”,
“id”: “d0ecd70c-0dd8-4f26-af81-0e83eccba0cf”,
“lastName”: “Dave”,
“name”: “Dattu Dave”,
“status”: “Active”,
“tenantID”: “1”,
“emailId”: “dattu.dave@gmail.com”,
“mobile”: “9898989898”,
“title”: “Mr”
}

Following is sync gateway config.json’s configurations. Where I have created two users like Tenant1 and Tenant2, I am using those credential in Couchbase lite using Java library in replicator configuration. I am using field tenantID to map document to specific channel. Please look into the following sync-gateway sync function.

			"users": {
				"tenant1": { "password": "tenant1", "admin_roles": ["roleTenant1"] },
				"tenant2": { "password": "tenant2", "admin_roles": ["roleTenant2"]}
            },
            "roles": {
              "roleTenant1": {
                    "admin_channels": [
                      "channel.tenant1"
                    ]
              },
              "roleTenant2": {
                    "admin_channels": [
                      "channel.tenant2"
                    ]
              }
            },

      "sync":  `
            function sync(doc, oldDoc) {                
            validateNotEmpty("collectionType", doc.collectionType);                                
            validateNotEmpty("tenantID", doc.tenantID);
            if (!isDelete()) {                      
			   var tenantID = getTenantID();
			   var channelId = "channel.tenant" + tenantID;
			   var roleID = "roleTenant"+tenantID;                       
			   channel(channelId);                                             
			   requireRole(roleID);				             
            }                
			
			// get email Id property
			function getTenantID() {
			  return (isDelete() ? oldDoc.tenantID : doc.tenantID);
			}				

			// Check if this is a document delete
			function isDelete() {
			  return (doc._deleted == true);
			}

			// Verify that specified property exists
			function validateNotEmpty(key, value) {
				console.log("ValidateNotEmpty key "+ key + " value " + value);
			  if (!value) {
					throw({forbidden: key + " is not provided."});
			  }
			}
		}`

Above mentioned documents got sync to couchbase server with following metadata. Does tag channels in metadata represent this document is mapped to what channels ??

{
“meta”: {
“id”: “d0ecd70c-0dd8-4f26-af81-0e83eccba0cf”,
“rev”: “7-1734f27a9cce00000000000000000000”,
“expiration”: 0,
“flags”: 0,
“type”: “json”
},
“xattrs”: {
“_sync”: {
“rev”: “1-1d09e81559429ba1688ef5c926f1373fc76ee4c3”,
“sequence”: 2307,
“recent_sequences”: [
2307
],
“history”: {
“revs”: [
“1-1d09e81559429ba1688ef5c926f1373fc76ee4c3”
],
“parents”: [
-1
],
“channels”: [
[
“channel.tenant1”
]
]
},
“channels”: {
“channel.tenant1”: null
},
“cas”: “0x0000ce9c7af23417”,
“value_crc32c”: “0xef75b16d”,
“time_saved”: “2022-12-28T11:46:10.091752727Z”
}
}
}

Moreover, I am planning to use Admin REST API to onboard new Tenant for e.g Tenant3 with following REST call.

curl -vX POST “http://35.89.199.55:4985/elsadatabase/_user/” -H “accept: application/json” -H “Content-Type: application/json” -d ‘{
“name”: “tenant3”,
“password”: “tenant3”,
“admin_channels”: [
“channel.tenant3”
],
“admin_roles”: [
“roleTenant3”
],
“email”: “”,
“disabled”: true
}’

On application startup replication configuration I have already achieved dynamically like fetching username and password tenant wise and all for which I have my own custom logic.

Could please guide this approach would be enough to handle multitenancy which couchabase sets like sync-gateway and couchbase server ?? As, per my understanding I also needs to add channels while coding replication like as below.

List<String> channels = new ArrayList<>();
channels.add("channel.tenant1");
thisConfig.setChannels(channels);

with this approach, I believe application would import only those document who is having this channel in metadata. Please correct me if I am wrong.

Appreciate you feedback in advance.

The recommended approach to multi-tenancy in Couchbase is using Scopes and Collections to fully isolate each tenant’s data.

Scopes and Collections were added to Couchbase Mobile in version 3.1