summaryrefslogtreecommitdiffstats
path: root/api/context.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/context.go')
-rw-r--r--api/context.go156
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)
+}