Merge branch 'advanced-permissions-phase-2'
Этот коммит содержится в:
@@ -97,7 +97,8 @@ type Routes struct {
|
||||
|
||||
Reactions *mux.Router // 'api/v4/reactions'
|
||||
|
||||
Roles *mux.Router // 'api/v4/roles'
|
||||
Roles *mux.Router // 'api/v4/roles'
|
||||
Schemes *mux.Router // 'api/v4/schemes'
|
||||
|
||||
Emojis *mux.Router // 'api/v4/emoji'
|
||||
Emoji *mux.Router // 'api/v4/emoji/{emoji_id:[A-Za-z0-9]+}'
|
||||
@@ -198,6 +199,7 @@ func Init(a *app.App, root *mux.Router) *API {
|
||||
api.BaseRoutes.OpenGraph = api.BaseRoutes.ApiRoot.PathPrefix("/opengraph").Subrouter()
|
||||
|
||||
api.BaseRoutes.Roles = api.BaseRoutes.ApiRoot.PathPrefix("/roles").Subrouter()
|
||||
api.BaseRoutes.Schemes = api.BaseRoutes.ApiRoot.PathPrefix("/schemes").Subrouter()
|
||||
|
||||
api.BaseRoutes.Image = api.BaseRoutes.ApiRoot.PathPrefix("/image").Subrouter()
|
||||
|
||||
@@ -227,6 +229,7 @@ func Init(a *app.App, root *mux.Router) *API {
|
||||
api.InitOpenGraph()
|
||||
api.InitPlugin()
|
||||
api.InitRole()
|
||||
api.InitScheme()
|
||||
api.InitImage()
|
||||
|
||||
root.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))
|
||||
|
||||
@@ -125,6 +125,7 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
||||
wsapi.Init(th.App, th.App.Srv.WebSocketRouter)
|
||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||
th.App.DoAdvancedPermissionsMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
|
||||
@@ -767,7 +768,7 @@ func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Chan
|
||||
|
||||
if cmr := <-me.App.Srv.Store.Channel().GetMember(channel.Id, user.Id); cmr.Err == nil {
|
||||
cm := cmr.Data.(*model.ChannelMember)
|
||||
cm.Roles = "channel_admin channel_user"
|
||||
cm.SchemeAdmin = true
|
||||
if sr := <-me.App.Srv.Store.Channel().UpdateMember(cm); sr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(sr.Err)
|
||||
@@ -783,28 +784,42 @@ func (me *TestHelper) MakeUserChannelAdmin(user *model.User, channel *model.Chan
|
||||
func (me *TestHelper) UpdateUserToTeamAdmin(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID}
|
||||
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
|
||||
if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
|
||||
tm := tmr.Data.(*model.TeamMember)
|
||||
tm.SchemeAdmin = true
|
||||
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(sr.Err)
|
||||
}
|
||||
} else {
|
||||
utils.EnableDebugLogForTest()
|
||||
mlog.Error(tmr.Err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(tmr.Err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) UpdateUserToNonTeamAdmin(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id, Roles: model.TEAM_USER_ROLE_ID}
|
||||
if tmr := <-me.App.Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
|
||||
if tmr := <-me.App.Srv.Store.Team().GetMember(team.Id, user.Id); tmr.Err == nil {
|
||||
tm := tmr.Data.(*model.TeamMember)
|
||||
tm.SchemeAdmin = false
|
||||
if sr := <-me.App.Srv.Store.Team().UpdateMember(tm); sr.Err != nil {
|
||||
utils.EnableDebugLogForTest()
|
||||
panic(sr.Err)
|
||||
}
|
||||
} else {
|
||||
utils.EnableDebugLogForTest()
|
||||
mlog.Error(tmr.Err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(tmr.Err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ func (api *API) InitChannel() {
|
||||
api.BaseRoutes.Channels.Handle("/direct", api.ApiSessionRequired(createDirectChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/group", api.ApiSessionRequired(createGroupChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/members/{user_id:[A-Za-z0-9]+}/view", api.ApiSessionRequired(viewChannel)).Methods("POST")
|
||||
api.BaseRoutes.Channels.Handle("/{channel_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateChannelScheme)).Methods("PUT")
|
||||
|
||||
api.BaseRoutes.ChannelsForTeam.Handle("", api.ApiSessionRequired(getPublicChannelsForTeam)).Methods("GET")
|
||||
api.BaseRoutes.ChannelsForTeam.Handle("/deleted", api.ApiSessionRequired(getDeletedChannelsForTeam)).Methods("GET")
|
||||
@@ -946,3 +947,53 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func updateChannelScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireChannelId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
schemeID := model.SchemeIDFromJson(r.Body)
|
||||
if schemeID == nil || len(*schemeID) != 26 {
|
||||
c.SetInvalidParam("scheme_id")
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.UpdateChannelScheme", "api.channel.update_channel_scheme.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, c.Params.ChannelId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
scheme, err := c.App.GetScheme(*schemeID)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if scheme.Scope != model.SCHEME_SCOPE_CHANNEL {
|
||||
c.Err = model.NewAppError("Api4.UpdateChannelScheme", "api.channel.update_channel_scheme.scheme_scope.error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := c.App.GetChannel(c.Params.ChannelId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
channel.SchemeId = &scheme.Id
|
||||
|
||||
_, err = c.App.UpdateChannelScheme(channel)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
@@ -1463,7 +1463,7 @@ func TestUpdateChannelRoles(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
Client := th.Client
|
||||
|
||||
const CHANNEL_ADMIN = "channel_admin channel_user"
|
||||
const CHANNEL_ADMIN = "channel_user channel_admin"
|
||||
const CHANNEL_MEMBER = "channel_user"
|
||||
|
||||
// User 1 creates a channel, making them channel admin by default.
|
||||
@@ -1914,3 +1914,79 @@ func TestAutocompleteChannels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateChannelScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
team := &model.Team{
|
||||
DisplayName: "Name",
|
||||
Description: "Some description",
|
||||
CompanyName: "Some company name",
|
||||
AllowOpenInvite: false,
|
||||
InviteId: "inviteid0",
|
||||
Name: "z-z-" + model.NewId() + "a",
|
||||
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
team, _ = th.SystemAdminClient.CreateTeam(team)
|
||||
|
||||
channel := &model.Channel{
|
||||
DisplayName: "Name",
|
||||
Name: "z-z-" + model.NewId() + "a",
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: team.Id,
|
||||
}
|
||||
channel, _ = th.SystemAdminClient.CreateChannel(channel)
|
||||
|
||||
channelScheme := &model.Scheme{
|
||||
DisplayName: "DisplayName",
|
||||
Name: model.NewId(),
|
||||
Description: "Some description",
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
channelScheme, _ = th.SystemAdminClient.CreateScheme(channelScheme)
|
||||
teamScheme := &model.Scheme{
|
||||
DisplayName: "DisplayName",
|
||||
Name: model.NewId(),
|
||||
Description: "Some description",
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
teamScheme, _ = th.SystemAdminClient.CreateScheme(teamScheme)
|
||||
|
||||
// Test the setup/base case.
|
||||
_, resp := th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
// Test various invalid channel and scheme id combinations.
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, "x")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme("x", channelScheme.Id)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme("x", "x")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
// Test that permissions are required.
|
||||
_, resp = th.Client.UpdateChannelScheme(channel.Id, channelScheme.Id)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// Test that a license is requried.
|
||||
th.App.SetLicense(nil)
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
|
||||
// Test an invalid scheme scope.
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, teamScheme.Id)
|
||||
fmt.Printf("resp: %+v\n", resp)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
// Test that an unauthenticated user gets rejected.
|
||||
th.SystemAdminClient.Logout()
|
||||
_, resp = th.SystemAdminClient.UpdateChannelScheme(channel.Id, channelScheme.Id)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
}
|
||||
|
||||
@@ -33,12 +33,6 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if emojiInterface := c.App.Emoji; emojiInterface != nil &&
|
||||
!emojiInterface.CanUserCreateEmoji(c.Session.Roles, c.Session.TeamMembers) {
|
||||
c.Err = model.NewAppError("getEmoji", "api.emoji.disabled.app_error", nil, "user_id="+c.Session.UserId, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if len(*c.App.Config().FileSettings.DriverName) == 0 {
|
||||
c.Err = model.NewAppError("createEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
@@ -54,6 +48,28 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Allow any user with MANAGE_EMOJIS permission at Team level to manage emojis at system level
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.Session.UserId)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_EMOJIS) {
|
||||
hasPermission := false
|
||||
for _, membership := range memberships {
|
||||
if c.App.SessionHasPermissionToTeam(c.Session, membership.TeamId, model.PERMISSION_MANAGE_EMOJIS) {
|
||||
hasPermission = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasPermission {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_EMOJIS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m := r.MultipartForm
|
||||
props := m.Value
|
||||
|
||||
@@ -110,11 +126,45 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != emoji.CreatorId && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.Err = model.NewAppError("deleteImage", "api.emoji.delete.permissions.app_error", nil, "user_id="+c.Session.UserId, http.StatusUnauthorized)
|
||||
// Allow any user with MANAGE_EMOJIS permission at Team level to manage emojis at system level
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.Session.UserId)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_EMOJIS) {
|
||||
hasPermission := false
|
||||
for _, membership := range memberships {
|
||||
if c.App.SessionHasPermissionToTeam(c.Session, membership.TeamId, model.PERMISSION_MANAGE_EMOJIS) {
|
||||
hasPermission = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasPermission {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_EMOJIS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if c.Session.UserId != emoji.CreatorId {
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_OTHERS_EMOJIS) {
|
||||
hasPermission := false
|
||||
for _, membership := range memberships {
|
||||
if c.App.SessionHasPermissionToTeam(c.Session, membership.TeamId, model.PERMISSION_MANAGE_OTHERS_EMOJIS) {
|
||||
hasPermission = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_OTHERS_EMOJIS)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = c.App.DeleteEmoji(emoji)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
|
||||
@@ -26,6 +26,11 @@ func TestCreateEmoji(t *testing.T) {
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = false })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
|
||||
emoji := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
@@ -141,6 +146,28 @@ func TestCreateEmoji(t *testing.T) {
|
||||
|
||||
_, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// try to create an emoji without permissions
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
_, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// create an emoji with permissions in one team
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
_, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestGetEmojiList(t *testing.T) {
|
||||
@@ -186,7 +213,7 @@ func TestGetEmojiList(t *testing.T) {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("failed to get emoji with id %v", emoji.Id)
|
||||
t.Fatalf("failed to get emoji with id %v, %v", emoji.Id, len(listEmoji))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +258,11 @@ func TestDeleteEmoji(t *testing.T) {
|
||||
}()
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableCustomEmoji = true })
|
||||
|
||||
defaultRolePermissions := th.SaveDefaultRolePermissions()
|
||||
defer func() {
|
||||
th.RestoreDefaultRolePermissions(defaultRolePermissions)
|
||||
}()
|
||||
|
||||
emoji := &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
@@ -277,14 +309,100 @@ func TestDeleteEmoji(t *testing.T) {
|
||||
_, resp = Client.DeleteEmoji("")
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
//Try to delete other user's custom emoji
|
||||
//Try to delete my custom emoji without permissions
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
_, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
//Try to delete other user's custom emoji without MANAGE_EMOJIS permissions
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
Client.Logout()
|
||||
th.LoginBasic2()
|
||||
ok, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
Client.Logout()
|
||||
th.LoginBasic()
|
||||
|
||||
//Try to delete other user's custom emoji without MANAGE_OTHERS_EMOJIS permissions
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
Client.Logout()
|
||||
th.LoginBasic2()
|
||||
ok, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
Client.Logout()
|
||||
th.LoginBasic()
|
||||
|
||||
//Try to delete other user's custom emoji with permissions
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
Client.Logout()
|
||||
th.LoginBasic2()
|
||||
ok, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
Client.Logout()
|
||||
th.LoginBasic()
|
||||
|
||||
//Try to delete my custom emoji with permissions at team level
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.TEAM_USER_ROLE_ID)
|
||||
_, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckNoError(t, resp)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
//Try to delete other user's custom emoji with permissions at team level
|
||||
emoji = &model.Emoji{
|
||||
CreatorId: th.BasicUser.Id,
|
||||
Name: model.NewId(),
|
||||
}
|
||||
|
||||
newEmoji, resp = Client.CreateEmoji(emoji, utils.CreateTestGif(t, 10, 10), "image.gif")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
th.RemovePermissionFromRole(model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id, model.SYSTEM_USER_ROLE_ID)
|
||||
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_EMOJIS.Id, model.TEAM_USER_ROLE_ID)
|
||||
th.AddPermissionToRole(model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id, model.TEAM_USER_ROLE_ID)
|
||||
|
||||
Client.Logout()
|
||||
th.LoginBasic2()
|
||||
ok, resp = Client.DeleteEmoji(newEmoji.Id)
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestGetEmoji(t *testing.T) {
|
||||
|
||||
41
api4/post.go
41
api4/post.go
@@ -246,11 +246,24 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_DELETE_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS)
|
||||
post, err := c.App.GetSinglePost(c.Params.PostId)
|
||||
if err != nil {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_POST)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId == post.UserId {
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_DELETE_POST) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_POST)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !c.App.SessionHasPermissionToChannel(c.Session, post.ChannelId, model.PERMISSION_DELETE_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_DELETE_OTHERS_POSTS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := c.App.DeletePost(c.Params.PostId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -364,11 +377,19 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
|
||||
originalPost, err := c.App.GetSinglePost(c.Params.PostId)
|
||||
if err != nil {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_POST)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != originalPost.UserId {
|
||||
if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
post.Id = c.Params.PostId
|
||||
|
||||
rpost, err := c.App.UpdatePost(c.App.PostWithProxyRemovedFromImageURLs(post), false)
|
||||
@@ -398,11 +419,19 @@ func patchPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
|
||||
originalPost, err := c.App.GetSinglePost(c.Params.PostId)
|
||||
if err != nil {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_POST)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Session.UserId != originalPost.UserId {
|
||||
if !c.App.SessionHasPermissionToChannelByPost(c.Session, c.Params.PostId, model.PERMISSION_EDIT_OTHERS_POSTS) {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHERS_POSTS)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
patchedPost, err := c.App.PatchPost(c.Params.PostId, c.App.PostPatchWithProxyRemovedFromImageURLs(post))
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
|
||||
@@ -100,6 +100,7 @@ func patchRole(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
model.PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_MANAGE_OAUTH.Id,
|
||||
model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
|
||||
model.PERMISSION_MANAGE_EMOJIS.Id,
|
||||
}
|
||||
|
||||
changedPermissions := model.PermissionsChangedByPatch(oldRole, patch)
|
||||
|
||||
211
api4/scheme.go
Обычный файл
211
api4/scheme.go
Обычный файл
@@ -0,0 +1,211 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (api *API) InitScheme() {
|
||||
api.BaseRoutes.Schemes.Handle("", api.ApiSessionRequired(getSchemes)).Methods("GET")
|
||||
api.BaseRoutes.Schemes.Handle("", api.ApiSessionRequired(createScheme)).Methods("POST")
|
||||
api.BaseRoutes.Schemes.Handle("/{scheme_id:[A-Za-z0-9]+}", api.ApiSessionRequired(deleteScheme)).Methods("DELETE")
|
||||
api.BaseRoutes.Schemes.Handle("/{scheme_id:[A-Za-z0-9]+}", api.ApiSessionRequiredTrustRequester(getScheme)).Methods("GET")
|
||||
api.BaseRoutes.Schemes.Handle("/{scheme_id:[A-Za-z0-9]+}/patch", api.ApiSessionRequired(patchScheme)).Methods("PUT")
|
||||
api.BaseRoutes.Schemes.Handle("/{scheme_id:[A-Za-z0-9]+}/teams", api.ApiSessionRequiredTrustRequester(getTeamsForScheme)).Methods("GET")
|
||||
api.BaseRoutes.Schemes.Handle("/{scheme_id:[A-Za-z0-9]+}/channels", api.ApiSessionRequiredTrustRequester(getChannelsForScheme)).Methods("GET")
|
||||
}
|
||||
|
||||
func createScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
scheme := model.SchemeFromJson(r.Body)
|
||||
if scheme == nil {
|
||||
c.SetInvalidParam("scheme")
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
|
||||
c.Err = model.NewAppError("Api4.CreateScheme", "api.scheme.create_scheme.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
var err *model.AppError
|
||||
if scheme, err = c.App.CreateScheme(scheme); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(scheme.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireSchemeId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
if scheme, err := c.App.GetScheme(c.Params.SchemeId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(scheme.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func getSchemes(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
scope := c.Params.Scope
|
||||
if scope != "" && scope != model.SCHEME_SCOPE_TEAM && scope != model.SCHEME_SCOPE_CHANNEL {
|
||||
c.SetInvalidParam("scope")
|
||||
return
|
||||
}
|
||||
|
||||
if schemes, err := c.App.GetSchemesPage(c.Params.Scope, c.Params.Page, c.Params.PerPage); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.SchemesToJson(schemes)))
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamsForScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireSchemeId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
scheme, err := c.App.GetScheme(c.Params.SchemeId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if scheme.Scope != model.SCHEME_SCOPE_TEAM {
|
||||
c.Err = model.NewAppError("Api4.GetTeamsForScheme", "api.scheme.get_teams_for_scheme.scope.error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if teams, err := c.App.GetTeamsForSchemePage(scheme, c.Params.Page, c.Params.PerPage); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(model.TeamListToJson(teams)))
|
||||
}
|
||||
}
|
||||
|
||||
func getChannelsForScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireSchemeId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
scheme, err := c.App.GetScheme(c.Params.SchemeId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if scheme.Scope != model.SCHEME_SCOPE_CHANNEL {
|
||||
c.Err = model.NewAppError("Api4.GetChannelsForScheme", "api.scheme.get_channels_for_scheme.scope.error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if channels, err := c.App.GetChannelsForSchemePage(scheme, c.Params.Page, c.Params.PerPage); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(channels.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func patchScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireSchemeId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
patch := model.SchemePatchFromJson(r.Body)
|
||||
if patch == nil {
|
||||
c.SetInvalidParam("scheme")
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
|
||||
c.Err = model.NewAppError("Api4.PatchScheme", "api.scheme.patch_scheme.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
scheme, err := c.App.GetScheme(c.Params.SchemeId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
if scheme, err = c.App.PatchScheme(scheme, patch); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("")
|
||||
w.Write([]byte(scheme.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func deleteScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireSchemeId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil || !*c.App.License().Features.CustomPermissionsSchemes {
|
||||
c.Err = model.NewAppError("Api4.DeleteScheme", "api.scheme.delete_scheme.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.App.DeleteScheme(c.Params.SchemeId); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
737
api4/scheme_test.go
Обычный файл
737
api4/scheme_test.go
Обычный файл
@@ -0,0 +1,737 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestCreateScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
// Basic test of creating a team scheme.
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
assert.Equal(t, s1.DisplayName, scheme1.DisplayName)
|
||||
assert.Equal(t, s1.Name, scheme1.Name)
|
||||
assert.Equal(t, s1.Description, scheme1.Description)
|
||||
assert.NotZero(t, s1.CreateAt)
|
||||
assert.Equal(t, s1.CreateAt, s1.UpdateAt)
|
||||
assert.Zero(t, s1.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, scheme1.Scope)
|
||||
assert.NotZero(t, len(s1.DefaultTeamAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultTeamUserRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelUserRole))
|
||||
|
||||
// Check the default roles have been created.
|
||||
_, roleRes1 := th.SystemAdminClient.GetRole(s1.DefaultTeamAdminRole)
|
||||
CheckNoError(t, roleRes1)
|
||||
_, roleRes2 := th.SystemAdminClient.GetRole(s1.DefaultTeamUserRole)
|
||||
CheckNoError(t, roleRes2)
|
||||
_, roleRes3 := th.SystemAdminClient.GetRole(s1.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes3)
|
||||
_, roleRes4 := th.SystemAdminClient.GetRole(s1.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes4)
|
||||
|
||||
// Basic Test of a Channel scheme.
|
||||
scheme2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s2, r2 := th.SystemAdminClient.CreateScheme(scheme2)
|
||||
CheckNoError(t, r2)
|
||||
|
||||
assert.Equal(t, s2.DisplayName, scheme2.DisplayName)
|
||||
assert.Equal(t, s2.Name, scheme2.Name)
|
||||
assert.Equal(t, s2.Description, scheme2.Description)
|
||||
assert.NotZero(t, s2.CreateAt)
|
||||
assert.Equal(t, s2.CreateAt, s2.UpdateAt)
|
||||
assert.Zero(t, s2.DeleteAt)
|
||||
assert.Equal(t, s2.Scope, scheme2.Scope)
|
||||
assert.Zero(t, len(s2.DefaultTeamAdminRole))
|
||||
assert.Zero(t, len(s2.DefaultTeamUserRole))
|
||||
assert.NotZero(t, len(s2.DefaultChannelAdminRole))
|
||||
assert.NotZero(t, len(s2.DefaultChannelUserRole))
|
||||
|
||||
// Check the default roles have been created.
|
||||
_, roleRes5 := th.SystemAdminClient.GetRole(s2.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes5)
|
||||
_, roleRes6 := th.SystemAdminClient.GetRole(s2.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes6)
|
||||
|
||||
// Try and create a scheme with an invalid scope.
|
||||
scheme3 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.NewId(),
|
||||
}
|
||||
|
||||
_, r3 := th.SystemAdminClient.CreateScheme(scheme3)
|
||||
CheckBadRequestStatus(t, r3)
|
||||
|
||||
// Try and create a scheme with an invalid display name.
|
||||
scheme4 := &model.Scheme{
|
||||
DisplayName: strings.Repeat(model.NewId(), 100),
|
||||
Name: "Name",
|
||||
Description: model.NewId(),
|
||||
Scope: model.NewId(),
|
||||
}
|
||||
_, r4 := th.SystemAdminClient.CreateScheme(scheme4)
|
||||
CheckBadRequestStatus(t, r4)
|
||||
|
||||
// Try and create a scheme with an invalid name.
|
||||
scheme8 := &model.Scheme{
|
||||
DisplayName: "DisplayName",
|
||||
Name: strings.Repeat(model.NewId(), 100),
|
||||
Description: model.NewId(),
|
||||
Scope: model.NewId(),
|
||||
}
|
||||
_, r8 := th.SystemAdminClient.CreateScheme(scheme8)
|
||||
CheckBadRequestStatus(t, r8)
|
||||
|
||||
// Try and create a scheme without the appropriate permissions.
|
||||
scheme5 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
_, r5 := th.Client.CreateScheme(scheme5)
|
||||
CheckForbiddenStatus(t, r5)
|
||||
|
||||
// Try and create a scheme without a license.
|
||||
th.App.SetLicense(nil)
|
||||
scheme6 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
_, r6 := th.SystemAdminClient.CreateScheme(scheme6)
|
||||
CheckNotImplementedStatus(t, r6)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
th.LoginSystemAdmin()
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
scheme7 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
_, r7 := th.SystemAdminClient.CreateScheme(scheme7)
|
||||
CheckNotImplementedStatus(t, r7)
|
||||
}
|
||||
|
||||
func TestGetScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
// Basic test of creating a team scheme.
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
assert.Equal(t, s1.DisplayName, scheme1.DisplayName)
|
||||
assert.Equal(t, s1.Name, scheme1.Name)
|
||||
assert.Equal(t, s1.Description, scheme1.Description)
|
||||
assert.NotZero(t, s1.CreateAt)
|
||||
assert.Equal(t, s1.CreateAt, s1.UpdateAt)
|
||||
assert.Zero(t, s1.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, scheme1.Scope)
|
||||
assert.NotZero(t, len(s1.DefaultTeamAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultTeamUserRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelUserRole))
|
||||
|
||||
s2, r2 := th.SystemAdminClient.GetScheme(s1.Id)
|
||||
CheckNoError(t, r2)
|
||||
|
||||
assert.Equal(t, s1, s2)
|
||||
|
||||
_, r3 := th.SystemAdminClient.GetScheme(model.NewId())
|
||||
CheckNotFoundStatus(t, r3)
|
||||
|
||||
_, r4 := th.SystemAdminClient.GetScheme("12345")
|
||||
CheckBadRequestStatus(t, r4)
|
||||
|
||||
th.SystemAdminClient.Logout()
|
||||
_, r5 := th.SystemAdminClient.GetScheme(s1.Id)
|
||||
CheckUnauthorizedStatus(t, r5)
|
||||
|
||||
th.SystemAdminClient.Login(th.SystemAdminUser.Username, th.SystemAdminUser.Password)
|
||||
th.App.SetLicense(nil)
|
||||
_, r6 := th.SystemAdminClient.GetScheme(s1.Id)
|
||||
CheckNoError(t, r6)
|
||||
|
||||
_, r7 := th.Client.GetScheme(s1.Id)
|
||||
CheckForbiddenStatus(t, r7)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
_, r8 := th.SystemAdminClient.GetScheme(s1.Id)
|
||||
CheckNotImplementedStatus(t, r8)
|
||||
}
|
||||
|
||||
func TestGetSchemes(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
scheme2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
_, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
_, r2 := th.SystemAdminClient.CreateScheme(scheme2)
|
||||
CheckNoError(t, r2)
|
||||
|
||||
l3, r3 := th.SystemAdminClient.GetSchemes("", 0, 100)
|
||||
CheckNoError(t, r3)
|
||||
|
||||
assert.NotZero(t, len(l3))
|
||||
|
||||
l4, r4 := th.SystemAdminClient.GetSchemes("team", 0, 100)
|
||||
CheckNoError(t, r4)
|
||||
|
||||
for _, s := range l4 {
|
||||
assert.Equal(t, "team", s.Scope)
|
||||
}
|
||||
|
||||
l5, r5 := th.SystemAdminClient.GetSchemes("channel", 0, 100)
|
||||
CheckNoError(t, r5)
|
||||
|
||||
for _, s := range l5 {
|
||||
assert.Equal(t, "channel", s.Scope)
|
||||
}
|
||||
|
||||
_, r6 := th.SystemAdminClient.GetSchemes("asdf", 0, 100)
|
||||
CheckBadRequestStatus(t, r6)
|
||||
|
||||
th.Client.Logout()
|
||||
_, r7 := th.Client.GetSchemes("", 0, 100)
|
||||
CheckUnauthorizedStatus(t, r7)
|
||||
|
||||
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
||||
_, r8 := th.Client.GetSchemes("", 0, 100)
|
||||
CheckForbiddenStatus(t, r8)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
_, r9 := th.SystemAdminClient.GetSchemes("", 0, 100)
|
||||
CheckNotImplementedStatus(t, r9)
|
||||
}
|
||||
|
||||
func TestGetTeamsForScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
scheme1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
team1 := &model.Team{
|
||||
Name: GenerateTestUsername(),
|
||||
DisplayName: "A Test Team",
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
result1 := <-th.App.Srv.Store.Team().Save(team1)
|
||||
assert.Nil(t, result1.Err)
|
||||
team1 = result1.Data.(*model.Team)
|
||||
|
||||
l2, r2 := th.SystemAdminClient.GetTeamsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r2)
|
||||
assert.Zero(t, len(l2))
|
||||
|
||||
team1.SchemeId = &scheme1.Id
|
||||
result2 := <-th.App.Srv.Store.Team().Update(team1)
|
||||
assert.Nil(t, result2.Err)
|
||||
team1 = result2.Data.(*model.Team)
|
||||
|
||||
l3, r3 := th.SystemAdminClient.GetTeamsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r3)
|
||||
assert.Len(t, l3, 1)
|
||||
assert.Equal(t, team1.Id, l3[0].Id)
|
||||
|
||||
team2 := &model.Team{
|
||||
Name: GenerateTestUsername(),
|
||||
DisplayName: "B Test Team",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &scheme1.Id,
|
||||
}
|
||||
result3 := <-th.App.Srv.Store.Team().Save(team2)
|
||||
assert.Nil(t, result3.Err)
|
||||
team2 = result3.Data.(*model.Team)
|
||||
|
||||
l4, r4 := th.SystemAdminClient.GetTeamsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r4)
|
||||
assert.Len(t, l4, 2)
|
||||
assert.Equal(t, team1.Id, l4[0].Id)
|
||||
assert.Equal(t, team2.Id, l4[1].Id)
|
||||
|
||||
l5, r5 := th.SystemAdminClient.GetTeamsForScheme(scheme1.Id, 1, 1)
|
||||
CheckNoError(t, r5)
|
||||
assert.Len(t, l5, 1)
|
||||
assert.Equal(t, team2.Id, l5[0].Id)
|
||||
|
||||
// Check various error cases.
|
||||
_, ri1 := th.SystemAdminClient.GetTeamsForScheme(model.NewId(), 0, 100)
|
||||
CheckNotFoundStatus(t, ri1)
|
||||
|
||||
_, ri2 := th.SystemAdminClient.GetTeamsForScheme("", 0, 100)
|
||||
CheckBadRequestStatus(t, ri2)
|
||||
|
||||
th.Client.Logout()
|
||||
_, ri3 := th.Client.GetTeamsForScheme(model.NewId(), 0, 100)
|
||||
CheckUnauthorizedStatus(t, ri3)
|
||||
|
||||
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
||||
_, ri4 := th.Client.GetTeamsForScheme(model.NewId(), 0, 100)
|
||||
CheckForbiddenStatus(t, ri4)
|
||||
|
||||
scheme2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
scheme2, rs2 := th.SystemAdminClient.CreateScheme(scheme2)
|
||||
CheckNoError(t, rs2)
|
||||
|
||||
_, ri5 := th.SystemAdminClient.GetTeamsForScheme(scheme2.Id, 0, 100)
|
||||
CheckBadRequestStatus(t, ri5)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
_, ri6 := th.SystemAdminClient.GetTeamsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNotImplementedStatus(t, ri6)
|
||||
}
|
||||
|
||||
func TestGetChannelsForScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
scheme1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
channel1 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "A Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}
|
||||
|
||||
result1 := <-th.App.Srv.Store.Channel().Save(channel1, 1000000)
|
||||
assert.Nil(t, result1.Err)
|
||||
channel1 = result1.Data.(*model.Channel)
|
||||
|
||||
l2, r2 := th.SystemAdminClient.GetChannelsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r2)
|
||||
assert.Zero(t, len(l2))
|
||||
|
||||
channel1.SchemeId = &scheme1.Id
|
||||
result2 := <-th.App.Srv.Store.Channel().Update(channel1)
|
||||
assert.Nil(t, result2.Err)
|
||||
channel1 = result2.Data.(*model.Channel)
|
||||
|
||||
l3, r3 := th.SystemAdminClient.GetChannelsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r3)
|
||||
assert.Len(t, l3, 1)
|
||||
assert.Equal(t, channel1.Id, l3[0].Id)
|
||||
|
||||
channel2 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "B Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &scheme1.Id,
|
||||
}
|
||||
result3 := <-th.App.Srv.Store.Channel().Save(channel2, 1000000)
|
||||
assert.Nil(t, result3.Err)
|
||||
channel2 = result3.Data.(*model.Channel)
|
||||
|
||||
l4, r4 := th.SystemAdminClient.GetChannelsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNoError(t, r4)
|
||||
assert.Len(t, l4, 2)
|
||||
assert.Equal(t, channel1.Id, l4[0].Id)
|
||||
assert.Equal(t, channel2.Id, l4[1].Id)
|
||||
|
||||
l5, r5 := th.SystemAdminClient.GetChannelsForScheme(scheme1.Id, 1, 1)
|
||||
CheckNoError(t, r5)
|
||||
assert.Len(t, l5, 1)
|
||||
assert.Equal(t, channel2.Id, l5[0].Id)
|
||||
|
||||
// Check various error cases.
|
||||
_, ri1 := th.SystemAdminClient.GetChannelsForScheme(model.NewId(), 0, 100)
|
||||
CheckNotFoundStatus(t, ri1)
|
||||
|
||||
_, ri2 := th.SystemAdminClient.GetChannelsForScheme("", 0, 100)
|
||||
CheckBadRequestStatus(t, ri2)
|
||||
|
||||
th.Client.Logout()
|
||||
_, ri3 := th.Client.GetChannelsForScheme(model.NewId(), 0, 100)
|
||||
CheckUnauthorizedStatus(t, ri3)
|
||||
|
||||
th.Client.Login(th.BasicUser.Username, th.BasicUser.Password)
|
||||
_, ri4 := th.Client.GetChannelsForScheme(model.NewId(), 0, 100)
|
||||
CheckForbiddenStatus(t, ri4)
|
||||
|
||||
scheme2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
scheme2, rs2 := th.SystemAdminClient.CreateScheme(scheme2)
|
||||
CheckNoError(t, rs2)
|
||||
|
||||
_, ri5 := th.SystemAdminClient.GetChannelsForScheme(scheme2.Id, 0, 100)
|
||||
CheckBadRequestStatus(t, ri5)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
_, ri6 := th.SystemAdminClient.GetChannelsForScheme(scheme1.Id, 0, 100)
|
||||
CheckNotImplementedStatus(t, ri6)
|
||||
}
|
||||
|
||||
func TestPatchScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
// Basic test of creating a team scheme.
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
assert.Equal(t, s1.DisplayName, scheme1.DisplayName)
|
||||
assert.Equal(t, s1.Name, scheme1.Name)
|
||||
assert.Equal(t, s1.Description, scheme1.Description)
|
||||
assert.NotZero(t, s1.CreateAt)
|
||||
assert.Equal(t, s1.CreateAt, s1.UpdateAt)
|
||||
assert.Zero(t, s1.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, scheme1.Scope)
|
||||
assert.NotZero(t, len(s1.DefaultTeamAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultTeamUserRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelAdminRole))
|
||||
assert.NotZero(t, len(s1.DefaultChannelUserRole))
|
||||
|
||||
s2, r2 := th.SystemAdminClient.GetScheme(s1.Id)
|
||||
CheckNoError(t, r2)
|
||||
|
||||
assert.Equal(t, s1, s2)
|
||||
|
||||
// Test with a valid patch.
|
||||
schemePatch := &model.SchemePatch{
|
||||
DisplayName: new(string),
|
||||
Name: new(string),
|
||||
Description: new(string),
|
||||
}
|
||||
*schemePatch.DisplayName = model.NewId()
|
||||
*schemePatch.Name = model.NewId()
|
||||
*schemePatch.Description = model.NewId()
|
||||
|
||||
s3, r3 := th.SystemAdminClient.PatchScheme(s2.Id, schemePatch)
|
||||
CheckNoError(t, r3)
|
||||
assert.Equal(t, s3.Id, s2.Id)
|
||||
assert.Equal(t, s3.DisplayName, *schemePatch.DisplayName)
|
||||
assert.Equal(t, s3.Name, *schemePatch.Name)
|
||||
assert.Equal(t, s3.Description, *schemePatch.Description)
|
||||
|
||||
s4, r4 := th.SystemAdminClient.GetScheme(s3.Id)
|
||||
CheckNoError(t, r4)
|
||||
assert.Equal(t, s3, s4)
|
||||
|
||||
// Test with a partial patch.
|
||||
*schemePatch.Name = model.NewId()
|
||||
*schemePatch.DisplayName = model.NewId()
|
||||
schemePatch.Description = nil
|
||||
|
||||
s5, r5 := th.SystemAdminClient.PatchScheme(s4.Id, schemePatch)
|
||||
CheckNoError(t, r5)
|
||||
assert.Equal(t, s5.Id, s4.Id)
|
||||
assert.Equal(t, s5.DisplayName, *schemePatch.DisplayName)
|
||||
assert.Equal(t, s5.Name, *schemePatch.Name)
|
||||
assert.Equal(t, s5.Description, s4.Description)
|
||||
|
||||
s6, r6 := th.SystemAdminClient.GetScheme(s5.Id)
|
||||
CheckNoError(t, r6)
|
||||
assert.Equal(t, s5, s6)
|
||||
|
||||
// Test with invalid patch.
|
||||
*schemePatch.Name = strings.Repeat(model.NewId(), 20)
|
||||
_, r7 := th.SystemAdminClient.PatchScheme(s6.Id, schemePatch)
|
||||
CheckBadRequestStatus(t, r7)
|
||||
|
||||
// Test with unknown ID.
|
||||
*schemePatch.Name = model.NewId()
|
||||
_, r8 := th.SystemAdminClient.PatchScheme(model.NewId(), schemePatch)
|
||||
CheckNotFoundStatus(t, r8)
|
||||
|
||||
// Test with invalid ID.
|
||||
_, r9 := th.SystemAdminClient.PatchScheme("12345", schemePatch)
|
||||
CheckBadRequestStatus(t, r9)
|
||||
|
||||
// Test without required permissions.
|
||||
_, r10 := th.Client.PatchScheme(s6.Id, schemePatch)
|
||||
CheckForbiddenStatus(t, r10)
|
||||
|
||||
// Test without license.
|
||||
th.App.SetLicense(nil)
|
||||
_, r11 := th.SystemAdminClient.PatchScheme(s6.Id, schemePatch)
|
||||
CheckNotImplementedStatus(t, r11)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
th.LoginSystemAdmin()
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
_, r12 := th.SystemAdminClient.PatchScheme(s6.Id, schemePatch)
|
||||
CheckNotImplementedStatus(t, r12)
|
||||
}
|
||||
|
||||
func TestDeleteScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("ValidTeamScheme", func(t *testing.T) {
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
// Create a team scheme.
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
// Retrieve the roles and check they are not deleted.
|
||||
role1, roleRes1 := th.SystemAdminClient.GetRole(s1.DefaultTeamAdminRole)
|
||||
CheckNoError(t, roleRes1)
|
||||
role2, roleRes2 := th.SystemAdminClient.GetRole(s1.DefaultTeamUserRole)
|
||||
CheckNoError(t, roleRes2)
|
||||
role3, roleRes3 := th.SystemAdminClient.GetRole(s1.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes3)
|
||||
role4, roleRes4 := th.SystemAdminClient.GetRole(s1.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes4)
|
||||
|
||||
assert.Zero(t, role1.DeleteAt)
|
||||
assert.Zero(t, role2.DeleteAt)
|
||||
assert.Zero(t, role3.DeleteAt)
|
||||
assert.Zero(t, role4.DeleteAt)
|
||||
|
||||
// Make sure this scheme is in use by a team.
|
||||
res := <-th.App.Srv.Store.Team().Save(&model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
})
|
||||
assert.Nil(t, res.Err)
|
||||
team := res.Data.(*model.Team)
|
||||
|
||||
// Delete the Scheme.
|
||||
_, r3 := th.SystemAdminClient.DeleteScheme(s1.Id)
|
||||
CheckNoError(t, r3)
|
||||
|
||||
// Check the roles were deleted.
|
||||
role1, roleRes1 = th.SystemAdminClient.GetRole(s1.DefaultTeamAdminRole)
|
||||
CheckNoError(t, roleRes1)
|
||||
role2, roleRes2 = th.SystemAdminClient.GetRole(s1.DefaultTeamUserRole)
|
||||
CheckNoError(t, roleRes2)
|
||||
role3, roleRes3 = th.SystemAdminClient.GetRole(s1.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes3)
|
||||
role4, roleRes4 = th.SystemAdminClient.GetRole(s1.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes4)
|
||||
|
||||
assert.NotZero(t, role1.DeleteAt)
|
||||
assert.NotZero(t, role2.DeleteAt)
|
||||
assert.NotZero(t, role3.DeleteAt)
|
||||
assert.NotZero(t, role4.DeleteAt)
|
||||
|
||||
// Check the team now uses the default scheme
|
||||
c2, resp := th.SystemAdminClient.GetTeam(team.Id, "")
|
||||
CheckNoError(t, resp)
|
||||
assert.Equal(t, "", *c2.SchemeId)
|
||||
})
|
||||
|
||||
t.Run("ValidChannelScheme", func(t *testing.T) {
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
// Create a channel scheme.
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
// Retrieve the roles and check they are not deleted.
|
||||
role3, roleRes3 := th.SystemAdminClient.GetRole(s1.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes3)
|
||||
role4, roleRes4 := th.SystemAdminClient.GetRole(s1.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes4)
|
||||
|
||||
assert.Zero(t, role3.DeleteAt)
|
||||
assert.Zero(t, role4.DeleteAt)
|
||||
|
||||
// Make sure this scheme is in use by a team.
|
||||
res := <-th.App.Srv.Store.Channel().Save(&model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}, -1)
|
||||
assert.Nil(t, res.Err)
|
||||
channel := res.Data.(*model.Channel)
|
||||
|
||||
// Delete the Scheme.
|
||||
_, r3 := th.SystemAdminClient.DeleteScheme(s1.Id)
|
||||
CheckNoError(t, r3)
|
||||
|
||||
// Check the roles were deleted.
|
||||
role3, roleRes3 = th.SystemAdminClient.GetRole(s1.DefaultChannelAdminRole)
|
||||
CheckNoError(t, roleRes3)
|
||||
role4, roleRes4 = th.SystemAdminClient.GetRole(s1.DefaultChannelUserRole)
|
||||
CheckNoError(t, roleRes4)
|
||||
|
||||
assert.NotZero(t, role3.DeleteAt)
|
||||
assert.NotZero(t, role4.DeleteAt)
|
||||
|
||||
// Check the channel now uses the default scheme
|
||||
c2, resp := th.SystemAdminClient.GetChannelByName(channel.Name, channel.TeamId, "")
|
||||
CheckNoError(t, resp)
|
||||
assert.Equal(t, "", *c2.SchemeId)
|
||||
})
|
||||
|
||||
t.Run("FailureCases", func(t *testing.T) {
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
scheme1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s1, r1 := th.SystemAdminClient.CreateScheme(scheme1)
|
||||
CheckNoError(t, r1)
|
||||
|
||||
// Test with unknown ID.
|
||||
_, r2 := th.SystemAdminClient.DeleteScheme(model.NewId())
|
||||
CheckNotFoundStatus(t, r2)
|
||||
|
||||
// Test with invalid ID.
|
||||
_, r3 := th.SystemAdminClient.DeleteScheme("12345")
|
||||
CheckBadRequestStatus(t, r3)
|
||||
|
||||
// Test without required permissions.
|
||||
_, r4 := th.Client.DeleteScheme(s1.Id)
|
||||
CheckForbiddenStatus(t, r4)
|
||||
|
||||
// Test without license.
|
||||
th.App.SetLicense(nil)
|
||||
_, r5 := th.SystemAdminClient.DeleteScheme(s1.Id)
|
||||
CheckNotImplementedStatus(t, r5)
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(false)
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense("custom_permissions_schemes"))
|
||||
|
||||
_, r6 := th.SystemAdminClient.DeleteScheme(s1.Id)
|
||||
CheckNotImplementedStatus(t, r6)
|
||||
})
|
||||
}
|
||||
53
api4/team.go
53
api4/team.go
@@ -20,6 +20,7 @@ const (
|
||||
func (api *API) InitTeam() {
|
||||
api.BaseRoutes.Teams.Handle("", api.ApiSessionRequired(createTeam)).Methods("POST")
|
||||
api.BaseRoutes.Teams.Handle("", api.ApiSessionRequired(getAllTeams)).Methods("GET")
|
||||
api.BaseRoutes.Teams.Handle("/{team_id:[A-Za-z0-9]+}/scheme", api.ApiSessionRequired(updateTeamScheme)).Methods("PUT")
|
||||
api.BaseRoutes.Teams.Handle("/search", api.ApiSessionRequired(searchTeams)).Methods("POST")
|
||||
api.BaseRoutes.TeamsForUser.Handle("", api.ApiSessionRequired(getTeamsForUser)).Methods("GET")
|
||||
api.BaseRoutes.TeamsForUser.Handle("/unread", api.ApiSessionRequired(getTeamsUnreadForUser)).Methods("GET")
|
||||
@@ -833,3 +834,55 @@ func removeTeamIcon(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.LogAudit("")
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func updateTeamScheme(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
schemeID := model.SchemeIDFromJson(r.Body)
|
||||
if schemeID == nil || (len(*schemeID) != 26 && *schemeID != "") {
|
||||
c.SetInvalidParam("scheme_id")
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.License() == nil {
|
||||
c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.license.error", nil, "", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToTeam(c.Session, c.Params.TeamId, model.PERMISSION_MANAGE_SYSTEM) {
|
||||
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||
return
|
||||
}
|
||||
|
||||
if *schemeID != "" {
|
||||
scheme, err := c.App.GetScheme(*schemeID)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
if scheme.Scope != model.SCHEME_SCOPE_TEAM {
|
||||
c.Err = model.NewAppError("Api4.UpdateTeamScheme", "api.team.update_team_scheme.scheme_scope.error", nil, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
team, err := c.App.GetTeam(c.Params.TeamId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
team.SchemeId = schemeID
|
||||
|
||||
_, err = c.App.UpdateTeamScheme(team)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
@@ -1675,7 +1675,7 @@ func TestUpdateTeamMemberRoles(t *testing.T) {
|
||||
|
||||
// user 1 (team admin) tries to demote system admin (not member of a team)
|
||||
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.SystemAdminUser.Id, TEAM_MEMBER)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
// user 1 (team admin) demotes system admin (member of a team)
|
||||
th.LinkUserToTeam(th.SystemAdminUser, th.BasicTeam)
|
||||
@@ -1701,7 +1701,7 @@ func TestUpdateTeamMemberRoles(t *testing.T) {
|
||||
|
||||
// user 1 (team admin) tries to promote a random user
|
||||
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, model.NewId(), TEAM_ADMIN)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
CheckNotFoundStatus(t, resp)
|
||||
|
||||
// user 1 (team admin) tries to promote invalid team permission
|
||||
_, resp = Client.UpdateTeamMemberRoles(th.BasicTeam.Id, th.BasicUser.Id, "junk")
|
||||
@@ -2055,3 +2055,75 @@ func TestRemoveTeamIcon(t *testing.T) {
|
||||
_, resp = Client.RemoveTeamIcon(team.Id)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
}
|
||||
|
||||
func TestUpdateTeamScheme(t *testing.T) {
|
||||
th := Setup().InitBasic().InitSystemAdmin()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
|
||||
th.App.SetPhase2PermissionsMigrationStatus(true)
|
||||
|
||||
team := &model.Team{
|
||||
DisplayName: "Name",
|
||||
Description: "Some description",
|
||||
CompanyName: "Some company name",
|
||||
AllowOpenInvite: false,
|
||||
InviteId: "inviteid0",
|
||||
Name: "z-z-" + model.NewId() + "a",
|
||||
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
team, _ = th.SystemAdminClient.CreateTeam(team)
|
||||
|
||||
teamScheme := &model.Scheme{
|
||||
DisplayName: "DisplayName",
|
||||
Name: model.NewId(),
|
||||
Description: "Some description",
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
teamScheme, _ = th.SystemAdminClient.CreateScheme(teamScheme)
|
||||
channelScheme := &model.Scheme{
|
||||
DisplayName: "DisplayName",
|
||||
Name: model.NewId(),
|
||||
Description: "Some description",
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
channelScheme, _ = th.SystemAdminClient.CreateScheme(channelScheme)
|
||||
|
||||
// Test the setup/base case.
|
||||
_, resp := th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
// Test the return to default scheme
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, "")
|
||||
CheckNoError(t, resp)
|
||||
|
||||
// Test various invalid team and scheme id combinations.
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, "x")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme("x", teamScheme.Id)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme("x", "x")
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
// Test that permissions are required.
|
||||
_, resp = th.Client.UpdateTeamScheme(team.Id, teamScheme.Id)
|
||||
CheckForbiddenStatus(t, resp)
|
||||
|
||||
// Test that a license is requried.
|
||||
th.App.SetLicense(nil)
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id)
|
||||
CheckNotImplementedStatus(t, resp)
|
||||
th.App.SetLicense(model.NewTestLicense(""))
|
||||
|
||||
// Test an invalid scheme scope.
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, channelScheme.Id)
|
||||
fmt.Printf("resp: %+v\n", resp)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
|
||||
// Test that an unauthenticated user gets rejected.
|
||||
th.SystemAdminClient.Logout()
|
||||
_, resp = th.SystemAdminClient.UpdateTeamScheme(team.Id, teamScheme.Id)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
}
|
||||
|
||||
106
app/app.go
106
app/app.go
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
|
||||
"github.com/mattermost/mattermost-server/jobs"
|
||||
tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin/pluginenv"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
)
|
||||
|
||||
const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete"
|
||||
const EMOJIS_PERMISSIONS_MIGRATION_KEY = "EmojisPermissionsMigrationComplete"
|
||||
|
||||
type App struct {
|
||||
goroutineCount int32
|
||||
@@ -56,7 +58,6 @@ type App struct {
|
||||
Compliance einterfaces.ComplianceInterface
|
||||
DataRetention einterfaces.DataRetentionInterface
|
||||
Elasticsearch einterfaces.ElasticsearchInterface
|
||||
Emoji einterfaces.EmojiInterface
|
||||
Ldap einterfaces.LdapInterface
|
||||
MessageExport einterfaces.MessageExportInterface
|
||||
Metrics einterfaces.MetricsInterface
|
||||
@@ -93,6 +94,8 @@ type App struct {
|
||||
clientConfig map[string]string
|
||||
clientConfigHash string
|
||||
diagnosticId string
|
||||
|
||||
phase2PermissionsMigrationComplete bool
|
||||
}
|
||||
|
||||
var appCount = 0
|
||||
@@ -285,12 +288,6 @@ func RegisterElasticsearchInterface(f func(*App) einterfaces.ElasticsearchInterf
|
||||
elasticsearchInterface = f
|
||||
}
|
||||
|
||||
var emojiInterface func(*App) einterfaces.EmojiInterface
|
||||
|
||||
func RegisterEmojiInterface(f func(*App) einterfaces.EmojiInterface) {
|
||||
emojiInterface = f
|
||||
}
|
||||
|
||||
var jobsDataRetentionJobInterface func(*App) ejobs.DataRetentionJobInterface
|
||||
|
||||
func RegisterJobsDataRetentionJobInterface(f func(*App) ejobs.DataRetentionJobInterface) {
|
||||
@@ -321,6 +318,12 @@ func RegisterJobsLdapSyncInterface(f func(*App) ejobs.LdapSyncInterface) {
|
||||
jobsLdapSyncInterface = f
|
||||
}
|
||||
|
||||
var jobsMigrationsInterface func(*App) tjobs.MigrationsJobInterface
|
||||
|
||||
func RegisterJobsMigrationsJobInterface(f func(*App) tjobs.MigrationsJobInterface) {
|
||||
jobsMigrationsInterface = f
|
||||
}
|
||||
|
||||
var ldapInterface func(*App) einterfaces.LdapInterface
|
||||
|
||||
func RegisterLdapInterface(f func(*App) einterfaces.LdapInterface) {
|
||||
@@ -367,9 +370,6 @@ func (a *App) initEnterprise() {
|
||||
if elasticsearchInterface != nil {
|
||||
a.Elasticsearch = elasticsearchInterface(a)
|
||||
}
|
||||
if emojiInterface != nil {
|
||||
a.Emoji = emojiInterface(a)
|
||||
}
|
||||
if ldapInterface != nil {
|
||||
a.Ldap = ldapInterface(a)
|
||||
a.AddConfigListener(func(_, cfg *model.Config) {
|
||||
@@ -415,6 +415,9 @@ func (a *App) initJobs() {
|
||||
if jobsLdapSyncInterface != nil {
|
||||
a.Jobs.LdapSync = jobsLdapSyncInterface(a)
|
||||
}
|
||||
if jobsMigrationsInterface != nil {
|
||||
a.Jobs.Migrations = jobsMigrationsInterface(a)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DiagnosticId() string {
|
||||
@@ -580,3 +583,86 @@ func (a *App) DoAdvancedPermissionsMigration() {
|
||||
mlog.Critical(fmt.Sprint(result.Err))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) SetPhase2PermissionsMigrationStatus(isComplete bool) error {
|
||||
if !isComplete {
|
||||
res := <-a.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
|
||||
if res.Err != nil {
|
||||
return res.Err
|
||||
}
|
||||
}
|
||||
a.phase2PermissionsMigrationComplete = isComplete
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) DoEmojisPermissionsMigration() {
|
||||
// If the migration is already marked as completed, don't do it again.
|
||||
if result := <-a.Srv.Store.System().GetByName(EMOJIS_PERMISSIONS_MIGRATION_KEY); result.Err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var role *model.Role = nil
|
||||
var systemAdminRole *model.Role = nil
|
||||
var err *model.AppError = nil
|
||||
|
||||
mlog.Info("Migrating emojis config to database.")
|
||||
switch *a.Config().ServiceSettings.RestrictCustomEmojiCreation {
|
||||
case model.RESTRICT_EMOJI_CREATION_ALL:
|
||||
role, err = a.GetRoleByName(model.SYSTEM_USER_ROLE_ID)
|
||||
if err != nil {
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical(err.Error())
|
||||
return
|
||||
}
|
||||
break
|
||||
case model.RESTRICT_EMOJI_CREATION_ADMIN:
|
||||
role, err = a.GetRoleByName(model.TEAM_ADMIN_ROLE_ID)
|
||||
if err != nil {
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical(err.Error())
|
||||
return
|
||||
}
|
||||
break
|
||||
case model.RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN:
|
||||
role = nil
|
||||
break
|
||||
default:
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical("Invalid restrict emoji creation setting")
|
||||
return
|
||||
}
|
||||
|
||||
if role != nil {
|
||||
role.Permissions = append(role.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
|
||||
if result := <-a.Srv.Store.Role().Save(role); result.Err != nil {
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical(result.Err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
systemAdminRole, err = a.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
|
||||
if err != nil {
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_EMOJIS.Id)
|
||||
systemAdminRole.Permissions = append(systemAdminRole.Permissions, model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id)
|
||||
if result := <-a.Srv.Store.Role().Save(systemAdminRole); result.Err != nil {
|
||||
mlog.Critical("Failed to migrate emojis creation permissions from mattermost config.")
|
||||
mlog.Critical(result.Err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
system := model.System{
|
||||
Name: EMOJIS_PERMISSIONS_MIGRATION_KEY,
|
||||
Value: "true",
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.System().Save(&system); result.Err != nil {
|
||||
mlog.Critical("Failed to mark emojis permissions migration as completed.")
|
||||
mlog.Critical(fmt.Sprint(result.Err))
|
||||
}
|
||||
}
|
||||
|
||||
135
app/app_test.go
135
app/app_test.go
@@ -455,3 +455,138 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
*config.ServiceSettings.PostEditTimeLimit = 300
|
||||
th.App.SaveConfig(config, false)
|
||||
}
|
||||
|
||||
func TestDoEmojisPermissionsMigration(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
if testStoreSqlSupplier == nil {
|
||||
t.Skip("This test requires a TestStore to be run.")
|
||||
}
|
||||
|
||||
// Add a license and change the policy config.
|
||||
restrictCustomEmojiCreation := *th.App.Config().ServiceSettings.RestrictCustomEmojiCreation
|
||||
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = restrictCustomEmojiCreation
|
||||
})
|
||||
}()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN
|
||||
})
|
||||
|
||||
th.ResetEmojisMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
expectedSystemAdmin := []string{
|
||||
model.PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
|
||||
model.PERMISSION_MANAGE_SYSTEM.Id,
|
||||
model.PERMISSION_MANAGE_ROLES.Id,
|
||||
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
|
||||
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
|
||||
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
|
||||
model.PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
|
||||
model.PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
|
||||
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
|
||||
model.PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
|
||||
model.PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
|
||||
model.PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
|
||||
model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
|
||||
model.PERMISSION_EDIT_OTHER_USERS.Id,
|
||||
model.PERMISSION_MANAGE_OAUTH.Id,
|
||||
model.PERMISSION_INVITE_USER.Id,
|
||||
model.PERMISSION_DELETE_POST.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
model.PERMISSION_ADD_USER_TO_TEAM.Id,
|
||||
model.PERMISSION_LIST_USERS_WITHOUT_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_JOBS.Id,
|
||||
model.PERMISSION_CREATE_POST_PUBLIC.Id,
|
||||
model.PERMISSION_CREATE_POST_EPHEMERAL.Id,
|
||||
model.PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
|
||||
model.PERMISSION_READ_USER_ACCESS_TOKEN.Id,
|
||||
model.PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
|
||||
model.PERMISSION_REMOVE_OTHERS_REACTIONS.Id,
|
||||
model.PERMISSION_LIST_TEAM_CHANNELS.Id,
|
||||
model.PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
|
||||
model.PERMISSION_READ_PUBLIC_CHANNEL.Id,
|
||||
model.PERMISSION_VIEW_TEAM.Id,
|
||||
model.PERMISSION_READ_CHANNEL.Id,
|
||||
model.PERMISSION_ADD_REACTION.Id,
|
||||
model.PERMISSION_REMOVE_REACTION.Id,
|
||||
model.PERMISSION_UPLOAD_FILE.Id,
|
||||
model.PERMISSION_GET_PUBLIC_LINK.Id,
|
||||
model.PERMISSION_CREATE_POST.Id,
|
||||
model.PERMISSION_USE_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_REMOVE_USER_FROM_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_TEAM.Id,
|
||||
model.PERMISSION_IMPORT_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_TEAM_ROLES.Id,
|
||||
model.PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||
model.PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_MANAGE_WEBHOOKS.Id,
|
||||
model.PERMISSION_EDIT_POST.Id,
|
||||
model.PERMISSION_MANAGE_EMOJIS.Id,
|
||||
model.PERMISSION_MANAGE_OTHERS_EMOJIS.Id,
|
||||
}
|
||||
|
||||
role1, err1 := th.App.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
|
||||
assert.Nil(t, err1)
|
||||
assert.Equal(t, expectedSystemAdmin, role1.Permissions, fmt.Sprintf("'%v' did not have expected permissions", model.SYSTEM_ADMIN_ROLE_ID))
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ADMIN
|
||||
})
|
||||
|
||||
th.ResetEmojisMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
role2, err2 := th.App.GetRoleByName(model.TEAM_ADMIN_ROLE_ID)
|
||||
assert.Nil(t, err2)
|
||||
expected2 := []string{
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_REMOVE_USER_FROM_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_TEAM.Id,
|
||||
model.PERMISSION_IMPORT_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_TEAM_ROLES.Id,
|
||||
model.PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||
model.PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
|
||||
model.PERMISSION_MANAGE_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
|
||||
model.PERMISSION_MANAGE_WEBHOOKS.Id,
|
||||
model.PERMISSION_DELETE_POST.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_MANAGE_EMOJIS.Id,
|
||||
}
|
||||
assert.Equal(t, expected2, role2.Permissions, fmt.Sprintf("'%v' did not have expected permissions", model.TEAM_ADMIN_ROLE_ID))
|
||||
|
||||
systemAdmin1, systemAdminErr1 := th.App.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
|
||||
assert.Nil(t, systemAdminErr1)
|
||||
assert.Equal(t, expectedSystemAdmin, systemAdmin1.Permissions, fmt.Sprintf("'%v' did not have expected permissions", model.SYSTEM_ADMIN_ROLE_ID))
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ALL
|
||||
})
|
||||
|
||||
th.ResetEmojisMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
role3, err3 := th.App.GetRoleByName(model.SYSTEM_USER_ROLE_ID)
|
||||
assert.Nil(t, err3)
|
||||
expected3 := []string{
|
||||
model.PERMISSION_CREATE_DIRECT_CHANNEL.Id,
|
||||
model.PERMISSION_CREATE_GROUP_CHANNEL.Id,
|
||||
model.PERMISSION_PERMANENT_DELETE_USER.Id,
|
||||
model.PERMISSION_CREATE_TEAM.Id,
|
||||
model.PERMISSION_MANAGE_EMOJIS.Id,
|
||||
}
|
||||
assert.Equal(t, expected3, role3.Permissions, fmt.Sprintf("'%v' did not have expected permissions", model.SYSTEM_USER_ROLE_ID))
|
||||
|
||||
systemAdmin2, systemAdminErr2 := th.App.GetRoleByName(model.SYSTEM_ADMIN_ROLE_ID)
|
||||
assert.Nil(t, systemAdminErr2)
|
||||
assert.Equal(t, expectedSystemAdmin, systemAdmin2.Permissions, fmt.Sprintf("'%v' did not have expected permissions", model.SYSTEM_ADMIN_ROLE_ID))
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||
|
||||
th.App.DoAdvancedPermissionsMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||
|
||||
@@ -316,6 +317,40 @@ func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel)
|
||||
return member
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateScheme() (*model.Scheme, []*model.Role) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
scheme, err := me.App.CreateScheme(&model.Scheme{
|
||||
DisplayName: "Test Scheme Display Name",
|
||||
Name: model.NewId(),
|
||||
Description: "Test scheme description",
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
roleIDs := []string{
|
||||
scheme.DefaultTeamAdminRole,
|
||||
scheme.DefaultTeamUserRole,
|
||||
scheme.DefaultChannelAdminRole,
|
||||
scheme.DefaultChannelUserRole,
|
||||
}
|
||||
|
||||
var roles []*model.Role
|
||||
for _, roleID := range roleIDs {
|
||||
role, err := me.App.GetRole(roleID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
|
||||
return scheme, roles
|
||||
}
|
||||
|
||||
func (me *TestHelper) TearDown() {
|
||||
me.App.Shutdown()
|
||||
os.Remove(me.tempConfigPath)
|
||||
@@ -399,6 +434,18 @@ func (me *TestHelper) ResetRoleMigration() {
|
||||
}
|
||||
}
|
||||
|
||||
func (me *TestHelper) ResetEmojisMigration() {
|
||||
if _, err := testStoreSqlSupplier.GetMaster().Exec("UPDATE Roles SET Permissions=REPLACE(Permissions, ', manage_emojis', '') WHERE builtin=True"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testClusterInterface.sendClearRoleCacheMessage()
|
||||
|
||||
if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": EMOJIS_PERMISSIONS_MIGRATION_KEY}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type FakeClusterInterface struct {
|
||||
clusterMessageHandler einterfaces.ClusterMessageHandler
|
||||
}
|
||||
|
||||
@@ -94,19 +94,6 @@ func (a *App) SessionHasPermissionToUser(session model.Session, userId string) b
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *App) SessionHasPermissionToPost(session model.Session, postId string, permission *model.Permission) bool {
|
||||
post, err := a.GetSinglePost(postId)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if post.UserId == session.UserId {
|
||||
return true
|
||||
}
|
||||
|
||||
return a.SessionHasPermissionToChannel(session, post.ChannelId, permission)
|
||||
}
|
||||
|
||||
func (a *App) HasPermissionTo(askingUserId string, permission *model.Permission) bool {
|
||||
user, err := a.GetUser(askingUserId)
|
||||
if err != nil {
|
||||
@@ -200,6 +187,10 @@ func (a *App) RolesGrantPermission(roleNames []string, permissionId string) bool
|
||||
}
|
||||
|
||||
for _, role := range roles {
|
||||
if role.DeleteAt != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
permissions := role.Permissions
|
||||
for _, permission := range permissions {
|
||||
if permission == permissionId {
|
||||
|
||||
@@ -32,7 +32,7 @@ func (a *App) CreateDefaultChannels(teamId string) ([]*model.Channel, *model.App
|
||||
return channels, nil
|
||||
}
|
||||
|
||||
func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole string, userRequestorId string) *model.AppError {
|
||||
func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
|
||||
var err *model.AppError = nil
|
||||
|
||||
var requestor *model.User
|
||||
@@ -52,7 +52,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
|
||||
cm := &model.ChannelMember{
|
||||
ChannelId: townSquare.Id,
|
||||
UserId: user.Id,
|
||||
Roles: channelRole,
|
||||
SchemeUser: true,
|
||||
SchemeAdmin: shouldBeAdmin,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
|
||||
@@ -85,7 +86,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, channelRole s
|
||||
cm := &model.ChannelMember{
|
||||
ChannelId: offTopic.Id,
|
||||
UserId: user.Id,
|
||||
Roles: channelRole,
|
||||
SchemeUser: true,
|
||||
SchemeAdmin: shouldBeAdmin,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
|
||||
@@ -166,7 +168,8 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan
|
||||
cm := &model.ChannelMember{
|
||||
ChannelId: sc.Id,
|
||||
UserId: channel.CreatorId,
|
||||
Roles: model.CHANNEL_USER_ROLE_ID + " " + model.CHANNEL_ADMIN_ROLE_ID,
|
||||
SchemeUser: true,
|
||||
SchemeAdmin: true,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
|
||||
@@ -322,7 +325,7 @@ func (a *App) createGroupChannel(userIds []string, creatorId string) (*model.Cha
|
||||
UserId: user.Id,
|
||||
ChannelId: group.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
Roles: model.CHANNEL_USER_ROLE_ID,
|
||||
SchemeUser: true,
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Channel().SaveMember(cm); result.Err != nil {
|
||||
@@ -351,6 +354,23 @@ func (a *App) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppE
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) UpdateChannelScheme(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||
var oldChannel *model.Channel
|
||||
var err *model.AppError
|
||||
if oldChannel, err = a.GetChannel(channel.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldChannel.SchemeId = channel.SchemeId
|
||||
|
||||
newChannel, err := a.UpdateChannel(oldChannel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newChannel, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateChannelPrivacy(oldChannel *model.Channel, user *model.User) (*model.Channel, *model.AppError) {
|
||||
if channel, err := a.UpdateChannel(oldChannel); err != nil {
|
||||
return channel, err
|
||||
@@ -438,6 +458,39 @@ func (a *App) PatchChannel(channel *model.Channel, patch *model.ChannelPatch, us
|
||||
return channel, err
|
||||
}
|
||||
|
||||
func (a *App) GetSchemeRolesForChannel(channelId string) (string, string, *model.AppError) {
|
||||
var channel *model.Channel
|
||||
var err *model.AppError
|
||||
|
||||
if channel, err = a.GetChannel(channelId); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if channel.SchemeId != nil && len(*channel.SchemeId) != 0 {
|
||||
if scheme, err := a.GetScheme(*channel.SchemeId); err != nil {
|
||||
return "", "", err
|
||||
} else {
|
||||
return scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
|
||||
}
|
||||
}
|
||||
|
||||
var team *model.Team
|
||||
|
||||
if team, err = a.GetTeam(channel.TeamId); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
|
||||
if scheme, err := a.GetScheme(*team.SchemeId); err != nil {
|
||||
return "", "", err
|
||||
} else {
|
||||
return scheme.DefaultChannelUserRole, scheme.DefaultChannelAdminRole, nil
|
||||
}
|
||||
}
|
||||
|
||||
return model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles string) (*model.ChannelMember, *model.AppError) {
|
||||
var member *model.ChannelMember
|
||||
var err *model.AppError
|
||||
@@ -445,14 +498,42 @@ func (a *App) UpdateChannelMemberRoles(channelId string, userId string, newRoles
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil {
|
||||
schemeUserRole, schemeAdminRole, err := a.GetSchemeRolesForChannel(channelId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
member.Roles = newRoles
|
||||
var newExplicitRoles []string
|
||||
member.SchemeUser = false
|
||||
member.SchemeAdmin = false
|
||||
|
||||
for _, roleName := range strings.Fields(newRoles) {
|
||||
if role, err := a.GetRoleByName(roleName); err != nil {
|
||||
err.StatusCode = http.StatusBadRequest
|
||||
return nil, err
|
||||
} else if !role.SchemeManaged {
|
||||
// The role is not scheme-managed, so it's OK to apply it to the explicit roles field.
|
||||
newExplicitRoles = append(newExplicitRoles, roleName)
|
||||
} else {
|
||||
// The role is scheme-managed, so need to check if it is part of the scheme for this channel or not.
|
||||
switch roleName {
|
||||
case schemeAdminRole:
|
||||
member.SchemeAdmin = true
|
||||
case schemeUserRole:
|
||||
member.SchemeUser = true
|
||||
default:
|
||||
// If not part of the scheme for this channel, then it is not allowed to apply it as an explicit role.
|
||||
return nil, model.NewAppError("UpdateChannelMemberRoles", "api.channel.update_channel_member_roles.scheme_role.app_error", nil, "role_name="+roleName, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
member.ExplicitRoles = strings.Join(newExplicitRoles, " ")
|
||||
|
||||
if result := <-a.Srv.Store.Channel().UpdateMember(member); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
member = result.Data.(*model.ChannelMember)
|
||||
}
|
||||
|
||||
a.InvalidateCacheForUser(userId)
|
||||
@@ -597,7 +678,7 @@ func (a *App) addUserToChannel(user *model.User, channel *model.Channel, teamMem
|
||||
ChannelId: channel.Id,
|
||||
UserId: user.Id,
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
Roles: model.CHANNEL_USER_ROLE_ID,
|
||||
SchemeUser: true,
|
||||
}
|
||||
if result := <-a.Srv.Store.Channel().SaveMember(newMember); result.Err != nil {
|
||||
mlog.Error(fmt.Sprintf("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err), mlog.String("user_id", user.Id))
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testi
|
||||
|
||||
// create a new user that joins the default channels
|
||||
user := th.CreateUser()
|
||||
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, model.CHANNEL_USER_ROLE_ID, "")
|
||||
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, false, "")
|
||||
|
||||
// there should be a ChannelMemberHistory record for the user
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, townSquareChannelId)).([]*model.ChannelMemberHistoryResult)
|
||||
@@ -146,7 +146,7 @@ func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing
|
||||
|
||||
// create a new user that joins the default channels
|
||||
user := th.CreateUser()
|
||||
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, model.CHANNEL_USER_ROLE_ID, "")
|
||||
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, false, "")
|
||||
|
||||
// there should be a ChannelMemberHistory record for the user
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, offTopicChannelId)).([]*model.ChannelMemberHistoryResult)
|
||||
@@ -381,3 +381,21 @@ func TestAddChannelMemberNoUserRequestor(t *testing.T) {
|
||||
assert.Equal(t, user.Username, post.Props["username"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppUpdateChannelScheme(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
channel := th.BasicChannel
|
||||
mockID := model.NewString("x")
|
||||
channel.SchemeId = mockID
|
||||
|
||||
updatedChannel, err := th.App.UpdateChannelScheme(channel)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if updatedChannel.SchemeId != mockID {
|
||||
t.Fatal("Wrong Channel SchemeId")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,33 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const permissionsExportBatchSize = 100
|
||||
|
||||
func (a *App) ResetPermissionsSystem() *model.AppError {
|
||||
// Reset all Teams to not have a scheme.
|
||||
if result := <-a.Srv.Store.Team().ResetAllTeamSchemes(); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
// Reset all Channels to not have a scheme.
|
||||
if result := <-a.Srv.Store.Channel().ResetAllChannelSchemes(); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
// Purge all schemes from the database.
|
||||
if result := <-a.Srv.Store.Scheme().PermanentDeleteAll(); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
// Purge all roles from the database.
|
||||
if result := <-a.Srv.Store.Role().PermanentDeleteAll(); result.Err != nil {
|
||||
return result.Err
|
||||
@@ -20,6 +43,142 @@ func (a *App) ResetPermissionsSystem() *model.AppError {
|
||||
|
||||
// Now that the permissions system has been reset, re-run the migration to reinitialise it.
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
a.DoEmojisPermissionsMigration()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ExportPermissions(w io.Writer) error {
|
||||
|
||||
next := a.SchemesIterator(permissionsExportBatchSize)
|
||||
var schemeBatch []*model.Scheme
|
||||
|
||||
for schemeBatch = next(); len(schemeBatch) > 0; schemeBatch = next() {
|
||||
|
||||
for _, scheme := range schemeBatch {
|
||||
|
||||
roleIDs := []string{
|
||||
scheme.DefaultTeamAdminRole,
|
||||
scheme.DefaultTeamUserRole,
|
||||
scheme.DefaultChannelAdminRole,
|
||||
scheme.DefaultChannelUserRole,
|
||||
}
|
||||
|
||||
roles := []*model.Role{}
|
||||
for _, roleID := range roleIDs {
|
||||
if len(roleID) == 0 {
|
||||
continue
|
||||
}
|
||||
role, err := a.GetRole(roleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roles = append(roles, role)
|
||||
}
|
||||
|
||||
schemeExport, err := json.Marshal(&model.SchemeConveyor{
|
||||
Name: scheme.Name,
|
||||
DisplayName: scheme.DisplayName,
|
||||
Description: scheme.Description,
|
||||
Scope: scheme.Scope,
|
||||
TeamAdmin: scheme.DefaultTeamAdminRole,
|
||||
TeamUser: scheme.DefaultTeamUserRole,
|
||||
ChannelAdmin: scheme.DefaultChannelAdminRole,
|
||||
ChannelUser: scheme.DefaultChannelUserRole,
|
||||
Roles: roles,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
schemeExport = append(schemeExport, []byte("\n")...)
|
||||
|
||||
_, err = w.Write(schemeExport)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ImportPermissions(jsonl io.Reader) error {
|
||||
createdSchemeIDs := []string{}
|
||||
|
||||
scanner := bufio.NewScanner(jsonl)
|
||||
|
||||
for scanner.Scan() {
|
||||
var schemeConveyor *model.SchemeConveyor
|
||||
err := json.Unmarshal(scanner.Bytes(), &schemeConveyor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the new Scheme. The new Roles are created automatically.
|
||||
var appErr *model.AppError
|
||||
schemeCreated, appErr := a.CreateScheme(schemeConveyor.Scheme())
|
||||
if appErr != nil {
|
||||
return errors.New(appErr.Message)
|
||||
}
|
||||
createdSchemeIDs = append(createdSchemeIDs, schemeCreated.Id)
|
||||
|
||||
schemeIn := schemeConveyor.Scheme()
|
||||
roleIDTuples := [][]string{
|
||||
{schemeCreated.DefaultTeamAdminRole, schemeIn.DefaultTeamAdminRole},
|
||||
{schemeCreated.DefaultTeamUserRole, schemeIn.DefaultTeamUserRole},
|
||||
{schemeCreated.DefaultChannelAdminRole, schemeIn.DefaultChannelAdminRole},
|
||||
{schemeCreated.DefaultChannelUserRole, schemeIn.DefaultChannelUserRole},
|
||||
}
|
||||
for _, roleIDTuple := range roleIDTuples {
|
||||
if len(roleIDTuple[0]) == 0 || len(roleIDTuple[1]) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
err = updateRole(a, schemeConveyor, roleIDTuple[0], roleIDTuple[1])
|
||||
if err != nil {
|
||||
// Delete the new Schemes. The new Roles are deleted automatically.
|
||||
for _, schemeID := range createdSchemeIDs {
|
||||
a.DeleteScheme(schemeID)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRole(a *App, sc *model.SchemeConveyor, roleCreatedID, defaultRoleID string) error {
|
||||
var err *model.AppError
|
||||
|
||||
roleCreated, err := a.GetRole(roleCreatedID)
|
||||
if err != nil {
|
||||
return errors.New(err.Message)
|
||||
}
|
||||
|
||||
var roleIn *model.Role
|
||||
for _, role := range sc.Roles {
|
||||
if role.Id == defaultRoleID {
|
||||
roleIn = role
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
roleCreated.Name = roleIn.Name
|
||||
roleCreated.DisplayName = roleIn.DisplayName
|
||||
roleCreated.Description = roleIn.Description
|
||||
roleCreated.Permissions = roleIn.Permissions
|
||||
|
||||
_, err = a.UpdateRole(roleCreated)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("%v: %v\n", err.Message, err.DetailedError))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
253
app/permissions_test.go
Обычный файл
253
app/permissions_test.go
Обычный файл
@@ -0,0 +1,253 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
type testWriter struct {
|
||||
write func(p []byte) (int, error)
|
||||
}
|
||||
|
||||
func (tw testWriter) Write(p []byte) (int, error) {
|
||||
return tw.write(p)
|
||||
}
|
||||
|
||||
func TestExportPermissions(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
var scheme *model.Scheme
|
||||
var roles []*model.Role
|
||||
withMigrationMarkedComplete(th, func() {
|
||||
scheme, roles = th.CreateScheme()
|
||||
})
|
||||
|
||||
results := [][]byte{}
|
||||
|
||||
tw := testWriter{
|
||||
write: func(p []byte) (int, error) {
|
||||
results = append(results, p)
|
||||
return len(p), nil
|
||||
},
|
||||
}
|
||||
|
||||
err := th.App.ExportPermissions(tw)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
t.Error("Expected export to have returned something.")
|
||||
}
|
||||
|
||||
firstResult := results[0]
|
||||
|
||||
var row map[string]interface{}
|
||||
err = json.Unmarshal(firstResult, &row)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
getRoleByID := func(id string) string {
|
||||
for _, role := range roles {
|
||||
if role.Id == id {
|
||||
return role.Id
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
expectations := map[string]func(str string) string{
|
||||
scheme.DisplayName: func(str string) string { return row["display_name"].(string) },
|
||||
scheme.Name: func(str string) string { return row["name"].(string) },
|
||||
scheme.Description: func(str string) string { return row["description"].(string) },
|
||||
scheme.Scope: func(str string) string { return row["scope"].(string) },
|
||||
scheme.DefaultTeamAdminRole: func(str string) string { return getRoleByID(str) },
|
||||
scheme.DefaultTeamUserRole: func(str string) string { return getRoleByID(str) },
|
||||
scheme.DefaultChannelAdminRole: func(str string) string { return getRoleByID(str) },
|
||||
scheme.DefaultChannelUserRole: func(str string) string { return getRoleByID(str) },
|
||||
}
|
||||
|
||||
for key, valF := range expectations {
|
||||
expected := key
|
||||
actual := valF(key)
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %v but got %v.", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestImportPermissions(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
name := model.NewId()
|
||||
displayName := model.NewId()
|
||||
description := "my test description"
|
||||
scope := model.SCHEME_SCOPE_CHANNEL
|
||||
roleName1 := model.NewId()
|
||||
roleName2 := model.NewId()
|
||||
|
||||
var results []*model.Scheme
|
||||
var beforeCount int
|
||||
withMigrationMarkedComplete(th, func() {
|
||||
|
||||
var appErr *model.AppError
|
||||
results, appErr = th.App.GetSchemes(scope, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
beforeCount = len(results)
|
||||
|
||||
json := fmt.Sprintf(`{"display_name":"%v","name":"%v","description":"%v","scope":"%v","default_team_admin_role":"","default_team_user_role":"","default_channel_admin_role":"yzfx3g9xjjfw8cqo6bpn33xr7o","default_channel_user_role":"a7s3cp4n33dfxbsrmyh9djao3a","roles":[{"id":"yzfx3g9xjjfw8cqo6bpn33xr7o","name":"%v","display_name":"Channel Admin Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589687,"update_at":1526475589687,"delete_at":0,"permissions":["manage_channel_roles"],"scheme_managed":true,"built_in":false},{"id":"a7s3cp4n33dfxbsrmyh9djao3a","name":"%v","display_name":"Channel User Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589688,"update_at":1526475589688,"delete_at":0,"permissions":["read_channel","add_reaction","remove_reaction","manage_public_channel_members","upload_file","get_public_link","create_post","use_slash_commands","manage_private_channel_members","delete_post","edit_post"],"scheme_managed":true,"built_in":false}]}`, displayName, name, description, scope, roleName1, roleName2)
|
||||
r := strings.NewReader(json)
|
||||
|
||||
err := th.App.ImportPermissions(r)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
results, appErr = th.App.GetSchemes(scope, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
actual := len(results)
|
||||
expected := beforeCount + 1
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %v roles but got %v.", expected, actual)
|
||||
}
|
||||
|
||||
newScheme := results[0]
|
||||
|
||||
channelAdminRole, appErr := th.App.GetRole(newScheme.DefaultChannelAdminRole)
|
||||
if appErr != nil {
|
||||
t.Error(appErr)
|
||||
}
|
||||
|
||||
channelUserRole, appErr := th.App.GetRole(newScheme.DefaultChannelUserRole)
|
||||
if appErr != nil {
|
||||
t.Error(appErr)
|
||||
}
|
||||
|
||||
expectations := map[string]string{
|
||||
newScheme.DisplayName: displayName,
|
||||
newScheme.Name: name,
|
||||
newScheme.Description: description,
|
||||
newScheme.Scope: scope,
|
||||
newScheme.DefaultTeamAdminRole: "",
|
||||
newScheme.DefaultTeamUserRole: "",
|
||||
channelAdminRole.Name: roleName1,
|
||||
channelUserRole.Name: roleName2,
|
||||
}
|
||||
|
||||
for actual, expected := range expectations {
|
||||
if actual != expected {
|
||||
t.Errorf("Expected %v but got %v.", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestImportPermissions_idempotentScheme(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
name := model.NewId()
|
||||
displayName := model.NewId()
|
||||
description := "my test description"
|
||||
scope := model.SCHEME_SCOPE_CHANNEL
|
||||
roleName1 := model.NewId()
|
||||
roleName2 := model.NewId()
|
||||
|
||||
json := fmt.Sprintf(`{"display_name":"%v","name":"%v","description":"%v","scope":"%v","default_team_admin_role":"","default_team_user_role":"","default_channel_admin_role":"yzfx3g9xjjfw8cqo6bpn33xr7o","default_channel_user_role":"a7s3cp4n33dfxbsrmyh9djao3a","roles":[{"id":"yzfx3g9xjjfw8cqo6bpn33xr7o","name":"%v","display_name":"Channel Admin Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589687,"update_at":1526475589687,"delete_at":0,"permissions":["manage_channel_roles"],"scheme_managed":true,"built_in":false},{"id":"a7s3cp4n33dfxbsrmyh9djao3a","name":"%v","display_name":"Channel User Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589688,"update_at":1526475589688,"delete_at":0,"permissions":["read_channel","add_reaction","remove_reaction","manage_public_channel_members","upload_file","get_public_link","create_post","use_slash_commands","manage_private_channel_members","delete_post","edit_post"],"scheme_managed":true,"built_in":false}]}`, displayName, name, description, scope, roleName1, roleName2)
|
||||
jsonl := strings.Repeat(json+"\n", 4)
|
||||
r := strings.NewReader(jsonl)
|
||||
|
||||
var results []*model.Scheme
|
||||
var expected int
|
||||
withMigrationMarkedComplete(th, func() {
|
||||
var appErr *model.AppError
|
||||
results, appErr = th.App.GetSchemes(model.SCHEME_SCOPE_CHANNEL, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
expected = len(results) + 1
|
||||
|
||||
err := th.App.ImportPermissions(r)
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
results, appErr = th.App.GetSchemes(model.SCHEME_SCOPE_CHANNEL, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
})
|
||||
actual := len(results)
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Expected count to be %v but got %v", expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestImportPermissions_schemeDeletedOnRoleFailure(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
name := model.NewId()
|
||||
displayName := model.NewId()
|
||||
description := "my test description"
|
||||
scope := model.SCHEME_SCOPE_CHANNEL
|
||||
roleName1 := model.NewId()
|
||||
roleName2 := "some invalid role name"
|
||||
|
||||
jsonl := fmt.Sprintf(`{"display_name":"%v","name":"%v","description":"%v","scope":"%v","default_team_admin_role":"","default_team_user_role":"","default_channel_admin_role":"yzfx3g9xjjfw8cqo6bpn33xr7o","default_channel_user_role":"a7s3cp4n33dfxbsrmyh9djao3a","roles":[{"id":"yzfx3g9xjjfw8cqo6bpn33xr7o","name":"%v","display_name":"Channel Admin Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589687,"update_at":1526475589687,"delete_at":0,"permissions":["manage_channel_roles"],"scheme_managed":true,"built_in":false},{"id":"a7s3cp4n33dfxbsrmyh9djao3a","name":"%v","display_name":"Channel User Role for Scheme my_scheme_1526475590","description":"","create_at":1526475589688,"update_at":1526475589688,"delete_at":0,"permissions":["read_channel","add_reaction","remove_reaction","manage_public_channel_members","upload_file","get_public_link","create_post","use_slash_commands","manage_private_channel_members","delete_post","edit_post"],"scheme_managed":true,"built_in":false}]}`, displayName, name, description, scope, roleName1, roleName2)
|
||||
r := strings.NewReader(jsonl)
|
||||
|
||||
var results []*model.Scheme
|
||||
var expected int
|
||||
withMigrationMarkedComplete(th, func() {
|
||||
var appErr *model.AppError
|
||||
results, appErr = th.App.GetSchemes(model.SCHEME_SCOPE_CHANNEL, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
expected = len(results)
|
||||
|
||||
err := th.App.ImportPermissions(r)
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
results, appErr = th.App.GetSchemes(model.SCHEME_SCOPE_CHANNEL, 0, 100)
|
||||
if appErr != nil {
|
||||
panic(appErr)
|
||||
}
|
||||
})
|
||||
actual := len(results)
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Expected count to be %v but got %v", expected, actual)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func withMigrationMarkedComplete(th *TestHelper, f func()) {
|
||||
// Mark the migration as done.
|
||||
<-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
|
||||
<-th.App.Srv.Store.System().Save(&model.System{Name: model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, Value: "true"})
|
||||
// Un-mark the migration at the end of the test.
|
||||
defer func() {
|
||||
<-th.App.Srv.Store.System().PermanentDeleteByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2)
|
||||
}()
|
||||
f()
|
||||
}
|
||||
168
app/scheme.go
Обычный файл
168
app/scheme.go
Обычный файл
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
func (a *App) GetScheme(id string) (*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Scheme().Get(id); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.(*model.Scheme), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetSchemesPage(scope string, page int, perPage int) ([]*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.GetSchemes(scope, page*perPage, perPage)
|
||||
}
|
||||
|
||||
func (a *App) GetSchemes(scope string, offset int, limit int) ([]*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Scheme().GetAllPage(scope, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.([]*model.Scheme), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Clear any user-provided values for trusted properties.
|
||||
scheme.DefaultTeamAdminRole = ""
|
||||
scheme.DefaultTeamUserRole = ""
|
||||
scheme.DefaultChannelAdminRole = ""
|
||||
scheme.DefaultChannelUserRole = ""
|
||||
scheme.CreateAt = 0
|
||||
scheme.UpdateAt = 0
|
||||
scheme.DeleteAt = 0
|
||||
|
||||
if result := <-a.Srv.Store.Scheme().Save(scheme); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return scheme, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) PatchScheme(scheme *model.Scheme, patch *model.SchemePatch) (*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scheme.Patch(patch)
|
||||
scheme, err := a.UpdateScheme(scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return scheme, err
|
||||
}
|
||||
|
||||
func (a *App) UpdateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Scheme().Save(scheme); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return scheme, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DeleteScheme(schemeId string) (*model.Scheme, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Scheme().Delete(schemeId); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.(*model.Scheme), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetTeamsForSchemePage(scheme *model.Scheme, page int, perPage int) ([]*model.Team, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.GetTeamsForScheme(scheme, page*perPage, perPage)
|
||||
}
|
||||
|
||||
func (a *App) GetTeamsForScheme(scheme *model.Scheme, offset int, limit int) ([]*model.Team, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Team().GetTeamsByScheme(scheme.Id, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.([]*model.Team), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetChannelsForSchemePage(scheme *model.Scheme, page int, perPage int) (model.ChannelList, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.GetChannelsForScheme(scheme, page*perPage, perPage)
|
||||
}
|
||||
|
||||
func (a *App) GetChannelsForScheme(scheme *model.Scheme, offset int, limit int) (model.ChannelList, *model.AppError) {
|
||||
if err := a.IsPhase2MigrationCompleted(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.Channel().GetChannelsByScheme(scheme.Id, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.(model.ChannelList), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) IsPhase2MigrationCompleted() *model.AppError {
|
||||
if a.phase2PermissionsMigrationComplete {
|
||||
return nil
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.System().GetByName(model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2); result.Err != nil {
|
||||
return model.NewAppError("App.IsPhase2MigrationCompleted", "app.schemes.is_phase_2_migration_completed.not_completed.app_error", nil, result.Err.Error(), http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
a.phase2PermissionsMigrationComplete = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) SchemesIterator(batchSize int) func() []*model.Scheme {
|
||||
offset := 0
|
||||
return func() []*model.Scheme {
|
||||
var result store.StoreResult
|
||||
if result = <-a.Srv.Store.Scheme().GetAllPage("", offset, batchSize); result.Err != nil {
|
||||
return []*model.Scheme{}
|
||||
}
|
||||
offset += batchSize
|
||||
return result.Data.([]*model.Scheme)
|
||||
}
|
||||
}
|
||||
96
app/team.go
96
app/team.go
@@ -114,6 +114,24 @@ func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) {
|
||||
return oldTeam, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateTeamScheme(team *model.Team) (*model.Team, *model.AppError) {
|
||||
var oldTeam *model.Team
|
||||
var err *model.AppError
|
||||
if oldTeam, err = a.GetTeam(team.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldTeam.SchemeId = team.SchemeId
|
||||
|
||||
if result := <-a.Srv.Store.Team().Update(oldTeam); result.Err != nil {
|
||||
return nil, result.Err
|
||||
}
|
||||
|
||||
a.sendTeamEvent(oldTeam, model.WEBSOCKET_EVENT_UPDATE_TEAM)
|
||||
|
||||
return oldTeam, nil
|
||||
}
|
||||
|
||||
func (a *App) PatchTeam(teamId string, patch *model.TeamPatch) (*model.Team, *model.AppError) {
|
||||
team, err := a.GetTeam(teamId)
|
||||
if err != nil {
|
||||
@@ -142,17 +160,31 @@ func (a *App) sendTeamEvent(team *model.Team, event string) {
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) GetSchemeRolesForTeam(teamId string) (string, string, *model.AppError) {
|
||||
var team *model.Team
|
||||
var err *model.AppError
|
||||
|
||||
if team, err = a.GetTeam(teamId); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if team.SchemeId != nil && len(*team.SchemeId) != 0 {
|
||||
if scheme, err := a.GetScheme(*team.SchemeId); err != nil {
|
||||
return "", "", err
|
||||
} else {
|
||||
return scheme.DefaultTeamUserRole, scheme.DefaultTeamAdminRole, nil
|
||||
}
|
||||
}
|
||||
|
||||
return model.TEAM_USER_ROLE_ID, model.TEAM_ADMIN_ROLE_ID, nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
|
||||
var member *model.TeamMember
|
||||
if result := <-a.Srv.Store.Team().GetTeamsForUser(userId); result.Err != nil {
|
||||
if result := <-a.Srv.Store.Team().GetMember(teamId, userId); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
members := result.Data.([]*model.TeamMember)
|
||||
for _, m := range members {
|
||||
if m.TeamId == teamId {
|
||||
member = m
|
||||
}
|
||||
}
|
||||
member = result.Data.(*model.TeamMember)
|
||||
}
|
||||
|
||||
if member == nil {
|
||||
@@ -160,14 +192,42 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := a.CheckRolesExist(strings.Fields(newRoles)); err != nil {
|
||||
schemeUserRole, schemeAdminRole, err := a.GetSchemeRolesForTeam(teamId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
member.Roles = newRoles
|
||||
var newExplicitRoles []string
|
||||
member.SchemeUser = false
|
||||
member.SchemeAdmin = false
|
||||
|
||||
for _, roleName := range strings.Fields(newRoles) {
|
||||
if role, err := a.GetRoleByName(roleName); err != nil {
|
||||
err.StatusCode = http.StatusBadRequest
|
||||
return nil, err
|
||||
} else if !role.SchemeManaged {
|
||||
// The role is not scheme-managed, so it's OK to apply it to the explicit roles field.
|
||||
newExplicitRoles = append(newExplicitRoles, roleName)
|
||||
} else {
|
||||
// The role is scheme-managed, so need to check if it is part of the scheme for this channel or not.
|
||||
switch roleName {
|
||||
case schemeAdminRole:
|
||||
member.SchemeAdmin = true
|
||||
case schemeUserRole:
|
||||
member.SchemeUser = true
|
||||
default:
|
||||
// If not part of the scheme for this channel, then it is not allowed to apply it as an explicit role.
|
||||
return nil, model.NewAppError("UpdateTeamMemberRoles", "api.channel.update_team_member_roles.scheme_role.app_error", nil, "role_name="+roleName, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
member.ExplicitRoles = strings.Join(newExplicitRoles, " ")
|
||||
|
||||
if result := <-a.Srv.Store.Team().UpdateMember(member); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
member = result.Data.(*model.TeamMember)
|
||||
}
|
||||
|
||||
a.ClearSessionCacheForUser(userId)
|
||||
@@ -293,13 +353,13 @@ func (a *App) AddUserToTeamByInviteId(inviteId string, userId string) (*model.Te
|
||||
// 3. a pointer to an AppError if something went wrong.
|
||||
func (a *App) joinUserToTeam(team *model.Team, user *model.User) (*model.TeamMember, bool, *model.AppError) {
|
||||
tm := &model.TeamMember{
|
||||
TeamId: team.Id,
|
||||
UserId: user.Id,
|
||||
Roles: model.TEAM_USER_ROLE_ID,
|
||||
TeamId: team.Id,
|
||||
UserId: user.Id,
|
||||
SchemeUser: true,
|
||||
}
|
||||
|
||||
if team.Email == user.Email {
|
||||
tm.Roles = model.TEAM_USER_ROLE_ID + " " + model.TEAM_ADMIN_ROLE_ID
|
||||
tm.SchemeAdmin = true
|
||||
}
|
||||
|
||||
if etmr := <-a.Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil {
|
||||
@@ -343,15 +403,11 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
|
||||
return uua.Err
|
||||
}
|
||||
|
||||
channelRole := model.CHANNEL_USER_ROLE_ID
|
||||
|
||||
if team.Email == user.Email {
|
||||
channelRole = model.CHANNEL_USER_ROLE_ID + " " + model.CHANNEL_ADMIN_ROLE_ID
|
||||
}
|
||||
shouldBeAdmin := team.Email == user.Email
|
||||
|
||||
// Soft error if there is an issue joining the default channels
|
||||
if err := a.JoinDefaultChannels(team.Id, user, channelRole, userRequestorId); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", user.Id, team.Id, err), mlog.String("user_id", user.Id))
|
||||
if err := a.JoinDefaultChannels(team.Id, user, shouldBeAdmin, userRequestorId); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Encountered an issue joining default channels err=%v", err), mlog.String("user_id", user.Id), mlog.String("team_id", team.Id))
|
||||
}
|
||||
|
||||
a.ClearSessionCacheForUser(user.Id)
|
||||
|
||||
@@ -559,3 +559,21 @@ func TestJoinUserToTeam(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAppUpdateTeamScheme(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
team := th.BasicTeam
|
||||
mockID := model.NewString("x")
|
||||
team.SchemeId = mockID
|
||||
|
||||
updatedTeam, err := th.App.UpdateTeamScheme(th.BasicTeam)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if updatedTeam.SchemeId != mockID {
|
||||
t.Fatal("Wrong Team SchemeId")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ func InitDBCommandContextCobra(command *cobra.Command) (*app.App, error) {
|
||||
}
|
||||
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
a.DoEmojisPermissionsMigration()
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ package commands
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
var PermissionsCmd = &cobra.Command{
|
||||
@@ -23,11 +26,32 @@ var ResetPermissionsCmd = &cobra.Command{
|
||||
RunE: resetPermissionsCmdF,
|
||||
}
|
||||
|
||||
var ExportPermissionsCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export permissions data",
|
||||
Long: "Export Roles and Schemes to JSONL for use by Mattermost permissions import.",
|
||||
Example: " permissions export > export.jsonl",
|
||||
RunE: exportPermissionsCmdF,
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
os.Setenv("MM_LOGSETTINGS_CONSOLELEVEL", "error")
|
||||
},
|
||||
}
|
||||
|
||||
var ImportPermissionsCmd = &cobra.Command{
|
||||
Use: "import [file]",
|
||||
Short: "Import permissions data",
|
||||
Long: "Import Roles and Schemes JSONL data as created by the Mattermost permissions export.",
|
||||
Example: " permissions import export.jsonl",
|
||||
RunE: importPermissionsCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
ResetPermissionsCmd.Flags().Bool("confirm", false, "Confirm you really want to reset the permissions system and a database backup has been performed.")
|
||||
|
||||
PermissionsCmd.AddCommand(
|
||||
ResetPermissionsCmd,
|
||||
ExportPermissionsCmd,
|
||||
ImportPermissionsCmd,
|
||||
)
|
||||
RootCmd.AddCommand(PermissionsCmd)
|
||||
}
|
||||
@@ -58,7 +82,51 @@ func resetPermissionsCmdF(command *cobra.Command, args []string) error {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Permissions system successfully reset")
|
||||
CommandPrettyPrintln("Permissions system successfully reset.")
|
||||
CommandPrettyPrintln("Changes will take effect gradually as the server caches expire.")
|
||||
CommandPrettyPrintln("For the changes to take effect immediately, go to the Mattermost System Console > General > Configuration and click \"Purge All Caches\".")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportPermissionsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer a.Shutdown()
|
||||
|
||||
if license := a.License(); license == nil {
|
||||
return errors.New(utils.T("cli.license.critical"))
|
||||
}
|
||||
|
||||
if err = a.ExportPermissions(os.Stdout); err != nil {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func importPermissionsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer a.Shutdown()
|
||||
|
||||
if license := a.License(); license == nil {
|
||||
return errors.New(utils.T("cli.license.critical"))
|
||||
}
|
||||
|
||||
file, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err := a.ImportPermissions(file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
40
cmd/mattermost/commands/permissions_test.go
Обычный файл
40
cmd/mattermost/commands/permissions_test.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestPermissionsExport_rejectsUnlicensed(t *testing.T) {
|
||||
permissionsLicenseRequiredTest(t, "export")
|
||||
}
|
||||
|
||||
func TestPermissionsImport_rejectsUnlicensed(t *testing.T) {
|
||||
permissionsLicenseRequiredTest(t, "import")
|
||||
}
|
||||
|
||||
func permissionsLicenseRequiredTest(t *testing.T, subcommand string) {
|
||||
th := api4.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
path, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
args := []string{"-test.run", "ExecCommand", "--", "--disableconfigwatch", "permissions", subcommand}
|
||||
output, err := exec.Command(path, args...).CombinedOutput()
|
||||
|
||||
actual := string(output)
|
||||
expected := utils.T("cli.license.critical")
|
||||
if !strings.Contains(actual, expected) {
|
||||
t.Errorf("Expected '%v' but got '%v'.", expected, actual)
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,11 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
var RolesCmd = &cobra.Command{
|
||||
@@ -55,7 +58,27 @@ func makeSystemAdminCmdF(command *cobra.Command, args []string) error {
|
||||
return errors.New("Unable to find user '" + args[i] + "'")
|
||||
}
|
||||
|
||||
if _, err := a.UpdateUserRoles(user.Id, "system_admin system_user", true); err != nil {
|
||||
systemAdmin := false
|
||||
systemUser := false
|
||||
|
||||
roles := strings.Fields(user.Roles)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case model.SYSTEM_ADMIN_ROLE_ID:
|
||||
systemAdmin = true
|
||||
case model.SYSTEM_USER_ROLE_ID:
|
||||
systemUser = true
|
||||
}
|
||||
}
|
||||
|
||||
if !systemUser {
|
||||
roles = append(roles, model.SYSTEM_USER_ROLE_ID)
|
||||
}
|
||||
if !systemAdmin {
|
||||
roles = append(roles, model.SYSTEM_ADMIN_ROLE_ID)
|
||||
}
|
||||
|
||||
if _, err := a.UpdateUserRoles(user.Id, strings.Join(roles, " "), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -80,7 +103,26 @@ func makeMemberCmdF(command *cobra.Command, args []string) error {
|
||||
return errors.New("Unable to find user '" + args[i] + "'")
|
||||
}
|
||||
|
||||
if _, err := a.UpdateUserRoles(user.Id, "system_user", true); err != nil {
|
||||
systemUser := false
|
||||
var newRoles []string
|
||||
|
||||
roles := strings.Fields(user.Roles)
|
||||
for _, role := range roles {
|
||||
switch role {
|
||||
case model.SYSTEM_ADMIN_ROLE_ID:
|
||||
default:
|
||||
if role == model.SYSTEM_USER_ROLE_ID {
|
||||
systemUser = true
|
||||
}
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
|
||||
if !systemUser {
|
||||
newRoles = append(roles, model.SYSTEM_USER_ROLE_ID)
|
||||
}
|
||||
|
||||
if _, err := a.UpdateUserRoles(user.Id, strings.Join(newRoles, " "), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,19 @@ func TestAssignRole(t *testing.T) {
|
||||
t.Fatal()
|
||||
} else {
|
||||
user := result.Data.(*model.User)
|
||||
if user.Roles != "system_admin system_user" {
|
||||
t.Fatal()
|
||||
if user.Roles != "system_user system_admin" {
|
||||
t.Fatal("Got wrong roles:", user.Roles)
|
||||
}
|
||||
}
|
||||
|
||||
CheckCommand(t, "roles", "member", th.BasicUser.Email)
|
||||
|
||||
if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err != nil {
|
||||
t.Fatal()
|
||||
} else {
|
||||
user := result.Data.(*model.User)
|
||||
if user.Roles != "system_user" {
|
||||
t.Fatal("Got wrong roles:", user.Roles, user.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func runServer(configFileLocation string, disableConfigWatch bool, usedPlatform
|
||||
}
|
||||
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
a.DoEmojisPermissionsMigration()
|
||||
|
||||
a.InitPlugins(*a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil)
|
||||
a.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package einterfaces
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
type EmojiInterface interface {
|
||||
CanUserCreateEmoji(string, []*model.TeamMember) bool
|
||||
}
|
||||
172
i18n/en.json
172
i18n/en.json
@@ -47,6 +47,54 @@
|
||||
"id": "September",
|
||||
"translation": "September"
|
||||
},
|
||||
{
|
||||
"id": "migrations.worker.run_advanced_permissions_phase_2_migration.invalid_progress",
|
||||
"translation": "Migration failed due to invalid progress data."
|
||||
},
|
||||
{
|
||||
"id": "migrations.worker.run_migration.unknown_key",
|
||||
"translation": "Cannot run migration job due to unknown migration key."
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.migrate_channel_members.open_transaction.app_error",
|
||||
"translation": "Failed to open the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.migrate_channel_members.select.app_error",
|
||||
"translation": "Failed to select the batch of channel members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.migrate_channel_members.rollback_transaction.app_error",
|
||||
"translation": "Failed to roll back the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.migrate_channel_members.update.app_error",
|
||||
"translation": "Failed to update the channel member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.migrate_channel_members.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.migrate_team_members.open_transaction.app_error",
|
||||
"translation": "Failed to open the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.migrate_team_members.select.app_error",
|
||||
"translation": " Failed to select the batch of team members"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.migrate_team_members.rollback_transaction.app_error",
|
||||
"translation": "Failed to roll back the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.migrate_team_members.update.app_error",
|
||||
"translation": "Failed to update the team member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.migrate_team_members.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the database transaction"
|
||||
},
|
||||
{
|
||||
"id": "api.admin.add_certificate.no_file.app_error",
|
||||
"translation": "No file under 'certificate' in request."
|
||||
@@ -195,6 +243,10 @@
|
||||
"id": "api.channel.can_manage_channel.private_restricted_system_admin.app_error",
|
||||
"translation": "Private Channel management and creation is restricted to System Administrators."
|
||||
},
|
||||
{
|
||||
"id": "app.schemes.is_phase_2_migration_completed.not_completed.app_error",
|
||||
"translation": "This API endpoint is not accessible as required migrations have not yet completed."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.can_manage_channel.private_restricted_team_admin.app_error",
|
||||
"translation": "Private Channel management and creation is restricted to Team and System Administrators."
|
||||
@@ -223,6 +275,18 @@
|
||||
"id": "api.channel.convert_channel_to_private.private_channel_error",
|
||||
"translation": "The channel requested to convert is already a private channel."
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.reset_all_channel_schemes.app_error",
|
||||
"translation": "We could not reset the channel schemes"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.permanent_delete_all.app_error",
|
||||
"translation": "We could not permanently delete the schemes"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.reset_all_team_schemes.app_error",
|
||||
"translation": "We could not reset the team schemes"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.create_channel.direct_channel.app_error",
|
||||
"translation": "Must use createDirectChannel API service for direct message channel creation"
|
||||
@@ -1272,10 +1336,6 @@
|
||||
"id": "api.emoji.delete.delete_reactions.app_error",
|
||||
"translation": "Unable to delete reactions when deleting emoji with emoji name %v"
|
||||
},
|
||||
{
|
||||
"id": "api.emoji.delete.permissions.app_error",
|
||||
"translation": "Invalid permissions to delete emoji."
|
||||
},
|
||||
{
|
||||
"id": "api.emoji.disabled.app_error",
|
||||
"translation": "Custom emoji have been disabled by the system admin."
|
||||
@@ -2502,6 +2562,14 @@
|
||||
"id": "api.team.update_team.permissions.app_error",
|
||||
"translation": "You do not have the appropriate permissions"
|
||||
},
|
||||
{
|
||||
"id": "api.team.update_team_scheme.license.error",
|
||||
"translation": "License does not support updating a team's scheme"
|
||||
},
|
||||
{
|
||||
"id": "api.team.update_team_scheme.scheme_scope.error",
|
||||
"translation": "Unable to set the scheme to the team because the supplied scheme is not a team scheme."
|
||||
},
|
||||
{
|
||||
"id": "api.templates.channel_name.group",
|
||||
"translation": "Group Message"
|
||||
@@ -6702,6 +6770,14 @@
|
||||
"id": "store.sql_reaction.delete.save.app_error",
|
||||
"translation": "Unable to delete reaction"
|
||||
},
|
||||
{
|
||||
"id": "api.scheme.get_teams_for_scheme.scope.error",
|
||||
"translation": "Unable to get the teams for scheme because the supplied scheme is not a team scheme."
|
||||
},
|
||||
{
|
||||
"id": "api.scheme.get_channels_for_scheme.scope.error",
|
||||
"translation": "Unable to get the channels for scheme because the supplied scheme is not a channel scheme."
|
||||
},
|
||||
{
|
||||
"id": "store.sql_reaction.delete_all_with_emoj_name.delete_reactions.app_error",
|
||||
"translation": "Unable to delete reactions with the given emoji name"
|
||||
@@ -6742,6 +6818,94 @@
|
||||
"id": "store.sql_role.get_by_name.app_error",
|
||||
"translation": "Unable to get role"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.update_channel_member_roles.scheme_role.app_error",
|
||||
"translation": "The provided role is managed by a Scheme and therefore cannot be applied directly to a Channel Member"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.update_team_member_roles.scheme_role.app_error",
|
||||
"translation": "The provided role is managed by a Scheme and therefore cannot be applied directly to a Team Member"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.update_channel_scheme.license.error",
|
||||
"translation": "License does not support updating a channel's scheme"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.update_channel_scheme.scheme_scope.error",
|
||||
"translation": "Unable to set the scheme to the channel because the supplied scheme is not a channel scheme."
|
||||
},
|
||||
{
|
||||
"id": "store.sql_channel.get_by_scheme.app_error",
|
||||
"translation": "Unable to get the channels for the provided scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_team.get_by_scheme.app_error",
|
||||
"translation": "Unable to get the channels for the provided scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_role.save.open_transaction.app_error",
|
||||
"translation": "Failed to open the transaction to save the role"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_role.save_role.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the transaction to save the role"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_role.save.invalid_role.app_error",
|
||||
"translation": "The provided role is invalid"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_role.delete.update.app_error",
|
||||
"translation": "Unable to delete the role"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save.open_transaction.app_error",
|
||||
"translation": "Failed to open the transaction to save the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save_scheme.commit_transaction.app_error",
|
||||
"translation": "Failed to commit the transaction to save the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save.invalid_scheme.app_error",
|
||||
"translation": "The provided scheme is invalid"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save.update.app_error",
|
||||
"translation": "Unable to update the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save.retrieve_default_scheme_roles.app_error",
|
||||
"translation": "Unable to retrieve the default scheme roles"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.save.insert.app_error",
|
||||
"translation": "Unable to create the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.get.app_error",
|
||||
"translation": "Unable to get the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.reset_teams.app_error",
|
||||
"translation": "Unable to reset all teams using this scheme to the default scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.delete.scheme_in_use.app_error",
|
||||
"translation": "Unable to delete the scheme as it in use by 1 or more teams or channels"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.reset_channels.app_error",
|
||||
"translation": "Unable to reset all channels using this scheme to the default scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.delete.role_update.app_error",
|
||||
"translation": "Unable to delete the roles belonging to this scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_scheme.delete.update.app_error",
|
||||
"translation": "Unable to delete the scheme"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_role.get_by_names.app_error",
|
||||
"translation": "Unable to get roles"
|
||||
|
||||
@@ -4,3 +4,7 @@
|
||||
package imports
|
||||
|
||||
// This is a placeholder so this package can be imported in Team Edition when it will be otherwise empty
|
||||
|
||||
import (
|
||||
_ "github.com/mattermost/mattermost-server/migrations"
|
||||
)
|
||||
|
||||
11
jobs/interfaces/migrations_interface.go
Обычный файл
11
jobs/interfaces/migrations_interface.go
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package interfaces
|
||||
|
||||
import "github.com/mattermost/mattermost-server/model"
|
||||
|
||||
type MigrationsJobInterface interface {
|
||||
MakeWorker() model.Worker
|
||||
MakeScheduler() model.Scheduler
|
||||
}
|
||||
@@ -106,6 +106,13 @@ func (srv *JobServer) SetJobCanceled(job *model.Job) *model.AppError {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
func (srv *JobServer) UpdateInProgressJobData(job *model.Job) *model.AppError {
|
||||
job.Status = model.JOB_STATUS_IN_PROGRESS
|
||||
job.LastActivityAt = model.GetMillis()
|
||||
result := <-srv.Store.Job().UpdateOptimistically(job, model.JOB_STATUS_IN_PROGRESS)
|
||||
return result.Err
|
||||
}
|
||||
|
||||
func (srv *JobServer) RequestCancellation(jobId string) *model.AppError {
|
||||
if result := <-srv.Store.Job().UpdateStatusOptimistically(jobId, model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED); result.Err != nil {
|
||||
return result.Err
|
||||
|
||||
@@ -107,6 +107,13 @@ func (watcher *Watcher) PollAndNotify() {
|
||||
default:
|
||||
}
|
||||
}
|
||||
} else if job.Type == model.JOB_TYPE_MIGRATIONS {
|
||||
if watcher.workers.Migrations != nil {
|
||||
select {
|
||||
case watcher.workers.Migrations.JobChannel() <- *job:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,10 @@ func (srv *JobServer) InitSchedulers() *Schedulers {
|
||||
schedulers.schedulers = append(schedulers.schedulers, ldapSyncInterface.MakeScheduler())
|
||||
}
|
||||
|
||||
if migrationsInterface := srv.Migrations; migrationsInterface != nil {
|
||||
schedulers.schedulers = append(schedulers.schedulers, migrationsInterface.MakeScheduler())
|
||||
}
|
||||
|
||||
schedulers.nextRunTimes = make([]*time.Time, len(schedulers.schedulers))
|
||||
return schedulers
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package jobs
|
||||
|
||||
import (
|
||||
ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
|
||||
tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
@@ -34,6 +35,7 @@ type JobServer struct {
|
||||
ElasticsearchAggregator ejobs.ElasticsearchAggregatorInterface
|
||||
ElasticsearchIndexer ejobs.ElasticsearchIndexerInterface
|
||||
LdapSync ejobs.LdapSyncInterface
|
||||
Migrations tjobs.MigrationsJobInterface
|
||||
}
|
||||
|
||||
func NewJobServer(configService ConfigService, store store.Store) *JobServer {
|
||||
|
||||
@@ -20,6 +20,7 @@ type Workers struct {
|
||||
ElasticsearchIndexing model.Worker
|
||||
ElasticsearchAggregation model.Worker
|
||||
LdapSync model.Worker
|
||||
Migrations model.Worker
|
||||
|
||||
listenerId string
|
||||
}
|
||||
@@ -50,6 +51,10 @@ func (srv *JobServer) InitWorkers() *Workers {
|
||||
workers.LdapSync = ldapSyncInterface.MakeWorker()
|
||||
}
|
||||
|
||||
if migrationsInterface := srv.Migrations; migrationsInterface != nil {
|
||||
workers.Migrations = migrationsInterface.MakeWorker()
|
||||
}
|
||||
|
||||
return workers
|
||||
}
|
||||
|
||||
@@ -77,6 +82,10 @@ func (workers *Workers) Start() *Workers {
|
||||
go workers.LdapSync.Run()
|
||||
}
|
||||
|
||||
if workers.Migrations != nil {
|
||||
go workers.Migrations.Run()
|
||||
}
|
||||
|
||||
go workers.Watcher.Start()
|
||||
})
|
||||
|
||||
@@ -152,6 +161,10 @@ func (workers *Workers) Stop() *Workers {
|
||||
workers.LdapSync.Stop()
|
||||
}
|
||||
|
||||
if workers.Migrations != nil {
|
||||
workers.Migrations.Stop()
|
||||
}
|
||||
|
||||
mlog.Info("Stopped workers")
|
||||
|
||||
return workers
|
||||
|
||||
106
migrations/advanced_permissions_phase_2.go
Обычный файл
106
migrations/advanced_permissions_phase_2.go
Обычный файл
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
type AdvancedPermissionsPhase2Progress struct {
|
||||
CurrentTable string `json:"current_table"`
|
||||
LastTeamId string `json:"last_team_id"`
|
||||
LastChannelId string `json:"last_channel_id"`
|
||||
LastUserId string `json:"last_user"`
|
||||
}
|
||||
|
||||
func (p *AdvancedPermissionsPhase2Progress) ToJson() string {
|
||||
b, _ := json.Marshal(p)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func AdvancedPermissionsPhase2ProgressFromJson(data io.Reader) *AdvancedPermissionsPhase2Progress {
|
||||
var o *AdvancedPermissionsPhase2Progress
|
||||
json.NewDecoder(data).Decode(&o)
|
||||
return o
|
||||
}
|
||||
|
||||
func (p *AdvancedPermissionsPhase2Progress) IsValid() bool {
|
||||
if len(p.LastChannelId) != 26 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(p.LastTeamId) != 26 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(p.LastUserId) != 26 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.CurrentTable {
|
||||
case "TeamMembers":
|
||||
case "ChannelMembers":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (worker *Worker) runAdvancedPermissionsPhase2Migration(lastDone string) (bool, string, *model.AppError) {
|
||||
var progress *AdvancedPermissionsPhase2Progress
|
||||
if len(lastDone) == 0 {
|
||||
// Haven't started the migration yet.
|
||||
progress = new(AdvancedPermissionsPhase2Progress)
|
||||
progress.CurrentTable = "TeamMembers"
|
||||
progress.LastChannelId = strings.Repeat("0", 26)
|
||||
progress.LastTeamId = strings.Repeat("0", 26)
|
||||
progress.LastUserId = strings.Repeat("0", 26)
|
||||
} else {
|
||||
progress = AdvancedPermissionsPhase2ProgressFromJson(strings.NewReader(lastDone))
|
||||
if !progress.IsValid() {
|
||||
return false, "", model.NewAppError("MigrationsWorker.runAdvancedPermissionsPhase2Migration", "migrations.worker.run_advanced_permissions_phase_2_migration.invalid_progress", map[string]interface{}{"progress": progress.ToJson()}, "", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
if progress.CurrentTable == "TeamMembers" {
|
||||
// Run a TeamMembers migration batch.
|
||||
if result := <-worker.app.Srv.Store.Team().MigrateTeamMembers(progress.LastTeamId, progress.LastUserId); result.Err != nil {
|
||||
return false, progress.ToJson(), result.Err
|
||||
} else {
|
||||
if result.Data == nil {
|
||||
// We haven't progressed. That means that we've reached the end of this stage of the migration, and should now advance to the next stage.
|
||||
progress.LastUserId = strings.Repeat("0", 26)
|
||||
progress.CurrentTable = "ChannelMembers"
|
||||
return false, progress.ToJson(), nil
|
||||
}
|
||||
|
||||
data := result.Data.(map[string]string)
|
||||
progress.LastTeamId = data["TeamId"]
|
||||
progress.LastUserId = data["UserId"]
|
||||
}
|
||||
} else if progress.CurrentTable == "ChannelMembers" {
|
||||
// Run a ChannelMembers migration batch.
|
||||
if result := <-worker.app.Srv.Store.Channel().MigrateChannelMembers(progress.LastChannelId, progress.LastUserId); result.Err != nil {
|
||||
return false, progress.ToJson(), result.Err
|
||||
} else {
|
||||
if result.Data == nil {
|
||||
// We haven't progressed. That means we've reached the end of this final stage of the migration.
|
||||
|
||||
return true, progress.ToJson(), nil
|
||||
}
|
||||
|
||||
data := result.Data.(map[string]string)
|
||||
progress.LastChannelId = data["ChannelId"]
|
||||
progress.LastUserId = data["UserId"]
|
||||
}
|
||||
}
|
||||
|
||||
return false, progress.ToJson(), nil
|
||||
}
|
||||
63
migrations/migrations.go
Обычный файл
63
migrations/migrations.go
Обычный файл
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
const (
|
||||
MIGRATION_STATE_UNSCHEDULED = "unscheduled"
|
||||
MIGRATION_STATE_IN_PROGRESS = "in_progress"
|
||||
MIGRATION_STATE_COMPLETED = "completed"
|
||||
|
||||
JOB_DATA_KEY_MIGRATION = "migration_key"
|
||||
JOB_DATA_KEY_MIGRATION_LAST_DONE = "last_done"
|
||||
)
|
||||
|
||||
type MigrationsJobInterfaceImpl struct {
|
||||
App *app.App
|
||||
}
|
||||
|
||||
func init() {
|
||||
app.RegisterJobsMigrationsJobInterface(func(a *app.App) tjobs.MigrationsJobInterface {
|
||||
return &MigrationsJobInterfaceImpl{a}
|
||||
})
|
||||
}
|
||||
|
||||
func MakeMigrationsList() []string {
|
||||
return []string{
|
||||
model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2,
|
||||
}
|
||||
}
|
||||
|
||||
func GetMigrationState(migration string, store store.Store) (string, *model.Job, *model.AppError) {
|
||||
if result := <-store.System().GetByName(migration); result.Err == nil {
|
||||
return MIGRATION_STATE_COMPLETED, nil, nil
|
||||
}
|
||||
|
||||
if result := <-store.Job().GetAllByType(model.JOB_TYPE_MIGRATIONS); result.Err != nil {
|
||||
return "", nil, result.Err
|
||||
} else {
|
||||
for _, job := range result.Data.([]*model.Job) {
|
||||
if key, ok := job.Data[JOB_DATA_KEY_MIGRATION]; ok {
|
||||
if key != migration {
|
||||
continue
|
||||
}
|
||||
|
||||
switch job.Status {
|
||||
case model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_PENDING:
|
||||
return MIGRATION_STATE_IN_PROGRESS, job, nil
|
||||
default:
|
||||
return MIGRATION_STATE_UNSCHEDULED, job, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MIGRATION_STATE_UNSCHEDULED, nil, nil
|
||||
}
|
||||
140
migrations/migrations_test.go
Обычный файл
140
migrations/migrations_test.go
Обычный файл
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store/storetest"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
|
||||
// Setup a global logger to catch tests logging outside of app context
|
||||
// The global logger will be stomped by apps initalizing but that's fine for testing. Ideally this won't happen.
|
||||
mlog.InitGlobalLogger(mlog.NewLogger(&mlog.LoggerConfiguration{
|
||||
EnableConsole: true,
|
||||
ConsoleJson: true,
|
||||
ConsoleLevel: "error",
|
||||
EnableFile: false,
|
||||
}))
|
||||
|
||||
utils.TranslationsPreInit()
|
||||
|
||||
// In the case where a dev just wants to run a single test, it's faster to just use the default
|
||||
// store.
|
||||
if filter := flag.Lookup("test.run").Value.String(); filter != "" && filter != "." {
|
||||
mlog.Info("-test.run used, not creating temporary containers")
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
status := 0
|
||||
|
||||
container, settings, err := storetest.NewMySQLContainer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
UseTestStore(container, settings)
|
||||
|
||||
defer func() {
|
||||
StopTestStore()
|
||||
os.Exit(status)
|
||||
}()
|
||||
|
||||
status = m.Run()
|
||||
}
|
||||
|
||||
func TestGetMigrationState(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
migrationKey := model.NewId()
|
||||
|
||||
th.DeleteAllJobsByTypeAndMigrationKey(model.JOB_TYPE_MIGRATIONS, migrationKey)
|
||||
|
||||
// Test with no job yet.
|
||||
state, job, err := GetMigrationState(migrationKey, th.App.Srv.Store)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, job)
|
||||
assert.Equal(t, "unscheduled", state)
|
||||
|
||||
// Test with the system table showing the migration as done.
|
||||
system := model.System{
|
||||
Name: migrationKey,
|
||||
Value: "true",
|
||||
}
|
||||
res1 := <-th.App.Srv.Store.System().Save(&system)
|
||||
assert.Nil(t, res1.Err)
|
||||
|
||||
state, job, err = GetMigrationState(migrationKey, th.App.Srv.Store)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, job)
|
||||
assert.Equal(t, "completed", state)
|
||||
|
||||
res2 := <-th.App.Srv.Store.System().PermanentDeleteByName(migrationKey)
|
||||
assert.Nil(t, res2.Err)
|
||||
|
||||
// Test with a job scheduled in "pending" state.
|
||||
j1 := &model.Job{
|
||||
Id: model.NewId(),
|
||||
CreateAt: model.GetMillis(),
|
||||
Data: map[string]string{
|
||||
JOB_DATA_KEY_MIGRATION: migrationKey,
|
||||
},
|
||||
Status: model.JOB_STATUS_PENDING,
|
||||
Type: model.JOB_TYPE_MIGRATIONS,
|
||||
}
|
||||
|
||||
j1 = (<-th.App.Srv.Store.Job().Save(j1)).Data.(*model.Job)
|
||||
|
||||
state, job, err = GetMigrationState(migrationKey, th.App.Srv.Store)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, j1.Id, job.Id)
|
||||
assert.Equal(t, "in_progress", state)
|
||||
|
||||
// Test with a job scheduled in "in progress" state.
|
||||
j2 := &model.Job{
|
||||
Id: model.NewId(),
|
||||
CreateAt: j1.CreateAt + 1,
|
||||
Data: map[string]string{
|
||||
JOB_DATA_KEY_MIGRATION: migrationKey,
|
||||
},
|
||||
Status: model.JOB_STATUS_IN_PROGRESS,
|
||||
Type: model.JOB_TYPE_MIGRATIONS,
|
||||
}
|
||||
|
||||
j2 = (<-th.App.Srv.Store.Job().Save(j2)).Data.(*model.Job)
|
||||
|
||||
state, job, err = GetMigrationState(migrationKey, th.App.Srv.Store)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, j2.Id, job.Id)
|
||||
assert.Equal(t, "in_progress", state)
|
||||
|
||||
// Test with a job scheduled in "error" state.
|
||||
j3 := &model.Job{
|
||||
Id: model.NewId(),
|
||||
CreateAt: j2.CreateAt + 1,
|
||||
Data: map[string]string{
|
||||
JOB_DATA_KEY_MIGRATION: migrationKey,
|
||||
},
|
||||
Status: model.JOB_STATUS_ERROR,
|
||||
Type: model.JOB_TYPE_MIGRATIONS,
|
||||
}
|
||||
|
||||
j3 = (<-th.App.Srv.Store.Job().Save(j3)).Data.(*model.Job)
|
||||
|
||||
state, job, err = GetMigrationState(migrationKey, th.App.Srv.Store)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, j3.Id, job.Id)
|
||||
assert.Equal(t, "unscheduled", state)
|
||||
}
|
||||
425
migrations/migrationstestlib.go
Обычный файл
425
migrations/migrationstestlib.go
Обычный файл
@@ -0,0 +1,425 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/plugin/pluginenv"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/store/storetest"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
type TestHelper struct {
|
||||
App *app.App
|
||||
BasicTeam *model.Team
|
||||
BasicUser *model.User
|
||||
BasicUser2 *model.User
|
||||
BasicChannel *model.Channel
|
||||
BasicPost *model.Post
|
||||
|
||||
SystemAdminUser *model.User
|
||||
|
||||
tempConfigPath string
|
||||
tempWorkspace string
|
||||
pluginHooks map[string]plugin.Hooks
|
||||
}
|
||||
|
||||
type persistentTestStore struct {
|
||||
store.Store
|
||||
}
|
||||
|
||||
func (*persistentTestStore) Close() {}
|
||||
|
||||
var testStoreContainer *storetest.RunningContainer
|
||||
var testStore *persistentTestStore
|
||||
var testStoreSqlSupplier *sqlstore.SqlSupplier
|
||||
var testClusterInterface *FakeClusterInterface
|
||||
|
||||
// UseTestStore sets the container and corresponding settings to use for tests. Once the tests are
|
||||
// complete (e.g. at the end of your TestMain implementation), you should call StopTestStore.
|
||||
func UseTestStore(container *storetest.RunningContainer, settings *model.SqlSettings) {
|
||||
testClusterInterface = &FakeClusterInterface{}
|
||||
testStoreContainer = container
|
||||
testStoreSqlSupplier = sqlstore.NewSqlSupplier(*settings, nil)
|
||||
testStore = &persistentTestStore{store.NewLayeredStore(testStoreSqlSupplier, nil, testClusterInterface)}
|
||||
}
|
||||
|
||||
func StopTestStore() {
|
||||
if testStoreContainer != nil {
|
||||
testStoreContainer.Stop()
|
||||
testStoreContainer = nil
|
||||
}
|
||||
}
|
||||
|
||||
func setupTestHelper(enterprise bool) *TestHelper {
|
||||
permConfig, err := os.Open(utils.FindConfigFile("config.json"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer permConfig.Close()
|
||||
tempConfig, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.Copy(tempConfig, permConfig)
|
||||
tempConfig.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
options := []app.Option{app.ConfigFile(tempConfig.Name()), app.DisableConfigWatch}
|
||||
if testStore != nil {
|
||||
options = append(options, app.StoreOverride(testStore))
|
||||
}
|
||||
|
||||
a, err := app.New(options...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
th := &TestHelper{
|
||||
App: a,
|
||||
pluginHooks: make(map[string]plugin.Hooks),
|
||||
tempConfigPath: tempConfig.Name(),
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.RateLimitSettings.Enable = false })
|
||||
prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress
|
||||
if testStore != nil {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
|
||||
}
|
||||
serverErr := th.App.StartServer()
|
||||
if serverErr != nil {
|
||||
panic(serverErr)
|
||||
}
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||
|
||||
th.App.DoAdvancedPermissionsMigration()
|
||||
th.App.DoEmojisPermissionsMigration()
|
||||
|
||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableOpenServer = true })
|
||||
|
||||
if enterprise {
|
||||
th.App.SetLicense(model.NewTestLicense())
|
||||
} else {
|
||||
th.App.SetLicense(nil)
|
||||
}
|
||||
|
||||
return th
|
||||
}
|
||||
|
||||
func SetupEnterprise() *TestHelper {
|
||||
return setupTestHelper(true)
|
||||
}
|
||||
|
||||
func Setup() *TestHelper {
|
||||
return setupTestHelper(false)
|
||||
}
|
||||
|
||||
func (me *TestHelper) InitBasic() *TestHelper {
|
||||
me.BasicTeam = me.CreateTeam()
|
||||
me.BasicUser = me.CreateUser()
|
||||
me.LinkUserToTeam(me.BasicUser, me.BasicTeam)
|
||||
me.BasicUser2 = me.CreateUser()
|
||||
me.LinkUserToTeam(me.BasicUser2, me.BasicTeam)
|
||||
me.BasicChannel = me.CreateChannel(me.BasicTeam)
|
||||
me.BasicPost = me.CreatePost(me.BasicChannel)
|
||||
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) InitSystemAdmin() *TestHelper {
|
||||
me.SystemAdminUser = me.CreateUser()
|
||||
me.App.UpdateUserRoles(me.SystemAdminUser.Id, model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_ADMIN_ROLE_ID, false)
|
||||
me.SystemAdminUser, _ = me.App.GetUser(me.SystemAdminUser.Id)
|
||||
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeEmail() string {
|
||||
return "success_" + model.NewId() + "@simulator.amazonses.com"
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateTeam() *model.Team {
|
||||
id := model.NewId()
|
||||
team := &model.Team{
|
||||
DisplayName: "dn_" + id,
|
||||
Name: "name" + id,
|
||||
Email: "success+" + id + "@simulator.amazonses.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
var err *model.AppError
|
||||
if team, err = me.App.CreateTeam(team); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
return team
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateUser() *model.User {
|
||||
id := model.NewId()
|
||||
|
||||
user := &model.User{
|
||||
Email: "success+" + id + "@simulator.amazonses.com",
|
||||
Username: "un_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
Password: "Password1",
|
||||
EmailVerified: true,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
var err *model.AppError
|
||||
if user, err = me.App.CreateUser(user); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
return user
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateChannel(team *model.Team) *model.Channel {
|
||||
return me.createChannel(team, model.CHANNEL_OPEN)
|
||||
}
|
||||
|
||||
func (me *TestHelper) createChannel(team *model.Team, channelType string) *model.Channel {
|
||||
id := model.NewId()
|
||||
|
||||
channel := &model.Channel{
|
||||
DisplayName: "dn_" + id,
|
||||
Name: "name_" + id,
|
||||
Type: channelType,
|
||||
TeamId: team.Id,
|
||||
CreatorId: me.BasicUser.Id,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
var err *model.AppError
|
||||
if channel, err = me.App.CreateChannel(channel, true); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
return channel
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
|
||||
utils.DisableDebugLogForTest()
|
||||
var err *model.AppError
|
||||
var channel *model.Channel
|
||||
if channel, err = me.App.CreateDirectChannel(me.BasicUser.Id, user.Id); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
return channel
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePost(channel *model.Channel) *model.Post {
|
||||
id := model.NewId()
|
||||
|
||||
post := &model.Post{
|
||||
UserId: me.BasicUser.Id,
|
||||
ChannelId: channel.Id,
|
||||
Message: "message_" + id,
|
||||
CreateAt: model.GetMillis() - 10000,
|
||||
}
|
||||
|
||||
utils.DisableDebugLogForTest()
|
||||
var err *model.AppError
|
||||
if post, err = me.App.CreatePost(post, channel, false); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
utils.EnableDebugLogForTest()
|
||||
return post
|
||||
}
|
||||
|
||||
func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
err := me.App.JoinUserToTeam(team, user, "")
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
member, err := me.App.AddUserToChannel(user, channel)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
|
||||
return member
|
||||
}
|
||||
|
||||
func (me *TestHelper) TearDown() {
|
||||
me.App.Shutdown()
|
||||
os.Remove(me.tempConfigPath)
|
||||
if err := recover(); err != nil {
|
||||
StopTestStore()
|
||||
panic(err)
|
||||
}
|
||||
if me.tempWorkspace != "" {
|
||||
os.RemoveAll(me.tempWorkspace)
|
||||
}
|
||||
}
|
||||
|
||||
type mockPluginSupervisor struct {
|
||||
hooks plugin.Hooks
|
||||
}
|
||||
|
||||
func (s *mockPluginSupervisor) Start(api plugin.API) error {
|
||||
return s.hooks.OnActivate(api)
|
||||
}
|
||||
|
||||
func (s *mockPluginSupervisor) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockPluginSupervisor) Hooks() plugin.Hooks {
|
||||
return s.hooks
|
||||
}
|
||||
|
||||
func (s *mockPluginSupervisor) Wait() error { return nil }
|
||||
|
||||
func (me *TestHelper) InstallPlugin(manifest *model.Manifest, hooks plugin.Hooks) {
|
||||
if me.tempWorkspace == "" {
|
||||
dir, err := ioutil.TempDir("", "apptest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
me.tempWorkspace = dir
|
||||
}
|
||||
|
||||
pluginDir := filepath.Join(me.tempWorkspace, "plugins")
|
||||
webappDir := filepath.Join(me.tempWorkspace, "webapp")
|
||||
me.App.InitPlugins(pluginDir, webappDir, func(bundle *model.BundleInfo) (plugin.Supervisor, error) {
|
||||
if hooks, ok := me.pluginHooks[bundle.Manifest.Id]; ok {
|
||||
return &mockPluginSupervisor{hooks}, nil
|
||||
}
|
||||
return pluginenv.DefaultSupervisorProvider(bundle)
|
||||
})
|
||||
|
||||
me.pluginHooks[manifest.Id] = hooks
|
||||
|
||||
manifestCopy := *manifest
|
||||
if manifestCopy.Backend == nil {
|
||||
manifestCopy.Backend = &model.ManifestBackend{}
|
||||
}
|
||||
manifestBytes, err := json.Marshal(&manifestCopy)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(pluginDir, manifest.Id), 0700); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(pluginDir, manifest.Id, "plugin.json"), manifestBytes, 0600); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *TestHelper) ResetRoleMigration() {
|
||||
if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Roles"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testClusterInterface.sendClearRoleCacheMessage()
|
||||
|
||||
if _, err := testStoreSqlSupplier.GetMaster().Exec("DELETE from Systems where Name = :Name", map[string]interface{}{"Name": app.ADVANCED_PERMISSIONS_MIGRATION_KEY}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *TestHelper) DeleteAllJobsByTypeAndMigrationKey(jobType string, migrationKey string) {
|
||||
if res := <-me.App.Srv.Store.Job().GetAllByType(model.JOB_TYPE_MIGRATIONS); res.Err != nil {
|
||||
panic(res.Err)
|
||||
} else {
|
||||
jobs := res.Data.([]*model.Job)
|
||||
|
||||
for _, job := range jobs {
|
||||
if key, ok := job.Data[JOB_DATA_KEY_MIGRATION]; ok && key == migrationKey {
|
||||
if res := <-me.App.Srv.Store.Job().Delete(job.Id); res.Err != nil {
|
||||
panic(res.Err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type FakeClusterInterface struct {
|
||||
clusterMessageHandler einterfaces.ClusterMessageHandler
|
||||
}
|
||||
|
||||
func (me *FakeClusterInterface) StartInterNodeCommunication() {}
|
||||
func (me *FakeClusterInterface) StopInterNodeCommunication() {}
|
||||
func (me *FakeClusterInterface) RegisterClusterMessageHandler(event string, crm einterfaces.ClusterMessageHandler) {
|
||||
me.clusterMessageHandler = crm
|
||||
}
|
||||
func (me *FakeClusterInterface) GetClusterId() string { return "" }
|
||||
func (me *FakeClusterInterface) IsLeader() bool { return false }
|
||||
func (me *FakeClusterInterface) GetMyClusterInfo() *model.ClusterInfo { return nil }
|
||||
func (me *FakeClusterInterface) GetClusterInfos() []*model.ClusterInfo { return nil }
|
||||
func (me *FakeClusterInterface) SendClusterMessage(cluster *model.ClusterMessage) {}
|
||||
func (me *FakeClusterInterface) NotifyMsg(buf []byte) {}
|
||||
func (me *FakeClusterInterface) GetClusterStats() ([]*model.ClusterStats, *model.AppError) {
|
||||
return nil, nil
|
||||
}
|
||||
func (me *FakeClusterInterface) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
||||
return []string{}, nil
|
||||
}
|
||||
func (me *FakeClusterInterface) ConfigChanged(previousConfig *model.Config, newConfig *model.Config, sendToOtherServer bool) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
func (me *FakeClusterInterface) sendClearRoleCacheMessage() {
|
||||
me.clusterMessageHandler(&model.ClusterMessage{
|
||||
Event: model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES,
|
||||
})
|
||||
}
|
||||
func (me *FakeClusterInterface) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
return nil, nil
|
||||
}
|
||||
110
migrations/scheduler.go
Обычный файл
110
migrations/scheduler.go
Обычный файл
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
const (
|
||||
MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS = 3600000 // 1 hour
|
||||
)
|
||||
|
||||
type Scheduler struct {
|
||||
App *app.App
|
||||
allMigrationsCompleted bool
|
||||
}
|
||||
|
||||
func (m *MigrationsJobInterfaceImpl) MakeScheduler() model.Scheduler {
|
||||
return &Scheduler{m.App, false}
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) Name() string {
|
||||
return "MigrationsScheduler"
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) JobType() string {
|
||||
return model.JOB_TYPE_MIGRATIONS
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) Enabled(cfg *model.Config) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) NextScheduleTime(cfg *model.Config, now time.Time, pendingJobs bool, lastSuccessfulJob *model.Job) *time.Time {
|
||||
if scheduler.allMigrationsCompleted {
|
||||
return nil
|
||||
}
|
||||
|
||||
nextTime := time.Now().Add(60 * time.Second)
|
||||
return &nextTime
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) ScheduleJob(cfg *model.Config, pendingJobs bool, lastSuccessfulJob *model.Job) (*model.Job, *model.AppError) {
|
||||
mlog.Debug("Scheduling Job", mlog.String("scheduler", scheduler.Name()))
|
||||
|
||||
// Work through the list of migrations in order. Schedule the first one that isn't done (assuming it isn't in progress already).
|
||||
for _, key := range MakeMigrationsList() {
|
||||
state, job, err := GetMigrationState(key, scheduler.App.Srv.Store)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to determine status of migration: ", mlog.String("scheduler", scheduler.Name()), mlog.String("migration_key", key), mlog.String("error", err.Error()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if state == MIGRATION_STATE_IN_PROGRESS {
|
||||
// Check the migration job isn't wedged.
|
||||
if job != nil && job.LastActivityAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS {
|
||||
mlog.Warn("Job appears to be wedged. Rescheduling another instance.", mlog.String("scheduler", scheduler.Name()), mlog.String("wedged_job_id", job.Id), mlog.String("migration_key", key))
|
||||
if err := scheduler.App.Jobs.SetJobError(job, nil); err != nil {
|
||||
mlog.Error("Worker: Failed to set job error", mlog.String("scheduler", scheduler.Name()), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
}
|
||||
return scheduler.createJob(key, job, scheduler.App.Srv.Store)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if state == MIGRATION_STATE_COMPLETED {
|
||||
// This migration is done. Continue to check the next.
|
||||
continue
|
||||
}
|
||||
|
||||
if state == MIGRATION_STATE_UNSCHEDULED {
|
||||
mlog.Debug("Scheduling a new job for migration.", mlog.String("scheduler", scheduler.Name()), mlog.String("migration_key", key))
|
||||
return scheduler.createJob(key, job, scheduler.App.Srv.Store)
|
||||
}
|
||||
|
||||
mlog.Error("Unknown migration state. Not doing anything.", mlog.String("migration_state", state))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If we reached here, then there aren't any migrations left to run.
|
||||
scheduler.allMigrationsCompleted = true
|
||||
mlog.Debug("All migrations are complete.", mlog.String("scheduler", scheduler.Name()))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) createJob(migrationKey string, lastJob *model.Job, store store.Store) (*model.Job, *model.AppError) {
|
||||
var lastDone string
|
||||
if lastJob != nil {
|
||||
lastDone = lastJob.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE]
|
||||
}
|
||||
|
||||
data := map[string]string{
|
||||
JOB_DATA_KEY_MIGRATION: migrationKey,
|
||||
JOB_DATA_KEY_MIGRATION_LAST_DONE: lastDone,
|
||||
}
|
||||
|
||||
if job, err := scheduler.App.Jobs.CreateJob(model.JOB_TYPE_MIGRATIONS, data); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return job, nil
|
||||
}
|
||||
}
|
||||
166
migrations/worker.go
Обычный файл
166
migrations/worker.go
Обычный файл
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/jobs"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
const (
|
||||
TIME_BETWEEN_BATCHES = 100
|
||||
)
|
||||
|
||||
type Worker struct {
|
||||
name string
|
||||
stop chan bool
|
||||
stopped chan bool
|
||||
jobs chan model.Job
|
||||
jobServer *jobs.JobServer
|
||||
app *app.App
|
||||
}
|
||||
|
||||
func (m *MigrationsJobInterfaceImpl) MakeWorker() model.Worker {
|
||||
worker := Worker{
|
||||
name: "Migrations",
|
||||
stop: make(chan bool, 1),
|
||||
stopped: make(chan bool, 1),
|
||||
jobs: make(chan model.Job),
|
||||
jobServer: m.App.Jobs,
|
||||
app: m.App,
|
||||
}
|
||||
|
||||
return &worker
|
||||
}
|
||||
|
||||
func (worker *Worker) Run() {
|
||||
mlog.Debug("Worker started", mlog.String("worker", worker.name))
|
||||
|
||||
defer func() {
|
||||
mlog.Debug("Worker finished", mlog.String("worker", worker.name))
|
||||
worker.stopped <- true
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-worker.stop:
|
||||
mlog.Debug("Worker received stop signal", mlog.String("worker", worker.name))
|
||||
return
|
||||
case job := <-worker.jobs:
|
||||
mlog.Debug("Worker received a new candidate job.", mlog.String("worker", worker.name))
|
||||
worker.DoJob(&job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) Stop() {
|
||||
mlog.Debug("Worker stopping", mlog.String("worker", worker.name))
|
||||
worker.stop <- true
|
||||
<-worker.stopped
|
||||
}
|
||||
|
||||
func (worker *Worker) JobChannel() chan<- model.Job {
|
||||
return worker.jobs
|
||||
}
|
||||
|
||||
func (worker *Worker) DoJob(job *model.Job) {
|
||||
if claimed, err := worker.jobServer.ClaimJob(job); err != nil {
|
||||
mlog.Info("Worker experienced an error while trying to claim job",
|
||||
mlog.String("worker", worker.name),
|
||||
mlog.String("job_id", job.Id),
|
||||
mlog.String("error", err.Error()))
|
||||
return
|
||||
} else if !claimed {
|
||||
return
|
||||
}
|
||||
|
||||
cancelCtx, cancelCancelWatcher := context.WithCancel(context.Background())
|
||||
cancelWatcherChan := make(chan interface{}, 1)
|
||||
go worker.app.Jobs.CancellationWatcher(cancelCtx, job.Id, cancelWatcherChan)
|
||||
|
||||
defer cancelCancelWatcher()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-cancelWatcherChan:
|
||||
mlog.Debug("Worker: Job has been canceled via CancellationWatcher", mlog.String("worker", worker.name), mlog.String("job_id", job.Id))
|
||||
worker.setJobCanceled(job)
|
||||
return
|
||||
|
||||
case <-worker.stop:
|
||||
mlog.Debug("Worker: Job has been canceled via Worker Stop", mlog.String("worker", worker.name), mlog.String("job_id", job.Id))
|
||||
worker.setJobCanceled(job)
|
||||
return
|
||||
|
||||
case <-time.After(TIME_BETWEEN_BATCHES * time.Millisecond):
|
||||
done, progress, err := worker.runMigration(job.Data[JOB_DATA_KEY_MIGRATION], job.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE])
|
||||
if err != nil {
|
||||
mlog.Error("Worker: Failed to run migration", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
worker.setJobError(job, err)
|
||||
return
|
||||
} else if done {
|
||||
mlog.Info("Worker: Job is complete", mlog.String("worker", worker.name), mlog.String("job_id", job.Id))
|
||||
worker.setJobSuccess(job)
|
||||
return
|
||||
} else {
|
||||
job.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE] = progress
|
||||
if err := worker.app.Jobs.UpdateInProgressJobData(job); err != nil {
|
||||
mlog.Error("Worker: Failed to update migration status data for job", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
worker.setJobError(job, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) setJobSuccess(job *model.Job) {
|
||||
if err := worker.app.Jobs.SetJobSuccess(job); err != nil {
|
||||
mlog.Error("Worker: Failed to set success for job", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
worker.setJobError(job, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) setJobError(job *model.Job, appError *model.AppError) {
|
||||
if err := worker.app.Jobs.SetJobError(job, appError); err != nil {
|
||||
mlog.Error("Worker: Failed to set job error", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) setJobCanceled(job *model.Job) {
|
||||
if err := worker.app.Jobs.SetJobCanceled(job); err != nil {
|
||||
mlog.Error("Worker: Failed to mark job as canceled", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
// Return parameters:
|
||||
// - whether the migration is completed on this run (true) or still incomplete (false).
|
||||
// - the updated lastDone string for the migration.
|
||||
// - any error which may have occurred while running the migration.
|
||||
func (worker *Worker) runMigration(key string, lastDone string) (bool, string, *model.AppError) {
|
||||
var done bool
|
||||
var progress string
|
||||
var err *model.AppError
|
||||
|
||||
switch key {
|
||||
case model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2:
|
||||
done, progress, err = worker.runAdvancedPermissionsPhase2Migration(lastDone)
|
||||
default:
|
||||
return false, "", model.NewAppError("MigrationsWorker.runMigration", "migrations.worker.run_migration.unknown_key", map[string]interface{}{"key": key}, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if done {
|
||||
if result := <-worker.app.Srv.Store.System().Save(&model.System{Name: key, Value: "true"}); result.Err != nil {
|
||||
return false, "", result.Err
|
||||
}
|
||||
}
|
||||
|
||||
return done, progress, err
|
||||
}
|
||||
@@ -32,20 +32,21 @@ const (
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Header string `json:"header"`
|
||||
Purpose string `json:"purpose"`
|
||||
LastPostAt int64 `json:"last_post_at"`
|
||||
TotalMsgCount int64 `json:"total_msg_count"`
|
||||
ExtraUpdateAt int64 `json:"extra_update_at"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
Type string `json:"type"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Header string `json:"header"`
|
||||
Purpose string `json:"purpose"`
|
||||
LastPostAt int64 `json:"last_post_at"`
|
||||
TotalMsgCount int64 `json:"total_msg_count"`
|
||||
ExtraUpdateAt int64 `json:"extra_update_at"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
}
|
||||
|
||||
type ChannelPatch struct {
|
||||
|
||||
@@ -28,14 +28,17 @@ type ChannelUnread struct {
|
||||
}
|
||||
|
||||
type ChannelMember struct {
|
||||
ChannelId string `json:"channel_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
LastViewedAt int64 `json:"last_viewed_at"`
|
||||
MsgCount int64 `json:"msg_count"`
|
||||
MentionCount int64 `json:"mention_count"`
|
||||
NotifyProps StringMap `json:"notify_props"`
|
||||
LastUpdateAt int64 `json:"last_update_at"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
LastViewedAt int64 `json:"last_viewed_at"`
|
||||
MsgCount int64 `json:"msg_count"`
|
||||
MentionCount int64 `json:"mention_count"`
|
||||
NotifyProps StringMap `json:"notify_props"`
|
||||
LastUpdateAt int64 `json:"last_update_at"`
|
||||
SchemeUser bool `json:"scheme_user"`
|
||||
SchemeAdmin bool `json:"scheme_admin"`
|
||||
ExplicitRoles string `json:"explicit_roles"`
|
||||
}
|
||||
|
||||
type ChannelMembers []ChannelMember
|
||||
|
||||
110
model/client4.go
110
model/client4.go
@@ -368,6 +368,14 @@ func (c *Client4) GetRolesRoute() string {
|
||||
return fmt.Sprintf("/roles")
|
||||
}
|
||||
|
||||
func (c *Client4) GetSchemesRoute() string {
|
||||
return fmt.Sprintf("/schemes")
|
||||
}
|
||||
|
||||
func (c *Client4) GetSchemeRoute(id string) string {
|
||||
return c.GetSchemesRoute() + fmt.Sprintf("/%v", id)
|
||||
}
|
||||
|
||||
func (c *Client4) GetAnalyticsRoute() string {
|
||||
return fmt.Sprintf("/analytics")
|
||||
}
|
||||
@@ -376,6 +384,14 @@ func (c *Client4) GetTimezonesRoute() string {
|
||||
return fmt.Sprintf(c.GetSystemRoute() + "/timezones")
|
||||
}
|
||||
|
||||
func (c *Client4) GetChannelSchemeRoute(channelId string) string {
|
||||
return fmt.Sprintf(c.GetChannelsRoute()+"/%v/scheme", channelId)
|
||||
}
|
||||
|
||||
func (c *Client4) GetTeamSchemeRoute(teamId string) string {
|
||||
return fmt.Sprintf(c.GetTeamsRoute()+"/%v/scheme", teamId)
|
||||
}
|
||||
|
||||
func (c *Client4) DoApiGet(url string, etag string) (*http.Response, *AppError) {
|
||||
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
|
||||
}
|
||||
@@ -3484,6 +3500,78 @@ func (c *Client4) PatchRole(roleId string, patch *RolePatch) (*Role, *Response)
|
||||
}
|
||||
}
|
||||
|
||||
// Schemes Section
|
||||
|
||||
// CreateScheme creates a new Scheme.
|
||||
func (c *Client4) CreateScheme(scheme *Scheme) (*Scheme, *Response) {
|
||||
if r, err := c.DoApiPost(c.GetSchemesRoute(), scheme.ToJson()); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return SchemeFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// GetScheme gets a single scheme by ID.
|
||||
func (c *Client4) GetScheme(id string) (*Scheme, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetSchemeRoute(id), ""); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return SchemeFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Get all schemes, sorted with the most recently created first, optionally filtered by scope.
|
||||
func (c *Client4) GetSchemes(scope string, page int, perPage int) ([]*Scheme, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetSchemesRoute()+fmt.Sprintf("?scope=%v&page=%v&per_page=%v", scope, page, perPage), ""); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return SchemesFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteScheme deletes a single scheme by ID.
|
||||
func (c *Client4) DeleteScheme(id string) (bool, *Response) {
|
||||
if r, err := c.DoApiDelete(c.GetSchemeRoute(id)); err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// PatchScheme partially updates a scheme in the system. Any missing fields are not updated.
|
||||
func (c *Client4) PatchScheme(id string, patch *SchemePatch) (*Scheme, *Response) {
|
||||
if r, err := c.DoApiPut(c.GetSchemeRoute(id)+"/patch", patch.ToJson()); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return SchemeFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the teams using this scheme, sorted alphabetically by display name.
|
||||
func (c *Client4) GetTeamsForScheme(schemeId string, page int, perPage int) ([]*Team, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/teams?page=%v&per_page=%v", page, perPage), ""); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return TeamListFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the channels using this scheme, sorted alphabetically by display name.
|
||||
func (c *Client4) GetChannelsForScheme(schemeId string, page int, perPage int) (ChannelList, *Response) {
|
||||
if r, err := c.DoApiGet(c.GetSchemeRoute(schemeId)+fmt.Sprintf("/channels?page=%v&per_page=%v", page, perPage), ""); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return *ChannelListFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin Section
|
||||
|
||||
// UploadPlugin takes an io.Reader stream pointing to the contents of a .tar.gz plugin.
|
||||
@@ -3589,3 +3677,25 @@ func (c *Client4) DeactivatePlugin(id string) (bool, *Response) {
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateChannelScheme will update a channel's scheme.
|
||||
func (c *Client4) UpdateChannelScheme(channelId, schemeId string) (bool, *Response) {
|
||||
sip := &SchemeIDPatch{SchemeID: &schemeId}
|
||||
if r, err := c.DoApiPut(c.GetChannelSchemeRoute(channelId), sip.ToJson()); err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTeamScheme will update a team's scheme.
|
||||
func (c *Client4) UpdateTeamScheme(teamId, schemeId string) (bool, *Response) {
|
||||
sip := &SchemeIDPatch{SchemeID: &schemeId}
|
||||
if r, err := c.DoApiPut(c.GetTeamSchemeRoute(teamId), sip.ToJson()); err != nil {
|
||||
return false, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return CheckStatusOK(r), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_USER = "inv_user"
|
||||
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
|
||||
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
|
||||
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
|
||||
|
||||
CLUSTER_SEND_BEST_EFFORT = "best_effort"
|
||||
CLUSTER_SEND_RELIABLE = "reliable"
|
||||
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
JOB_TYPE_ELASTICSEARCH_POST_INDEXING = "elasticsearch_post_indexing"
|
||||
JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION = "elasticsearch_post_aggregation"
|
||||
JOB_TYPE_LDAP_SYNC = "ldap_sync"
|
||||
JOB_TYPE_MIGRATIONS = "migrations"
|
||||
|
||||
JOB_STATUS_PENDING = "pending"
|
||||
JOB_STATUS_IN_PROGRESS = "in_progress"
|
||||
@@ -52,6 +53,7 @@ func (j *Job) IsValid() *AppError {
|
||||
case JOB_TYPE_ELASTICSEARCH_POST_AGGREGATION:
|
||||
case JOB_TYPE_LDAP_SYNC:
|
||||
case JOB_TYPE_MESSAGE_EXPORT:
|
||||
case JOB_TYPE_MIGRATIONS:
|
||||
default:
|
||||
return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ type Features struct {
|
||||
EmailNotificationContents *bool `json:"email_notification_contents"`
|
||||
DataRetention *bool `json:"data_retention"`
|
||||
MessageExport *bool `json:"message_export"`
|
||||
CustomPermissionsSchemes *bool `json:"custom_permissions_schemes"`
|
||||
|
||||
// after we enabled more features for webrtc we'll need to control them with this
|
||||
FutureFeatures *bool `json:"future_features"`
|
||||
@@ -76,6 +77,7 @@ func (f *Features) ToMap() map[string]interface{} {
|
||||
"email_notification_contents": *f.EmailNotificationContents,
|
||||
"data_retention": *f.DataRetention,
|
||||
"message_export": *f.MessageExport,
|
||||
"custom_permissions_schemes": *f.CustomPermissionsSchemes,
|
||||
"future": *f.FutureFeatures,
|
||||
}
|
||||
}
|
||||
@@ -152,6 +154,10 @@ func (f *Features) SetDefaults() {
|
||||
if f.MessageExport == nil {
|
||||
f.MessageExport = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.CustomPermissionsSchemes == nil {
|
||||
f.CustomPermissionsSchemes = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *License) IsExpired() bool {
|
||||
|
||||
@@ -27,6 +27,8 @@ func TestLicenseFeaturesToMap(t *testing.T) {
|
||||
CheckTrue(t, m["elastic_search"].(bool))
|
||||
CheckTrue(t, m["email_notification_contents"].(bool))
|
||||
CheckTrue(t, m["data_retention"].(bool))
|
||||
CheckTrue(t, m["message_export"].(bool))
|
||||
CheckTrue(t, m["custom_permissions_schemes"].(bool))
|
||||
CheckTrue(t, m["future"].(bool))
|
||||
}
|
||||
|
||||
@@ -48,6 +50,8 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.Elasticsearch)
|
||||
CheckTrue(t, *f.EmailNotificationContents)
|
||||
CheckTrue(t, *f.DataRetention)
|
||||
CheckTrue(t, *f.MessageExport)
|
||||
CheckTrue(t, *f.CustomPermissionsSchemes)
|
||||
CheckTrue(t, *f.FutureFeatures)
|
||||
|
||||
f = Features{}
|
||||
@@ -67,6 +71,8 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
*f.SAML = true
|
||||
*f.Elasticsearch = true
|
||||
*f.DataRetention = true
|
||||
*f.MessageExport = true
|
||||
*f.CustomPermissionsSchemes = true
|
||||
*f.EmailNotificationContents = true
|
||||
|
||||
f.SetDefaults()
|
||||
@@ -85,6 +91,8 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
|
||||
CheckTrue(t, *f.Elasticsearch)
|
||||
CheckTrue(t, *f.EmailNotificationContents)
|
||||
CheckTrue(t, *f.DataRetention)
|
||||
CheckTrue(t, *f.MessageExport)
|
||||
CheckTrue(t, *f.CustomPermissionsSchemes)
|
||||
CheckFalse(t, *f.FutureFeatures)
|
||||
}
|
||||
|
||||
@@ -166,6 +174,8 @@ func TestLicenseToFromJson(t *testing.T) {
|
||||
CheckBool(t, *f1.SAML, *f.SAML)
|
||||
CheckBool(t, *f1.Elasticsearch, *f.Elasticsearch)
|
||||
CheckBool(t, *f1.DataRetention, *f.DataRetention)
|
||||
CheckBool(t, *f1.MessageExport, *f.MessageExport)
|
||||
CheckBool(t, *f1.CustomPermissionsSchemes, *f.CustomPermissionsSchemes)
|
||||
CheckBool(t, *f1.FutureFeatures, *f.FutureFeatures)
|
||||
|
||||
invalid := `{"asdf`
|
||||
|
||||
8
model/migration.go
Обычный файл
8
model/migration.go
Обычный файл
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
const (
|
||||
MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2 = "migration_advanced_permissions_phase_2"
|
||||
)
|
||||
@@ -50,6 +50,8 @@ var PERMISSION_MANAGE_WEBHOOKS *Permission
|
||||
var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission
|
||||
var PERMISSION_MANAGE_OAUTH *Permission
|
||||
var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
|
||||
var PERMISSION_MANAGE_EMOJIS *Permission
|
||||
var PERMISSION_MANAGE_OTHERS_EMOJIS *Permission
|
||||
var PERMISSION_CREATE_POST *Permission
|
||||
var PERMISSION_CREATE_POST_PUBLIC *Permission
|
||||
var PERMISSION_CREATE_POST_EPHEMERAL *Permission
|
||||
@@ -286,6 +288,18 @@ func initializePermissions() {
|
||||
"authentication.permissions.manage_system_wide_oauth.description",
|
||||
PERMISSION_SCOPE_SYSTEM,
|
||||
}
|
||||
PERMISSION_MANAGE_EMOJIS = &Permission{
|
||||
"manage_emojis",
|
||||
"authentication.permissions.manage_emojis.name",
|
||||
"authentication.permissions.manage_emojis.description",
|
||||
PERMISSION_SCOPE_TEAM,
|
||||
}
|
||||
PERMISSION_MANAGE_OTHERS_EMOJIS = &Permission{
|
||||
"manage_others_emojis",
|
||||
"authentication.permissions.manage_others_emojis.name",
|
||||
"authentication.permissions.manage_others_emojis.description",
|
||||
PERMISSION_SCOPE_TEAM,
|
||||
}
|
||||
PERMISSION_CREATE_POST = &Permission{
|
||||
"create_post",
|
||||
"authentication.permissions.create_post.name",
|
||||
@@ -424,6 +438,8 @@ func initializePermissions() {
|
||||
PERMISSION_MANAGE_OTHERS_WEBHOOKS,
|
||||
PERMISSION_MANAGE_OAUTH,
|
||||
PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH,
|
||||
PERMISSION_MANAGE_EMOJIS,
|
||||
PERMISSION_MANAGE_OTHERS_EMOJIS,
|
||||
PERMISSION_CREATE_POST,
|
||||
PERMISSION_CREATE_POST_PUBLIC,
|
||||
PERMISSION_CREATE_POST_EPHEMERAL,
|
||||
|
||||
@@ -39,6 +39,7 @@ type Role struct {
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
Permissions []string `json:"permissions"`
|
||||
SchemeManaged bool `json:"scheme_managed"`
|
||||
BuiltIn bool `json:"built_in"`
|
||||
}
|
||||
|
||||
type RolePatch struct {
|
||||
@@ -187,6 +188,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_USE_SLASH_COMMANDS.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[CHANNEL_ADMIN_ROLE_ID] = &Role{
|
||||
@@ -197,6 +199,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_MANAGE_CHANNEL_ROLES.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[TEAM_USER_ROLE_ID] = &Role{
|
||||
@@ -210,6 +213,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_VIEW_TEAM.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[TEAM_POST_ALL_ROLE_ID] = &Role{
|
||||
@@ -219,7 +223,8 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
Permissions: []string{
|
||||
PERMISSION_CREATE_POST.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
SchemeManaged: false,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[TEAM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
|
||||
@@ -229,7 +234,8 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
Permissions: []string{
|
||||
PERMISSION_CREATE_POST_PUBLIC.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
SchemeManaged: false,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[TEAM_ADMIN_ROLE_ID] = &Role{
|
||||
@@ -249,6 +255,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_MANAGE_WEBHOOKS.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[SYSTEM_USER_ROLE_ID] = &Role{
|
||||
@@ -261,6 +268,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_PERMANENT_DELETE_USER.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[SYSTEM_POST_ALL_ROLE_ID] = &Role{
|
||||
@@ -270,7 +278,8 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
Permissions: []string{
|
||||
PERMISSION_CREATE_POST.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
SchemeManaged: false,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[SYSTEM_POST_ALL_PUBLIC_ROLE_ID] = &Role{
|
||||
@@ -280,7 +289,8 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
Permissions: []string{
|
||||
PERMISSION_CREATE_POST_PUBLIC.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
SchemeManaged: false,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[SYSTEM_USER_ACCESS_TOKEN_ROLE_ID] = &Role{
|
||||
@@ -292,7 +302,8 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
PERMISSION_READ_USER_ACCESS_TOKEN.Id,
|
||||
PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
|
||||
},
|
||||
SchemeManaged: true,
|
||||
SchemeManaged: false,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
roles[SYSTEM_ADMIN_ROLE_ID] = &Role{
|
||||
@@ -345,6 +356,7 @@ func MakeDefaultRoles() map[string]*Role {
|
||||
roles[CHANNEL_ADMIN_ROLE_ID].Permissions...,
|
||||
),
|
||||
SchemeManaged: true,
|
||||
BuiltIn: true,
|
||||
}
|
||||
|
||||
return roles
|
||||
|
||||
192
model/scheme.go
Обычный файл
192
model/scheme.go
Обычный файл
@@ -0,0 +1,192 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
SCHEME_DISPLAY_NAME_MAX_LENGTH = 128
|
||||
SCHEME_NAME_MAX_LENGTH = 64
|
||||
SCHEME_DESCRIPTION_MAX_LENGTH = 1024
|
||||
SCHEME_SCOPE_TEAM = "team"
|
||||
SCHEME_SCOPE_CHANNEL = "channel"
|
||||
)
|
||||
|
||||
type Scheme struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Description string `json:"description"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
Scope string `json:"scope"`
|
||||
DefaultTeamAdminRole string `json:"default_team_admin_role"`
|
||||
DefaultTeamUserRole string `json:"default_team_user_role"`
|
||||
DefaultChannelAdminRole string `json:"default_channel_admin_role"`
|
||||
DefaultChannelUserRole string `json:"default_channel_user_role"`
|
||||
}
|
||||
|
||||
type SchemePatch struct {
|
||||
Name *string `json:"name"`
|
||||
DisplayName *string `json:"display_name"`
|
||||
Description *string `json:"description"`
|
||||
}
|
||||
|
||||
type SchemeIDPatch struct {
|
||||
SchemeID *string `json:"scheme_id"`
|
||||
}
|
||||
|
||||
// SchemeConveyor is used for importing and exporting a Scheme and its associated Roles.
|
||||
type SchemeConveyor struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Description string `json:"description"`
|
||||
Scope string `json:"scope"`
|
||||
TeamAdmin string `json:"default_team_admin_role"`
|
||||
TeamUser string `json:"default_team_user_role"`
|
||||
ChannelAdmin string `json:"default_channel_admin_role"`
|
||||
ChannelUser string `json:"default_channel_user_role"`
|
||||
Roles []*Role `json:"roles"`
|
||||
}
|
||||
|
||||
func (sc *SchemeConveyor) Scheme() *Scheme {
|
||||
return &Scheme{
|
||||
DisplayName: sc.DisplayName,
|
||||
Name: sc.Name,
|
||||
Description: sc.Description,
|
||||
Scope: sc.Scope,
|
||||
DefaultTeamAdminRole: sc.TeamAdmin,
|
||||
DefaultTeamUserRole: sc.TeamUser,
|
||||
DefaultChannelAdminRole: sc.ChannelAdmin,
|
||||
DefaultChannelUserRole: sc.ChannelUser,
|
||||
}
|
||||
}
|
||||
|
||||
func (scheme *Scheme) ToJson() string {
|
||||
b, _ := json.Marshal(scheme)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func SchemeFromJson(data io.Reader) *Scheme {
|
||||
var scheme *Scheme
|
||||
json.NewDecoder(data).Decode(&scheme)
|
||||
return scheme
|
||||
}
|
||||
|
||||
func SchemesToJson(schemes []*Scheme) string {
|
||||
b, _ := json.Marshal(schemes)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func SchemesFromJson(data io.Reader) []*Scheme {
|
||||
var schemes []*Scheme
|
||||
if err := json.NewDecoder(data).Decode(&schemes); err == nil {
|
||||
return schemes
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (scheme *Scheme) IsValid() bool {
|
||||
if len(scheme.Id) != 26 {
|
||||
return false
|
||||
}
|
||||
|
||||
return scheme.IsValidForCreate()
|
||||
}
|
||||
|
||||
func (scheme *Scheme) IsValidForCreate() bool {
|
||||
if len(scheme.DisplayName) == 0 || len(scheme.DisplayName) > SCHEME_DISPLAY_NAME_MAX_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
if !IsValidSchemeName(scheme.Name) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(scheme.Description) > SCHEME_DESCRIPTION_MAX_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
switch scheme.Scope {
|
||||
case SCHEME_SCOPE_TEAM, SCHEME_SCOPE_CHANNEL:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
if !IsValidRoleName(scheme.DefaultChannelAdminRole) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !IsValidRoleName(scheme.DefaultChannelUserRole) {
|
||||
return false
|
||||
}
|
||||
|
||||
if scheme.Scope == SCHEME_SCOPE_TEAM {
|
||||
if !IsValidRoleName(scheme.DefaultTeamAdminRole) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !IsValidRoleName(scheme.DefaultTeamUserRole) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if scheme.Scope == SCHEME_SCOPE_CHANNEL {
|
||||
if len(scheme.DefaultTeamAdminRole) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(scheme.DefaultTeamUserRole) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (scheme *Scheme) Patch(patch *SchemePatch) {
|
||||
if patch.DisplayName != nil {
|
||||
scheme.DisplayName = *patch.DisplayName
|
||||
}
|
||||
if patch.Name != nil {
|
||||
scheme.Name = *patch.Name
|
||||
}
|
||||
if patch.Description != nil {
|
||||
scheme.Description = *patch.Description
|
||||
}
|
||||
}
|
||||
|
||||
func (patch *SchemePatch) ToJson() string {
|
||||
b, _ := json.Marshal(patch)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func SchemePatchFromJson(data io.Reader) *SchemePatch {
|
||||
var patch *SchemePatch
|
||||
json.NewDecoder(data).Decode(&patch)
|
||||
return patch
|
||||
}
|
||||
|
||||
func SchemeIDFromJson(data io.Reader) *string {
|
||||
var p *SchemeIDPatch
|
||||
json.NewDecoder(data).Decode(&p)
|
||||
return p.SchemeID
|
||||
}
|
||||
|
||||
func (p *SchemeIDPatch) ToJson() string {
|
||||
b, _ := json.Marshal(p)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func IsValidSchemeName(name string) bool {
|
||||
re := regexp.MustCompile(fmt.Sprintf("^[a-z0-9_]{0,%d}$", SCHEME_NAME_MAX_LENGTH))
|
||||
return re.MatchString(name)
|
||||
}
|
||||
@@ -26,20 +26,21 @@ const (
|
||||
)
|
||||
|
||||
type Team struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
Type string `json:"type"`
|
||||
CompanyName string `json:"company_name"`
|
||||
AllowedDomains string `json:"allowed_domains"`
|
||||
InviteId string `json:"invite_id"`
|
||||
AllowOpenInvite bool `json:"allow_open_invite"`
|
||||
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Email string `json:"email"`
|
||||
Type string `json:"type"`
|
||||
CompanyName string `json:"company_name"`
|
||||
AllowedDomains string `json:"allowed_domains"`
|
||||
InviteId string `json:"invite_id"`
|
||||
AllowOpenInvite bool `json:"allow_open_invite"`
|
||||
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
|
||||
SchemeId *string `json:"scheme_id"`
|
||||
}
|
||||
|
||||
type TeamPatch struct {
|
||||
|
||||
@@ -11,10 +11,13 @@ import (
|
||||
)
|
||||
|
||||
type TeamMember struct {
|
||||
TeamId string `json:"team_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
TeamId string `json:"team_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
SchemeUser bool `json:"scheme_user"`
|
||||
SchemeAdmin bool `json:"scheme_admin"`
|
||||
ExplicitRoles string `json:"explicit_roles"`
|
||||
}
|
||||
|
||||
type TeamUnread struct {
|
||||
|
||||
@@ -24,6 +24,7 @@ type LayeredStore struct {
|
||||
TmpContext context.Context
|
||||
ReactionStore ReactionStore
|
||||
RoleStore RoleStore
|
||||
SchemeStore SchemeStore
|
||||
DatabaseLayer LayeredStoreDatabaseLayer
|
||||
LocalCacheLayer *LocalCacheSupplier
|
||||
RedisLayer *RedisSupplier
|
||||
@@ -39,6 +40,7 @@ func NewLayeredStore(db LayeredStoreDatabaseLayer, metrics einterfaces.MetricsIn
|
||||
|
||||
store.ReactionStore = &LayeredReactionStore{store}
|
||||
store.RoleStore = &LayeredRoleStore{store}
|
||||
store.SchemeStore = &LayeredSchemeStore{store}
|
||||
|
||||
// Setup the chain
|
||||
if ENABLE_EXPERIMENTAL_REDIS {
|
||||
@@ -167,6 +169,10 @@ func (s *LayeredStore) Role() RoleStore {
|
||||
return s.RoleStore
|
||||
}
|
||||
|
||||
func (s *LayeredStore) Scheme() SchemeStore {
|
||||
return s.SchemeStore
|
||||
}
|
||||
|
||||
func (s *LayeredStore) MarkSystemRanUnitTests() {
|
||||
s.DatabaseLayer.MarkSystemRanUnitTests()
|
||||
}
|
||||
@@ -253,8 +259,48 @@ func (s *LayeredRoleStore) GetByNames(names []string) StoreChannel {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredRoleStore) Delete(roldId string) StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.RoleDelete(s.TmpContext, roldId)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredRoleStore) PermanentDeleteAll() StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.RolePermanentDeleteAll(s.TmpContext)
|
||||
})
|
||||
}
|
||||
|
||||
type LayeredSchemeStore struct {
|
||||
*LayeredStore
|
||||
}
|
||||
|
||||
func (s *LayeredSchemeStore) Save(scheme *model.Scheme) StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.SchemeSave(s.TmpContext, scheme)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredSchemeStore) Get(schemeId string) StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.SchemeGet(s.TmpContext, schemeId)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredSchemeStore) Delete(schemeId string) StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.SchemeDelete(s.TmpContext, schemeId)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredSchemeStore) GetAllPage(scope string, offset int, limit int) StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.SchemeGetAllPage(s.TmpContext, scope, offset, limit)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LayeredSchemeStore) PermanentDeleteAll() StoreChannel {
|
||||
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
|
||||
return supplier.SchemePermanentDeleteAll(s.TmpContext)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,5 +35,13 @@ type LayeredStoreSupplier interface {
|
||||
RoleGet(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
RoleGetByName(ctx context.Context, name string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
RoleGetByNames(ctx context.Context, names []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
RoleDelete(ctx context.Context, roldId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
|
||||
// Schemes
|
||||
SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ const (
|
||||
ROLE_CACHE_SIZE = 20000
|
||||
ROLE_CACHE_SEC = 30 * 60
|
||||
|
||||
SCHEME_CACHE_SIZE = 20000
|
||||
SCHEME_CACHE_SEC = 30 * 60
|
||||
|
||||
CLEAR_CACHE_MESSAGE_DATA = ""
|
||||
)
|
||||
|
||||
@@ -25,6 +28,7 @@ type LocalCacheSupplier struct {
|
||||
next LayeredStoreSupplier
|
||||
reactionCache *utils.Cache
|
||||
roleCache *utils.Cache
|
||||
schemeCache *utils.Cache
|
||||
metrics einterfaces.MetricsInterface
|
||||
cluster einterfaces.ClusterInterface
|
||||
}
|
||||
@@ -33,6 +37,7 @@ func NewLocalCacheSupplier(metrics einterfaces.MetricsInterface, cluster einterf
|
||||
supplier := &LocalCacheSupplier{
|
||||
reactionCache: utils.NewLruWithParams(REACTION_CACHE_SIZE, "Reaction", REACTION_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_REACTIONS),
|
||||
roleCache: utils.NewLruWithParams(ROLE_CACHE_SIZE, "Role", ROLE_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES),
|
||||
schemeCache: utils.NewLruWithParams(SCHEME_CACHE_SIZE, "Scheme", SCHEME_CACHE_SEC, model.CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES),
|
||||
metrics: metrics,
|
||||
cluster: cluster,
|
||||
}
|
||||
|
||||
@@ -69,6 +69,17 @@ func (s *LocalCacheSupplier) RoleGetByNames(ctx context.Context, roleNames []str
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
result := s.Next().RoleDelete(ctx, roleId, hints...)
|
||||
|
||||
if result.Err == nil {
|
||||
role := result.Data.(*model.Role)
|
||||
s.doInvalidateCacheCluster(s.roleCache, role.Name)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
defer s.roleCache.Purge()
|
||||
defer s.doClearCacheCluster(s.roleCache)
|
||||
|
||||
54
store/local_cache_supplier_schemes.go
Обычный файл
54
store/local_cache_supplier_schemes.go
Обычный файл
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (s *LocalCacheSupplier) handleClusterInvalidateScheme(msg *model.ClusterMessage) {
|
||||
if msg.Data == CLEAR_CACHE_MESSAGE_DATA {
|
||||
s.schemeCache.Purge()
|
||||
} else {
|
||||
s.schemeCache.Remove(msg.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
if len(scheme.Id) != 0 {
|
||||
defer s.doInvalidateCacheCluster(s.schemeCache, scheme.Id)
|
||||
}
|
||||
return s.Next().SchemeSave(ctx, scheme, hints...)
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
if result := s.doStandardReadCache(ctx, s.schemeCache, schemeId, hints...); result != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
result := s.Next().SchemeGet(ctx, schemeId, hints...)
|
||||
|
||||
s.doStandardAddToCache(ctx, s.schemeCache, schemeId, result, hints...)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
defer s.doInvalidateCacheCluster(s.schemeCache, schemeId)
|
||||
defer s.doClearCacheCluster(s.roleCache)
|
||||
|
||||
return s.Next().SchemeDelete(ctx, schemeId, hints...)
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...)
|
||||
}
|
||||
|
||||
func (s *LocalCacheSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
defer s.doClearCacheCluster(s.schemeCache)
|
||||
|
||||
return s.Next().SchemePermanentDeleteAll(ctx, hints...)
|
||||
}
|
||||
@@ -83,6 +83,21 @@ func (s *RedisSupplier) RoleGetByNames(ctx context.Context, roleNames []string,
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) RoleDelete(ctx context.Context, roleId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
result := s.Next().RoleGet(ctx, roleId, hints...)
|
||||
|
||||
if result.Err == nil {
|
||||
role := result.Data.(*model.Role)
|
||||
key := buildRedisKeyForRoleName(role.Name)
|
||||
|
||||
if err := s.client.Del(key).Err(); err != nil {
|
||||
mlog.Error("Redis failed to remove key " + key + " Error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) RolePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
defer func() {
|
||||
if keys, err := s.client.Keys("roles:*").Result(); err != nil {
|
||||
|
||||
35
store/redis_supplier_schemes.go
Обычный файл
35
store/redis_supplier_schemes.go
Обычный файл
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (s *RedisSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
// TODO: Redis caching.
|
||||
return s.Next().SchemeSave(ctx, scheme, hints...)
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
// TODO: Redis caching.
|
||||
return s.Next().SchemeGet(ctx, schemeId, hints...)
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
// TODO: Redis caching.
|
||||
return s.Next().SchemeDelete(ctx, schemeId, hints...)
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
// TODO: Redis caching.
|
||||
return s.Next().SchemeGetAllPage(ctx, scope, offset, limit, hints...)
|
||||
}
|
||||
|
||||
func (s *RedisSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
|
||||
// TODO: Redis caching.
|
||||
return s.Next().SchemePermanentDeleteAll(ctx, hints...)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/gorp"
|
||||
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -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)
|
||||
@@ -713,6 +909,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
|
||||
@@ -747,14 +962,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
|
||||
@@ -768,38 +994,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()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -843,30 +1079,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 {
|
||||
@@ -887,17 +1130,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
|
||||
|
||||
@@ -1214,21 +1472,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()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1420,7 +1670,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 := ""
|
||||
|
||||
@@ -1435,11 +1685,98 @@ 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.ChannelList
|
||||
_, 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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// This function does the Advanced Permissions Phase 2 migration for ChannelMember objects. It performs the migration
|
||||
// in batches as a single transaction per batch to ensure consistency but to also minimise execution time to avoid
|
||||
// causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function
|
||||
// *after* the new Schemes functionality has been used on an installation will have unintended consequences.
|
||||
func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
var transaction *gorp.Transaction
|
||||
var err error
|
||||
|
||||
if transaction, err = s.GetMaster().Begin(); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var channelMembers []channelMember
|
||||
if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:FromChannelId, :FromUserId) ORDER BY ChannelId, UserId LIMIT 100", map[string]interface{}{"FromChannelId": fromChannelId, "FromUserId": fromUserId}); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.select.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if len(channelMembers) == 0 {
|
||||
// No more channel members in query result means that the migration has finished.
|
||||
return
|
||||
}
|
||||
|
||||
for _, member := range channelMembers {
|
||||
roles := strings.Fields(member.Roles)
|
||||
var newRoles []string
|
||||
member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true}
|
||||
member.SchemeUser = sql.NullBool{Bool: false, Valid: true}
|
||||
for _, role := range roles {
|
||||
if role == model.CHANNEL_ADMIN_ROLE_ID {
|
||||
member.SchemeAdmin = sql.NullBool{Bool: true, Valid: true}
|
||||
} else if role == model.CHANNEL_USER_ROLE_ID {
|
||||
member.SchemeUser = sql.NullBool{Bool: true, Valid: true}
|
||||
} else {
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
member.Roles = strings.Join(newRoles, " ")
|
||||
|
||||
if _, err := transaction.Update(&member); err != nil {
|
||||
if err2 := transaction.Rollback(); err2 != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.rollback_transaction.app_error", nil, err2.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.update.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := transaction.Commit(); err != nil {
|
||||
if err2 := transaction.Rollback(); err2 != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.rollback_transaction.app_error", nil, err2.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
result.Err = model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["ChannelId"] = channelMembers[len(channelMembers)-1].ChannelId
|
||||
data["UserId"] = channelMembers[len(channelMembers)-1].UserId
|
||||
result.Data = data
|
||||
})
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) ResetAllChannelSchemes() store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
if _, err := s.GetMaster().Exec("UPDATE Channels SET SchemeId=''"); err != nil {
|
||||
result.Err = model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
14
store/sqlstore/scheme_store_test.go
Обычный файл
14
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)
|
||||
}
|
||||
298
store/sqlstore/scheme_supplier.go
Обычный файл
298
store/sqlstore/scheme_supplier.go
Обычный файл
@@ -0,0 +1,298 @@
|
||||
// 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(model.SCHEME_NAME_MAX_LENGTH).SetUnique(true)
|
||||
table.ColMap("DisplayName").SetMaxSize(model.SCHEME_DISPLAY_NAME_MAX_LENGTH)
|
||||
table.ColMap("Description").SetMaxSize(model.SCHEME_DESCRIPTION_MAX_LENGTH)
|
||||
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()
|
||||
if len(scheme.Name) == 0 {
|
||||
scheme.Name = 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
|
||||
}
|
||||
|
||||
// Update any teams or channels using this scheme to the default scheme.
|
||||
if scheme.Scope == model.SCHEME_SCOPE_TEAM {
|
||||
if _, err := s.GetReplica().Exec("UPDATE Teams SET SchemeId = '' WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
|
||||
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.reset_teams.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
|
||||
return result
|
||||
}
|
||||
} else if scheme.Scope == model.SCHEME_SCOPE_CHANNEL {
|
||||
if _, err := s.GetReplica().Exec("UPDATE Channels SET SchemeId = '' WHERE SchemeId = :SchemeId", map[string]interface{}{"SchemeId": schemeId}); err != nil {
|
||||
result.Err = model.NewAppError("SqlSchemeStore.Delete", "store.sql_scheme.reset_channels.app_error", nil, "Id="+schemeId+", "+err.Error(), http.StatusInternalServerError)
|
||||
return result
|
||||
}
|
||||
|
||||
// Blow away the channel caches.
|
||||
s.Channel().ClearCaches()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func (s *SqlSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
result := store.NewSupplierResult()
|
||||
|
||||
var schemes []*model.Scheme
|
||||
|
||||
scopeClause := ""
|
||||
if len(scope) > 0 {
|
||||
scopeClause = " AND Scope=:Scope "
|
||||
}
|
||||
|
||||
if _, err := s.GetReplica().Select(&schemes, "SELECT * from Schemes WHERE DeleteAt = 0 "+scopeClause+" ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Limit": limit, "Offset": offset, "Scope": scope}); err != nil {
|
||||
result.Err = model.NewAppError("SqlSchemeStore.Get", "store.sql_scheme.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
result.Data = schemes
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SqlSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
result := store.NewSupplierResult()
|
||||
|
||||
if _, err := s.GetMaster().Exec("DELETE from Schemes"); err != nil {
|
||||
result.Err = model.NewAppError("SqlSchemeStore.PermanentDeleteAll", "store.sql_scheme.permanent_delete_all.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ type SqlSupplierOldStores struct {
|
||||
plugin store.PluginStore
|
||||
channelMemberHistory store.ChannelMemberHistoryStore
|
||||
role store.RoleStore
|
||||
scheme store.SchemeStore
|
||||
}
|
||||
|
||||
type SqlSupplier struct {
|
||||
@@ -141,6 +142,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
||||
|
||||
initSqlSupplierReactions(supplier)
|
||||
initSqlSupplierRoles(supplier)
|
||||
initSqlSupplierSchemes(supplier)
|
||||
|
||||
err := supplier.GetMaster().CreateTablesIfNotExists()
|
||||
if err != nil {
|
||||
@@ -493,6 +495,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 {
|
||||
mlog.Critical(fmt.Sprintf("Failed to create column %v", 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 {
|
||||
mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
|
||||
time.Sleep(time.Second)
|
||||
os.Exit(EXIT_CREATE_COLUMN_MYSQL)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
} else {
|
||||
mlog.Critical("Failed to create column because of missing driver")
|
||||
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) {
|
||||
@@ -865,6 +901,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()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/gorp"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
@@ -20,6 +22,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 +146,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)
|
||||
@@ -325,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
|
||||
@@ -353,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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -373,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)
|
||||
@@ -392,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()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -452,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 := ""
|
||||
|
||||
@@ -467,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()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -569,3 +714,91 @@ 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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// This function does the Advanced Permissions Phase 2 migration for TeamMember objects. It performs the migration
|
||||
// in batches as a single transaction per batch to ensure consistency but to also minimise execution time to avoid
|
||||
// causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function
|
||||
// *after* the new Schemes functionality has been used on an installation will have unintended consequences.
|
||||
func (s SqlTeamStore) MigrateTeamMembers(fromTeamId string, fromUserId string) store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
var transaction *gorp.Transaction
|
||||
var err error
|
||||
|
||||
if transaction, err = s.GetMaster().Begin(); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var teamMembers []teamMember
|
||||
if _, err := transaction.Select(&teamMembers, "SELECT * from TeamMembers WHERE (TeamId, UserId) > (:FromTeamId, :FromUserId) ORDER BY TeamId, UserId LIMIT 100", map[string]interface{}{"FromTeamId": fromTeamId, "FromUserId": fromUserId}); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.select.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if len(teamMembers) == 0 {
|
||||
// No more team members in query result means that the migration has finished.
|
||||
return
|
||||
}
|
||||
|
||||
for _, member := range teamMembers {
|
||||
roles := strings.Fields(member.Roles)
|
||||
var newRoles []string
|
||||
member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true}
|
||||
member.SchemeUser = sql.NullBool{Bool: false, Valid: true}
|
||||
for _, role := range roles {
|
||||
if role == model.TEAM_ADMIN_ROLE_ID {
|
||||
member.SchemeAdmin = sql.NullBool{Bool: true, Valid: true}
|
||||
} else if role == model.TEAM_USER_ROLE_ID {
|
||||
member.SchemeUser = sql.NullBool{Bool: true, Valid: true}
|
||||
} else {
|
||||
newRoles = append(newRoles, role)
|
||||
}
|
||||
}
|
||||
member.Roles = strings.Join(newRoles, " ")
|
||||
|
||||
if _, err := transaction.Update(&member); err != nil {
|
||||
if err2 := transaction.Rollback(); err2 != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.rollback_transaction.app_error", nil, err2.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.update.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := transaction.Commit(); err != nil {
|
||||
if err2 := transaction.Rollback(); err2 != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.rollback_transaction.app_error", nil, err2.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
result.Err = model.NewAppError("SqlTeamStore.MigrateTeamMembers", "store.sql_team.migrate_team_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["TeamId"] = teamMembers[len(teamMembers)-1].TeamId
|
||||
data["UserId"] = teamMembers[len(teamMembers)-1].UserId
|
||||
result.Data = data
|
||||
})
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) ResetAllTeamSchemes() store.StoreChannel {
|
||||
return store.Do(func(result *store.StoreResult) {
|
||||
if _, err := s.GetMaster().Exec("UPDATE Teams SET SchemeId=''"); err != nil {
|
||||
result.Err = model.NewAppError("SqlTeamStore.ResetAllTeamSchemes", "store.sql_team.reset_all_team_schemes.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -425,8 +425,24 @@ func UpgradeDatabaseToVersion410(sqlStore SqlStore) {
|
||||
}
|
||||
|
||||
func UpgradeDatabaseToVersion50(sqlStore SqlStore) {
|
||||
// TODO: Uncomment following condition when version 3.10.0 is released
|
||||
// This version of Mattermost includes an App-Layer migration which migrates from hard-coded emojis configured
|
||||
// in `config.json` to a `Permission` in the database. The migration code can be seen
|
||||
// in the file `app/app.go` in the function `DoEmojisPermissionsMigration()`.
|
||||
|
||||
// TODO: Uncomment following condition when version 5.0.0 is released
|
||||
//if shouldPerformUpgrade(sqlStore, VERSION_4_10_0, VERSION_5_0_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')")
|
||||
sqlStore.CreateColumnIfNotExists("IncomingWebhooks", "ChannelLocked", "boolean", "boolean", "0")
|
||||
|
||||
// saveSchemaVersion(sqlStore, VERSION_5_0_0)
|
||||
|
||||
@@ -60,6 +60,7 @@ type Store interface {
|
||||
FileInfo() FileInfoStore
|
||||
Reaction() ReactionStore
|
||||
Role() RoleStore
|
||||
Scheme() SchemeStore
|
||||
Job() JobStore
|
||||
UserAccessToken() UserAccessTokenStore
|
||||
ChannelMemberHistory() ChannelMemberHistoryStore
|
||||
@@ -103,6 +104,9 @@ type TeamStore interface {
|
||||
RemoveAllMembersByTeam(teamId string) StoreChannel
|
||||
RemoveAllMembersByUser(userId string) StoreChannel
|
||||
UpdateLastTeamIconUpdate(teamId string, curTime int64) StoreChannel
|
||||
GetTeamsByScheme(schemeId string, offset int, limit int) StoreChannel
|
||||
MigrateTeamMembers(fromTeamId string, fromUserId string) StoreChannel
|
||||
ResetAllTeamSchemes() StoreChannel
|
||||
}
|
||||
|
||||
type ChannelStore interface {
|
||||
@@ -160,6 +164,9 @@ type ChannelStore interface {
|
||||
AnalyticsDeletedTypeCount(teamId string, channelType string) StoreChannel
|
||||
GetChannelUnread(channelId, userId string) StoreChannel
|
||||
ClearCaches()
|
||||
GetChannelsByScheme(schemeId string, offset int, limit int) StoreChannel
|
||||
MigrateChannelMembers(fromChannelId string, fromUserId string) StoreChannel
|
||||
ResetAllChannelSchemes() StoreChannel
|
||||
}
|
||||
|
||||
type ChannelMemberHistoryStore interface {
|
||||
@@ -475,5 +482,14 @@ type RoleStore interface {
|
||||
Get(roleId string) StoreChannel
|
||||
GetByName(name string) StoreChannel
|
||||
GetByNames(names []string) StoreChannel
|
||||
Delete(roldId string) StoreChannel
|
||||
PermanentDeleteAll() StoreChannel
|
||||
}
|
||||
|
||||
type SchemeStore interface {
|
||||
Save(scheme *model.Scheme) StoreChannel
|
||||
Get(schemeId string) StoreChannel
|
||||
GetAllPage(scope string, offset int, limit int) StoreChannel
|
||||
Delete(schemeId string) StoreChannel
|
||||
PermanentDeleteAll() StoreChannel
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package storetest
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -16,6 +17,8 @@ import (
|
||||
)
|
||||
|
||||
func TestChannelStore(t *testing.T, ss store.Store) {
|
||||
createDefaultRoles(t, ss)
|
||||
|
||||
t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) })
|
||||
t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) })
|
||||
t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) })
|
||||
@@ -49,6 +52,10 @@ func TestChannelStore(t *testing.T, ss store.Store) {
|
||||
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
|
||||
t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) })
|
||||
t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) })
|
||||
t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) })
|
||||
t.Run("MigrateChannelMembers", func(t *testing.T) { testChannelStoreMigrateChannelMembers(t, ss) })
|
||||
t.Run("ResetAllChannelSchemes", func(t *testing.T) { testResetAllChannelSchemes(t, ss) })
|
||||
|
||||
}
|
||||
|
||||
func testChannelStoreSave(t *testing.T, ss store.Store) {
|
||||
@@ -2171,3 +2178,183 @@ func testChannelStoreMaxChannelsPerTeam(t *testing.T, ss store.Store) {
|
||||
result = <-ss.Channel().Save(channel, 1)
|
||||
assert.Nil(t, result.Err)
|
||||
}
|
||||
|
||||
func testChannelStoreGetChannelsByScheme(t *testing.T, ss store.Store) {
|
||||
// Create some schemes.
|
||||
s1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
|
||||
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
|
||||
|
||||
// Create and save some teams.
|
||||
c1 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
c2 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
c3 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
}
|
||||
|
||||
c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel)
|
||||
c2 = (<-ss.Channel().Save(c2, 100)).Data.(*model.Channel)
|
||||
c3 = (<-ss.Channel().Save(c3, 100)).Data.(*model.Channel)
|
||||
|
||||
// Get the channels by a valid Scheme ID.
|
||||
res1 := <-ss.Channel().GetChannelsByScheme(s1.Id, 0, 100)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.(model.ChannelList)
|
||||
assert.Len(t, d1, 2)
|
||||
|
||||
// Get the channels by a valid Scheme ID where there aren't any matching Channel.
|
||||
res2 := <-ss.Channel().GetChannelsByScheme(s2.Id, 0, 100)
|
||||
assert.Nil(t, res2.Err)
|
||||
d2 := res2.Data.(model.ChannelList)
|
||||
assert.Len(t, d2, 0)
|
||||
|
||||
// Get the channels by an invalid Scheme ID.
|
||||
res3 := <-ss.Channel().GetChannelsByScheme(model.NewId(), 0, 100)
|
||||
assert.Nil(t, res3.Err)
|
||||
d3 := res3.Data.(model.ChannelList)
|
||||
assert.Len(t, d3, 0)
|
||||
}
|
||||
|
||||
func testChannelStoreMigrateChannelMembers(t *testing.T, ss store.Store) {
|
||||
s1 := model.NewId()
|
||||
c1 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1,
|
||||
}
|
||||
c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel)
|
||||
|
||||
cm1 := &model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "channel_admin channel_user",
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
cm2 := &model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "channel_user",
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
cm3 := &model.ChannelMember{
|
||||
ChannelId: c1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "something_else",
|
||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||
}
|
||||
|
||||
cm1 = (<-ss.Channel().SaveMember(cm1)).Data.(*model.ChannelMember)
|
||||
cm2 = (<-ss.Channel().SaveMember(cm2)).Data.(*model.ChannelMember)
|
||||
cm3 = (<-ss.Channel().SaveMember(cm3)).Data.(*model.ChannelMember)
|
||||
|
||||
lastDoneChannelId := strings.Repeat("0", 26)
|
||||
lastDoneUserId := strings.Repeat("0", 26)
|
||||
|
||||
for {
|
||||
res := <-ss.Channel().MigrateChannelMembers(lastDoneChannelId, lastDoneUserId)
|
||||
if assert.Nil(t, res.Err) {
|
||||
if res.Data == nil {
|
||||
break
|
||||
}
|
||||
data := res.Data.(map[string]string)
|
||||
lastDoneChannelId = data["ChannelId"]
|
||||
lastDoneUserId = data["UserId"]
|
||||
}
|
||||
}
|
||||
|
||||
ss.Channel().ClearCaches()
|
||||
|
||||
res1 := <-ss.Channel().GetMember(cm1.ChannelId, cm1.UserId)
|
||||
assert.Nil(t, res1.Err)
|
||||
cm1b := res1.Data.(*model.ChannelMember)
|
||||
assert.Equal(t, "", cm1b.ExplicitRoles)
|
||||
assert.True(t, cm1b.SchemeUser)
|
||||
assert.True(t, cm1b.SchemeAdmin)
|
||||
|
||||
res2 := <-ss.Channel().GetMember(cm2.ChannelId, cm2.UserId)
|
||||
assert.Nil(t, res2.Err)
|
||||
cm2b := res2.Data.(*model.ChannelMember)
|
||||
assert.Equal(t, "", cm2b.ExplicitRoles)
|
||||
assert.True(t, cm2b.SchemeUser)
|
||||
assert.False(t, cm2b.SchemeAdmin)
|
||||
|
||||
res3 := <-ss.Channel().GetMember(cm3.ChannelId, cm3.UserId)
|
||||
assert.Nil(t, res3.Err)
|
||||
cm3b := res3.Data.(*model.ChannelMember)
|
||||
assert.Equal(t, "something_else", cm3b.ExplicitRoles)
|
||||
assert.False(t, cm3b.SchemeUser)
|
||||
assert.False(t, cm3b.SchemeAdmin)
|
||||
}
|
||||
|
||||
func testResetAllChannelSchemes(t *testing.T, ss store.Store) {
|
||||
s1 := &model.Scheme{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
|
||||
|
||||
c1 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
c2 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: "Name",
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
c1 = (<-ss.Channel().Save(c1, 100)).Data.(*model.Channel)
|
||||
c2 = (<-ss.Channel().Save(c2, 100)).Data.(*model.Channel)
|
||||
|
||||
assert.Equal(t, s1.Id, *c1.SchemeId)
|
||||
assert.Equal(t, s1.Id, *c2.SchemeId)
|
||||
|
||||
res := <-ss.Channel().ResetAllChannelSchemes()
|
||||
assert.Nil(t, res.Err)
|
||||
|
||||
c1 = (<-ss.Channel().Get(c1.Id, true)).Data.(*model.Channel)
|
||||
c2 = (<-ss.Channel().Get(c2.Id, true)).Data.(*model.Channel)
|
||||
|
||||
assert.Equal(t, "", *c1.SchemeId)
|
||||
assert.Equal(t, "", *c2.SchemeId)
|
||||
}
|
||||
|
||||
@@ -258,6 +258,22 @@ func (_m *ChannelStore) GetChannels(teamId string, userId string) store.StoreCha
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetChannelsByScheme provides a mock function with given fields: schemeId, offset, limit
|
||||
func (_m *ChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
|
||||
ret := _m.Called(schemeId, offset, limit)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
|
||||
r0 = rf(schemeId, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetDeleted provides a mock function with given fields: team_id, offset, limit
|
||||
func (_m *ChannelStore) GetDeleted(team_id string, offset int, limit int) store.StoreChannel {
|
||||
ret := _m.Called(team_id, offset, limit)
|
||||
@@ -567,6 +583,22 @@ func (_m *ChannelStore) IsUserInChannelUseCache(userId string, channelId string)
|
||||
return r0
|
||||
}
|
||||
|
||||
// MigrateChannelMembers provides a mock function with given fields: fromChannelId, fromUserId
|
||||
func (_m *ChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId string) store.StoreChannel {
|
||||
ret := _m.Called(fromChannelId, fromUserId)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
|
||||
r0 = rf(fromChannelId, fromUserId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PermanentDelete provides a mock function with given fields: channelId
|
||||
func (_m *ChannelStore) PermanentDelete(channelId string) store.StoreChannel {
|
||||
ret := _m.Called(channelId)
|
||||
@@ -647,6 +679,22 @@ func (_m *ChannelStore) RemoveMember(channelId string, userId string) store.Stor
|
||||
return r0
|
||||
}
|
||||
|
||||
// ResetAllChannelSchemes provides a mock function with given fields:
|
||||
func (_m *ChannelStore) ResetAllChannelSchemes() store.StoreChannel {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func() store.StoreChannel); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Restore provides a mock function with given fields: channelId, time
|
||||
func (_m *ChannelStore) Restore(channelId string, time int64) store.StoreChannel {
|
||||
ret := _m.Called(channelId, time)
|
||||
|
||||
@@ -432,6 +432,29 @@ func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, roldId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, roldId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RoleGet provides a mock function with given fields: ctx, roleId, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
@@ -547,6 +570,137 @@ func (_m *LayeredStoreDatabaseLayer) RoleSave(ctx context.Context, role *model.R
|
||||
return r0
|
||||
}
|
||||
|
||||
// Scheme provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) Scheme() store.SchemeStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.SchemeStore
|
||||
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.SchemeStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, schemeId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, schemeId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, schemeId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, schemeId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeGetAllPage provides a mock function with given fields: ctx, scope, offset, limit, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, scope, offset, limit)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, scope, offset, limit, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemePermanentDeleteAll provides a mock function with given fields: ctx, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
|
||||
func (_m *LayeredStoreDatabaseLayer) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, scheme)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, scheme, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Session provides a mock function with given fields:
|
||||
func (_m *LayeredStoreDatabaseLayer) Session() store.SessionStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -145,6 +145,29 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode
|
||||
return r0
|
||||
}
|
||||
|
||||
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
|
||||
func (_m *LayeredStoreSupplier) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, roldId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, roldId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RoleGet provides a mock function with given fields: ctx, roleId, hints
|
||||
func (_m *LayeredStoreSupplier) RoleGet(ctx context.Context, roleId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
@@ -260,6 +283,121 @@ func (_m *LayeredStoreSupplier) RoleSave(ctx context.Context, role *model.Role,
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeDelete provides a mock function with given fields: ctx, schemeId, hints
|
||||
func (_m *LayeredStoreSupplier) SchemeDelete(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, schemeId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, schemeId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeGet provides a mock function with given fields: ctx, schemeId, hints
|
||||
func (_m *LayeredStoreSupplier) SchemeGet(ctx context.Context, schemeId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, schemeId)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, schemeId, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeGetAllPage provides a mock function with given fields: ctx, scope, offset, limit, hints
|
||||
func (_m *LayeredStoreSupplier) SchemeGetAllPage(ctx context.Context, scope string, offset int, limit int, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, scope, offset, limit)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, int, int, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, scope, offset, limit, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemePermanentDeleteAll provides a mock function with given fields: ctx, hints
|
||||
func (_m *LayeredStoreSupplier) SchemePermanentDeleteAll(ctx context.Context, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SchemeSave provides a mock function with given fields: ctx, scheme, hints
|
||||
func (_m *LayeredStoreSupplier) SchemeSave(ctx context.Context, scheme *model.Scheme, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
|
||||
_va := make([]interface{}, len(hints))
|
||||
for _i := range hints {
|
||||
_va[_i] = hints[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, scheme)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 *store.LayeredStoreSupplierResult
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.Scheme, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
|
||||
r0 = rf(ctx, scheme, hints...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetChainNext provides a mock function with given fields: _a0
|
||||
func (_m *LayeredStoreSupplier) SetChainNext(_a0 store.LayeredStoreSupplier) {
|
||||
_m.Called(_a0)
|
||||
|
||||
@@ -13,6 +13,22 @@ type RoleStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: roldId
|
||||
func (_m *RoleStore) Delete(roldId string) store.StoreChannel {
|
||||
ret := _m.Called(roldId)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||
r0 = rf(roldId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: roleId
|
||||
func (_m *RoleStore) Get(roleId string) store.StoreChannel {
|
||||
ret := _m.Called(roleId)
|
||||
|
||||
94
store/storetest/mocks/SchemeStore.go
Обычный файл
94
store/storetest/mocks/SchemeStore.go
Обычный файл
@@ -0,0 +1,94 @@
|
||||
// Code generated by mockery v1.0.0
|
||||
|
||||
// Regenerate this file using `make store-mocks`.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import model "github.com/mattermost/mattermost-server/model"
|
||||
import store "github.com/mattermost/mattermost-server/store"
|
||||
|
||||
// SchemeStore is an autogenerated mock type for the SchemeStore type
|
||||
type SchemeStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: schemeId
|
||||
func (_m *SchemeStore) Delete(schemeId string) store.StoreChannel {
|
||||
ret := _m.Called(schemeId)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||
r0 = rf(schemeId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: schemeId
|
||||
func (_m *SchemeStore) Get(schemeId string) store.StoreChannel {
|
||||
ret := _m.Called(schemeId)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||
r0 = rf(schemeId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetAllPage provides a mock function with given fields: scope, offset, limit
|
||||
func (_m *SchemeStore) GetAllPage(scope string, offset int, limit int) store.StoreChannel {
|
||||
ret := _m.Called(scope, offset, limit)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
|
||||
r0 = rf(scope, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PermanentDeleteAll provides a mock function with given fields:
|
||||
func (_m *SchemeStore) PermanentDeleteAll() store.StoreChannel {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func() store.StoreChannel); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: scheme
|
||||
func (_m *SchemeStore) Save(scheme *model.Scheme) store.StoreChannel {
|
||||
ret := _m.Called(scheme)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(*model.Scheme) store.StoreChannel); ok {
|
||||
r0 = rf(scheme)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
@@ -143,6 +143,20 @@ func (_m *SqlStore) CreateColumnIfNotExists(tableName string, columnName string,
|
||||
return r0
|
||||
}
|
||||
|
||||
// CreateColumnIfNotExistsNoDefault provides a mock function with given fields: tableName, columnName, mySqlColType, postgresColType
|
||||
func (_m *SqlStore) CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
|
||||
ret := _m.Called(tableName, columnName, mySqlColType, postgresColType)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(string, string, string, string) bool); ok {
|
||||
r0 = rf(tableName, columnName, mySqlColType, postgresColType)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CreateCompositeIndexIfNotExists provides a mock function with given fields: indexName, tableName, columnNames
|
||||
func (_m *SqlStore) CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool {
|
||||
ret := _m.Called(indexName, tableName, columnNames)
|
||||
@@ -554,6 +568,22 @@ func (_m *SqlStore) Role() store.RoleStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Scheme provides a mock function with given fields:
|
||||
func (_m *SqlStore) Scheme() store.SchemeStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.SchemeStore
|
||||
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.SchemeStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Session provides a mock function with given fields:
|
||||
func (_m *SqlStore) Session() store.SessionStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -299,6 +299,22 @@ func (_m *Store) Role() store.RoleStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// Scheme provides a mock function with given fields:
|
||||
func (_m *Store) Scheme() store.SchemeStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.SchemeStore
|
||||
if rf, ok := ret.Get(0).(func() store.SchemeStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.SchemeStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Session provides a mock function with given fields:
|
||||
func (_m *Store) Session() store.SessionStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -237,6 +237,22 @@ func (_m *TeamStore) GetMembersByIds(teamId string, userIds []string) store.Stor
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetTeamsByScheme provides a mock function with given fields: schemeId, offset, limit
|
||||
func (_m *TeamStore) GetTeamsByScheme(schemeId string, offset int, limit int) store.StoreChannel {
|
||||
ret := _m.Called(schemeId, offset, limit)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string, int, int) store.StoreChannel); ok {
|
||||
r0 = rf(schemeId, offset, limit)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetTeamsByUserId provides a mock function with given fields: userId
|
||||
func (_m *TeamStore) GetTeamsByUserId(userId string) store.StoreChannel {
|
||||
ret := _m.Called(userId)
|
||||
@@ -285,6 +301,22 @@ func (_m *TeamStore) GetTotalMemberCount(teamId string) store.StoreChannel {
|
||||
return r0
|
||||
}
|
||||
|
||||
// MigrateTeamMembers provides a mock function with given fields: fromTeamId, fromUserId
|
||||
func (_m *TeamStore) MigrateTeamMembers(fromTeamId string, fromUserId string) store.StoreChannel {
|
||||
ret := _m.Called(fromTeamId, fromUserId)
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
|
||||
r0 = rf(fromTeamId, fromUserId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PermanentDelete provides a mock function with given fields: teamId
|
||||
func (_m *TeamStore) PermanentDelete(teamId string) store.StoreChannel {
|
||||
ret := _m.Called(teamId)
|
||||
@@ -349,6 +381,22 @@ func (_m *TeamStore) RemoveMember(teamId string, userId string) store.StoreChann
|
||||
return r0
|
||||
}
|
||||
|
||||
// ResetAllTeamSchemes provides a mock function with given fields:
|
||||
func (_m *TeamStore) ResetAllTeamSchemes() store.StoreChannel {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.StoreChannel
|
||||
if rf, ok := ret.Get(0).(func() store.StoreChannel); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.StoreChannel)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: team
|
||||
func (_m *TeamStore) Save(team *model.Team) store.StoreChannel {
|
||||
ret := _m.Called(team)
|
||||
|
||||
@@ -17,6 +17,7 @@ func TestRoleStore(t *testing.T, ss store.Store) {
|
||||
t.Run("Get", func(t *testing.T) { testRoleStoreGet(t, ss) })
|
||||
t.Run("GetByName", func(t *testing.T) { testRoleStoreGetByName(t, ss) })
|
||||
t.Run("GetNames", func(t *testing.T) { testRoleStoreGetByNames(t, ss) })
|
||||
t.Run("Delete", func(t *testing.T) { testRoleStoreDelete(t, ss) })
|
||||
t.Run("PermanentDeleteAll", func(t *testing.T) { testRoleStorePermanentDeleteAll(t, ss) })
|
||||
}
|
||||
|
||||
@@ -244,6 +245,49 @@ func testRoleStoreGetByNames(t *testing.T, ss store.Store) {
|
||||
assert.NotContains(t, roles6, d3)
|
||||
}
|
||||
|
||||
func testRoleStoreDelete(t *testing.T, ss store.Store) {
|
||||
// Save a role to test with.
|
||||
r1 := &model.Role{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Permissions: []string{
|
||||
"invite_user",
|
||||
"create_public_channel",
|
||||
"add_user_to_team",
|
||||
},
|
||||
SchemeManaged: false,
|
||||
}
|
||||
|
||||
res1 := <-ss.Role().Save(r1)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.(*model.Role)
|
||||
assert.Len(t, d1.Id, 26)
|
||||
|
||||
// Check the role is there.
|
||||
res2 := <-ss.Role().Get(d1.Id)
|
||||
assert.Nil(t, res2.Err)
|
||||
|
||||
// Delete the role.
|
||||
res3 := <-ss.Role().Delete(d1.Id)
|
||||
assert.Nil(t, res3.Err)
|
||||
|
||||
// Check the role is deleted there.
|
||||
res4 := <-ss.Role().Get(d1.Id)
|
||||
assert.Nil(t, res4.Err)
|
||||
d2 := res4.Data.(*model.Role)
|
||||
assert.NotZero(t, d2.DeleteAt)
|
||||
|
||||
res5 := <-ss.Role().GetByName(d1.Name)
|
||||
assert.Nil(t, res5.Err)
|
||||
d3 := res5.Data.(*model.Role)
|
||||
assert.NotZero(t, d3.DeleteAt)
|
||||
|
||||
// Try and delete a role that does not exist.
|
||||
res6 := <-ss.Role().Delete(model.NewId())
|
||||
assert.NotNil(t, res6.Err)
|
||||
}
|
||||
|
||||
func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
|
||||
r1 := &model.Role{
|
||||
Name: model.NewId(),
|
||||
@@ -256,6 +300,7 @@ func testRoleStorePermanentDeleteAll(t *testing.T, ss store.Store) {
|
||||
},
|
||||
SchemeManaged: false,
|
||||
}
|
||||
|
||||
r2 := &model.Role{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
|
||||
425
store/storetest/scheme_store.go
Обычный файл
425
store/storetest/scheme_store.go
Обычный файл
@@ -0,0 +1,425 @@
|
||||
// Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
func TestSchemeStore(t *testing.T, ss store.Store) {
|
||||
createDefaultRoles(t, ss)
|
||||
|
||||
t.Run("Save", func(t *testing.T) { testSchemeStoreSave(t, ss) })
|
||||
t.Run("Get", func(t *testing.T) { testSchemeStoreGet(t, ss) })
|
||||
t.Run("GetAllPage", func(t *testing.T) { testSchemeStoreGetAllPage(t, ss) })
|
||||
t.Run("Delete", func(t *testing.T) { testSchemeStoreDelete(t, ss) })
|
||||
t.Run("PermanentDeleteAll", func(t *testing.T) { testSchemeStorePermanentDeleteAll(t, ss) })
|
||||
}
|
||||
|
||||
func createDefaultRoles(t *testing.T, ss store.Store) {
|
||||
<-ss.Role().Save(&model.Role{
|
||||
Name: model.TEAM_ADMIN_ROLE_ID,
|
||||
DisplayName: model.TEAM_ADMIN_ROLE_ID,
|
||||
Permissions: []string{
|
||||
model.PERMISSION_EDIT_OTHERS_POSTS.Id,
|
||||
model.PERMISSION_DELETE_OTHERS_POSTS.Id,
|
||||
},
|
||||
})
|
||||
|
||||
<-ss.Role().Save(&model.Role{
|
||||
Name: model.TEAM_USER_ROLE_ID,
|
||||
DisplayName: model.TEAM_USER_ROLE_ID,
|
||||
Permissions: []string{
|
||||
model.PERMISSION_VIEW_TEAM.Id,
|
||||
model.PERMISSION_ADD_USER_TO_TEAM.Id,
|
||||
},
|
||||
})
|
||||
|
||||
<-ss.Role().Save(&model.Role{
|
||||
Name: model.CHANNEL_ADMIN_ROLE_ID,
|
||||
DisplayName: model.CHANNEL_ADMIN_ROLE_ID,
|
||||
Permissions: []string{
|
||||
model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
|
||||
model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
|
||||
},
|
||||
})
|
||||
|
||||
<-ss.Role().Save(&model.Role{
|
||||
Name: model.CHANNEL_USER_ROLE_ID,
|
||||
DisplayName: model.CHANNEL_USER_ROLE_ID,
|
||||
Permissions: []string{
|
||||
model.PERMISSION_READ_CHANNEL.Id,
|
||||
model.PERMISSION_CREATE_POST.Id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testSchemeStoreSave(t *testing.T, ss store.Store) {
|
||||
// Save a new scheme.
|
||||
s1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
// Check all fields saved correctly.
|
||||
res1 := <-ss.Scheme().Save(s1)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.(*model.Scheme)
|
||||
assert.Len(t, d1.Id, 26)
|
||||
assert.Equal(t, s1.DisplayName, d1.DisplayName)
|
||||
assert.Equal(t, s1.Name, d1.Name)
|
||||
assert.Equal(t, s1.Description, d1.Description)
|
||||
assert.NotZero(t, d1.CreateAt)
|
||||
assert.NotZero(t, d1.UpdateAt)
|
||||
assert.Zero(t, d1.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, d1.Scope)
|
||||
assert.Len(t, d1.DefaultTeamAdminRole, 26)
|
||||
assert.Len(t, d1.DefaultTeamUserRole, 26)
|
||||
assert.Len(t, d1.DefaultChannelAdminRole, 26)
|
||||
assert.Len(t, d1.DefaultChannelUserRole, 26)
|
||||
|
||||
// Check the default roles were created correctly.
|
||||
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
|
||||
assert.Nil(t, roleRes1.Err)
|
||||
role1 := roleRes1.Data.(*model.Role)
|
||||
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
|
||||
assert.True(t, role1.SchemeManaged)
|
||||
|
||||
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
|
||||
assert.Nil(t, roleRes2.Err)
|
||||
role2 := roleRes2.Data.(*model.Role)
|
||||
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
|
||||
assert.True(t, role2.SchemeManaged)
|
||||
|
||||
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
|
||||
assert.Nil(t, roleRes3.Err)
|
||||
role3 := roleRes3.Data.(*model.Role)
|
||||
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
|
||||
assert.True(t, role3.SchemeManaged)
|
||||
|
||||
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
|
||||
assert.Nil(t, roleRes4.Err)
|
||||
role4 := roleRes4.Data.(*model.Role)
|
||||
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
|
||||
assert.True(t, role4.SchemeManaged)
|
||||
|
||||
// Change the scheme description and update.
|
||||
d1.Description = model.NewId()
|
||||
|
||||
res2 := <-ss.Scheme().Save(d1)
|
||||
assert.Nil(t, res2.Err)
|
||||
d2 := res2.Data.(*model.Scheme)
|
||||
assert.Equal(t, d1.Id, d2.Id)
|
||||
assert.Equal(t, s1.DisplayName, d2.DisplayName)
|
||||
assert.Equal(t, s1.Name, d2.Name)
|
||||
assert.Equal(t, d1.Description, d2.Description)
|
||||
assert.NotZero(t, d2.CreateAt)
|
||||
assert.NotZero(t, d2.UpdateAt)
|
||||
assert.Zero(t, d2.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, d2.Scope)
|
||||
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
|
||||
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
|
||||
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
|
||||
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
|
||||
|
||||
// Try saving one with an invalid ID set.
|
||||
s3 := &model.Scheme{
|
||||
Id: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
res3 := <-ss.Scheme().Save(s3)
|
||||
assert.NotNil(t, res3.Err)
|
||||
}
|
||||
|
||||
func testSchemeStoreGet(t *testing.T, ss store.Store) {
|
||||
// Save a scheme to test with.
|
||||
s1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
res1 := <-ss.Scheme().Save(s1)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.(*model.Scheme)
|
||||
assert.Len(t, d1.Id, 26)
|
||||
|
||||
// Get a valid scheme
|
||||
res2 := <-ss.Scheme().Get(d1.Id)
|
||||
assert.Nil(t, res2.Err)
|
||||
d2 := res1.Data.(*model.Scheme)
|
||||
assert.Equal(t, d1.Id, d2.Id)
|
||||
assert.Equal(t, s1.DisplayName, d2.DisplayName)
|
||||
assert.Equal(t, s1.Name, d2.Name)
|
||||
assert.Equal(t, d1.Description, d2.Description)
|
||||
assert.NotZero(t, d2.CreateAt)
|
||||
assert.NotZero(t, d2.UpdateAt)
|
||||
assert.Zero(t, d2.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, d2.Scope)
|
||||
assert.Equal(t, d1.DefaultTeamAdminRole, d2.DefaultTeamAdminRole)
|
||||
assert.Equal(t, d1.DefaultTeamUserRole, d2.DefaultTeamUserRole)
|
||||
assert.Equal(t, d1.DefaultChannelAdminRole, d2.DefaultChannelAdminRole)
|
||||
assert.Equal(t, d1.DefaultChannelUserRole, d2.DefaultChannelUserRole)
|
||||
|
||||
// Get an invalid scheme
|
||||
res3 := <-ss.Scheme().Get(model.NewId())
|
||||
assert.NotNil(t, res3.Err)
|
||||
}
|
||||
|
||||
func testSchemeStoreGetAllPage(t *testing.T, ss store.Store) {
|
||||
// Save a scheme to test with.
|
||||
schemes := []*model.Scheme{
|
||||
{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
},
|
||||
{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
},
|
||||
{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
},
|
||||
{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
},
|
||||
}
|
||||
|
||||
for _, scheme := range schemes {
|
||||
store.Must(ss.Scheme().Save(scheme))
|
||||
}
|
||||
|
||||
r1 := <-ss.Scheme().GetAllPage("", 0, 2)
|
||||
assert.Nil(t, r1.Err)
|
||||
s1 := r1.Data.([]*model.Scheme)
|
||||
assert.Len(t, s1, 2)
|
||||
|
||||
r2 := <-ss.Scheme().GetAllPage("", 2, 2)
|
||||
assert.Nil(t, r2.Err)
|
||||
s2 := r2.Data.([]*model.Scheme)
|
||||
assert.Len(t, s2, 2)
|
||||
assert.NotEqual(t, s1[0].DisplayName, s2[0].DisplayName)
|
||||
assert.NotEqual(t, s1[0].DisplayName, s2[1].DisplayName)
|
||||
assert.NotEqual(t, s1[1].DisplayName, s2[0].DisplayName)
|
||||
assert.NotEqual(t, s1[1].DisplayName, s2[1].DisplayName)
|
||||
assert.NotEqual(t, s1[0].Name, s2[0].Name)
|
||||
assert.NotEqual(t, s1[0].Name, s2[1].Name)
|
||||
assert.NotEqual(t, s1[1].Name, s2[0].Name)
|
||||
assert.NotEqual(t, s1[1].Name, s2[1].Name)
|
||||
|
||||
r3 := <-ss.Scheme().GetAllPage("team", 0, 1000)
|
||||
assert.Nil(t, r3.Err)
|
||||
s3 := r3.Data.([]*model.Scheme)
|
||||
assert.NotZero(t, len(s3))
|
||||
for _, s := range s3 {
|
||||
assert.Equal(t, "team", s.Scope)
|
||||
}
|
||||
|
||||
r4 := <-ss.Scheme().GetAllPage("channel", 0, 1000)
|
||||
assert.Nil(t, r4.Err)
|
||||
s4 := r4.Data.([]*model.Scheme)
|
||||
assert.NotZero(t, len(s4))
|
||||
for _, s := range s4 {
|
||||
assert.Equal(t, "channel", s.Scope)
|
||||
}
|
||||
}
|
||||
|
||||
func testSchemeStoreDelete(t *testing.T, ss store.Store) {
|
||||
// Save a new scheme.
|
||||
s1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
// Check all fields saved correctly.
|
||||
res1 := <-ss.Scheme().Save(s1)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.(*model.Scheme)
|
||||
assert.Len(t, d1.Id, 26)
|
||||
assert.Equal(t, s1.DisplayName, d1.DisplayName)
|
||||
assert.Equal(t, s1.Name, d1.Name)
|
||||
assert.Equal(t, s1.Description, d1.Description)
|
||||
assert.NotZero(t, d1.CreateAt)
|
||||
assert.NotZero(t, d1.UpdateAt)
|
||||
assert.Zero(t, d1.DeleteAt)
|
||||
assert.Equal(t, s1.Scope, d1.Scope)
|
||||
assert.Len(t, d1.DefaultTeamAdminRole, 26)
|
||||
assert.Len(t, d1.DefaultTeamUserRole, 26)
|
||||
assert.Len(t, d1.DefaultChannelAdminRole, 26)
|
||||
assert.Len(t, d1.DefaultChannelUserRole, 26)
|
||||
|
||||
// Check the default roles were created correctly.
|
||||
roleRes1 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
|
||||
assert.Nil(t, roleRes1.Err)
|
||||
role1 := roleRes1.Data.(*model.Role)
|
||||
assert.Equal(t, role1.Permissions, []string{"edit_others_posts", "delete_others_posts"})
|
||||
assert.True(t, role1.SchemeManaged)
|
||||
|
||||
roleRes2 := <-ss.Role().Get(d1.DefaultTeamUserRole)
|
||||
assert.Nil(t, roleRes2.Err)
|
||||
role2 := roleRes2.Data.(*model.Role)
|
||||
assert.Equal(t, role2.Permissions, []string{"view_team", "add_user_to_team"})
|
||||
assert.True(t, role2.SchemeManaged)
|
||||
|
||||
roleRes3 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
|
||||
assert.Nil(t, roleRes3.Err)
|
||||
role3 := roleRes3.Data.(*model.Role)
|
||||
assert.Equal(t, role3.Permissions, []string{"manage_public_channel_members", "manage_private_channel_members"})
|
||||
assert.True(t, role3.SchemeManaged)
|
||||
|
||||
roleRes4 := <-ss.Role().Get(d1.DefaultChannelUserRole)
|
||||
assert.Nil(t, roleRes4.Err)
|
||||
role4 := roleRes4.Data.(*model.Role)
|
||||
assert.Equal(t, role4.Permissions, []string{"read_channel", "create_post"})
|
||||
assert.True(t, role4.SchemeManaged)
|
||||
|
||||
// Delete the scheme.
|
||||
res2 := <-ss.Scheme().Delete(d1.Id)
|
||||
if !assert.Nil(t, res2.Err) {
|
||||
t.Fatal(res2.Err)
|
||||
}
|
||||
d2 := res2.Data.(*model.Scheme)
|
||||
assert.NotZero(t, d2.DeleteAt)
|
||||
|
||||
// Check that the roles are deleted too.
|
||||
roleRes5 := <-ss.Role().Get(d1.DefaultTeamAdminRole)
|
||||
assert.Nil(t, roleRes5.Err)
|
||||
role5 := roleRes5.Data.(*model.Role)
|
||||
assert.NotZero(t, role5.DeleteAt)
|
||||
|
||||
roleRes6 := <-ss.Role().Get(d1.DefaultTeamUserRole)
|
||||
assert.Nil(t, roleRes6.Err)
|
||||
role6 := roleRes6.Data.(*model.Role)
|
||||
assert.NotZero(t, role6.DeleteAt)
|
||||
|
||||
roleRes7 := <-ss.Role().Get(d1.DefaultChannelAdminRole)
|
||||
assert.Nil(t, roleRes7.Err)
|
||||
role7 := roleRes7.Data.(*model.Role)
|
||||
assert.NotZero(t, role7.DeleteAt)
|
||||
|
||||
roleRes8 := <-ss.Role().Get(d1.DefaultChannelUserRole)
|
||||
assert.Nil(t, roleRes8.Err)
|
||||
role8 := roleRes8.Data.(*model.Role)
|
||||
assert.NotZero(t, role8.DeleteAt)
|
||||
|
||||
// Try deleting a scheme that does not exist.
|
||||
res3 := <-ss.Scheme().Delete(model.NewId())
|
||||
assert.NotNil(t, res3.Err)
|
||||
|
||||
// Try deleting a team scheme that's in use.
|
||||
s4 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
res4 := <-ss.Scheme().Save(s4)
|
||||
assert.Nil(t, res4.Err)
|
||||
d4 := res4.Data.(*model.Scheme)
|
||||
|
||||
t4 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &d4.Id,
|
||||
}
|
||||
tres4 := <-ss.Team().Save(t4)
|
||||
assert.Nil(t, tres4.Err)
|
||||
t4 = tres4.Data.(*model.Team)
|
||||
|
||||
sres4 := <-ss.Scheme().Delete(d4.Id)
|
||||
assert.Nil(t, sres4.Err)
|
||||
|
||||
tres5 := <-ss.Team().Get(t4.Id)
|
||||
assert.Nil(t, tres5.Err)
|
||||
t5 := tres5.Data.(*model.Team)
|
||||
assert.Equal(t, "", *t5.SchemeId)
|
||||
|
||||
// Try deleting a channel scheme that's in use.
|
||||
s5 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
res5 := <-ss.Scheme().Save(s5)
|
||||
assert.Nil(t, res5.Err)
|
||||
d5 := res5.Data.(*model.Scheme)
|
||||
|
||||
c5 := &model.Channel{
|
||||
TeamId: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
SchemeId: &d5.Id,
|
||||
}
|
||||
cres5 := <-ss.Channel().Save(c5, -1)
|
||||
assert.Nil(t, cres5.Err)
|
||||
c5 = cres5.Data.(*model.Channel)
|
||||
|
||||
sres5 := <-ss.Scheme().Delete(d5.Id)
|
||||
assert.Nil(t, sres5.Err)
|
||||
|
||||
cres6 := <-ss.Channel().Get(c5.Id, true)
|
||||
assert.Nil(t, cres6.Err)
|
||||
c6 := cres6.Data.(*model.Channel)
|
||||
assert.Equal(t, "", *c6.SchemeId)
|
||||
}
|
||||
|
||||
func testSchemeStorePermanentDeleteAll(t *testing.T, ss store.Store) {
|
||||
s1 := &model.Scheme{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s2 := &model.Scheme{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_CHANNEL,
|
||||
}
|
||||
|
||||
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
|
||||
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
|
||||
|
||||
res := <-ss.Scheme().PermanentDeleteAll()
|
||||
assert.Nil(t, res.Err)
|
||||
|
||||
res1 := <-ss.Scheme().Get(s1.Id)
|
||||
assert.NotNil(t, res1.Err)
|
||||
|
||||
res2 := <-ss.Scheme().Get(s2.Id)
|
||||
assert.NotNil(t, res2.Err)
|
||||
|
||||
res3 := <-ss.Scheme().GetAllPage("", 0, 100000)
|
||||
assert.Nil(t, res3.Err)
|
||||
assert.Len(t, res3.Data.([]*model.Scheme), 0)
|
||||
}
|
||||
@@ -44,6 +44,7 @@ type Store struct {
|
||||
PluginStore mocks.PluginStore
|
||||
ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore
|
||||
RoleStore mocks.RoleStore
|
||||
SchemeStore mocks.SchemeStore
|
||||
}
|
||||
|
||||
func (s *Store) Team() store.TeamStore { return &s.TeamStore }
|
||||
@@ -70,6 +71,7 @@ func (s *Store) Job() store.JobStore { return &s.JobSt
|
||||
func (s *Store) UserAccessToken() store.UserAccessTokenStore { return &s.UserAccessTokenStore }
|
||||
func (s *Store) Plugin() store.PluginStore { return &s.PluginStore }
|
||||
func (s *Store) Role() store.RoleStore { return &s.RoleStore }
|
||||
func (s *Store) Scheme() store.SchemeStore { return &s.SchemeStore }
|
||||
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
||||
return &s.ChannelMemberHistoryStore
|
||||
}
|
||||
@@ -107,5 +109,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
||||
&s.ChannelMemberHistoryStore,
|
||||
&s.PluginStore,
|
||||
&s.RoleStore,
|
||||
&s.SchemeStore,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
)
|
||||
|
||||
func TestTeamStore(t *testing.T, ss store.Store) {
|
||||
createDefaultRoles(t, ss)
|
||||
|
||||
t.Run("Save", func(t *testing.T) { testTeamStoreSave(t, ss) })
|
||||
t.Run("Update", func(t *testing.T) { testTeamStoreUpdate(t, ss) })
|
||||
t.Run("UpdateDisplayName", func(t *testing.T) { testTeamStoreUpdateDisplayName(t, ss) })
|
||||
@@ -34,6 +39,9 @@ func TestTeamStore(t *testing.T, ss store.Store) {
|
||||
t.Run("GetChannelUnreadsForAllTeams", func(t *testing.T) { testGetChannelUnreadsForAllTeams(t, ss) })
|
||||
t.Run("GetChannelUnreadsForTeam", func(t *testing.T) { testGetChannelUnreadsForTeam(t, ss) })
|
||||
t.Run("UpdateLastTeamIconUpdate", func(t *testing.T) { testUpdateLastTeamIconUpdate(t, ss) })
|
||||
t.Run("GetTeamsByScheme", func(t *testing.T) { testGetTeamsByScheme(t, ss) })
|
||||
t.Run("MigrateTeamMembers", func(t *testing.T) { testTeamStoreMigrateTeamMembers(t, ss) })
|
||||
t.Run("ResetAllTeamSchemes", func(t *testing.T) { testResetAllTeamSchemes(t, ss) })
|
||||
}
|
||||
|
||||
func testTeamStoreSave(t *testing.T, ss store.Store) {
|
||||
@@ -1029,3 +1037,179 @@ func testUpdateLastTeamIconUpdate(t *testing.T, ss store.Store) {
|
||||
t.Fatal("LastTeamIconUpdate not updated")
|
||||
}
|
||||
}
|
||||
|
||||
func testGetTeamsByScheme(t *testing.T, ss store.Store) {
|
||||
// Create some schemes.
|
||||
s1 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s2 := &model.Scheme{
|
||||
DisplayName: model.NewId(),
|
||||
Name: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
|
||||
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
|
||||
s2 = (<-ss.Scheme().Save(s2)).Data.(*model.Scheme)
|
||||
|
||||
// Create and save some teams.
|
||||
t1 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
t2 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
t3 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
}
|
||||
|
||||
t1 = (<-ss.Team().Save(t1)).Data.(*model.Team)
|
||||
t2 = (<-ss.Team().Save(t2)).Data.(*model.Team)
|
||||
t3 = (<-ss.Team().Save(t3)).Data.(*model.Team)
|
||||
|
||||
// Get the teams by a valid Scheme ID.
|
||||
res1 := <-ss.Team().GetTeamsByScheme(s1.Id, 0, 100)
|
||||
assert.Nil(t, res1.Err)
|
||||
d1 := res1.Data.([]*model.Team)
|
||||
assert.Len(t, d1, 2)
|
||||
|
||||
// Get the teams by a valid Scheme ID where there aren't any matching Teams.
|
||||
res2 := <-ss.Team().GetTeamsByScheme(s2.Id, 0, 100)
|
||||
assert.Nil(t, res2.Err)
|
||||
d2 := res2.Data.([]*model.Team)
|
||||
assert.Len(t, d2, 0)
|
||||
|
||||
// Get the teams by an invalid Scheme ID.
|
||||
res3 := <-ss.Team().GetTeamsByScheme(model.NewId(), 0, 100)
|
||||
assert.Nil(t, res3.Err)
|
||||
d3 := res3.Data.([]*model.Team)
|
||||
assert.Len(t, d3, 0)
|
||||
}
|
||||
|
||||
func testTeamStoreMigrateTeamMembers(t *testing.T, ss store.Store) {
|
||||
s1 := model.NewId()
|
||||
t1 := &model.Team{
|
||||
DisplayName: "Name",
|
||||
Name: "z-z-z" + model.NewId() + "b",
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
InviteId: model.NewId(),
|
||||
SchemeId: &s1,
|
||||
}
|
||||
t1 = store.Must(ss.Team().Save(t1)).(*model.Team)
|
||||
|
||||
tm1 := &model.TeamMember{
|
||||
TeamId: t1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "team_admin team_user",
|
||||
}
|
||||
tm2 := &model.TeamMember{
|
||||
TeamId: t1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "team_user",
|
||||
}
|
||||
tm3 := &model.TeamMember{
|
||||
TeamId: t1.Id,
|
||||
UserId: model.NewId(),
|
||||
ExplicitRoles: "something_else",
|
||||
}
|
||||
|
||||
tm1 = (<-ss.Team().SaveMember(tm1, -1)).Data.(*model.TeamMember)
|
||||
tm2 = (<-ss.Team().SaveMember(tm2, -1)).Data.(*model.TeamMember)
|
||||
tm3 = (<-ss.Team().SaveMember(tm3, -1)).Data.(*model.TeamMember)
|
||||
|
||||
lastDoneTeamId := strings.Repeat("0", 26)
|
||||
lastDoneUserId := strings.Repeat("0", 26)
|
||||
|
||||
for {
|
||||
res := <-ss.Team().MigrateTeamMembers(lastDoneTeamId, lastDoneUserId)
|
||||
if assert.Nil(t, res.Err) {
|
||||
if res.Data == nil {
|
||||
break
|
||||
}
|
||||
data := res.Data.(map[string]string)
|
||||
lastDoneTeamId = data["TeamId"]
|
||||
lastDoneUserId = data["UserId"]
|
||||
}
|
||||
}
|
||||
|
||||
res1 := <-ss.Team().GetMember(tm1.TeamId, tm1.UserId)
|
||||
assert.Nil(t, res1.Err)
|
||||
tm1b := res1.Data.(*model.TeamMember)
|
||||
assert.Equal(t, "", tm1b.ExplicitRoles)
|
||||
assert.True(t, tm1b.SchemeUser)
|
||||
assert.True(t, tm1b.SchemeAdmin)
|
||||
|
||||
res2 := <-ss.Team().GetMember(tm2.TeamId, tm2.UserId)
|
||||
assert.Nil(t, res2.Err)
|
||||
tm2b := res2.Data.(*model.TeamMember)
|
||||
assert.Equal(t, "", tm2b.ExplicitRoles)
|
||||
assert.True(t, tm2b.SchemeUser)
|
||||
assert.False(t, tm2b.SchemeAdmin)
|
||||
|
||||
res3 := <-ss.Team().GetMember(tm3.TeamId, tm3.UserId)
|
||||
assert.Nil(t, res3.Err)
|
||||
tm3b := res3.Data.(*model.TeamMember)
|
||||
assert.Equal(t, "something_else", tm3b.ExplicitRoles)
|
||||
assert.False(t, tm3b.SchemeUser)
|
||||
assert.False(t, tm3b.SchemeAdmin)
|
||||
}
|
||||
|
||||
func testResetAllTeamSchemes(t *testing.T, ss store.Store) {
|
||||
s1 := &model.Scheme{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Description: model.NewId(),
|
||||
Scope: model.SCHEME_SCOPE_TEAM,
|
||||
}
|
||||
s1 = (<-ss.Scheme().Save(s1)).Data.(*model.Scheme)
|
||||
|
||||
t1 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
t2 := &model.Team{
|
||||
Name: model.NewId(),
|
||||
DisplayName: model.NewId(),
|
||||
Email: model.NewId() + "@nowhere.com",
|
||||
Type: model.TEAM_OPEN,
|
||||
SchemeId: &s1.Id,
|
||||
}
|
||||
|
||||
t1 = (<-ss.Team().Save(t1)).Data.(*model.Team)
|
||||
t2 = (<-ss.Team().Save(t2)).Data.(*model.Team)
|
||||
|
||||
assert.Equal(t, s1.Id, *t1.SchemeId)
|
||||
assert.Equal(t, s1.Id, *t2.SchemeId)
|
||||
|
||||
res := <-ss.Team().ResetAllTeamSchemes()
|
||||
assert.Nil(t, res.Err)
|
||||
|
||||
t1 = (<-ss.Team().Get(t1.Id)).Data.(*model.Team)
|
||||
t2 = (<-ss.Team().Get(t2.Id)).Data.(*model.Team)
|
||||
|
||||
assert.Equal(t, "", *t1.SchemeId)
|
||||
assert.Equal(t, "", *t2.SchemeId)
|
||||
}
|
||||
|
||||
@@ -554,6 +554,8 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
|
||||
hasImageProxy := c.ServiceSettings.ImageProxyType != nil && *c.ServiceSettings.ImageProxyType != "" && c.ServiceSettings.ImageProxyURL != nil && *c.ServiceSettings.ImageProxyURL != ""
|
||||
props["HasImageProxy"] = strconv.FormatBool(hasImageProxy)
|
||||
|
||||
props["RunJobs"] = strconv.FormatBool(*c.JobSettings.RunJobs)
|
||||
|
||||
// Set default values for all options that require a license.
|
||||
props["ExperimentalHideTownSquareinLHS"] = "false"
|
||||
props["ExperimentalTownSquareIsReadOnly"] = "false"
|
||||
|
||||
@@ -151,6 +151,7 @@ func GetClientLicense(l *model.License) map[string]string {
|
||||
props["PhoneNumber"] = l.Customer.PhoneNumber
|
||||
props["EmailNotificationContents"] = strconv.FormatBool(*l.Features.EmailNotificationContents)
|
||||
props["MessageExport"] = strconv.FormatBool(*l.Features.MessageExport)
|
||||
props["CustomPermissionsSchemes"] = strconv.FormatBool(*l.Features.CustomPermissionsSchemes)
|
||||
}
|
||||
|
||||
return props
|
||||
|
||||
@@ -490,6 +490,17 @@ func (c *Context) RequireRoleId() *Context {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) RequireSchemeId() *Context {
|
||||
if c.Err != nil {
|
||||
return c
|
||||
}
|
||||
|
||||
if len(c.Params.SchemeId) != 26 {
|
||||
c.SetInvalidUrlParam("scheme_id")
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) RequireRoleName() *Context {
|
||||
if c.Err != nil {
|
||||
return c
|
||||
|
||||
@@ -47,6 +47,8 @@ type Params struct {
|
||||
ActionId string
|
||||
RoleId string
|
||||
RoleName string
|
||||
SchemeId string
|
||||
Scope string
|
||||
Page int
|
||||
PerPage int
|
||||
LogsPerPage int
|
||||
@@ -167,6 +169,12 @@ func ParamsFromRequest(r *http.Request) *Params {
|
||||
params.RoleName = val
|
||||
}
|
||||
|
||||
if val, ok := props["scheme_id"]; ok {
|
||||
params.SchemeId = val
|
||||
}
|
||||
|
||||
params.Scope = query.Get("scope")
|
||||
|
||||
if val, err := strconv.Atoi(query.Get("page")); err != nil || val < 0 {
|
||||
params.Page = PAGE_DEFAULT
|
||||
} else {
|
||||
|
||||
@@ -61,6 +61,7 @@ func Setup() *TestHelper {
|
||||
ApiClient = model.NewAPIv4Client(URL)
|
||||
|
||||
a.DoAdvancedPermissionsMigration()
|
||||
a.DoEmojisPermissionsMigration()
|
||||
|
||||
a.Srv.Store.MarkSystemRanUnitTests()
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user