MM-8796: Full implementation of "Schemes" in Store/Model/App layers. (#8357)

* Add Scheme model and stub store.

* Port ChannelStore to be Scheme aware.

* Make almost all the API/APP layer work with ChannelSchemes.

Only thing still hacky is UpdateChannelMemberRoles().

* Add basic SchemeStore implementation.

* Migrate UpdateChannelMemberRoles properly and fix tests.

* Update store tests and mocks so they work.

* Include creating default roles in Scheme create store function.

* Implement role deletion and start scheme deletion.

* Only use non-deleted roles for authorization.

* Add GetByScheme method to Team store.

* Add GetChannelsByScheme.

* Update store mocks.

* Implement scheme deletion in the store.

* Rename is valid function.

* Add offset and limit to queries to fetch teams and channels by scheme.

* Fix queries.

* Implement scheme awareness in Team store and add a migration.

* Tidy up ChannelStore mapping functions and add exhaustive unit tests.

* Add all missing i18n.

* Proper tests for TeamStore internal functions and fix them.

* Make additional TeamMember fields nullable.

* Make new ChannelMember fields nullable.

* Create new nullable columns without defaults.

* Make new fields in large tables nullalble.

* Fix empty list of TeamMembers.

* Deduplicate SQL queries.

* Fix spelling.

* Fix review comment.

* More review fixes.

* More review fixes.
This commit is contained in:
George Goldberg
2018-04-20 19:49:13 +01:00
committed by Martin Kraft
parent 853445dc2e
commit cd55c44c8f
48 changed files with 3605 additions and 176 deletions

View File

@@ -245,28 +245,36 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) { func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID} if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { tm := tmr.Data.(*model.TeamMember)
tm.SchemeAdmin = true
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
utils.EnableDebugLogForTest()
panic(sr.Err)
}
} else {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
l4g.Error(tmr.Err.Error())
l4g.Close()
time.Sleep(time.Second)
panic(tmr.Err) panic(tmr.Err)
} }
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }
func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) { func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID} if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { tm := tmr.Data.(*model.TeamMember)
tm.SchemeAdmin = false
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
utils.EnableDebugLogForTest()
panic(sr.Err)
}
} else {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
l4g.Error(tmr.Err.Error())
l4g.Close()
time.Sleep(time.Second)
panic(tmr.Err) panic(tmr.Err)
} }
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }
@@ -275,7 +283,7 @@ func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Chan
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil { if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
cm := cmr.Data.(*model.ChannelMember) cm := cmr.Data.(*model.ChannelMember)
cm.Roles = "channel_admin channel_user" cm.SchemeAdmin = true
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil { if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
panic(sr.Err) panic(sr.Err)
@@ -293,7 +301,7 @@ func (me *TestHelper) MakeUserChannelUser(user *model.User, channel *model.Chann
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil { if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
cm := cmr.Data.(*model.ChannelMember) cm := cmr.Data.(*model.ChannelMember)
cm.Roles = "channel_user" cm.SchemeAdmin = false
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil { if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
panic(sr.Err) panic(sr.Err)

View File

@@ -765,7 +765,7 @@ func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Chan
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil { if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
cm := cmr.Data.(*model.ChannelMember) cm := cmr.Data.(*model.ChannelMember)
cm.Roles = "channel_admin channel_user" cm.SchemeAdmin = true
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil { if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
panic(sr.Err) panic(sr.Err)
@@ -781,28 +781,36 @@ func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Chan
func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) { func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID} if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { tm := tmr.Data.(*model.TeamMember)
tm.SchemeAdmin = true
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
utils.EnableDebugLogForTest()
panic(sr.Err)
}
} else {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
l4g.Error(tmr.Err.Error())
l4g.Close()
time.Sleep(time.Second)
panic(tmr.Err) panic(tmr.Err)
} }
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }
func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) { func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
utils.DisableDebugLogForTest() utils.DisableDebugLogForTest()
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID} if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil { tm := tmr.Data.(*model.TeamMember)
tm.SchemeAdmin = false
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
utils.EnableDebugLogForTest()
panic(sr.Err)
}
} else {
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
l4g.Error(tmr.Err.Error())
l4g.Close()
time.Sleep(time.Second)
panic(tmr.Err) panic(tmr.Err)
} }
utils.EnableDebugLogForTest() utils.EnableDebugLogForTest()
} }

View File

@@ -1388,7 +1388,7 @@ func TestUpdateChannelRoles(t *testing.T) {
defer th.TearDown() defer th.TearDown()
Client := th.Client Client := th.Client
const CHANNEL_ADMIN = "channel_admin channel_user" const CHANNEL_ADMIN = "channel_user channel_admin"
const CHANNEL_MEMBER = "channel_user" const CHANNEL_MEMBER = "channel_user"
// User 1 creates a channel, making them channel admin by default. // User 1 creates a channel, making them channel admin by default.

View File

@@ -1672,7 +1672,7 @@ func TestUpdateTeamMemberRoles(t *testing.T) {
// user 1 (team admin) tries to demote system admin (not member of a team) // user 1 (team admin) tries to demote system admin (not member of a team)
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.SystemAdminUser.Id, TEAM_MEMBER) _, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.SystemAdminUser.Id, TEAM_MEMBER)
CheckBadRequestStatus(t, resp) CheckNotFoundStatus(t, resp)
// user 1 (team admin) demotes system admin (member of a team) // user 1 (team admin) demotes system admin (member of a team)
th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam) th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam)
@@ -1698,7 +1698,7 @@ func TestUpdateTeamMemberRoles(t *testing.T) {
// user 1 (team admin) tries to promote a random user // user 1 (team admin) tries to promote a random user
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, model.NewId(), TEAM_ADMIN) _, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, model.NewId(), TEAM_ADMIN)
CheckBadRequestStatus(t, resp) CheckNotFoundStatus(t, resp)
// user 1 (team admin) tries to promote invalid team permission // user 1 (team admin) tries to promote invalid team permission
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.BasicUser.Id, "junk") _, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.BasicUser.Id, "junk")

View File

@@ -199,6 +199,10 @@ func (a *App) RolesGrantPermission(roleNames []string, permissionId string) bool
} }
for _, role := range roles { for _, role := range roles {
if role.DeleteAt != 0 {
continue
}
permissions := role.Permissions permissions := role.Permissions
for _, permission := range permissions { for _, permission := range permissions {
if permission == permissionId { if permission == permissionId {

View File

@@ -32,7 +32,7 @@ func (a *App) CreateDefaultChannels(teamId string) ([]*model.Channel, *model.App
return channels, nil return channels, nil
} }
func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole string, userRequestorId string) *model.AppError { func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
var err *model.AppError = nil var err *model.AppError = nil
var requestor *model.User var requestor *model.User
@@ -52,7 +52,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
cm := &model.ChannelMember{ cm := &model.ChannelMember{
ChannelId: townSquare.Id, ChannelId: townSquare.Id,
UserId: user.Id, UserId: user.Id,
Roles: channelRole, SchemeUser: true,
SchemeAdmin: shouldBeAdmin,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
} }
@@ -85,7 +86,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
cm := &model.ChannelMember{ cm := &model.ChannelMember{
ChannelId: offTopic.Id, ChannelId: offTopic.Id,
UserId: user.Id, UserId: user.Id,
Roles: channelRole, SchemeUser: true,
SchemeAdmin: shouldBeAdmin,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
} }
@@ -166,7 +168,8 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan
cm := &model.ChannelMember{ cm := &model.ChannelMember{
ChannelId: sc.Id, ChannelId: sc.Id,
UserId: channel.CreatorId, UserId: channel.CreatorId,
Roles: model.CHANNEL_USER_ROLE_ID + " " + model.CHANNEL_ADMIN_ROLE_ID, SchemeUser: true,
SchemeAdmin: true,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
} }
@@ -322,7 +325,7 @@ func (a *App) createGroupChannel(userIds []string, creatorId string) (*model.Cha
UserId: user.Id, UserId: user.Id,
ChannelId: group.Id, ChannelId: group.Id,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID, SchemeUser: true,
} }
if result := <-a.Srv.Store.Channel().SaveMember(cm); result.Err != nil { if result := <-a.Srv.Store.Channel().SaveMember(cm); result.Err != nil {
@@ -432,6 +435,39 @@ func (a *App) PatchChannel(channel *model.Channel, patch *model.ChannelPatch, us
return channel, err return channel, err
} }
func (a *App) GetSchemeRolesForChannel(channelId string) (string, string, *model.AppError) {
var channel *model.Channel
var err *model.AppError
if channel, err = a.GetChannel(channelId); err != nil {
return "", "", err
}
if channel.SchemeId != nil && len(*channel.SchemeId) != 0 {
if scheme, err := a.GetScheme(*channel.SchemeId); err != nil {
return "", "", err
} else {
return scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
}
}
var team *model.Team
if team, err = a.GetTeam(channel.TeamId); err != nil {
return "", "", err
}
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
if scheme, err := a.GetScheme(*team.SchemeId); err != nil {
return "", "", err
} else {
return scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
}
}
return model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, nil
}
func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) { func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
var member *model.ChannelMember var member *model.ChannelMember
var err *model.AppError var err *model.AppError
@@ -439,14 +475,42 @@ func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles
return nil, err return nil, err
} }
if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil { schemeUserRole, schemeAdminRole, err := a.GetSchemeRolesForChannel(channelId)
if err != nil {
return nil, err return nil, err
} }
member.Roles = newRoles var newExplicitRoles []string
member.SchemeUser = false
member.SchemeAdmin = false
for _, roleName := range strings.Fields(newRoles) {
if role, err := a.GetRoleByName(roleName); err != nil {
err.StatusCode = http.StatusBadRequest
return nil, err
} else if !role.SchemeManaged {
// The role is not scheme-managed, so it's OK to apply it to the explicit roles field.
newExplicitRoles = append(newExplicitRoles, roleName)
} else {
// The role is scheme-managed, so need to check if it is part of the scheme for this channel or not.
switch roleName {
case schemeAdminRole:
member.SchemeAdmin = true
case schemeUserRole:
member.SchemeUser = true
default:
// If not part of the scheme for this channel, then it is not allowed to apply it as an explicit role.
return nil, model.NewAppError("UpdateChannelMemberRoles", "api.channel.update_channel_member_roles.scheme_role.app_error", nil, "role_name="+roleName, http.StatusBadRequest)
}
}
}
member.ExplicitRoles = strings.Join(newExplicitRoles, " ")
if result := <-a.Srv.Store.Channel().UpdateMember(member); result.Err != nil { if result := <-a.Srv.Store.Channel().UpdateMember(member); result.Err != nil {
return nil, result.Err return nil, result.Err
} else {
member = result.Data.(*model.ChannelMember)
} }
a.InvalidateCacheForUser(userId) a.InvalidateCacheForUser(userId)
@@ -591,7 +655,7 @@ func (a *App) addUserToChannel(user *model.User, channel *model.Channel, teamMem
ChannelId: channel.Id, ChannelId: channel.Id,
UserId: user.Id, UserId: user.Id,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID, SchemeUser: true,
} }
if result := <-a.Srv.Store.Channel().SaveMember(newMember); result.Err != nil { if result := <-a.Srv.Store.Channel().SaveMember(newMember); result.Err != nil {
l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err) l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err)

View File

@@ -120,7 +120,7 @@ func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testi
// create a new user that joins the default channels // create a new user that joins the default channels
user := th.CreateUser() user := th.CreateUser()
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, model.CHANNEL_USER_ROLE_ID, "") th.App.JoinDefaultChannels(th.BasicTeam.Id, user, false, "")
// there should be a ChannelMemberHistory record for the user // there should be a ChannelMemberHistory record for the user
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, townSquareChannelId)).([]*model.ChannelMemberHistoryResult) histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, townSquareChannelId)).([]*model.ChannelMemberHistoryResult)
@@ -146,7 +146,7 @@ func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing
// create a new user that joins the default channels // create a new user that joins the default channels
user := th.CreateUser() user := th.CreateUser()
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, model.CHANNEL_USER_ROLE_ID, "") th.App.JoinDefaultChannels(th.BasicTeam.Id, user, false, "")
// there should be a ChannelMemberHistory record for the user // there should be a ChannelMemberHistory record for the user
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, offTopicChannelId)).([]*model.ChannelMemberHistoryResult) histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, offTopicChannelId)).([]*model.ChannelMemberHistoryResult)

14
app/scheme.go Normal file
View File

@@ -0,0 +1,14 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import "github.com/mattermost/mattermost-server/model"
func (a *App) GetScheme(id string) (*model.Scheme, *model.AppError) {
if result := <-a.Srv.Store.Scheme().Get(id); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.Scheme), nil
}
}

View File

