Migrate to stateless app.App (#17542)
* add request context * move initialialization to server * use app interface instead of global app functions * remove app context from webconn * cleanup * remove duplicated services * move context to separate package * remove finalize init method and move content to NewServer function * restart workers and schedulers after adding license for tests * reflect review comments Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c09369f14a
Коммит
5ea06e51d0
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/audit"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
|
||||
type Context struct {
|
||||
App app.AppIface
|
||||
AppContext *request.Context
|
||||
Logger *mlog.Logger
|
||||
Params *Params
|
||||
Err *model.AppError
|
||||
@@ -51,13 +53,13 @@ func (c *Context) LogAuditRecWithLevel(rec *audit.Record, level mlog.LogLevel) {
|
||||
// MakeAuditRecord creates a audit record pre-populated with data from this context.
|
||||
func (c *Context) MakeAuditRecord(event string, initialStatus string) *audit.Record {
|
||||
rec := &audit.Record{
|
||||
APIPath: c.App.Path(),
|
||||
APIPath: c.AppContext.Path(),
|
||||
Event: event,
|
||||
Status: initialStatus,
|
||||
UserID: c.App.Session().UserId,
|
||||
SessionID: c.App.Session().Id,
|
||||
Client: c.App.UserAgent(),
|
||||
IPAddress: c.App.IpAddress(),
|
||||
UserID: c.AppContext.Session().UserId,
|
||||
SessionID: c.AppContext.Session().Id,
|
||||
Client: c.AppContext.UserAgent(),
|
||||
IPAddress: c.AppContext.IpAddress(),
|
||||
Meta: audit.Meta{audit.KeyClusterID: c.App.GetClusterId()},
|
||||
}
|
||||
rec.AddMetaTypeConverter(model.AuditModelTypeConv)
|
||||
@@ -66,7 +68,7 @@ func (c *Context) MakeAuditRecord(event string, initialStatus string) *audit.Rec
|
||||
}
|
||||
|
||||
func (c *Context) LogAudit(extraInfo string) {
|
||||
audit := &model.Audit{UserId: c.App.Session().UserId, IpAddress: c.App.IpAddress(), Action: c.App.Path(), ExtraInfo: extraInfo, SessionId: c.App.Session().Id}
|
||||
audit := &model.Audit{UserId: c.AppContext.Session().UserId, IpAddress: c.AppContext.IpAddress(), Action: c.AppContext.Path(), ExtraInfo: extraInfo, SessionId: c.AppContext.Session().Id}
|
||||
if err := c.App.Srv().Store.Audit().Save(audit); err != nil {
|
||||
appErr := model.NewAppError("LogAudit", "app.audit.save.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
c.LogErrorByCode(appErr)
|
||||
@@ -75,11 +77,11 @@ func (c *Context) LogAudit(extraInfo string) {
|
||||
|
||||
func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
|
||||
|
||||
if c.App.Session().UserId != "" {
|
||||
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.App.Session().UserId)
|
||||
if c.AppContext.Session().UserId != "" {
|
||||
extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.AppContext.Session().UserId)
|
||||
}
|
||||
|
||||
audit := &model.Audit{UserId: userId, IpAddress: c.App.IpAddress(), Action: c.App.Path(), ExtraInfo: extraInfo, SessionId: c.App.Session().Id}
|
||||
audit := &model.Audit{UserId: userId, IpAddress: c.AppContext.IpAddress(), Action: c.AppContext.Path(), ExtraInfo: extraInfo, SessionId: c.AppContext.Session().Id}
|
||||
if err := c.App.Srv().Store.Audit().Save(audit); err != nil {
|
||||
appErr := model.NewAppError("LogAuditWithUserId", "app.audit.save.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
c.LogErrorByCode(appErr)
|
||||
@@ -106,33 +108,33 @@ func (c *Context) LogErrorByCode(err *model.AppError) {
|
||||
}
|
||||
|
||||
func (c *Context) IsSystemAdmin() bool {
|
||||
return c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM)
|
||||
return c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM)
|
||||
}
|
||||
|
||||
func (c *Context) SessionRequired() {
|
||||
if !*c.App.Config().ServiceSettings.EnableUserAccessTokens &&
|
||||
c.App.Session().Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN &&
|
||||
c.App.Session().Props[model.SESSION_PROP_IS_BOT] != model.SESSION_PROP_IS_BOT_VALUE {
|
||||
c.AppContext.Session().Props[model.SESSION_PROP_TYPE] == model.SESSION_TYPE_USER_ACCESS_TOKEN &&
|
||||
c.AppContext.Session().Props[model.SESSION_PROP_IS_BOT] != model.SESSION_PROP_IS_BOT_VALUE {
|
||||
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserAccessToken", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.Session().UserId == "" {
|
||||
if c.AppContext.Session().UserId == "" {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "UserRequired", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) CloudKeyRequired() {
|
||||
if license := c.App.Srv().License(); license == nil || !*license.Features.Cloud || c.App.Session().Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_CLOUD_KEY {
|
||||
if license := c.App.Srv().License(); license == nil || !*license.Features.Cloud || c.AppContext.Session().Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_CLOUD_KEY {
|
||||
c.Err = model.NewAppError("", "api.context.session_expired.app_error", nil, "TokenRequired", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if license := c.App.Srv().License(); license == nil || !*license.Features.RemoteClusterService || c.AppContext.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
|
||||
}
|
||||
@@ -145,11 +147,11 @@ func (c *Context) MfaRequired() {
|
||||
}
|
||||
|
||||
// OAuth integrations are excepted
|
||||
if c.App.Session().IsOAuth {
|
||||
if c.AppContext.Session().IsOAuth {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.GetUser(c.App.Session().UserId)
|
||||
user, err := c.App.GetUser(c.AppContext.Session().UserId)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("MfaRequired", "api.context.get_user.app_error", nil, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
@@ -167,7 +169,7 @@ func (c *Context) MfaRequired() {
|
||||
|
||||
// Special case to let user get themself
|
||||
subpath, _ := utils.GetSubpathFromConfig(c.App.Config())
|
||||
if c.App.Path() == path.Join(subpath, "/api/v4/users/me") {
|
||||
if c.AppContext.Path() == path.Join(subpath, "/api/v4/users/me") {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -185,8 +187,8 @@ func (c *Context) MfaRequired() {
|
||||
// ExtendSessionExpiryIfNeeded will update Session.ExpiresAt based on session lengths in config.
|
||||
// Session cookies will be resent to the client with updated max age.
|
||||
func (c *Context) ExtendSessionExpiryIfNeeded(w http.ResponseWriter, r *http.Request) {
|
||||
if ok := c.App.ExtendSessionExpiryIfNeeded(c.App.Session()); ok {
|
||||
c.App.AttachSessionCookies(w, r)
|
||||
if ok := c.App.ExtendSessionExpiryIfNeeded(c.AppContext.Session()); ok {
|
||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +283,7 @@ func NewJSONEncodingError() *model.AppError {
|
||||
}
|
||||
|
||||
func (c *Context) SetPermissionError(permissions ...*model.Permission) {
|
||||
c.Err = c.App.MakePermissionError(permissions)
|
||||
c.Err = c.App.MakePermissionError(c.AppContext.Session(), permissions)
|
||||
}
|
||||
|
||||
func (c *Context) SetSiteURLHeader(url string) {
|
||||
@@ -298,7 +300,7 @@ func (c *Context) RequireUserId() *Context {
|
||||
}
|
||||
|
||||
if c.Params.UserId == model.ME {
|
||||
c.Params.UserId = c.App.Session().UserId
|
||||
c.Params.UserId = c.AppContext.Session().UserId
|
||||
}
|
||||
|
||||
if !model.IsValidId(c.Params.UserId) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/plugin/plugintest/mock"
|
||||
"github.com/mattermost/mattermost-server/v5/store/storetest/mocks"
|
||||
@@ -41,7 +42,8 @@ func TestCloudKeyRequired(t *testing.T) {
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: &request.Context{},
|
||||
}
|
||||
|
||||
c.CloudKeyRequired()
|
||||
@@ -69,7 +71,7 @@ func TestMfaRequired(t *testing.T) {
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("mfa"))
|
||||
|
||||
th.App.SetSession(&model.Session{Id: "abc", UserId: "userid"})
|
||||
th.Context.SetSession(&model.Session{Id: "abc", UserId: "userid"})
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.AnnouncementSettings.UserNoticesEnabled = false
|
||||
@@ -79,7 +81,8 @@ func TestMfaRequired(t *testing.T) {
|
||||
})
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
|
||||
c.MfaRequired()
|
||||
|
||||
146
web/handlers.go
146
web/handlers.go
@@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
app_opentracing "github.com/mattermost/mattermost-server/v5/app/opentracing"
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/services/tracing"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
@@ -40,37 +41,37 @@ func GetHandlerName(h func(*Context, http.ResponseWriter, *http.Request)) string
|
||||
|
||||
func (w *Web) NewHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
return &Handler{
|
||||
GetGlobalAppOptions: w.GetGlobalAppOptions,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
App: w.app,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Web) NewStaticHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
// Determine the CSP SHA directive needed for subpath support, if any. This value is fixed
|
||||
// on server start and intentionally requires a restart to take effect.
|
||||
subpath, _ := utils.GetSubpathFromConfig(w.ConfigService.Config())
|
||||
subpath, _ := utils.GetSubpathFromConfig(w.app.Config())
|
||||
|
||||
return &Handler{
|
||||
GetGlobalAppOptions: w.GetGlobalAppOptions,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
App: w.app,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
|
||||
cspShaDirective: utils.GetSubpathScriptHash(subpath),
|
||||
}
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
GetGlobalAppOptions app.AppOptionCreator
|
||||
App app.AppIface
|
||||
HandleFunc func(*Context, http.ResponseWriter, *http.Request)
|
||||
HandlerName string
|
||||
RequireSession bool
|
||||
@@ -104,19 +105,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
mlog.Debug("Received HTTP request", responseLogFields...)
|
||||
}()
|
||||
|
||||
c := &Context{}
|
||||
c.App = app.New(
|
||||
h.GetGlobalAppOptions()...,
|
||||
)
|
||||
c.App.InitServer()
|
||||
c := &Context{
|
||||
AppContext: &request.Context{},
|
||||
App: h.App,
|
||||
}
|
||||
|
||||
t, _ := i18n.GetTranslationsAndLocaleFromRequest(r)
|
||||
c.App.SetT(t)
|
||||
c.App.SetRequestId(requestID)
|
||||
c.App.SetIpAddress(utils.GetIPAddress(r, c.App.Config().ServiceSettings.TrustedProxyIPHeader))
|
||||
c.App.SetUserAgent(r.UserAgent())
|
||||
c.App.SetAcceptLanguage(r.Header.Get("Accept-Language"))
|
||||
c.App.SetPath(r.URL.Path)
|
||||
c.AppContext.SetT(t)
|
||||
c.AppContext.SetRequestId(requestID)
|
||||
c.AppContext.SetIpAddress(utils.GetIPAddress(r, c.App.Config().ServiceSettings.TrustedProxyIPHeader))
|
||||
c.AppContext.SetUserAgent(r.UserAgent())
|
||||
c.AppContext.SetAcceptLanguage(r.Header.Get("Accept-Language"))
|
||||
c.AppContext.SetPath(r.URL.Path)
|
||||
c.Params = ParamsFromRequest(r)
|
||||
c.Logger = c.App.Log()
|
||||
|
||||
@@ -125,10 +125,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
carrier := opentracing.HTTPHeadersCarrier(r.Header)
|
||||
_ = opentracing.GlobalTracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier)
|
||||
ext.HTTPMethod.Set(span, r.Method)
|
||||
ext.HTTPUrl.Set(span, c.App.Path())
|
||||
ext.PeerAddress.Set(span, c.App.IpAddress())
|
||||
span.SetTag("request_id", c.App.RequestId())
|
||||
span.SetTag("user_agent", c.App.UserAgent())
|
||||
ext.HTTPUrl.Set(span, c.AppContext.Path())
|
||||
ext.PeerAddress.Set(span, c.AppContext.IpAddress())
|
||||
span.SetTag("request_id", c.AppContext.RequestId())
|
||||
span.SetTag("user_agent", c.AppContext.UserAgent())
|
||||
|
||||
defer func() {
|
||||
if c.Err != nil {
|
||||
@@ -138,7 +138,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
span.Finish()
|
||||
}()
|
||||
c.App.SetContext(ctx)
|
||||
c.AppContext.SetContext(ctx)
|
||||
|
||||
tmpSrv := *c.App.Srv()
|
||||
tmpSrv.Store = opentracinglayer.New(c.App.Srv().Store, ctx)
|
||||
@@ -158,7 +158,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
siteURLHeader := app.GetProtocol(r) + "://" + r.Host + subpath
|
||||
c.SetSiteURLHeader(siteURLHeader)
|
||||
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.App.RequestId())
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.AppContext.RequestId())
|
||||
w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.Srv().License() != nil))
|
||||
|
||||
if *c.App.Config().ServiceSettings.TLSStrictTransport {
|
||||
@@ -219,11 +219,11 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
} else if !session.IsOAuth && tokenLocation == app.TokenLocationQueryString {
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized)
|
||||
} else {
|
||||
c.App.SetSession(session)
|
||||
c.AppContext.SetSession(session)
|
||||
}
|
||||
|
||||
// Rate limit by UserID
|
||||
if c.App.Srv().RateLimiter != nil && c.App.Srv().RateLimiter.UserIdRateLimit(c.App.Session().UserId, w) {
|
||||
if c.App.Srv().RateLimiter != nil && c.App.Srv().RateLimiter.UserIdRateLimit(c.AppContext.Session().UserId, w) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
c.Logger.Warn("Invalid CWS token", mlog.Err(err))
|
||||
c.Err = err
|
||||
} else {
|
||||
c.App.SetSession(session)
|
||||
c.AppContext.SetSession(session)
|
||||
}
|
||||
} else if token != "" && c.App.Srv().License() != nil && *c.App.Srv().License().Features.RemoteClusterService && tokenLocation == app.TokenLocationRemoteClusterHeader {
|
||||
// Get the remote cluster
|
||||
@@ -249,16 +249,16 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
c.Logger.Warn("Invalid remote cluster token", mlog.Err(err))
|
||||
c.Err = err
|
||||
} else {
|
||||
c.App.SetSession(session)
|
||||
c.AppContext.SetSession(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Logger = c.App.Log().With(
|
||||
mlog.String("path", c.App.Path()),
|
||||
mlog.String("request_id", c.App.RequestId()),
|
||||
mlog.String("ip_addr", c.App.IpAddress()),
|
||||
mlog.String("user_id", c.App.Session().UserId),
|
||||
mlog.String("path", c.AppContext.Path()),
|
||||
mlog.String("request_id", c.AppContext.RequestId()),
|
||||
mlog.String("ip_addr", c.AppContext.IpAddress()),
|
||||
mlog.String("user_id", c.AppContext.Session().UserId),
|
||||
mlog.String("method", r.Method),
|
||||
)
|
||||
|
||||
@@ -287,7 +287,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// shape IP:PORT (it will be "@" in Linux, for example)
|
||||
isLocalOrigin := !strings.Contains(r.RemoteAddr, ":")
|
||||
if *c.App.Config().ServiceSettings.EnableLocalMode && isLocalOrigin {
|
||||
c.App.SetSession(&model.Session{Local: true})
|
||||
c.AppContext.SetSession(&model.Session{Local: true})
|
||||
} else if !isLocalOrigin {
|
||||
c.Err = model.NewAppError("", "api.context.local_origin_required.app_error", nil, "LocalOriginRequired", http.StatusUnauthorized)
|
||||
}
|
||||
@@ -299,8 +299,8 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Handle errors that have occurred
|
||||
if c.Err != nil {
|
||||
c.Err.Translate(c.App.T)
|
||||
c.Err.RequestId = c.App.RequestId()
|
||||
c.Err.Translate(c.AppContext.T)
|
||||
c.Err.RequestId = c.AppContext.RequestId()
|
||||
c.LogErrorByCode(c.Err)
|
||||
|
||||
c.Err.Where = r.URL.Path
|
||||
@@ -382,7 +382,7 @@ func (h *Handler) checkCSRFToken(c *Context, r *http.Request, token string, toke
|
||||
}
|
||||
|
||||
if !csrfCheckPassed {
|
||||
c.App.SetSession(&model.Session{})
|
||||
c.AppContext.SetSession(&model.Session{})
|
||||
c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
@@ -394,16 +394,16 @@ func (h *Handler) checkCSRFToken(c *Context, r *http.Request, token string, toke
|
||||
// granted.
|
||||
func (w *Web) ApiHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
handler := &Handler{
|
||||
GetGlobalAppOptions: w.GetGlobalAppOptions,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
App: w.app,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
}
|
||||
if *w.ConfigService.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
if *w.app.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
return gziphandler.GzipHandler(handler)
|
||||
}
|
||||
return handler
|
||||
@@ -414,16 +414,16 @@ func (w *Web) ApiHandler(h func(*Context, http.ResponseWriter, *http.Request)) h
|
||||
// websocket.
|
||||
func (w *Web) ApiHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
handler := &Handler{
|
||||
GetGlobalAppOptions: w.GetGlobalAppOptions,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: true,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
App: w.app,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: false,
|
||||
TrustRequester: true,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
}
|
||||
if *w.ConfigService.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
if *w.app.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
return gziphandler.GzipHandler(handler)
|
||||
}
|
||||
return handler
|
||||
@@ -433,16 +433,16 @@ func (w *Web) ApiHandlerTrustRequester(h func(*Context, http.ResponseWriter, *ht
|
||||
// be granted.
|
||||
func (w *Web) ApiSessionRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
handler := &Handler{
|
||||
GetGlobalAppOptions: w.GetGlobalAppOptions,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: true,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
App: w.app,
|
||||
HandleFunc: h,
|
||||
HandlerName: GetHandlerName(h),
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: true,
|
||||
IsStatic: false,
|
||||
IsLocal: false,
|
||||
}
|
||||
if *w.ConfigService.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
if *w.app.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
return gziphandler.GzipHandler(handler)
|
||||
}
|
||||
return handler
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestHandlerServeHTTPErrors(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
handler := web.NewHandler(handlerForHTTPErrors)
|
||||
|
||||
var flagtests = []struct {
|
||||
@@ -84,7 +84,7 @@ func TestHandlerServeHTTPSecureTransport(t *testing.T) {
|
||||
*config.ServiceSettings.TLSStrictTransportMaxAge = 6000
|
||||
})
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
handler := web.NewHandler(handlerForHTTPSecureTransport)
|
||||
|
||||
request := httptest.NewRequest("GET", "/api/v4/test", nil)
|
||||
@@ -136,15 +136,15 @@ func TestHandlerServeCSRFToken(t *testing.T) {
|
||||
t.Errorf("Expected nil, got %s", err)
|
||||
}
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
|
||||
handler := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
App: web.app,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
@@ -219,12 +219,12 @@ func TestHandlerServeCSRFToken(t *testing.T) {
|
||||
// Handler with RequireSession set to false
|
||||
|
||||
handlerNoSession := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
App: th.App,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
}
|
||||
|
||||
// CSRF Token Used - Success Expected
|
||||
@@ -263,15 +263,15 @@ func TestHandlerServeCSPHeader(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
|
||||
handler := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
App: web.app,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest("POST", "/api/v4/test", nil)
|
||||
@@ -285,15 +285,15 @@ func TestHandlerServeCSPHeader(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
|
||||
handler := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
App: web.app,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest("POST", "/", nil)
|
||||
@@ -325,15 +325,15 @@ func TestHandlerServeCSPHeader(t *testing.T) {
|
||||
*cfg.ServiceSettings.SiteURL = *cfg.ServiceSettings.SiteURL + "/subpath"
|
||||
})
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
|
||||
handler := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
App: web.app,
|
||||
HandleFunc: handlerForCSPHeader,
|
||||
RequireSession: false,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: true,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest("POST", "/", nil)
|
||||
@@ -380,15 +380,15 @@ func TestHandlerServeInvalidToken(t *testing.T) {
|
||||
*cfg.ServiceSettings.SiteURL = tc.SiteURL
|
||||
})
|
||||
|
||||
web := New(th.Server, th.Server.AppOptions, th.Server.Router)
|
||||
web := New(th.App, th.Server.Router)
|
||||
|
||||
handler := Handler{
|
||||
GetGlobalAppOptions: web.GetGlobalAppOptions,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
App: web.app,
|
||||
HandleFunc: handlerForCSRFToken,
|
||||
RequireSession: true,
|
||||
TrustRequester: false,
|
||||
RequireMfa: false,
|
||||
IsStatic: false,
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
@@ -422,7 +422,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r.Header.Set(model.HEADER_CSRF_TOKEN, token)
|
||||
@@ -452,8 +453,9 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
Logger: th.App.Log(),
|
||||
App: th.App,
|
||||
Logger: th.App.Log(),
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r.Header.Set(model.HEADER_REQUESTED_WITH, model.HEADER_REQUESTED_WITH_XML)
|
||||
@@ -501,8 +503,9 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
Logger: th.App.Log(),
|
||||
App: th.App,
|
||||
Logger: th.App.Log(),
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r.Header.Set(model.HEADER_REQUESTED_WITH, model.HEADER_REQUESTED_WITH_XML)
|
||||
@@ -532,7 +535,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
session := &model.Session{
|
||||
@@ -561,7 +565,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodGet, "", nil)
|
||||
session := &model.Session{
|
||||
@@ -590,7 +595,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationHeader
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
session := &model.Session{
|
||||
@@ -619,7 +625,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r.Header.Set(model.HEADER_CSRF_TOKEN, token)
|
||||
@@ -644,7 +651,8 @@ func TestCheckCSRFToken(t *testing.T) {
|
||||
tokenLocation := app.TokenLocationCookie
|
||||
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: th.Context,
|
||||
}
|
||||
r, _ := http.NewRequest(http.MethodPost, "", nil)
|
||||
r.Header.Set(model.HEADER_CSRF_TOKEN, token)
|
||||
|
||||
30
web/oauth.go
30
web/oauth.go
@@ -54,7 +54,7 @@ func authorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.App.Session().IsOAuth {
|
||||
if c.AppContext.Session().IsOAuth {
|
||||
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
|
||||
c.Err.DetailedError += ", attempted access by oauth app"
|
||||
return
|
||||
@@ -64,7 +64,7 @@ func authorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer c.LogAuditRec(auditRec)
|
||||
c.LogAudit("attempt")
|
||||
|
||||
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.App.Session().UserId, authRequest)
|
||||
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.AppContext.Session().UserId, authRequest)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
@@ -89,7 +89,7 @@ func deauthorizeOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec := c.MakeAuditRecord("deauthorizeOAuthApp", audit.Fail)
|
||||
defer c.LogAuditRec(auditRec)
|
||||
|
||||
err := c.App.DeauthorizeOAuthAppForUser(c.App.Session().UserId, clientId)
|
||||
err := c.App.DeauthorizeOAuthAppForUser(c.AppContext.Session().UserId, clientId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -134,7 +134,7 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// here we should check if the user is logged in
|
||||
if c.App.Session().UserId == "" {
|
||||
if c.AppContext.Session().UserId == "" {
|
||||
if loginHint == model.USER_AUTH_SERVICE_SAML {
|
||||
http.Redirect(w, r, c.GetSiteURLHeader()+"/login/sso/saml?redirect_to="+url.QueryEscape(r.RequestURI), http.StatusFound)
|
||||
} else {
|
||||
@@ -155,14 +155,14 @@ func authorizeOAuthPage(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
isAuthorized := false
|
||||
|
||||
if _, err := c.App.GetPreferenceByCategoryAndNameForUser(c.App.Session().UserId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, authRequest.ClientId); err == nil {
|
||||
if _, err := c.App.GetPreferenceByCategoryAndNameForUser(c.AppContext.Session().UserId, model.PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP, authRequest.ClientId); err == nil {
|
||||
// when we support scopes we should check if the scopes match
|
||||
isAuthorized = true
|
||||
}
|
||||
|
||||
// Automatically allow if the app is trusted
|
||||
if oauthApp.IsTrusted || isAuthorized {
|
||||
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.App.Session().UserId, authRequest)
|
||||
redirectUrl, err := c.App.AllowOAuthAppAccessToUser(c.AppContext.Session().UserId, authRequest)
|
||||
|
||||
if err != nil {
|
||||
utils.RenderWebAppError(c.App.Config(), w, r, err, c.App.AsymmetricSigningKey())
|
||||
@@ -295,15 +295,15 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err.Translate(c.App.T)
|
||||
err.Translate(c.AppContext.T)
|
||||
c.LogErrorByCode(err)
|
||||
renderError(err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := c.App.CompleteOAuth(service, body, teamId, props, tokenUser)
|
||||
user, err := c.App.CompleteOAuth(c.AppContext, service, body, teamId, props, tokenUser)
|
||||
if err != nil {
|
||||
err.Translate(c.App.T)
|
||||
err.Translate(c.AppContext.T)
|
||||
c.LogErrorByCode(err)
|
||||
renderError(err)
|
||||
return
|
||||
@@ -314,9 +314,9 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
} else if action == model.OAUTH_ACTION_SSO_TO_EMAIL {
|
||||
redirectURL = app.GetProtocol(r) + "://" + r.Host + "/claim?email=" + url.QueryEscape(props["email"])
|
||||
} else {
|
||||
err = c.App.DoLogin(w, r, user, "", isMobile, false, false)
|
||||
err = c.App.DoLogin(c.AppContext, w, r, user, "", isMobile, false, false)
|
||||
if err != nil {
|
||||
err.Translate(c.App.T)
|
||||
err.Translate(c.AppContext.T)
|
||||
mlog.Error(err.Error())
|
||||
renderError(err)
|
||||
return
|
||||
@@ -324,19 +324,19 @@ func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Old mobile version
|
||||
if isMobile && !hasRedirectURL {
|
||||
c.App.AttachSessionCookies(w, r)
|
||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||
return
|
||||
} else
|
||||
// New mobile version
|
||||
if isMobile && hasRedirectURL {
|
||||
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
|
||||
model.SESSION_COOKIE_TOKEN: c.App.Session().Token,
|
||||
model.SESSION_COOKIE_CSRF: c.App.Session().GetCSRF(),
|
||||
model.SESSION_COOKIE_TOKEN: c.AppContext.Session().Token,
|
||||
model.SESSION_COOKIE_CSRF: c.AppContext.Session().GetCSRF(),
|
||||
})
|
||||
utils.RenderMobileAuthComplete(w, redirectURL)
|
||||
return
|
||||
} else { // For web
|
||||
c.App.AttachSessionCookies(w, r)
|
||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||
|
||||
// If no redirect url is passed, get the default one
|
||||
if !hasRedirectURL {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
@@ -530,14 +531,15 @@ func TestOAuthComplete_ErrorMessages(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
c := &Context{
|
||||
App: th.App,
|
||||
App: th.App,
|
||||
AppContext: &request.Context{},
|
||||
Params: &Params{
|
||||
Service: "gitlab",
|
||||
},
|
||||
}
|
||||
|
||||
translationFunc := i18n.GetUserTranslations("en")
|
||||
c.App.SetT(translationFunc)
|
||||
c.AppContext.SetT(translationFunc)
|
||||
buffer := &bytes.Buffer{}
|
||||
c.Logger = mlog.NewTestingLogger(t, buffer)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true })
|
||||
|
||||
14
web/saml.go
14
web/saml.go
@@ -112,7 +112,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
handleError := func(err *model.AppError) {
|
||||
if isMobile && hasRedirectURL {
|
||||
err.Translate(c.App.T)
|
||||
err.Translate(c.AppContext.T)
|
||||
utils.RenderMobileError(c.App.Config(), w, err, redirectURL)
|
||||
} else {
|
||||
c.Err = err
|
||||
@@ -120,7 +120,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
user, err := samlInterface.DoLogin(encodedXML, relayProps)
|
||||
user, err := samlInterface.DoLogin(c.AppContext, encodedXML, relayProps)
|
||||
if err != nil {
|
||||
c.LogAudit("fail")
|
||||
mlog.Error(err.Error())
|
||||
@@ -137,7 +137,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
switch action {
|
||||
case model.OAUTH_ACTION_SIGNUP:
|
||||
if teamId := relayProps["team_id"]; teamId != "" {
|
||||
if err = c.App.AddUserToTeamByTeamId(teamId, user); err != nil {
|
||||
if err = c.App.AddUserToTeamByTeamId(c.AppContext, teamId, user); err != nil {
|
||||
c.LogErrorByCode(err)
|
||||
break
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("obtained_user_id", user.Id)
|
||||
c.LogAuditWithUserId(user.Id, "obtained user")
|
||||
|
||||
err = c.App.DoLogin(w, r, user, "", isMobile, false, true)
|
||||
err = c.App.DoLogin(c.AppContext, w, r, user, "", isMobile, false, true)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
handleError(err)
|
||||
@@ -172,14 +172,14 @@ func completeSaml(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.Success()
|
||||
c.LogAuditWithUserId(user.Id, "success")
|
||||
|
||||
c.App.AttachSessionCookies(w, r)
|
||||
c.App.AttachSessionCookies(c.AppContext, w, r)
|
||||
|
||||
if hasRedirectURL {
|
||||
if isMobile {
|
||||
// Mobile clients with redirect url support
|
||||
redirectURL = utils.AppendQueryParamsToURL(redirectURL, map[string]string{
|
||||
model.SESSION_COOKIE_TOKEN: c.App.Session().Token,
|
||||
model.SESSION_COOKIE_CSRF: c.App.Session().GetCSRF(),
|
||||
model.SESSION_COOKIE_TOKEN: c.AppContext.Session().Token,
|
||||
model.SESSION_COOKIE_CSRF: c.AppContext.Session().GetCSRF(),
|
||||
})
|
||||
utils.RenderMobileAuthComplete(w, redirectURL)
|
||||
} else {
|
||||
|
||||
@@ -21,20 +21,20 @@ import (
|
||||
var robotsTxt = []byte("User-agent: *\nDisallow: /\n")
|
||||
|
||||
func (w *Web) InitStatic() {
|
||||
if *w.ConfigService.Config().ServiceSettings.WebserverMode != "disabled" {
|
||||
if err := utils.UpdateAssetsSubpathFromConfig(w.ConfigService.Config()); err != nil {
|
||||
if *w.app.Config().ServiceSettings.WebserverMode != "disabled" {
|
||||
if err := utils.UpdateAssetsSubpathFromConfig(w.app.Config()); err != nil {
|
||||
mlog.Error("Failed to update assets subpath from config", mlog.Err(err))
|
||||
}
|
||||
|
||||
staticDir, _ := fileutils.FindDir(model.CLIENT_DIR)
|
||||
mlog.Debug("Using client directory", mlog.String("clientDir", staticDir))
|
||||
|
||||
subpath, _ := utils.GetSubpathFromConfig(w.ConfigService.Config())
|
||||
subpath, _ := utils.GetSubpathFromConfig(w.app.Config())
|
||||
|
||||
staticHandler := staticFilesHandler(http.StripPrefix(path.Join(subpath, "static"), http.FileServer(http.Dir(staticDir))))
|
||||
pluginHandler := staticFilesHandler(http.StripPrefix(path.Join(subpath, "static", "plugins"), http.FileServer(http.Dir(*w.ConfigService.Config().PluginSettings.ClientDirectory))))
|
||||
pluginHandler := staticFilesHandler(http.StripPrefix(path.Join(subpath, "static", "plugins"), http.FileServer(http.Dir(*w.app.Config().PluginSettings.ClientDirectory))))
|
||||
|
||||
if *w.ConfigService.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
if *w.app.Config().ServiceSettings.WebserverMode == "gzip" {
|
||||
staticHandler = gziphandler.GzipHandler(staticHandler)
|
||||
pluginHandler = gziphandler.GzipHandler(pluginHandler)
|
||||
}
|
||||
@@ -58,7 +58,10 @@ func (w *Web) InitStatic() {
|
||||
func root(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if !CheckClientCompatibility(r.UserAgent()) {
|
||||
renderUnsupportedBrowser(c.App, w, r)
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
data := renderUnsupportedBrowser(c.AppContext, r)
|
||||
|
||||
c.App.Srv().TemplatesContainer().Render(w, "unsupported_browser", data)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/templates"
|
||||
)
|
||||
|
||||
@@ -44,13 +44,12 @@ type SystemBrowser struct {
|
||||
MakeDefaultString string
|
||||
}
|
||||
|
||||
func renderUnsupportedBrowser(app app.AppIface, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
func renderUnsupportedBrowser(ctx *request.Context, r *http.Request) templates.Data {
|
||||
|
||||
data := templates.Data{
|
||||
Props: map[string]interface{}{
|
||||
"DownloadAppOrUpgradeBrowserString": app.T("web.error.unsupported_browser.download_app_or_upgrade_browser"),
|
||||
"LearnMoreString": app.T("web.error.unsupported_browser.learn_more"),
|
||||
"DownloadAppOrUpgradeBrowserString": ctx.T("web.error.unsupported_browser.download_app_or_upgrade_browser"),
|
||||
"LearnMoreString": ctx.T("web.error.unsupported_browser.learn_more"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -63,98 +62,99 @@ func renderUnsupportedBrowser(app app.AppIface, w http.ResponseWriter, r *http.R
|
||||
|
||||
// Basic heading translations
|
||||
if isSafari {
|
||||
data.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support_version")
|
||||
data.Props["NoLongerSupportString"] = ctx.T("web.error.unsupported_browser.no_longer_support_version")
|
||||
} else {
|
||||
data.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support")
|
||||
data.Props["NoLongerSupportString"] = ctx.T("web.error.unsupported_browser.no_longer_support")
|
||||
}
|
||||
|
||||
// Mattermost app version
|
||||
if isWindows {
|
||||
data.Props["App"] = renderMattermostAppWindows(app)
|
||||
data.Props["App"] = renderMattermostAppWindows(ctx)
|
||||
} else if isMacOSX {
|
||||
data.Props["App"] = renderMattermostAppMac(app)
|
||||
data.Props["App"] = renderMattermostAppMac(ctx)
|
||||
}
|
||||
|
||||
// Browsers to download
|
||||
// Show a link to Safari if you're using safari and it's outdated
|
||||
// Can't show on Mac all the time because there's no way to open it via URI
|
||||
browsers := []Browser{renderBrowserChrome(app), renderBrowserFirefox(app)}
|
||||
browsers := []Browser{renderBrowserChrome(ctx), renderBrowserFirefox(ctx)}
|
||||
if isSafari {
|
||||
browsers = append(browsers, renderBrowserSafari(app))
|
||||
browsers = append(browsers, renderBrowserSafari(ctx))
|
||||
}
|
||||
data.Props["Browsers"] = browsers
|
||||
|
||||
// If on Windows 10, show link to Edge
|
||||
if isWindows10 {
|
||||
data.Props["SystemBrowser"] = renderSystemBrowserEdge(app, r)
|
||||
data.Props["SystemBrowser"] = renderSystemBrowserEdge(ctx, r)
|
||||
}
|
||||
|
||||
app.Srv().TemplatesContainer().Render(w, "unsupported_browser", data)
|
||||
return data
|
||||
|
||||
}
|
||||
|
||||
func renderMattermostAppMac(app app.AppIface) MattermostApp {
|
||||
func renderMattermostAppMac(ctx *request.Context) MattermostApp {
|
||||
return MattermostApp{
|
||||
"/static/images/browser-icons/mac.png",
|
||||
app.T("web.error.unsupported_browser.download_the_app"),
|
||||
app.T("web.error.unsupported_browser.min_os_version.mac"),
|
||||
app.T("web.error.unsupported_browser.download"),
|
||||
ctx.T("web.error.unsupported_browser.download_the_app"),
|
||||
ctx.T("web.error.unsupported_browser.min_os_version.mac"),
|
||||
ctx.T("web.error.unsupported_browser.download"),
|
||||
"https://mattermost.com/download/#mattermostApps",
|
||||
app.T("web.error.unsupported_browser.install_guide.mac"),
|
||||
ctx.T("web.error.unsupported_browser.install_guide.mac"),
|
||||
"https://docs.mattermost.com/install/desktop.html#mac-os-x-10-9",
|
||||
}
|
||||
}
|
||||
|
||||
func renderMattermostAppWindows(app app.AppIface) MattermostApp {
|
||||
func renderMattermostAppWindows(ctx *request.Context) MattermostApp {
|
||||
return MattermostApp{
|
||||
"/static/images/browser-icons/windows.svg",
|
||||
app.T("web.error.unsupported_browser.download_the_app"),
|
||||
app.T("web.error.unsupported_browser.min_os_version.windows"),
|
||||
app.T("web.error.unsupported_browser.download"),
|
||||
ctx.T("web.error.unsupported_browser.download_the_app"),
|
||||
ctx.T("web.error.unsupported_browser.min_os_version.windows"),
|
||||
ctx.T("web.error.unsupported_browser.download"),
|
||||
"https://mattermost.com/download/#mattermostApps",
|
||||
app.T("web.error.unsupported_browser.install_guide.windows"),
|
||||
ctx.T("web.error.unsupported_browser.install_guide.windows"),
|
||||
"https://docs.mattermost.com/install/desktop.html#windows-10-windows-8-1-windows-7",
|
||||
}
|
||||
}
|
||||
|
||||
func renderBrowserChrome(app app.AppIface) Browser {
|
||||
func renderBrowserChrome(ctx *request.Context) Browser {
|
||||
return Browser{
|
||||
"/static/images/browser-icons/chrome.svg",
|
||||
app.T("web.error.unsupported_browser.browser_title.chrome"),
|
||||
app.T("web.error.unsupported_browser.min_browser_version.chrome"),
|
||||
ctx.T("web.error.unsupported_browser.browser_title.chrome"),
|
||||
ctx.T("web.error.unsupported_browser.min_browser_version.chrome"),
|
||||
"http://www.google.com/chrome",
|
||||
app.T("web.error.unsupported_browser.browser_get_latest.chrome"),
|
||||
ctx.T("web.error.unsupported_browser.browser_get_latest.chrome"),
|
||||
}
|
||||
}
|
||||
|
||||
func renderBrowserFirefox(app app.AppIface) Browser {
|
||||
func renderBrowserFirefox(ctx *request.Context) Browser {
|
||||
return Browser{
|
||||
"/static/images/browser-icons/firefox.svg",
|
||||
app.T("web.error.unsupported_browser.browser_title.firefox"),
|
||||
app.T("web.error.unsupported_browser.min_browser_version.firefox"),
|
||||
ctx.T("web.error.unsupported_browser.browser_title.firefox"),
|
||||
ctx.T("web.error.unsupported_browser.min_browser_version.firefox"),
|
||||
"https://www.mozilla.org/firefox/new/",
|
||||
app.T("web.error.unsupported_browser.browser_get_latest.firefox"),
|
||||
ctx.T("web.error.unsupported_browser.browser_get_latest.firefox"),
|
||||
}
|
||||
}
|
||||
|
||||
func renderBrowserSafari(app app.AppIface) Browser {
|
||||
func renderBrowserSafari(ctx *request.Context) Browser {
|
||||
return Browser{
|
||||
"/static/images/browser-icons/safari.svg",
|
||||
app.T("web.error.unsupported_browser.browser_title.safari"),
|
||||
app.T("web.error.unsupported_browser.min_browser_version.safari"),
|
||||
ctx.T("web.error.unsupported_browser.browser_title.safari"),
|
||||
ctx.T("web.error.unsupported_browser.min_browser_version.safari"),
|
||||
"macappstore://showUpdatesPage",
|
||||
app.T("web.error.unsupported_browser.browser_get_latest.safari"),
|
||||
ctx.T("web.error.unsupported_browser.browser_get_latest.safari"),
|
||||
}
|
||||
}
|
||||
|
||||
func renderSystemBrowserEdge(app app.AppIface, r *http.Request) SystemBrowser {
|
||||
func renderSystemBrowserEdge(ctx *request.Context, r *http.Request) SystemBrowser {
|
||||
return SystemBrowser{
|
||||
"/static/images/browser-icons/edge.svg",
|
||||
app.T("web.error.unsupported_browser.browser_title.edge"),
|
||||
app.T("web.error.unsupported_browser.min_browser_version.edge"),
|
||||
app.T("web.error.unsupported_browser.open_system_browser.edge"),
|
||||
ctx.T("web.error.unsupported_browser.browser_title.edge"),
|
||||
ctx.T("web.error.unsupported_browser.min_browser_version.edge"),
|
||||
ctx.T("web.error.unsupported_browser.open_system_browser.edge"),
|
||||
"microsoft-edge:http://" + r.Host + r.RequestURI, //TODO: Can we get HTTP or HTTPS? If someone's server doesn't have a redirect this won't work
|
||||
"ms-settings:defaultapps",
|
||||
app.T("web.error.unsupported_browser.system_browser_or"),
|
||||
app.T("web.error.unsupported_browser.system_browser_make_default"),
|
||||
ctx.T("web.error.unsupported_browser.system_browser_or"),
|
||||
ctx.T("web.error.unsupported_browser.system_browser_make_default"),
|
||||
}
|
||||
}
|
||||
|
||||
31
web/web.go
31
web/web.go
@@ -13,24 +13,21 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/services/configservice"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
GetGlobalAppOptions app.AppOptionCreator
|
||||
ConfigService configservice.ConfigService
|
||||
MainRouter *mux.Router
|
||||
app app.AppIface
|
||||
MainRouter *mux.Router
|
||||
}
|
||||
|
||||
func New(config configservice.ConfigService, globalOptions app.AppOptionCreator, root *mux.Router) *Web {
|
||||
func New(a app.AppIface, root *mux.Router) *Web {
|
||||
mlog.Debug("Initializing web routes")
|
||||
|
||||
web := &Web{
|
||||
GetGlobalAppOptions: globalOptions,
|
||||
ConfigService: config,
|
||||
MainRouter: root,
|
||||
app: a,
|
||||
MainRouter: root,
|
||||
}
|
||||
|
||||
web.InitOAuth()
|
||||
@@ -60,24 +57,24 @@ func CheckClientCompatibility(agentString string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Handle404(config configservice.ConfigService, w http.ResponseWriter, r *http.Request) {
|
||||
func Handle404(a app.AppIface, w http.ResponseWriter, r *http.Request) {
|
||||
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
|
||||
ipAddress := utils.GetIPAddress(r, config.Config().ServiceSettings.TrustedProxyIPHeader)
|
||||
ipAddress := utils.GetIPAddress(r, a.Config().ServiceSettings.TrustedProxyIPHeader)
|
||||
mlog.Debug("not found handler triggered", mlog.String("path", r.URL.Path), mlog.Int("code", 404), mlog.String("ip", ipAddress))
|
||||
|
||||
if IsApiCall(config, r) {
|
||||
if IsApiCall(a, r) {
|
||||
w.WriteHeader(err.StatusCode)
|
||||
err.DetailedError = "There doesn't appear to be an api call for the url='" + r.URL.Path + "'. Typo? are you missing a team_id or user_id as part of the url?"
|
||||
w.Write([]byte(err.ToJson()))
|
||||
} else if *config.Config().ServiceSettings.WebserverMode == "disabled" {
|
||||
} else if *a.Config().ServiceSettings.WebserverMode == "disabled" {
|
||||
http.NotFound(w, r)
|
||||
} else {
|
||||
utils.RenderWebAppError(config.Config(), w, r, err, config.AsymmetricSigningKey())
|
||||
utils.RenderWebAppError(a.Config(), w, r, err, a.AsymmetricSigningKey())
|
||||
}
|
||||
}
|
||||
|
||||
func IsApiCall(config configservice.ConfigService, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(config.Config())
|
||||
func IsApiCall(a app.AppIface, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
return strings.HasPrefix(r.URL.Path, path.Join(subpath, "api")+"/")
|
||||
}
|
||||
@@ -88,8 +85,8 @@ func IsWebhookCall(a app.AppIface, r *http.Request) bool {
|
||||
return strings.HasPrefix(r.URL.Path, path.Join(subpath, "hooks")+"/")
|
||||
}
|
||||
|
||||
func IsOAuthApiCall(config configservice.ConfigService, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(config.Config())
|
||||
func IsOAuthApiCall(a app.AppIface, r *http.Request) bool {
|
||||
subpath, _ := utils.GetSubpathFromConfig(a.Config())
|
||||
|
||||
if r.Method == "POST" && r.URL.Path == path.Join(subpath, "oauth", "authorize") {
|
||||
return true
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/app/request"
|
||||
"github.com/mattermost/mattermost-server/v5/config"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/plugin"
|
||||
@@ -30,9 +31,10 @@ var ApiClient *model.Client4
|
||||
var URL string
|
||||
|
||||
type TestHelper struct {
|
||||
App app.AppIface
|
||||
Server *app.Server
|
||||
Web *Web
|
||||
App app.AppIface
|
||||
Context *request.Context
|
||||
Server *app.Server
|
||||
Web *Web
|
||||
|
||||
BasicUser *model.User
|
||||
BasicChannel *model.Channel
|
||||
@@ -108,10 +110,10 @@ func setupTestHelper(includeCacheLayer bool) *TestHelper {
|
||||
*cfg.PasswordSettings.Number = false
|
||||
})
|
||||
|
||||
ctx := &request.Context{}
|
||||
a := app.New(app.ServerConnector(s))
|
||||
a.InitServer()
|
||||
|
||||
web := New(s, s.AppOptions, s.Router)
|
||||
web := New(a, s.Router)
|
||||
URL = fmt.Sprintf("http://localhost:%v", s.ListenAddr.Port)
|
||||
ApiClient = model.NewAPIv4Client(URL)
|
||||
|
||||
@@ -123,6 +125,7 @@ func setupTestHelper(includeCacheLayer bool) *TestHelper {
|
||||
|
||||
th := &TestHelper{
|
||||
App: a,
|
||||
Context: ctx,
|
||||
Server: s,
|
||||
Web: web,
|
||||
IncludeCacheLayer: includeCacheLayer,
|
||||
@@ -135,21 +138,25 @@ func (th *TestHelper) InitPlugins() *TestHelper {
|
||||
pluginDir := filepath.Join(th.tempWorkspace, "plugins")
|
||||
webappDir := filepath.Join(th.tempWorkspace, "webapp")
|
||||
|
||||
th.App.InitPlugins(pluginDir, webappDir)
|
||||
th.App.InitPlugins(th.Context, pluginDir, webappDir)
|
||||
|
||||
return th
|
||||
}
|
||||
|
||||
func (th *TestHelper) NewPluginAPI(manifest *model.Manifest) plugin.API {
|
||||
return th.App.NewPluginAPI(th.Context, manifest)
|
||||
}
|
||||
|
||||
func (th *TestHelper) InitBasic() *TestHelper {
|
||||
th.SystemAdminUser, _ = th.App.CreateUser(&model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_ADMIN_ROLE_ID})
|
||||
th.SystemAdminUser, _ = th.App.CreateUser(th.Context, &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_ADMIN_ROLE_ID})
|
||||
|
||||
user, _ := th.App.CreateUser(&model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_USER_ROLE_ID})
|
||||
user, _ := th.App.CreateUser(th.Context, &model.User{Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", EmailVerified: true, Roles: model.SYSTEM_USER_ROLE_ID})
|
||||
|
||||
team, _ := th.App.CreateTeam(&model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: user.Email, Type: model.TEAM_OPEN})
|
||||
team, _ := th.App.CreateTeam(th.Context, &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: user.Email, Type: model.TEAM_OPEN})
|
||||
|
||||
th.App.JoinUserToTeam(team, user, "")
|
||||
th.App.JoinUserToTeam(th.Context, team, user, "")
|
||||
|
||||
channel, _ := th.App.CreateChannel(&model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id, CreatorId: user.Id}, true)
|
||||
channel, _ := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "Test API Name", Name: "zz" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id, CreatorId: user.Id}, true)
|
||||
|
||||
th.BasicUser = user
|
||||
th.BasicChannel = channel
|
||||
@@ -263,7 +270,7 @@ func TestPublicFilesRequest(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginID := "com.mattermost.sample"
|
||||
|
||||
@@ -49,7 +49,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if *c.App.Config().LogSettings.EnableWebhookDebugging {
|
||||
if c.Err != nil {
|
||||
mlog.Debug("Incoming webhook received", mlog.String("webhook_id", id), mlog.String("request_id", c.App.RequestId()), mlog.String("payload", incomingWebhookPayload.ToJson()))
|
||||
mlog.Debug("Incoming webhook received", mlog.String("webhook_id", id), mlog.String("request_id", c.AppContext.RequestId()), mlog.String("payload", incomingWebhookPayload.ToJson()))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -85,7 +85,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
err = c.App.HandleIncomingWebhook(id, incomingWebhookPayload)
|
||||
err = c.App.HandleIncomingWebhook(c.AppContext, id, incomingWebhookPayload)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -105,7 +105,7 @@ func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
appErr := c.App.HandleCommandWebhook(id, response)
|
||||
appErr := c.App.HandleCommandWebhook(c.AppContext, id, response)
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ChannelLockedWebhook", func(t *testing.T) {
|
||||
channel, err := th.App.CreateChannel(&model.Channel{TeamId: th.BasicTeam.Id, Name: model.NewId(), DisplayName: model.NewId(), Type: model.CHANNEL_OPEN, CreatorId: th.BasicUser.Id}, true)
|
||||
channel, err := th.App.CreateChannel(th.Context, &model.Channel{TeamId: th.BasicTeam.Id, Name: model.NewId(), DisplayName: model.NewId(), Type: model.CHANNEL_OPEN, CreatorId: th.BasicUser.Id}, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
hook, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, th.BasicChannel, &model.IncomingWebhook{ChannelId: th.BasicChannel.Id, ChannelLocked: true})
|
||||
|
||||
Ссылка в новой задаче
Block a user