github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+incompatible/web/handlers.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package web
     5  
     6  import (
     7  	"fmt"
     8  	"net/http"
     9  	"time"
    10  
    11  	"github.com/mattermost/mattermost-server/app"
    12  	"github.com/mattermost/mattermost-server/mlog"
    13  	"github.com/mattermost/mattermost-server/model"
    14  	"github.com/mattermost/mattermost-server/utils"
    15  )
    16  
    17  func (w *Web) NewHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
    18  	return &Handler{
    19  		App:            w.App,
    20  		HandleFunc:     h,
    21  		RequireSession: false,
    22  		TrustRequester: false,
    23  		RequireMfa:     false,
    24  		IsStatic:       false,
    25  	}
    26  }
    27  
    28  func (w *Web) NewStaticHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
    29  	return &Handler{
    30  		App:            w.App,
    31  		HandleFunc:     h,
    32  		RequireSession: false,
    33  		TrustRequester: false,
    34  		RequireMfa:     false,
    35  		IsStatic:       true,
    36  	}
    37  }
    38  
    39  type Handler struct {
    40  	App            *app.App
    41  	HandleFunc     func(*Context, http.ResponseWriter, *http.Request)
    42  	RequireSession bool
    43  	TrustRequester bool
    44  	RequireMfa     bool
    45  	IsStatic       bool
    46  }
    47  
    48  func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    49  	now := time.Now()
    50  	mlog.Debug(fmt.Sprintf("%v - %v", r.Method, r.URL.Path))
    51  
    52  	c := &Context{}
    53  	c.App = h.App
    54  	c.T, _ = utils.GetTranslationsAndLocale(w, r)
    55  	c.RequestId = model.NewId()
    56  	c.IpAddress = utils.GetIpAddress(r)
    57  	c.Params = ParamsFromRequest(r)
    58  	c.Path = r.URL.Path
    59  	c.Log = c.App.Log
    60  
    61  	token, tokenLocation := app.ParseAuthTokenFromRequest(r)
    62  
    63  	// CSRF Check
    64  	if tokenLocation == app.TokenLocationCookie && h.RequireSession && !h.TrustRequester {
    65  		if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML {
    66  			c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized)
    67  			token = ""
    68  		}
    69  	}
    70  
    71  	c.SetSiteURLHeader(app.GetProtocol(r) + "://" + r.Host)
    72  
    73  	w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
    74  	w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.BuildNumber, c.App.ClientConfigHash(), c.App.License() != nil))
    75  
    76  	if h.IsStatic {
    77  		// Instruct the browser not to display us in an iframe unless is the same origin for anti-clickjacking
    78  		w.Header().Set("X-Frame-Options", "SAMEORIGIN")
    79  		w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'")
    80  	} else {
    81  		// All api response bodies will be JSON formatted by default
    82  		w.Header().Set("Content-Type", "application/json")
    83  
    84  		if r.Method == "GET" {
    85  			w.Header().Set("Expires", "0")
    86  		}
    87  	}
    88  
    89  	if len(token) != 0 {
    90  		session, err := c.App.GetSession(token)
    91  
    92  		if err != nil {
    93  			c.Log.Info("Invalid session", mlog.Err(err))
    94  			if err.StatusCode == http.StatusInternalServerError {
    95  				c.Err = err
    96  			} else if h.RequireSession {
    97  				c.RemoveSessionCookie(w, r)
    98  				c.Err = model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized)
    99  			}
   100  		} else if !session.IsOAuth && tokenLocation == app.TokenLocationQueryString {
   101  			c.Err = model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized)
   102  		} else {
   103  			c.Session = *session
   104  		}
   105  
   106  		// Rate limit by UserID
   107  		if c.App.Srv.RateLimiter != nil && c.App.Srv.RateLimiter.UserIdRateLimit(c.Session.UserId, w) {
   108  			return
   109  		}
   110  	}
   111  
   112  	c.Log = c.App.Log.With(
   113  		mlog.String("path", c.Path),
   114  		mlog.String("request_id", c.RequestId),
   115  		mlog.String("ip_addr", c.IpAddress),
   116  		mlog.String("user_id", c.Session.UserId),
   117  		mlog.String("method", r.Method),
   118  	)
   119  
   120  	if c.Err == nil && h.RequireSession {
   121  		c.SessionRequired()
   122  	}
   123  
   124  	if c.Err == nil && h.RequireMfa {
   125  		c.MfaRequired()
   126  	}
   127  
   128  	if c.Err == nil {
   129  		h.HandleFunc(c, w, r)
   130  	}
   131  
   132  	// Handle errors that have occurred
   133  	if c.Err != nil {
   134  		c.Err.Translate(c.T)
   135  		c.Err.RequestId = c.RequestId
   136  
   137  		if c.Err.Id == "api.context.session_expired.app_error" {
   138  			c.LogInfo(c.Err)
   139  		} else {
   140  			c.LogError(c.Err)
   141  		}
   142  
   143  		c.Err.Where = r.URL.Path
   144  
   145  		// Block out detailed error when not in developer mode
   146  		if !*c.App.Config().ServiceSettings.EnableDeveloper {
   147  			c.Err.DetailedError = ""
   148  		}
   149  
   150  		// Sanitize all 5xx error messages in hardened mode
   151  		if *c.App.Config().ServiceSettings.ExperimentalEnableHardenedMode && c.Err.StatusCode >= 500 {
   152  			c.Err.Id = ""
   153  			c.Err.Message = "Internal Server Error"
   154  			c.Err.DetailedError = ""
   155  			c.Err.StatusCode = 500
   156  			c.Err.Where = ""
   157  			c.Err.IsOAuth = false
   158  		}
   159  
   160  		w.WriteHeader(c.Err.StatusCode)
   161  		w.Write([]byte(c.Err.ToJson()))
   162  
   163  		if c.App.Metrics != nil {
   164  			c.App.Metrics.IncrementHttpError()
   165  		}
   166  	}
   167  
   168  	if c.App.Metrics != nil {
   169  		c.App.Metrics.IncrementHttpRequest()
   170  
   171  		if r.URL.Path != model.API_URL_SUFFIX+"/websocket" {
   172  			elapsed := float64(time.Since(now)) / float64(time.Second)
   173  			c.App.Metrics.ObserveHttpRequestDuration(elapsed)
   174  		}
   175  	}
   176  }