func TestRebuildRoleChannels(t *testing.T) { computer := mockComputer{channels: ch.AtSequence(ch.SetOf("derived1", "derived2"), 1)} auth := NewAuthenticator(gTestBucket, &computer) role, _ := auth.NewRole("testRole", ch.SetOf("explicit1")) err := auth.InvalidateChannels(role) assert.Equals(t, err, nil) role2, err := auth.GetRole("testRole") assert.Equals(t, err, nil) assert.DeepEquals(t, role2.Channels(), ch.AtSequence(ch.SetOf("explicit1", "derived1", "derived2", "!"), 1)) }
func TestRebuildUserChannels(t *testing.T) { computer := mockComputer{channels: ch.AtSequence(ch.SetOf("derived1", "derived2"), 1)} auth := NewAuthenticator(gTestBucket, &computer) user, _ := auth.NewUser("testUser", "password", ch.SetOf("explicit1")) user.setChannels(nil) err := auth.Save(user) assert.Equals(t, err, nil) user2, err := auth.GetUser("testUser") assert.Equals(t, err, nil) assert.DeepEquals(t, user2.Channels(), ch.AtSequence(ch.SetOf("explicit1", "derived1", "derived2", "!"), 1)) }
func TestChangeIndexChanges(t *testing.T) { base.EnableLogKey("DIndex+") db := setupTestDBForChangeIndex(t) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC", "PBS", "NBC", "TBS")) authenticator.Save(user) // Write an entry to the bucket WriteDirectWithKey(db, "1c856b5724dcf4273c3993619900ce7f", []string{}, 1) time.Sleep(20 * time.Millisecond) changes, err := db.GetChanges(base.SetOf("*"), ChangesOptions{Since: simpleClockSequence(0)}) assert.True(t, err == nil) assert.Equals(t, len(changes), 1) time.Sleep(20 * time.Millisecond) // Write a few more entries to the bucket WriteDirectWithKey(db, "12389b182ababd12fff662848edeb908", []string{}, 1) time.Sleep(20 * time.Millisecond) changes, err = db.GetChanges(base.SetOf("*"), ChangesOptions{Since: simpleClockSequence(0)}) assert.True(t, err == nil) assert.Equals(t, len(changes), 2) }
// Test backfill of late arriving sequences to the channel caches func TestChannelCacheBackfill(t *testing.T) { base.LogKeys["Cache"] = true base.LogKeys["Changes"] = true base.LogKeys["Changes+"] = true db := setupTestDBWithCacheOptions(t, shortWaitCache()) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC", "PBS", "NBC", "TBS")) authenticator.Save(user) // Simulate seq 3 being delayed - write 1,2,4,5 WriteDirect(db, []string{"ABC", "NBC"}, 1) WriteDirect(db, []string{"ABC"}, 2) WriteDirect(db, []string{"ABC", "PBS"}, 5) WriteDirect(db, []string{"ABC", "PBS"}, 6) // Test that retrieval isn't blocked by skipped sequences db.changeCache.waitForSequence(6) db.user, _ = authenticator.GetUser("naomi") changes, err := db.GetChanges(base.SetOf("*"), ChangesOptions{Since: SequenceID{Seq: 0}}) assertNoError(t, err, "Couldn't GetChanges") assert.Equals(t, len(changes), 4) assert.DeepEquals(t, changes[0], &ChangeEntry{ Seq: SequenceID{Seq: 1, TriggeredBy: 0, LowSeq: 2}, ID: "doc-1", Changes: []ChangeRev{{"rev": "1-a"}}}) lastSeq := changes[len(changes)-1].Seq // Validate insert to various cache states WriteDirect(db, []string{"ABC", "NBC", "PBS", "TBS"}, 3) WriteDirect(db, []string{"CBS"}, 7) db.changeCache.waitForSequence(7) // verify insert at start (PBS) pbsCache := db.changeCache.channelCaches["PBS"] assert.True(t, verifyCacheSequences(pbsCache, []uint64{3, 5, 6})) // verify insert at middle (ABC) abcCache := db.changeCache.channelCaches["ABC"] assert.True(t, verifyCacheSequences(abcCache, []uint64{1, 2, 3, 5, 6})) // verify insert at end (NBC) nbcCache := db.changeCache.channelCaches["NBC"] assert.True(t, verifyCacheSequences(nbcCache, []uint64{1, 3})) // verify insert to empty cache (TBS) tbsCache := db.changeCache.channelCaches["TBS"] assert.True(t, verifyCacheSequences(tbsCache, []uint64{3})) // verify changes has three entries (needs to resend all since previous LowSeq, which // will be the late arriver (3) along with 5, 6) changes, err = db.GetChanges(base.SetOf("*"), ChangesOptions{Since: lastSeq}) assert.Equals(t, len(changes), 3) assert.DeepEquals(t, changes[0], &ChangeEntry{ Seq: SequenceID{Seq: 3, LowSeq: 3}, ID: "doc-3", Changes: []ChangeRev{{"rev": "1-a"}}}) }
// Updates the Channels property of a document object with current & past channels. // Returns the set of channels that have changed (document joined or left in this revision) func (doc *document) updateChannels(newChannels base.Set) (changedChannels base.Set) { var changed []string oldChannels := doc.Channels if oldChannels == nil { oldChannels = channels.ChannelMap{} doc.Channels = oldChannels } else { // Mark every no-longer-current channel as unsubscribed: curSequence := doc.Sequence for channel, removal := range oldChannels { if removal == nil && !newChannels.Contains(channel) { oldChannels[channel] = &channels.ChannelRemoval{ Seq: curSequence, RevID: doc.CurrentRev, Deleted: doc.hasFlag(channels.Deleted)} changed = append(changed, channel) } } } // Mark every current channel as subscribed: for channel, _ := range newChannels { if value, exists := oldChannels[channel]; value != nil || !exists { oldChannels[channel] = nil changed = append(changed, channel) } } if changed != nil { base.LogTo("CRUD", "\tDoc %q in channels %q", doc.ID, newChannels) changedChannels = channels.SetOf(changed...) } return }
// Unit test for bug #673 func TestUpdatePrincipal(t *testing.T) { base.LogKeys["Cache"] = true base.LogKeys["Changes"] = true base.LogKeys["Changes+"] = true db := setupTestDB(t) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC")) authenticator.Save(user) // Validate that a call to UpdatePrincipals with no changes to the user doesn't allocate a sequence userInfo, err := db.GetPrincipal("naomi", true) userInfo.ExplicitChannels = base.SetOf("ABC") _, err = db.UpdatePrincipal(*userInfo, true, true) assertNoError(t, err, "Unable to update principal") nextSeq, err := db.sequences.nextSequence() assert.Equals(t, nextSeq, uint64(1)) // Validate that a call to UpdatePrincipals with changes to the user does allocate a sequence userInfo, err = db.GetPrincipal("naomi", true) userInfo.ExplicitChannels = base.SetOf("ABC", "PBS") _, err = db.UpdatePrincipal(*userInfo, true, true) assertNoError(t, err, "Unable to update principal") nextSeq, err = db.sequences.nextSequence() assert.Equals(t, nextSeq, uint64(3)) }
func postChanges(t *testing.T, it indexTester) { response := it.sendAdminRequest("PUT", "/_logging", `{"Changes":true, "Changes+":true, "HTTP":true, "DIndex+":true}`) assert.True(t, response != nil) // Create user: a := it.ServerContext().Database("db").Authenticator() bernard, err := a.NewUser("bernard", "letmein", channels.SetOf("PBS")) assert.True(t, err == nil) a.Save(bernard) // Put several documents response = it.sendAdminRequest("PUT", "/db/pbs1", `{"value":1, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/abc1", `{"value":1, "channel":["ABC"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs2", `{"value":2, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs3", `{"value":3, "channel":["PBS"]}`) assertStatus(t, response, 201) var changes struct { Results []db.ChangeEntry Last_Seq db.SequenceID } changesJSON := `{"style":"all_docs", "heartbeat":300000, "feed":"longpoll", "limit":50, "since":"0"}` changesResponse := it.send(requestByUser("POST", "/db/_changes", changesJSON, "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") assert.Equals(t, len(changes.Results), 3) }
func TestPostChangesSameVbucket(t *testing.T) { it := initIndexTester(true, `function(doc) {channel(doc.channel);}`) defer it.Close() response := it.sendAdminRequest("PUT", "/_logging", `{"Changes":true, "Changes+":true, "HTTP":true, "DIndex+":true}`) assert.True(t, response != nil) // Create user: a := it.ServerContext().Database("db").Authenticator() bernard, err := a.NewUser("bernard", "letmein", channels.SetOf("PBS")) assert.True(t, err == nil) a.Save(bernard) // Put several documents with ids that hash to the same vbucket response = it.sendAdminRequest("PUT", "/db/pbs0000609", `{"value":1, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs0000799", `{"value":2, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs0003428", `{"value":3, "channel":["PBS"]}`) assertStatus(t, response, 201) var changes struct { Results []db.ChangeEntry Last_Seq db.SequenceID } changesJSON := `{"style":"all_docs", "heartbeat":300000, "feed":"longpoll", "limit":50, "since":"0"}` changesResponse := it.send(requestByUser("POST", "/db/_changes", changesJSON, "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") assert.Equals(t, len(changes.Results), 3) }
func TestIndexChangesAdminBackfill(t *testing.T) { db := setupTestDBForChangeIndex(t) defer tearDownTestDB(t, db) base.EnableLogKey("IndexChanges") base.EnableLogKey("Hash+") base.EnableLogKey("Changes+") base.EnableLogKey("Backfill") db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC")) user.SetSequence(1) authenticator.Save(user) // Create docs on multiple channels: db.Put("both_1", Body{"channels": []string{"ABC", "PBS"}}) db.Put("doc0000609", Body{"channels": []string{"PBS"}}) db.Put("doc0000799", Body{"channels": []string{"ABC"}}) time.Sleep(100 * time.Millisecond) // Check the _changes feed: db.user, _ = authenticator.GetUser("naomi") changes, err := db.GetChanges(base.SetOf("*"), getZeroSequence(db)) assertNoError(t, err, "Couldn't GetChanges") printChanges(changes) assert.Equals(t, len(changes), 3) // Modify user to have access to both channels: log.Println("Get Principal") userInfo, err := db.GetPrincipal("naomi", true) assert.True(t, userInfo != nil) userInfo.ExplicitChannels = base.SetOf("ABC", "PBS") _, err = db.UpdatePrincipal(*userInfo, true, true) assertNoError(t, err, "UpdatePrincipal failed") time.Sleep(100 * time.Millisecond) // Write a few more docs (that should be returned as non-backfill) db.Put("doc_nobackfill_1", Body{"channels": []string{"PBS"}}) db.Put("doc_nobackfill_2", Body{"channels": []string{"PBS"}}) time.Sleep(100 * time.Millisecond) // Check the _changes feed: log.Println("Get User") db.user, _ = authenticator.GetUser("naomi") db.changeCache.waitForSequence(1) time.Sleep(100 * time.Millisecond) lastSeq := getLastSeq(changes) lastSeq, _ = db.ParseSequenceID(lastSeq.String()) changes, err = db.GetChanges(base.SetOf("*"), ChangesOptions{Since: lastSeq}) assertNoError(t, err, "Couldn't GetChanges") printChanges(changes) assert.Equals(t, len(changes), 5) verifyChange(t, changes, "both_1", true) verifyChange(t, changes, "doc0000609", true) verifyChange(t, changes, "doc_nobackfill_1", false) verifyChange(t, changes, "doc_nobackfill_2", false) }
// Test notification when buffered entries are processed after a user doc arrives. func TestChannelCacheBufferingWithUserDoc(t *testing.T) { base.EnableLogKey("Cache") base.EnableLogKey("Cache+") base.EnableLogKey("Changes") base.EnableLogKey("Changes+") db := setupTestDBWithCacheOptions(t, CacheOptions{}) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Simulate seq 1 (user doc) being delayed - write 2 first WriteDirect(db, []string{"ABC"}, 2) // Start wait for doc in ABC waiter := db.tapListener.NewWaiterWithChannels(channels.SetOf("ABC"), nil) successChan := make(chan bool) go func() { waiter.Wait() close(successChan) }() // Simulate a user doc update WriteUserDirect(db, "bernard", 1) // Wait 3 seconds for notification, else fail the test. select { case <-successChan: log.Println("notification successful") case <-time.After(time.Second * 3): assertFailed(t, "No notification after 3 seconds") } }
func TestIndexChangesMultipleRevisions(t *testing.T) { it := initIndexTester(true, `function(doc) {channel(doc.channel);}`) defer it.Close() response := it.sendAdminRequest("PUT", "/_logging", `{"Changes":true, "Changes+":true, "HTTP":true}`) assert.True(t, response != nil) // Create user: a := it.ServerContext().Database("db").Authenticator() bernard, err := a.NewUser("bernard", "letmein", channels.SetOf("ABC")) assert.True(t, err == nil) a.Save(bernard) var writeResponse struct { Id string Rev string } // Start 10 goroutines, creating 100 docs each, in channel ABC var wg sync.WaitGroup numWriters := 0 docsPerWriter := 100 for i := 0; i < numWriters; i++ { wg.Add(1) go func(i int) { defer wg.Done() revIds := make([]string, docsPerWriter) for j := 0; j < docsPerWriter; j++ { docID := fmt.Sprintf("doc-%d", i*docsPerWriter+j) url := fmt.Sprintf("/db/%s", docID) response = it.sendAdminRequest("PUT", url, `{"channel":["ABC"]}`) json.Unmarshal(response.Body.Bytes(), &writeResponse) revIds[j] = writeResponse.Rev } // write revisions for j := 0; j < docsPerWriter; j++ { docID := fmt.Sprintf("doc-%d", i*docsPerWriter+j) url := fmt.Sprintf("/db/%s?rev=%s", docID, revIds[j]) response = it.sendAdminRequest("PUT", url, `{"modified":true, "channel":["ABC"]}`) } }(i) } wg.Wait() // Wait for indexing time.Sleep(1 * time.Second) var changes struct { Results []db.ChangeEntry Last_Seq interface{} } changesResponse := it.send(requestByUser("GET", "/db/_changes", "", "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") assert.Equals(t, len(changes.Results), numWriters*docsPerWriter) }
func TestMultiChannelUserAndDocs(t *testing.T) { it := initIndexTester(true, `function(doc) {channel(doc.channel);}`) defer it.Close() response := it.sendAdminRequest("PUT", "/_logging", `{"Changes":true, "Changes+":true, "HTTP":true, "Debug":true}`) assert.True(t, response != nil) // Create user: a := it.ServerContext().Database("db").Authenticator() bernard, err := a.NewUser("bernard", "letmein", channels.SetOf("ABC", "NBC", "CBS")) assert.True(t, err == nil) a.Save(bernard) // Put documents in ABC for a := 0; a < 30; a++ { // Put a doc with id "ABC_[a]" url := fmt.Sprintf("/db/ABC_%d", a) response = it.sendAdminRequest("PUT", url, `{"channel":["ABC"]}`) assertStatus(t, response, 201) } // Put documents in NBC, CBS for nc := 0; nc < 40; nc++ { url := fmt.Sprintf("/db/NBC_CBS_%d", nc) response = it.sendAdminRequest("PUT", url, `{"channel":["NBC","CBS"]}`) assertStatus(t, response, 201) } // Put documents in ABC, NBC, CBS for anc := 0; anc < 60; anc++ { url := fmt.Sprintf("/db/ABC_NBC_CBS_%d", anc) response = it.sendAdminRequest("PUT", url, `{"channel":["ABC","NBC","CBS"]}`) assertStatus(t, response, 201) } time.Sleep(10 * time.Millisecond) type simpleChangeResult struct { Seq string ID string } var changes struct { Results []simpleChangeResult Last_Seq interface{} } //changesJSON := `{"filter":"sync_gateway/bychannel", "channels":"PBS"}` changesResponse := it.send(requestByUser("GET", "/db/_changes", "", "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") assert.Equals(t, len(changes.Results), 130) /* for _, result := range changes.Results { log.Printf("result: {%+v, %+v}", result.ID, result.Seq) } log.Printf("last_seq:%v", changes.Last_Seq) */ }
// Test low sequence handling of late arriving sequences to a continuous changes feed, when the // user doesn't have visibility to some of the late arriving sequences func TestLowSequenceHandlingAcrossChannels(t *testing.T) { //base.LogKeys["Cache"] = true //base.LogKeys["Changes"] = true //base.LogKeys["Changes+"] = true db := setupTestDBWithCacheOptions(t, shortWaitCache()) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC")) authenticator.Save(user) // Simulate seq 3 and 4 being delayed - write 1,2,5,6 WriteDirect(db, []string{"ABC"}, 1) WriteDirect(db, []string{"ABC"}, 2) WriteDirect(db, []string{"PBS"}, 5) WriteDirect(db, []string{"ABC", "PBS"}, 6) db.changeCache.waitForSequence(6) db.user, _ = authenticator.GetUser("naomi") // Start changes feed var options ChangesOptions options.Since = SequenceID{Seq: 0} options.Terminator = make(chan bool) options.Continuous = true options.Wait = true feed, err := db.MultiChangesFeed(base.SetOf("*"), options) assert.True(t, err == nil) // Go-routine to work the feed channel and write to an array for use by assertions var changes = make([]*ChangeEntry, 0, 50) time.Sleep(50 * time.Millisecond) err = appendFromFeed(&changes, feed, 3) // Validate the initial sequences arrive as expected assert.True(t, err == nil) assert.Equals(t, len(changes), 3) assert.True(t, verifyChangesFullSequences(changes, []string{"1", "2", "2::6"})) // Test backfill of sequence the user doesn't have visibility to WriteDirect(db, []string{"PBS"}, 3) WriteDirect(db, []string{"ABC"}, 9) db.changeCache.waitForSequenceWithMissing(9) time.Sleep(50 * time.Millisecond) err = appendFromFeed(&changes, feed, 1) assert.Equals(t, len(changes), 4) assert.True(t, verifyChangesFullSequences(changes, []string{"1", "2", "2::6", "3::9"})) close(options.Terminator) }
func TestAccessFunction(t *testing.T) { /* var logKeys = map[string]bool { "CRUD": true, "Access": true, } base.UpdateLogKeys(logKeys, true) */ db := setupTestDB(t) defer tearDownTestDB(t, db) authenticator := auth.NewAuthenticator(db.Bucket, db) var err error db.ChannelMapper = channels.NewChannelMapper(`function(doc){access(doc.users,doc.userChannels);}`) user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("Netflix")) user.SetExplicitRoles(channels.TimedSet{"animefan": channels.NewVbSimpleSequence(1), "tumblr": channels.NewVbSimpleSequence(1)}) assertNoError(t, authenticator.Save(user), "Save") body := Body{"users": []string{"naomi"}, "userChannels": []string{"Hulu"}} _, err = db.Put("doc1", body) assertNoError(t, err, "") body = Body{"users": []string{"role:animefan"}, "userChannels": []string{"CrunchyRoll"}} _, err = db.Put("doc2", body) assertNoError(t, err, "") // Create the role _after_ creating the documents, to make sure the previously-indexed access // privileges are applied. role, _ := authenticator.NewRole("animefan", nil) authenticator.Save(role) user, err = authenticator.GetUser("naomi") assertNoError(t, err, "GetUser") expected := channels.AtSequence(channels.SetOf("Hulu", "Netflix", "!"), 1) assert.DeepEquals(t, user.Channels(), expected) expected.AddChannel("CrunchyRoll", 2) assert.DeepEquals(t, user.InheritedChannels(), expected) }
func TestSaveUsers(t *testing.T) { auth := NewAuthenticator(gTestBucket, nil) user, _ := auth.NewUser("testUser", "password", ch.SetOf("test")) err := auth.Save(user) assert.Equals(t, err, nil) user2, err := auth.GetUser("testUser") assert.Equals(t, err, nil) assert.DeepEquals(t, user2, user) }
func TestSaveRoles(t *testing.T) { auth := NewAuthenticator(gTestBucket, nil) role, _ := auth.NewRole("testRole", ch.SetOf("test")) err := auth.Save(role) assert.Equals(t, err, nil) role2, err := auth.GetRole("testRole") assert.Equals(t, err, nil) assert.DeepEquals(t, role2, role) }
// Test _changes with channel filter func postChangesChannelFilter(t *testing.T, it indexTester) { response := it.sendAdminRequest("PUT", "/_logging", `{"Changes":true, "Changes+":true, "HTTP":true, "DIndex+":true}`) assert.True(t, response != nil) // Create user: a := it.ServerContext().Database("db").Authenticator() bernard, err := a.NewUser("bernard", "letmein", channels.SetOf("PBS")) assert.True(t, err == nil) a.Save(bernard) // Put several documents response = it.sendAdminRequest("PUT", "/db/pbs1-0000609", `{"channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/samevbdiffchannel-0000609", `{"channel":["ABC"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/samevbdiffchannel-0000799", `{"channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs2-0000609", `{"channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs3-0000609", `{"channel":["PBS"]}`) assertStatus(t, response, 201) var changes struct { Results []db.ChangeEntry Last_Seq interface{} } changesJSON := `{"filter":"sync_gateway/bychannel", "channels":"PBS"}` changesResponse := it.send(requestByUser("POST", "/db/_changes", changesJSON, "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") assert.Equals(t, len(changes.Results), 4) // Put several more documents, some to the same vbuckets response = it.sendAdminRequest("PUT", "/db/pbs1-0000799", `{"value":1, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/abc1-0000609", `{"value":1, "channel":["ABC"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs2-0000799", `{"value":2, "channel":["PBS"]}`) assertStatus(t, response, 201) response = it.sendAdminRequest("PUT", "/db/pbs4", `{"value":4, "channel":["PBS"]}`) assertStatus(t, response, 201) time.Sleep(10 * time.Millisecond) changesResponse = it.send(requestByUser("POST", "/db/_changes", changesJSON, "bernard")) err = json.Unmarshal(changesResponse.Body.Bytes(), &changes) assertNoError(t, err, "Error unmarshalling changes response") for _, result := range changes.Results { log.Printf("changes result:%+v", result) } assert.Equals(t, len(changes.Results), 7) }
// Unit test for bug #314 func TestChangesAfterChannelAdded(t *testing.T) { base.LogKeys["Cache"] = true base.LogKeys["Changes"] = true base.LogKeys["Changes+"] = true db := setupTestDB(t) defer tearDownTestDB(t, db) db.ChannelMapper = channels.NewDefaultChannelMapper() // Create a user with access to channel ABC authenticator := db.Authenticator() user, _ := authenticator.NewUser("naomi", "letmein", channels.SetOf("ABC")) authenticator.Save(user) // Create a doc on two channels (sequence 1): revid, _ := db.Put("doc1", Body{"channels": []string{"ABC", "PBS"}}) // Modify user to have access to both channels (sequence 2): userInfo, err := db.GetPrincipal("naomi", true) assert.True(t, userInfo != nil) userInfo.ExplicitChannels = base.SetOf("ABC", "PBS") _, err = db.UpdatePrincipal(*userInfo, true, true) assertNoError(t, err, "UpdatePrincipal failed") // Check the _changes feed: db.changeCache.waitForSequence(1) db.user, _ = authenticator.GetUser("naomi") changes, err := db.GetChanges(base.SetOf("*"), ChangesOptions{Since: SequenceID{Seq: 1}}) assertNoError(t, err, "Couldn't GetChanges") assert.Equals(t, len(changes), 2) assert.DeepEquals(t, changes[0], &ChangeEntry{ Seq: SequenceID{Seq: 1, TriggeredBy: 2}, ID: "doc1", Changes: []ChangeRev{{"rev": revid}}}) assert.DeepEquals(t, changes[1], &ChangeEntry{ Seq: SequenceID{Seq: 2}, ID: "_user/naomi", Changes: []ChangeRev{}}) // Add a new doc (sequence 3): revid, _ = db.Put("doc2", Body{"channels": []string{"PBS"}}) // Check the _changes feed -- this is to make sure the changeCache properly received // sequence 2 (the user doc) and isn't stuck waiting for it. db.changeCache.waitForSequence(3) changes, err = db.GetChanges(base.SetOf("*"), ChangesOptions{Since: SequenceID{Seq: 2}}) assertNoError(t, err, "Couldn't GetChanges (2nd)") assert.Equals(t, len(changes), 1) assert.DeepEquals(t, changes[0], &ChangeEntry{ Seq: SequenceID{Seq: 3}, ID: "doc2", Changes: []ChangeRev{{"rev": revid}}}) }
func TestUpdateDesignDoc(t *testing.T) { db := setupTestDB(t) defer tearDownTestDB(t, db) err := db.PutDesignDoc("official", DesignDoc{}) assertNoError(t, err, "add design doc as admin") authenticator := auth.NewAuthenticator(db.Bucket, db) db.user, _ = authenticator.NewUser("naomi", "letmein", channels.SetOf("Netflix")) err = db.PutDesignDoc("_design/pwn3d", DesignDoc{}) assertHTTPError(t, err, 403) }
func (auth *Authenticator) defaultGuestUser() User { user := &userImpl{ roleImpl: roleImpl{ ExplicitChannels_: ch.AtSequence(ch.SetOf(), 1), }, userImplBody: userImplBody{ Disabled_: true, }, auth: auth, } user.Channels_ = user.ExplicitChannels_.Copy() return user }
func makeChangeEntry(logEntry *LogEntry, seqID SequenceID, channelName string) ChangeEntry { change := ChangeEntry{ Seq: seqID, ID: logEntry.DocID, Deleted: (logEntry.Flags & channels.Deleted) != 0, Changes: []ChangeRev{{"rev": logEntry.RevID}}, branched: (logEntry.Flags & channels.Branched) != 0, } if logEntry.Flags&channels.Removed != 0 { change.Removed = channels.SetOf(channelName) } return change }
func TestRebuildChannelsError(t *testing.T) { computer := mockComputer{} auth := NewAuthenticator(gTestBucket, &computer) role, err := auth.NewRole("testRole2", ch.SetOf("explicit1")) assert.Equals(t, err, nil) assert.Equals(t, auth.InvalidateChannels(role), nil) computer.err = fmt.Errorf("I'm sorry, Dave.") role2, err := auth.GetRole("testRole2") assert.Equals(t, role2, nil) assert.DeepEquals(t, err, computer.err) }
func TestSerializeRole(t *testing.T) { auth := NewAuthenticator(gTestBucket, nil) role, _ := auth.NewRole("froods", ch.SetOf("hoopy", "public")) encoded, _ := json.Marshal(role) assert.True(t, encoded != nil) log.Printf("Marshaled Role as: %s", encoded) elor := &roleImpl{} err := json.Unmarshal(encoded, elor) assert.True(t, err == nil) assert.DeepEquals(t, elor.Name(), role.Name()) assert.DeepEquals(t, elor.ExplicitChannels(), role.ExplicitChannels()) }
func TestViewQueryUserAccess(t *testing.T) { var rt restTester rt.ServerContext().Database("db").SetUserViewsEnabled(true) response := rt.sendAdminRequest("PUT", "/db/_design/foo", `{"views":{"bar": {"map":"function (doc, meta) { if (doc.type != 'type1') { return; } if (doc.state == 'state1' || doc.state == 'state2' || doc.state == 'state3') { emit(doc.state, meta.id); }}"}}}`) assertStatus(t, response, 201) response = rt.sendRequest("PUT", "/db/doc1", `{"type":"type1", "state":"state1"}`) assertStatus(t, response, 201) response = rt.sendRequest("PUT", "/db/doc2", `{"type":"type1", "state":"state2"}`) assertStatus(t, response, 201) response = rt.sendRequest("PUT", "/db/doc3", `{"type":"type2", "state":"state2"}`) assertStatus(t, response, 201) time.Sleep(5 * time.Second) response = rt.sendAdminRequest("GET", "/db/_design/foo/_view/bar?stale=false", ``) assertStatus(t, response, 200) var result sgbucket.ViewResult json.Unmarshal(response.Body.Bytes(), &result) assert.Equals(t, len(result.Rows), 2) assert.DeepEquals(t, result.Rows[0], &sgbucket.ViewRow{ID: "doc1", Key: "state1", Value: "doc1"}) assert.DeepEquals(t, result.Rows[1], &sgbucket.ViewRow{ID: "doc2", Key: "state2", Value: "doc2"}) response = rt.sendAdminRequest("GET", "/db/_design/foo/_view/bar?stale=false", ``) assertStatus(t, response, 200) json.Unmarshal(response.Body.Bytes(), &result) assert.Equals(t, len(result.Rows), 2) assert.DeepEquals(t, result.Rows[0], &sgbucket.ViewRow{ID: "doc1", Key: "state1", Value: "doc1"}) assert.DeepEquals(t, result.Rows[1], &sgbucket.ViewRow{ID: "doc2", Key: "state2", Value: "doc2"}) // Create a user: a := rt.ServerContext().Database("db").Authenticator() testUser, _ := a.NewUser("testUser", "123456", channels.SetOf("*")) a.Save(testUser) var userResult sgbucket.ViewResult request, _ := http.NewRequest("GET", "/db/_design/foo/_view/bar?stale=false", nil) request.SetBasicAuth("testUser", "123456") userResponse := rt.send(request) assertStatus(t, userResponse, 200) json.Unmarshal(userResponse.Body.Bytes(), &userResult) assert.Equals(t, len(result.Rows), 2) assert.DeepEquals(t, result.Rows[0], &sgbucket.ViewRow{ID: "doc1", Key: "state1", Value: "doc1"}) assert.DeepEquals(t, result.Rows[1], &sgbucket.ViewRow{ID: "doc2", Key: "state2", Value: "doc2"}) // Disable user view access, retry rt.ServerContext().Database("db").SetUserViewsEnabled(false) request, _ = http.NewRequest("GET", "/db/_design/foo/_view/bar?stale=false", nil) request.SetBasicAuth("testUser", "123456") userResponse = rt.send(request) assertStatus(t, userResponse, 403) }
func TestUserViewQuery(t *testing.T) { rt := restTester{syncFn: `function(doc) {channel(doc.channel)}`} a := rt.ServerContext().Database("db").Authenticator() rt.ServerContext().Database("db").SetUserViewsEnabled(true) // Create a view: response := rt.sendAdminRequest("PUT", "/db/_design/foo", `{"views":{"bar": {"map": "function(doc) {emit(doc.key, doc.value);}"}}}`) assertStatus(t, response, 201) // Create docs: response = rt.sendRequest("PUT", "/db/doc1", `{"key":10, "value":"ten", "channel":"W"}`) assertStatus(t, response, 201) response = rt.sendRequest("PUT", "/db/doc2", `{"key":7, "value":"seven", "channel":"Q"}`) assertStatus(t, response, 201) // Create a user: quinn, _ := a.NewUser("quinn", "123456", channels.SetOf("Q", "q")) a.Save(quinn) // Have the user query the view: request, _ := http.NewRequest("GET", "/db/_design/foo/_view/bar?include_docs=true", nil) request.SetBasicAuth("quinn", "123456") response = rt.send(request) assertStatus(t, response, 200) var result sgbucket.ViewResult json.Unmarshal(response.Body.Bytes(), &result) assert.Equals(t, len(result.Rows), 1) assert.Equals(t, result.TotalRows, 1) row := result.Rows[0] assert.Equals(t, row.Key, float64(7)) assert.Equals(t, row.Value, "seven") assert.DeepEquals(t, *row.Doc, map[string]interface{}{"key": 7.0, "value": "seven", "channel": "Q"}) // Admin should see both rows: response = rt.sendAdminRequest("GET", "/db/_design/foo/_view/bar", ``) assertStatus(t, response, 200) json.Unmarshal(response.Body.Bytes(), &result) assert.Equals(t, len(result.Rows), 2) row = result.Rows[0] assert.Equals(t, row.Key, float64(7)) assert.Equals(t, row.Value, "seven") assert.DeepEquals(t, *row.Doc, map[string]interface{}{"key": 7.0, "value": "seven", "channel": "Q"}) row = result.Rows[1] assert.Equals(t, row.Key, float64(10)) assert.Equals(t, row.Value, "ten") assert.DeepEquals(t, *row.Doc, map[string]interface{}{"key": 10.0, "value": "ten", "channel": "W"}) // Make sure users are not allowed to query internal views: request, _ = http.NewRequest("GET", "/db/_design/sync_gateway/_view/access", nil) request.SetBasicAuth("quinn", "123456") response = rt.send(request) assertStatus(t, response, 403) }
func CouchbaseTestAccessFunctionWithVbuckets(t *testing.T) { //base.LogKeys["CRUD"] = true //base.LogKeys["Access"] = true db := setupTestDB(t) defer tearDownTestDB(t, db) db.SequenceType = ClockSequenceType authenticator := auth.NewAuthenticator(db.Bucket, db) var err error db.ChannelMapper = channels.NewChannelMapper(`function(doc){access(doc.users,doc.userChannels);}`) user, _ := authenticator.NewUser("bernard", "letmein", channels.SetOf("Netflix")) assertNoError(t, authenticator.Save(user), "Save") body := Body{"users": []string{"bernard"}, "userChannels": []string{"ABC"}} _, err = db.Put("doc1", body) assertNoError(t, err, "") time.Sleep(100 * time.Millisecond) user, err = authenticator.GetUser("bernard") assertNoError(t, err, "GetUser") expected := channels.TimedSetFromString("ABC:5.1,Netflix:1,!:1") assert.DeepEquals(t, user.Channels(), expected) body = Body{"users": []string{"bernard"}, "userChannels": []string{"NBC"}} _, err = db.Put("doc2", body) assertNoError(t, err, "") time.Sleep(100 * time.Millisecond) user, err = authenticator.GetUser("bernard") assertNoError(t, err, "GetUser") expected = channels.TimedSetFromString("ABC:5.1,NBC:12.1,Netflix:1,!:1") assert.DeepEquals(t, user.Channels(), expected) // Have another doc assign a new channel, and one of the previously present channels body = Body{"users": []string{"bernard"}, "userChannels": []string{"ABC", "PBS"}} _, err = db.Put("doc3", body) assertNoError(t, err, "") time.Sleep(100 * time.Millisecond) user, err = authenticator.GetUser("bernard") assertNoError(t, err, "GetUser") expected = channels.TimedSetFromString("ABC:5.1,NBC:12.1,PBS:11.1,Netflix:1,!:1") assert.DeepEquals(t, user.Channels(), expected) }
func TestSessionExtension(t *testing.T) { var rt restTester a := auth.NewAuthenticator(rt.bucket(), nil) user, err := a.GetUser("") assert.Equals(t, err, nil) user.SetDisabled(true) err = a.Save(user) assert.Equals(t, err, nil) user, err = a.GetUser("") assert.Equals(t, err, nil) assert.True(t, user.Disabled()) log.Printf("hello") response := rt.sendRequest("PUT", "/db/doc", `{"hi": "there"}`) assertStatus(t, response, 401) user, err = a.NewUser("pupshaw", "letmein", channels.SetOf("*")) a.Save(user) assertStatus(t, rt.sendAdminRequest("GET", "/db/_session", ""), 200) response = rt.sendAdminRequest("POST", "/db/_session", `{"name":"pupshaw", "ttl":10}`) assertStatus(t, response, 200) var body db.Body json.Unmarshal(response.Body.Bytes(), &body) sessionId := body["session_id"].(string) sessionExpiration := body["expires"].(string) assert.True(t, sessionId != "") assert.True(t, sessionExpiration != "") assert.True(t, body["cookie_name"].(string) == "SyncGatewaySession") reqHeaders := map[string]string{ "Cookie": "SyncGatewaySession=" + body["session_id"].(string), } response = rt.sendRequestWithHeaders("PUT", "/db/doc1", `{"hi": "there"}`, reqHeaders) assertStatus(t, response, 201) assert.True(t, response.Header().Get("Set-Cookie") == "") //Sleep for 2 seconds, this will ensure 10% of the 100 seconds session ttl has elapsed and //should cause a new Cookie to be sent by the server with the same session ID and an extended expiration date time.Sleep(2 * time.Second) response = rt.sendRequestWithHeaders("PUT", "/db/doc2", `{"hi": "there"}`, reqHeaders) assertStatus(t, response, 201) assert.True(t, response.Header().Get("Set-Cookie") != "") }
func TestRoleInheritance(t *testing.T) { // Create some roles: auth := NewAuthenticator(gTestBucket, nil) role, _ := auth.NewRole("square", ch.SetOf("dull", "duller", "dullest")) assert.Equals(t, auth.Save(role), nil) role, _ = auth.NewRole("frood", ch.SetOf("hoopy", "hoopier", "hoopiest")) assert.Equals(t, auth.Save(role), nil) user, _ := auth.NewUser("arthur", "password", ch.SetOf("britain")) user.(*userImpl).setRolesSince(ch.TimedSet{"square": ch.NewVbSimpleSequence(0x3), "nonexistent": ch.NewVbSimpleSequence(0x42), "frood": ch.NewVbSimpleSequence(0x4)}) assert.DeepEquals(t, user.RoleNames(), ch.TimedSet{"square": ch.NewVbSimpleSequence(0x3), "nonexistent": ch.NewVbSimpleSequence(0x42), "frood": ch.NewVbSimpleSequence(0x4)}) auth.Save(user) user2, err := auth.GetUser("arthur") assert.Equals(t, err, nil) log.Printf("Channels = %s", user2.Channels()) assert.DeepEquals(t, user2.Channels(), ch.AtSequence(ch.SetOf("!", "britain"), 1)) assert.DeepEquals(t, user2.InheritedChannels(), ch.TimedSet{"!": ch.NewVbSimpleSequence(0x1), "britain": ch.NewVbSimpleSequence(0x1), "dull": ch.NewVbSimpleSequence(0x3), "duller": ch.NewVbSimpleSequence(0x3), "dullest": ch.NewVbSimpleSequence(0x3), "hoopy": ch.NewVbSimpleSequence(0x4), "hoopier": ch.NewVbSimpleSequence(0x4), "hoopiest": ch.NewVbSimpleSequence(0x4)}) assert.True(t, user2.CanSeeChannel("britain")) assert.True(t, user2.CanSeeChannel("duller")) assert.True(t, user2.CanSeeChannel("hoopy")) assert.Equals(t, user2.AuthorizeAllChannels(ch.SetOf("britain", "dull", "hoopiest")), nil) }
//Test that TTL values greater than the default max offset TTL 2592000 seconds are processed correctly // fixes #974 func TestSessionTtlGreaterThan30Days(t *testing.T) { var rt restTester a := auth.NewAuthenticator(rt.bucket(), nil) user, err := a.GetUser("") assert.Equals(t, err, nil) user.SetDisabled(true) err = a.Save(user) assert.Equals(t, err, nil) user, err = a.GetUser("") assert.Equals(t, err, nil) assert.True(t, user.Disabled()) response := rt.sendRequest("PUT", "/db/doc", `{"hi": "there"}`) assertStatus(t, response, 401) user, err = a.NewUser("pupshaw", "letmein", channels.SetOf("*")) a.Save(user) //create a session with the maximum offset ttl value (30days) 2592000 seconds response = rt.sendAdminRequest("POST", "/db/_session", `{"name":"pupshaw", "ttl":2592000}`) assertStatus(t, response, 200) layout := "2006-01-02T15:04:05" var body db.Body json.Unmarshal(response.Body.Bytes(), &body) log.Printf("expires %s", body["expires"].(string)) expires, err := time.Parse(layout, body["expires"].(string)[:19]) assert.Equals(t, err, nil) //create a session with a ttl value one second greater thatn the max offset ttl 2592001 seconds response = rt.sendAdminRequest("POST", "/db/_session", `{"name":"pupshaw", "ttl":2592001}`) assertStatus(t, response, 200) body = nil json.Unmarshal(response.Body.Bytes(), &body) log.Printf("expires2 %s", body["expires"].(string)) expires2, err := time.Parse(layout, body["expires"].(string)[:19]) assert.Equals(t, err, nil) //Allow a ten second drift between the expires dates, to pass test on slow servers acceptableTimeDelta := time.Duration(10) * time.Second //The difference between the two expires dates should be less than the acceptable time delta assert.True(t, expires2.Sub(expires) < acceptableTimeDelta) }
func TestSerializeUser(t *testing.T) { auth := NewAuthenticator(gTestBucket, nil) user, _ := auth.NewUser("me", "letmein", ch.SetOf("me", "public")) user.SetEmail("*****@*****.**") encoded, _ := json.Marshal(user) assert.True(t, encoded != nil) log.Printf("Marshaled User as: %s", encoded) resu := &userImpl{} err := json.Unmarshal(encoded, resu) assert.True(t, err == nil) assert.DeepEquals(t, resu.Name(), user.Name()) assert.DeepEquals(t, resu.Email(), user.Email()) assert.DeepEquals(t, resu.ExplicitChannels(), user.ExplicitChannels()) assert.True(t, resu.Authenticate("letmein")) assert.False(t, resu.Authenticate("123456")) }