summaryrefslogtreecommitdiffstats
path: root/store/sqlstore
diff options
context:
space:
mode:
authorGeorge Goldberg <george@gberg.me>2018-04-20 19:49:13 +0100
committerMartin Kraft <mkraft@users.noreply.github.com>2018-04-20 14:49:13 -0400
commitcd55c44c8fd8f61cdb7cbfb57a588be82c7aa0ab (patch)
tree2979276d03b5bca72b549d7576eab104ceefd495 /store/sqlstore
parent853445dc2ea68f765faa04ad14618b04f1081c43 (diff)
downloadchat-cd55c44c8fd8f61cdb7cbfb57a588be82c7aa0ab.tar.gz
chat-cd55c44c8fd8f61cdb7cbfb57a588be82c7aa0ab.tar.bz2
chat-cd55c44c8fd8f61cdb7cbfb57a588be82c7aa0ab.zip
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.
Diffstat (limited to 'store/sqlstore')
-rw-r--r--store/sqlstore/channel_store.go363
-rw-r--r--store/sqlstore/channel_store_test.go926
-rw-r--r--store/sqlstore/role_supplier.go80
-rw-r--r--store/sqlstore/scheme_store_test.go14
-rw-r--r--store/sqlstore/scheme_supplier.go272
-rw-r--r--store/sqlstore/store.go2
-rw-r--r--store/sqlstore/supplier.go40
-rw-r--r--store/sqlstore/team_store.go190
-rw-r--r--store/sqlstore/team_store_test.go367
-rw-r--r--store/sqlstore/upgrade.go13
10 files changed, 2192 insertions, 75 deletions
diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go
index 21785c461..0ddbc7221 100644
--- a/store/sqlstore/channel_store.go
+++ b/store/sqlstore/channel_store.go
@@ -13,6 +13,7 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/gorp"
+
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
@@ -37,6 +38,200 @@ type SqlChannelStore struct {
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 allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_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("Purpose").SetMaxSize(250)
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("UserId").SetMaxSize(26)
tablem.ColMap("Roles").SetMaxSize(64)
@@ -138,12 +334,12 @@ func (s SqlChannelStore) CreateDirectChannel(userId string, otherUserId string)
cm1 := &model.ChannelMember{
UserId: userId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
- Roles: model.CHANNEL_USER_ROLE_ID,
+ SchemeUser: true,
}
cm2 := &model.ChannelMember{
UserId: otherUserId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
- Roles: model.CHANNEL_USER_ROLE_ID,
+ SchemeUser: true,
}
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 {
return store.Do(func(result *store.StoreResult) {
// 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 {
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 {
result.Err = mu.Err
}
@@ -770,14 +985,25 @@ func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *mode
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"}) {
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 {
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 {
- 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
@@ -791,38 +1017,48 @@ func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) store.StoreCh
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)
} 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 {
return store.Do(func(result *store.StoreResult) {
- var members model.ChannelMembers
- _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
+ var dbMembers channelMemberWithSchemeRolesList
+ _, 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 {
result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError)
} else {
- result.Data = &members
+ result.Data = dbMembers.ToModel()
}
})
}
func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel {
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 {
result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound)
} 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)
}
} 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 {
return store.Do(func(result *store.StoreResult) {
- member := &model.ChannelMember{}
- if err := s.GetReplica().SelectOne(
- member,
- `SELECT
- ChannelMembers.*
- FROM
- ChannelMembers,
- Posts
+ var dbMember channelMemberWithSchemeRoles
+ if err := s.GetReplica().SelectOne(&dbMember,
+ `
+ SELECT
+ ChannelMembers.*,
+ TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
+ TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
+ 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
- ChannelMembers.ChannelId = Posts.ChannelId
- AND ChannelMembers.UserId = :UserId
+ ChannelMembers.UserId = :UserId
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)
} 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 {
return store.Do(func(result *store.StoreResult) {
if allowFromCache {
@@ -910,17 +1153,32 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
}
}
- var data []allChannelMember
- _, 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})
+ var data allChannelMembers
+ _, 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 {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
} else {
-
- ids := make(map[string]string)
- for i := range data {
- ids[data[i].ChannelId] = data[i].Roles
- }
+ ids := data.ToMapStringString()
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 {
return store.Do(func(result *store.StoreResult) {
- members := &model.ChannelMembers{}
- _, err := s.GetReplica().Select(members, `
- 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})
+ var dbMembers channelMemberWithSchemeRolesList
+ _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
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)
} 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 {
return store.Do(func(result *store.StoreResult) {
- var members model.ChannelMembers
+ var dbMembers channelMemberWithSchemeRolesList
props := make(map[string]interface{})
idQuery := ""
@@ -1470,11 +1720,22 @@ func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) sto
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)
} 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
}
})
}
diff --git a/store/sqlstore/channel_store_test.go b/store/sqlstore/channel_store_test.go
index 8e5ad5f0f..0e8b4191a 100644
--- a/store/sqlstore/channel_store_test.go
+++ b/store/sqlstore/channel_store_test.go
@@ -4,11 +4,937 @@
package sqlstore
import (
+ "database/sql"
"testing"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestChannelStore(t *testing.T) {
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)
+ })
+}
diff --git a/store/sqlstore/role_supplier.go b/store/sqlstore/role_supplier.go
index ddbdaca52..19ef602eb 100644
--- a/store/sqlstore/role_supplier.go
+++ b/store/sqlstore/role_supplier.go
@@ -10,6 +10,8 @@ import (
"net/http"
"strings"
+ "github.com/mattermost/gorp"
+
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
@@ -24,6 +26,7 @@ type Role struct {
DeleteAt int64
Permissions string
SchemeManaged bool
+ BuiltIn bool
}
func NewRoleFromModel(role *model.Role) *Role {
@@ -47,6 +50,7 @@ func NewRoleFromModel(role *model.Role) *Role {
DeleteAt: role.DeleteAt,
Permissions: permissions,
SchemeManaged: role.SchemeManaged,
+ BuiltIn: role.BuiltIn,
}
}
@@ -61,6 +65,7 @@ func (role Role) ToModel() *model.Role {
DeleteAt: role.DeleteAt,
Permissions: strings.Fields(role.Permissions),
SchemeManaged: role.SchemeManaged,
+ BuiltIn: role.BuiltIn,
}
}
@@ -84,21 +89,52 @@ func (s *SqlSupplier) RoleSave(ctx context.Context, role *model.Role, hints ...s
return result
}
- dbRole := NewRoleFromModel(role)
- if len(dbRole.Id) == 0 {
- dbRole.Id = model.NewId()
- dbRole.CreateAt = model.GetMillis()
- dbRole.UpdateAt = dbRole.CreateAt
- if err := s.GetMaster().Insert(dbRole); err != nil {
- result.Err = model.NewAppError("SqlRoleStore.Save", "store.sql_role.save.insert.app_error", nil, err.Error(), http.StatusInternalServerError)
+ if len(role.Id) == 0 {
+ if transaction, err := s.GetMaster().Begin(); err != nil {
+ result.Err = model.NewAppError("SqlRoleStore.RoleSave", "store.sql_role.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
+ return result
+ } else {
+ result = s.createRole(ctx, role, transaction, hints...)
+
+ 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 {
+ dbRole := NewRoleFromModel(role)
+
dbRole.UpdateAt = model.GetMillis()
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)
} 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.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()
@@ -175,6 +211,36 @@ func (s *SqlSupplier) RoleGetByNames(ctx context.Context, names []string, hints
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 {
result := store.NewSupplierResult()
diff --git a/store/sqlstore/scheme_store_test.go b/store/sqlstore/scheme_store_test.go
new file mode 100644
index 000000000..b07495715
--- /dev/null
+++ b/store/sqlstore/scheme_store_test.go
@@ -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)
+}
diff --git a/store/sqlstore/scheme_supplier.go b/store/sqlstore/scheme_supplier.go
new file mode 100644
index 000000000..278d1a3c4
--- /dev/null
+++ b/store/sqlstore/scheme_supplier.go
@@ -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
+}
diff --git a/store/sqlstore/store.go b/store/sqlstore/store.go
index 1c623f0b1..fc7b3be18 100644
--- a/store/sqlstore/store.go
+++ b/store/sqlstore/store.go
@@ -52,6 +52,7 @@ type SqlStore interface {
DoesTableExist(tablename string) bool
DoesColumnExist(tableName string, columName 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
RemoveTableIfExists(tableName string) bool
RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool
@@ -88,4 +89,5 @@ type SqlStore interface {
Plugin() store.PluginStore
UserAccessToken() store.UserAccessTokenStore
Role() store.RoleStore
+ Scheme() store.SchemeStore
}
diff --git a/store/sqlstore/supplier.go b/store/sqlstore/supplier.go
index 99b35a664..db24ba980 100644
--- a/store/sqlstore/supplier.go
+++ b/store/sqlstore/supplier.go
@@ -89,6 +89,7 @@ type SqlSupplierOldStores struct {
plugin store.PluginStore
channelMemberHistory store.ChannelMemberHistoryStore
role store.RoleStore
+ scheme store.SchemeStore
}
type SqlSupplier struct {
@@ -139,6 +140,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
initSqlSupplierReactions(supplier)
initSqlSupplierRoles(supplier)
+ initSqlSupplierSchemes(supplier)
err := supplier.GetMaster().CreateTablesIfNotExists()
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 {
if !ss.DoesColumnExist(tableName, columnName) {
@@ -834,6 +870,10 @@ func (ss *SqlSupplier) Role() store.RoleStore {
return ss.oldStores.role
}
+func (ss *SqlSupplier) Scheme() store.SchemeStore {
+ return ss.oldStores.scheme
+}
+
func (ss *SqlSupplier) DropAllTables() {
ss.master.TruncateTables()
}
diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go
index 6528b8e4c..5c39dc839 100644
--- a/store/sqlstore/team_store.go
+++ b/store/sqlstore/team_store.go
@@ -7,6 +7,7 @@ import (
"database/sql"
"net/http"
"strconv"
+ "strings"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
@@ -20,6 +21,116 @@ type SqlTeamStore struct {
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 {
s := &SqlTeamStore{sqlStore}
@@ -34,7 +145,7 @@ func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
table.ColMap("AllowedDomains").SetMaxSize(500)
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("UserId").SetMaxSize(26)
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 {
return store.Do(func(result *store.StoreResult) {
if result.Err = member.IsValid(); result.Err != nil {
return
}
+ dbMember := NewTeamMemberFromModel(member)
+
if maxUsersPerTeam >= 0 {
if count, err := s.GetMaster().SelectInt(
`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"}) {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", TEAM_MEMBER_EXISTS_ERROR, nil, "team_id="+member.TeamId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest)
} 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)
}
} 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
}
- 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)
} 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 {
return store.Do(func(result *store.StoreResult) {
- var member model.TeamMember
- err := s.GetReplica().SelectOne(&member, "SELECT * FROM TeamMembers WHERE TeamId = :TeamId AND UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
+ var dbMember teamMemberWithSchemeRoles
+ 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 == 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)
@@ -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)
}
} else {
- result.Data = &member
+ result.Data = dbMember.ToModel()
}
})
}
func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
- var members []*model.TeamMember
- _, 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})
+ var dbMembers teamMemberWithSchemeRolesList
+ _, 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 {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
} 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 {
return store.Do(func(result *store.StoreResult) {
- var members []*model.TeamMember
+ var dbMembers teamMemberWithSchemeRolesList
props := make(map[string]interface{})
idQuery := ""
@@ -468,22 +612,22 @@ func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.Sto
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)
} else {
- result.Data = members
+ result.Data = dbMembers.ToModel()
}
})
}
func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
- var members []*model.TeamMember
- _, err := s.GetReplica().Select(&members, "SELECT * FROM TeamMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId})
+ var dbMembers teamMemberWithSchemeRolesList
+ _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId", map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
} 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
+ }
+ })
+}
diff --git a/store/sqlstore/team_store_test.go b/store/sqlstore/team_store_test.go
index 6618285c4..4aaefd1a6 100644
--- a/store/sqlstore/team_store_test.go
+++ b/store/sqlstore/team_store_test.go
@@ -4,11 +4,378 @@
package sqlstore
import (
+ "database/sql"
"testing"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestTeamStore(t *testing.T) {
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)
+ })
+}
diff --git a/store/sqlstore/upgrade.go b/store/sqlstore/upgrade.go
index 059d1a866..1d288eae0 100644
--- a/store/sqlstore/upgrade.go
+++ b/store/sqlstore/upgrade.go
@@ -420,5 +420,18 @@ func UpgradeDatabaseToVersion410(sqlStore SqlStore) {
sqlStore.RemoveIndexIfExists("ClientId_2", "OAuthAccessData")
// 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)
//}
}