Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Users sync #545

Merged
merged 9 commits into from
Apr 3, 2024
Merged
12 changes: 6 additions & 6 deletions server/ce2e/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,13 @@ func TestSelectiveSync(t *testing.T) {
_, err = conn.Exec("UPDATE Users SET RemoteId = (SELECT remoteId FROM remoteclusters WHERE pluginid=$1) WHERE Username = 'msteams_synthetic'", pluginID)
require.NoError(t, err)

team, _, err := adminClient.GetTeamByName(context.Background(), "test", "")
require.NoError(t, err)
// team, _, err := adminClient.GetTeamByName(context.Background(), "test", "")
// require.NoError(t, err)

require.EventuallyWithT(t, func(c *assert.CollectT) {
suggestions, _, _ := adminClient.ListCommandAutocompleteSuggestions(context.Background(), "/msteams", team.Id)
assert.Len(c, suggestions, 1)
}, 10*time.Second, 500*time.Millisecond)
// require.EventuallyWithT(t, func(c *assert.CollectT) {
// suggestions, _, _ := adminClient.ListCommandAutocompleteSuggestions(context.Background(), "/msteams", team.Id)
// assert.Len(c, suggestions, 1)
// }, 10*time.Second, 500*time.Millisecond)

ttCases := []struct {
name string
Expand Down
132 changes: 74 additions & 58 deletions server/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ func (p *Plugin) syncUsers() {
done := p.GetMetrics().ObserveWorker(metrics.WorkerSyncUsers)
defer done()

// Get the users registered in MS Teams
msUsers, err := p.GetClientForApp().ListUsers()
if err != nil {
p.API.LogWarn("Unable to list MS Teams users during sync user job", "error", err.Error())
Expand All @@ -629,6 +630,7 @@ func (p *Plugin) syncUsers() {
return
}

// Map MM users with MS Teams users
mmUsersMap := make(map[string]*model.User, len(mmUsers))
for _, u := range mmUsers {
mmUsersMap[u.Email] = u
Expand All @@ -643,11 +645,16 @@ func (p *Plugin) syncUsers() {
}

userSuffixID := 1

// The email field is mandatory in MM, if there is no email we skip the user
if msUser.Mail == "" {
continue
}

// Determine if the user is already present
mmUser, isUserPresent := mmUsersMap[msUser.Mail]

// Set the authData if promoting syntetic users
authData := ""
if configuration.AutomaticallyPromoteSyntheticUsers {
switch configuration.SyntheticUserAuthData {
Expand All @@ -659,26 +666,40 @@ func (p *Plugin) syncUsers() {
authData = msUser.UserPrincipalName
}
}

username := "msteams_" + slug.Make(msUser.DisplayName)
if isUserPresent {
if teamsUserID, _ := p.store.MattermostToTeamsUserID(mmUser.Id); teamsUserID == "" {
if err = p.store.SetUserInfo(mmUser.Id, msUser.ID, nil); err != nil {
p.API.LogWarn("Unable to store Mattermost user ID vs Teams user ID in sync user job", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
// Update the user if needed
if p.IsRemoteUser(mmUser) {
if !syncGuestUsers && msUser.Type == msteamsUserTypeGuest {
if mmUser.DeleteAt == 0 {
// if the user is a guest and should not sync, deactivate it
p.API.LogInfo("Deactivating the guest user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID)
if err := p.API.UpdateUserActive(mmUser.Id, false); err != nil {
p.API.LogWarn("Unable to deactivate the guest user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
}
continue
}

if teamsUserID, _ := p.store.MattermostToTeamsUserID(mmUser.Id); teamsUserID == "" {
if err = p.store.SetUserInfo(mmUser.Id, msUser.ID, nil); err != nil {
p.API.LogWarn("Unable to store Mattermost user ID vs Teams user ID in sync user job", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
}
}

if p.IsRemoteUser(mmUser) {
if msUser.IsAccountEnabled {
// Activate the deactivated Mattermost user corresponding to the MS Teams user.
if mmUser.DeleteAt != 0 {
p.API.LogInfo("Activating the inactive user", "user_id", mmUser.Id, "teams_user_id", msUser.ID)
p.API.LogInfo("Activating the inactive user", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "type", msUser.Type)
if err := p.API.UpdateUserActive(mmUser.Id, true); err != nil {
p.API.LogWarn("Unable to activate the user", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
}
} else {
// Deactivate the active Mattermost user corresponding to the MS Teams user.
if mmUser.DeleteAt == 0 {
p.API.LogInfo("Deactivating the Mattermost user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID)
p.API.LogInfo("Deactivating the Mattermost user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "type", msUser.Type)
if err := p.API.UpdateUserActive(mmUser.Id, false); err != nil {
p.API.LogWarn("Unable to deactivate the Mattermost user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
Expand All @@ -693,6 +714,7 @@ func (p *Plugin) syncUsers() {
continue
}

// Update AuthService/AuthData if it changed
if configuration.AutomaticallyPromoteSyntheticUsers && (mmUser.AuthService != configuration.SyntheticUserAuthService || (user.AuthData != nil && authData != "" && *user.AuthData != authData)) {
p.API.LogInfo("Updating user auth service", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "auth_service", configuration.SyntheticUserAuthService)
if _, err := p.API.UpdateUserAuth(mmUser.Id, &model.UserAuth{
Expand All @@ -702,40 +724,68 @@ func (p *Plugin) syncUsers() {
p.API.LogWarn("Unable to update user auth service during sync user job", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
}
}
}

if msUser.Type == msteamsUserTypeGuest {
// Check if syncing of MS Teams guest users is disabled.
if !syncGuestUsers {
if isUserPresent && p.IsRemoteUser(mmUser) && mmUser.DeleteAt == 0 {
// Deactivate the Mattermost user corresponding to the MS Teams guest user.
p.API.LogInfo("Deactivating the guest user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID)
if err := p.API.UpdateUserActive(mmUser.Id, false); err != nil {
p.API.LogWarn("Unable to deactivate the guest user account", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
// Update the user profile if needed
shouldUpdate := false
if !strings.HasPrefix(mmUser.Username, "msteams_") && username != mmUser.Username {
mmUser.Username = username
shouldUpdate = true
}

continue
if mmUser.FirstName != msUser.DisplayName {
mmUser.FirstName = msUser.DisplayName
shouldUpdate = true
}

if !mmUser.EmailVerified {
mmUser.EmailVerified = true
shouldUpdate = true
}

if shouldUpdate {
for {
p.API.LogInfo("Updating user profile", "user_id", mmUser.Id, "teams_user_id", msUser.ID)
_, err := p.API.UpdateUser(mmUser)
if err != nil {
if err.Id == "app.user.save.username_exists.app_error" {
// When there is already a user with the same username, start using the suffix
mmUser.Username = username + "-" + fmt.Sprint(userSuffixID)
userSuffixID++
continue
}

p.API.LogWarn("Unable to update user during sync user job", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
break
}

break
}
}
}
} else if !msUser.IsAccountEnabled {
continue
} else {
// If we are not sync'ing guests, but the user is a MS Team guest, deactivate it from the get go
deleteAt := int64(0)
if !syncGuestUsers && msUser.Type == msteamsUserTypeGuest {
deleteAt = model.GetMillis()
}
}

username := "msteams_" + slug.Make(msUser.DisplayName)
if !isUserPresent {
newMMUser := &model.User{
Email: msUser.Mail,
RemoteId: &p.remoteID,
FirstName: msUser.DisplayName,
Username: username,
EmailVerified: true,
DeleteAt: deleteAt,
}

if configuration.AutomaticallyPromoteSyntheticUsers && authData != "" {
p.API.LogInfo("Creating new synthetic user", "teams_user_id", msUser.ID, "auth_service", configuration.SyntheticUserAuthService)
p.API.LogInfo("Creating new synthetic user", "teams_user_id", msUser.ID, "auth_service", configuration.SyntheticUserAuthService, "as_guest", msUser.Type == msteamsUserTypeGuest)
newMMUser.AuthService = configuration.SyntheticUserAuthService
newMMUser.AuthData = &authData
} else {
p.API.LogInfo("Creating new synthetic user", "teams_user_id", msUser.ID)
p.API.LogInfo("Creating new synthetic user", "teams_user_id", msUser.ID, "as_guest", msUser.Type == msteamsUserTypeGuest)
newMMUser.Password = p.GenerateRandomPassword()
}

Expand Down Expand Up @@ -778,40 +828,6 @@ func (p *Plugin) syncUsers() {
if err = p.store.SetUserInfo(newUser.Id, msUser.ID, nil); err != nil {
p.API.LogWarn("Unable to set user info during sync user job", "user_id", newUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
}
} else if p.IsRemoteUser(mmUser) {
shouldUpdate := false
if !strings.HasPrefix(mmUser.Username, "msteams_") && username != mmUser.Username {
mmUser.Username = username
shouldUpdate = true
}

if mmUser.FirstName != msUser.DisplayName {
mmUser.FirstName = msUser.DisplayName
shouldUpdate = true
}

if !mmUser.EmailVerified {
mmUser.EmailVerified = true
shouldUpdate = true
}

if shouldUpdate {
for {
_, err := p.API.UpdateUser(mmUser)
if err != nil {
if err.Id == "app.user.save.username_exists.app_error" {
mmUser.Username = username + "-" + fmt.Sprint(userSuffixID)
userSuffixID++
continue
}

p.API.LogWarn("Unable to update user during sync user job", "user_id", mmUser.Id, "teams_user_id", msUser.ID, "error", err.Error())
break
}

break
}
}
}
}
p.GetMetrics().ObserveUpstreamUsers(activeMSTeamsUsersCount)
Expand Down
Loading