Details
-
Bug
-
Resolution: Fixed
-
Major
-
2.7.0
-
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
- relates to
-
CBL-1115 CBL purges accessible documents on filtered replication
- Closed