Files
Felipe MartinandGitHub d7f9e52f82 MM-68830: Preserve unknown permissions during migrations on downgrade (#36888)
* MM-68830: Preserve unknown permissions during migrations on downgrade

A server that was upgraded to a newer release (which introduced new
permissions and wrote them into roles) and then downgraded fails fatally
at startup: the permissions migration re-saves every role, and
Role.Save() rejects any permission the older binary does not recognize,
making the downgrade unrecoverable.

Add RoleStore.SavePreservingUnknownPermissions, used only by
doPermissionsMigration, which tolerates and preserves permissions this
build does not recognize (logging a warning) instead of rejecting the
role. The regular Save() — and therefore the role API path — stays
strict, so unknown permissions cannot be introduced through user input.

Unrecognized permissions are kept on disk so they are not lost on a
later re-upgrade.

* MM-68830: assert save forwarding in role cache tests

Address review feedback: assert the underlying store's Save and
SavePreservingUnknownPermissions are actually invoked (the cache
invalidation defer fires regardless of forwarding), and check the
returned errors.

* MM-68830: address review feedback

- Shorten log message in validateForSave
- Rename validationRole -> roleCopy for clarity
- Trim doc comments to describe behavior only
- List all unknown permissions in IsValidWithoutId error
- Assert specific error type in storetest

* MM-68830: add Role.Clone and use it in validateForSave

* MM-68830: add tests for Role.Clone

* MM-68830: fix scheme id deep copy assertion in Role.Clone test
2026-06-09 11:18:19 +02:00

696 lines
21 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
func TestRoleStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
t.Run("Save", func(t *testing.T) { testRoleStoreSave(t, rctx, ss) })
t.Run("SavePreservingUnknownPermissions", func(t *testing.T) { testRoleStoreSavePreservingUnknownPermissions(t, rctx, ss) })
t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, rctx, ss) })
t.Run("GetAll", func(t *testing.T) { testRoleStoreGetAll(t, rctx, ss) })
t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, rctx, ss) })
t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, rctx, ss) })
t.Run("Delete", func(t *testing.T) { testRoleStoreDelete(t, rctx, ss) })
t.Run("PermanentDeleteAll", func(t *testing.T) { testRoleStorePermanentDeleteAll(t, rctx, ss) })
t.Run("LowerScopedChannelSchemeRoles_AllChannelSchemeRoles", func(t *testing.T) { testRoleStoreLowerScopedChannelSchemeRoles(t, rctx, ss) })
t.Run("ChannelHigherScopedPermissionsBlankTeamSchemeChannelGuest", func(t *testing.T) {
testRoleStoreChannelHigherScopedPermissionsBlankTeamSchemeChannelGuest(t, rctx, ss, s)
})
}
func testRoleStoreSave(t *testing.T, rctx request.CTX, ss store.Store) {
// Save a new role.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
d1, err := ss.Role().Save(r1)
assert.NoError(t, err)
assert.Len(t, d1.Id, 26)
assert.Equal(t, r1.Name, d1.Name)
assert.Equal(t, r1.DisplayName, d1.DisplayName)
assert.Equal(t, r1.Description, d1.Description)
assert.Equal(t, r1.Permissions, d1.Permissions)
assert.Equal(t, r1.SchemeManaged, d1.SchemeManaged)
// Change the role permissions and update.
d1.Permissions = []string{
"invite_user",
"add_user_to_team",
"delete_public_channel",
}
d2, err := ss.Role().Save(d1)
assert.NoError(t, err)
assert.Len(t, d2.Id, 26)
assert.Equal(t, r1.Name, d2.Name)
assert.Equal(t, r1.DisplayName, d2.DisplayName)
assert.Equal(t, r1.Description, d2.Description)
assert.Equal(t, d1.Permissions, d2.Permissions)
assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged)
// Try saving one with an invalid ID set.
r3 := &model.Role{
Id: model.NewId(),
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
_, err = ss.Role().Save(r3)
assert.Error(t, err)
// Try saving one with a duplicate "name" field.
r4 := &model.Role{
Name: r1.Name,
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
_, err = ss.Role().Save(r4)
assert.Error(t, err)
}
func testRoleStoreSavePreservingUnknownPermissions(t *testing.T, _ request.CTX, ss store.Store) {
// A role whose permissions are all valid for this build saves and round-trips
// unchanged, just like Save.
t.Run("preserves known permissions like Save", func(t *testing.T) {
r := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{"invite_user", "add_user_to_team"},
SchemeManaged: false,
}
saved, err := ss.Role().SavePreservingUnknownPermissions(r)
require.NoError(t, err)
assert.Equal(t, r.Permissions, saved.Permissions)
})
// The downgrade scenario from MM-68830: an existing role gains a permission this
// build does not recognize (written by a newer release before the downgrade). The
// migration re-saves every role; Save would reject the unknown permission, but
// SavePreservingUnknownPermissions must keep it so it is not lost on a future upgrade.
t.Run("tolerates and persists unknown permissions on update", func(t *testing.T) {
unknown := "manage_own_agent_from_the_future"
// Create the role as a known-good role first (the pre-downgrade state).
existing, err := ss.Role().Save(&model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{"invite_user"},
})
require.NoError(t, err)
// Simulate the newer release having added an unknown permission to the role.
existing.Permissions = append(existing.Permissions, unknown)
// Sanity check: the regular Save rejects the unknown permission.
_, err = ss.Role().Save(existing)
require.Error(t, err)
saved, err := ss.Role().SavePreservingUnknownPermissions(existing)
require.NoError(t, err)
assert.Contains(t, saved.Permissions, unknown, "unknown permission should be preserved")
// It must actually be persisted, not just returned.
fetched, err := ss.Role().Get(saved.Id)
require.NoError(t, err)
assert.Contains(t, fetched.Permissions, unknown)
})
// Tolerating unknown permissions must not mask genuine structural problems.
t.Run("still rejects structurally invalid roles", func(t *testing.T) {
r := &model.Role{
Name: "invalid-name",
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{"manage_own_agent_from_the_future"},
SchemeManaged: false,
}
_, err := ss.Role().SavePreservingUnknownPermissions(r)
require.Error(t, err)
var invErr *store.ErrInvalidInput
require.ErrorAs(t, err, &invErr)
})
}
func testRoleStoreGetAll(t *testing.T, rctx request.CTX, ss store.Store) {
prev, err := ss.Role().GetAll()
require.NoError(t, err)
prevCount := len(prev)
// Save a role to test with.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
_, err = ss.Role().Save(r1)
require.NoError(t, err)
r2 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
_, err = ss.Role().Save(r2)
require.NoError(t, err)
data, err := ss.Role().GetAll()
require.NoError(t, err)
assert.Len(t, data, prevCount+2)
}
func testRoleStoreGet(t *testing.T, rctx request.CTX, ss store.Store) {
// Save a role to test with.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
d1, err := ss.Role().Save(r1)
assert.NoError(t, err)
assert.Len(t, d1.Id, 26)
// Get a valid role
d2, err := ss.Role().Get(d1.Id)
assert.NoError(t, err)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, r1.Name, d2.Name)
assert.Equal(t, r1.DisplayName, d2.DisplayName)
assert.Equal(t, r1.Description, d2.Description)
assert.Equal(t, r1.Permissions, d2.Permissions)
assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged)
// Get an invalid role
_, err = ss.Role().Get(model.NewId())
assert.Error(t, err)
}
func testRoleStoreGetByName(t *testing.T, rctx request.CTX, ss store.Store) {
// Save a role to test with.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
d1, err := ss.Role().Save(r1)
assert.NoError(t, err)
assert.Len(t, d1.Id, 26)
// Get a valid role
d2, err := ss.Role().GetByName(context.Background(), d1.Name)
assert.NoError(t, err)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, r1.Name, d2.Name)
assert.Equal(t, r1.DisplayName, d2.DisplayName)
assert.Equal(t, r1.Description, d2.Description)
assert.Equal(t, r1.Permissions, d2.Permissions)
assert.Equal(t, r1.SchemeManaged, d2.SchemeManaged)
// Get an invalid role
_, err = ss.Role().GetByName(context.Background(), model.NewId())
assert.Error(t, err)
}
func testRoleStoreGetByNames(t *testing.T, rctx request.CTX, ss store.Store) {
// Save some roles to test with.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
r2 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"read_channel",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
r3 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"delete_private_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
d1, err := ss.Role().Save(r1)
assert.NoError(t, err)
assert.Len(t, d1.Id, 26)
d2, err := ss.Role().Save(r2)
assert.NoError(t, err)
assert.Len(t, d2.Id, 26)
d3, err := ss.Role().Save(r3)
assert.NoError(t, err)
assert.Len(t, d3.Id, 26)
// Get two valid roles.
n4 := []string{r1.Name, r2.Name}
roles4, err := ss.Role().GetByNames(n4)
assert.NoError(t, err)
assert.Len(t, roles4, 2)
assert.Contains(t, roles4, d1)
assert.Contains(t, roles4, d2)
assert.NotContains(t, roles4, d3)
// Get two invalid roles.
n5 := []string{model.NewId(), model.NewId()}
roles5, err := ss.Role().GetByNames(n5)
assert.NoError(t, err)
assert.Empty(t, roles5)
// Get one valid one and one invalid one.
n6 := []string{r1.Name, model.NewId()}
roles6, err := ss.Role().GetByNames(n6)
assert.NoError(t, err)
assert.Len(t, roles6, 1)
assert.Contains(t, roles6, d1)
assert.NotContains(t, roles6, d2)
assert.NotContains(t, roles6, d3)
}
func testRoleStoreDelete(t *testing.T, rctx request.CTX, ss store.Store) {
// Save a role to test with.
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
d1, err := ss.Role().Save(r1)
assert.NoError(t, err)
assert.Len(t, d1.Id, 26)
// Check the role is there.
_, err = ss.Role().Get(d1.Id)
assert.NoError(t, err)
// Delete the role.
_, err = ss.Role().Delete(d1.Id)
assert.NoError(t, err)
// Check the role is deleted there.
d2, err := ss.Role().Get(d1.Id)
assert.NoError(t, err)
assert.NotZero(t, d2.DeleteAt)
d3, err := ss.Role().GetByName(context.Background(), d1.Name)
assert.NoError(t, err)
assert.NotZero(t, d3.DeleteAt)
// Try and delete a role that does not exist.
_, err = ss.Role().Delete(model.NewId())
assert.Error(t, err)
}
func testRoleStorePermanentDeleteAll(t *testing.T, rctx request.CTX, ss store.Store) {
r1 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"invite_user",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
r2 := &model.Role{
Name: model.NewId(),
DisplayName: model.NewId(),
Description: model.NewId(),
Permissions: []string{
"read_channel",
"create_public_channel",
"add_user_to_team",
},
SchemeManaged: false,
}
_, err := ss.Role().Save(r1)
require.NoError(t, err)
_, err = ss.Role().Save(r2)
require.NoError(t, err)
roles, err := ss.Role().GetByNames([]string{r1.Name, r2.Name})
assert.NoError(t, err)
assert.Len(t, roles, 2)
err = ss.Role().PermanentDeleteAll()
assert.NoError(t, err)
roles, err = ss.Role().GetByNames([]string{r1.Name, r2.Name})
assert.NoError(t, err)
assert.Empty(t, roles)
}
func testRoleStoreLowerScopedChannelSchemeRoles(t *testing.T, rctx request.CTX, ss store.Store) {
createDefaultRoles(ss)
teamScheme1 := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeTeam,
}
teamScheme1, err := ss.Scheme().Save(teamScheme1)
require.NoError(t, err)
defer ss.Scheme().Delete(teamScheme1.Id)
teamScheme2 := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeTeam,
}
teamScheme2, err = ss.Scheme().Save(teamScheme2)
require.NoError(t, err)
defer ss.Scheme().Delete(teamScheme2.Id)
channelScheme1 := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeChannel,
}
channelScheme1, err = ss.Scheme().Save(channelScheme1)
require.NoError(t, err)
defer ss.Scheme().Delete(channelScheme1.Id)
channelScheme2 := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeChannel,
}
channelScheme2, err = ss.Scheme().Save(channelScheme2)
require.NoError(t, err)
defer ss.Scheme().Delete(channelScheme1.Id)
team1 := &model.Team{
DisplayName: "Name",
Name: "zz" + model.NewId(),
Email: MakeEmail(),
Type: model.TeamOpen,
SchemeId: &teamScheme1.Id,
}
team1, err = ss.Team().Save(team1)
require.NoError(t, err)
defer ss.Team().PermanentDelete(team1.Id)
team2 := &model.Team{
DisplayName: "Name",
Name: "zz" + model.NewId(),
Email: MakeEmail(),
Type: model.TeamOpen,
SchemeId: &teamScheme2.Id,
}
team2, err = ss.Team().Save(team2)
require.NoError(t, err)
defer ss.Team().PermanentDelete(team2.Id)
channel1 := &model.Channel{
TeamId: team1.Id,
DisplayName: "Display " + model.NewId(),
Name: "zz" + model.NewId() + "b",
Type: model.ChannelTypeOpen,
SchemeId: &channelScheme1.Id,
}
channel1, nErr := ss.Channel().Save(rctx, channel1, -1)
require.NoError(t, nErr)
defer ss.Channel().Delete(channel1.Id, 0)
channel2 := &model.Channel{
TeamId: team2.Id,
DisplayName: "Display " + model.NewId(),
Name: "zz" + model.NewId() + "b",
Type: model.ChannelTypeOpen,
SchemeId: &channelScheme2.Id,
}
channel2, nErr = ss.Channel().Save(rctx, channel2, -1)
require.NoError(t, nErr)
defer ss.Channel().Delete(channel2.Id, 0)
t.Run("ChannelRolesUnderTeamRole", func(t *testing.T) {
t.Run("guest role for the right team's channels are returned", func(t *testing.T) {
actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelGuestRole)
require.NoError(t, err)
roleByName := make(map[string]*model.Role, len(actualRoles))
for _, role := range actualRoles {
roleByName[role.Name] = role
}
require.Contains(t, roleByName, channelScheme1.DefaultChannelGuestRole)
require.NotContains(t, roleByName, channelScheme2.DefaultChannelGuestRole)
require.NotNil(t, roleByName[channelScheme1.DefaultChannelGuestRole].SchemeId)
assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelGuestRole].SchemeId)
})
t.Run("user role for the right team's channels are returned", func(t *testing.T) {
actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelUserRole)
require.NoError(t, err)
roleByName := make(map[string]*model.Role, len(actualRoles))
for _, role := range actualRoles {
roleByName[role.Name] = role
}
require.Contains(t, roleByName, channelScheme1.DefaultChannelUserRole)
require.NotContains(t, roleByName, channelScheme2.DefaultChannelUserRole)
require.NotNil(t, roleByName[channelScheme1.DefaultChannelUserRole].SchemeId)
assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelUserRole].SchemeId)
})
t.Run("admin role for the right team's channels are returned", func(t *testing.T) {
actualRoles, err := ss.Role().ChannelRolesUnderTeamRole(teamScheme1.DefaultChannelAdminRole)
require.NoError(t, err)
roleByName := make(map[string]*model.Role, len(actualRoles))
for _, role := range actualRoles {
roleByName[role.Name] = role
}
require.Contains(t, roleByName, channelScheme1.DefaultChannelAdminRole)
require.NotContains(t, roleByName, channelScheme2.DefaultChannelAdminRole)
require.NotNil(t, roleByName[channelScheme1.DefaultChannelAdminRole].SchemeId)
assert.Equal(t, channelScheme1.Id, *roleByName[channelScheme1.DefaultChannelAdminRole].SchemeId)
})
})
t.Run("AllChannelSchemeRoles", func(t *testing.T) {
t.Run("guest role for the right team's channels are returned", func(t *testing.T) {
actualRoles, err := ss.Role().AllChannelSchemeRoles()
require.NoError(t, err)
roleByName := make(map[string]*model.Role, len(actualRoles))
for _, role := range actualRoles {
roleByName[role.Name] = role
}
allRoleNames := []string{
channelScheme1.DefaultChannelGuestRole,
channelScheme2.DefaultChannelGuestRole,
channelScheme1.DefaultChannelUserRole,
channelScheme2.DefaultChannelUserRole,
channelScheme1.DefaultChannelAdminRole,
channelScheme2.DefaultChannelAdminRole,
}
for _, roleName := range allRoleNames {
require.Contains(t, roleByName, roleName)
}
// Roles for channelScheme1 must carry channelScheme1's ID.
for _, roleName := range []string{
channelScheme1.DefaultChannelGuestRole,
channelScheme1.DefaultChannelUserRole,
channelScheme1.DefaultChannelAdminRole,
} {
require.NotNil(t, roleByName[roleName].SchemeId)
assert.Equal(t, channelScheme1.Id, *roleByName[roleName].SchemeId)
}
// Roles for channelScheme2 must carry channelScheme2's ID.
for _, roleName := range []string{
channelScheme2.DefaultChannelGuestRole,
channelScheme2.DefaultChannelUserRole,
channelScheme2.DefaultChannelAdminRole,
} {
require.NotNil(t, roleByName[roleName].SchemeId)
assert.Equal(t, channelScheme2.Id, *roleByName[roleName].SchemeId)
}
})
})
}
func testRoleStoreChannelHigherScopedPermissionsBlankTeamSchemeChannelGuest(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
teamScheme := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeTeam,
}
teamScheme, err := ss.Scheme().Save(teamScheme)
require.NoError(t, err)
defer ss.Scheme().Delete(teamScheme.Id)
channelScheme := &model.Scheme{
DisplayName: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SchemeScopeChannel,
}
channelScheme, err = ss.Scheme().Save(channelScheme)
require.NoError(t, err)
defer ss.Scheme().Delete(channelScheme.Id)
team := &model.Team{
DisplayName: "Name",
Name: "zz" + model.NewId(),
Email: MakeEmail(),
Type: model.TeamOpen,
SchemeId: &teamScheme.Id,
}
team, err = ss.Team().Save(team)
require.NoError(t, err)
defer ss.Team().PermanentDelete(team.Id)
channel := &model.Channel{
TeamId: team.Id,
DisplayName: "Display " + model.NewId(),
Name: "zz" + model.NewId() + "b",
Type: model.ChannelTypeOpen,
SchemeId: &channelScheme.Id,
}
channel, nErr := ss.Channel().Save(rctx, channel, -1)
require.NoError(t, nErr)
defer ss.Channel().Delete(channel.Id, 0)
channelSchemeUserRole, err := ss.Role().GetByName(context.Background(), channelScheme.DefaultChannelUserRole)
require.NoError(t, err)
channelSchemeUserRole.Permissions = []string{}
_, err = ss.Role().Save(channelSchemeUserRole)
require.NoError(t, err)
teamSchemeUserRole, err := ss.Role().GetByName(context.Background(), teamScheme.DefaultChannelUserRole)
require.NoError(t, err)
teamSchemeUserRole.Permissions = []string{model.PermissionUploadFile.Id}
_, err = ss.Role().Save(teamSchemeUserRole)
require.NoError(t, err)
// get the channel scheme user role again and ensure that it has the permission inherited from the team
// scheme user role
roleMapBefore, err := ss.Role().ChannelHigherScopedPermissions([]string{channelSchemeUserRole.Name})
require.NoError(t, err)
// blank-out the guest role to simulate an old team scheme, ensure it's blank
result, sqlErr := s.GetMaster().Exec(fmt.Sprintf("UPDATE Schemes SET DefaultChannelGuestRole = '' WHERE Id = '%s'", teamScheme.Id))
require.NoError(t, sqlErr)
rows, serr := result.RowsAffected()
require.NoError(t, serr)
require.Equal(t, int64(1), rows)
teamScheme, err = ss.Scheme().Get(teamScheme.Id)
require.NoError(t, err)
require.Equal(t, "", teamScheme.DefaultChannelGuestRole)
// trigger a cache clear
_, err = ss.Role().Save(channelSchemeUserRole)
require.NoError(t, err)
roleMapAfter, err := ss.Role().ChannelHigherScopedPermissions([]string{channelSchemeUserRole.Name})
require.NoError(t, err)
require.Equal(t, len(roleMapBefore), len(roleMapAfter))
}