@@ -141,17 +141,31 @@ func (a *App) sendTeamEvent(team *model.Team, event string) {
a.Publish(message) a.Publish(message)
} }
func (a *App) GetSchemeRolesForTeam(teamId string) (string, string, *model.AppError) {
var team *model.Team
var err *model.AppError
if team, err = a.GetTeam(teamId); err != nil {
return "", "", err
}
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
if scheme, err := a.GetScheme(*team.SchemeId); err != nil {
return "", "", err
} else {
return scheme.DefaultTeamUserRole, scheme.DefaultTeamAdminRole, nil
}
}
return model.TEAM_USER_ROLE_ID, model.TEAM_ADMIN_ROLE_ID, nil
}
func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) { func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
var member *model.TeamMember var member *model.TeamMember
if result := <-a.Srv.Store.Team().GetTeamsForUser(userId); result.Err != nil { if result := <-a.Srv.Store.Team().GetMember(teamId, userId); result.Err != nil {
return nil, result.Err return nil, result.Err
} else { } else {
members := result.Data.([]*model.TeamMember) member = result.Data.(*model.TeamMember)
for _, m := range members {
if m.TeamId == teamId {
member = m
}
}
} }
if member == nil { if member == nil {
@@ -159,14 +173,42 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin
return nil, err return nil, err
} }
if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil { schemeUserRole, schemeAdminRole, err := a.GetSchemeRolesForTeam(teamId)
if err != nil {
return nil, err return nil, err
} }
member.Roles = newRoles var newExplicitRoles []string
member.SchemeUser = false
member.SchemeAdmin = false
for _, roleName := range strings.Fields(newRoles) {
if role, err := a.GetRoleByName(roleName); err != nil {
err.StatusCode = http.StatusBadRequest
return nil, err
} else if !role.SchemeManaged {
// The role is not scheme-managed, so it's OK to apply it to the explicit roles field.
newExplicitRoles = append(newExplicitRoles, roleName)
} else {
// The role is scheme-managed, so need to check if it is part of the scheme for this channel or not.
switch roleName {
case schemeAdminRole:
member.SchemeAdmin = true
case schemeUserRole:
member.SchemeUser = true
default:
// If not part of the scheme for this channel, then it is not allowed to apply it as an explicit role.
return nil, model.NewAppError("UpdateTeamMemberRoles", "api.channel.update_team_member_roles.scheme_role.app_error", nil, "role_name="+roleName, http.StatusBadRequest)
}
}
}
member.ExplicitRoles = strings.Join(newExplicitRoles, " ")
if result := <-a.Srv.Store.Team().UpdateMember(member); result.Err != nil { if result := <-a.Srv.Store.Team().UpdateMember(member); result.Err != nil {
return nil, result.Err return nil, result.Err
} else {
member = result.Data.(*model.TeamMember)
} }
a.ClearSessionCacheForUser(userId) a.ClearSessionCacheForUser(userId)
@@ -292,13 +334,13 @@ func (a *App) AddUserToTeamByInviteId(inviteId string, userId string) (*model.Te
// 3. a pointer to an AppError if something went wrong. // 3. a pointer to an AppError if something went wrong.
func (a *App) joinUserToTeam(team *model.Team, user *model.User) (*model.TeamMember, bool, *model.AppError) { func (a *App) joinUserToTeam(team *model.Team, user *model.User) (*model.TeamMember, bool, *model.AppError) {
tm := &model.TeamMember{ tm := &model.TeamMember{
TeamId: team.Id, TeamId: team.Id,
UserId: user.Id, UserId: user.Id,
Roles: model.TEAM_USER_ROLE_ID, SchemeUser: true,
} }
if team.Email == user.Email { if team.Email == user.Email {
tm.Roles = model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID tm.SchemeAdmin = true
} }
if etmr := <-a.Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil { if etmr := <-a.Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil {
@@ -342,14 +384,10 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
return uua.Err return uua.Err
} }
channelRole := model.CHANNEL_USER_ROLE_ID shouldBeAdmin := team.Email == user.Email
if team.Email == user.Email {
channelRole = model.CHANNEL_USER_ROLE_ID + " " + model.CHANNEL_ADMIN_ROLE_ID
}
// Soft error if there is an issue joining the default channels // Soft error if there is an issue joining the default channels
if err := a.JoinDefaultChannels(team.Id, user, channelRole, userRequestorId); err != nil { if err := a.JoinDefaultChannels(team.Id, user, shouldBeAdmin, userRequestorId); err != nil {
l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err) l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err)
} }

View File

@@ -6690,6 +6690,86 @@
"id": "store.sql_role.get_by_name.app_error", "id": "store.sql_role.get_by_name.app_error",
"translation": "Unable to get role" "translation": "Unable to get role"
}, },
{
"id": "api.channel.update_channel_member_roles.scheme_role.app_error",
"translation": "The provided role is managed by a Scheme and therefore cannot be applied directly to a Channel Member"
},
{
"id": "api.channel.update_team_member_roles.scheme_role.app_error",
"translation": "The provided role is managed by a Scheme and therefore cannot be applied directly to a Team Member"
},
{
"id": "store.sql_channel.get_by_scheme.app_error",
"translation": "Unable to get the channels for the provided scheme"
},
{
"id": "store.sql_team.get_by_scheme.app_error",
"translation": "Unable to get the channels for the provided scheme"
},
{
"id": "store.sql_role.save.open_transaction.app_error",
"translation": "Failed to open the transaction to save the role"
},
{
"id": "store.sql_role.save_role.commit_transaction.app_error",
"translation": "Failed to commit the transaction to save the role"
},
{
"id": "store.sql_role.save.invalid_role.app_error",
"translation": "The provided role is invalid"
},
{
"id": "store.sql_role.delete.update.app_error",
"translation": "Unable to delete the role"
},
{
"id": "store.sql_scheme.save.open_transaction.app_error",
"translation": "Failed to open the transaction to save the scheme"
},
{
"id": "store.sql_scheme.save_scheme.commit_transaction.app_error",
"translation": "Failed to commit the transaction to save the scheme"
},
{
"id": "store.sql_scheme.save.invalid_scheme.app_error",
"translation": "The provided scheme is invalid"
},
{
"id": "store.sql_scheme.save.update.app_error",
"translation": "Unable to update the scheme"
},
{
"id": "store.sql_scheme.save.retrieve_default_scheme_roles.app_error",
"translation": "Unable to retrieve the default scheme roles"
},
{
"id": "store.sql_scheme.save.insert.app_error",
"translation": "Unable to create the scheme"
},
{
"id": "store.sql_scheme.get.app_error",
"translation": "Unable to get the scheme"
},
{
"id": "store.sql_scheme.team_count.app_error",
"translation": "Unable to count the number of teams using this scheme"
},
{
"id": "store.sql_scheme.delete.scheme_in_use.app_error",
"translation": "Unable to delete the scheme as it in use by 1 or more teams or channels"
},
{
"id": "store.sql_scheme.channel_count.app_error",
"translation": "Unable to count the number of channels using this scheme"
},
{
"id": "store.sql_scheme.delete.role_update.app_error",
"translation": "Unable to delete the roles belonging to this scheme"
},
{
"id": "store.sql_scheme.delete.update.app_error",
"translation": "Unable to delete the scheme"
},
{ {
"id": "store.sql_role.get_by_names.app_error", "id": "store.sql_role.get_by_names.app_error",
"translation": "Unable to get roles" "translation": "Unable to get roles"

View File

@@ -32,20 +32,21 @@ const (
) )
type Channel struct { type Channel struct {
Id string `json:"id"` Id string `json:"id"`
CreateAt int64 `json:"create_at"` CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"` UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
Type string `json:"type"` Type string `json:"type"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
Name string `json:"name"` Name string `json:"name"`
Header string `json:"header"` Header string `json:"header"`
Purpose string `json:"purpose"` Purpose string `json:"purpose"`
LastPostAt int64 `json:"last_post_at"` LastPostAt int64 `json:"last_post_at"`
TotalMsgCount int64 `json:"total_msg_count"` TotalMsgCount int64 `json:"total_msg_count"`
ExtraUpdateAt int64 `json:"extra_update_at"` ExtraUpdateAt int64 `json:"extra_update_at"`
CreatorId string `json:"creator_id"` CreatorId string `json:"creator_id"`
SchemeId *string `json:"scheme_id"`
} }
type ChannelPatch struct { type ChannelPatch struct {

View File

@@ -28,14 +28,17 @@ type ChannelUnread struct {
} }
type ChannelMember struct { type ChannelMember struct {
ChannelId string `json:"channel_id"` ChannelId string `json:"channel_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
Roles string `json:"roles"` Roles string `json:"roles"`
LastViewedAt int64 `json:"last_viewed_at"` LastViewedAt int64 `json:"last_viewed_at"`
MsgCount int64 `json:"msg_count"` MsgCount int64 `json:"msg_count"`
MentionCount int64 `json:"mention_count"` MentionCount int64 `json:"mention_count"`
NotifyProps StringMap `json:"notify_props"` NotifyProps StringMap `json:"notify_props"`
LastUpdateAt int64 `json:"last_update_at"` LastUpdateAt int64 `json:"last_update_at"`
SchemeUser bool `json:"scheme_user"`
SchemeAdmin bool `json:"scheme_admin"`
ExplicitRoles string `json:"explicit_roles"`
} }
type ChannelMembers []ChannelMember type ChannelMembers []ChannelMember

View File

@@ -22,6 +22,7 @@ const (
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user" CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user"
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user" CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles" CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
CLUSTER_SEND_BEST_EFFORT = "best_effort" CLUSTER_SEND_BEST_EFFORT = "best_effort"
CLUSTER_SEND_RELIABLE = "reliable" CLUSTER_SEND_RELIABLE = "reliable"

View File

@@ -39,6 +39,7 @@ type Role struct {
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
Permissions []string `json:"permissions"` Permissions []string `json:"permissions"`
SchemeManaged bool `json:"scheme_managed"` SchemeManaged bool `json:"scheme_managed"`
BuiltIn bool `json:"built_in"`
} }
type RolePatch struct { type RolePatch struct {
@@ -187,6 +188,7 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_USE_SLASH_COMMANDS.Id, PERMISSION_USE_SLASH_COMMANDS.Id,
}, },
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
roles[CHANNEL_ADMIN_ROLE_ID] = &Role{ roles[CHANNEL_ADMIN_ROLE_ID] = &Role{
@@ -197,6 +199,7 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_MANAGE_CHANNEL_ROLES.Id, PERMISSION_MANAGE_CHANNEL_ROLES.Id,
}, },
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
roles[TEAM_USER_ROLE_ID] = &Role{ roles[TEAM_USER_ROLE_ID] = &Role{
@@ -210,6 +213,7 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_VIEW_TEAM.Id, PERMISSION_VIEW_TEAM.Id,
}, },
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
roles[TEAM_POST_ALL_ROLE_ID] = &Role{ roles[TEAM_POST_ALL_ROLE_ID] = &Role{
@@ -219,7 +223,8 @@ func MakeDefaultRoles() map[string]*Role {
Permissions: []string{ Permissions: []string{
PERMISSION_CREATE_POST.Id, PERMISSION_CREATE_POST.Id,
}, },
SchemeManaged: true, SchemeManaged: false,
BuiltIn: true,
} }
roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
@@ -229,7 +234,8 @@ func MakeDefaultRoles() map[string]*Role {
Permissions: []string{ Permissions: []string{
PERMISSION_CREATE_POST_PUBLIC.Id, PERMISSION_CREATE_POST_PUBLIC.Id,
}, },
SchemeManaged: true, SchemeManaged: false,
BuiltIn: true,
} }
roles[TEAM_ADMIN_ROLE_ID] = &Role{ roles[TEAM_ADMIN_ROLE_ID] = &Role{
@@ -249,6 +255,7 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_MANAGE_WEBHOOKS.Id, PERMISSION_MANAGE_WEBHOOKS.Id,
}, },
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
roles[SYSTEM_USER_ROLE_ID] = &Role{ roles[SYSTEM_USER_ROLE_ID] = &Role{
@@ -261,6 +268,7 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_PERMANENT_DELETE_USER.Id, PERMISSION_PERMANENT_DELETE_USER.Id,
}, },
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{ roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
@@ -270,7 +278,8 @@ func MakeDefaultRoles() map[string]*Role {
Permissions: []string{ Permissions: []string{
PERMISSION_CREATE_POST.Id, PERMISSION_CREATE_POST.Id,
}, },
SchemeManaged: true, SchemeManaged: false,
BuiltIn: true,
} }
roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{ roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
@@ -280,7 +289,8 @@ func MakeDefaultRoles() map[string]*Role {
Permissions: []string{ Permissions: []string{
PERMISSION_CREATE_POST_PUBLIC.Id, PERMISSION_CREATE_POST_PUBLIC.Id,
}, },
SchemeManaged: true, SchemeManaged: false,
BuiltIn: true,
} }
roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{ roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{
@@ -292,7 +302,8 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_READ_USER_ACCESS_TOKEN.Id, PERMISSION_READ_USER_ACCESS_TOKEN.Id,
PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id, PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
}, },
SchemeManaged: true, SchemeManaged: false,
BuiltIn: true,
} }
roles[SYSTEM_ADMIN_ROLE_ID] = &Role{ roles[SYSTEM_ADMIN_ROLE_ID] = &Role{
@@ -345,6 +356,7 @@ func MakeDefaultRoles() map[string]*Role {
roles[CHANNEL_ADMIN_ROLE_ID].Permissions..., roles[CHANNEL_ADMIN_ROLE_ID].Permissions...,
), ),
SchemeManaged: true, SchemeManaged: true,
BuiltIn: true,
} }
return roles return roles

