diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 86fb87fd82..512c308137 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -251,6 +251,26 @@ func Setup(tb testing.TB) *TestHelper { return th } +func SetupAndApplyConfigBeforeLogin(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper { + if testing.Short() { + tb.SkipNow() + } + + if mainHelper == nil { + tb.SkipNow() + } + + dbStore := mainHelper.GetStore() + dbStore.DropAllTables() + dbStore.MarkSystemRanUnitTests() + mainHelper.PreloadMigrations() + searchEngine := mainHelper.GetSearchEngine() + th := setupTestHelper(dbStore, searchEngine, false, true, nil, nil) + th.App.UpdateConfig(updateConfig) + th.InitLogin() + return th +} + func SetupConfig(tb testing.TB, updateConfig func(cfg *model.Config)) *TestHelper { if testing.Short() { tb.SkipNow() diff --git a/api4/user.go b/api4/user.go index 4cf55a6d46..9a07f3a8c2 100644 --- a/api4/user.go +++ b/api4/user.go @@ -1852,6 +1852,11 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) { c.App.AttachSessionCookies(c.AppContext, w, r) } + // For context see: https://mattermost.atlassian.net/browse/MM-39583 + if c.App.Srv().License() != nil && *c.App.Srv().License().Features.Cloud { + c.App.AttachCloudSessionCookie(c.AppContext, w, r) + } + userTermsOfService, err := c.App.GetUserTermsOfService(user.Id) if err != nil && err.StatusCode != http.StatusNotFound { c.Err = err diff --git a/api4/user_test.go b/api4/user_test.go index e4dd5f90cb..58aa461926 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -3640,6 +3640,46 @@ func TestLoginCookies(t *testing.T) { }) } }) + + t.Run("should return cookie with MMCLOUDURL for cloud installations", func(t *testing.T) { + updateConfig := func(cfg *model.Config) { + *cfg.ServiceSettings.AllowCookiesForSubdomains = true + *cfg.ServiceSettings.SiteURL = "https://testchips.cloud.mattermost.com" + } + th := SetupAndApplyConfigBeforeLogin(t, updateConfig).InitBasic() + defer th.TearDown() + + th.App.Srv().SetLicense(model.NewTestLicense("cloud")) + + _, resp, _ := th.Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + val := strings.Split(resp.Header["Set-Cookie"][0], ";") + cloudSessionCookie := strings.Split(val[0], "=")[1] + domain := strings.Split(val[2], "=")[1] + + assert.Equal(t, "testchips", cloudSessionCookie) + assert.Equal(t, "mattermost.com", domain) + }) + + t.Run("should NOT return cookie with MMCLOUDURL for NON cloud installations", func(t *testing.T) { + updateConfig := func(cfg *model.Config) { + *cfg.ServiceSettings.AllowCookiesForSubdomains = true + *cfg.ServiceSettings.SiteURL = "https://testchips.com" + } + th := SetupAndApplyConfigBeforeLogin(t, updateConfig).InitBasic() + defer th.TearDown() + + _, resp, _ := th.Client.Login(th.BasicUser.Email, th.BasicUser.Password) + + cloudSessionCookie := "" + for _, cookie := range resp.Header["Set-Cookie"] { + if match := regexp.MustCompile("^" + model.SessionCookieCloudUrl + "=([a-z0-9]+)").FindStringSubmatch(cookie); match != nil { + cloudSessionCookie = match[1] + } + } + // no cookie set + assert.Equal(t, "", cloudSessionCookie) + }) } func TestCBALogin(t *testing.T) { diff --git a/app/app_iface.go b/app/app_iface.go index d5a3824642..6bf77be339 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -391,6 +391,7 @@ type AppIface interface { AllowOAuthAppAccessToUser(userID string, authRequest *model.AuthorizeRequest) (string, *model.AppError) AppendFile(fr io.Reader, path string) (int64, *model.AppError) AsymmetricSigningKey() *ecdsa.PrivateKey + AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request) AttachDeviceId(sessionID string, deviceID string, expiresAt int64) *model.AppError AttachSessionCookies(c *request.Context, w http.ResponseWriter, r *http.Request) AuthenticateUserForLogin(c *request.Context, id, loginId, password, mfaToken, cwsToken string, ldapOnly bool) (user *model.User, err *model.AppError) diff --git a/app/login.go b/app/login.go index 148a443f50..fd13f3a999 100644 --- a/app/login.go +++ b/app/login.go @@ -238,6 +238,39 @@ func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request return nil } +func (a *App) AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request) { + secure := false + if GetProtocol(r) == "https" { + secure = true + } + + maxAge := *a.Config().ServiceSettings.SessionLengthWebInDays * 60 * 60 * 24 + domain := a.GetCookieDomain() + subpath, _ := utils.GetSubpathFromConfig(a.Config()) + expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0) + + var val string + if strings.Contains(domain, "localhost") { + val = "localhost" + } else { + val = strings.SplitN(domain, ".", 2)[0] + domain = strings.SplitN(domain, ".", 3)[2] + } + + cookie := &http.Cookie{ + Name: model.SessionCookieCloudUrl, + Value: val, + Path: subpath, + MaxAge: maxAge, + Expires: expiresAt, + HttpOnly: true, + Domain: domain, + Secure: secure, + } + + http.SetCookie(w, cookie) +} + func (a *App) AttachSessionCookies(c *request.Context, w http.ResponseWriter, r *http.Request) { secure := false if GetProtocol(r) == "https" { diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 87f48b4680..695bce2a25 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -686,6 +686,21 @@ func (a *OpenTracingAppLayer) AsymmetricSigningKey() *ecdsa.PrivateKey { return resultVar0 } +func (a *OpenTracingAppLayer) AttachCloudSessionCookie(c *request.Context, w http.ResponseWriter, r *http.Request) { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AttachCloudSessionCookie") + + a.ctx = newCtx + a.app.Srv().Store.SetContext(newCtx) + defer func() { + a.app.Srv().Store.SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + a.app.AttachCloudSessionCookie(c, w, r) +} + func (a *OpenTracingAppLayer) AttachDeviceId(sessionID string, deviceID string, expiresAt int64) *model.AppError { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.AttachDeviceId") diff --git a/model/session.go b/model/session.go index 72f8d646a9..36955583ef 100644 --- a/model/session.go +++ b/model/session.go @@ -15,6 +15,7 @@ const ( SessionCookieToken = "MMAUTHTOKEN" SessionCookieUser = "MMUSERID" SessionCookieCsrf = "MMCSRF" + SessionCookieCloudUrl = "MMCLOUDURL" SessionCacheSize = 35000 SessionPropPlatform = "platform" SessionPropOs = "os"