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) log.Printf("Channels = %s", user2.Channels()) assert.DeepEquals(t, user2.Channels(), ch.AtSequence(ch.SetOf("explicit1", "derived1", "derived2"), 1)) }
func TestRebuildUserRoles(t *testing.T) { computer := mockComputer{roles: ch.AtSequence(base.SetOf("role1", "role2"), 3)} auth := NewAuthenticator(gTestBucket, &computer) user, _ := auth.NewUser("testUser", "letmein", nil) user.SetExplicitRoles(ch.TimedSet{"role3": 1, "role1": 1}) err := auth.InvalidateRoles(user) assert.Equals(t, err, nil) user2, err := auth.GetUser("testUser") assert.Equals(t, err, nil) expected := ch.AtSequence(base.SetOf("role1", "role3"), 1) expected.AddChannel("role2", 3) assert.DeepEquals(t, user2.RoleNames(), expected) }
// Updates a document's channel/role UserAccessMap with new access settings from an AccessMap. // Returns an array of the user/role names whose access has changed as a result. func (accessMap *UserAccessMap) updateAccess(doc *document, newAccess channels.AccessMap) (changedUsers []string) { // Update users already appearing in doc.Access: for name, access := range *accessMap { if access.UpdateAtSequence(newAccess[name], doc.Sequence) { if len(access) == 0 { delete(*accessMap, name) } changedUsers = append(changedUsers, name) } } // Add new users who are in newAccess but not accessMap: for name, access := range newAccess { if _, existed := (*accessMap)[name]; !existed { if *accessMap == nil { *accessMap = UserAccessMap{} } (*accessMap)[name] = channels.AtSequence(access, doc.Sequence) changedUsers = append(changedUsers, name) } } if changedUsers != nil { what := "channel" if accessMap == &doc.RoleAccess { what = "role" } base.LogTo("Access", "Doc %q grants %s access: %v", doc.ID, what, *accessMap) } return changedUsers }
func (rt *restTester) setAdminParty(partyTime bool) { a := rt.ServerContext().Database("db").Authenticator() guest, _ := a.GetUser("") guest.SetDisabled(!partyTime) var chans channels.TimedSet if partyTime { chans = channels.AtSequence(base.SetOf("*"), 1) } guest.SetExplicitChannels(chans) a.Save(guest) }
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 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).setRoleNames([]string{"square", "nonexistent", "frood"}) assert.DeepEquals(t, user.RoleNames(), []string{"square", "nonexistent", "frood"}) 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.AtSequence(ch.SetOf("britain", "dull", "duller", "dullest", "hoopy", "hoopier", "hoopiest"), 1)) 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) }
func (user *userImpl) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &user.userImplBody); err != nil { return err } else if err := json.Unmarshal(data, &user.roleImpl); err != nil { return err } // Migrate "admin_roles" field: if user.OldExplicitRoles_ != nil { user.ExplicitRoles_ = ch.AtSequence(base.SetFromArray(user.OldExplicitRoles_), 1) user.OldExplicitRoles_ = nil } return nil }
func TestAccessFunction(t *testing.T) { //base.LogKeys["CRUD"] = true //base.LogKeys["Access"] = 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.SetExplicitRoleNames([]string{"animefan", "tumblr"}) 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) }
// Returns the (ordered) union of all of the changes made to multiple channels. func (db *Database) MultiChangesFeed(chans base.Set, options ChangesOptions) (<-chan *ChangeEntry, error) { if len(chans) == 0 { return nil, nil } base.LogTo("Changes", "MultiChangesFeed(%s, %+v) ...", chans, options) var changeWaiter *changeWaiter if options.Wait { options.Wait = false changeWaiter = db.tapListener.NewWaiterWithChannels(chans, db.user) } if options.Since == nil { options.Since = channels.TimedSet{} } output := make(chan *ChangeEntry, kChangesViewPageSize) go func() { defer close(output) // This loop is used to re-run the fetch after every database change, in Wait mode outer: for { // Restrict to available channels, expand wild-card, and find since when these channels // have been available to the user: var channelsSince channels.TimedSet if db.user != nil { channelsSince = db.user.FilterToAvailableChannels(chans) } else { channelsSince = channels.AtSequence(chans, 1) } base.LogTo("Changes", "MultiChangesFeed: channels expand to %s ...", channelsSince) // Populate the parallel arrays of channels and names: latestSequence, err := db.LastSequence() if err != nil { base.Warn("MultiChangesFeed got error from LastSequence: %v", err) return } feeds := make([]<-chan *ChangeEntry, 0, len(channelsSince)) names := make([]string, 0, len(channelsSince)) for name, _ := range channelsSince { var feed <-chan *ChangeEntry if latestSequence > options.Since[name] { var err error feed, err = db.changesFeed(name, options) if err != nil { base.Warn("MultiChangesFeed got error reading changes feed %q: %v", name, err) return } } feeds = append(feeds, feed) names = append(names, name) } current := make([]*ChangeEntry, len(feeds)) // This loop reads the available entries from all the feeds in parallel, merges them, // and writes them to the output channel: var sentSomething bool for { //FIX: This assumes Reverse or Limit aren't set in the options // Read more entries to fill up the current[] array: for i, cur := range current { if cur == nil && feeds[i] != nil { var ok bool current[i], ok = <-feeds[i] if !ok { feeds[i] = nil } } } // Find the current entry with the minimum sequence: var minSeq uint64 = math.MaxUint64 var minEntry *ChangeEntry for _, cur := range current { if cur != nil && cur.seqNo < minSeq { minSeq = cur.seqNo minEntry = cur } } if minEntry == nil { break // Exit the loop when there are no more entries } // Clear the current entries for the sequence just sent: for i, cur := range current { if cur != nil && cur.seqNo == minSeq { current[i] = nil // Update the public sequence ID and encode it into the entry: options.Since[names[i]] = minSeq cur.Seq = options.Since.String() cur.seqNo = 0 // 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) } } } } // Send the entry, and repeat the loop: select { case <-options.Terminator: base.LogTo("Changes+", "Aborting MultiChangesFeed") return case output <- minEntry: } sentSomething = true // Stop when we hit the limit (if any): if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break outer } } } if sentSomething || changeWaiter == nil { break } // If nothing found, and in wait mode: wait for the db to change, then run again. // First notify the reader that we're waiting by sending a nil. output <- nil if !changeWaiter.Wait() { break } // Before checking again, update the User object in case its channel access has // changed while waiting: if err := db.ReloadUser(); err != nil { base.Warn("Error reloading user %q: %v", db.user.Name(), err) return } } base.LogTo("Changes", "MultiChangesFeed done") }() return output, nil }
// Returns the (ordered) union of all of the changes made to multiple channels. func (db *Database) MultiChangesFeed(chans base.Set, options ChangesOptions) (<-chan *ChangeEntry, error) { if len(chans) == 0 { return nil, nil } base.LogTo("Changes", "MultiChangesFeed(%s, %+v) ...", chans, options) if (options.Continuous || options.Wait) && options.Terminator == nil { base.Warn("MultiChangesFeed: Terminator missing for Continuous/Wait mode") } output := make(chan *ChangeEntry, 50) go func() { defer func() { base.LogTo("Changes", "MultiChangesFeed done") close(output) }() var changeWaiter *changeWaiter var userChangeCount uint64 if options.Wait { options.Wait = false changeWaiter = db.tapListener.NewWaiterWithChannels(chans, db.user) userChangeCount = changeWaiter.CurrentUserCount() } // This loop is used to re-run the fetch after every database change, in Wait mode outer: for { // Restrict to available channels, expand wild-card, and find since when these channels // have been available to the user: var channelsSince channels.TimedSet if db.user != nil { channelsSince = db.user.FilterToAvailableChannels(chans) } else { channelsSince = channels.AtSequence(chans, 0) } base.LogTo("Changes+", "MultiChangesFeed: channels expand to %#v ...", channelsSince) // Populate the parallel arrays of channels and names: feeds := make([]<-chan *ChangeEntry, 0, len(channelsSince)) names := make([]string, 0, len(channelsSince)) for name, seqAddedAt := range channelsSince { chanOpts := options if seqAddedAt > 1 && options.Since.Before(SequenceID{Seq: seqAddedAt}) { // Newly added channel so send all of it to user: chanOpts.Since = SequenceID{Seq: 0, TriggeredBy: seqAddedAt} } feed, err := db.changesFeed(name, chanOpts) if err != nil { base.Warn("MultiChangesFeed got error reading changes feed %q: %v", name, err) return } feeds = append(feeds, feed) names = append(names, name) } // If the user object has changed, create a special pseudo-feed for it: if db.user != nil { userSeq := SequenceID{Seq: db.user.Sequence()} if options.Since.Before(userSeq) { name := db.user.Name() if name == "" { name = "GUEST" } entry := ChangeEntry{ Seq: userSeq, ID: "_user/" + name, Changes: []ChangeRev{}, } userFeed := make(chan *ChangeEntry, 1) userFeed <- &entry close(userFeed) feeds = append(feeds, userFeed) names = append(names, entry.ID) } } current := make([]*ChangeEntry, len(feeds)) // This loop reads the available entries from all the feeds in parallel, merges them, // and writes them to the output channel: var sentSomething bool for { // Read more entries to fill up the current[] array: for i, cur := range current { if cur == nil && feeds[i] != nil { var ok bool current[i], ok = <-feeds[i] if !ok { feeds[i] = nil } } } // Find the current entry with the minimum sequence: minSeq := MaxSequenceID var minEntry *ChangeEntry for _, cur := range current { if cur != nil && cur.Seq.Before(minSeq) { minSeq = cur.Seq minEntry = cur } } if minEntry == nil { break // Exit the loop when there are no more entries } // Clear the current entries for the sequence just sent: for i, cur := range current { if cur != nil && cur.Seq == minSeq { current[i] = nil //options.Since = minSeq //TEMP ??? // 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) } } } } if !options.Since.Before(minSeq) { continue // out of order; skip it } // Add the doc body or the conflicting rev IDs, if those options are set: if options.IncludeDocs || options.Conflicts { db.addDocToChangeEntry(minEntry, options) } // Send the entry, and repeat the loop: base.LogTo("Changes+", "MultiChangesFeed sending %+v", minEntry) select { case <-options.Terminator: return case output <- minEntry: } sentSomething = true // Stop when we hit the limit (if any): if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break outer } } } if !options.Continuous && (sentSomething || changeWaiter == nil) { break } // If nothing found, and in wait mode: wait for the db to change, then run again. // First notify the reader that we're waiting by sending a nil. base.LogTo("Changes+", "MultiChangesFeed waiting...") output <- nil if !changeWaiter.Wait() { break } // Check whether I was terminated while waiting for a change: select { case <-options.Terminator: return default: } // Before checking again, update the User object in case its channel access has // changed while waiting: if newCount := changeWaiter.CurrentUserCount(); newCount > userChangeCount { base.LogTo("Changes+", "MultiChangesFeed reloading user %q", db.user.Name()) userChangeCount = newCount if err := db.ReloadUser(); err != nil { base.Warn("Error reloading user %q: %v", db.user.Name(), err) return } } } }() return output, nil }
func TestUserAccess(t *testing.T) { // User with no access: auth := NewAuthenticator(gTestBucket, nil) user, _ := auth.NewUser("foo", "password", nil) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf()) assert.False(t, user.CanSeeChannel("x")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.False(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("*"))) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) assert.False(t, user.AuthorizeAnyChannel(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAnyChannel(ch.SetOf()) == nil) // User with access to one channel: user.setChannels(ch.AtSequence(ch.SetOf("x"), 1)) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf("x")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.True(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) assert.True(t, user.AuthorizeAnyChannel(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAnyChannel(ch.SetOf("y")) == nil) assert.False(t, user.AuthorizeAnyChannel(ch.SetOf()) == nil) // User with access to one channel and one derived channel: user.setChannels(ch.AtSequence(ch.SetOf("x", "z"), 1)) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf("x", "z")) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("x")), ch.SetOf("x")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.True(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) // User with access to two channels: user.setChannels(ch.AtSequence(ch.SetOf("x", "z"), 1)) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf("x", "z")) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("x")), ch.SetOf("x")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.True(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) user.setChannels(ch.AtSequence(ch.SetOf("x", "y"), 1)) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf("x", "y")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.True(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.True(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.False(t, canSeeAllChannels(user, ch.SetOf("x", "y", "z"))) assert.True(t, user.AuthorizeAllChannels(ch.SetOf("x", "y")) == nil) assert.False(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) // User with wildcard access: user.setChannels(ch.AtSequence(ch.SetOf("*", "q"), 1)) assert.DeepEquals(t, user.ExpandWildCardChannel(ch.SetOf("*")), ch.SetOf("*", "q")) assert.True(t, user.CanSeeChannel("*")) assert.True(t, canSeeAllChannels(user, ch.SetOf())) assert.True(t, canSeeAllChannels(user, ch.SetOf("x"))) assert.True(t, canSeeAllChannels(user, ch.SetOf("x", "y"))) assert.True(t, user.AuthorizeAllChannels(ch.SetOf("x", "y")) == nil) assert.True(t, user.AuthorizeAllChannels(ch.SetOf("*")) == nil) assert.True(t, user.AuthorizeAnyChannel(ch.SetOf("x")) == nil) assert.True(t, user.AuthorizeAnyChannel(ch.SetOf("*")) == nil) assert.True(t, user.AuthorizeAnyChannel(ch.SetOf()) == nil) }
func (role *roleImpl) initRole(name string, channels base.Set) error { channels = ch.ExpandingStar(channels) role.Name_ = name role.ExplicitChannels_ = ch.AtSequence(channels, 1) return role.validate() }