From 26f238af2a2ecba4e446a713f559054024bb3901 Mon Sep 17 00:00:00 2001 From: Jason Blais Date: Wed, 6 Jun 2018 11:36:22 -0400 Subject: Add missing diagnostics (#8911) * Update diagnostics.go * Update diagnostics.go --- app/diagnostics.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/diagnostics.go b/app/diagnostics.go index c1b3a015a..d833cdbec 100644 --- a/app/diagnostics.go +++ b/app/diagnostics.go @@ -250,6 +250,8 @@ func (a *App) trackConfig() { "isdefault_image_proxy_options": isDefault(*cfg.ServiceSettings.ImageProxyOptions, ""), "websocket_url": isDefault(*cfg.ServiceSettings.WebsocketURL, ""), "allow_cookies_for_subdomains": *cfg.ServiceSettings.AllowCookiesForSubdomains, + "enable_api_team_deletion": *cfg.ServiceSettings.EnableAPITeamDeletion, + "experimental_enable_hardened_mode": *cfg.ServiceSettings.ExperimentalEnableHardenedMode, }) a.SendDiagnostic(TRACK_CONFIG_TEAM, map[string]interface{}{ @@ -263,6 +265,7 @@ func (a *App) trackConfig() { "restrict_public_channel_deletion": *cfg.TeamSettings.RestrictPublicChannelDeletion, "restrict_private_channel_deletion": *cfg.TeamSettings.RestrictPrivateChannelDeletion, "enable_open_server": *cfg.TeamSettings.EnableOpenServer, + "enable_user_deactivation": *cfg.TeamSettings.EnableUserDeactivation, "enable_custom_brand": *cfg.TeamSettings.EnableCustomBrand, "restrict_direct_message": *cfg.TeamSettings.RestrictDirectMessage, "max_notifications_per_channel": *cfg.TeamSettings.MaxNotificationsPerChannel, @@ -410,6 +413,7 @@ func (a *App) trackConfig() { "isdefault_nickname_attribute": isDefault(*cfg.LdapSettings.NicknameAttribute, model.LDAP_SETTINGS_DEFAULT_NICKNAME_ATTRIBUTE), "isdefault_id_attribute": isDefault(*cfg.LdapSettings.IdAttribute, model.LDAP_SETTINGS_DEFAULT_ID_ATTRIBUTE), "isdefault_position_attribute": isDefault(*cfg.LdapSettings.PositionAttribute, model.LDAP_SETTINGS_DEFAULT_POSITION_ATTRIBUTE), + "isdefault_login_id_attribute": isDefault(*cfg.LdapSettings.LoginIdAttribute, ""), "isdefault_login_field_name": isDefault(*cfg.LdapSettings.LoginFieldName, model.LDAP_SETTINGS_DEFAULT_LOGIN_FIELD_NAME), "isdefault_login_button_color": isDefault(*cfg.LdapSettings.LoginButtonColor, ""), "isdefault_login_button_border_color": isDefault(*cfg.LdapSettings.LoginButtonBorderColor, ""), @@ -525,7 +529,8 @@ func (a *App) trackConfig() { }) a.SendDiagnostic(TRACK_CONFIG_DISPLAY, map[string]interface{}{ - "experimental_timezone": *cfg.DisplaySettings.ExperimentalTimezone, + "experimental_timezone": *cfg.DisplaySettings.ExperimentalTimezone, + "isdefault_custom_url_schemes": len(*cfg.DisplaySettings.CustomUrlSchemes) != 0, }) a.SendDiagnostic(TRACK_CONFIG_TIMEZONE, map[string]interface{}{ -- cgit v1.2.3-1-g7c22 From 1d961b1632d7cac5574f48f447755fbeddbdfa4b Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Wed, 6 Jun 2018 12:02:22 -0400 Subject: Fix push notification styling backwards compatibility (#8913) --- app/notification.go | 1 + model/push_notification.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/notification.go b/app/notification.go index 741dadd9a..a3c1857d5 100644 --- a/app/notification.go +++ b/app/notification.go @@ -722,6 +722,7 @@ func (a *App) sendPushNotification(post *model.Post, user *model.User, channel * msg.Badge = int(badge.Data.(int64)) } + msg.Version = model.PUSH_MESSAGE_V2 msg.Type = model.PUSH_TYPE_MESSAGE msg.TeamId = channel.TeamId msg.ChannelId = channel.Id diff --git a/model/push_notification.go b/model/push_notification.go index 0d7ba77ac..5268a98f8 100644 --- a/model/push_notification.go +++ b/model/push_notification.go @@ -17,6 +17,7 @@ const ( PUSH_TYPE_MESSAGE = "message" PUSH_TYPE_CLEAR = "clear" + PUSH_MESSAGE_V2 = "v2" // The category is set to handle a set of interactive Actions // with the push notifications @@ -44,6 +45,7 @@ type PushNotification struct { OverrideUsername string `json:"override_username"` OverrideIconUrl string `json:"override_icon_url"` FromWebhook string `json:"from_webhook"` + Version string `json:"version"` } func (me *PushNotification) ToJson() string { -- cgit v1.2.3-1-g7c22 From 927b11f6e247746f0f5ceeae592b9c525e3c2f76 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Thu, 7 Jun 2018 14:19:19 -0400 Subject: MM-10803: remove premature user sanitization on deactivation (#8926) * remove unused UpdateNonSSOUserActive * MM-10803: stop prematurely sanitizing users on deactivate This change was preceded by the removal of UpdateNonSSOUserActive to ensure there are no APIs relying on the sanitized return value. * MM-10803: test websocket events after UpdateUserActive --- api4/apitestlib.go | 4 ++ api4/user_test.go | 184 +++++++++++++++++++++++++++++++++++++++-------------- app/user.go | 19 ------ app/user_test.go | 18 ------ 4 files changed, 140 insertions(+), 85 deletions(-) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 22084a1d6..8293a03f7 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -271,6 +271,10 @@ func (me *TestHelper) CreateWebSocketClient() (*model.WebSocketClient, *model.Ap return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv.ListenAddr.Port), me.Client.AuthToken) } +func (me *TestHelper) CreateWebSocketSystemAdminClient() (*model.WebSocketClient, *model.AppError) { + return model.NewWebSocketClient4(fmt.Sprintf("ws://localhost:%v", me.App.Srv.ListenAddr.Port), me.SystemAdminClient.AuthToken) +} + func (me *TestHelper) CreateUser() *model.User { return me.CreateUserWithClient(me.Client) } diff --git a/api4/user_test.go b/api4/user_test.go index 593208c92..1044e6162 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -513,7 +513,7 @@ func TestSearchUsers(t *testing.T) { t.Fatal("should have found user") } - _, err := th.App.UpdateNonSSOUserActive(th.BasicUser2.Id, false) + _, err := th.App.UpdateActive(th.BasicUser2, false) if err != nil { t.Fatal(err) } @@ -630,7 +630,7 @@ func TestSearchUsers(t *testing.T) { th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowEmailAddress = false }) th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowFullName = false }) - _, err = th.App.UpdateNonSSOUserActive(th.BasicUser2.Id, true) + _, err = th.App.UpdateActive(th.BasicUser2, true) if err != nil { t.Fatal(err) } @@ -1190,71 +1190,159 @@ func TestUpdateUserRoles(t *testing.T) { CheckBadRequestStatus(t, resp) } +func assertExpectedWebsocketEvent(t *testing.T, client *model.WebSocketClient, event string, test func(*model.WebSocketEvent)) { + for { + select { + case resp, ok := <-client.EventChannel: + if !ok { + t.Fatalf("channel closed before receiving expected event %s", model.WEBSOCKET_EVENT_USER_UPDATED) + } else if resp.Event == model.WEBSOCKET_EVENT_USER_UPDATED { + test(resp) + return + } + case <-time.After(5 * time.Second): + t.Fatalf("failed to receive expected event %s", model.WEBSOCKET_EVENT_USER_UPDATED) + } + } +} + +func assertWebsocketEventUserUpdatedWithEmail(t *testing.T, client *model.WebSocketClient, email string) { + assertExpectedWebsocketEvent(t, client, model.WEBSOCKET_EVENT_USER_UPDATED, func(event *model.WebSocketEvent) { + if eventUser, ok := event.Data["user"].(map[string]interface{}); !ok { + t.Fatalf("expected user") + } else if userEmail, ok := eventUser["email"].(string); !ok { + t.Fatalf("expected email %s, but got nil", email) + } else { + assert.Equal(t, email, userEmail) + } + }) +} + func TestUpdateUserActive(t *testing.T) { - th := Setup().InitBasic().InitSystemAdmin() - defer th.TearDown() + t.Run("basic tests", func(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() - Client := th.Client - SystemAdminClient := th.SystemAdminClient - user := th.BasicUser + Client := th.Client + SystemAdminClient := th.SystemAdminClient + user := th.BasicUser - EnableUserDeactivation := th.App.Config().TeamSettings.EnableUserDeactivation - defer func() { - th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableUserDeactivation = EnableUserDeactivation }) - }() + EnableUserDeactivation := th.App.Config().TeamSettings.EnableUserDeactivation + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableUserDeactivation = EnableUserDeactivation }) + }() - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = true }) - pass, resp := Client.UpdateUserActive(user.Id, false) - CheckNoError(t, resp) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = true }) + pass, resp := Client.UpdateUserActive(user.Id, false) + CheckNoError(t, resp) - if !pass { - t.Fatal("should have returned true") - } + if !pass { + t.Fatal("should have returned true") + } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = false }) - pass, resp = Client.UpdateUserActive(user.Id, false) - CheckUnauthorizedStatus(t, resp) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = false }) + pass, resp = Client.UpdateUserActive(user.Id, false) + CheckUnauthorizedStatus(t, resp) - if pass { - t.Fatal("should have returned false") - } + if pass { + t.Fatal("should have returned false") + } - th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = true }) - pass, resp = Client.UpdateUserActive(user.Id, false) - CheckUnauthorizedStatus(t, resp) + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = true }) + pass, resp = Client.UpdateUserActive(user.Id, false) + CheckUnauthorizedStatus(t, resp) - if pass { - t.Fatal("should have returned false") - } + if pass { + t.Fatal("should have returned false") + } - th.LoginBasic2() + th.LoginBasic2() - _, resp = Client.UpdateUserActive(user.Id, true) - CheckForbiddenStatus(t, resp) + _, resp = Client.UpdateUserActive(user.Id, true) + CheckForbiddenStatus(t, resp) - _, resp = Client.UpdateUserActive(GenerateTestId(), true) - CheckForbiddenStatus(t, resp) + _, resp = Client.UpdateUserActive(GenerateTestId(), true) + CheckForbiddenStatus(t, resp) - _, resp = Client.UpdateUserActive("junk", true) - CheckBadRequestStatus(t, resp) + _, resp = Client.UpdateUserActive("junk", true) + CheckBadRequestStatus(t, resp) - Client.Logout() + Client.Logout() - _, resp = Client.UpdateUserActive(user.Id, true) - CheckUnauthorizedStatus(t, resp) + _, resp = Client.UpdateUserActive(user.Id, true) + CheckUnauthorizedStatus(t, resp) - _, resp = SystemAdminClient.UpdateUserActive(user.Id, true) - CheckNoError(t, resp) + _, resp = SystemAdminClient.UpdateUserActive(user.Id, true) + CheckNoError(t, resp) - _, resp = SystemAdminClient.UpdateUserActive(user.Id, false) - CheckNoError(t, resp) + _, resp = SystemAdminClient.UpdateUserActive(user.Id, false) + CheckNoError(t, resp) - authData := model.NewId() - result := <-th.App.Srv.Store.User().UpdateAuthData(user.Id, "random", &authData, "", true) - require.Nil(t, result.Err) + authData := model.NewId() + result := <-th.App.Srv.Store.User().UpdateAuthData(user.Id, "random", &authData, "", true) + require.Nil(t, result.Err) - _, resp = SystemAdminClient.UpdateUserActive(user.Id, false) - CheckNoError(t, resp) + _, resp = SystemAdminClient.UpdateUserActive(user.Id, false) + CheckNoError(t, resp) + }) + + t.Run("websocket events", func(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer th.TearDown() + + SystemAdminClient := th.SystemAdminClient + user := th.BasicUser2 + + EnableUserDeactivation := th.App.Config().TeamSettings.EnableUserDeactivation + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableUserDeactivation = EnableUserDeactivation }) + }() + + th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.EnableUserDeactivation = true }) + + webSocketClient, err := th.CreateWebSocketClient() + assert.Nil(t, err) + defer webSocketClient.Close() + + webSocketClient.Listen() + + time.Sleep(300 * time.Millisecond) + if resp := <-webSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { + t.Fatal("should have responded OK to authentication challenge") + } + + adminWebSocketClient, err := th.CreateWebSocketSystemAdminClient() + assert.Nil(t, err) + defer adminWebSocketClient.Close() + + adminWebSocketClient.Listen() + + time.Sleep(300 * time.Millisecond) + if resp := <-adminWebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { + t.Fatal("should have responded OK to authentication challenge") + } + + ShowEmailAddress := th.App.Config().PrivacySettings.ShowEmailAddress + defer func() { + th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowEmailAddress = ShowEmailAddress }) + }() + + // Verify that both admins and regular users see the email when privacy settings allow same. + th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowEmailAddress = true }) + _, resp := SystemAdminClient.UpdateUserActive(user.Id, false) + CheckNoError(t, resp) + + assertWebsocketEventUserUpdatedWithEmail(t, webSocketClient, user.Email) + assertWebsocketEventUserUpdatedWithEmail(t, adminWebSocketClient, user.Email) + + // Verify that only admins see the email when privacy settings hide emails. + th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowEmailAddress = false }) + _, resp = SystemAdminClient.UpdateUserActive(user.Id, true) + CheckNoError(t, resp) + + assertWebsocketEventUserUpdatedWithEmail(t, webSocketClient, "") + assertWebsocketEventUserUpdatedWithEmail(t, adminWebSocketClient, user.Email) + }) } func TestGetUsers(t *testing.T) { diff --git a/app/user.go b/app/user.go index 2325c6338..ccf8dd40e 100644 --- a/app/user.go +++ b/app/user.go @@ -862,22 +862,6 @@ func (a *App) UpdatePasswordAsUser(userId, currentPassword, newPassword string) return a.UpdatePasswordSendEmail(user, newPassword, T("api.user.update_password.menu")) } -func (a *App) UpdateNonSSOUserActive(userId string, active bool) (*model.User, *model.AppError) { - var user *model.User - var err *model.AppError - if user, err = a.GetUser(userId); err != nil { - return nil, err - } - - if user.IsSSOUser() { - err := model.NewAppError("UpdateActive", "api.user.update_active.no_deactivate_sso.app_error", nil, "userId="+user.Id, http.StatusBadRequest) - err.StatusCode = http.StatusBadRequest - return nil, err - } - - return a.UpdateActive(user, active) -} - func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.AppError) { if active { user.DeleteAt = 0 @@ -895,9 +879,6 @@ func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.A } ruser := result.Data.([2]*model.User)[0] - options := a.Config().GetSanitizeOptions() - options["passwordupdate"] = false - ruser.Sanitize(options) if !active { a.SetStatusOffline(ruser.Id, false) diff --git a/app/user_test.go b/app/user_test.go index f0e026fa9..b557d296b 100644 --- a/app/user_test.go +++ b/app/user_test.go @@ -96,24 +96,6 @@ func TestCreateOAuthUser(t *testing.T) { } } -func TestDeactivateSSOUser(t *testing.T) { - th := Setup().InitBasic() - defer th.TearDown() - - r := rand.New(rand.NewSource(time.Now().UnixNano())) - glUser := oauthgitlab.GitLabUser{Id: int64(r.Intn(1000)) + 1, Username: "o" + model.NewId(), Email: model.NewId() + "@simulator.amazonses.com", Name: "Joram Wilander"} - - json := glUser.ToJson() - user, err := th.App.CreateOAuthUser(model.USER_AUTH_SERVICE_GITLAB, strings.NewReader(json), th.BasicTeam.Id) - if err != nil { - t.Fatal(err) - } - defer th.App.PermanentDeleteUser(user) - - _, err = th.App.UpdateNonSSOUserActive(user.Id, false) - assert.Equal(t, "api.user.update_active.no_deactivate_sso.app_error", err.Id) -} - func TestCreateProfileImage(t *testing.T) { b, err := CreateProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba", "luximbi.ttf") if err != nil { -- cgit v1.2.3-1-g7c22 From 566539bc67c8e467f88401551f999436e27fe70a Mon Sep 17 00:00:00 2001 From: Martin Kraft Date: Fri, 8 Jun 2018 09:07:15 -0400 Subject: MM-10264: Adds system scheme to permissions import/export. (#8924) * MM-10264: Adds system scheme to permissions import/export. * MM-10264: Switches to more likely unique name. * MM-10264: Changed collision prevention string. * MM-10264: Rolls back created schemes in all error cases. * MM-10264: Test fix for more rollback cases. --- app/permissions.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++--- app/permissions_test.go | 2 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/app/permissions.go b/app/permissions.go index 5b1b49de2..d86ceab5d 100644 --- a/app/permissions.go +++ b/app/permissions.go @@ -14,6 +14,7 @@ import ( ) const permissionsExportBatchSize = 100 +const systemSchemeName = "00000000-0000-0000-0000-000000000000" // Prevents collisions with user-created schemes. func (a *App) ResetPermissionsSystem() *model.AppError { // Reset all Teams to not have a scheme. @@ -101,6 +102,31 @@ func (a *App) ExportPermissions(w io.Writer) error { } + defaultRoleNames := []string{} + for _, dr := range model.MakeDefaultRoles() { + defaultRoleNames = append(defaultRoleNames, dr.Name) + } + + roles, appErr := a.GetRolesByNames(defaultRoleNames) + if appErr != nil { + return errors.New(appErr.Message) + } + + schemeExport, err := json.Marshal(&model.SchemeConveyor{ + Name: systemSchemeName, + Roles: roles, + }) + if err != nil { + return err + } + + schemeExport = append(schemeExport, []byte("\n")...) + + _, err = w.Write(schemeExport) + if err != nil { + return err + } + return nil } @@ -113,13 +139,33 @@ func (a *App) ImportPermissions(jsonl io.Reader) error { var schemeConveyor *model.SchemeConveyor err := json.Unmarshal(scanner.Bytes(), &schemeConveyor) if err != nil { + rollback(a, createdSchemeIDs) return err } + if schemeConveyor.Name == systemSchemeName { + for _, roleIn := range schemeConveyor.Roles { + dbRole, err := a.GetRoleByName(roleIn.Name) + if err != nil { + rollback(a, createdSchemeIDs) + return errors.New(err.Message) + } + _, err = a.PatchRole(dbRole, &model.RolePatch{ + Permissions: &roleIn.Permissions, + }) + if err != nil { + rollback(a, createdSchemeIDs) + return err + } + } + continue + } + // Create the new Scheme. The new Roles are created automatically. var appErr *model.AppError schemeCreated, appErr := a.CreateScheme(schemeConveyor.Scheme()) if appErr != nil { + rollback(a, createdSchemeIDs) return errors.New(appErr.Message) } createdSchemeIDs = append(createdSchemeIDs, schemeCreated.Id) @@ -139,21 +185,26 @@ func (a *App) ImportPermissions(jsonl io.Reader) error { err = updateRole(a, schemeConveyor, roleNameTuple[0], roleNameTuple[1]) if err != nil { // Delete the new Schemes. The new Roles are deleted automatically. - for _, schemeID := range createdSchemeIDs { - a.DeleteScheme(schemeID) - } + rollback(a, createdSchemeIDs) return err } } } if err := scanner.Err(); err != nil { + rollback(a, createdSchemeIDs) return err } return nil } +func rollback(a *App, createdSchemeIDs []string) { + for _, schemeID := range createdSchemeIDs { + a.DeleteScheme(schemeID) + } +} + func updateRole(a *App, sc *model.SchemeConveyor, roleCreatedName, defaultRoleName string) error { var err *model.AppError diff --git a/app/permissions_test.go b/app/permissions_test.go index 3c70dc026..ca98461e7 100644 --- a/app/permissions_test.go +++ b/app/permissions_test.go @@ -179,7 +179,7 @@ func TestImportPermissions_idempotentScheme(t *testing.T) { if appErr != nil { panic(appErr) } - expected = len(results) + 1 + expected = len(results) err := th.App.ImportPermissions(r) if err == nil { -- cgit v1.2.3-1-g7c22 From d4761a94cebfd667373e1635dd1c9ad334b3b80c Mon Sep 17 00:00:00 2001 From: George Goldberg Date: Mon, 11 Jun 2018 15:24:08 +0100 Subject: Fix idempotency of scheme migrations. (#8935) This fixes the issue where if the migration tries to migrate an already scheme-aware member object it would end up removing it's scheme-derived roles. Instead, only if the member object is unmigrated do we default to setting the scheme-derived role booleans to false. We tell if it is an unmigrated member object by checking if the booleans are set to null. --- store/sqlstore/channel_store.go | 8 ++++++-- store/sqlstore/team_store.go | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index 5f336d904..476665514 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -1733,8 +1733,12 @@ func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId 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} + if !member.SchemeAdmin.Valid { + member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true} + } + if !member.SchemeUser.Valid { + 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} diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index fe4c09175..22f0bdb29 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -755,8 +755,12 @@ func (s SqlTeamStore) MigrateTeamMembers(fromTeamId string, fromUserId string) s 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} + if !member.SchemeAdmin.Valid { + member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true} + } + if !member.SchemeUser.Valid { + 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} -- cgit v1.2.3-1-g7c22