MM-27493 Shared channels (MVP) (#17301)
Remote Cluster Service - provides ability for multiple Mattermost cluster instances to create a trusted connection with each other and exchange messages - trusted connections are managed via slash commands (for now) - facilitates features requiring inter-cluster communication, such as Shared Channels Shared Channels Service - provides ability to shared channels between one or more Mattermost cluster instances (using trusted connection) - sharing/unsharing of channels is managed via slash commands (for now)
Этот коммит содержится в:
@@ -131,6 +131,13 @@ func (c *Context) CloudKeyRequired() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) RemoteClusterTokenRequired() {
|
||||
if license := c.App.Srv().License(); license == nil || !*license.Features.RemoteClusterService || c.App.Session().Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_REMOTECLUSTER_TOKEN {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "TokenRequired", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) MfaRequired() {
|
||||
// Must be licensed for MFA and have it configured for enforcement
|
||||
if license := c.App.Srv().License(); license == nil || !*license.Features.MFA || !*c.App.Config().ServiceSettings.EnableMultifactorAuthentication || !*c.App.Config().ServiceSettings.EnforceMultifactorAuthentication {
|
||||
@@ -209,6 +216,18 @@ func (c *Context) SetServerBusyError() {
|
||||
c.Err = NewServerBusyError()
|
||||
}
|
||||
|
||||
func (c *Context) SetInvalidRemoteIdError(id string) {
|
||||
c.Err = NewInvalidRemoteIdError(id)
|
||||
}
|
||||
|
||||
func (c *Context) SetInvalidRemoteClusterTokenError() {
|
||||
c.Err = NewInvalidRemoteClusterTokenError()
|
||||
}
|
||||
|
||||
func (c *Context) SetJSONEncodingError() {
|
||||
c.Err = NewJSONEncodingError()
|
||||
}
|
||||
|
||||
func (c *Context) SetCommandNotFoundError() {
|
||||
c.Err = model.NewAppError("GetCommand", "store.sql_command.save.get.app_error", nil, "", http.StatusNotFound)
|
||||
}
|
||||
@@ -246,6 +265,21 @@ func NewServerBusyError() *model.AppError {
|
||||
return err
|
||||
}
|
||||
|
||||
func NewInvalidRemoteIdError(parameter string) *model.AppError {
|
||||
err := model.NewAppError("Context", "api.context.remote_id_invalid.app_error", map[string]interface{}{"RemoteId": parameter}, "", http.StatusBadRequest)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewInvalidRemoteClusterTokenError() *model.AppError {
|
||||
err := model.NewAppError("Context", "api.context.remote_id_invalid.app_error", nil, "", http.StatusUnauthorized)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewJSONEncodingError() *model.AppError {
|
||||
err := model.NewAppError("Context", "api.context.json_encoding.app_error", nil, "", http.StatusInternalServerError)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Context) SetPermissionError(permissions ...*model.Permission) {
|
||||
c.Err = c.App.MakePermissionError(permissions)
|
||||
}
|
||||
@@ -685,3 +719,7 @@ func (c *Context) RequireInvoiceId() *Context {
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Context) GetRemoteID(r *http.Request) string {
|
||||
return r.Header.Get(model.HEADER_REMOTECLUSTER_ID)
|
||||
}
|
||||
|
||||
@@ -70,16 +70,17 @@ func (w *Web) NewStaticHandler(h func(*Context, http.ResponseWriter, *http.Reque
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
GetGlobalAppOptions app.AppOptionCreator
|
||||
HandleFunc func(*Context, http.ResponseWriter, *http.Request)
|
||||
HandlerName string
|
||||
RequireSession bool
|
||||
RequireCloudKey bool
|
||||
TrustRequester bool
|
||||
RequireMfa bool
|
||||
IsStatic bool
|
||||
IsLocal bool
|
||||
DisableWhenBusy bool
|
||||
GetGlobalAppOptions app.AppOptionCreator
|
||||
HandleFunc func(*Context, http.ResponseWriter, *http.Request)
|
||||
HandlerName string
|
||||
RequireSession bool
|
||||
RequireCloudKey bool
|
||||
RequireRemoteClusterToken bool
|
||||
TrustRequester bool
|
||||
RequireMfa bool
|
||||
IsStatic bool
|
||||
IsLocal bool
|
||||
DisableWhenBusy bool
|
||||
|
||||
cspShaDirective string
|
||||
}
|
||||
@@ -204,7 +205,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
token, tokenLocation := app.ParseAuthTokenFromRequest(r)
|
||||
|
||||
if token != "" && tokenLocation != app.TokenLocationCloudHeader {
|
||||
if token != "" && tokenLocation != app.TokenLocationCloudHeader && tokenLocation != app.TokenLocationRemoteClusterHeader {
|
||||
session, err := c.App.GetSession(token)
|
||||
defer app.ReturnSessionToPool(session)
|
||||
|
||||
@@ -237,6 +238,21 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
c.App.SetSession(session)
|
||||
}
|
||||
} else if token != "" && c.App.Srv().License() != nil && *c.App.Srv().License().Features.RemoteClusterService && tokenLocation == app.TokenLocationRemoteClusterHeader {
|
||||
// Get the remote cluster
|
||||
if remoteId := c.GetRemoteID(r); remoteId == "" {
|
||||
c.Logger.Warn("Missing remote cluster id") //
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.remote_id_missing.app_error", nil, "", http.StatusUnauthorized)
|
||||
} else {
|
||||
// Check the token is correct for the remote cluster id.
|
||||
session, err := c.App.GetRemoteClusterSession(token, remoteId)
|
||||
if err != nil {
|
||||
c.Logger.Warn("Invalid remote cluster token", mlog.Err(err))
|
||||
c.Err = err
|
||||
} else {
|
||||
c.App.SetSession(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Logger = c.App.Log().With(
|
||||
@@ -263,6 +279,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
c.CloudKeyRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.RequireRemoteClusterToken {
|
||||
c.RemoteClusterTokenRequired()
|
||||
}
|
||||
|
||||
if c.Err == nil && h.IsLocal {
|
||||
// if the connection is local, RemoteAddr shouldn't have the
|
||||
// shape IP:PORT (it will be "@" in Linux, for example)
|
||||
|
||||
Ссылка в новой задаче
Block a user