Unable to access data via Sync Gateway REST API - SOLVED

We have Sync Gateway 2.8 running with a Couchbase 7.1 installation and we’re having some difficulty setting things up. Initially we had several buckets, each with a sync function that hardcoded the channel name for that data (e.g. bucket “one” would have a sync function that set the channel for all docs to “one” and enforced it for access).

When we realized that this wouldn’t scale, we moved all the data from several buckets into one large bucket and made a sync function that would set the channel based on a field in every document. We then imported the data from the other buckets using cbimport. When I look at the data via N1QL or the Admin Console “Documents”, they all look correct, including the “channels” in the metadata.

Now, for some reason, our test users who should have access to these channels cannot access some of them. They are seeing failures in Couchbase Lite on iOS, and we can duplicate the problem from the command line via curl.

Here’s some concrete examples to hopefully make it clearer:

# Configuration:
$ curl -kSs -u "admin:password" -H "content-type: application/json" -H "accept: application/json" https://localhost:4985/licensees/_config | jq '.'
{
  "server": "couchbase://sdpmwdeusd0",
  "pool": "default",
  "bucket": "bucket-licensees",
  "username": "Administrator",
  "password": "****",
  "name": "licensees",
  "sync": "function(doc, oldDoc) {\n                if (doc.doc_type) {\n                    if (doc.licensee_id === undefined ) {\n                        channel(\"!\");\n                    } else if (doc.doc_type === \"Licensee\") {\n                        requireAccess(doc._id);\n                        channel(doc._id);\n                    } else {\n                        requireAccess(doc.licensee_id);\n                        channel(doc.licensee_id);\n                    }\n                }\n        }",
  "users": {
    "GUEST": {
      "name": "",
      "admin_channels": [
        "*"
      ],
      "all_channels": null,
      "disabled": true,
      "password": "****"
    },
    "admin": {
      "name": "admin",
      "admin_channels": [
        "*"
      ],
      "all_channels": null,
      "password": "****"
    },
    "mike.totman@safedoorpm.com": {
      "name": "mike.totman@safedoorpm.com",
      "admin_channels": [
        "canadoor-dev",
        "docks-doors-dev",
        "just-docks-dev"
      ],
      "all_channels": null,
      "password": "****"
    }
  },
  "revs_limit": 20,
  "import_docs": true,
  "import_backup_old_rev": false,
  "cache": {
    "rev_cache": {},
    "channel_cache": {}
  },
  "unsupported": {
    "user_views": {},
    "oidc_test_provider": {},
    "api_endpoints": {},
    "warning_thresholds": {
      "xattr_size_bytes": 943718,
      "channels_per_doc": 50,
      "access_and_role_grants_per_doc": 50
    },
    "oidc_tls_skip_verify": false,
    "sgr_tls_skip_verify": false
  },
  "deprecated": {},
  "enable_shared_bucket_access": true,
  "session_cookie_name": "",
  "session_cookie_http_only": false,
  "allow_conflicts": false,
  "num_index_replicas": 0,
  "use_views": false
}
# Create a document using our admin user
$ curl -kSs -u "admin:password" -H "content-type: application/json" -H "accept: application/json" -X PUT "https://localhost:4984/licensees/test-doc" --data-binary @/tmp/doc.json
{"error":"Forbidden","reason":"sg missing channel access"}
$ jq '.' /tmp/doc.json
{
  "_id": "test-doc",
  "doc_type": "test",
  "licensee_id": "test"
}

doc_type is a field required in all of our app’s documents. licensee_id is in most of them, but it is not required.

Sync function as it appears in the config file, for readability:

function(doc, oldDoc) {
	if (doc.doc_type) {
		if (doc.licensee_id === undefined ) {
			channel("!");
		} else if (doc.doc_type === "Licensee") {
			requireAccess(doc._id);
			channel(doc._id);
		} else {
			requireAccess(doc.licensee_id);
			channel(doc.licensee_id);
		}
	}
}

Previously we could at least add documents with curl PUT and read them back with the admin user, but it would fail with the error “Forbidden” if we used other users.

Any idea what’s going on? Does that sync function look correct? What else should we be looking at?

Hi @mtotman,

requireAccess() only allows the user access if they have explicit access to that channel. This means that if the user only has access to the * channel, they will not be able get past requireAccess("test").

To combat this, the sync function could be changed to allow access to the channel, and the wildcard channel (ie. requireAccess(doc.licensee_id,'*')).

More information about this can be found in the docs: Sync Function | Couchbase Docs.

Hope this helps!

OK, that was a red herring, I forgot to add “test” to my user’s channels. After doing that, then restarting, taking the database offline, resyncing, and then bringing it back online, I can now PUT that test document, but when I try to read it back I get the “Forbidden” response:

# Fails with my user
$ curl -kSs -u "mike.totman@safedoorpm.com:password" -H "content-type: application/json" -H "accept: application/json" "https://localhost:4984/licensees/test-doc"
{"error":"Forbidden","reason":"forbidden"}
# Succeed with admin user
$ curl -kSs -u "admin:password" -H "content-type: application/json" -H "accept: application/json" "https://localhost:4984/licensees/test-doc" | jq '.'
{
  "_id": "test-doc",
  "_rev": "1-8263afa6c189026047f15e4c679a34ec",
  "doc_type": "test",
  "licensee_id": "test"
}

Notice that the “admin” user can access the document, though they don’t have the explicit channel, just the wildcard “*”.

For completeness, here’s what the document looks like in the Web Console via the Documents item (left column).

Data:

{
  "doc_type": "test",
  "licensee_id": "test"
}

Metadata:

{
  "meta": {
    "id": "test-doc",
    "rev": "4-16fb09606f8000000000000000000000",
    "expiration": 0,
    "flags": 0,
    "type": "json"
  },
  "xattrs": {
    "_sync": {
      "rev": "1-8263afa6c189026047f15e4c679a34ec",
      "sequence": 46093,
      "recent_sequences": [
        46093
      ],
      "history": {
        "revs": [
          "1-8263afa6c189026047f15e4c679a34ec"
        ],
        "parents": [
          -1
        ],
        "channels": [
          [
            "test"
          ]
        ]
      },
      "channels": {
        "test": null
      },
      "cas": "0x0000806f6009fb16",
      "value_crc32c": "0x90126e6c",
      "time_saved": "2022-06-22T19:49:19.165187462Z"
    }
  }
}

Argh, found the error: apparently somewhere along the line I must have altered the user settings for the database using the API, so the changes in the configuration file were being ignored.

When I check the user via the ...:4985/licensees/_user/mike.totman@safedoorpm.com API I saw that I did not in fact have the channels assigned to my user. Deleting that user and then restarting SG re-created the user with the channels specified for me in the configuration file and now I can access the data properly.