Skip to content

Commit a2c6edf

Browse files
committed
Return 404 from authorizeDoc when an uncached non-leaf rev is requested
1 parent 9d5d83c commit a2c6edf

File tree

3 files changed

+75
-10
lines changed

3 files changed

+75
-10
lines changed

db/crud.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,8 +549,12 @@ func (col *DatabaseCollectionWithUser) authorizeDoc(ctx context.Context, doc *Do
549549

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

556560
return col.user.AuthorizeAnyCollectionChannel(col.ScopeName, col.Name, channelsForRev)
@@ -698,7 +702,7 @@ func (db *DatabaseCollectionWithUser) get1xRevFromDoc(ctx context.Context, doc *
698702
// Update: this applies to non-deletions too, since the client may have lost access to
699703
// the channel and gotten a "removed" entry in the _changes feed. It then needs to
700704
// incorporate that tombstone and for that it needs to see the _revisions property.
701-
if revid == "" || doc.History[revid] == nil {
705+
if revid == "" || doc.History[revid] == nil || err == ErrMissing {
702706
return nil, false, err
703707
}
704708
if doc.History[revid].Deleted {

db/database_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,70 @@ func TestIsServerless(t *testing.T) {
457457
}
458458
}
459459

460+
func TestUncachedOldRevisionChannel(t *testing.T) {
461+
db, ctx := setupTestDB(t)
462+
defer db.Close(ctx)
463+
collection := GetSingleDatabaseCollectionWithUser(t, db)
464+
collection.ChannelMapper = channels.NewChannelMapper(ctx, channels.DocChannelsSyncFunction, db.Options.JavascriptTimeout)
465+
466+
auth := db.Authenticator(base.TestCtx(t))
467+
468+
userAlice, err := auth.NewUser("alice", "pass", base.SetOf("ABC"))
469+
require.NoError(t, err, "Error creating user")
470+
471+
collection.user = userAlice
472+
473+
// Create the first revision of doc1.
474+
rev1Body := Body{
475+
"k1": "v1",
476+
"channels": []string{"ABC"},
477+
}
478+
rev1ID, _, err := collection.Put(ctx, "doc1", rev1Body)
479+
require.NoError(t, err, "Error creating doc")
480+
481+
rev2Body := Body{
482+
"k2": "v2",
483+
"channels": []string{"ABC"},
484+
BodyRev: rev1ID,
485+
}
486+
rev2ID, _, err := collection.Put(ctx, "doc1", rev2Body)
487+
require.NoError(t, err, "Error creating doc")
488+
489+
rev3Body := Body{
490+
"k3": "v3",
491+
"channels": []string{"ABC"},
492+
BodyRev: rev2ID,
493+
}
494+
rev3ID, _, err := collection.Put(ctx, "doc1", rev3Body)
495+
require.NoError(t, err, "Error creating doc")
496+
require.NotEmpty(t, rev3ID, "Error creating doc")
497+
498+
body, err := collection.Get1xRevBody(ctx, "doc1", rev2ID, true, nil)
499+
require.NoError(t, err, "Error getting 1x rev body")
500+
501+
// old rev was cached so still retains channel information
502+
_, rev1Digest := ParseRevID(ctx, rev1ID)
503+
_, rev2Digest := ParseRevID(ctx, rev2ID)
504+
bodyExpected := Body{
505+
"k2": "v2",
506+
"channels": []string{"ABC"},
507+
BodyRevisions: Revisions{
508+
RevisionsStart: 2,
509+
RevisionsIds: []string{rev2Digest, rev1Digest},
510+
},
511+
BodyId: "doc1",
512+
BodyRev: rev2ID,
513+
}
514+
require.Equal(t, bodyExpected, body)
515+
516+
// Flush the revision cache to force load from backup revision
517+
collection.FlushRevisionCacheForTest()
518+
519+
// 404 because we lost the non-leaf channel information after cache flush
520+
_, _, _, _, _, _, _, _, err = collection.Get1xRevAndChannels(ctx, "doc1", rev2ID, false)
521+
assertHTTPError(t, err, 404)
522+
}
523+
460524
// Test removal handling for unavailable multi-channel revisions.
461525
func TestGetRemovalMultiChannel(t *testing.T) {
462526
db, ctx := setupTestDB(t)

db/document.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,13 @@ func (sd *SyncData) UnmarshalJSON(b []byte) error {
119119

120120
// determine set of current channels based on removal entries.
121121
func (sd *SyncData) getCurrentChannels() base.Set {
122-
if len(sd.Channels) > 0 {
123-
ch := base.SetOf()
124-
for channelName, channelRemoval := range sd.Channels {
125-
if channelRemoval == nil || channelRemoval.Seq == 0 {
126-
ch.Add(channelName)
127-
}
122+
ch := base.SetOf()
123+
for channelName, channelRemoval := range sd.Channels {
124+
if channelRemoval == nil || channelRemoval.Seq == 0 {
125+
ch.Add(channelName)
128126
}
129-
return ch
130127
}
131-
return nil
128+
return ch
132129
}
133130

134131
func (sd *SyncData) HashRedact(salt string) SyncData {

0 commit comments

Comments
 (0)