95
model/scheme.go Normal file
View File

@@ -0,0 +1,95 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
const (
SCHEME_NAME_MAX_LENGTH = 64
SCHEME_DESCRIPTION_MAX_LENGTH = 1024
SCHEME_SCOPE_TEAM = "team"
SCHEME_SCOPE_CHANNEL = "channel"
)
type Scheme struct {
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
Scope string `json:"scope"`
DefaultTeamAdminRole string `json:"default_team_admin_role"`
DefaultTeamUserRole string `json:"default_team_user_role"`
DefaultChannelAdminRole string `json:"default_channel_admin_role"`
DefaultChannelUserRole string `json:"default_channel_user_role"`
}
func (scheme *Scheme) ToJson() string {
b, _ := json.Marshal(scheme)
return string(b)
}
func SchemeFromJson(data io.Reader) *Scheme {
var scheme *Scheme
json.NewDecoder(data).Decode(&scheme)
return scheme
}
func (scheme *Scheme) IsValid() bool {
if len(scheme.Id) != 26 {
return false
}
return scheme.IsValidForCreate()
}
func (scheme *Scheme) IsValidForCreate() bool {
if len(scheme.Name) == 0 || len(scheme.Name) > SCHEME_NAME_MAX_LENGTH {
return false
}
if len(scheme.Description) > SCHEME_DESCRIPTION_MAX_LENGTH {
return false
}
switch scheme.Scope {
case SCHEME_SCOPE_TEAM, SCHEME_SCOPE_CHANNEL:
default:
return false
}
if !IsValidRoleName(scheme.DefaultChannelAdminRole) {
return false
}
if !IsValidRoleName(scheme.DefaultChannelUserRole) {
return false
}
if scheme.Scope == SCHEME_SCOPE_TEAM {
if !IsValidRoleName(scheme.DefaultTeamAdminRole) {
return false
}
if !IsValidRoleName(scheme.DefaultTeamUserRole) {
return false
}
}
if scheme.Scope == SCHEME_SCOPE_CHANNEL {
if len(scheme.DefaultTeamAdminRole) != 0 {
return false
}
if len(scheme.DefaultTeamUserRole) != 0 {
return false
}
}
return true
}

View File

@@ -26,20 +26,21 @@ const (
) )
type Team struct { type Team struct {
Id string `json:"id"` Id string `json:"id"`
CreateAt int64 `json:"create_at"` CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"` UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Email string `json:"email"` Email string `json:"email"`
Type string `json:"type"` Type string `json:"type"`
CompanyName string `json:"company_name"` CompanyName string `json:"company_name"`
AllowedDomains string `json:"allowed_domains"` AllowedDomains string `json:"allowed_domains"`
InviteId string `json:"invite_id"` InviteId string `json:"invite_id"`
AllowOpenInvite bool `json:"allow_open_invite"` AllowOpenInvite bool `json:"allow_open_invite"`
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"` LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
SchemeId *string `json:"scheme_id"`
} }
type TeamPatch struct { type TeamPatch struct {

View File

@@ -11,10 +11,13 @@ import (
) )
type TeamMember struct { type TeamMember struct {
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
Roles string `json:"roles"` Roles string `json:"roles"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
SchemeUser bool `json:"scheme_user"`
SchemeAdmin bool `json:"scheme_admin"`
ExplicitRoles string `json:"explicit_roles"`
} }
type TeamUnread struct { type TeamUnread struct {

View File

@@ -24,6 +24,7 @@ type LayeredStore struct {
TmpContext context.Context TmpContext context.Context
ReactionStore ReactionStore ReactionStore ReactionStore
RoleStore RoleStore RoleStore RoleStore
SchemeStore SchemeStore
DatabaseLayer LayeredStoreDatabaseLayer DatabaseLayer LayeredStoreDatabaseLayer
LocalCacheLayer *LocalCacheSupplier LocalCacheLayer *LocalCacheSupplier
RedisLayer *RedisSupplier RedisLayer *RedisSupplier
@@ -39,6 +40,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
store.ReactionStore = &LayeredReactionStore{store} store.ReactionStore = &LayeredReactionStore{store}
store.RoleStore = &LayeredRoleStore{store} store.RoleStore = &LayeredRoleStore{store}
store.SchemeStore = &LayeredSchemeStore{store}
// Setup the chain // Setup the chain
if ENABLE_EXPERIMENTAL_REDIS { if ENABLE_EXPERIMENTAL_REDIS {
@@ -167,6 +169,10 @@ func (s *LayeredStore) Role() RoleStore {
return s.RoleStore return s.RoleStore
} }
func (s *LayeredStore) Scheme() SchemeStore {
return s.SchemeStore
}
func (s *LayeredStore) MarkSystemRanUnitTests() { func (s *LayeredStore) MarkSystemRanUnitTests() {
s.DatabaseLayer.MarkSystemRanUnitTests() s.DatabaseLayer.MarkSystemRanUnitTests()
} }
@@ -253,8 +259,36 @@ func (s *LayeredRoleStore) GetByNames(names []string) StoreChannel {
}) })
} }
func (s *LayeredRoleStore) Delete(roldId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.RoleDelete(s.TmpContext, roldId)
})
}
func (s *LayeredRoleStore) PermanentDeleteAll() StoreChannel { func (s *LayeredRoleStore) PermanentDeleteAll() StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult { return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.RolePermanentDeleteAll(s.TmpContext) return supplier.RolePermanentDeleteAll(s.TmpContext)
}) })
} }
type LayeredSchemeStore struct {
*LayeredStore
}
func (s *LayeredSchemeStore) Save(scheme *model.Scheme) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeSave(s.TmpContext, scheme)
})
}
func (s *LayeredSchemeStore) Get(schemeId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeGet(s.TmpContext, schemeId)
})
}
func (s *LayeredSchemeStore) Delete(schemeId string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.SchemeDelete(s.TmpContext, schemeId)
})
}

View File

@@ -35,5 +35,11 @@ type LayeredStoreSupplier interface {
RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
RoleDelete(ctx context.Context, roldId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
// Schemes
SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
} }

View File

@@ -18,6 +18,9 @@ const (
ROLE_CACHE_SIZE = 20000 ROLE_CACHE_SIZE = 20000
ROLE_CACHE_SEC = 30 * 60 ROLE_CACHE_SEC = 30 * 60
SCHEME_CACHE_SIZE = 20000
SCHEME_CACHE_SEC = 30 * 60
CLEAR_CACHE_MESSAGE_DATA = "" CLEAR_CACHE_MESSAGE_DATA = ""
) )
@@ -25,6 +28,7 @@ type LocalCacheSupplier struct {
next LayeredStoreSupplier next LayeredStoreSupplier
reactionCache *utils.Cache reactionCache *utils.Cache
roleCache *utils.Cache roleCache *utils.Cache
schemeCache *utils.Cache
metrics einterfaces.MetricsInterface metrics einterfaces.MetricsInterface
cluster einterfaces.ClusterInterface cluster einterfaces.ClusterInterface
} }
@@ -33,6 +37,7 @@ func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterf
supplier := &LocalCacheSupplier{ supplier := &LocalCacheSupplier{
reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS), reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS),
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES), roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
metrics: metrics, metrics: metrics,
cluster: cluster, cluster: cluster,
} }

View File

@@ -69,6 +69,17 @@ func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []str
return result return result
} }
func (s *LocalCacheSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
result := s.Next().RoleDelete(ctx, roleId, hints...)
if result.Err == nil {
role := result.Data.(*model.Role)
s.doInvalidateCacheCluster(s.roleCache, role.Name)
}
return result
}
func (s *LocalCacheSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { func (s *LocalCacheSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer s.roleCache.Purge() defer s.roleCache.Purge()
defer s.doClearCacheCluster(s.roleCache) defer s.doClearCacheCluster(s.roleCache)

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"context"
"github.com/mattermost/mattermost-server/model"
)
func (s *LocalCacheSupplier) handleClusterInvalidateScheme(msg *model.ClusterMessage) {
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
s.schemeCache.Purge()
} else {
s.schemeCache.Remove(msg.Data)
}
}
func (s *LocalCacheSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if len(scheme.Id) != 0 {
defer s.doInvalidateCacheCluster(s.schemeCache, scheme.Id)
}
return s.Next().SchemeSave(ctx, scheme, hints...)
}
func (s *LocalCacheSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if result := s.doStandardReadCache(ctx, s.schemeCache, schemeId, hints...); result != nil {
return result
}
result := s.Next().SchemeGet(ctx, schemeId, hints...)
s.doStandardAddToCache(ctx, s.schemeCache, schemeId, result, hints...)
return result
}
func (s *LocalCacheSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer s.doInvalidateCacheCluster(s.schemeCache, schemeId)
defer s.doClearCacheCluster(s.roleCache)
return s.Next().SchemeDelete(ctx, schemeId, hints...)
}

View File

@@ -84,6 +84,21 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
return result return result
} }
func (s *RedisSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
result := s.Next().RoleGet(ctx, roleId, hints...)
if result.Err == nil {
role := result.Data.(*model.Role)
key := buildRedisKeyForRoleName(role.Name)
if err := s.client.Del(key).Err(); err != nil {
l4g.Error("Redis failed to remove key " + key + " Error: " + err.Error())
}
}
return result
}
func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult { func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
defer func() { defer func() {
if keys, err := s.client.Keys("roles:*").Result(); err != nil { if keys, err := s.client.Keys("roles:*").Result(); err != nil {

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"context"
"github.com/mattermost/mattermost-server/model"
)
func (s *RedisSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeSave(ctx, scheme, hints...)
}
func (s *RedisSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeGet(ctx, schemeId, hints...)
}
func (s *RedisSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// TODO: Redis caching.
return s.Next().SchemeDelete(ctx, schemeId, hints...)
}

View File

@@ -13,6 +13,7 @@ import (
l4g "github.com/alecthomas/log4go" l4g "github.com/alecthomas/log4go"
"github.com/mattermost/gorp" "github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/einterfaces" "github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store"
@@ -37,6 +38,200 @@ type SqlChannelStore struct {
metrics einterfaces.MetricsInterface metrics einterfaces.MetricsInterface
} }
type channelMember struct {
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
}
func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember {
return &channelMember{
ChannelId: cm.ChannelId,
UserId: cm.UserId,
Roles: cm.ExplicitRoles,
LastViewedAt: cm.LastViewedAt,
MsgCount: cm.MsgCount,
MentionCount: cm.MentionCount,
NotifyProps: cm.NotifyProps,
LastUpdateAt: cm.LastUpdateAt,
SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser},
SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin},
}
}
type channelMemberWithSchemeRoles struct {
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
ChannelSchemeDefaultUserRole sql.NullString
ChannelSchemeDefaultAdminRole sql.NullString
}
type channelMemberWithSchemeRolesList []channelMemberWithSchemeRoles
func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember {
var roles []string
var explicitRoles []string
// Identify any system-wide scheme derived roles that are in "Roles" field due to not yet being migrated,
// and exclude them from ExplicitRoles field.
schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool
schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool
for _, role := range strings.Fields(db.Roles) {
isImplicit := false
if role == model.CHANNEL_USER_ROLE_ID {
// We have an implicit role via the system scheme. Override the "schemeUser" field to true.
schemeUser = true
isImplicit = true
} else if role == model.CHANNEL_ADMIN_ROLE_ID {
// We have an implicit role via the system scheme.
schemeAdmin = true
isImplicit = true
}
if !isImplicit {
explicitRoles = append(explicitRoles, role)
}
roles = append(roles, role)
}
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String)
} else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String)
} else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
return &model.ChannelMember{
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MentionCount: db.MentionCount,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: schemeAdmin,
SchemeUser: schemeUser,
ExplicitRoles: strings.Join(explicitRoles, " "),
}
}
func (db channelMemberWithSchemeRolesList) ToModel() *model.ChannelMembers {
cms := model.ChannelMembers{}
for _, cm := range db {
cms = append(cms, *cm.ToModel())
}
return &cms
}
type allChannelMember struct {
ChannelId string
Roles string
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
ChannelSchemeDefaultUserRole sql.NullString
ChannelSchemeDefaultAdminRole sql.NullString
}
type allChannelMembers []allChannelMember
func (db allChannelMember) Process() (string, string) {
roles := strings.Fields(db.Roles)
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String)
} else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String)
} else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
return db.ChannelId, strings.Join(roles, " ")
}
func (db allChannelMembers) ToMapStringString() map[string]string {
result := make(map[string]string)
for _, item := range db {
key, value := item.Process()
result[key] = value
}
return result
}
var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE) var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE) var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE) var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
@@ -76,8 +271,9 @@ func NewSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface)
table.ColMap("Header").SetMaxSize(1024) table.ColMap("Header").SetMaxSize(1024)
table.ColMap("Purpose").SetMaxSize(250) table.ColMap("Purpose").SetMaxSize(250)
table.ColMap("CreatorId").SetMaxSize(26) table.ColMap("CreatorId").SetMaxSize(26)
table.ColMap("SchemeId").SetMaxSize(26)
tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId") tablem := db.AddTableWithName(channelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
tablem.ColMap("ChannelId").SetMaxSize(26) tablem.ColMap("ChannelId").SetMaxSize(26)
tablem.ColMap("UserId").SetMaxSize(26) tablem.ColMap("UserId").SetMaxSize(26)
tablem.ColMap("Roles").SetMaxSize(64) tablem.ColMap("Roles").SetMaxSize(64)
@@ -138,12 +334,12 @@ func (s SqlChannelStore) CreateDirectChannel(userId string, otherUserId string)
cm1 := &model.ChannelMember{ cm1 := &model.ChannelMember{
UserId: userId, UserId: userId,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID, SchemeUser: true,
} }
cm2 := &model.ChannelMember{ cm2 := &model.ChannelMember{
UserId: otherUserId, UserId: otherUserId,
NotifyProps: model.GetDefaultChannelNotifyProps(), NotifyProps: model.GetDefaultChannelNotifyProps(),
Roles: model.CHANNEL_USER_ROLE_ID, SchemeUser: true,
} }
return s.SaveDirectChannel(channel, cm1, cm2) return s.SaveDirectChannel(channel, cm1, cm2)
@@ -732,6 +928,25 @@ func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int) store.
}) })
} }
var CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
SELECT
ChannelMembers.*,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
`
func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChannel { func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
// Grab the channel we are saving this member to // Grab the channel we are saving this member to
@@ -750,7 +965,7 @@ func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChan
if err := transaction.Commit(); err != nil { if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
// If successfull record members have changed in channel // If successful record members have changed in channel
if mu := <-s.extraUpdated(channel); mu.Err != nil { if mu := <-s.extraUpdated(channel); mu.Err != nil {
result.Err = mu.Err result.Err = mu.Err
} }
@@ -770,14 +985,25 @@ func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *mode
return result return result
} }
if err := transaction.Insert(member); err != nil { dbMember := NewChannelMemberFromModel(member)
if err := transaction.Insert(dbMember); err != nil {
if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey"}) { if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey"}) {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.exists.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest) result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.exists.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
} else { } else {
result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.save.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.save.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
} }
} else { } else {
result.Data = member var retrievedMember channelMemberWithSchemeRoles
if err := transaction.SelectOne(&retrievedMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": dbMember.ChannelId, "UserId": dbMember.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+dbMember.ChannelId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+dbMember.ChannelId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
} }
return result return result
@@ -791,38 +1017,48 @@ func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) store.StoreCh
return return
} }
if _, err := s.GetMaster().Update(member); err != nil { if _, err := s.GetMaster().Update(NewChannelMemberFromModel(member)); err != nil {
result.Err = model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = member var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = dbMember.ToModel()
}
} }
}) })
} }
func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.StoreChannel { func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var members model.ChannelMembers var dbMembers channelMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset}) _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = &members result.Data = dbMembers.ToModel()
} }
}) })
} }
func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel { func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var member model.ChannelMember var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound) result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound)
} else { } else {
result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError)
} }
} else { } else {
result.Data = &member result.Data = dbMember.ToModel()
} }
}) })
} }
@@ -866,30 +1102,37 @@ func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string
func (s SqlChannelStore) GetMemberForPost(postId string, userId string) store.StoreChannel { func (s SqlChannelStore) GetMemberForPost(postId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
member := &model.ChannelMember{} var dbMember channelMemberWithSchemeRoles
if err := s.GetReplica().SelectOne( if err := s.GetReplica().SelectOne(&dbMember,
member, `
`SELECT SELECT
ChannelMembers.* ChannelMembers.*,
FROM TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
ChannelMembers, TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
Posts ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Posts ON ChannelMembers.ChannelId = Posts.ChannelId
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
WHERE WHERE
ChannelMembers.ChannelId = Posts.ChannelId ChannelMembers.UserId = :UserId
AND ChannelMembers.UserId = :UserId
AND Posts.Id = :PostId`, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil { AND Posts.Id = :PostId`, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = member result.Data = dbMember.ToModel()
} }
}) })
} }
type allChannelMember struct {
ChannelId string
Roles string
}
func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel { func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
if allowFromCache { if allowFromCache {
@@ -910,17 +1153,32 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
} }
} }
var data []allChannelMember var data allChannelMembers
_, err := s.GetReplica().Select(&data, "SELECT ChannelId, Roles FROM Channels, ChannelMembers WHERE Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.UserId = :UserId AND Channels.DeleteAt = 0", map[string]interface{}{"UserId": userId}) _, err := s.GetReplica().Select(&data, `
SELECT
ChannelMembers.ChannelId, ChannelMembers.Roles, ChannelMembers.SchemeUser, ChannelMembers.SchemeAdmin,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
FROM
ChannelMembers
INNER JOIN
Channels ON ChannelMembers.ChannelId = Channels.Id
LEFT JOIN
Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
LEFT JOIN
Teams ON Channels.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
WHERE
Channels.DeleteAt = 0
AND ChannelMembers.UserId = :UserId`, map[string]interface{}{"UserId": userId})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
} else { } else {
ids := data.ToMapStringString()
ids := make(map[string]string)
for i := range data {
ids[data[i].ChannelId] = data[i].Roles
}
result.Data = ids result.Data = ids
@@ -1249,21 +1507,13 @@ func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType st
func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.StoreChannel { func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
members := &model.ChannelMembers{} var dbMembers channelMemberWithSchemeRolesList
_, err := s.GetReplica().Select(members, ` _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
SELECT cm.*
FROM ChannelMembers cm
INNER JOIN Channels c
ON c.Id = cm.ChannelId
AND (c.TeamId = :TeamId OR c.TeamId = '')
AND c.DeleteAt = 0
WHERE cm.UserId = :UserId
`, map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = members result.Data = dbMembers.ToModel()
} }
}) })
} }
@@ -1455,7 +1705,7 @@ func (s SqlChannelStore) performSearch(searchQuery string, term string, paramete
func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) store.StoreChannel { func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var members model.ChannelMembers var dbMembers channelMemberWithSchemeRolesList
props := make(map[string]interface{}) props := make(map[string]interface{})
idQuery := "" idQuery := ""
@@ -1470,11 +1720,22 @@ func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) sto
props["ChannelId"] = channelId props["ChannelId"] = channelId
if _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId IN ("+idQuery+")", props); err != nil { if _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId IN ("+idQuery+")", props); err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = &members result.Data = dbMembers.ToModel()
}
})
}
func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var channels []*model.Channel
_, err := s.GetReplica().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetChannelsByScheme", "store.sql_channel.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = channels
} }
}) })
} }

