Revert "[MM-37984] Allow Desktop App to authenticate via external providers outside of the app on supported servers (#23795)" (#24039)
This reverts commit abdf4e58c3.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
38fd8cd6aa
Коммит
823ef27d6c
@@ -9,8 +9,6 @@ import (
|
|||||||
"github.com/mattermost/gziphandler"
|
"github.com/mattermost/gziphandler"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/app"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/web"
|
"github.com/mattermost/mattermost/server/v8/channels/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -202,17 +200,6 @@ func (api *API) APILocal(h handlerFunc) http.Handler {
|
|||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) RateLimitedHandler(apiHandler http.Handler, settings model.RateLimitSettings) http.Handler {
|
|
||||||
settings.SetDefaults()
|
|
||||||
|
|
||||||
rateLimiter, err := app.NewRateLimiter(&settings, []string{})
|
|
||||||
if err != nil {
|
|
||||||
mlog.Error("getRateLimitedHandler", mlog.Err(err))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return rateLimiter.RateLimitHandler(apiHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func requireLicense(c *Context) *model.AppError {
|
func requireLicense(c *Context) *model.AppError {
|
||||||
if c.App.Channels().License() == nil {
|
if c.App.Channels().License() == nil {
|
||||||
err := model.NewAppError("", "api.license_error", nil, "", http.StatusNotImplemented)
|
err := model.NewAppError("", "api.license_error", nil, "", http.StatusNotImplemented)
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ func (api *API) InitUser() {
|
|||||||
api.BaseRoutes.User.Handle("/mfa/generate", api.APISessionRequiredMfa(generateMfaSecret)).Methods("POST")
|
api.BaseRoutes.User.Handle("/mfa/generate", api.APISessionRequiredMfa(generateMfaSecret)).Methods("POST")
|
||||||
|
|
||||||
api.BaseRoutes.Users.Handle("/login", api.APIHandler(login)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/login", api.APIHandler(login)).Methods("POST")
|
||||||
api.BaseRoutes.Users.Handle("/login/desktop_token", api.RateLimitedHandler(api.APIHandler(loginWithDesktopToken), model.RateLimitSettings{PerSec: model.NewInt(2), MaxBurst: model.NewInt(1)})).Methods("POST")
|
|
||||||
api.BaseRoutes.Users.Handle("/login/switch", api.APIHandler(switchAccountType)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/login/switch", api.APIHandler(switchAccountType)).Methods("POST")
|
||||||
api.BaseRoutes.Users.Handle("/login/cws", api.APIHandlerTrustRequester(loginCWS)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/login/cws", api.APIHandlerTrustRequester(loginCWS)).Methods("POST")
|
||||||
api.BaseRoutes.Users.Handle("/logout", api.APIHandler(logout)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/logout", api.APIHandler(logout)).Methods("POST")
|
||||||
@@ -1967,30 +1966,6 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loginWithDesktopToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
props := model.MapFromJSON(r.Body)
|
|
||||||
token := props["token"]
|
|
||||||
deviceId := props["device_id"]
|
|
||||||
|
|
||||||
user, err := c.App.ValidateDesktopToken(token, time.Now().Add(-model.DesktopTokenTTL).Unix())
|
|
||||||
if err != nil {
|
|
||||||
c.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.App.DoLogin(c.AppContext, w, r, user, deviceId, false, false, false)
|
|
||||||
if err != nil {
|
|
||||||
c.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(user); err != nil {
|
|
||||||
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loginCWS(c *Context, w http.ResponseWriter, r *http.Request) {
|
func loginCWS(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
campaignToURL := map[string]string{
|
campaignToURL := map[string]string{
|
||||||
"focalboard": "/boards",
|
"focalboard": "/boards",
|
||||||
|
|||||||
@@ -433,7 +433,6 @@ type AppIface interface {
|
|||||||
AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request)
|
AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request)
|
||||||
AttachDeviceId(sessionID string, deviceID string, expiresAt int64) *model.AppError
|
AttachDeviceId(sessionID string, deviceID string, expiresAt int64) *model.AppError
|
||||||
AttachSessionCookies(c *request.Context, w http.ResponseWriter, r *http.Request)
|
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)
|
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)
|
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)
|
AutocompleteChannels(c request.CTX, userID, term string) (model.ChannelListWithTeamData, *model.AppError)
|
||||||
@@ -486,7 +485,6 @@ type AppIface interface {
|
|||||||
CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError)
|
CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError)
|
||||||
CreateCommand(cmd *model.Command) (*model.Command, *model.AppError)
|
CreateCommand(cmd *model.Command) (*model.Command, *model.AppError)
|
||||||
CreateCommandWebhook(commandID string, args *model.CommandArgs) (*model.CommandWebhook, *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)
|
CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError)
|
||||||
CreateGroup(group *model.Group) (*model.Group, *model.AppError)
|
CreateGroup(group *model.Group) (*model.Group, *model.AppError)
|
||||||
CreateGroupChannel(c request.CTX, userIDs []string, creatorId string) (*model.Channel, *model.AppError)
|
CreateGroupChannel(c request.CTX, userIDs []string, creatorId string) (*model.Channel, *model.AppError)
|
||||||
@@ -703,8 +701,8 @@ type AppIface interface {
|
|||||||
GetOAuthAppsByCreator(userID string, page, perPage int) ([]*model.OAuthApp, *model.AppError)
|
GetOAuthAppsByCreator(userID string, page, perPage int) ([]*model.OAuthApp, *model.AppError)
|
||||||
GetOAuthCodeRedirect(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
GetOAuthCodeRedirect(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError)
|
||||||
GetOAuthImplicitRedirect(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, desktopToken string) (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, desktopToken string) (string, *model.AppError)
|
GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string) (string, *model.AppError)
|
||||||
GetOAuthStateToken(token string) (*model.Token, *model.AppError)
|
GetOAuthStateToken(token string) (*model.Token, *model.AppError)
|
||||||
GetOnboarding() (*model.System, *model.AppError)
|
GetOnboarding() (*model.System, *model.AppError)
|
||||||
GetOpenGraphMetadata(requestURL string) ([]byte, error)
|
GetOpenGraphMetadata(requestURL string) ([]byte, error)
|
||||||
@@ -1178,7 +1176,6 @@ type AppIface interface {
|
|||||||
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool
|
UserAlreadyNotifiedOnRequiredFeature(user string, feature model.MattermostFeature) bool
|
||||||
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
|
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
|
||||||
UserIsFirstAdmin(user *model.User) bool
|
UserIsFirstAdmin(user *model.User) bool
|
||||||
ValidateDesktopToken(token string, expiryTime int64) (*model.User, *model.AppError)
|
|
||||||
VerifyEmailFromToken(c request.CTX, userSuppliedTokenString string) *model.AppError
|
VerifyEmailFromToken(c request.CTX, userSuppliedTokenString string) *model.AppError
|
||||||
VerifyUserEmail(userID, email 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)
|
ViewChannel(c request.CTX, view *model.ChannelView, userID string, currentSessionId string, collapsedThreadsSupported bool) (map[string]int64, *model.AppError)
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
// 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
|
return accessRsp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool, desktopToken string) (string, *model.AppError) {
|
func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, service, teamID, action, redirectTo, loginHint string, isMobile bool) (string, *model.AppError) {
|
||||||
stateProps := map[string]string{}
|
stateProps := map[string]string{}
|
||||||
stateProps["action"] = action
|
stateProps["action"] = action
|
||||||
if teamID != "" {
|
if teamID != "" {
|
||||||
@@ -442,10 +442,6 @@ func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, serv
|
|||||||
stateProps["redirect_to"] = redirectTo
|
stateProps["redirect_to"] = redirectTo
|
||||||
}
|
}
|
||||||
|
|
||||||
if desktopToken != "" {
|
|
||||||
stateProps["desktop_token"] = desktopToken
|
|
||||||
}
|
|
||||||
|
|
||||||
stateProps[model.UserAuthServiceIsMobile] = strconv.FormatBool(isMobile)
|
stateProps[model.UserAuthServiceIsMobile] = strconv.FormatBool(isMobile)
|
||||||
|
|
||||||
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, loginHint)
|
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, loginHint)
|
||||||
@@ -456,17 +452,13 @@ func (a *App) GetOAuthLoginEndpoint(w http.ResponseWriter, r *http.Request, serv
|
|||||||
return authURL, nil
|
return authURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string, desktopToken string) (string, *model.AppError) {
|
func (a *App) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service, teamID string) (string, *model.AppError) {
|
||||||
stateProps := map[string]string{}
|
stateProps := map[string]string{}
|
||||||
stateProps["action"] = model.OAuthActionSignup
|
stateProps["action"] = model.OAuthActionSignup
|
||||||
if teamID != "" {
|
if teamID != "" {
|
||||||
stateProps["team_id"] = teamID
|
stateProps["team_id"] = teamID
|
||||||
}
|
}
|
||||||
|
|
||||||
if desktopToken != "" {
|
|
||||||
stateProps["desktop_token"] = desktopToken
|
|
||||||
}
|
|
||||||
|
|
||||||
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, "")
|
authURL, err := a.GetAuthorizationCode(w, r, service, stateProps, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -771,28 +771,6 @@ func (a *OpenTracingAppLayer) AttachSessionCookies(c *request.Context, w http.Re
|
|||||||
a.app.AttachSessionCookies(c, w, r)
|
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) {
|
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
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AuthenticateUserForLogin")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AuthenticateUserForLogin")
|
||||||
@@ -2064,28 +2042,6 @@ func (a *OpenTracingAppLayer) CreateDefaultMemberships(c *request.Context, param
|
|||||||
return resultVar0
|
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) {
|
func (a *OpenTracingAppLayer) CreateEmoji(c request.CTX, sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateEmoji")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateEmoji")
|
||||||
@@ -7552,7 +7508,7 @@ func (a *OpenTracingAppLayer) GetOAuthImplicitRedirect(userID string, authReques
|
|||||||
return resultVar0, resultVar1
|
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, desktopToken string) (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) (string, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthLoginEndpoint")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthLoginEndpoint")
|
||||||
|
|
||||||
@@ -7564,7 +7520,7 @@ func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *ht
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0, resultVar1 := a.app.GetOAuthLoginEndpoint(w, r, service, teamID, action, redirectTo, loginHint, isMobile, desktopToken)
|
resultVar0, resultVar1 := a.app.GetOAuthLoginEndpoint(w, r, service, teamID, action, redirectTo, loginHint, isMobile)
|
||||||
|
|
||||||
if resultVar1 != nil {
|
if resultVar1 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar1))
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
@@ -7574,7 +7530,7 @@ func (a *OpenTracingAppLayer) GetOAuthLoginEndpoint(w http.ResponseWriter, r *ht
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string, desktopToken string) (string, *model.AppError) {
|
func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *http.Request, service string, teamID string) (string, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthSignupEndpoint")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOAuthSignupEndpoint")
|
||||||
|
|
||||||
@@ -7586,7 +7542,7 @@ func (a *OpenTracingAppLayer) GetOAuthSignupEndpoint(w http.ResponseWriter, r *h
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
defer span.Finish()
|
defer span.Finish()
|
||||||
resultVar0, resultVar1 := a.app.GetOAuthSignupEndpoint(w, r, service, teamID, desktopToken)
|
resultVar0, resultVar1 := a.app.GetOAuthSignupEndpoint(w, r, service, teamID)
|
||||||
|
|
||||||
if resultVar1 != nil {
|
if resultVar1 != nil {
|
||||||
span.LogFields(spanlog.Error(resultVar1))
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
@@ -18720,28 +18676,6 @@ func (a *OpenTracingAppLayer) UserIsInAdminRoleGroup(userID string, syncableID s
|
|||||||
return resultVar0, resultVar1
|
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 {
|
func (a *OpenTracingAppLayer) ValidateUserPermissionsOnChannels(c request.CTX, userId string, channelIds []string) []string {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ValidateUserPermissionsOnChannels")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ValidateUserPermissionsOnChannels")
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost/server/v8/channels/audit"
|
"github.com/mattermost/mattermost/server/v8/channels/audit"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
"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/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/expirynotify"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_delete"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_delete"
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_process"
|
"github.com/mattermost/mattermost/server/v8/channels/jobs/export_process"
|
||||||
@@ -1636,12 +1635,6 @@ func (s *Server) initJobs() {
|
|||||||
hosted_purchase_screening.MakeScheduler(s.Jobs, s.License()),
|
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
|
s.platform.Jobs = s.Jobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -218,8 +218,6 @@ channels/db/migrations/mysql/000108_remove_orphaned_oauth_preferences.down.sql
|
|||||||
channels/db/migrations/mysql/000108_remove_orphaned_oauth_preferences.up.sql
|
channels/db/migrations/mysql/000108_remove_orphaned_oauth_preferences.up.sql
|
||||||
channels/db/migrations/mysql/000109_create_persistent_notifications.down.sql
|
channels/db/migrations/mysql/000109_create_persistent_notifications.down.sql
|
||||||
channels/db/migrations/mysql/000109_create_persistent_notifications.up.sql
|
channels/db/migrations/mysql/000109_create_persistent_notifications.up.sql
|
||||||
channels/db/migrations/mysql/000110_create_desktop_tokens.down.sql
|
|
||||||
channels/db/migrations/mysql/000110_create_desktop_tokens.up.sql
|
|
||||||
channels/db/migrations/postgres/000001_create_teams.down.sql
|
channels/db/migrations/postgres/000001_create_teams.down.sql
|
||||||
channels/db/migrations/postgres/000001_create_teams.up.sql
|
channels/db/migrations/postgres/000001_create_teams.up.sql
|
||||||
channels/db/migrations/postgres/000002_create_team_members.down.sql
|
channels/db/migrations/postgres/000002_create_team_members.down.sql
|
||||||
@@ -438,5 +436,3 @@ channels/db/migrations/postgres/000108_remove_orphaned_oauth_preferences.down.sq
|
|||||||
channels/db/migrations/postgres/000108_remove_orphaned_oauth_preferences.up.sql
|
channels/db/migrations/postgres/000108_remove_orphaned_oauth_preferences.up.sql
|
||||||
channels/db/migrations/postgres/000109_create_persistent_notifications.down.sql
|
channels/db/migrations/postgres/000109_create_persistent_notifications.down.sql
|
||||||
channels/db/migrations/postgres/000109_create_persistent_notifications.up.sql
|
channels/db/migrations/postgres/000109_create_persistent_notifications.up.sql
|
||||||
channels/db/migrations/postgres/000110_create_desktop_tokens.down.sql
|
|
||||||
channels/db/migrations/postgres/000110_create_desktop_tokens.up.sql
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS DesktopTokens;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS DesktopTokens (
|
|
||||||
DesktopToken varchar(64) NOT NULL,
|
|
||||||
UserId varchar(26) NULL,
|
|
||||||
CreateAt bigint NOT NULL,
|
|
||||||
PRIMARY KEY (DesktopToken)
|
|
||||||
);
|
|
||||||
|
|
||||||
SET @preparedStatement = (SELECT IF(
|
|
||||||
(
|
|
||||||
SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS
|
|
||||||
WHERE table_name = 'DesktopTokens'
|
|
||||||
AND table_schema = DATABASE()
|
|
||||||
AND index_name = 'idx_desktoptokens_createat'
|
|
||||||
) > 0,
|
|
||||||
'SELECT 1',
|
|
||||||
'CREATE INDEX idx_desktoptokens_createat ON DesktopTokens(CreateAt);'
|
|
||||||
));
|
|
||||||
|
|
||||||
PREPARE createIndexIfNotExists FROM @preparedStatement;
|
|
||||||
EXECUTE createIndexIfNotExists;
|
|
||||||
DEALLOCATE PREPARE createIndexIfNotExists;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS desktoptokens;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS desktoptokens (
|
|
||||||
desktoptoken VARCHAR(64) NOT NULL,
|
|
||||||
userid VARCHAR(26),
|
|
||||||
createat BIGINT NOT NULL,
|
|
||||||
PRIMARY KEY (desktoptoken)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_desktoptokens_createat ON desktoptokens(createat);
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
package cleanup_desktop_tokens
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
|
||||||
)
|
|
||||||
|
|
||||||
const schedFreq = 1 * time.Hour
|
|
||||||
|
|
||||||
func MakeScheduler(jobServer *jobs.JobServer) model.Scheduler {
|
|
||||||
isEnabled := func(cfg *model.Config) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return jobs.NewPeriodicScheduler(jobServer, model.JobTypeCleanupDesktopTokens, schedFreq, isEnabled)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
package cleanup_desktop_tokens
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/jobs"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/platform/services/configservice"
|
|
||||||
)
|
|
||||||
|
|
||||||
const jobName = "CleanupDesktopTokens"
|
|
||||||
const maxAge = 5 * time.Minute
|
|
||||||
|
|
||||||
type AppIface interface {
|
|
||||||
configservice.ConfigService
|
|
||||||
ListDirectory(path string) ([]string, *model.AppError)
|
|
||||||
FileModTime(path string) (time.Time, *model.AppError)
|
|
||||||
RemoveFile(path string) *model.AppError
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeWorker(jobServer *jobs.JobServer, store store.Store) model.Worker {
|
|
||||||
isEnabled := func(cfg *model.Config) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
execute := func(job *model.Job) error {
|
|
||||||
defer jobServer.HandleJobPanic(job)
|
|
||||||
|
|
||||||
return store.DesktopTokens().DeleteOlderThan(time.Now().Add(-maxAge).Unix())
|
|
||||||
}
|
|
||||||
worker := jobs.NewSimpleWorker(jobName, jobServer, execute, isEnabled)
|
|
||||||
return worker
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,6 @@ type OpenTracingLayer struct {
|
|||||||
CommandStore store.CommandStore
|
CommandStore store.CommandStore
|
||||||
CommandWebhookStore store.CommandWebhookStore
|
CommandWebhookStore store.CommandWebhookStore
|
||||||
ComplianceStore store.ComplianceStore
|
ComplianceStore store.ComplianceStore
|
||||||
DesktopTokensStore store.DesktopTokensStore
|
|
||||||
DraftStore store.DraftStore
|
DraftStore store.DraftStore
|
||||||
EmojiStore store.EmojiStore
|
EmojiStore store.EmojiStore
|
||||||
FileInfoStore store.FileInfoStore
|
FileInfoStore store.FileInfoStore
|
||||||
@@ -97,10 +96,6 @@ func (s *OpenTracingLayer) Compliance() store.ComplianceStore {
|
|||||||
return s.ComplianceStore
|
return s.ComplianceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayer) DesktopTokens() store.DesktopTokensStore {
|
|
||||||
return s.DesktopTokensStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayer) Draft() store.DraftStore {
|
func (s *OpenTracingLayer) Draft() store.DraftStore {
|
||||||
return s.DraftStore
|
return s.DraftStore
|
||||||
}
|
}
|
||||||
@@ -281,11 +276,6 @@ type OpenTracingLayerComplianceStore struct {
|
|||||||
Root *OpenTracingLayer
|
Root *OpenTracingLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenTracingLayerDesktopTokensStore struct {
|
|
||||||
store.DesktopTokensStore
|
|
||||||
Root *OpenTracingLayer
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenTracingLayerDraftStore struct {
|
type OpenTracingLayerDraftStore struct {
|
||||||
store.DraftStore
|
store.DraftStore
|
||||||
Root *OpenTracingLayer
|
Root *OpenTracingLayer
|
||||||
@@ -3281,114 +3271,6 @@ func (s *OpenTracingLayerComplianceStore) Update(compliance *model.Compliance) (
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) Delete(desktopToken string) error {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.Delete")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
err := s.DesktopTokensStore.Delete(desktopToken)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) DeleteByUserId(userId string) error {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.DeleteByUserId")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
err := s.DesktopTokensStore.DeleteByUserId(userId)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) DeleteOlderThan(minCreatedAt int64) error {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.DeleteOlderThan")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
err := s.DesktopTokensStore.DeleteOlderThan(minCreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) GetUserId(desktopToken string, minCreatedAt int64) (string, error) {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.GetUserId")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
result, err := s.DesktopTokensStore.GetUserId(desktopToken, minCreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) Insert(desktopToken string, createdAt int64, userId *string) error {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.Insert")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
err := s.DesktopTokensStore.Insert(desktopToken, createdAt, userId)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDesktopTokensStore) SetUserId(desktopToken string, minCreatedAt int64, userId string) error {
|
|
||||||
origCtx := s.Root.Store.Context()
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DesktopTokensStore.SetUserId")
|
|
||||||
s.Root.Store.SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
s.Root.Store.SetContext(origCtx)
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
err := s.DesktopTokensStore.SetUserId(desktopToken, minCreatedAt, userId)
|
|
||||||
if err != nil {
|
|
||||||
span.LogFields(spanlog.Error(err))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *OpenTracingLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
func (s *OpenTracingLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
||||||
origCtx := s.Root.Store.Context()
|
origCtx := s.Root.Store.Context()
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DraftStore.Delete")
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "DraftStore.Delete")
|
||||||
@@ -13148,7 +13030,6 @@ func New(childStore store.Store, ctx context.Context) *OpenTracingLayer {
|
|||||||
newStore.CommandStore = &OpenTracingLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
newStore.CommandStore = &OpenTracingLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
||||||
newStore.CommandWebhookStore = &OpenTracingLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
newStore.CommandWebhookStore = &OpenTracingLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
||||||
newStore.ComplianceStore = &OpenTracingLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
newStore.ComplianceStore = &OpenTracingLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
||||||
newStore.DesktopTokensStore = &OpenTracingLayerDesktopTokensStore{DesktopTokensStore: childStore.DesktopTokens(), Root: &newStore}
|
|
||||||
newStore.DraftStore = &OpenTracingLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
newStore.DraftStore = &OpenTracingLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
||||||
newStore.EmojiStore = &OpenTracingLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
newStore.EmojiStore = &OpenTracingLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
||||||
newStore.FileInfoStore = &OpenTracingLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
newStore.FileInfoStore = &OpenTracingLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ type RetryLayer struct {
|
|||||||
CommandStore store.CommandStore
|
CommandStore store.CommandStore
|
||||||
CommandWebhookStore store.CommandWebhookStore
|
CommandWebhookStore store.CommandWebhookStore
|
||||||
ComplianceStore store.ComplianceStore
|
ComplianceStore store.ComplianceStore
|
||||||
DesktopTokensStore store.DesktopTokensStore
|
|
||||||
DraftStore store.DraftStore
|
DraftStore store.DraftStore
|
||||||
EmojiStore store.EmojiStore
|
EmojiStore store.EmojiStore
|
||||||
FileInfoStore store.FileInfoStore
|
FileInfoStore store.FileInfoStore
|
||||||
@@ -100,10 +99,6 @@ func (s *RetryLayer) Compliance() store.ComplianceStore {
|
|||||||
return s.ComplianceStore
|
return s.ComplianceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetryLayer) DesktopTokens() store.DesktopTokensStore {
|
|
||||||
return s.DesktopTokensStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayer) Draft() store.DraftStore {
|
func (s *RetryLayer) Draft() store.DraftStore {
|
||||||
return s.DraftStore
|
return s.DraftStore
|
||||||
}
|
}
|
||||||
@@ -284,11 +279,6 @@ type RetryLayerComplianceStore struct {
|
|||||||
Root *RetryLayer
|
Root *RetryLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
type RetryLayerDesktopTokensStore struct {
|
|
||||||
store.DesktopTokensStore
|
|
||||||
Root *RetryLayer
|
|
||||||
}
|
|
||||||
|
|
||||||
type RetryLayerDraftStore struct {
|
type RetryLayerDraftStore struct {
|
||||||
store.DraftStore
|
store.DraftStore
|
||||||
Root *RetryLayer
|
Root *RetryLayer
|
||||||
@@ -3660,132 +3650,6 @@ func (s *RetryLayerComplianceStore) Update(compliance *model.Compliance) (*model
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) Delete(desktopToken string) error {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
err := s.DesktopTokensStore.Delete(desktopToken)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !isRepeatableError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tries++
|
|
||||||
if tries >= 3 {
|
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) DeleteByUserId(userId string) error {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
err := s.DesktopTokensStore.DeleteByUserId(userId)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !isRepeatableError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tries++
|
|
||||||
if tries >= 3 {
|
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) DeleteOlderThan(minCreatedAt int64) error {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
err := s.DesktopTokensStore.DeleteOlderThan(minCreatedAt)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !isRepeatableError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tries++
|
|
||||||
if tries >= 3 {
|
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) GetUserId(desktopToken string, minCreatedAt int64) (string, error) {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
result, err := s.DesktopTokensStore.GetUserId(desktopToken, minCreatedAt)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) Insert(desktopToken string, createdAt int64, userId *string) error {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
err := s.DesktopTokensStore.Insert(desktopToken, createdAt, userId)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !isRepeatableError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tries++
|
|
||||||
if tries >= 3 {
|
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDesktopTokensStore) SetUserId(desktopToken string, minCreatedAt int64, userId string) error {
|
|
||||||
|
|
||||||
tries := 0
|
|
||||||
for {
|
|
||||||
err := s.DesktopTokensStore.SetUserId(desktopToken, minCreatedAt, userId)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !isRepeatableError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tries++
|
|
||||||
if tries >= 3 {
|
|
||||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *RetryLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
func (s *RetryLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
||||||
|
|
||||||
tries := 0
|
tries := 0
|
||||||
@@ -14989,7 +14853,6 @@ func New(childStore store.Store) *RetryLayer {
|
|||||||
newStore.CommandStore = &RetryLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
newStore.CommandStore = &RetryLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
||||||
newStore.CommandWebhookStore = &RetryLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
newStore.CommandWebhookStore = &RetryLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
||||||
newStore.ComplianceStore = &RetryLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
newStore.ComplianceStore = &RetryLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
||||||
newStore.DesktopTokensStore = &RetryLayerDesktopTokensStore{DesktopTokensStore: childStore.DesktopTokens(), Root: &newStore}
|
|
||||||
newStore.DraftStore = &RetryLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
newStore.DraftStore = &RetryLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
||||||
newStore.EmojiStore = &RetryLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
newStore.EmojiStore = &RetryLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
||||||
newStore.FileInfoStore = &RetryLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
newStore.FileInfoStore = &RetryLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ func genStore() *mocks.Store {
|
|||||||
mock.On("PostAcknowledgement").Return(&mocks.PostAcknowledgementStore{})
|
mock.On("PostAcknowledgement").Return(&mocks.PostAcknowledgementStore{})
|
||||||
mock.On("PostPersistentNotification").Return(&mocks.PostPersistentNotificationStore{})
|
mock.On("PostPersistentNotification").Return(&mocks.PostPersistentNotificationStore{})
|
||||||
mock.On("TrueUpReview").Return(&mocks.TrueUpReviewStore{})
|
mock.On("TrueUpReview").Return(&mocks.TrueUpReviewStore{})
|
||||||
mock.On("DesktopTokens").Return(&mocks.DesktopTokensStore{})
|
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
package sqlstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/einterfaces"
|
|
||||||
sq "github.com/mattermost/squirrel"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SqlDesktopTokensStore struct {
|
|
||||||
*SqlStore
|
|
||||||
metrics einterfaces.MetricsInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSqlDesktopTokensStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) store.DesktopTokensStore {
|
|
||||||
return &SqlDesktopTokensStore{
|
|
||||||
SqlStore: sqlStore,
|
|
||||||
metrics: metrics,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) GetUserId(desktopToken string, minCreateAt int64) (string, error) {
|
|
||||||
query := s.getQueryBuilder().
|
|
||||||
Select("UserId").
|
|
||||||
From("DesktopTokens").
|
|
||||||
Where(sq.Eq{
|
|
||||||
"DesktopToken": desktopToken,
|
|
||||||
}).
|
|
||||||
Where(sq.GtOrEq{
|
|
||||||
"CreateAt": minCreateAt,
|
|
||||||
})
|
|
||||||
|
|
||||||
dt := struct{ UserId sql.NullString }{}
|
|
||||||
err := s.GetReplicaX().GetBuilder(&dt, query)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return "", store.NewErrNotFound("DesktopTokens", desktopToken)
|
|
||||||
}
|
|
||||||
return "", errors.Wrapf(err, "No token for %s", desktopToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the string is NULL, if so just return a blank string
|
|
||||||
if dt.UserId.Valid {
|
|
||||||
return dt.UserId.String, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) Insert(desktopToken string, createAt int64, userId *string) error {
|
|
||||||
builder := s.getQueryBuilder().
|
|
||||||
Insert("DesktopTokens").
|
|
||||||
Columns("DesktopToken", "CreateAt", "UserId").
|
|
||||||
Values(desktopToken, createAt, userId)
|
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "insert_desktoptokens_tosql")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to insert token row")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) SetUserId(desktopToken string, minCreateAt int64, userId string) error {
|
|
||||||
builder := s.getQueryBuilder().
|
|
||||||
Update("DesktopTokens").
|
|
||||||
Set("UserId", userId).
|
|
||||||
Where(sq.Eq{
|
|
||||||
"DesktopToken": desktopToken,
|
|
||||||
}).
|
|
||||||
Where(sq.GtOrEq{
|
|
||||||
"CreateAt": minCreateAt,
|
|
||||||
})
|
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "set_userid_desktoptokens_tosql")
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := s.GetMasterX().Exec(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to update token row")
|
|
||||||
}
|
|
||||||
|
|
||||||
num, err := result.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "nothing updated")
|
|
||||||
}
|
|
||||||
if num == 0 {
|
|
||||||
return errors.New("no rows updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) Delete(desktopToken string) error {
|
|
||||||
builder := s.getQueryBuilder().
|
|
||||||
Delete("DesktopTokens").
|
|
||||||
Where(sq.Eq{
|
|
||||||
"DesktopToken": desktopToken,
|
|
||||||
})
|
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "delete_desktoptokens_tosql")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to delete token row")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) DeleteByUserId(userId string) error {
|
|
||||||
builder := s.getQueryBuilder().
|
|
||||||
Delete("DesktopTokens").
|
|
||||||
Where(sq.Eq{
|
|
||||||
"UserId": userId,
|
|
||||||
})
|
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "delete_by_userid_desktoptokens_tosql")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to delete token row")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqlDesktopTokensStore) DeleteOlderThan(minCreateAt int64) error {
|
|
||||||
builder := s.getQueryBuilder().
|
|
||||||
Delete("DesktopTokens").
|
|
||||||
Where(sq.Lt{
|
|
||||||
"CreateAt": minCreateAt,
|
|
||||||
})
|
|
||||||
|
|
||||||
query, args, err := builder.ToSql()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "delete_old_desktoptokens_tosql")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to delete token row")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
package sqlstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDesktopTokensStore(t *testing.T) {
|
|
||||||
StoreTestWithSqlStore(t, storetest.TestDesktopTokensStore)
|
|
||||||
}
|
|
||||||
@@ -105,7 +105,6 @@ type SqlStoreStores struct {
|
|||||||
postAcknowledgement store.PostAcknowledgementStore
|
postAcknowledgement store.PostAcknowledgementStore
|
||||||
postPersistentNotification store.PostPersistentNotificationStore
|
postPersistentNotification store.PostPersistentNotificationStore
|
||||||
trueUpReview store.TrueUpReviewStore
|
trueUpReview store.TrueUpReviewStore
|
||||||
desktopTokens store.DesktopTokensStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SqlStore struct {
|
type SqlStore struct {
|
||||||
@@ -227,7 +226,6 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
|
|||||||
store.stores.postAcknowledgement = newSqlPostAcknowledgementStore(store)
|
store.stores.postAcknowledgement = newSqlPostAcknowledgementStore(store)
|
||||||
store.stores.postPersistentNotification = newSqlPostPersistentNotificationStore(store)
|
store.stores.postPersistentNotification = newSqlPostPersistentNotificationStore(store)
|
||||||
store.stores.trueUpReview = newSqlTrueUpReviewStore(store)
|
store.stores.trueUpReview = newSqlTrueUpReviewStore(store)
|
||||||
store.stores.desktopTokens = newSqlDesktopTokensStore(store, metrics)
|
|
||||||
|
|
||||||
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
|
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
|
||||||
|
|
||||||
@@ -1079,10 +1077,6 @@ func (ss *SqlStore) TrueUpReview() store.TrueUpReviewStore {
|
|||||||
return ss.stores.trueUpReview
|
return ss.stores.trueUpReview
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *SqlStore) DesktopTokens() store.DesktopTokensStore {
|
|
||||||
return ss.stores.desktopTokens
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *SqlStore) DropAllTables() {
|
func (ss *SqlStore) DropAllTables() {
|
||||||
var tableSchemaFn string
|
var tableSchemaFn string
|
||||||
if ss.DriverName() == model.DatabaseDriverPostgres {
|
if ss.DriverName() == model.DatabaseDriverPostgres {
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ type Store interface {
|
|||||||
PostAcknowledgement() PostAcknowledgementStore
|
PostAcknowledgement() PostAcknowledgementStore
|
||||||
PostPersistentNotification() PostPersistentNotificationStore
|
PostPersistentNotification() PostPersistentNotificationStore
|
||||||
TrueUpReview() TrueUpReviewStore
|
TrueUpReview() TrueUpReviewStore
|
||||||
DesktopTokens() DesktopTokensStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RetentionPolicyStore interface {
|
type RetentionPolicyStore interface {
|
||||||
@@ -666,15 +665,6 @@ type TokenStore interface {
|
|||||||
RemoveAllTokensByType(tokenType string) error
|
RemoveAllTokensByType(tokenType string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DesktopTokensStore interface {
|
|
||||||
GetUserId(desktopToken string, minCreatedAt int64) (string, error)
|
|
||||||
Insert(desktopToken string, createdAt int64, userId *string) error
|
|
||||||
SetUserId(desktopToken string, minCreatedAt int64, userId string) error
|
|
||||||
Delete(desktopToken string) error
|
|
||||||
DeleteByUserId(userId string) error
|
|
||||||
DeleteOlderThan(minCreatedAt int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmojiStore interface {
|
type EmojiStore interface {
|
||||||
Save(emoji *model.Emoji) (*model.Emoji, error)
|
Save(emoji *model.Emoji) (*model.Emoji, error)
|
||||||
Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error)
|
Get(ctx context.Context, id string, allowFromCache bool) (*model.Emoji, error)
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
package storetest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
|
||||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDesktopTokensStore(t *testing.T, ss store.Store, s SqlStore) {
|
|
||||||
t.Run("GetUserId", func(t *testing.T) { testGetUserId(t, ss) })
|
|
||||||
t.Run("Insert", func(t *testing.T) { testInsert(t, ss) })
|
|
||||||
t.Run("SetUserId", func(t *testing.T) { testSetUserId(t, ss) })
|
|
||||||
t.Run("Delete", func(t *testing.T) { testDeleteToken(t, ss) })
|
|
||||||
t.Run("DeleteByUserId", func(t *testing.T) { testDeleteByUserId(t, ss) })
|
|
||||||
t.Run("DeleteOlderThan", func(t *testing.T) { testDeleteOlderThan(t, ss) })
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGetUserId(t *testing.T, ss store.Store) {
|
|
||||||
err := ss.DesktopTokens().Insert("token_with_id", 1000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ss.DesktopTokens().SetUserId("token_with_id", 1000, "user_id")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = ss.DesktopTokens().Insert("token_without_id", 2000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("get user id", func(t *testing.T) {
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("token_with_id", 1000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "user_id", userId)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("get user id - expired", func(t *testing.T) {
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("token_with_id", 10000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.IsType(t, &store.ErrNotFound{}, err)
|
|
||||||
assert.Equal(t, "", userId)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("get user id - no id set", func(t *testing.T) {
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("token_without_id", 2000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "", userId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testInsert(t *testing.T, ss store.Store) {
|
|
||||||
t.Run("insert", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().Insert("token", 1000, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("insert with user id", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().Insert("token_2", 1000, model.NewString("user_id"))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("insert - token too long", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().Insert(
|
|
||||||
"this token is way way way WAAAAAAAAAAAAAY WAAAAAAAAAAAAAY WAAAAAAAAAAAAAY TOO LONG",
|
|
||||||
1000,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSetUserId(t *testing.T, ss store.Store) {
|
|
||||||
err := ss.DesktopTokens().Insert("new_token", 1000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ss.DesktopTokens().Insert("new_token_2", 1000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("set user id", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().SetUserId("new_token", 1000, "user_id")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("new_token", 1000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "user_id", userId)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set user id - doesn't exist", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().SetUserId("different_token", 1000, "user_id")
|
|
||||||
assert.Error(t, err)
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("different_token", 1000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set user id - expired", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().SetUserId("new_token", 2000, "user_id")
|
|
||||||
assert.Error(t, err)
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("new_token", 2000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("set user id - user id too long", func(t *testing.T) {
|
|
||||||
err := ss.DesktopTokens().SetUserId(
|
|
||||||
"new_token_2",
|
|
||||||
1000,
|
|
||||||
"user_id that is WAAAAAAAAAAAAAY WAAAAAAAAAAAAAY WAAAAAAAAAAAAAY TOO LONG",
|
|
||||||
)
|
|
||||||
assert.Error(t, err)
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("new_token_2", 1000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "", userId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDeleteToken(t *testing.T, ss store.Store) {
|
|
||||||
err := ss.DesktopTokens().Insert("deleteable_token", 3000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ss.DesktopTokens().SetUserId("deleteable_token", 3000, "user_id")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("delete", func(t *testing.T) {
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("deleteable_token", 3000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "user_id", userId)
|
|
||||||
|
|
||||||
err = ss.DesktopTokens().Delete("deleteable_token")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("deleteable_token", 3000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDeleteByUserId(t *testing.T, ss store.Store) {
|
|
||||||
err := ss.DesktopTokens().Insert("deleteable_token_2", 4000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ss.DesktopTokens().SetUserId("deleteable_token_2", 4000, "deleteable_user_id")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("delete by user id", func(t *testing.T) {
|
|
||||||
userId, err := ss.DesktopTokens().GetUserId("deleteable_token_2", 3000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "deleteable_user_id", userId)
|
|
||||||
|
|
||||||
err = ss.DesktopTokens().DeleteByUserId("deleteable_user_id")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("deleteable_token_2", 3000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testDeleteOlderThan(t *testing.T, ss store.Store) {
|
|
||||||
err := ss.DesktopTokens().Insert("deleteable_token_old", 1000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = ss.DesktopTokens().Insert("deleteable_token_new", 5000, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("delete older than", func(t *testing.T) {
|
|
||||||
_, err := ss.DesktopTokens().GetUserId("deleteable_token_old", 1000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("deleteable_token_new", 5000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = ss.DesktopTokens().DeleteOlderThan(2000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("deleteable_token_old", 1000)
|
|
||||||
assert.Error(t, err)
|
|
||||||
_, err = ss.DesktopTokens().GetUserId("deleteable_token_new", 5000)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
// Code generated by mockery v2.23.2. DO NOT EDIT.
|
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
|
||||||
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import mock "github.com/stretchr/testify/mock"
|
|
||||||
|
|
||||||
// DesktopTokensStore is an autogenerated mock type for the DesktopTokensStore type
|
|
||||||
type DesktopTokensStore struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete provides a mock function with given fields: desktopToken
|
|
||||||
func (_m *DesktopTokensStore) Delete(desktopToken string) error {
|
|
||||||
ret := _m.Called(desktopToken)
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
|
||||||
r0 = rf(desktopToken)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteByUserId provides a mock function with given fields: userId
|
|
||||||
func (_m *DesktopTokensStore) DeleteByUserId(userId string) error {
|
|
||||||
ret := _m.Called(userId)
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
|
||||||
r0 = rf(userId)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteOlderThan provides a mock function with given fields: minCreatedAt
|
|
||||||
func (_m *DesktopTokensStore) DeleteOlderThan(minCreatedAt int64) error {
|
|
||||||
ret := _m.Called(minCreatedAt)
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func(int64) error); ok {
|
|
||||||
r0 = rf(minCreatedAt)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserId provides a mock function with given fields: desktopToken, minCreatedAt
|
|
||||||
func (_m *DesktopTokensStore) GetUserId(desktopToken string, minCreatedAt int64) (string, error) {
|
|
||||||
ret := _m.Called(desktopToken, minCreatedAt)
|
|
||||||
|
|
||||||
var r0 string
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(0).(func(string, int64) (string, error)); ok {
|
|
||||||
return rf(desktopToken, minCreatedAt)
|
|
||||||
}
|
|
||||||
if rf, ok := ret.Get(0).(func(string, int64) string); ok {
|
|
||||||
r0 = rf(desktopToken, minCreatedAt)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Get(0).(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(string, int64) error); ok {
|
|
||||||
r1 = rf(desktopToken, minCreatedAt)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert provides a mock function with given fields: desktopToken, createdAt, userId
|
|
||||||
func (_m *DesktopTokensStore) Insert(desktopToken string, createdAt int64, userId *string) error {
|
|
||||||
ret := _m.Called(desktopToken, createdAt, userId)
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func(string, int64, *string) error); ok {
|
|
||||||
r0 = rf(desktopToken, createdAt, userId)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUserId provides a mock function with given fields: desktopToken, minCreatedAt, userId
|
|
||||||
func (_m *DesktopTokensStore) SetUserId(desktopToken string, minCreatedAt int64, userId string) error {
|
|
||||||
ret := _m.Called(desktopToken, minCreatedAt, userId)
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func(string, int64, string) error); ok {
|
|
||||||
r0 = rf(desktopToken, minCreatedAt, userId)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockConstructorTestingTNewDesktopTokensStore interface {
|
|
||||||
mock.TestingT
|
|
||||||
Cleanup(func())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDesktopTokensStore creates a new instance of DesktopTokensStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
|
||||||
func NewDesktopTokensStore(t mockConstructorTestingTNewDesktopTokensStore) *DesktopTokensStore {
|
|
||||||
mock := &DesktopTokensStore{}
|
|
||||||
mock.Mock.Test(t)
|
|
||||||
|
|
||||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
|
||||||
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
@@ -187,22 +187,6 @@ func (_m *Store) Context() context.Context {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// DesktopTokens provides a mock function with given fields:
|
|
||||||
func (_m *Store) DesktopTokens() store.DesktopTokensStore {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 store.DesktopTokensStore
|
|
||||||
if rf, ok := ret.Get(0).(func() store.DesktopTokensStore); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(store.DesktopTokensStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draft provides a mock function with given fields:
|
// Draft provides a mock function with given fields:
|
||||||
func (_m *Store) Draft() store.DraftStore {
|
func (_m *Store) Draft() store.DraftStore {
|
||||||
ret := _m.Called()
|
ret := _m.Called()
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ type Store struct {
|
|||||||
PostAcknowledgementStore mocks.PostAcknowledgementStore
|
PostAcknowledgementStore mocks.PostAcknowledgementStore
|
||||||
PostPersistentNotificationStore mocks.PostPersistentNotificationStore
|
PostPersistentNotificationStore mocks.PostPersistentNotificationStore
|
||||||
TrueUpReviewStore mocks.TrueUpReviewStore
|
TrueUpReviewStore mocks.TrueUpReviewStore
|
||||||
DesktopTokensStore mocks.DesktopTokensStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||||
@@ -104,7 +103,6 @@ func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
|||||||
return &s.ChannelMemberHistoryStore
|
return &s.ChannelMemberHistoryStore
|
||||||
}
|
}
|
||||||
func (s *Store) TrueUpReview() store.TrueUpReviewStore { return &s.TrueUpReviewStore }
|
func (s *Store) TrueUpReview() store.TrueUpReviewStore { return &s.TrueUpReviewStore }
|
||||||
func (s *Store) DesktopTokens() store.DesktopTokensStore { return &s.DesktopTokensStore }
|
|
||||||
func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdminStore }
|
func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdminStore }
|
||||||
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
||||||
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
|
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
|
||||||
@@ -178,6 +176,5 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
|||||||
&s.PostPriorityStore,
|
&s.PostPriorityStore,
|
||||||
&s.PostAcknowledgementStore,
|
&s.PostAcknowledgementStore,
|
||||||
&s.PostPersistentNotificationStore,
|
&s.PostPersistentNotificationStore,
|
||||||
&s.DesktopTokensStore,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ type TimerLayer struct {
|
|||||||
CommandStore store.CommandStore
|
CommandStore store.CommandStore
|
||||||
CommandWebhookStore store.CommandWebhookStore
|
CommandWebhookStore store.CommandWebhookStore
|
||||||
ComplianceStore store.ComplianceStore
|
ComplianceStore store.ComplianceStore
|
||||||
DesktopTokensStore store.DesktopTokensStore
|
|
||||||
DraftStore store.DraftStore
|
DraftStore store.DraftStore
|
||||||
EmojiStore store.EmojiStore
|
EmojiStore store.EmojiStore
|
||||||
FileInfoStore store.FileInfoStore
|
FileInfoStore store.FileInfoStore
|
||||||
@@ -96,10 +95,6 @@ func (s *TimerLayer) Compliance() store.ComplianceStore {
|
|||||||
return s.ComplianceStore
|
return s.ComplianceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayer) DesktopTokens() store.DesktopTokensStore {
|
|
||||||
return s.DesktopTokensStore
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayer) Draft() store.DraftStore {
|
func (s *TimerLayer) Draft() store.DraftStore {
|
||||||
return s.DraftStore
|
return s.DraftStore
|
||||||
}
|
}
|
||||||
@@ -280,11 +275,6 @@ type TimerLayerComplianceStore struct {
|
|||||||
Root *TimerLayer
|
Root *TimerLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimerLayerDesktopTokensStore struct {
|
|
||||||
store.DesktopTokensStore
|
|
||||||
Root *TimerLayer
|
|
||||||
}
|
|
||||||
|
|
||||||
type TimerLayerDraftStore struct {
|
type TimerLayerDraftStore struct {
|
||||||
store.DraftStore
|
store.DraftStore
|
||||||
Root *TimerLayer
|
Root *TimerLayer
|
||||||
@@ -3010,102 +3000,6 @@ func (s *TimerLayerComplianceStore) Update(compliance *model.Compliance) (*model
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) Delete(desktopToken string) error {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
err := s.DesktopTokensStore.Delete(desktopToken)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.Delete", success, elapsed)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) DeleteByUserId(userId string) error {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
err := s.DesktopTokensStore.DeleteByUserId(userId)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.DeleteByUserId", success, elapsed)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) DeleteOlderThan(minCreatedAt int64) error {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
err := s.DesktopTokensStore.DeleteOlderThan(minCreatedAt)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.DeleteOlderThan", success, elapsed)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) GetUserId(desktopToken string, minCreatedAt int64) (string, error) {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
result, err := s.DesktopTokensStore.GetUserId(desktopToken, minCreatedAt)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.GetUserId", success, elapsed)
|
|
||||||
}
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) Insert(desktopToken string, createdAt int64, userId *string) error {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
err := s.DesktopTokensStore.Insert(desktopToken, createdAt, userId)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.Insert", success, elapsed)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDesktopTokensStore) SetUserId(desktopToken string, minCreatedAt int64, userId string) error {
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
err := s.DesktopTokensStore.SetUserId(desktopToken, minCreatedAt, userId)
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
|
||||||
if s.Root.Metrics != nil {
|
|
||||||
success := "false"
|
|
||||||
if err == nil {
|
|
||||||
success = "true"
|
|
||||||
}
|
|
||||||
s.Root.Metrics.ObserveStoreMethodDuration("DesktopTokensStore.SetUserId", success, elapsed)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *TimerLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
func (s *TimerLayerDraftStore) Delete(userID string, channelID string, rootID string) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
@@ -11847,7 +11741,6 @@ func New(childStore store.Store, metrics einterfaces.MetricsInterface) *TimerLay
|
|||||||
newStore.CommandStore = &TimerLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
newStore.CommandStore = &TimerLayerCommandStore{CommandStore: childStore.Command(), Root: &newStore}
|
||||||
newStore.CommandWebhookStore = &TimerLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
newStore.CommandWebhookStore = &TimerLayerCommandWebhookStore{CommandWebhookStore: childStore.CommandWebhook(), Root: &newStore}
|
||||||
newStore.ComplianceStore = &TimerLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
newStore.ComplianceStore = &TimerLayerComplianceStore{ComplianceStore: childStore.Compliance(), Root: &newStore}
|
||||||
newStore.DesktopTokensStore = &TimerLayerDesktopTokensStore{DesktopTokensStore: childStore.DesktopTokens(), Root: &newStore}
|
|
||||||
newStore.DraftStore = &TimerLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
newStore.DraftStore = &TimerLayerDraftStore{DraftStore: childStore.Draft(), Root: &newStore}
|
||||||
newStore.EmojiStore = &TimerLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
newStore.EmojiStore = &TimerLayerEmojiStore{EmojiStore: childStore.Emoji(), Root: &newStore}
|
||||||
newStore.FileInfoStore = &TimerLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
newStore.FileInfoStore = &TimerLayerFileInfoStore{FileInfoStore: childStore.FileInfo(), Root: &newStore}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/mattermost/mattermost/server/public/shared/i18n"
|
"github.com/mattermost/mattermost/server/public/shared/i18n"
|
||||||
@@ -345,29 +344,6 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
} else { // For web
|
} else { // For web
|
||||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
desktopToken := ""
|
|
||||||
if val, ok := props["desktop_token"]; ok {
|
|
||||||
desktopToken = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if desktopToken != "" {
|
|
||||||
desktopTokenErr := c.App.AuthenticateDesktopToken(desktopToken, time.Now().Add(-model.DesktopTokenTTL).Unix(), user)
|
|
||||||
if desktopTokenErr != nil {
|
|
||||||
desktopTokenErr.Translate(c.AppContext.T)
|
|
||||||
c.LogErrorByCode(desktopTokenErr)
|
|
||||||
renderError(desktopTokenErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
queryString := map[string]string{
|
|
||||||
"desktopAuthComplete": "true",
|
|
||||||
}
|
|
||||||
if val, ok := props["redirect_to"]; ok {
|
|
||||||
queryString["redirect_to"] = val
|
|
||||||
}
|
|
||||||
redirectURL = utils.AppendQueryParamsToURL(c.GetSiteURLHeader()+"/login/desktop", queryString)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
@@ -382,15 +358,6 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
loginHint := r.URL.Query().Get("login_hint")
|
loginHint := r.URL.Query().Get("login_hint")
|
||||||
redirectURL := r.URL.Query().Get("redirect_to")
|
redirectURL := r.URL.Query().Get("redirect_to")
|
||||||
desktopToken := r.URL.Query().Get("desktop_token")
|
|
||||||
if desktopToken != "" {
|
|
||||||
desktopTokenErr := c.App.CreateDesktopToken(desktopToken, time.Now().Unix())
|
|
||||||
|
|
||||||
if desktopTokenErr != nil {
|
|
||||||
c.Err = desktopTokenErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if redirectURL != "" && !utils.IsValidWebAuthRedirectURL(c.App.Config(), redirectURL) {
|
if redirectURL != "" && !utils.IsValidWebAuthRedirectURL(c.App.Config(), redirectURL) {
|
||||||
c.Err = model.NewAppError("loginWithOAuth", "api.invalid_redirect_url", nil, "", http.StatusBadRequest)
|
c.Err = model.NewAppError("loginWithOAuth", "api.invalid_redirect_url", nil, "", http.StatusBadRequest)
|
||||||
@@ -403,7 +370,7 @@ func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
authURL, err := c.App.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAuthActionLogin, redirectURL, loginHint, false, desktopToken)
|
authURL, err := c.App.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAuthActionLogin, redirectURL, loginHint, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -432,7 +399,7 @@ func mobileLoginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
authURL, err := c.App.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAuthActionMobile, redirectURL, "", true, "")
|
authURL, err := c.App.GetOAuthLoginEndpoint(w, r, c.Params.Service, teamId, model.OAuthActionMobile, redirectURL, "", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -460,17 +427,7 @@ func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
desktopToken := r.URL.Query().Get("desktop_token")
|
authURL, err := c.App.GetOAuthSignupEndpoint(w, r, c.Params.Service, teamId)
|
||||||
if desktopToken != "" {
|
|
||||||
desktopTokenErr := c.App.CreateDesktopToken(desktopToken, time.Now().Unix())
|
|
||||||
|
|
||||||
if desktopTokenErr != nil {
|
|
||||||
c.Err = desktopTokenErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
authURL, err := c.App.GetOAuthSignupEndpoint(w, r, c.Params.Service, teamId, desktopToken)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||||
@@ -60,16 +59,6 @@ func loginWithSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
relayProps["redirect_to"] = redirectURL
|
relayProps["redirect_to"] = redirectURL
|
||||||
}
|
}
|
||||||
|
|
||||||
desktopToken := r.URL.Query().Get("desktop_token")
|
|
||||||
if desktopToken != "" {
|
|
||||||
desktopTokenErr := c.App.CreateDesktopToken(desktopToken, time.Now().Unix())
|
|
||||||
if desktopTokenErr != nil {
|
|
||||||
c.Err = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
relayProps["desktop_token"] = desktopToken
|
|
||||||
}
|
|
||||||
|
|
||||||
relayProps[model.UserAuthServiceIsMobile] = strconv.FormatBool(isMobile)
|
relayProps[model.UserAuthServiceIsMobile] = strconv.FormatBool(isMobile)
|
||||||
|
|
||||||
if len(relayProps) > 0 {
|
if len(relayProps) > 0 {
|
||||||
@@ -196,26 +185,6 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||||
|
|
||||||
desktopToken := relayProps["desktop_token"]
|
|
||||||
if desktopToken != "" {
|
|
||||||
desktopTokenErr := c.App.AuthenticateDesktopToken(desktopToken, time.Now().Add(-model.DesktopTokenTTL).Unix(), user)
|
|
||||||
if desktopTokenErr != nil {
|
|
||||||
handleError(desktopTokenErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
queryString := map[string]string{
|
|
||||||
"desktopAuthComplete": "true",
|
|
||||||
}
|
|
||||||
if val, ok := relayProps["redirect_to"]; ok {
|
|
||||||
queryString["redirect_to"] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
redirectURL = utils.AppendQueryParamsToURL(c.GetSiteURLHeader()+"/login/desktop", queryString)
|
|
||||||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasRedirectURL {
|
if hasRedirectURL {
|
||||||
if isMobile {
|
if isMobile {
|
||||||
// Mobile clients with redirect url support
|
// Mobile clients with redirect url support
|
||||||
|
|||||||
@@ -5023,30 +5023,6 @@
|
|||||||
"id": "app.custom_group.unique_name",
|
"id": "app.custom_group.unique_name",
|
||||||
"translation": "group name is not unique"
|
"translation": "group name is not unique"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "app.desktop_token.authenticate.invalid_or_expired",
|
|
||||||
"translation": "Token does not exist or is expired"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "app.desktop_token.create.collision",
|
|
||||||
"translation": "This token already exists in the system"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "app.desktop_token.create.error",
|
|
||||||
"translation": "An error occured attempted to create the token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "app.desktop_token.validate.expired",
|
|
||||||
"translation": "Token is expired"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "app.desktop_token.validate.invalid",
|
|
||||||
"translation": "Token is not valid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "app.desktop_token.validate.no_user",
|
|
||||||
"translation": "Cannot find a user for this token"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "app.draft.delete.app_error",
|
"id": "app.draft.delete.app_error",
|
||||||
"translation": "Unable to delete the Draft."
|
"translation": "Unable to delete the Draft."
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ const (
|
|||||||
JobTypePostPersistentNotifications = "post_persistent_notifications"
|
JobTypePostPersistentNotifications = "post_persistent_notifications"
|
||||||
JobTypeInstallPluginNotifyAdmin = "install_plugin_notify_admin"
|
JobTypeInstallPluginNotifyAdmin = "install_plugin_notify_admin"
|
||||||
JobTypeHostedPurchaseScreening = "hosted_purchase_screening"
|
JobTypeHostedPurchaseScreening = "hosted_purchase_screening"
|
||||||
JobTypeCleanupDesktopTokens = "cleanup_desktop_tokens"
|
|
||||||
|
|
||||||
JobStatusPending = "pending"
|
JobStatusPending = "pending"
|
||||||
JobStatusInProgress = "in_progress"
|
JobStatusInProgress = "in_progress"
|
||||||
@@ -66,7 +65,6 @@ var AllJobTypes = [...]string{
|
|||||||
JobTypeExtractContent,
|
JobTypeExtractContent,
|
||||||
JobTypeLastAccessiblePost,
|
JobTypeLastAccessiblePost,
|
||||||
JobTypeLastAccessibleFile,
|
JobTypeLastAccessibleFile,
|
||||||
JobTypeCleanupDesktopTokens,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Job struct {
|
type Job struct {
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ const (
|
|||||||
UserLocaleMaxLength = 5
|
UserLocaleMaxLength = 5
|
||||||
UserTimezoneMaxRunes = 256
|
UserTimezoneMaxRunes = 256
|
||||||
UserRolesMaxLength = 256
|
UserRolesMaxLength = 256
|
||||||
|
|
||||||
DesktopTokenTTL = time.Minute * 3
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//msgp:tuple User
|
//msgp:tuple User
|
||||||
|
|||||||
@@ -45,40 +45,6 @@ export function login(loginId: string, password: string, mfaToken = ''): ActionF
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loginWithDesktopToken(token: string): ActionFunc {
|
|
||||||
return async (dispatch: DispatchFunc) => {
|
|
||||||
dispatch({type: UserTypes.LOGIN_REQUEST, data: null});
|
|
||||||
|
|
||||||
try {
|
|
||||||
// This is partial user profile we recieved when we login. We still need to make getMe for complete user profile.
|
|
||||||
const loggedInUserProfile = await Client4.loginWithDesktopToken(token);
|
|
||||||
|
|
||||||
dispatch(
|
|
||||||
batchActions([
|
|
||||||
{
|
|
||||||
type: UserTypes.LOGIN_SUCCESS,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: UserTypes.RECEIVED_ME,
|
|
||||||
data: loggedInUserProfile,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
dispatch(loadRolesIfNeeded(loggedInUserProfile.roles.split(' ')));
|
|
||||||
} catch (error) {
|
|
||||||
dispatch({
|
|
||||||
type: UserTypes.LOGIN_FAILURE,
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
dispatch(logError(error as ServerError));
|
|
||||||
return {error};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {data: true};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loginById(id: string, password: string): ActionFunc {
|
export function loginById(id: string, password: string): ActionFunc {
|
||||||
return async (dispatch: DispatchFunc) => {
|
return async (dispatch: DispatchFunc) => {
|
||||||
dispatch({type: UserTypes.LOGIN_REQUEST, data: null});
|
dispatch({type: UserTypes.LOGIN_REQUEST, data: null});
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
.DesktopAuthToken {
|
|
||||||
display: flex;
|
|
||||||
width: 540px;
|
|
||||||
height: 80vh;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.DesktopAuthToken__main {
|
|
||||||
display: flex;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
color: var(--sidebar-header-bg);
|
|
||||||
font-size: 40px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
line-height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.DesktopAuthToken__sub {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
color: rgba(var(--center-channel-color-rgb), 0.72);
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 28px;
|
|
||||||
|
|
||||||
&.complete {
|
|
||||||
max-width: 442px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.DesktopAuthToken__bottom {
|
|
||||||
margin-top: auto;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React, {useEffect, useRef, useState} from 'react';
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
|
||||||
import {useHistory, useLocation} from 'react-router-dom';
|
|
||||||
|
|
||||||
import crypto from 'crypto';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import {UserProfile} from '@mattermost/types/users';
|
|
||||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
|
||||||
import {DispatchFunc} from 'mattermost-redux/types/actions';
|
|
||||||
|
|
||||||
import {loginWithDesktopToken} from 'actions/views/login';
|
|
||||||
|
|
||||||
import './desktop_auth_token.scss';
|
|
||||||
|
|
||||||
const BOTTOM_MESSAGE_TIMEOUT = 10000;
|
|
||||||
const POLLING_INTERVAL = 2000;
|
|
||||||
|
|
||||||
enum DesktopAuthStatus {
|
|
||||||
None,
|
|
||||||
Polling,
|
|
||||||
Expired,
|
|
||||||
Complete,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
href: string;
|
|
||||||
onLogin: (userProfile: UserProfile) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DesktopAuthToken: React.FC<Props> = ({href, onLogin}: Props) => {
|
|
||||||
const dispatch = useDispatch<DispatchFunc>();
|
|
||||||
const history = useHistory();
|
|
||||||
const {search} = useLocation();
|
|
||||||
const query = new URLSearchParams(search);
|
|
||||||
|
|
||||||
const [status, setStatus] = useState(query.get('desktopAuthComplete') ? DesktopAuthStatus.Complete : DesktopAuthStatus.None);
|
|
||||||
const [token, setToken] = useState('');
|
|
||||||
const [showBottomMessage, setShowBottomMessage] = useState<React.ReactNode>();
|
|
||||||
|
|
||||||
const interval = useRef<NodeJS.Timer>();
|
|
||||||
|
|
||||||
const {SiteURL} = useSelector(getConfig);
|
|
||||||
|
|
||||||
const tryDesktopLogin = async () => {
|
|
||||||
const {data: userProfile, error: loginError} = await dispatch(loginWithDesktopToken(token));
|
|
||||||
|
|
||||||
if (loginError && loginError.server_error_id && loginError.server_error_id.length !== 0) {
|
|
||||||
if (loginError.server_error_id === 'app.desktop_token.validate.expired') {
|
|
||||||
clearInterval(interval.current as unknown as number);
|
|
||||||
setStatus(DesktopAuthStatus.Expired);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(interval.current as unknown as number);
|
|
||||||
setStatus(DesktopAuthStatus.Complete);
|
|
||||||
await onLogin(userProfile as UserProfile);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getExternalLoginURL = () => {
|
|
||||||
const parsedURL = new URL(href);
|
|
||||||
|
|
||||||
const params = new URLSearchParams(parsedURL.searchParams);
|
|
||||||
params.set('desktop_token', token);
|
|
||||||
|
|
||||||
return `${parsedURL.origin}${parsedURL.pathname}?${params.toString()}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const openDesktopApp = () => {
|
|
||||||
if (!SiteURL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const url = new URL(SiteURL);
|
|
||||||
const redirectTo = query.get('redirect_to');
|
|
||||||
if (redirectTo) {
|
|
||||||
url.pathname += redirectTo;
|
|
||||||
}
|
|
||||||
url.protocol = 'mattermost';
|
|
||||||
window.location.href = url.toString();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setShowBottomMessage(false);
|
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
setShowBottomMessage(true);
|
|
||||||
}, BOTTOM_MESSAGE_TIMEOUT) as unknown as number;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
};
|
|
||||||
}, [status]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!token) {
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = getExternalLoginURL();
|
|
||||||
window.open(url);
|
|
||||||
|
|
||||||
setStatus(DesktopAuthStatus.Polling);
|
|
||||||
interval.current = setInterval(tryDesktopLogin, POLLING_INTERVAL);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval.current as unknown as number);
|
|
||||||
};
|
|
||||||
}, [token]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (status === DesktopAuthStatus.Complete) {
|
|
||||||
openDesktopApp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setToken(crypto.randomBytes(32).toString('hex'));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
let mainMessage;
|
|
||||||
let subMessage;
|
|
||||||
let bottomMessage;
|
|
||||||
|
|
||||||
if (status === DesktopAuthStatus.Polling) {
|
|
||||||
mainMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.polling.redirectingToBrowser'
|
|
||||||
defaultMessage='Redirecting to browser...'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
subMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.polling.awaitingToken'
|
|
||||||
defaultMessage='Authenticating in the browser, awaiting valid token.'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
bottomMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.polling.isComplete'
|
|
||||||
defaultMessage='Authentication complete? <a>Check token now</a>'
|
|
||||||
values={{
|
|
||||||
a: (chunks: React.ReactNode) => {
|
|
||||||
return (
|
|
||||||
<a onClick={tryDesktopLogin}>
|
|
||||||
{chunks}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === DesktopAuthStatus.Complete) {
|
|
||||||
mainMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.complete.youAreNowLoggedIn'
|
|
||||||
defaultMessage='You are now logged in'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
subMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.complete.openMattermost'
|
|
||||||
defaultMessage='Click on <b>Open Mattermost</b> in the browser prompt to <a>launch the desktop app</a>'
|
|
||||||
values={{
|
|
||||||
a: (chunks: React.ReactNode) => {
|
|
||||||
return (
|
|
||||||
<a onClick={openDesktopApp}>
|
|
||||||
{chunks}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
b: (chunks: React.ReactNode) => (<b>{chunks}</b>),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
bottomMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.complete.havingTrouble'
|
|
||||||
defaultMessage='Having trouble logging in? <a>Open Mattermost in your browser</a>'
|
|
||||||
values={{
|
|
||||||
a: (chunks: React.ReactNode) => {
|
|
||||||
return (
|
|
||||||
<a onClick={() => history.push('/')}>
|
|
||||||
{chunks}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status === DesktopAuthStatus.Expired) {
|
|
||||||
mainMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.expired.somethingWentWrong'
|
|
||||||
defaultMessage='Something went wrong'
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
subMessage = (
|
|
||||||
<FormattedMessage
|
|
||||||
id='desktop_auth_token.expired.restartFlow'
|
|
||||||
defaultMessage={'Click <a>here</a> to try again.'}
|
|
||||||
values={{
|
|
||||||
a: (chunks: React.ReactNode) => {
|
|
||||||
return (
|
|
||||||
<a onClick={() => history.push('/')}>
|
|
||||||
{chunks}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
bottomMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='DesktopAuthToken'>
|
|
||||||
<h1 className='DesktopAuthToken__main'>
|
|
||||||
{mainMessage}
|
|
||||||
</h1>
|
|
||||||
<p className={classNames('DesktopAuthToken__sub', {complete: status === DesktopAuthStatus.Complete})}>
|
|
||||||
{subMessage}
|
|
||||||
</p>
|
|
||||||
<div className='DesktopAuthToken__bottom'>
|
|
||||||
{showBottomMessage ? bottomMessage : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DesktopAuthToken;
|
|
||||||
@@ -13,7 +13,6 @@ export type ExternalLoginButtonType = {
|
|||||||
label: string;
|
label: string;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
direction?: 'row' | 'column';
|
direction?: 'row' | 'column';
|
||||||
onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExternalLoginButton = ({
|
const ExternalLoginButton = ({
|
||||||
@@ -23,14 +22,12 @@ const ExternalLoginButton = ({
|
|||||||
label,
|
label,
|
||||||
style,
|
style,
|
||||||
direction = 'row',
|
direction = 'row',
|
||||||
onClick,
|
|
||||||
}: ExternalLoginButtonType) => (
|
}: ExternalLoginButtonType) => (
|
||||||
<a
|
<a
|
||||||
id={id}
|
id={id}
|
||||||
className={classNames('external-login-button', {'direction-column': direction === 'column'}, id)}
|
className={classNames('external-login-button', {'direction-column': direction === 'column'}, id)}
|
||||||
href={url}
|
href={url}
|
||||||
style={style}
|
style={style}
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
<span className='external-login-button-label'>
|
<span className='external-login-button-label'>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import React, {useState, useEffect, useRef, useCallback, FormEvent} from 'react';
|
import React, {useState, useEffect, useRef, useCallback, FormEvent} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {Link, useLocation, useHistory, Route} from 'react-router-dom';
|
import {Link, useLocation, useHistory} from 'react-router-dom';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
@@ -32,9 +32,7 @@ import {setNeedsLoggedInLimitReachedCheck} from 'actions/views/admin';
|
|||||||
import {trackEvent} from 'actions/telemetry_actions';
|
import {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
import AlertBanner, {ModeType, AlertBannerProps} from 'components/alert_banner';
|
import AlertBanner, {ModeType, AlertBannerProps} from 'components/alert_banner';
|
||||||
import DesktopAuthToken from 'components/desktop_auth_token';
|
|
||||||
import ExternalLoginButton, {ExternalLoginButtonType} from 'components/external_login_button/external_login_button';
|
import ExternalLoginButton, {ExternalLoginButtonType} from 'components/external_login_button/external_login_button';
|
||||||
import ExternalLink from 'components/external_link';
|
|
||||||
import AlternateLinkLayout from 'components/header_footer_route/content_layouts/alternate_link';
|
import AlternateLinkLayout from 'components/header_footer_route/content_layouts/alternate_link';
|
||||||
import ColumnLayout from 'components/header_footer_route/content_layouts/column';
|
import ColumnLayout from 'components/header_footer_route/content_layouts/column';
|
||||||
import {CustomizeHeaderType} from 'components/header_footer_route/header_footer_route';
|
import {CustomizeHeaderType} from 'components/header_footer_route/header_footer_route';
|
||||||
@@ -56,12 +54,12 @@ import {GlobalState} from 'types/store';
|
|||||||
import Constants from 'utils/constants';
|
import Constants from 'utils/constants';
|
||||||
import {showNotification} from 'utils/notifications';
|
import {showNotification} from 'utils/notifications';
|
||||||
import {t} from 'utils/i18n';
|
import {t} from 'utils/i18n';
|
||||||
import {isDesktopApp} from 'utils/user_agent';
|
|
||||||
import {setCSRFFromCookie} from 'utils/utils';
|
import {setCSRFFromCookie} from 'utils/utils';
|
||||||
|
|
||||||
import LoginMfa from './login_mfa';
|
import LoginMfa from './login_mfa';
|
||||||
|
|
||||||
import './login.scss';
|
import './login.scss';
|
||||||
|
import ExternalLink from 'components/external_link';
|
||||||
|
|
||||||
const MOBILE_SCREEN_WIDTH = 1200;
|
const MOBILE_SCREEN_WIDTH = 1200;
|
||||||
|
|
||||||
@@ -150,8 +148,6 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
|||||||
const query = new URLSearchParams(search);
|
const query = new URLSearchParams(search);
|
||||||
const redirectTo = query.get('redirect_to');
|
const redirectTo = query.get('redirect_to');
|
||||||
|
|
||||||
const [desktopLoginLink, setDesktopLoginLink] = useState('');
|
|
||||||
|
|
||||||
const getExternalLoginOptions = () => {
|
const getExternalLoginOptions = () => {
|
||||||
const externalLoginOptions: ExternalLoginButtonType[] = [];
|
const externalLoginOptions: ExternalLoginButtonType[] = [];
|
||||||
|
|
||||||
@@ -160,76 +156,55 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithGitLab) {
|
if (enableSignUpWithGitLab) {
|
||||||
const url = `${Client4.getOAuthRoute()}/gitlab/login${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'gitlab',
|
id: 'gitlab',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/gitlab/login${search}`,
|
||||||
icon: <LoginGitlabIcon/>,
|
icon: <LoginGitlabIcon/>,
|
||||||
label: GitLabButtonText || formatMessage({id: 'login.gitlab', defaultMessage: 'GitLab'}),
|
label: GitLabButtonText || formatMessage({id: 'login.gitlab', defaultMessage: 'GitLab'}),
|
||||||
style: {color: GitLabButtonColor, borderColor: GitLabButtonColor},
|
style: {color: GitLabButtonColor, borderColor: GitLabButtonColor},
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithGoogle) {
|
if (enableSignUpWithGoogle) {
|
||||||
const url = `${Client4.getOAuthRoute()}/google/login${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'google',
|
id: 'google',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/google/login${search}`,
|
||||||
icon: <LoginGoogleIcon/>,
|
icon: <LoginGoogleIcon/>,
|
||||||
label: formatMessage({id: 'login.google', defaultMessage: 'Google'}),
|
label: formatMessage({id: 'login.google', defaultMessage: 'Google'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithOffice365) {
|
if (enableSignUpWithOffice365) {
|
||||||
const url = `${Client4.getOAuthRoute()}/office365/login${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'office365',
|
id: 'office365',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/office365/login${search}`,
|
||||||
icon: <LoginOffice365Icon/>,
|
icon: <LoginOffice365Icon/>,
|
||||||
label: formatMessage({id: 'login.office365', defaultMessage: 'Office 365'}),
|
label: formatMessage({id: 'login.office365', defaultMessage: 'Office 365'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithOpenId) {
|
if (enableSignUpWithOpenId) {
|
||||||
const url = `${Client4.getOAuthRoute()}/openid/login${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'openid',
|
id: 'openid',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/openid/login${search}`,
|
||||||
icon: <LoginOpenIDIcon/>,
|
icon: <LoginOpenIDIcon/>,
|
||||||
label: OpenIdButtonText || formatMessage({id: 'login.openid', defaultMessage: 'Open ID'}),
|
label: OpenIdButtonText || formatMessage({id: 'login.openid', defaultMessage: 'Open ID'}),
|
||||||
style: {color: OpenIdButtonColor, borderColor: OpenIdButtonColor},
|
style: {color: OpenIdButtonColor, borderColor: OpenIdButtonColor},
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithSaml) {
|
if (enableSignUpWithSaml) {
|
||||||
const url = `${Client4.getUrl()}/login/sso/saml${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'saml',
|
id: 'saml',
|
||||||
url,
|
url: `${Client4.getUrl()}/login/sso/saml${search}`,
|
||||||
icon: <LockIcon/>,
|
icon: <LockIcon/>,
|
||||||
label: SamlLoginButtonText || formatMessage({id: 'login.saml', defaultMessage: 'SAML'}),
|
label: SamlLoginButtonText || formatMessage({id: 'login.saml', defaultMessage: 'SAML'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return externalLoginOptions;
|
return externalLoginOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
const desktopExternalAuth = (href: string) => {
|
|
||||||
return (event: React.MouseEvent) => {
|
|
||||||
if (isDesktopApp()) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
setDesktopLoginLink(href);
|
|
||||||
history.push(`/login/desktop${search}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const dismissAlert = () => {
|
const dismissAlert = () => {
|
||||||
setAlertBanner(null);
|
setAlertBanner(null);
|
||||||
setHasError(false);
|
setHasError(false);
|
||||||
@@ -403,11 +378,6 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
|||||||
}, [onCustomizeHeader, search, showMfa, isMobileView, getAlternateLink]);
|
}, [onCustomizeHeader, search, showMfa, isMobileView, getAlternateLink]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// We don't want to redirect outside of this route if we're doing Desktop App auth
|
|
||||||
if (query.get('desktopAuthComplete')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentUser) {
|
if (currentUser) {
|
||||||
if (redirectTo && redirectTo.match(/^\/([^/]|$)/)) {
|
if (redirectTo && redirectTo.match(/^\/([^/]|$)/)) {
|
||||||
history.push(redirectTo);
|
history.push(redirectTo);
|
||||||
@@ -626,10 +596,6 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await postSubmit(userProfile);
|
|
||||||
};
|
|
||||||
|
|
||||||
const postSubmit = async (userProfile: UserProfile) => {
|
|
||||||
if (graphQLEnabled) {
|
if (graphQLEnabled) {
|
||||||
await dispatch(loadMe());
|
await dispatch(loadMe());
|
||||||
} else {
|
} else {
|
||||||
@@ -788,20 +754,6 @@ const Login = ({onCustomizeHeader}: LoginProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desktopLoginLink || query.get('desktopAuthComplete')) {
|
|
||||||
return (
|
|
||||||
<Route
|
|
||||||
path={'/login/desktop'}
|
|
||||||
render={() => (
|
|
||||||
<DesktopAuthToken
|
|
||||||
href={desktopLoginLink}
|
|
||||||
onLogin={postSubmit}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="gitlab"
|
id="gitlab"
|
||||||
key="gitlab"
|
key="gitlab"
|
||||||
label="GitLab"
|
label="GitLab"
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
@@ -316,7 +315,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="gitlab"
|
id="gitlab"
|
||||||
key="gitlab"
|
key="gitlab"
|
||||||
label="GitLab"
|
label="GitLab"
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
@@ -330,7 +328,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="google"
|
id="google"
|
||||||
key="google"
|
key="google"
|
||||||
label="Google"
|
label="Google"
|
||||||
onClick={[Function]}
|
|
||||||
url="/oauth/google/signup"
|
url="/oauth/google/signup"
|
||||||
/>
|
/>
|
||||||
<ExternalLoginButton
|
<ExternalLoginButton
|
||||||
@@ -338,7 +335,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="office365"
|
id="office365"
|
||||||
key="office365"
|
key="office365"
|
||||||
label="Office 365"
|
label="Office 365"
|
||||||
onClick={[Function]}
|
|
||||||
url="/oauth/office365/signup"
|
url="/oauth/office365/signup"
|
||||||
/>
|
/>
|
||||||
<ExternalLoginButton
|
<ExternalLoginButton
|
||||||
@@ -346,7 +342,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="openid"
|
id="openid"
|
||||||
key="openid"
|
key="openid"
|
||||||
label="Open ID"
|
label="Open ID"
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"borderColor": "",
|
"borderColor": "",
|
||||||
@@ -360,7 +355,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="ldap"
|
id="ldap"
|
||||||
key="ldap"
|
key="ldap"
|
||||||
label="AD/LDAP Credentials"
|
label="AD/LDAP Credentials"
|
||||||
onClick={[Function]}
|
|
||||||
url="/login?extra=create_ldap"
|
url="/login?extra=create_ldap"
|
||||||
/>
|
/>
|
||||||
<ExternalLoginButton
|
<ExternalLoginButton
|
||||||
@@ -368,7 +362,6 @@ exports[`components/signup/Signup should match snapshot for all signup options e
|
|||||||
id="saml"
|
id="saml"
|
||||||
key="saml"
|
key="saml"
|
||||||
label="SAML"
|
label="SAML"
|
||||||
onClick={[Function]}
|
|
||||||
url="/login/sso/saml?action=signup"
|
url="/login/sso/saml?action=signup"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import React, {useState, useEffect, useRef, useCallback, FocusEvent} from 'react';
|
import React, {useState, useEffect, useRef, useCallback, FocusEvent} from 'react';
|
||||||
|
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {useLocation, useHistory, Route} from 'react-router-dom';
|
import {useLocation, useHistory} from 'react-router-dom';
|
||||||
import {useSelector, useDispatch} from 'react-redux';
|
import {useSelector, useDispatch} from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
@@ -54,11 +54,9 @@ import useCWSAvailabilityCheck from 'components/common/hooks/useCWSAvailabilityC
|
|||||||
import ExternalLink from 'components/external_link';
|
import ExternalLink from 'components/external_link';
|
||||||
|
|
||||||
import {Constants, HostedCustomerLinks, ItemStatus, ValidationErrors} from 'utils/constants';
|
import {Constants, HostedCustomerLinks, ItemStatus, ValidationErrors} from 'utils/constants';
|
||||||
import {isDesktopApp} from 'utils/user_agent';
|
|
||||||
import {isValidUsername, isValidPassword, getPasswordConfig, getRoleFromTrackFlow, getMediumFromTrackFlow} from 'utils/utils';
|
import {isValidUsername, isValidPassword, getPasswordConfig, getRoleFromTrackFlow, getMediumFromTrackFlow} from 'utils/utils';
|
||||||
|
|
||||||
import './signup.scss';
|
import './signup.scss';
|
||||||
import DesktopAuthToken from 'components/desktop_auth_token';
|
|
||||||
|
|
||||||
const MOBILE_SCREEN_WIDTH = 1200;
|
const MOBILE_SCREEN_WIDTH = 1200;
|
||||||
|
|
||||||
@@ -150,8 +148,6 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
const canSubmit = Boolean(email && name && password) && !hasError && !loading;
|
const canSubmit = Boolean(email && name && password) && !hasError && !loading;
|
||||||
const {error: passwordInfo} = isValidPassword('', getPasswordConfig(config), intl);
|
const {error: passwordInfo} = isValidPassword('', getPasswordConfig(config), intl);
|
||||||
|
|
||||||
const [desktopLoginLink, setDesktopLoginLink] = useState('');
|
|
||||||
|
|
||||||
const subscribeToSecurityNewsletterFunc = () => {
|
const subscribeToSecurityNewsletterFunc = () => {
|
||||||
try {
|
try {
|
||||||
Client4.subscribeToNewsletter({email, subscribed_content: 'security_newsletter'});
|
Client4.subscribeToNewsletter({email, subscribed_content: 'security_newsletter'});
|
||||||
@@ -169,48 +165,40 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enableSignUpWithGitLab) {
|
if (enableSignUpWithGitLab) {
|
||||||
const url = `${Client4.getOAuthRoute()}/gitlab/signup${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'gitlab',
|
id: 'gitlab',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/gitlab/signup${search}`,
|
||||||
icon: <LoginGitlabIcon/>,
|
icon: <LoginGitlabIcon/>,
|
||||||
label: GitLabButtonText || formatMessage({id: 'login.gitlab', defaultMessage: 'GitLab'}),
|
label: GitLabButtonText || formatMessage({id: 'login.gitlab', defaultMessage: 'GitLab'}),
|
||||||
style: {color: GitLabButtonColor, borderColor: GitLabButtonColor},
|
style: {color: GitLabButtonColor, borderColor: GitLabButtonColor},
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLicensed && enableSignUpWithGoogle) {
|
if (isLicensed && enableSignUpWithGoogle) {
|
||||||
const url = `${Client4.getOAuthRoute()}/google/signup${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'google',
|
id: 'google',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/google/signup${search}`,
|
||||||
icon: <LoginGoogleIcon/>,
|
icon: <LoginGoogleIcon/>,
|
||||||
label: formatMessage({id: 'login.google', defaultMessage: 'Google'}),
|
label: formatMessage({id: 'login.google', defaultMessage: 'Google'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLicensed && enableSignUpWithOffice365) {
|
if (isLicensed && enableSignUpWithOffice365) {
|
||||||
const url = `${Client4.getOAuthRoute()}/office365/signup${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'office365',
|
id: 'office365',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/office365/signup${search}`,
|
||||||
icon: <LoginOffice365Icon/>,
|
icon: <LoginOffice365Icon/>,
|
||||||
label: formatMessage({id: 'login.office365', defaultMessage: 'Office 365'}),
|
label: formatMessage({id: 'login.office365', defaultMessage: 'Office 365'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLicensed && enableSignUpWithOpenId) {
|
if (isLicensed && enableSignUpWithOpenId) {
|
||||||
const url = `${Client4.getOAuthRoute()}/openid/signup${search}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'openid',
|
id: 'openid',
|
||||||
url,
|
url: `${Client4.getOAuthRoute()}/openid/signup${search}`,
|
||||||
icon: <LoginOpenIDIcon/>,
|
icon: <LoginOpenIDIcon/>,
|
||||||
label: OpenIdButtonText || formatMessage({id: 'login.openid', defaultMessage: 'Open ID'}),
|
label: OpenIdButtonText || formatMessage({id: 'login.openid', defaultMessage: 'Open ID'}),
|
||||||
style: {color: OpenIdButtonColor, borderColor: OpenIdButtonColor},
|
style: {color: OpenIdButtonColor, borderColor: OpenIdButtonColor},
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +211,6 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
url: `${Client4.getUrl()}/login?${newSearchParam.toString()}`,
|
url: `${Client4.getUrl()}/login?${newSearchParam.toString()}`,
|
||||||
icon: <LockIcon/>,
|
icon: <LockIcon/>,
|
||||||
label: LdapLoginFieldName || formatMessage({id: 'signup.ldap', defaultMessage: 'AD/LDAP Credentials'}),
|
label: LdapLoginFieldName || formatMessage({id: 'signup.ldap', defaultMessage: 'AD/LDAP Credentials'}),
|
||||||
onClick: () => {},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,13 +218,11 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
const newSearchParam = new URLSearchParams(search);
|
const newSearchParam = new URLSearchParams(search);
|
||||||
newSearchParam.set('action', 'signup');
|
newSearchParam.set('action', 'signup');
|
||||||
|
|
||||||
const url = `${Client4.getUrl()}/login/sso/saml?${newSearchParam.toString()}`;
|
|
||||||
externalLoginOptions.push({
|
externalLoginOptions.push({
|
||||||
id: 'saml',
|
id: 'saml',
|
||||||
url,
|
url: `${Client4.getUrl()}/login/sso/saml?${newSearchParam.toString()}`,
|
||||||
icon: <LockIcon/>,
|
icon: <LockIcon/>,
|
||||||
label: SamlLoginButtonText || formatMessage({id: 'login.saml', defaultMessage: 'SAML'}),
|
label: SamlLoginButtonText || formatMessage({id: 'login.saml', defaultMessage: 'SAML'}),
|
||||||
onClick: desktopExternalAuth(url),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,17 +295,6 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
setIsMobileView(window.innerWidth < MOBILE_SCREEN_WIDTH);
|
setIsMobileView(window.innerWidth < MOBILE_SCREEN_WIDTH);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
const desktopExternalAuth = (href: string) => {
|
|
||||||
return (event: React.MouseEvent) => {
|
|
||||||
if (isDesktopApp()) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
setDesktopLoginLink(href);
|
|
||||||
history.push(`/signup_user_complete/desktop${search}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(removeGlobalItem('team'));
|
dispatch(removeGlobalItem('team'));
|
||||||
trackEvent('signup', 'signup_user_01_welcome', {...getRoleFromTrackFlow(), ...getMediumFromTrackFlow()});
|
trackEvent('signup', 'signup_user_01_welcome', {...getRoleFromTrackFlow(), ...getMediumFromTrackFlow()});
|
||||||
@@ -474,12 +448,6 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await postSignupSuccess();
|
|
||||||
};
|
|
||||||
|
|
||||||
const postSignupSuccess = async () => {
|
|
||||||
const redirectTo = (new URLSearchParams(search)).get('redirect_to');
|
|
||||||
|
|
||||||
if (graphQLEnabled) {
|
if (graphQLEnabled) {
|
||||||
await dispatch(loadMe());
|
await dispatch(loadMe());
|
||||||
} else {
|
} else {
|
||||||
@@ -727,20 +695,6 @@ const Signup = ({onCustomizeHeader}: SignupProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desktopLoginLink) {
|
|
||||||
return (
|
|
||||||
<Route
|
|
||||||
path={'/signup_user_complete/desktop'}
|
|
||||||
render={() => (
|
|
||||||
<DesktopAuthToken
|
|
||||||
href={desktopLoginLink}
|
|
||||||
onLogin={postSignupSuccess}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let emailCustomLabelForInput: CustomMessageInputType = parsedEmail ? {
|
let emailCustomLabelForInput: CustomMessageInputType = parsedEmail ? {
|
||||||
type: ItemStatus.INFO,
|
type: ItemStatus.INFO,
|
||||||
value: formatMessage(
|
value: formatMessage(
|
||||||
|
|||||||
@@ -3288,14 +3288,6 @@
|
|||||||
"demote_to_user_modal.demote": "Demote",
|
"demote_to_user_modal.demote": "Demote",
|
||||||
"demote_to_user_modal.desc": "This action demotes the user {username} to a guest. It will restrict the user's ability to join public channels and interact with users outside of the channels they are currently members of. Are you sure you want to demote user {username} to guest?",
|
"demote_to_user_modal.desc": "This action demotes the user {username} to a guest. It will restrict the user's ability to join public channels and interact with users outside of the channels they are currently members of. Are you sure you want to demote user {username} to guest?",
|
||||||
"demote_to_user_modal.title": "Demote User {username} to Guest",
|
"demote_to_user_modal.title": "Demote User {username} to Guest",
|
||||||
"desktop_auth_token.complete.havingTrouble": "Having trouble logging in? <a>Open Mattermost in your browser</a>",
|
|
||||||
"desktop_auth_token.complete.openMattermost": "Click on <b>Open Mattermost</b> in the browser prompt to <a>launch the desktop app</a>",
|
|
||||||
"desktop_auth_token.complete.youAreNowLoggedIn": "You are now logged in",
|
|
||||||
"desktop_auth_token.expired.restartFlow": "Click <a>here</a> to try again.",
|
|
||||||
"desktop_auth_token.expired.somethingWentWrong": "Something went wrong",
|
|
||||||
"desktop_auth_token.polling.awaitingToken": "Authenticating in the browser, awaiting valid token.",
|
|
||||||
"desktop_auth_token.polling.isComplete": "Authentication complete? <a>Check token now</a>",
|
|
||||||
"desktop_auth_token.polling.redirectingToBrowser": "Redirecting to browser...",
|
|
||||||
"device_icons.android": "Android Icon",
|
"device_icons.android": "Android Icon",
|
||||||
"device_icons.apple": "Apple Icon",
|
"device_icons.apple": "Apple Icon",
|
||||||
"device_icons.linux": "Linux Icon",
|
"device_icons.linux": "Linux Icon",
|
||||||
|
|||||||
@@ -756,18 +756,6 @@ export default class Client4 {
|
|||||||
return profile;
|
return profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
loginWithDesktopToken = async (token: string) => {
|
|
||||||
const body: any = {
|
|
||||||
token,
|
|
||||||
deviceId: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
return await this.doFetch<UserProfile>(
|
|
||||||
`${this.getUsersRoute()}/login/desktop_token`,
|
|
||||||
{method: 'post', body: JSON.stringify(body)},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
loginById = (id: string, password: string, token = '') => {
|
loginById = (id: string, password: string, token = '') => {
|
||||||
this.trackEvent('api', 'api_users_login');
|
this.trackEvent('api', 'api_users_login');
|
||||||
const body: any = {
|
const body: any = {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user