diff --git a/app/oauth.go b/app/oauth.go index f36d2ee230b..1e423bbd58a 100644 --- a/app/oauth.go +++ b/app/oauth.go @@ -921,7 +921,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service } func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, password, code, service string) (string, *model.AppError) { - if a.Srv().License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer { + if !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer { return "", model.NewAppError("emailToOAuth", "api.user.email_to_oauth.not_available.app_error", nil, "", http.StatusForbidden) } @@ -951,7 +951,7 @@ func (a *App) SwitchEmailToOAuth(w http.ResponseWriter, r *http.Request, email, } func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *model.AppError) { - if a.Srv().License() != nil && !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer { + if !*a.Config().ServiceSettings.ExperimentalEnableAuthenticationTransfer { return "", model.NewAppError("oauthToEmail", "api.user.oauth_to_email.not_available.app_error", nil, "", http.StatusForbidden) } diff --git a/app/security_update_check.go b/app/security_update_check.go deleted file mode 100644 index ad0c226a72e..00000000000 --- a/app/security_update_check.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package app - -import ( - "io/ioutil" - "net/http" - "net/url" - "runtime" - "strconv" - - "github.com/mattermost/mattermost-server/v5/model" - "github.com/mattermost/mattermost-server/v5/shared/i18n" - "github.com/mattermost/mattermost-server/v5/shared/mail" - "github.com/mattermost/mattermost-server/v5/shared/mlog" -) - -const ( - PropSecurityURL = "https://securityupdatecheck.mattermost.com" - SecurityUpdatePeriod = 86400000 // 24 hours in milliseconds. - - PropSecurityID = "id" - PropSecurityBuild = "b" - PropSecurityEnterpriseReady = "be" - PropSecurityDatabase = "db" - PropSecurityOS = "os" - PropSecurityUserCount = "uc" - PropSecurityTeamCount = "tc" - PropSecurityActiveUserCount = "auc" - PropSecurityUnitTests = "ut" -) - -func (s *Server) DoSecurityUpdateCheck() { - if !*s.Config().ServiceSettings.EnableSecurityFixAlert { - return - } - - props, err := s.Store.System().Get() - if err != nil { - return - } - - lastSecurityTime, _ := strconv.ParseInt(props[model.SYSTEM_LAST_SECURITY_TIME], 10, 0) - currentTime := model.GetMillis() - - if (currentTime - lastSecurityTime) > SecurityUpdatePeriod { - mlog.Debug("Checking for security update from Mattermost") - - v := url.Values{} - - v.Set(PropSecurityID, s.TelemetryId()) - v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber) - v.Set(PropSecurityEnterpriseReady, model.BuildEnterpriseReady) - v.Set(PropSecurityDatabase, *s.Config().SqlSettings.DriverName) - v.Set(PropSecurityOS, runtime.GOOS) - - if props[model.SYSTEM_RAN_UNIT_TESTS] != "" { - v.Set(PropSecurityUnitTests, "1") - } else { - v.Set(PropSecurityUnitTests, "0") - } - - systemSecurityLastTime := &model.System{Name: model.SYSTEM_LAST_SECURITY_TIME, Value: strconv.FormatInt(currentTime, 10)} - if lastSecurityTime == 0 { - s.Store.System().Save(systemSecurityLastTime) - } else { - s.Store.System().Update(systemSecurityLastTime) - } - - if count, err := s.Store.User().Count(model.UserCountOptions{IncludeDeleted: true}); err == nil { - v.Set(PropSecurityUserCount, strconv.FormatInt(count, 10)) - } - - if ucr, err := s.Store.Status().GetTotalActiveUsersCount(); err == nil { - v.Set(PropSecurityActiveUserCount, strconv.FormatInt(ucr, 10)) - } - - if teamCount, err := s.Store.Team().AnalyticsTeamCount(nil); err == nil { - v.Set(PropSecurityTeamCount, strconv.FormatInt(teamCount, 10)) - } - - res, err := http.Get(PropSecurityURL + "/security?" + v.Encode()) - if err != nil { - mlog.Error("Failed to get security update information from Mattermost.") - return - } - - defer res.Body.Close() - - bulletins := model.SecurityBulletinsFromJson(res.Body) - - for _, bulletin := range bulletins { - if bulletin.AppliesToVersion == model.CurrentVersion { - if props["SecurityBulletin_"+bulletin.Id] == "" { - users, userErr := s.Store.User().GetSystemAdminProfiles() - if userErr != nil { - mlog.Error("Failed to get system admins for security update information from Mattermost.") - return - } - - resBody, err := http.Get(PropSecurityURL + "/bulletins/" + bulletin.Id) - if err != nil { - mlog.Error("Failed to get security bulletin details") - return - } - - body, err := ioutil.ReadAll(resBody.Body) - resBody.Body.Close() - if err != nil || resBody.StatusCode != 200 { - mlog.Error("Failed to read security bulletin details") - return - } - - for _, user := range users { - mlog.Info("Sending security bulletin", mlog.String("bulletin_id", bulletin.Id), mlog.String("user_email", user.Email)) - license := s.License() - mailConfig := s.MailServiceConfig() - mail.SendMailUsingConfig(user.Email, i18n.T("mattermost.bulletin.subject"), string(body), mailConfig, license != nil && *license.Features.Compliance, "") - } - - bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id} - s.Store.System().Save(bulletinSeen) - } - } - } - } -} diff --git a/app/server.go b/app/server.go index 55f7b22f293..3315a347f7a 100644 --- a/app/server.go +++ b/app/server.go @@ -710,9 +710,6 @@ func maxInt(a, b int) int { } func (s *Server) runJobs() { - s.Go(func() { - runSecurityJob(s) - }) s.Go(func() { firstRun, err := s.getFirstServerRunTimestamp() if err != nil { @@ -1410,13 +1407,6 @@ func (s *Server) checkPushNotificationServerUrl() { } } -func runSecurityJob(s *Server) { - doSecurity(s) - model.CreateRecurringTask("Security", func() { - doSecurity(s) - }, time.Hour*4) -} - func runTokenCleanupJob(s *Server) { doTokenCleanup(s) model.CreateRecurringTask("Token Cleanup", func() { @@ -1478,10 +1468,6 @@ func runCheckAdminSupportStatusJob(a *App, c *request.Context) { }, time.Hour*model.WARN_METRIC_JOB_INTERVAL) } -func doSecurity(s *Server) { - s.DoSecurityUpdateCheck() -} - func doTokenCleanup(s *Server) { s.Store.Token().Cleanup() } @@ -1766,28 +1752,6 @@ func (s *Server) doLicenseExpirationCheck() { return } - users, err := s.Store.User().GetSystemAdminProfiles() - if err != nil { - mlog.Error("Failed to get system admins for license expired message from Mattermost.") - return - } - - //send email to admin(s) - for _, user := range users { - user := user - if user.Email == "" { - mlog.Error("Invalid system admin email.", mlog.String("user_email", user.Email)) - continue - } - - mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email)) - s.Go(func() { - if err := s.EmailService.SendRemoveExpiredLicenseEmail(user.Email, user.Locale, *s.Config().ServiceSettings.SiteURL); err != nil { - mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err)) - } - }) - } - //remove the license s.RemoveLicense() } diff --git a/app/user.go b/app/user.go index b0217969e6c..56c35cc9da5 100644 --- a/app/user.go +++ b/app/user.go @@ -2017,6 +2017,11 @@ func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provide } } + if oauthUser.Roles != user.Roles { + user.Roles = oauthUser.Roles + userAttrsChanged = true + } + if user.DeleteAt > 0 { // Make sure they are not disabled user.DeleteAt = 0 diff --git a/cmd/mattermost/main.go b/cmd/mattermost/main.go index 2d7a307771a..47bf78957e3 100644 --- a/cmd/mattermost/main.go +++ b/cmd/mattermost/main.go @@ -23,6 +23,7 @@ import ( _ "github.com/mattermost/mattermost-server/v5/app/slashcommands" // Plugins _ "github.com/mattermost/mattermost-server/v5/model/gitlab" + _ "github.com/mattermost/mattermost-server/v5/model/openid" // Enterprise Imports _ "github.com/mattermost/mattermost-server/v5/imports" ) diff --git a/config/client.go b/config/client.go index ca6a29dcd75..67b48dd3033 100644 --- a/config/client.go +++ b/config/client.go @@ -312,6 +312,10 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable) props["GuestAccountsEnforceMultifactorAuthentication"] = strconv.FormatBool(*c.GuestAccountsSettings.EnforceMultifactorAuthentication) + props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable) + props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor + props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText + if license != nil { if *license.Features.LDAP { props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable) @@ -337,12 +341,6 @@ func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *m props["EnableSignUpWithOffice365"] = strconv.FormatBool(*c.Office365Settings.Enable) } - if *license.Features.OpenId { - props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable) - props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor - props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText - } - if *license.Features.CustomTermsOfService { props["EnableCustomTermsOfService"] = strconv.FormatBool(*c.SupportSettings.CustomTermsOfServiceEnabled) props["CustomTermsOfServiceReAcceptancePeriod"] = strconv.FormatInt(int64(*c.SupportSettings.CustomTermsOfServiceReAcceptancePeriod), 10) diff --git a/model/openid/openid.go b/model/openid/openid.go new file mode 100644 index 00000000000..ce440c55dc6 --- /dev/null +++ b/model/openid/openid.go @@ -0,0 +1,151 @@ +package oauthopenid + +import ( + "encoding/json" + "net/http" + "io" + "io/ioutil" + "strings" + + "github.com/mattermost/mattermost-server/v5/einterfaces" + "github.com/mattermost/mattermost-server/v5/model" + "github.com/mattermost/mattermost-server/v5/services/httpservice" + "github.com/mattermost/mattermost-server/v5/shared/mlog" + "github.com/mattermost/mattermost-server/v5/utils/testutils" +) + +type OpenIdProvider struct { +} + +type OpenIdProviderUrls struct { + AuthEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + UserApiEndpoint string `json:"userinfo_endpoint"` +} + +// Keycloak sepcific +type OpenIdUser struct { + Id string `json:"uniqueId"` + Username string `json:"preferred_username"` + Email string `json:"email"` + FirstName string `json:"given_name"` + LastName string `json:"family_name"` + Roles []string `json:"roles"` +} + +func init() { + provider := &OpenIdProvider{} + einterfaces.RegisterOauthProvider(model.SERVICE_OPENID, provider) +} + +func userFromOpenIdUser(oiu *OpenIdUser) *model.User { + user := &model.User{} + username := oiu.Username + user.Username = model.CleanUsername(username) + user.FirstName = oiu.FirstName + user.LastName = oiu.LastName + user.Email = oiu.Email + user.Email = strings.ToLower(user.Email) + user.AuthData = model.NewString(oiu.getAuthData()) + user.AuthService = model.SERVICE_OPENID + var roles string + roles = model.SYSTEM_USER_ROLE_ID + for _, r := range oiu.Roles { + if r == "mattermost_admins" { + roles = model.SYSTEM_USER_ROLE_ID+" "+model.SYSTEM_ADMIN_ROLE_ID + } else { + mlog.Debug("Skipping unknown role when processing user: " + username + " role: " + r) + } + } + user.Roles = roles + mlog.Debug("Parsed user from openId as model user: " + user.ToJson()) + + return user +} + +func openIdUserFromJson(data io.Reader) (*OpenIdUser, error) { + var oiu OpenIdUser + body, err1 := ioutil.ReadAll(data) + if err1 != nil { + return nil, err1 + } + mlog.Debug("Received OpenID User data: " + string(body)) + err2 := json.Unmarshal(body, &oiu) + if err2 != nil { + return nil, err2 + } + return &oiu, nil +} + +func (oiu *OpenIdUser) ToJson() string { + b, err := json.Marshal(oiu) + if err != nil { + return "" + } + return string(b) +} + + +func (oiu *OpenIdUser) getAuthData() string { + return oiu.Id +} + +func (m *OpenIdProvider) GetUserFromJson(data io.Reader, tokenUser *model.User) (*model.User, error) { + oiu, err := openIdUserFromJson(data) + if err != nil { + return nil, err + } + return userFromOpenIdUser(oiu), nil +} + +func (m *OpenIdProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) { + // This is suuuper janky. But needed a good way to make the http client with just a config + h := httpservice.MakeHTTPService(&testutils.StaticConfigService{Cfg: config}) + req, err := http.NewRequest("GET", *config.OpenIdSettings.DiscoveryEndpoint, nil) + if err != nil { + mlog.Warn("Error while making discovery request", mlog.Err(err)) + return nil, err + } + + req.Header.Set("Accept", "application/json") + resp, err2 := h.MakeClient(true).Do(req) + if err2 != nil { + mlog.Warn("Error while fetching discovery request", mlog.Err(err2)) + return nil, err2 + } + + defer resp.Body.Close() + + var oidcUrls OpenIdProviderUrls + err3 := json.NewDecoder(resp.Body).Decode(&oidcUrls) + if err3 != nil { + mlog.Warn("Error while deserializing discovery response", mlog.Err(err3)) + return nil, err3 + } + + // Merge the 'discovered' endpoints with the original settings + newSettings := &model.SSOSettings{ + Enable: config.OpenIdSettings.Enable, + Id: config.OpenIdSettings.Id, + Secret: config.OpenIdSettings.Secret, + Scope: config.OpenIdSettings.Scope, + AuthEndpoint: &oidcUrls.AuthEndpoint, + TokenEndpoint: &oidcUrls.TokenEndpoint, + UserApiEndpoint: &oidcUrls.UserApiEndpoint, + DiscoveryEndpoint: config.OpenIdSettings.DiscoveryEndpoint, + ButtonText: config.OpenIdSettings.ButtonText, + ButtonColor: config.OpenIdSettings.ButtonColor, + } + return newSettings, nil +} + +func (m *OpenIdProvider) GetUserFromIdToken(idToken string) (*model.User, error) { + return nil, nil +} + +func (m *OpenIdProvider) IsSameUser(dbUser, oauthUser *model.User) bool { + // PCTE converted from emails as unique to SSO ID as unique + // so check IDs first (which will be in authData), then check email + return *dbUser.AuthData == *oauthUser.AuthData || + dbUser.Email == oauthUser.Email +} diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go index fd94fc63fff..8f895c32e21 100644 --- a/store/opentracinglayer/opentracinglayer.go +++ b/store/opentracinglayer/opentracinglayer.go @@ -9957,24 +9957,6 @@ func (s *OpenTracingLayerUserStore) GetRecentlyActiveUsersForTeam(teamID string, return result, err } -func (s *OpenTracingLayerUserStore) GetSystemAdminProfiles() (map[string]*model.User, error) { - origCtx := s.Root.Store.Context() - span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "UserStore.GetSystemAdminProfiles") - s.Root.Store.SetContext(newCtx) - defer func() { - s.Root.Store.SetContext(origCtx) - }() - - defer span.Finish() - result, err := s.UserStore.GetSystemAdminProfiles() - if err != nil { - span.LogFields(spanlog.Error(err)) - ext.Error.Set(span, true) - } - - return result, err -} - func (s *OpenTracingLayerUserStore) GetTeamGroupUsers(teamID string) ([]*model.User, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "UserStore.GetTeamGroupUsers") diff --git a/store/retrylayer/retrylayer.go b/store/retrylayer/retrylayer.go index dbef70d4acc..aed41883a86 100644 --- a/store/retrylayer/retrylayer.go +++ b/store/retrylayer/retrylayer.go @@ -10818,26 +10818,6 @@ func (s *RetryLayerUserStore) GetRecentlyActiveUsersForTeam(teamID string, offse } -func (s *RetryLayerUserStore) GetSystemAdminProfiles() (map[string]*model.User, error) { - - tries := 0 - for { - result, err := s.UserStore.GetSystemAdminProfiles() - if err == nil { - return result, nil - } - if !isRepeatableError(err) { - return result, err - } - tries++ - if tries >= 3 { - err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures") - return result, err - } - } - -} - func (s *RetryLayerUserStore) GetTeamGroupUsers(teamID string) ([]*model.User, error) { tries := 0 diff --git a/store/sqlstore/user_store.go b/store/sqlstore/user_store.go index ed390438d74..5ff68124451 100644 --- a/store/sqlstore/user_store.go +++ b/store/sqlstore/user_store.go @@ -1046,31 +1046,6 @@ func (us SqlUserStore) GetProfileByGroupChannelIdsForUser(userId string, channel return usersByChannelId, nil } -func (us SqlUserStore) GetSystemAdminProfiles() (map[string]*model.User, error) { - query := us.usersQuery. - Where("Roles LIKE ?", "%system_admin%"). - OrderBy("u.Username ASC") - - queryString, args, err := query.ToSql() - if err != nil { - return nil, errors.Wrap(err, "get_system_admin_profiles_tosql") - } - - var users []*model.User - if _, err := us.GetReplica().Select(&users, queryString, args...); err != nil { - return nil, errors.Wrap(err, "failed to find Users") - } - - userMap := make(map[string]*model.User) - - for _, u := range users { - u.Sanitize(map[string]bool{}) - userMap[u.Id] = u - } - - return userMap, nil -} - func (us SqlUserStore) GetByEmail(email string) (*model.User, error) { query := us.usersQuery.Where("Email = lower(?)", email) diff --git a/store/store.go b/store/store.go index 21c5dae0a59..2f2ff46456a 100644 --- a/store/store.go +++ b/store/store.go @@ -382,7 +382,6 @@ type UserStore interface { GetEtagForAllProfiles() string GetEtagForProfiles(teamID string) string UpdateFailedPasswordAttempts(userID string, attempts int) error - GetSystemAdminProfiles() (map[string]*model.User, error) PermanentDelete(userID string) error AnalyticsActiveCount(time int64, options model.UserCountOptions) (int64, error) AnalyticsActiveCountForPeriod(startTime int64, endTime int64, options model.UserCountOptions) (int64, error) diff --git a/store/storetest/mocks/UserStore.go b/store/storetest/mocks/UserStore.go index 7155381b2a3..f13254c1eb8 100644 --- a/store/storetest/mocks/UserStore.go +++ b/store/storetest/mocks/UserStore.go @@ -891,29 +891,6 @@ func (_m *UserStore) GetRecentlyActiveUsersForTeam(teamID string, offset int, li return r0, r1 } -// GetSystemAdminProfiles provides a mock function with given fields: -func (_m *UserStore) GetSystemAdminProfiles() (map[string]*model.User, error) { - ret := _m.Called() - - var r0 map[string]*model.User - if rf, ok := ret.Get(0).(func() map[string]*model.User); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(map[string]*model.User) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // GetTeamGroupUsers provides a mock function with given fields: teamID func (_m *UserStore) GetTeamGroupUsers(teamID string) ([]*model.User, error) { ret := _m.Called(teamID) diff --git a/store/storetest/user_store.go b/store/storetest/user_store.go index 5e91d50224c..547f5fcd2eb 100644 --- a/store/storetest/user_store.go +++ b/store/storetest/user_store.go @@ -59,7 +59,6 @@ func TestUserStore(t *testing.T, ss store.Store, s SqlStore) { t.Run("GetProfilesByIds", func(t *testing.T) { testUserStoreGetProfilesByIds(t, ss) }) t.Run("GetProfileByGroupChannelIdsForUser", func(t *testing.T) { testUserStoreGetProfileByGroupChannelIdsForUser(t, ss) }) t.Run("GetProfilesByUsernames", func(t *testing.T) { testUserStoreGetProfilesByUsernames(t, ss) }) - t.Run("GetSystemAdminProfiles", func(t *testing.T) { testUserStoreGetSystemAdminProfiles(t, ss) }) t.Run("GetByEmail", func(t *testing.T) { testUserStoreGetByEmail(t, ss) }) t.Run("GetByAuthData", func(t *testing.T) { testUserStoreGetByAuthData(t, ss) }) t.Run("GetByUsername", func(t *testing.T) { testUserStoreGetByUsername(t, ss) }) @@ -1755,56 +1754,6 @@ func testUserStoreGetProfilesByUsernames(t *testing.T, ss store.Store) { }) } -func testUserStoreGetSystemAdminProfiles(t *testing.T, ss store.Store) { - teamId := model.NewId() - - u1, err := ss.User().Save(&model.User{ - Email: MakeEmail(), - Roles: model.SYSTEM_USER_ROLE_ID + " " + model.SYSTEM_ADMIN_ROLE_ID, - Username: "u1" + model.NewId(), - }) - require.NoError(t, err) - defer func() { require.NoError(t, ss.User().PermanentDelete(u1.Id)) }() - _, nErr := ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, -1) - require.NoError(t, nErr) - - u2, err := ss.User().Save(&model.User{ - Email: MakeEmail(), - Username: "u2" + model.NewId(), - }) - require.NoError(t, err) - defer func() { require.NoError(t, ss.User().PermanentDelete(u2.Id)) }() - _, nErr = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}, -1) - require.NoError(t, nErr) - - u3, err := ss.User().Save(&model.User{ - Email: MakeEmail(), - Roles: model.SYSTEM_USER_ROLE_ID + " " + model.SYSTEM_ADMIN_ROLE_ID, - Username: "u3" + model.NewId(), - }) - require.NoError(t, err) - defer func() { require.NoError(t, ss.User().PermanentDelete(u3.Id)) }() - _, nErr = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u3.Id}, -1) - require.NoError(t, nErr) - _, nErr = ss.Bot().Save(&model.Bot{ - UserId: u3.Id, - Username: u3.Username, - OwnerId: u1.Id, - }) - require.NoError(t, nErr) - u3.IsBot = true - defer func() { require.NoError(t, ss.Bot().PermanentDelete(u3.Id)) }() - - t.Run("all system admin profiles", func(t *testing.T) { - result, userError := ss.User().GetSystemAdminProfiles() - require.NoError(t, userError) - assert.Equal(t, map[string]*model.User{ - u1.Id: sanitized(u1), - u3.Id: sanitized(u3), - }, result) - }) -} - func testUserStoreGetByEmail(t *testing.T, ss store.Store) { teamId := model.NewId() diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go index b53f1792afd..75110f0ba73 100644 --- a/store/timerlayer/timerlayer.go +++ b/store/timerlayer/timerlayer.go @@ -8980,22 +8980,6 @@ func (s *TimerLayerUserStore) GetRecentlyActiveUsersForTeam(teamID string, offse return result, err } -func (s *TimerLayerUserStore) GetSystemAdminProfiles() (map[string]*model.User, error) { - start := timemodule.Now() - - result, err := s.UserStore.GetSystemAdminProfiles() - - elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second) - if s.Root.Metrics != nil { - success := "false" - if err == nil { - success = "true" - } - s.Root.Metrics.ObserveStoreMethodDuration("UserStore.GetSystemAdminProfiles", success, elapsed) - } - return result, err -} - func (s *TimerLayerUserStore) GetTeamGroupUsers(teamID string) ([]*model.User, error) { start := timemodule.Now()