View File

@@ -4,11 +4,937 @@
package sqlstore package sqlstore
import ( import (
"database/sql"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest" "github.com/mattermost/mattermost-server/store/storetest"
) )
func TestChannelStore(t *testing.T) { func TestChannelStore(t *testing.T) {
StoreTest(t, storetest.TestChannelStore) StoreTest(t, storetest.TestChannelStore)
} }
func TestChannelStoreInternalDataTypes(t *testing.T) {
t.Run("NewChannelMemberFromModel", func(t *testing.T) { testNewChannelMemberFromModel(t) })
t.Run("ChannelMemberWithSchemeRolesToModel", func(t *testing.T) { testChannelMemberWithSchemeRolesToModel(t) })
t.Run("AllChannelMemberProcess", func(t *testing.T) { testAllChannelMemberProcess(t) })
}
func testNewChannelMemberFromModel(t *testing.T) {
m := model.ChannelMember{
ChannelId: model.NewId(),
UserId: model.NewId(),
Roles: "channel_user channel_admin custom_role",
LastViewedAt: 12345,
MsgCount: 2,
MentionCount: 1,
NotifyProps: model.StringMap{"key": "value"},
LastUpdateAt: 54321,
SchemeUser: true,
SchemeAdmin: true,
ExplicitRoles: "custom_role",
}
db := NewChannelMemberFromModel(&m)
assert.Equal(t, m.ChannelId, db.ChannelId)
assert.Equal(t, m.UserId, db.UserId)
assert.Equal(t, m.LastViewedAt, db.LastViewedAt)
assert.Equal(t, m.MsgCount, db.MsgCount)
assert.Equal(t, m.MentionCount, db.MentionCount)
assert.Equal(t, m.NotifyProps, db.NotifyProps)
assert.Equal(t, m.LastUpdateAt, db.LastUpdateAt)
assert.Equal(t, true, db.SchemeUser.Valid)
assert.Equal(t, true, db.SchemeAdmin.Valid)
assert.Equal(t, m.SchemeUser, db.SchemeUser.Bool)
assert.Equal(t, m.SchemeAdmin, db.SchemeAdmin.Bool)
assert.Equal(t, m.ExplicitRoles, db.Roles)
}
func testChannelMemberWithSchemeRolesToModel(t *testing.T) {
t.Run("BasicProperties", func(t *testing.T) {
// Test all the non-roles properties here.
db := channelMemberWithSchemeRoles{
ChannelId: model.NewId(),
UserId: model.NewId(),
Roles: "custom_role",
LastViewedAt: 12345,
MsgCount: 2,
MentionCount: 1,
NotifyProps: model.StringMap{"key": "value"},
LastUpdateAt: 54321,
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, db.ChannelId, m.ChannelId)
assert.Equal(t, db.UserId, m.UserId)
assert.Equal(t, "custom_role channel_user channel_admin", m.Roles)
assert.Equal(t, db.LastViewedAt, m.LastViewedAt)
assert.Equal(t, db.MsgCount, m.MsgCount)
assert.Equal(t, db.MentionCount, m.MentionCount)
assert.Equal(t, db.NotifyProps, m.NotifyProps)
assert.Equal(t, db.LastUpdateAt, m.LastUpdateAt)
assert.Equal(t, db.SchemeUser.Bool, m.SchemeUser)
assert.Equal(t, db.SchemeAdmin.Bool, m.SchemeAdmin)
assert.Equal(t, db.Roles, m.ExplicitRoles)
})
// Example data *before* the Phase 2 migration has taken place.
t.Run("Unmigrated_NoScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_admin channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_admin channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user custom_role", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "channel_user channel_admin custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user channel_admin custom_role", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data *after* the Phase 2 migration has taken place.
t.Run("Migrated_NoScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "channel_user channel_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role channel_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role channel_user channel_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_NoScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a channel scheme.
t.Run("Migrated_ChannelScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_ChannelScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a team scheme.
t.Run("Migrated_TeamScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "tscheme_channeluser", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "tscheme_channeluser tscheme_channeladmin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role tscheme_channeluser", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "custom_role tscheme_channeluser tscheme_channeladmin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
// Example data with a team and channel scheme.
t.Run("Migrated_TeamAndChannelScheme_User", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_Admin", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_CustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_UserAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_AdminAndCustomRole", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "custom_role cscheme_user cscheme_admin", cm.Roles)
assert.Equal(t, true, cm.SchemeUser)
assert.Equal(t, true, cm.SchemeAdmin)
assert.Equal(t, "custom_role", cm.ExplicitRoles)
})
t.Run("Migrated_TeamAndChannelScheme_NoRoles", func(t *testing.T) {
db := channelMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
cm := db.ToModel()
assert.Equal(t, "", cm.Roles)
assert.Equal(t, false, cm.SchemeUser)
assert.Equal(t, false, cm.SchemeAdmin)
assert.Equal(t, "", cm.ExplicitRoles)
})
}
func testAllChannelMemberProcess(t *testing.T) {
t.Run("Unmigrated_User", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
t.Run("Unmigrated_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user channel_admin",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user channel_admin", roles)
})
t.Run("Unmigrated_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("Unmigrated_Custom", func(t *testing.T) {
db := allChannelMember{
Roles: "custom",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "custom", roles)
})
t.Run("MigratedNoScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
t.Run("MigratedNoScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user channel_admin", roles)
})
t.Run("MigratedNoScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedChannelScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user", roles)
})
t.Run("MigratedChannelScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user cscheme_admin", roles)
})
t.Run("MigratedChannelScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedTeamScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "tscheme_channeluser", roles)
})
t.Run("MigratedTeamScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "tscheme_channeluser tscheme_channeladmin", roles)
})
t.Run("MigratedTeamScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("MigratedTeamAndChannelScheme_User", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user", roles)
})
t.Run("MigratedTeamAndChannelScheme_Admin", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "cscheme_user cscheme_admin", roles)
})
t.Run("MigratedTeamAndChannelScheme_Neither", func(t *testing.T) {
db := allChannelMember{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_channeluser"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_channeladmin"},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: true, String: "cscheme_user"},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "cscheme_admin"},
}
_, roles := db.Process()
assert.Equal(t, "", roles)
})
t.Run("DeduplicationCheck", func(t *testing.T) {
db := allChannelMember{
Roles: "channel_user",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
ChannelSchemeDefaultUserRole: sql.NullString{Valid: false},
ChannelSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
_, roles := db.Process()
assert.Equal(t, "channel_user", roles)
})
}

View File

