diff options
Diffstat (limited to 'api/context.go')
-rw-r--r-- | api/context.go | 156 |
1 files changed, 89 insertions, 67 deletions
diff --git a/api/context.go b/api/context.go index aaf304e2c..d90fbd9ee 100644 --- a/api/context.go +++ b/api/context.go @@ -4,6 +4,7 @@ package api import ( + "fmt" "net" "net/http" "net/url" @@ -29,12 +30,9 @@ type Context struct { } type Page struct { - TemplateName string - Title string - SiteName string - FeedbackEmail string - SiteURL string - Props map[string]string + TemplateName string + Props map[string]string + ClientProps map[string]string } func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { @@ -82,58 +80,57 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c.RequestId = model.NewId() c.IpAddress = GetIpAddress(r) - protocol := "http" + token := "" + isTokenFromQueryString := false - // if the request came from the ELB then assume this is produciton - // and redirect all http requests to https - if utils.Cfg.ServiceSettings.UseSSL { - forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO) - if forwardProto == "http" { - l4g.Info("redirecting http request to https for %v", r.URL.Path) - http.Redirect(w, r, "https://"+r.Host, http.StatusTemporaryRedirect) - return - } else { - protocol = "https" + // Attempt to parse token out of the header + authHeader := r.Header.Get(model.HEADER_AUTH) + if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER { + // Default session token + token = authHeader[7:] + + } else if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN { + // OAuth token + token = authHeader[6:] + } + + // Attempt to parse the token from the cookie + if len(token) == 0 { + if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil { + token = cookie.Value } } + // Attempt to parse token out of the query string + if len(token) == 0 { + token = r.URL.Query().Get("access_token") + isTokenFromQueryString = true + } + + protocol := GetProtocol(r) c.setSiteURL(protocol + "://" + r.Host) w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) - w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version) + w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v", model.CurrentVersion, utils.CfgLastModified)) // Instruct the browser not to display us in an iframe for anti-clickjacking if !h.isApi { w.Header().Set("X-Frame-Options", "DENY") w.Header().Set("Content-Security-Policy", "frame-ancestors none") + } else { + // All api response bodies will be JSON formatted by default + w.Header().Set("Content-Type", "application/json") } - sessionId := "" - - // attempt to parse the session token from the header - if ah := r.Header.Get(model.HEADER_AUTH); ah != "" { - if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" { - sessionId = ah[7:] - } - } - - // attempt to parse the session token from the cookie - if sessionId == "" { - if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil { - sessionId = cookie.Value - } - } - - if sessionId != "" { - + if len(token) != 0 { var session *model.Session - if ts, ok := sessionCache.Get(sessionId); ok { + if ts, ok := sessionCache.Get(token); ok { session = ts.(*model.Session) } if session == nil { - if sessionResult := <-Srv.Store.Session().Get(sessionId); sessionResult.Err != nil { - c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "id="+sessionId+", err="+sessionResult.Err.DetailedError)) + if sessionResult := <-Srv.Store.Session().Get(token); sessionResult.Err != nil { + c.LogError(model.NewAppError("ServeHTTP", "Invalid session", "token="+token+", err="+sessionResult.Err.DetailedError)) } else { session = sessionResult.Data.(*model.Session) } @@ -141,7 +138,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if session == nil || session.IsExpired() { c.RemoveSessionCookie(w) - c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "id="+sessionId) + c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token) + c.Err.StatusCode = http.StatusUnauthorized + } else if !session.IsOAuth && isTokenFromQueryString { + c.Err = model.NewAppError("ServeHTTP", "Session is not OAuth but token was provided in the query string", "token="+token) c.Err.StatusCode = http.StatusUnauthorized } else { c.Session = *session @@ -165,10 +165,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c.SystemAdminRequired() } - if c.Err == nil && h.isUserActivity && sessionId != "" && len(c.Session.UserId) > 0 { + if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 { go func() { - if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, sessionId, model.GetMillis())).Err; err != nil { - l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, sessionId, err) + if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, c.Session.Id, model.GetMillis())).Err; err != nil { + l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, c.Session.Id, err) } }() } @@ -195,8 +195,16 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } +func GetProtocol(r *http.Request) string { + if r.Header.Get(model.HEADER_FORWARDED_PROTO) == "https" { + return "https" + } else { + return "http" + } +} + func (c *Context) LogAudit(extraInfo string) { - audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId} + audit := &model.Audit{UserId: c.Session.UserId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} if r := <-Srv.Store.Audit().Save(audit); r.Err != nil { c.LogError(r.Err) } @@ -208,7 +216,7 @@ func (c *Context) LogAuditWithUserId(userId, extraInfo string) { extraInfo = strings.TrimSpace(extraInfo + " session_user=" + c.Session.UserId) } - audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.AltId} + audit := &model.Audit{UserId: userId, IpAddress: c.IpAddress, Action: c.Path, ExtraInfo: extraInfo, SessionId: c.Session.Id} if r := <-Srv.Store.Audit().Save(audit); r.Err != nil { c.LogError(r.Err) } @@ -285,9 +293,20 @@ func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) b } func (c *Context) IsSystemAdmin() bool { - if strings.Contains(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) && IsPrivateIpAddress(c.IpAddress) { + // TODO XXX FIXME && IsPrivateIpAddress(c.IpAddress) + if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) { + return true + } + return false +} + +func (c *Context) HasSystemAdminPermissions(where string) bool { + if c.IsSystemAdmin() { return true } + + c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+c.Session.UserId) + c.Err.StatusCode = http.StatusForbidden return false } @@ -297,13 +316,13 @@ func (c *Context) IsTeamAdmin(userId string) bool { return false } else { user := uresult.Data.(*model.User) - return strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId + return model.IsInRole(c.Session.Roles, model.ROLE_TEAM_ADMIN) && user.TeamId == c.Session.TeamId } } func (c *Context) RemoveSessionCookie(w http.ResponseWriter) { - sessionCache.Remove(c.Session.Id) + sessionCache.Remove(c.Session.Token) cookie := &http.Cookie{ Name: model.SESSION_TOKEN, @@ -360,6 +379,11 @@ func (c *Context) GetSiteURL() string { func GetIpAddress(r *http.Request) string { address := r.Header.Get(model.HEADER_FORWARDED) + + if len(address) == 0 { + address = r.Header.Get(model.HEADER_REAL_IP) + } + if len(address) == 0 { address, _, _ = net.SplitHostPort(r.RemoteAddr) } @@ -414,10 +438,10 @@ func IsBetaDomain(r *http.Request) bool { } var privateIpAddress = []*net.IPNet{ - &net.IPNet{IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)}, - &net.IPNet{IP: net.IPv4(176, 16, 0, 1), Mask: net.IPv4Mask(255, 255, 0, 0)}, - &net.IPNet{IP: net.IPv4(192, 168, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}, - &net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 252)}, + {IP: net.IPv4(10, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)}, + {IP: net.IPv4(176, 16, 0, 1), Mask: net.IPv4Mask(255, 255, 0, 0)}, + {IP: net.IPv4(192, 168, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 0)}, + {IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 255, 255, 252)}, } func IsPrivateIpAddress(ipAddress string) bool { @@ -432,25 +456,19 @@ func IsPrivateIpAddress(ipAddress string) bool { } func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) { + props := make(map[string]string) + props["Message"] = err.Message + props["Details"] = err.DetailedError - protocol := "http" - if utils.Cfg.ServiceSettings.UseSSL { - forwardProto := r.Header.Get(model.HEADER_FORWARDED_PROTO) - if forwardProto != "http" { - protocol = "https" - } + pathParts := strings.Split(r.URL.Path, "/") + if len(pathParts) > 1 { + props["SiteURL"] = GetProtocol(r) + "://" + r.Host + "/" + pathParts[1] + } else { + props["SiteURL"] = GetProtocol(r) + "://" + r.Host } - SiteURL := protocol + "://" + r.Host - - m := make(map[string]string) - m["Message"] = err.Message - m["Details"] = err.DetailedError - m["SiteName"] = utils.Cfg.ServiceSettings.SiteName - m["SiteURL"] = SiteURL - w.WriteHeader(err.StatusCode) - ServerTemplates.ExecuteTemplate(w, "error.html", m) + ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientProps: utils.ClientProperties}) } func Handle404(w http.ResponseWriter, r *http.Request) { @@ -459,3 +477,7 @@ func Handle404(w http.ResponseWriter, r *http.Request) { l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r)) RenderWebError(err, w, r) } + +func AddSessionToCache(session *model.Session) { + sessionCache.Add(session.Token, session) +} |