Uploaded image for project: 'Couchbase Gateway'
  1. Couchbase Gateway
  2. CBG-946

Repeated change entries on access grant to doc's previous channel

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • Major
    • 2.8.0
    • 2.7.0
    • SyncGateway
    • Security Level: Public
    • None
    • CBG Sprint 55
    • 2

    Description

      N.B. The downstream effects of this are still to be investigated, but the behaviour in and of itself is incorrect so raising this now

      Consider the following scenario:

      • User jflath has access to channel foo.
      • Doc test is created in channels [ foo , bar ].
      • Doc test is updated to remove it from channel bar, leaving just foo.
      • User jflath is granted access to channel bar.
      • User jflath starts a changes feed with no channel filter.

      Expected behaviour

      • jflath receives test in the changes feed with Removed:[bar]

      Observed behaviour

      • jflath receives test in the changes feed without Removed:[bar]
      • jflath also receives test in the changes feed with Removed:[bar]

      Repro

      • Create the user (here done in config):

      "users": {
        "GUEST": {
          "admin_channels": [
            "*"
          ],
          "disabled": true
        },
        "jflath": {
          "admin_channels": ["foo"],
          "password": "pass"
        }
      }
      

      • Add doc in both channels:

      $ curl localhost:4985/db/test -X PUT -H 'Content-type: application/json' -d '{"channels":["foo", "bar"]}'
       
      {"id":"test","ok":true,"rev":"1-5467c30804fb9448df4873941c0c8ed8"}
      
      

      • Remove doc from bar:

      $ curl localhost:4985/db/test -X PUT -H 'Content-type: application/json' -d '{"channels":["foo"], "_rev":"1-5467c30804fb9448df4873941c0c8ed8"}'
       
      {"id":"test","ok":true,"rev":"2-61b71bf116a8ee0051649282b29d276d"}
      

      • Grant jflath access to bar:

      $ curl localhost:4985/db/_user/jflath -X PUT -d '{"admin_channels": ["foo", "bar"]}'
      

      • First check changes from admin perspective - everything looks normal:

      $ curl localhost:4985/db/_changes
       
      {"results":[
      {"seq":1,"id":"_user/","changes":[{"rev":""}]}
      ,{"seq":4,"id":"test","changes":[{"rev":"2-61b71bf116a8ee0051649282b29d276d"}]}
      ,{"seq":5,"id":"_user/jflath","changes":[{"rev":""}]}
      ],
      "last_seq":"5"}
      

      • Get changes as jflath, receive two entries for the same doc:

      $ curl localhost:4984/db/_changes -u jflath:pass
       
      {"results":[
      {"seq":4,"id":"test","changes":[{"rev":"2-61b71bf116a8ee0051649282b29d276d"}]}
      ,{"seq":"5:4","id":"test","removed":["bar"],"changes":[{"rev":"2-61b71bf116a8ee0051649282b29d276d"}]}
      ,{"seq":5,"id":"_user/jflath","changes":[]}
      ],
      "last_seq":"5"}
      

      Problem

      The changes response gives us a good clue - the same doc/rev is listed under 4 and 5:4 - we get the 4 from jflath's access to foo and the 5:4 from the access to bar. We do have handling for the case where a doc has been removed from one of the channels being requested but not others, and check here:

      https://github.com/couchbase/sync_gateway/blob/master/db/changes.go#L634-L650

      				for i, cur := range current {
      					if cur != nil && cur.Seq == minSeq {
      						current[i] = nil
      						// Track whether this is a removal from all user's channels
      						if cur.Removed == nil && minEntry.allRemoved == true {
      							minEntry.allRemoved = false
      						}
      						// Also concatenate the matching entries' Removed arrays:
      						if cur != minEntry && cur.Removed != nil {
      							if minEntry.Removed == nil {
      								minEntry.Removed = cur.Removed
      							} else {
      								minEntry.Removed = minEntry.Removed.Union(cur.Removed)
      							}
      						}
      					}
      				}
      

      The trouble is the check of cur.Seq == minSeq doesn't work when comparing 4 and 5:4, so they aren't treated as the same entry. My naive fix would be to tweak the check to compare cur.Seq.Seq == minSeq.Seq (i.e. the "real" seq of the doc only, not factoring in the TriggeredBy or LowSeq), but I don't think this would completely fix things based on how we pull items off the current array. It does work for this test case though...

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              sarath.kumarsivan Sarath Kumar Sivan (Inactive)
              James Flather James Flather (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes

                  PagerDuty