@@ -10,6 +10,8 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store"
) )
@@ -24,6 +26,7 @@ type Role struct {
DeleteAt int64 DeleteAt int64
Permissions string Permissions string
SchemeManaged bool SchemeManaged bool
BuiltIn bool
} }
func NewRoleFromModel(role *model.Role) *Role { func NewRoleFromModel(role *model.Role) *Role {
@@ -47,6 +50,7 @@ func NewRoleFromModel(role *model.Role) *Role {
DeleteAt: role.DeleteAt, DeleteAt: role.DeleteAt,
Permissions: permissions, Permissions: permissions,
SchemeManaged: role.SchemeManaged, SchemeManaged: role.SchemeManaged,
BuiltIn: role.BuiltIn,
} }
} }
@@ -61,6 +65,7 @@ func (role Role) ToModel() *model.Role {
DeleteAt: role.DeleteAt, DeleteAt: role.DeleteAt,
Permissions: strings.Fields(role.Permissions), Permissions: strings.Fields(role.Permissions),
SchemeManaged: role.SchemeManaged, SchemeManaged: role.SchemeManaged,
BuiltIn: role.BuiltIn,
} }
} }
@@ -84,21 +89,52 @@ func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...s
return result return result
} }
dbRole := NewRoleFromModel(role) if len(role.Id) == 0 {
if len(dbRole.Id) == 0 { if transaction, err := s.GetMaster().Begin(); err != nil {
dbRole.Id = model.NewId() result.Err = model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
dbRole.CreateAt = model.GetMillis() return result
dbRole.UpdateAt = dbRole.CreateAt } else {
if err := s.GetMaster().Insert(dbRole); err != nil { result = s.createRole(ctx, role, transaction, hints...)
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
if result.Err != nil {
transaction.Rollback()
} else if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save_role.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
} }
} else { } else {
dbRole := NewRoleFromModel(role)
dbRole.UpdateAt = model.GetMillis() dbRole.UpdateAt = model.GetMillis()
if rowsChanged, err := s.GetMaster().Update(dbRole); err != nil { if rowsChanged, err := s.GetMaster().Update(dbRole); err != nil {
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 { } else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, "no record to update", http.StatusInternalServerError) result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.update.app_error", nil, "no record to update", http.StatusInternalServerError)
} }
result.Data = dbRole.ToModel()
}
return result
}
func (s *SqlSupplier) createRole(ctx context.Context, role *model.Role, transaction *gorp.Transaction, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Check the role is valid before proceeding.
if !role.IsValidWithoutId() {
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.invalid_role.app_error", nil, "", http.StatusBadRequest)
return result
}
dbRole := NewRoleFromModel(role)
dbRole.Id = model.NewId()
dbRole.CreateAt = model.GetMillis()
dbRole.UpdateAt = dbRole.CreateAt
if err := transaction.Insert(dbRole); err != nil {
result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
result.Data = dbRole.ToModel() result.Data = dbRole.ToModel()
@@ -175,6 +211,36 @@ func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints
return result return result
} }
func (s *SqlSupplier) RoleDelete(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Get the role.
var role *Role
if err := s.GetReplica().SelectOne(&role, "SELECT * from Roles WHERE Id = :Id", map[string]interface{}{"Id": roleId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.get.app_error", nil, "Id="+roleId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.get.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return result
}
time := model.GetMillis()
role.DeleteAt = time
role.UpdateAt = time
if rowsChanged, err := s.GetMaster().Update(role); err != nil {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.delete.update.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlRoleStore.Delete", "store.sql_role.delete.update.app_error", nil, "no record to update", http.StatusInternalServerError)
} else {
result.Data = role.ToModel()
}
return result
}
func (s *SqlSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { func (s *SqlSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult() result := store.NewSupplierResult()

View File

@@ -0,0 +1,14 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestSchemeStore(t *testing.T) {
StoreTest(t, storetest.TestSchemeStore)
}

View File

@@ -0,0 +1,272 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sqlstore
import (
"context"
"database/sql"
"fmt"
"net/http"
"strings"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
func initSqlSupplierSchemes(sqlStore SqlStore) {
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Scheme{}, "Schemes").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26)
table.ColMap("Name").SetMaxSize(64)
table.ColMap("Description").SetMaxSize(1024)
table.ColMap("Scope").SetMaxSize(32)
table.ColMap("DefaultTeamAdminRole").SetMaxSize(64)
table.ColMap("DefaultTeamUserRole").SetMaxSize(64)
table.ColMap("DefaultChannelAdminRole").SetMaxSize(64)
table.ColMap("DefaultChannelUserRole").SetMaxSize(64)
}
}
func (s *SqlSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
if len(scheme.Id) == 0 {
if transaction, err := s.GetMaster().Begin(); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.SaveScheme", "store.sql_scheme.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
} else {
result = s.createScheme(ctx, scheme, transaction, hints...)
if result.Err != nil {
transaction.Rollback()
} else if err := transaction.Commit(); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.SchemeSave", "store.sql_scheme.save_scheme.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
} else {
if !scheme.IsValid() {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.invalid_scheme.app_error", nil, "", http.StatusBadRequest)
return result
}
scheme.UpdateAt = model.GetMillis()
if rowsChanged, err := s.GetMaster().Update(scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.update.app_error", nil, err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.update.app_error", nil, "no record to update", http.StatusInternalServerError)
}
result.Data = scheme
}
return result
}
func (s *SqlSupplier) createScheme(ctx context.Context, scheme *model.Scheme, transaction *gorp.Transaction, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Fetch the default system scheme roles to populate default permissions.
defaultRoleNames := []string{model.TEAM_ADMIN_ROLE_ID, model.TEAM_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, model.CHANNEL_USER_ROLE_ID}
defaultRoles := make(map[string]*model.Role)
if rolesResult := s.RoleGetByNames(ctx, defaultRoleNames); rolesResult.Err != nil {
result.Err = rolesResult.Err
return result
} else {
for _, role := range rolesResult.Data.([]*model.Role) {
switch role.Name {
case model.TEAM_ADMIN_ROLE_ID:
defaultRoles[model.TEAM_ADMIN_ROLE_ID] = role
case model.TEAM_USER_ROLE_ID:
defaultRoles[model.TEAM_USER_ROLE_ID] = role
case model.CHANNEL_ADMIN_ROLE_ID:
defaultRoles[model.CHANNEL_ADMIN_ROLE_ID] = role
case model.CHANNEL_USER_ROLE_ID:
defaultRoles[model.CHANNEL_USER_ROLE_ID] = role
}
}
if len(defaultRoles) != 4 {
result.Err = model.NewAppError("SqlSchemeStore.SaveScheme", "store.sql_scheme.save.retrieve_default_scheme_roles.app_error", nil, "", http.StatusInternalServerError)
return result
}
}
// Create the appropriate default roles for the scheme.
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
// Team Admin Role
teamAdminRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Team Admin Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.TEAM_ADMIN_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, teamAdminRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultTeamAdminRole = saveRoleResult.Data.(*model.Role).Id
}
// Team User Role
teamUserRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Team User Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.TEAM_USER_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, teamUserRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultTeamUserRole = saveRoleResult.Data.(*model.Role).Id
}
}
if scheme.Scope == model.SCHEME_SCOPE_TEAM || scheme.Scope == model.SCHEME_SCOPE_CHANNEL {
// Channel Admin Role
channelAdminRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Channel Admin Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.CHANNEL_ADMIN_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, channelAdminRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultChannelAdminRole = saveRoleResult.Data.(*model.Role).Id
}
// Channel User Role
channelUserRole := &model.Role{
Name: model.NewId(),
DisplayName: fmt.Sprintf("Channel User Role for Scheme %s", scheme.Name),
Permissions: defaultRoles[model.CHANNEL_USER_ROLE_ID].Permissions,
SchemeManaged: true,
}
if saveRoleResult := s.createRole(ctx, channelUserRole, transaction); saveRoleResult.Err != nil {
result.Err = saveRoleResult.Err
return result
} else {
scheme.DefaultChannelUserRole = saveRoleResult.Data.(*model.Role).Id
}
}
scheme.Id = model.NewId()
scheme.CreateAt = model.GetMillis()
scheme.UpdateAt = scheme.CreateAt
// Validate the scheme
if !scheme.IsValidForCreate() {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.invalid_scheme.app_error", nil, "", http.StatusBadRequest)
return result
}
if err := transaction.Insert(scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Save", "store.sql_scheme.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
}
result.Data = scheme
return result
}
func (s *SqlSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
var scheme model.Scheme
if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlSchemeStore.Get", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlSchemeStore.Get", "store.sql_scheme.get.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
result.Data = &scheme
return result
}
func (s *SqlSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
// Get the scheme
var scheme model.Scheme
if err := s.GetReplica().SelectOne(&scheme, "SELECT * from Schemes WHERE Id = :Id", map[string]interface{}{"Id": schemeId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.get.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
}
return result
}
// Check that the scheme isn't being used on any Teams or Channels.
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
if c, err := s.GetReplica().SelectInt("SELECT COUNT(*) FROM Teams WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.team_count.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
} else {
if c > 0 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.scheme_in_use.app_error", nil, "Id="+schemeId, http.StatusInternalServerError)
return result
}
}
} else if scheme.Scope == model.SCHEME_SCOPE_CHANNEL {
if c, err := s.GetReplica().SelectInt("SELECT COUNT(*) FROM Channels WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.channel_count.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
} else {
if c > 0 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.scheme_in_use.app_error", nil, "Id="+schemeId, http.StatusInternalServerError)
return result
}
}
}
// Delete the roles belonging to the scheme.
roleIds := []string{scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole}
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
roleIds = append(roleIds, scheme.DefaultTeamUserRole, scheme.DefaultTeamAdminRole)
}
var inQueryList []string
queryArgs := make(map[string]interface{})
for i, roleId := range roleIds {
inQueryList = append(inQueryList, fmt.Sprintf(":RoleId%v", i))
queryArgs[fmt.Sprintf("RoleId%v", i)] = roleId
}
inQuery := strings.Join(inQueryList, ", ")
time := model.GetMillis()
queryArgs["UpdateAt"] = time
queryArgs["DeleteAt"] = time
if _, err := s.GetMaster().Exec("UPDATE Roles SET UpdateAt = :UpdateAt, DeleteAt = :DeleteAt WHERE Id IN ("+inQuery+")", queryArgs); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.role_update.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
return result
}
// Delete the scheme itself.
scheme.UpdateAt = time
scheme.DeleteAt = time
if rowsChanged, err := s.GetMaster().Update(&scheme); err != nil {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.update.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
} else if rowsChanged != 1 {
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.delete.update.app_error", nil, "no record to update", http.StatusInternalServerError)
} else {
result.Data = &scheme
}
return result
}

View File

@@ -52,6 +52,7 @@ type SqlStore interface {
DoesTableExist(tablename string) bool DoesTableExist(tablename string) bool
DoesColumnExist(tableName string, columName string) bool DoesColumnExist(tableName string, columName string) bool
CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool
CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool
RemoveColumnIfExists(tableName string, columnName string) bool RemoveColumnIfExists(tableName string, columnName string) bool
RemoveTableIfExists(tableName string) bool RemoveTableIfExists(tableName string) bool
RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool
@@ -88,4 +89,5 @@ type SqlStore interface {
Plugin() store.PluginStore Plugin() store.PluginStore
UserAccessToken() store.UserAccessTokenStore UserAccessToken() store.UserAccessTokenStore
Role() store.RoleStore Role() store.RoleStore
Scheme() store.SchemeStore
} }

View File

