Skip to content

Commit

Permalink
CBG-3559: Inherited channels from roles are not checked when running …
Browse files Browse the repository at this point in the history
…changes feed filtered to a channel (#6551) (#6552)

* CBG-3559: ensure we check inherited channels across assigned roles when filtering the changes feed to availible channels to the user

* chnage implementation, add userImpl canSeeCollectionChannelSince

* remove unused function
  • Loading branch information
gregns1 authored Oct 24, 2023
1 parent 37e3612 commit 1e1a686
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
10 changes: 10 additions & 0 deletions auth/user_collection_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,13 @@ func (user *userImpl) AuthorizeAnyCollectionChannel(scope, collection string, ch

return user.UnauthError("You are not allowed to see this")
}

func (user *userImpl) canSeeCollectionChannelSince(scope, collection, channel string) uint64 {
minSeq := user.roleImpl.canSeeCollectionChannelSince(scope, collection, channel)
for _, role := range user.GetRoles() {
if seq := role.canSeeCollectionChannelSince(scope, collection, channel); seq > 0 && (seq < minSeq || minSeq == 0) {
minSeq = seq
}
}
return minSeq
}
65 changes: 65 additions & 0 deletions rest/changestest/changes_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,71 @@ func TestDocDeletionFromChannel(t *testing.T) {
assert.Equal(t, 200, response.Code)
}

// TestChangesFeedOnInheritedChannelsFromRoles:
// - Create a role with access to channel "A"
// - Create a user with the role just created
// - Put some docs on the database with channel "A" assigned
// - Run changes feed as the user with a channel filter on it to channel A
func TestChangesFeedOnInheritedChannelsFromRoles(t *testing.T) {
base.SetUpTestLogging(t, base.LevelInfo, base.KeyChanges, base.KeyHTTP)

rt := rest.NewRestTester(t, &rest.RestTesterConfig{SyncFn: channels.DocChannelsSyncFunction})
defer rt.Close()
collection := rt.GetSingleTestDatabaseCollection()

// create role with collection channel access set to channel A
resp := rt.SendAdminRequest(http.MethodPut, "/{{.db}}/_role/role", rest.GetRolePayload(t, "", "", collection, []string{"A"}))
rest.RequireStatus(t, resp, http.StatusCreated)

// create user and assign the role create above to that user
resp = rt.SendAdminRequest(http.MethodPut, "/{{.db}}/_user/alice", `{"name": "alice", "password": "letmein", "admin_roles": ["role"]}`)
rest.RequireStatus(t, resp, http.StatusCreated)

// Put a some doc on the database with channel A assigned
for i := 0; i < 5; i++ {
resp = rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/"+fmt.Sprint(i), `{"source": "rt", "channels":["A"]}`)
rest.RequireStatus(t, resp, http.StatusCreated)
}

// Start changes feed as the user filtered to channel A, expect 6 changes (5 docs + user doc)
changes, err := rt.WaitForChanges(6, "/{{.keyspace}}/_changes?filter=sync_gateway/bychannel&channels=A", "alice", false)
require.NoError(t, err)
assert.Equal(t, 6, len(changes.Results))
}

// TestChangesFeedOnInheritedChannelsFromRolesDefaultCollection:
// - Same concept as TestChangesFeedOnInheritedChannelsFromRoles just with use of default collection instead
// - Create a role with access to channel "A"
// - Create a user with the role just created
// - Put some docs on the database with channel "A" assigned
// - Run changes feed as the user with a channel filter on it to channel A
func TestChangesFeedOnInheritedChannelsFromRolesDefaultCollection(t *testing.T) {
base.SetUpTestLogging(t, base.LevelInfo, base.KeyChanges, base.KeyHTTP)

rt := rest.NewRestTesterDefaultCollection(t, &rest.RestTesterConfig{SyncFn: channels.DocChannelsSyncFunction})
defer rt.Close()
collection := rt.GetSingleTestDatabaseCollection()

// create role with collection channel access set to channel A
resp := rt.SendAdminRequest(http.MethodPut, "/{{.db}}/_role/role", rest.GetRolePayload(t, "", "", collection, []string{"A"}))
rest.RequireStatus(t, resp, http.StatusCreated)

// create user and assign the role create above to that user
resp = rt.SendAdminRequest(http.MethodPut, "/{{.db}}/_user/alice", `{"name": "alice", "password": "letmein", "admin_roles": ["role"]}`)
rest.RequireStatus(t, resp, http.StatusCreated)

// Put a some doc on the database with channel A assigned
for i := 0; i < 5; i++ {
resp = rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/"+fmt.Sprint(i), `{"source": "rt", "channels":["A"]}`)
rest.RequireStatus(t, resp, http.StatusCreated)
}

// Start changes feed as the user filtered to channel A, expect 6 changes (5 docs + user doc)
changes, err := rt.WaitForChanges(6, "/{{.keyspace}}/_changes?filter=sync_gateway/bychannel&channels=A", "alice", false)
require.NoError(t, err)
assert.Equal(t, 6, len(changes.Results))
}

func TestPostChanges(t *testing.T) {

base.SetUpTestLogging(t, base.LevelInfo, base.KeyChanges, base.KeyHTTP)
Expand Down

0 comments on commit 1e1a686

Please sign in to comment.