Skip to content

Commit

Permalink
Return 404 from authorizeDoc when an uncached non-leaf rev is requested
Browse files Browse the repository at this point in the history
  • Loading branch information
bbrks committed Jan 16, 2024
1 parent b63995c commit 044ab30
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 10 deletions.
8 changes: 6 additions & 2 deletions db/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,12 @@ func (col *DatabaseCollectionWithUser) authorizeDoc(ctx context.Context, doc *Do

channelsForRev, ok := doc.channelsForRev(revid)
if !ok {
// No such revision; let the caller proceed and return a 404
// No such revision
// let the caller proceed and return a 404
return nil
} else if channelsForRev == nil {
// non-leaf (no channel info) - force 404 (caller would find the rev if it tried to look)
return ErrMissing
}

return col.user.AuthorizeAnyCollectionChannel(col.ScopeName, col.Name, channelsForRev)
Expand Down Expand Up @@ -695,7 +699,7 @@ func (db *DatabaseCollectionWithUser) get1xRevFromDoc(ctx context.Context, doc *
// Update: this applies to non-deletions too, since the client may have lost access to
// the channel and gotten a "removed" entry in the _changes feed. It then needs to
// incorporate that tombstone and for that it needs to see the _revisions property.
if revid == "" || doc.History[revid] == nil {
if revid == "" || doc.History[revid] == nil || err == ErrMissing {
return nil, false, err
}
if doc.History[revid].Deleted {
Expand Down
64 changes: 64 additions & 0 deletions db/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,70 @@ func TestIsServerless(t *testing.T) {
}
}

func TestUncachedOldRevisionChannel(t *testing.T) {
db, ctx := setupTestDB(t)
defer db.Close(ctx)
collection := GetSingleDatabaseCollectionWithUser(t, db)
collection.ChannelMapper = channels.NewChannelMapper(ctx, channels.DocChannelsSyncFunction, db.Options.JavascriptTimeout)

auth := db.Authenticator(base.TestCtx(t))

userAlice, err := auth.NewUser("alice", "pass", base.SetOf("ABC"))
require.NoError(t, err, "Error creating user")

collection.user = userAlice

// Create the first revision of doc1.
rev1Body := Body{
"k1": "v1",
"channels": []string{"ABC"},
}
rev1ID, _, err := collection.Put(ctx, "doc1", rev1Body)
require.NoError(t, err, "Error creating doc")

rev2Body := Body{
"k2": "v2",
"channels": []string{"ABC"},
BodyRev: rev1ID,
}
rev2ID, _, err := collection.Put(ctx, "doc1", rev2Body)
require.NoError(t, err, "Error creating doc")

rev3Body := Body{
"k3": "v3",
"channels": []string{"ABC"},
BodyRev: rev2ID,
}
rev3ID, _, err := collection.Put(ctx, "doc1", rev3Body)
require.NoError(t, err, "Error creating doc")
require.NotEmpty(t, rev3ID, "Error creating doc")

body, err := collection.Get1xRevBody(ctx, "doc1", rev2ID, true, nil)
require.NoError(t, err, "Error getting 1x rev body")

// old rev was cached so still retains channel information
_, rev1Digest := ParseRevID(ctx, rev1ID)
_, rev2Digest := ParseRevID(ctx, rev2ID)
bodyExpected := Body{
"k2": "v2",
"channels": []string{"ABC"},
BodyRevisions: Revisions{
RevisionsStart: 2,
RevisionsIds: []string{rev2Digest, rev1Digest},
},
BodyId: "doc1",
BodyRev: rev2ID,
}
require.Equal(t, bodyExpected, body)

// Flush the revision cache to force load from backup revision
collection.FlushRevisionCacheForTest()

// 404 because we lost the non-leaf channel information after cache flush
_, _, _, _, _, _, _, _, err = collection.Get1xRevAndChannels(ctx, "doc1", rev2ID, false)
assertHTTPError(t, err, 404)
}

// Test removal handling for unavailable multi-channel revisions.
func TestGetRemovalMultiChannel(t *testing.T) {
db, ctx := setupTestDB(t)
Expand Down
13 changes: 5 additions & 8 deletions db/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,13 @@ func (sd *SyncData) UnmarshalJSON(b []byte) error {

// determine set of current channels based on removal entries.
func (sd *SyncData) getCurrentChannels() base.Set {
if len(sd.Channels) > 0 {
ch := base.SetOf()
for channelName, channelRemoval := range sd.Channels {
if channelRemoval == nil || channelRemoval.Seq == 0 {
ch.Add(channelName)
}
ch := base.SetOf()
for channelName, channelRemoval := range sd.Channels {
if channelRemoval == nil || channelRemoval.Seq == 0 {
ch.Add(channelName)
}
return ch
}
return nil
return ch
}

func (sd *SyncData) HashRedact(salt string) SyncData {
Expand Down

0 comments on commit 044ab30

Please sign in to comment.