[MM-12958] Support running two Mattermost instances on the same domain using subpaths (#10493)
Этот коммит содержится в:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
func (a *App) CheckForClientSideCert(r *http.Request) (string, string, string) {
|
||||
@@ -164,11 +165,13 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
}
|
||||
|
||||
domain := a.GetCookieDomain()
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0)
|
||||
sessionCookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_TOKEN,
|
||||
Value: session.Token,
|
||||
Path: "/",
|
||||
Path: subpath,
|
||||
MaxAge: maxAge,
|
||||
Expires: expiresAt,
|
||||
HttpOnly: true,
|
||||
@@ -179,7 +182,7 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
userCookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_USER,
|
||||
Value: user.Id,
|
||||
Path: "/",
|
||||
Path: subpath,
|
||||
MaxAge: maxAge,
|
||||
Expires: expiresAt,
|
||||
Domain: domain,
|
||||
@@ -189,7 +192,7 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
csrfCookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_CSRF,
|
||||
Value: session.GetCSRF(),
|
||||
Path: "/",
|
||||
Path: subpath,
|
||||
MaxAge: maxAge,
|
||||
Expires: expiresAt,
|
||||
Domain: domain,
|
||||
|
||||
@@ -654,11 +654,13 @@ func (a *App) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, servi
|
||||
}
|
||||
|
||||
cookieValue := model.NewId()
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
expiresAt := time.Unix(model.GetMillis()/1000+int64(OAUTH_COOKIE_MAX_AGE_SECONDS), 0)
|
||||
oauthCookie := &http.Cookie{
|
||||
Name: COOKIE_OAUTH,
|
||||
Value: cookieValue,
|
||||
Path: "/",
|
||||
Path: subpath,
|
||||
MaxAge: OAUTH_COOKIE_MAX_AGE_SECONDS,
|
||||
Expires: expiresAt,
|
||||
HttpOnly: true,
|
||||
@@ -741,10 +743,12 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
|
||||
mlog.Error(appErr.Error())
|
||||
}
|
||||
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
httpCookie := &http.Cookie{
|
||||
Name: COOKIE_OAUTH,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
Path: subpath,
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
@@ -468,43 +468,120 @@ func TestAuthorizeOAuthUser(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("enabled and properly configured", func(t *testing.T) {
|
||||
userData := "Hello, World!"
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/token":
|
||||
json.NewEncoder(w).Encode(&model.AccessResponse{
|
||||
AccessToken: model.NewId(),
|
||||
TokenType: model.ACCESS_TOKEN_TYPE,
|
||||
})
|
||||
case "/user":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(userData))
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
th := setup(true, true, true, server.URL)
|
||||
defer th.TearDown()
|
||||
|
||||
cookie := model.NewId()
|
||||
request := makeRequest(t, cookie)
|
||||
|
||||
stateProps := map[string]string{
|
||||
"team_id": model.NewId(),
|
||||
"token": makeToken(th, cookie).Token,
|
||||
testCases := []struct {
|
||||
Description string
|
||||
SiteURL string
|
||||
ExpectedSetCookieHeaderRegexp string
|
||||
}{
|
||||
{"no subpath", "http://localhost:8065", "^MMOAUTH=; Path=/"},
|
||||
{"subpath", "http://localhost:8065/subpath", "^MMOAUTH=; Path=/subpath"},
|
||||
}
|
||||
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
|
||||
body, receivedTeamId, receivedStateProps, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "")
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Description, func(t *testing.T) {
|
||||
userData := "Hello, World!"
|
||||
|
||||
require.NotNil(t, body)
|
||||
bodyBytes, bodyErr := ioutil.ReadAll(body)
|
||||
require.Nil(t, bodyErr)
|
||||
assert.Equal(t, userData, string(bodyBytes))
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/token":
|
||||
json.NewEncoder(w).Encode(&model.AccessResponse{
|
||||
AccessToken: model.NewId(),
|
||||
TokenType: model.ACCESS_TOKEN_TYPE,
|
||||
})
|
||||
case "/user":
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(userData))
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
assert.Equal(t, stateProps["team_id"], receivedTeamId)
|
||||
assert.Equal(t, stateProps, receivedStateProps)
|
||||
assert.Nil(t, err)
|
||||
th := setup(true, true, true, server.URL)
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.SiteURL = tc.SiteURL
|
||||
})
|
||||
|
||||
cookie := model.NewId()
|
||||
request := makeRequest(t, cookie)
|
||||
|
||||
stateProps := map[string]string{
|
||||
"team_id": model.NewId(),
|
||||
"token": makeToken(th, cookie).Token,
|
||||
}
|
||||
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
|
||||
|
||||
recorder := httptest.ResponseRecorder{}
|
||||
body, receivedTeamId, receivedStateProps, err := th.App.AuthorizeOAuthUser(&recorder, request, model.SERVICE_GITLAB, "", state, "")
|
||||
|
||||
require.NotNil(t, body)
|
||||
bodyBytes, bodyErr := ioutil.ReadAll(body)
|
||||
require.Nil(t, bodyErr)
|
||||
assert.Equal(t, userData, string(bodyBytes))
|
||||
|
||||
assert.Equal(t, stateProps["team_id"], receivedTeamId)
|
||||
assert.Equal(t, stateProps, receivedStateProps)
|
||||
assert.Nil(t, err)
|
||||
|
||||
cookies := recorder.Header().Get("Set-Cookie")
|
||||
assert.Regexp(t, tc.ExpectedSetCookieHeaderRegexp, cookies)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetAuthorizationCode(t *testing.T) {
|
||||
t.Run("not enabled", func(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.GitLabSettings.Enable = false
|
||||
})
|
||||
|
||||
_, err := th.App.GetAuthorizationCode(nil, nil, model.SERVICE_GITLAB, map[string]string{}, "")
|
||||
require.NotNil(t, err)
|
||||
assert.Equal(t, "api.user.get_authorization_code.unsupported.app_error", err.Id)
|
||||
})
|
||||
|
||||
t.Run("enabled and properly configured", func(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.GitLabSettings.Enable = true
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
Description string
|
||||
SiteURL string
|
||||
ExpectedSetCookieHeaderRegexp string
|
||||
}{
|
||||
{"no subpath", "http://localhost:8065", "^MMOAUTH=[a-z0-9]+; Path=/"},
|
||||
{"subpath", "http://localhost:8065/subpath", "^MMOAUTH=[a-z0-9]+; Path=/subpath"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Description, func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.SiteURL = tc.SiteURL
|
||||
})
|
||||
|
||||
request, _ := http.NewRequest(http.MethodGet, "https://mattermost.example.com", nil)
|
||||
|
||||
stateProps := map[string]string{
|
||||
"email": "email@example.com",
|
||||
"action": "action",
|
||||
}
|
||||
|
||||
recorder := httptest.ResponseRecorder{}
|
||||
url, err := th.App.GetAuthorizationCode(&recorder, request, model.SERVICE_GITLAB, stateProps, "")
|
||||
require.Nil(t, err)
|
||||
assert.NotEmpty(t, url)
|
||||
|
||||
cookies := recorder.Header().Get("Set-Cookie")
|
||||
assert.Regexp(t, tc.ExpectedSetCookieHeaderRegexp, cookies)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user