[MM-37984] Allow Desktop App to authenticate via external providers outside of the app on supported servers (#23795)
* WIP * Add rate limiting for desktop token API * Missing mocks * Style fixes * Update snapshots * Maybe use an actual redirect link :P * Refactoring for tests * Add tests for server * Fix lint issue * Fix tests * Fix lint * Add front-end screen component * Component logic * Style changes * Quick style fix * Lint fixes * Initial PR feedback * Enable logging into the browser as well when completing the login process * Refactor to push more logic to the other component * Remove unnecessary helper code * Fix i18n --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5e3c03a0a8
Коммит
abdf4e58c3
@@ -433,6 +433,7 @@ type AppIface interface {
|
||||
AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request)
|
||||
AttachDeviceId(sessionID string, deviceID string, expiresAt int64) *model.AppError
|
||||
AttachSessionCookies(c *request.Context, w http.ResponseWriter, r *http.Request)
|
||||
AuthenticateDesktopToken(token string, expiryTime int64, user *model.User) *model.AppError
|
||||
AuthenticateUserForLogin(c *request.Context, id, loginId, password, mfaToken, cwsToken string, ldapOnly bool) (user *model.User, err *model.AppError)
|
||||
AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service, code, state, redirectURI string) (io.ReadCloser, string, map[string]string, *model.User, *model.AppError)
|
||||
AutocompleteChannels(c request.CTX, userID, term string) (model.ChannelListWithTeamData, *model.AppError)
|
||||
@@ -485,6 +486,7 @@ type AppIface interface {
|
||||
CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError)
|
||||
CreateCommand(cmd *model.Command) (*model.Command, *model.AppError)
|
||||
CreateCommandWebhook(commandID string, args *model.CommandArgs) (*model.CommandWebhook, *model.AppError)
|
||||
CreateDesktopToken(token string, createdAt int64) *model.AppError
|
||||
CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError)
|
||||
CreateGroup(group *model.Group) (*model.Group, *model.AppError)
|
||||
CreateGroupChannel(c request.CTX, userIDs []string, creatorId string) (*model.Channel, *model.AppError)
|
||||
@@ -701,8 +703,8 @@ type AppIface interface {
|
||||
GetOAuthAppsByCreator(userID string, page, perPage int) ([]*model.OAuthApp, *model.AppError)
|
||||
GetOAuthCodeRedirect(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
||||
GetOAuthImplicitRedirect(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
||||
GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool) (string, *model.AppError)
|
||||
GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string) (string, *model.AppError)
|
||||
GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool, desktopToken string) (string, *model.AppError)
|
||||
GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string, desktopToken string) (string, *model.AppError)
|
||||
GetOAuthStateToken(token string) (*model.Token, *model.AppError)
|
||||
GetOnboarding() (*model.System, *model.AppError)
|
||||
GetOpenGraphMetadata(requestURL string) ([]byte, error)
|
||||
@@ -1176,6 +1178,7 @@ type AppIface interface {
|
||||
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool
|
||||
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
|
||||
UserIsFirstAdmin(user *model.User) bool
|
||||
ValidateDesktopToken(token string, expiryTime int64) (*model.User, *model.AppError)
|
||||
VerifyEmailFromToken(c request.CTX, userSuppliedTokenString string) *model.AppError
|
||||
VerifyUserEmail(userID, email string) *model.AppError
|
||||
ViewChannel(c request.CTX, view *model.ChannelView, userID string, currentSessionId string, collapsedThreadsSupported bool) (map[string]int64, *model.AppError)
|
||||
|
||||
78
server/channels/app/desktop_login.go
Обычный файл
78
server/channels/app/desktop_login.go
Обычный файл
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
func (a *App) CreateDesktopToken(token string, createdAt int64) *model.AppError {
|
||||
// Check if the token already exists in the database somehow
|
||||
// If so return an error
|
||||
_, getErr := a.Srv().Store().DesktopTokens().GetUserId(token, 0)
|
||||
if getErr == nil {
|
||||
return model.NewAppError("CreateDesktopToken", "app.desktop_token.create.collision", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Create token in the database
|
||||
err := a.Srv().Store().DesktopTokens().Insert(token, createdAt, nil)
|
||||
if err != nil {
|
||||
return model.NewAppError("CreateDesktopToken", "app.desktop_token.create.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) AuthenticateDesktopToken(token string, expiryTime int64, user *model.User) *model.AppError {
|
||||
// Throw an error if the token is expired
|
||||
err := a.Srv().Store().DesktopTokens().SetUserId(token, expiryTime, user.Id)
|
||||
if err != nil {
|
||||
// Delete the token if it is expired
|
||||
a.Srv().Go(func() {
|
||||
a.Srv().Store().DesktopTokens().Delete(token)
|
||||
})
|
||||
|
||||
return model.NewAppError("AuthenticateDesktopToken", "app.desktop_token.authenticate.invalid_or_expired", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ValidateDesktopToken(token string, expiryTime int64) (*model.User, *model.AppError) {
|
||||
// Check if token is expired
|
||||
userId, err := a.Srv().Store().DesktopTokens().GetUserId(token, expiryTime)
|
||||
if err != nil {
|
||||
// Delete the token if it is expired
|
||||
a.Srv().Go(func() {
|
||||
a.Srv().Store().DesktopTokens().Delete(token)
|
||||
})
|
||||
|
||||
return nil, model.NewAppError("ValidateDesktopToken", "app.desktop_token.validate.expired", nil, err.Error(), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// If there's no user id, it's not authenticated yet
|
||||
if userId == "" {
|
||||
return nil, model.NewAppError("ValidateDesktopToken", "app.desktop_token.validate.invalid", nil, "", http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// Get the user profile
|
||||
user, userErr := a.GetUser(userId)
|
||||
if userErr != nil {
|
||||
// Delete the token if the user is invalid somehow
|
||||
a.Srv().Go(func() {
|
||||
a.Srv().Store().DesktopTokens().Delete(token)
|
||||
})
|
||||
|
||||
return nil, model.NewAppError("ValidateDesktopToken", "app.desktop_token.validate.no_user", nil, userErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Clean up other tokens if they exist
|
||||
a.Srv().Go(func() {
|
||||
a.Srv().Store().DesktopTokens().DeleteByUserId(userId)
|
||||
})
|
||||
|
||||
return user, nil
|
||||
}
|
||||
124
server/channels/app/desktop_login_test.go
Обычный файл
124
server/channels/app/desktop_login_test.go
Обычный файл
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
TTL = time.Minute * 3
|
||||
ExpiredLength = time.Minute * 10
|
||||
)
|
||||
|
||||
func TestCreateDesktopToken(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
existingErr := th.App.CreateDesktopToken("existing_token", time.Now().Unix())
|
||||
require.Nil(t, existingErr)
|
||||
|
||||
t.Run("create token", func(t *testing.T) {
|
||||
err := th.App.CreateDesktopToken("new_token", time.Now().Unix())
|
||||
assert.Nil(t, err)
|
||||
|
||||
user, err := th.App.ValidateDesktopToken("new_token", time.Now().Add(-TTL).Unix())
|
||||
assert.Nil(t, user)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "app.desktop_token.validate.invalid", err.Id)
|
||||
})
|
||||
|
||||
t.Run("create token - already exists", func(t *testing.T) {
|
||||
err := th.App.CreateDesktopToken("existing_token", time.Now().Unix())
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "app.desktop_token.create.collision", err.Id)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuthenticateDesktopToken(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
err := th.App.CreateDesktopToken("unauthenticated_token", time.Now().Unix())
|
||||
require.Nil(t, err)
|
||||
|
||||
err = th.App.CreateDesktopToken("expired_token", time.Now().Add(-ExpiredLength).Unix())
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("authenticate token", func(t *testing.T) {
|
||||
err := th.App.AuthenticateDesktopToken("unauthenticated_token", time.Now().Add(-TTL).Unix(), th.BasicUser)
|
||||
assert.Nil(t, err)
|
||||
|
||||
user, err := th.App.ValidateDesktopToken("unauthenticated_token", time.Now().Add(-TTL).Unix())
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, user)
|
||||
assert.Equal(t, th.BasicUser.Id, user.Id)
|
||||
})
|
||||
|
||||
t.Run("authenticate token - expired", func(t *testing.T) {
|
||||
err := th.App.AuthenticateDesktopToken("expired_token", time.Now().Add(-TTL).Unix(), th.BasicUser)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "app.desktop_token.authenticate.invalid_or_expired", err.Id)
|
||||
|
||||
_, err = th.App.ValidateDesktopToken("expired_token", time.Now().Add(-TTL).Unix())
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestValidateDesktopToken(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
err := th.App.CreateDesktopToken("authenticated_token", time.Now().Unix())
|
||||
require.Nil(t, err)
|
||||
err = th.App.AuthenticateDesktopToken("authenticated_token", time.Now().Add(-TTL).Unix(), th.BasicUser)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = th.App.CreateDesktopToken("expired_token_2", time.Now().Add(-ExpiredLength).Unix())
|
||||
require.Nil(t, err)
|
||||
err = th.App.AuthenticateDesktopToken("expired_token_2", time.Now().Add(-ExpiredLength).Unix(), th.BasicUser)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = th.App.CreateDesktopToken("unauthenticated_token_2", time.Now().Unix())
|
||||
require.Nil(t, err)
|
||||
|
||||
badUser := model.User{Id: "some_garbage_user_id"}
|
||||
err = th.App.CreateDesktopToken("authenticated_token_bad_user", time.Now().Unix())
|
||||
require.Nil(t, err)
|
||||
err = th.App.AuthenticateDesktopToken("authenticated_token_bad_user", time.Now().Add(-TTL).Unix(), &badUser)
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("validate token", func(t *testing.T) {
|
||||
user, err := th.App.ValidateDesktopToken("authenticated_token", time.Now().Add(-TTL).Unix())
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, user)
|
||||
assert.Equal(t, th.BasicUser.Id, user.Id)
|
||||
})
|
||||
|
||||
t.Run("validate token - expired", func(t *testing.T) {
|
||||
user, err := th.App.ValidateDesktopToken("expired_token_2", time.Now().Add(-TTL).Unix())
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, user)
|
||||
assert.Equal(t, "app.desktop_token.validate.expired", err.Id)
|
||||
})
|
||||
|
||||
t.Run("validate token - not authenticated", func(t *testing.T) {
|
||||
user, err := th.App.ValidateDesktopToken("unauthenticated_token_2", time.Now().Add(-TTL).Unix())
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, user)
|
||||
assert.Equal(t, "app.desktop_token.validate.invalid", err.Id)
|
||||
})
|
||||
|
||||
t.Run("validate token - bad user id", func(t *testing.T) {
|
||||
user, err := th.App.ValidateDesktopToken("authenticated_token_bad_user", time.Now().Add(-TTL).Unix())
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, user)
|
||||
assert.Equal(t, "app.desktop_token.validate.no_user", err.Id)
|
||||
})
|
||||
}
|
||||
@@ -431,7 +431,7 @@ func (a *App) newSessionUpdateToken(app *model.OAuthApp, accessData *model.Acces
|
||||
return accessRsp, nil
|
||||
}
|
||||
|
||||
func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool) (string, *model.AppError) {
|
||||
func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool, desktopToken string) (string, *model.AppError) {
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = action
|
||||
if teamID != "" {
|
||||
@@ -442,6 +442,10 @@ func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, serv
|
||||
stateProps["redirect_to"] = redirectTo
|
||||
}
|
||||
|
||||
if desktopToken != "" {
|
||||
stateProps["desktop_token"] = desktopToken
|
||||
}
|
||||
|
||||
stateProps[model.UserAuthServiceIsMobile] = strconv.FormatBool(isMobile)
|
||||
|
||||
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, loginHint)
|
||||
@@ -452,13 +456,17 @@ func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, serv
|
||||
return authURL, nil
|
||||
}
|
||||
|
||||
func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string) (string, *model.AppError) {
|
||||
func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string, desktopToken string) (string, *model.AppError) {
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAuthActionSignup
|
||||
if teamID != "" {
|
||||
stateProps["team_id"] = teamID
|
||||
}
|
||||
|
||||
if desktopToken != "" {
|
||||
stateProps["desktop_token"] = desktopToken
|
||||
}
|
||||
|
||||
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -771,6 +771,28 @@ func (a *OpenTracingAppLayer) AttachSessionCookies(c *request.Context, w http.Re
|
||||
a.app.AttachSessionCookies(c, w, r)
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AuthenticateDesktopToken(token string, expiryTime int64, user *model.User) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AuthenticateDesktopToken")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.AuthenticateDesktopToken(token, expiryTime, user)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) AuthenticateUserForLogin(c *request.Context, id string, loginId string, password string, mfaToken string, cwsToken string, ldapOnly bool) (user *model.User, err *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AuthenticateUserForLogin")
|
||||
@@ -2042,6 +2064,28 @@ func (a *OpenTracingAppLayer) CreateDefaultMemberships(c *request.Context, param
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) CreateDesktopToken(token string, createdAt int64) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateDesktopToken")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.CreateDesktopToken(token, createdAt)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateEmoji")
|
||||
@@ -7508,7 +7552,7 @@ func (a *OpenTracingAppLayer) GetOAuthImplicitRedirect(userID string, authReques
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string, action string, redirectTo string, loginHint string, isMobile bool) (string, *model.AppError) {
|
||||
func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string, action string, redirectTo string, loginHint string, isMobile bool, desktopToken string) (string, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthLoginEndpoint")
|
||||
|
||||
@@ -7520,7 +7564,7 @@ func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *ht
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetOAuthLoginEndpoint(w, r, service, teamID, action, redirectTo, loginHint, isMobile)
|
||||
resultVar0, resultVar1 := a.app.GetOAuthLoginEndpoint(w, r, service, teamID, action, redirectTo, loginHint, isMobile, desktopToken)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
@@ -7530,7 +7574,7 @@ func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *ht
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string) (string, *model.AppError) {
|
||||
func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string, desktopToken string) (string, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthSignupEndpoint")
|
||||
|
||||
@@ -7542,7 +7586,7 @@ func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *h
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.GetOAuthSignupEndpoint(w, r, service, teamID)
|
||||
resultVar0, resultVar1 := a.app.GetOAuthSignupEndpoint(w, r, service, teamID, desktopToken)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
@@ -18676,6 +18720,28 @@ func (a *OpenTracingAppLayer) UserIsInAdminRoleGroup(userID string, syncableID s
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) ValidateDesktopToken(token string, expiryTime int64) (*model.User, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ValidateDesktopToken")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0, resultVar1 := a.app.ValidateDesktopToken(token, expiryTime)
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) ValidateUserPermissionsOnChannels(c request.CTX, userId string, channelIds []string) []string {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ValidateUserPermissionsOnChannels")
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/mattermost/mattermost/server/v8/channels/audit"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/active_users"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/cleanup_desktop_tokens"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/expirynotify"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_delete"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_process"
|
||||
@@ -1635,6 +1636,12 @@ func (s *Server) initJobs() {
|
||||
hosted_purchase_screening.MakeScheduler(s.Jobs, s.License()),
|
||||
)
|
||||
|
||||
s.Jobs.RegisterJobType(
|
||||
model.JobTypeCleanupDesktopTokens,
|
||||
cleanup_desktop_tokens.MakeWorker(s.Jobs, s.Store()),
|
||||
cleanup_desktop_tokens.MakeScheduler(s.Jobs),
|
||||
)
|
||||
|
||||
s.platform.Jobs = s.Jobs
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user