Files
zeekay 8feb24ea1f One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that
resolves to the retired workspace's repository, not this one — while
server/public still published github.com/mattermost/mattermost/server/public.
Both are github.com/hanzoteam/server/... now, which is where this code
actually lives.

That second half is what made the binary unbootable. Upstream's public path
stayed claimed by upstream, mattermost-plugin-ai imported it, and the build
linked public/model twice; public/plugin's gob.Register then ran twice under
one name and panicked at init, before main. Reverting the rename traded the
brand for a working binary. Rebranding the plugin alongside it needs no such
trade: the agents fork is github.com/hanzoteam/agents/v2 against our public
module, so no module named mattermost/mattermost is in the graph at all.

Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and
it is ours.
2026-08-28 23:35:36 -07:00

695 lines
21 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hanzoteam/server/server/public/model"
"github.com/hanzoteam/server/server/public/shared/request"
"github.com/hanzoteam/server/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(rctx, 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(rctx, 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(rctx, 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(rctx, channelScheme.DefaultChannelUserRole)
require.NoError(t, err)
channelSchemeUserRole.Permissions = []string{}
_, err = ss.Role().Save(channelSchemeUserRole)
require.NoError(t, err)
teamSchemeUserRole, err := ss.Role().GetByName(rctx, 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))
}