@@ -89,6 +89,7 @@ type SqlSupplierOldStores struct {
plugin store.PluginStore plugin store.PluginStore
channelMemberHistory store.ChannelMemberHistoryStore channelMemberHistory store.ChannelMemberHistoryStore
role store.RoleStore role store.RoleStore
scheme store.SchemeStore
} }
type SqlSupplier struct { type SqlSupplier struct {
@@ -139,6 +140,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
initSqlSupplierReactions(supplier) initSqlSupplierReactions(supplier)
initSqlSupplierRoles(supplier) initSqlSupplierRoles(supplier)
initSqlSupplierSchemes(supplier)
err := supplier.GetMaster().CreateTablesIfNotExists() err := supplier.GetMaster().CreateTablesIfNotExists()
if err != nil { if err != nil {
@@ -462,6 +464,40 @@ func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName stri
} }
} }
func (ss *SqlSupplier) CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
if ss.DoesColumnExist(tableName, columnName) {
return false
}
if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType)
if err != nil {
l4g.Critical(utils.T("store.sql.create_column.critical"), err)
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
}
return true
} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType)
if err != nil {
l4g.Critical(utils.T("store.sql.create_column.critical"), err)
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MYSQL)
}
return true
} else {
l4g.Critical(utils.T("store.sql.create_column_missing_driver.critical"))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_COLUMN_MISSING)
return false
}
}
func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string) bool { func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string) bool {
if !ss.DoesColumnExist(tableName, columnName) { if !ss.DoesColumnExist(tableName, columnName) {
@@ -834,6 +870,10 @@ func (ss *SqlSupplier) Role() store.RoleStore {
return ss.oldStores.role return ss.oldStores.role
} }
func (ss *SqlSupplier) Scheme() store.SchemeStore {
return ss.oldStores.scheme
}
func (ss *SqlSupplier) DropAllTables() { func (ss *SqlSupplier) DropAllTables() {
ss.master.TruncateTables() ss.master.TruncateTables()
} }

View File

@@ -7,6 +7,7 @@ import (
"database/sql" "database/sql"
"net/http" "net/http"
"strconv" "strconv"
"strings"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store"
@@ -20,6 +21,116 @@ type SqlTeamStore struct {
SqlStore SqlStore
} }
type teamMember struct {
TeamId string
UserId string
Roles string
DeleteAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
}
func NewTeamMemberFromModel(tm *model.TeamMember) *teamMember {
return &teamMember{
TeamId: tm.TeamId,
UserId: tm.UserId,
Roles: tm.ExplicitRoles,
DeleteAt: tm.DeleteAt,
SchemeUser: sql.NullBool{Valid: true, Bool: tm.SchemeUser},
SchemeAdmin: sql.NullBool{Valid: true, Bool: tm.SchemeAdmin},
}
}
type teamMemberWithSchemeRoles struct {
TeamId string
UserId string
Roles string
DeleteAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
TeamSchemeDefaultUserRole sql.NullString
TeamSchemeDefaultAdminRole sql.NullString
}
type teamMemberWithSchemeRolesList []teamMemberWithSchemeRoles
func (db teamMemberWithSchemeRoles) ToModel() *model.TeamMember {
var roles []string
var explicitRoles []string
// Identify any scheme derived roles that are in "Roles" field due to not yet being migrated, and exclude
// them from ExplicitRoles field.
schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool
schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool
for _, role := range strings.Fields(db.Roles) {
isImplicit := false
if role == model.TEAM_USER_ROLE_ID {
// We have an implicit role via the system scheme. Override the "schemeUser" field to true.
schemeUser = true
isImplicit = true
} else if role == model.TEAM_ADMIN_ROLE_ID {
// We have an implicit role via the system scheme.
schemeAdmin = true
isImplicit = true
}
if !isImplicit {
explicitRoles = append(explicitRoles, role)
}
roles = append(roles, role)
}
// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
// them to the Roles field for backwards compatibility reasons.
var schemeImpliedRoles []string
if db.SchemeUser.Valid && db.SchemeUser.Bool {
if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.TEAM_USER_ROLE_ID)
}
}
if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
} else {
schemeImpliedRoles = append(schemeImpliedRoles, model.TEAM_ADMIN_ROLE_ID)
}
}
for _, impliedRole := range schemeImpliedRoles {
alreadyThere := false
for _, role := range roles {
if role == impliedRole {
alreadyThere = true
}
}
if !alreadyThere {
roles = append(roles, impliedRole)
}
}
tm := &model.TeamMember{
TeamId: db.TeamId,
UserId: db.UserId,
Roles: strings.Join(roles, " "),
DeleteAt: db.DeleteAt,
SchemeUser: schemeUser,
SchemeAdmin: schemeAdmin,
ExplicitRoles: strings.Join(explicitRoles, " "),
}
return tm
}
func (db teamMemberWithSchemeRolesList) ToModel() []*model.TeamMember {
tms := make([]*model.TeamMember, 0)
for _, tm := range db {
tms = append(tms, tm.ToModel())
}
return tms
}
func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore { func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
s := &SqlTeamStore{sqlStore} s := &SqlTeamStore{sqlStore}
@@ -34,7 +145,7 @@ func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
table.ColMap("AllowedDomains").SetMaxSize(500) table.ColMap("AllowedDomains").SetMaxSize(500)
table.ColMap("InviteId").SetMaxSize(32) table.ColMap("InviteId").SetMaxSize(32)
tablem := db.AddTableWithName(model.TeamMember{}, "TeamMembers").SetKeys(false, "TeamId", "UserId") tablem := db.AddTableWithName(teamMember{}, "TeamMembers").SetKeys(false, "TeamId", "UserId")
tablem.ColMap("TeamId").SetMaxSize(26) tablem.ColMap("TeamId").SetMaxSize(26)
tablem.ColMap("UserId").SetMaxSize(26) tablem.ColMap("UserId").SetMaxSize(26)
tablem.ColMap("Roles").SetMaxSize(64) tablem.ColMap("Roles").SetMaxSize(64)
@@ -326,12 +437,27 @@ func (s SqlTeamStore) AnalyticsTeamCount() store.StoreChannel {
}) })
} }
var TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
SELECT
TeamMembers.*,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole
FROM
TeamMembers
LEFT JOIN
Teams ON TeamMembers.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
`
func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int) store.StoreChannel { func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
if result.Err = member.IsValid(); result.Err != nil { if result.Err = member.IsValid(); result.Err != nil {
return return
} }
dbMember := NewTeamMemberFromModel(member)
if maxUsersPerTeam >= 0 { if maxUsersPerTeam >= 0 {
if count, err := s.GetMaster().SelectInt( if count, err := s.GetMaster().SelectInt(
`SELECT `SELECT
@@ -354,14 +480,23 @@ func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int)
} }
} }
if err := s.GetMaster().Insert(member); err != nil { if err := s.GetMaster().Insert(dbMember); err != nil {
if IsUniqueConstraintError(err, []string{"TeamId", "teammembers_pkey", "PRIMARY"}) { if IsUniqueConstraintError(err, []string{"TeamId", "teammembers_pkey", "PRIMARY"}) {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", TEAM_MEMBER_EXISTS_ERROR, nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest) result.Err = model.NewAppError("SqlTeamStore.SaveMember", TEAM_MEMBER_EXISTS_ERROR, nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
} else { } else {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.save_member.save.app_error", nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.save_member.save.app_error", nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
} }
} else { } else {
result.Data = member var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": dbMember.TeamId, "UserId": dbMember.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+dbMember.TeamId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.app_error", nil, "team_id="+dbMember.TeamId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
} }
}) })
} }
@@ -374,18 +509,27 @@ func (s SqlTeamStore) UpdateMember(member *model.TeamMember) store.StoreChannel
return return
} }
if _, err := s.GetMaster().Update(member); err != nil { if _, err := s.GetMaster().Update(NewTeamMemberFromModel(member)); err != nil {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.save_member.save.app_error", nil, err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.save_member.save.app_error", nil, err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = member var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": member.TeamId, "UserId": member.UserId}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+member.TeamId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.app_error", nil, "team_id="+member.TeamId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = retrievedMember.ToModel()
}
} }
}) })
} }
func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel { func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var member model.TeamMember var dbMember teamMemberWithSchemeRoles
err := s.GetReplica().SelectOne(&member, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId}) err := s.GetReplica().SelectOne(&dbMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.missing.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusNotFound) result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.missing.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusNotFound)
@@ -393,19 +537,19 @@ func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusInternalServerError)
} }
} else { } else {
result.Data = &member result.Data = dbMember.ToModel()
} }
}) })
} }
func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int) store.StoreChannel { func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}) _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = members result.Data = dbMembers.ToModel()
} }
}) })
} }
@@ -453,7 +597,7 @@ func (s SqlTeamStore) GetActiveMemberCount(teamId string) store.StoreChannel {
func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.StoreChannel { func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember var dbMembers teamMemberWithSchemeRolesList
props := make(map[string]interface{}) props := make(map[string]interface{})
idQuery := "" idQuery := ""
@@ -468,22 +612,22 @@ func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.Sto
props["TeamId"] = teamId props["TeamId"] = teamId
if _, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND UserId IN ("+idQuery+") AND DeleteAt = 0", props); err != nil { if _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId IN ("+idQuery+") AND TeamMembers.DeleteAt = 0", props); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = members result.Data = dbMembers.ToModel()
} }
}) })
} }
func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel { func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) { return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMember var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId", map[string]interface{}{"UserId": userId})
if err != nil { if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError) result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
} else { } else {
result.Data = members result.Data = dbMembers.ToModel()
} }
}) })
} }
@@ -570,3 +714,15 @@ func (us SqlTeamStore) UpdateLastTeamIconUpdate(teamId string, curTime int64) st
} }
}) })
} }
func (s SqlTeamStore) GetTeamsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var teams []*model.Team
_, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetTeamsByScheme", "store.sql_team.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = teams
}
})
}

View File

@@ -4,11 +4,378 @@
package sqlstore package sqlstore
import ( import (
"database/sql"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest" "github.com/mattermost/mattermost-server/store/storetest"
) )
func TestTeamStore(t *testing.T) { func TestTeamStore(t *testing.T) {
StoreTest(t, storetest.TestTeamStore) StoreTest(t, storetest.TestTeamStore)
} }
func TestTeamStoreInternalDataTypes(t *testing.T) {
t.Run("NewTeamMemberFromModel", func(t *testing.T) { testNewTeamMemberFromModel(t) })
t.Run("TeamMemberWithSchemeRolesToModel", func(t *testing.T) { testTeamMemberWithSchemeRolesToModel(t) })
}
func testNewTeamMemberFromModel(t *testing.T) {
m := model.TeamMember{
TeamId: model.NewId(),
UserId: model.NewId(),
Roles: "team_user team_admin custom_role",
DeleteAt: 12345,
SchemeUser: true,
SchemeAdmin: true,
ExplicitRoles: "custom_role",
}
db := NewTeamMemberFromModel(&m)
assert.Equal(t, m.TeamId, db.TeamId)
assert.Equal(t, m.UserId, db.UserId)
assert.Equal(t, m.DeleteAt, db.DeleteAt)
assert.Equal(t, true, db.SchemeUser.Valid)
assert.Equal(t, true, db.SchemeAdmin.Valid)
assert.Equal(t, m.SchemeUser, db.SchemeUser.Bool)
assert.Equal(t, m.SchemeAdmin, db.SchemeAdmin.Bool)
assert.Equal(t, m.ExplicitRoles, db.Roles)
}
func testTeamMemberWithSchemeRolesToModel(t *testing.T) {
// Test all the non-role-related properties here.
t.Run("BasicProperties", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
TeamId: model.NewId(),
UserId: model.NewId(),
Roles: "custom_role",
DeleteAt: 12345,
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, db.TeamId, m.TeamId)
assert.Equal(t, db.UserId, m.UserId)
assert.Equal(t, "custom_role team_user team_admin", m.Roles)
assert.Equal(t, db.DeleteAt, m.DeleteAt)
assert.Equal(t, db.SchemeUser.Bool, m.SchemeUser)
assert.Equal(t, db.SchemeAdmin.Bool, m.SchemeAdmin)
assert.Equal(t, db.Roles, m.ExplicitRoles)
})
// Example data *before* the Phase 2 migration has taken place.
t.Run("Unmigrated_NoScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user team_admin",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user custom_role", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "team_user team_admin custom_role",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin custom_role", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Unmigrated_NoScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: false, Bool: false},
SchemeAdmin: sql.NullBool{Valid: false, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
// Example data *after* the Phase 2 migration has taken place.
t.Run("Migrated_NoScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role team_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "custom_role team_user team_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_NoScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: false},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: false},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
// Example data with a team scheme.
t.Run("Migrated_TeamScheme_User", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "tscheme_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_Admin", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "tscheme_user tscheme_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_CustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_UserAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role tscheme_user", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_AdminAndCustomRole", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "custom_role",
SchemeUser: sql.NullBool{Valid: true, Bool: true},
SchemeAdmin: sql.NullBool{Valid: true, Bool: true},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "custom_role tscheme_user tscheme_admin", m.Roles)
assert.Equal(t, true, m.SchemeUser)
assert.Equal(t, true, m.SchemeAdmin)
assert.Equal(t, "custom_role", m.ExplicitRoles)
})
t.Run("Migrated_TeamScheme_NoRoles", func(t *testing.T) {
db := teamMemberWithSchemeRoles{
Roles: "",
SchemeUser: sql.NullBool{Valid: true, Bool: false},
SchemeAdmin: sql.NullBool{Valid: true, Bool: false},
TeamSchemeDefaultUserRole: sql.NullString{Valid: true, String: "tscheme_user"},
TeamSchemeDefaultAdminRole: sql.NullString{Valid: true, String: "tscheme_admin"},
}
m := db.ToModel()
assert.Equal(t, "", m.Roles)
assert.Equal(t, false, m.SchemeUser)
assert.Equal(t, false, m.SchemeAdmin)
assert.Equal(t, "", m.ExplicitRoles)
})
}

View File

@@ -420,5 +420,18 @@ func UpgradeDatabaseToVersion410(sqlStore SqlStore) {
sqlStore.RemoveIndexIfExists("ClientId_2", "OAuthAccessData") sqlStore.RemoveIndexIfExists("ClientId_2", "OAuthAccessData")
// saveSchemaVersion(sqlStore, VERSION_4_10_0) // saveSchemaVersion(sqlStore, VERSION_4_10_0)
sqlStore.CreateColumnIfNotExistsNoDefault("Teams", "SchemeId", "varchar(26)", "varchar(26)")
sqlStore.CreateColumnIfNotExistsNoDefault("Channels", "SchemeId", "varchar(26)", "varchar(26)")
sqlStore.CreateColumnIfNotExistsNoDefault("TeamMembers", "SchemeUser", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("TeamMembers", "SchemeAdmin", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("ChannelMembers", "SchemeUser", "boolean", "boolean")
sqlStore.CreateColumnIfNotExistsNoDefault("ChannelMembers", "SchemeAdmin", "boolean", "boolean")
sqlStore.CreateColumnIfNotExists("Roles", "BuiltIn", "boolean", "boolean", "0")
sqlStore.GetMaster().Exec("UPDATE Roles SET BuiltIn=true")
sqlStore.GetMaster().Exec("UPDATE Roles SET SchemeManaged=false WHERE Name NOT IN ('system_user', 'system_admin', 'team_user', 'team_admin', 'channel_user', 'channel_admin')")
// saveSchemaVersion(sqlStore, VERSION_4_9_0)
//} //}
} }

View File

@@ -62,6 +62,7 @@ type Store interface {
FileInfo() FileInfoStore FileInfo() FileInfoStore
Reaction() ReactionStore Reaction() ReactionStore
Role() RoleStore Role() RoleStore
Scheme() SchemeStore
Job() JobStore Job() JobStore
UserAccessToken() UserAccessTokenStore UserAccessToken() UserAccessTokenStore
ChannelMemberHistory() ChannelMemberHistoryStore ChannelMemberHistory() ChannelMemberHistoryStore
@@ -105,6 +106,7 @@ type TeamStore interface {
RemoveAllMembersByTeam(teamId string) StoreChannel RemoveAllMembersByTeam(teamId string) StoreChannel
RemoveAllMembersByUser(userId string) StoreChannel RemoveAllMembersByUser(userId string) StoreChannel
UpdateLastTeamIconUpdate(teamId string, curTime int64) StoreChannel UpdateLastTeamIconUpdate(teamId string, curTime int64) StoreChannel
GetTeamsByScheme(schemeId string, offset int, limit int) StoreChannel
} }
type ChannelStore interface { type ChannelStore interface {
@@ -162,6 +164,7 @@ type ChannelStore interface {
AnalyticsDeletedTypeCount(teamId string, channelType string) StoreChannel AnalyticsDeletedTypeCount(teamId string, channelType string) StoreChannel
GetChannelUnread(channelId, userId string) StoreChannel GetChannelUnread(channelId, userId string) StoreChannel
ClearCaches() ClearCaches()
GetChannelsByScheme(schemeId string, offset int, limit int) StoreChannel
} }
type ChannelMemberHistoryStore interface { type ChannelMemberHistoryStore interface {
@@ -477,5 +480,12 @@ type RoleStore interface {
Get(roleId string) StoreChannel Get(roleId string) StoreChannel
GetByName(name string) StoreChannel GetByName(name string) StoreChannel
GetByNames(names []string) StoreChannel GetByNames(names []string) StoreChannel
Delete(roldId string) StoreChannel
PermanentDeleteAll() StoreChannel PermanentDeleteAll() StoreChannel
} }
type SchemeStore interface {
Save(scheme *model.Scheme) StoreChannel
Get(schemeId string) StoreChannel
Delete(schemeId string) StoreChannel
}

View File

@@ -16,6 +16,8 @@ import (
) )
func TestChannelStore(t *testing.T, ss store.Store) { func TestChannelStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) }) t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) })
t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) }) t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) })
t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) }) t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) })
@@ -49,6 +51,8 @@ func TestChannelStore(t *testing.T, ss store.Store) {
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) }) t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) }) t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) })
t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) }) t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) })
t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) })
} }
func testChannelStoreSave(t *testing.T, ss store.Store) { func testChannelStoreSave(t *testing.T, ss store.Store) {
@@ -2186,3 +2190,67 @@ func testChannelStoreMaxChannelsPerTeam(t *testing.T, ss store.Store) {
result = <-ss.Channel().Save(channel, 1) result = <-ss.Channel().Save(channel, 1)
assert.Nil(t, result.Err) assert.Nil(t, result.Err)
} }
func testChannelStoreGetChannelsByScheme(t *testing.T, ss store.Store) {
// Create some schemes.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
s2 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
// Create and save some teams.
c1 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &s1.Id,
}
c2 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &s1.Id,
}
c3 := &model.Channel{
TeamId: model.NewId(),
DisplayName: "Name",
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
}
c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel)
c2 = (<-ss.Channel().Save(c2, 100)).Data.(*model.Channel)
c3 = (<-ss.Channel().Save(c3, 100)).Data.(*model.Channel)
// Get the channels by a valid Scheme ID.
res1 := <-ss.Channel().GetChannelsByScheme(s1.Id, 0, 100)
assert.Nil(t, res1.Err)
d1 := res1.Data.([]*model.Channel)
assert.Len(t, d1, 2)
// Get the channels by a valid Scheme ID where there aren't any matching Channel.
res2 := <-ss.Channel().GetChannelsByScheme(s2.Id, 0, 100)
assert.Nil(t, res2.Err)
d2 := res2.Data.([]*model.Channel)
assert.Len(t, d2, 0)
// Get the channels by an invalid Scheme ID.
res3 := <-ss.Channel().GetChannelsByScheme(model.NewId(), 0, 100)
assert.Nil(t, res3.Err)
d3 := res3.Data.([]*model.Channel)
assert.Len(t, d3, 0)
}

View File

@@ -258,6 +258,22 @@ func (_m *ChannelStore) GetChannels(teamId string, userId string) store.StoreCha
return r0 return r0
} }
// GetChannelsByScheme provides a mock function with given fields: schemeId, offset, limit
func (_m *ChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
ret := _m.Called(schemeId, offset, limit)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(schemeId, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetDeleted provides a mock function with given fields: team_id, offset, limit // GetDeleted provides a mock function with given fields: team_id, offset, limit
func (_m *ChannelStore) GetDeleted(team_id string, offset int, limit int) store.StoreChannel { func (_m *ChannelStore) GetDeleted(team_id string, offset int, limit int) store.StoreChannel {
ret := _m.Called(team_id, offset, limit) ret := _m.Called(team_id, offset, limit)

View File

@@ -432,6 +432,29 @@ func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore {
return r0 return r0
} }
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreDatabaseLayer) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, roldId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, roldId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// RoleGet provides a mock function with given fields: ctx, roleId, hints // RoleGet provides a mock function with given fields: ctx, roleId, hints
func (_m *LayeredStoreDatabaseLayer) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { func (_m *LayeredStoreDatabaseLayer) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints)) _va := make([]interface{}, len(hints))
@@ -547,6 +570,91 @@ func (_m *LayeredStoreDatabaseLayer) RoleSave(ctx context.Context, role *model.R
return r0 return r0
} }
// Scheme provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreDatabaseLayer) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, schemeId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreDatabaseLayer) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, schemeId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
func (_m *LayeredStoreDatabaseLayer) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, scheme)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, scheme, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// Session provides a mock function with given fields: // Session provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore { func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore {
ret := _m.Called() ret := _m.Called()

View File

@@ -145,6 +145,29 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode
return r0 return r0
} }
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreSupplier) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, roldId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, roldId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// RoleGet provides a mock function with given fields: ctx, roleId, hints // RoleGet provides a mock function with given fields: ctx, roleId, hints
func (_m *LayeredStoreSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult { func (_m *LayeredStoreSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints)) _va := make([]interface{}, len(hints))
@@ -260,6 +283,75 @@ func (_m *LayeredStoreSupplier) RoleSave(ctx context.Context, role *model.Role,
return r0 return r0
} }
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, schemeId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
func (_m *LayeredStoreSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, schemeId)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, schemeId, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
func (_m *LayeredStoreSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, scheme)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, scheme, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// SetChainNext provides a mock function with given fields: _a0 // SetChainNext provides a mock function with given fields: _a0
func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) { func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) {
_m.Called(_a0) _m.Called(_a0)

View File

@@ -13,6 +13,22 @@ type RoleStore struct {
mock.Mock mock.Mock
} }
// Delete provides a mock function with given fields: roldId
func (_m *RoleStore) Delete(roldId string) store.StoreChannel {
ret := _m.Called(roldId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(roldId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Get provides a mock function with given fields: roleId // Get provides a mock function with given fields: roleId
func (_m *RoleStore) Get(roleId string) store.StoreChannel { func (_m *RoleStore) Get(roleId string) store.StoreChannel {
ret := _m.Called(roleId) ret := _m.Called(roleId)

View File

@@ -0,0 +1,62 @@
// Code generated by mockery v1.0.0
// Regenerate this file using `make store-mocks`.
package mocks
import mock "github.com/stretchr/testify/mock"
import model "github.com/mattermost/mattermost-server/model"
import store "github.com/mattermost/mattermost-server/store"
// SchemeStore is an autogenerated mock type for the SchemeStore type
type SchemeStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: schemeId
func (_m *SchemeStore) Delete(schemeId string) store.StoreChannel {
ret := _m.Called(schemeId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(schemeId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Get provides a mock function with given fields: schemeId
func (_m *SchemeStore) Get(schemeId string) store.StoreChannel {
ret := _m.Called(schemeId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
r0 = rf(schemeId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Save provides a mock function with given fields: scheme
func (_m *SchemeStore) Save(scheme *model.Scheme) store.StoreChannel {
ret := _m.Called(scheme)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(*model.Scheme) store.StoreChannel); ok {
r0 = rf(scheme)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}

View File

@@ -554,6 +554,22 @@ func (_m *SqlStore) Role() store.RoleStore {
return r0 return r0
} }
// Scheme provides a mock function with given fields:
func (_m *SqlStore) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// Session provides a mock function with given fields: // Session provides a mock function with given fields:
func (_m *SqlStore) Session() store.SessionStore { func (_m *SqlStore) Session() store.SessionStore {
ret := _m.Called() ret := _m.Called()

View File

@@ -299,6 +299,22 @@ func (_m *Store) Role() store.RoleStore {
return r0 return r0
} }
// Scheme provides a mock function with given fields:
func (_m *Store) Scheme() store.SchemeStore {
ret := _m.Called()
var r0 store.SchemeStore
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.SchemeStore)
}
}
return r0
}
// Session provides a mock function with given fields: // Session provides a mock function with given fields:
func (_m *Store) Session() store.SessionStore { func (_m *Store) Session() store.SessionStore {
ret := _m.Called() ret := _m.Called()

View File

@@ -237,6 +237,22 @@ func (_m *TeamStore) GetMembersByIds(teamId string, userIds []string) store.Stor
return r0 return r0
} }
// GetTeamsByScheme provides a mock function with given fields: schemeId, offset, limit
func (_m *TeamStore) GetTeamsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
ret := _m.Called(schemeId, offset, limit)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
r0 = rf(schemeId, offset, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// GetTeamsByUserId provides a mock function with given fields: userId // GetTeamsByUserId provides a mock function with given fields: userId
func (_m *TeamStore) GetTeamsByUserId(userId string) store.StoreChannel { func (_m *TeamStore) GetTeamsByUserId(userId string) store.StoreChannel {
ret := _m.Called(userId) ret := _m.Called(userId)

View File

@@ -17,6 +17,7 @@ func TestRoleStore(t *testing.T, ss store.Store) {
t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, ss) }) t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, ss) })
t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, ss) }) t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, ss) })
t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, ss) }) t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, ss) })
t.Run("Delete", func(t *testing.T) { testRoleStoreDelete(t, ss) })
t.Run("PermanentDeleteAll", func(t *testing.T) { testRoleStorePermanentDeleteAll(t, ss) }) t.Run("PermanentDeleteAll", func(t *testing.T) { testRoleStorePermanentDeleteAll(t, ss) })
} }
@@ -244,6 +245,49 @@ func testRoleStoreGetByNames(t *testing.T, ss store.Store) {
assert.NotContains(t, roles6, d3) assert.NotContains(t, roles6, d3)
} }
func testRoleStoreDelete(t *testing.T, 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,
}
res1 := <-ss.Role().Save(r1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Role)
assert.Len(t, d1.Id, 26)
// Check the role is there.
res2 := <-ss.Role().Get(d1.Id)
assert.Nil(t, res2.Err)
// Delete the role.
res3 := <-ss.Role().Delete(d1.Id)
assert.Nil(t, res3.Err)
// Check the role is deleted there.
res4 := <-ss.Role().Get(d1.Id)
assert.Nil(t, res4.Err)
d2 := res4.Data.(*model.Role)
assert.NotZero(t, d2.DeleteAt)
res5 := <-ss.Role().GetByName(d1.Name)
assert.Nil(t, res5.Err)
d3 := res5.Data.(*model.Role)
assert.NotZero(t, d3.DeleteAt)
// Try and delete a role that does not exist.
res6 := <-ss.Role().Delete(model.NewId())
assert.NotNil(t, res6.Err)
}
func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) { func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
r1 := &model.Role{ r1 := &model.Role{
Name: model.NewId(), Name: model.NewId(),
@@ -256,6 +300,7 @@ func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
}, },
SchemeManaged: false, SchemeManaged: false,
} }
r2 := &model.Role{ r2 := &model.Role{
Name: model.NewId(), Name: model.NewId(),
DisplayName: model.NewId(), DisplayName: model.NewId(),

View File

@@ -0,0 +1,303 @@
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package storetest
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
func TestSchemeStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testSchemeStoreSave(t, ss) })
t.Run("Get", func(t *testing.T) { testSchemeStoreGet(t, ss) })
t.Run("Delete", func(t *testing.T) { testSchemeStoreDelete(t, ss) })
}
func createDefaultRoles(t *testing.T, ss store.Store) {
<-ss.Role().Save(&model.Role{
Name: model.TEAM_ADMIN_ROLE_ID,
DisplayName: model.TEAM_ADMIN_ROLE_ID,
Permissions: []string{
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.TEAM_USER_ROLE_ID,
DisplayName: model.TEAM_USER_ROLE_ID,
Permissions: []string{
model.PERMISSION_VIEW_TEAM.Id,
model.PERMISSION_ADD_USER_TO_TEAM.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.CHANNEL_ADMIN_ROLE_ID,
DisplayName: model.CHANNEL_ADMIN_ROLE_ID,
Permissions: []string{
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
},
})
<-ss.Role().Save(&model.Role{
Name: model.CHANNEL_USER_ROLE_ID,
DisplayName: model.CHANNEL_USER_ROLE_ID,
Permissions: []string{
model.PERMISSION_READ_CHANNEL.Id,
model.PERMISSION_CREATE_POST.Id,
},
})
}
func testSchemeStoreSave(t *testing.T, ss store.Store) {
// Save a new scheme.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
// Check all fields saved correctly.
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
assert.Equal(t, s1.Name, d1.Name)
assert.Equal(t, s1.Description, d1.Description)
assert.NotZero(t, d1.CreateAt)
assert.NotZero(t, d1.UpdateAt)
assert.Zero(t, d1.DeleteAt)
assert.Equal(t, s1.Scope, d1.Scope)
assert.Len(t, d1.DefaultTeamAdminRole, 26)
assert.Len(t, d1.DefaultTeamUserRole, 26)
assert.Len(t, d1.DefaultChannelAdminRole, 26)
assert.Len(t, d1.DefaultChannelUserRole, 26)
// Check the default roles were created correctly.
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes1.Err)
role1 := roleRes1.Data.(*model.Role)
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
assert.True(t, role1.SchemeManaged)
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes2.Err)
role2 := roleRes2.Data.(*model.Role)
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
assert.True(t, role2.SchemeManaged)
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes3.Err)
role3 := roleRes3.Data.(*model.Role)
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
assert.True(t, role3.SchemeManaged)
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes4.Err)
role4 := roleRes4.Data.(*model.Role)
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
assert.True(t, role4.SchemeManaged)
// Change the scheme description and update.
d1.Description = model.NewId()
res2 := <-ss.Scheme().Save(d1)
assert.Nil(t, res2.Err)
d2 := res2.Data.(*model.Scheme)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, s1.Name, d2.Name)
assert.Equal(t, d1.Description, d2.Description)
assert.NotZero(t, d2.CreateAt)
assert.NotZero(t, d2.UpdateAt)
assert.Zero(t, d2.DeleteAt)
assert.Equal(t, s1.Scope, d2.Scope)
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
// Try saving one with an invalid ID set.
s3 := &model.Scheme{
Id: model.NewId(),
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res3 := <-ss.Scheme().Save(s3)
assert.NotNil(t, res3.Err)
}
func testSchemeStoreGet(t *testing.T, ss store.Store) {
// Save a scheme to test with.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
// Get a valid scheme
res2 := <-ss.Scheme().Get(d1.Id)
assert.Nil(t, res2.Err)
d2 := res1.Data.(*model.Scheme)
assert.Equal(t, d1.Id, d2.Id)
assert.Equal(t, s1.Name, d2.Name)
assert.Equal(t, d1.Description, d2.Description)
assert.NotZero(t, d2.CreateAt)
assert.NotZero(t, d2.UpdateAt)
assert.Zero(t, d2.DeleteAt)
assert.Equal(t, s1.Scope, d2.Scope)
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
// Get an invalid scheme
res3 := <-ss.Scheme().Get(model.NewId())
assert.NotNil(t, res3.Err)
}
func testSchemeStoreDelete(t *testing.T, ss store.Store) {
// Save a new scheme.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
// Check all fields saved correctly.
res1 := <-ss.Scheme().Save(s1)
assert.Nil(t, res1.Err)
d1 := res1.Data.(*model.Scheme)
assert.Len(t, d1.Id, 26)
assert.Equal(t, s1.Name, d1.Name)
assert.Equal(t, s1.Description, d1.Description)
assert.NotZero(t, d1.CreateAt)
assert.NotZero(t, d1.UpdateAt)
assert.Zero(t, d1.DeleteAt)
assert.Equal(t, s1.Scope, d1.Scope)
assert.Len(t, d1.DefaultTeamAdminRole, 26)
assert.Len(t, d1.DefaultTeamUserRole, 26)
assert.Len(t, d1.DefaultChannelAdminRole, 26)
assert.Len(t, d1.DefaultChannelUserRole, 26)
// Check the default roles were created correctly.
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes1.Err)
role1 := roleRes1.Data.(*model.Role)
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
assert.True(t, role1.SchemeManaged)
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes2.Err)
role2 := roleRes2.Data.(*model.Role)
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
assert.True(t, role2.SchemeManaged)
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes3.Err)
role3 := roleRes3.Data.(*model.Role)
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
assert.True(t, role3.SchemeManaged)
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes4.Err)
role4 := roleRes4.Data.(*model.Role)
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
assert.True(t, role4.SchemeManaged)
// Delete the scheme.
res2 := <-ss.Scheme().Delete(d1.Id)
if !assert.Nil(t, res2.Err) {
t.Fatal(res2.Err)
}
d2 := res2.Data.(*model.Scheme)
assert.NotZero(t, d2.DeleteAt)
// Check that the roles are deleted too.
roleRes5 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
assert.Nil(t, roleRes5.Err)
role5 := roleRes5.Data.(*model.Role)
assert.NotZero(t, role5.DeleteAt)
roleRes6 := <-ss.Role().Get(d1.DefaultTeamUserRole)
assert.Nil(t, roleRes6.Err)
role6 := roleRes6.Data.(*model.Role)
assert.NotZero(t, role6.DeleteAt)
roleRes7 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
assert.Nil(t, roleRes7.Err)
role7 := roleRes7.Data.(*model.Role)
assert.NotZero(t, role7.DeleteAt)
roleRes8 := <-ss.Role().Get(d1.DefaultChannelUserRole)
assert.Nil(t, roleRes8.Err)
role8 := roleRes8.Data.(*model.Role)
assert.NotZero(t, role8.DeleteAt)
// Try deleting a scheme that does not exist.
res3 := <-ss.Scheme().Delete(model.NewId())
assert.NotNil(t, res3.Err)
// Try deleting a team scheme that's in use.
s4 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
res4 := <-ss.Scheme().Save(s4)
assert.Nil(t, res4.Err)
d4 := res4.Data.(*model.Scheme)
t4 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &d4.Id,
}
tres4 := <-ss.Team().Save(t4)
assert.Nil(t, tres4.Err)
t4 = tres4.Data.(*model.Team)
sres4 := <-ss.Scheme().Delete(d4.Id)
assert.NotNil(t, sres4.Err)
// Try deleting a channel scheme that's in use.
s5 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_CHANNEL,
}
res5 := <-ss.Scheme().Save(s5)
assert.Nil(t, res5.Err)
d5 := res5.Data.(*model.Scheme)
c5 := &model.Channel{
TeamId: model.NewId(),
DisplayName: model.NewId(),
Name: model.NewId(),
Type: model.CHANNEL_OPEN,
SchemeId: &d5.Id,
}
cres5 := <-ss.Channel().Save(c5, -1)
assert.Nil(t, cres5.Err)
c5 = cres5.Data.(*model.Channel)
sres5 := <-ss.Scheme().Delete(d5.Id)
assert.NotNil(t, sres5.Err)
}

View File

@@ -44,6 +44,7 @@ type Store struct {
PluginStore mocks.PluginStore PluginStore mocks.PluginStore
ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore
RoleStore mocks.RoleStore RoleStore mocks.RoleStore
SchemeStore mocks.SchemeStore
} }
func (s *Store) Team() store.TeamStore { return &s.TeamStore } func (s *Store) Team() store.TeamStore { return &s.TeamStore }
@@ -70,6 +71,7 @@ func (s *Store) Job() store.JobStore { return &s.JobSt
func (s *Store) UserAccessToken() store.UserAccessTokenStore { return &s.UserAccessTokenStore } func (s *Store) UserAccessToken() store.UserAccessTokenStore { return &s.UserAccessTokenStore }
func (s *Store) Plugin() store.PluginStore { return &s.PluginStore } func (s *Store) Plugin() store.PluginStore { return &s.PluginStore }
func (s *Store) Role() store.RoleStore { return &s.RoleStore } func (s *Store) Role() store.RoleStore { return &s.RoleStore }
func (s *Store) Scheme() store.SchemeStore { return &s.SchemeStore }
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore { func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
return &s.ChannelMemberHistoryStore return &s.ChannelMemberHistoryStore
} }
@@ -107,5 +109,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
&s.ChannelMemberHistoryStore, &s.ChannelMemberHistoryStore,
&s.PluginStore, &s.PluginStore,
&s.RoleStore, &s.RoleStore,
&s.SchemeStore,
) )
} }

View File

@@ -7,11 +7,15 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store" "github.com/mattermost/mattermost-server/store"
) )
func TestTeamStore(t *testing.T, ss store.Store) { func TestTeamStore(t *testing.T, ss store.Store) {
createDefaultRoles(t, ss)
t.Run("Save", func(t *testing.T) { testTeamStoreSave(t, ss) }) t.Run("Save", func(t *testing.T) { testTeamStoreSave(t, ss) })
t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) }) t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) })
t.Run("UpdateDisplayName", func(t *testing.T) { testTeamStoreUpdateDisplayName(t, ss) }) t.Run("UpdateDisplayName", func(t *testing.T) { testTeamStoreUpdateDisplayName(t, ss) })
@@ -34,6 +38,7 @@ func TestTeamStore(t *testing.T, ss store.Store) {
t.Run("GetChannelUnreadsForAllTeams", func(t *testing.T) { testGetChannelUnreadsForAllTeams(t, ss) }) t.Run("GetChannelUnreadsForAllTeams", func(t *testing.T) { testGetChannelUnreadsForAllTeams(t, ss) })
t.Run("GetChannelUnreadsForTeam", func(t *testing.T) { testGetChannelUnreadsForTeam(t, ss) }) t.Run("GetChannelUnreadsForTeam", func(t *testing.T) { testGetChannelUnreadsForTeam(t, ss) })
t.Run("UpdateLastTeamIconUpdate", func(t *testing.T) { testUpdateLastTeamIconUpdate(t, ss) }) t.Run("UpdateLastTeamIconUpdate", func(t *testing.T) { testUpdateLastTeamIconUpdate(t, ss) })
t.Run("GetTeamsByScheme", func(t *testing.T) { testGetTeamsByScheme(t, ss) })
} }
func testTeamStoreSave(t *testing.T, ss store.Store) { func testTeamStoreSave(t *testing.T, ss store.Store) {
@@ -1029,3 +1034,67 @@ func testUpdateLastTeamIconUpdate(t *testing.T, ss store.Store) {
t.Fatal("LastTeamIconUpdate not updated") t.Fatal("LastTeamIconUpdate not updated")
} }
} }
func testGetTeamsByScheme(t *testing.T, ss store.Store) {
// Create some schemes.
s1 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
s2 := &model.Scheme{
Name: model.NewId(),
Description: model.NewId(),
Scope: model.SCHEME_SCOPE_TEAM,
}
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
// Create and save some teams.
t1 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &s1.Id,
}
t2 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
SchemeId: &s1.Id,
}
t3 := &model.Team{
Name: model.NewId(),
DisplayName: model.NewId(),
Email: model.NewId() + "@nowhere.com",
Type: model.TEAM_OPEN,
}
t1 = (<-ss.Team().Save(t1)).Data.(*model.Team)
t2 = (<-ss.Team().Save(t2)).Data.(*model.Team)
t3 = (<-ss.Team().Save(t3)).Data.(*model.Team)
// Get the teams by a valid Scheme ID.
res1 := <-ss.Team().GetTeamsByScheme(s1.Id, 0, 100)
assert.Nil(t, res1.Err)
d1 := res1.Data.([]*model.Team)
assert.Len(t, d1, 2)
// Get the teams by a valid Scheme ID where there aren't any matching Teams.
res2 := <-ss.Team().GetTeamsByScheme(s2.Id, 0, 100)
assert.Nil(t, res2.Err)
d2 := res2.Data.([]*model.Team)
assert.Len(t, d2, 0)
// Get the teams by an invalid Scheme ID.
res3 := <-ss.Team().GetTeamsByScheme(model.NewId(), 0, 100)
assert.Nil(t, res3.Err)
d3 := res3.Data.([]*model.Team)
assert.Len(t, d3, 0)
}