Commit fa3a0df2 authored by =Corey Hulen's avatar =Corey Hulen

Adding multi-session cookie

parent 36658c13
...@@ -30,12 +30,12 @@ type Context struct { ...@@ -30,12 +30,12 @@ type Context struct {
} }
type Page struct { type Page struct {
TemplateName string TemplateName string
Props map[string]string Props map[string]string
ClientCfg map[string]string ClientCfg map[string]string
User *model.User User *model.User
Team *model.Team Team *model.Team
Session *model.Session SessionTokenHash string
} }
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
...@@ -99,8 +99,29 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -99,8 +99,29 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Attempt to parse the token from the cookie // Attempt to parse the token from the cookie
if len(token) == 0 { if len(token) == 0 {
if cookie, err := r.Cookie(model.SESSION_TOKEN); err == nil { if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
token = cookie.Value multiToken := cookie.Value
fmt.Println(">>>>>>>> multiToken: " + multiToken)
if len(multiToken) > 0 {
tokens := strings.Split(multiToken, " ")
// If there is only 1 token in the cookie then just use it like normal
if len(tokens) == 1 {
token = multiToken
} else {
// If it is a multi-session token then find the correct session
sessionTokenHash := r.Header.Get(model.HEADER_MM_SESSION_TOKEN_HASH)
fmt.Println(">>>>>>>> sessionHash: " + sessionTokenHash + " url=" + r.URL.Path)
for _, t := range tokens {
if sessionTokenHash == model.HashPassword(t) {
token = token
break
}
}
}
}
} }
} }
...@@ -179,6 +200,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ...@@ -179,6 +200,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(c.Err.ToJson())) w.Write([]byte(c.Err.ToJson()))
} else { } else {
if c.Err.StatusCode == http.StatusUnauthorized { if c.Err.StatusCode == http.StatusUnauthorized {
fmt.Println("!!!!!!!!!!!!!!!! url=" + r.URL.Path)
http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else { } else {
RenderWebError(c.Err, w, r) RenderWebError(c.Err, w, r)
...@@ -310,25 +332,13 @@ func (c *Context) IsTeamAdmin() bool { ...@@ -310,25 +332,13 @@ func (c *Context) IsTeamAdmin() bool {
func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) { func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
sessionCache.Remove(c.Session.Token)
cookie := &http.Cookie{
Name: model.SESSION_TOKEN,
Value: "",
Path: "/",
MaxAge: -1,
HttpOnly: true,
}
http.SetCookie(w, cookie)
multiToken := "" multiToken := ""
if oldMultiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil { if oldMultiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
multiToken = oldMultiCookie.Value multiToken = oldMultiCookie.Value
} }
multiCookie := &http.Cookie{ multiCookie := &http.Cookie{
Name: model.MULTI_SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)), Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
Path: "/", Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS, MaxAge: model.SESSION_TIME_WEB_IN_SECS,
...@@ -500,7 +510,7 @@ func GetSession(token string) *model.Session { ...@@ -500,7 +510,7 @@ func GetSession(token string) *model.Session {
func FindMultiSessionForTeamId(r *http.Request, teamId string) *model.Session { func FindMultiSessionForTeamId(r *http.Request, teamId string) *model.Session {
if multiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil { if multiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
multiToken := multiCookie.Value multiToken := multiCookie.Value
if len(multiToken) > 0 { if len(multiToken) > 0 {
......
...@@ -428,21 +428,14 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, ...@@ -428,21 +428,14 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
} }
w.Header().Set(model.HEADER_TOKEN, session.Token) w.Header().Set(model.HEADER_TOKEN, session.Token)
sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN,
Value: session.Token,
Path: "/",
MaxAge: maxAge,
HttpOnly: true,
}
http.SetCookie(w, sessionCookie)
multiToken := "" multiToken := ""
if originalMultiSessionCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil { if originalMultiSessionCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
multiToken = originalMultiSessionCookie.Value multiToken = originalMultiSessionCookie.Value
} }
fmt.Println("original: " + multiToken)
// Attempt to clean all the old tokens or duplicate tokens // Attempt to clean all the old tokens or duplicate tokens
if len(multiToken) > 0 { if len(multiToken) > 0 {
tokens := strings.Split(multiToken, " ") tokens := strings.Split(multiToken, " ")
...@@ -463,8 +456,10 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User, ...@@ -463,8 +456,10 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
multiToken = strings.TrimSpace(session.Token + " " + multiToken) multiToken = strings.TrimSpace(session.Token + " " + multiToken)
fmt.Println("new: " + multiToken)
multiSessionCookie := &http.Cookie{ multiSessionCookie := &http.Cookie{
Name: model.MULTI_SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: multiToken, Value: multiToken,
Path: "/", Path: "/",
MaxAge: maxAge, MaxAge: maxAge,
......
...@@ -111,7 +111,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -111,7 +111,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) {
// Respond with an auth token this can be overriden by a specific test as required // Respond with an auth token this can be overriden by a specific test as required
sessionCookie := &http.Cookie{ sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN, Name: model.SESSION_COOKIE_TOKEN,
Value: client.AuthToken, Value: client.AuthToken,
Path: "/", Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS, MaxAge: model.SESSION_TIME_WEB_IN_SECS,
......
...@@ -16,17 +16,18 @@ import ( ...@@ -16,17 +16,18 @@ import (
) )
const ( const (
HEADER_REQUEST_ID = "X-Request-ID" HEADER_REQUEST_ID = "X-Request-ID"
HEADER_VERSION_ID = "X-Version-ID" HEADER_VERSION_ID = "X-Version-ID"
HEADER_ETAG_SERVER = "ETag" HEADER_ETAG_SERVER = "ETag"
HEADER_ETAG_CLIENT = "If-None-Match" HEADER_ETAG_CLIENT = "If-None-Match"
HEADER_FORWARDED = "X-Forwarded-For" HEADER_FORWARDED = "X-Forwarded-For"
HEADER_REAL_IP = "X-Real-IP" HEADER_REAL_IP = "X-Real-IP"
HEADER_FORWARDED_PROTO = "X-Forwarded-Proto" HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
HEADER_TOKEN = "token" HEADER_TOKEN = "token"
HEADER_BEARER = "BEARER" HEADER_BEARER = "BEARER"
HEADER_AUTH = "Authorization" HEADER_AUTH = "Authorization"
API_URL_SUFFIX = "/api/v1" HEADER_MM_SESSION_TOKEN_HASH = "X-MM-TokenHash"
API_URL_SUFFIX = "/api/v1"
) )
type Result struct { type Result struct {
...@@ -293,7 +294,7 @@ func (c *Client) login(m map[string]string) (*Result, *AppError) { ...@@ -293,7 +294,7 @@ func (c *Client) login(m map[string]string) (*Result, *AppError) {
} else { } else {
c.AuthToken = r.Header.Get(HEADER_TOKEN) c.AuthToken = r.Header.Get(HEADER_TOKEN)
c.AuthType = HEADER_BEARER c.AuthType = HEADER_BEARER
sessionToken := getCookie(SESSION_TOKEN, r) sessionToken := getCookie(SESSION_COOKIE_TOKEN, r)
if c.AuthToken != sessionToken.Value { if c.AuthToken != sessionToken.Value {
NewAppError("/users/login", "Authentication tokens didn't match", "") NewAppError("/users/login", "Authentication tokens didn't match", "")
......
...@@ -9,8 +9,7 @@ import ( ...@@ -9,8 +9,7 @@ import (
) )
const ( const (
SESSION_TOKEN = "MMSID" SESSION_COOKIE_TOKEN = "MMTOKEN"
MULTI_SESSION_TOKEN = "MMSIDMU"
SESSION_TIME_WEB_IN_DAYS = 30 SESSION_TIME_WEB_IN_DAYS = 30
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
SESSION_TIME_MOBILE_IN_DAYS = 30 SESSION_TIME_MOBILE_IN_DAYS = 30
......
...@@ -48,14 +48,14 @@ function handleError(methodName, xhr, status, err) { ...@@ -48,14 +48,14 @@ function handleError(methodName, xhr, status, err) {
track('api', 'api_weberror', methodName, 'message', msg); track('api', 'api_weberror', methodName, 'message', msg);
if (xhr.status === 401) { // if (xhr.status === 401) {
if (window.location.href.indexOf('/channels') === 0) { // if (window.location.href.indexOf('/channels') === 0) {
window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search); // window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
} else { // } else {
var teamURL = window.location.href.split('/channels')[0]; // var teamURL = window.location.href.split('/channels')[0];
window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search); // window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
} // }
} // }
return e; return e;
} }
......
...@@ -18,13 +18,6 @@ ...@@ -18,13 +18,6 @@
<link rel="manifest" href="/static/config/manifest.json"> <link rel="manifest" href="/static/config/manifest.json">
<!-- Android add to homescreen --> <!-- Android add to homescreen -->
<script>
window.mm_config = {{ .ClientCfg }};
window.mm_team = {{ .Team }};
window.mm_user = {{ .User }};
window.mm_session = {{ .Session }};
</script>
<!-- CSS Should always go first --> <!-- CSS Should always go first -->
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css"> <link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css"> <link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css">
...@@ -46,6 +39,16 @@ ...@@ -46,6 +39,16 @@
<style id="antiClickjack">body{display:none !important;}</style> <style id="antiClickjack">body{display:none !important;}</style>
<script>
window.mm_config = {{ .ClientCfg }};
window.mm_team = {{ .Team }};
window.mm_user = {{ .User }};
window.mm_session_token_hash = {{ .SessionTokenHash }};
$.ajaxSetup({
headers: { 'X-MM-TokenHash': mm_session_token_hash }
});
</script>
<script> <script>
window.onerror = function(msg, url, line, column, stack) { window.onerror = function(msg, url, line, column, stack) {
var l = {}; var l = {};
......
...@@ -15,11 +15,7 @@ import ( ...@@ -15,11 +15,7 @@ import (
"gopkg.in/fsnotify.v1" "gopkg.in/fsnotify.v1"
"html/template" "html/template"
"net/http" "net/http"
<<<<<<< HEAD
"net/url" "net/url"
"regexp"
=======
>>>>>>> master
"strconv" "strconv"
"strings" "strings"
) )
...@@ -48,8 +44,8 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) { ...@@ -48,8 +44,8 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) {
me.User.Sanitize(map[string]bool{}) me.User.Sanitize(map[string]bool{})
} }
if me.Session != nil { if len(c.Session.Token) > 0 {
me.Session.Sanitize() me.SessionTokenHash = model.HashPassword(c.Session.Token)
} }
if err := Templates.ExecuteTemplate(w, me.TemplateName, me); err != nil { if err := Templates.ExecuteTemplate(w, me.TemplateName, me); err != nil {
...@@ -95,9 +91,9 @@ func InitWeb() { ...@@ -95,9 +91,9 @@ func InitWeb() {
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET") mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/channels/{channelname}", api.AppHandler(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here. mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
watchAndParseTemplates() watchAndParseTemplates()
} }
...@@ -205,7 +201,6 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -205,7 +201,6 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
page := NewHtmlTemplatePage("home", "Home") page := NewHtmlTemplatePage("home", "Home")
page.Team = team page.Team = team
page.User = user page.User = user
page.Session = &c.Session
page.Render(c, w) page.Render(c, w)
} }
} }
...@@ -236,26 +231,10 @@ func login(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -236,26 +231,10 @@ func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
team = tResult.Data.(*model.Team) team = tResult.Data.(*model.Team)
} }
// If we are already logged into this team then go to town-square
if len(c.Session.UserId) != 0 && c.Session.TeamId == team.Id {
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect)
return
}
// We still might be able to switch to this team because we've logged in before // We still might be able to switch to this team because we've logged in before
session := api.FindMultiSessionForTeamId(r, team.Id) session := api.FindMultiSessionForTeamId(r, team.Id)
if session != nil { if session != nil {
w.Header().Set(model.HEADER_TOKEN, session.Token) w.Header().Set(model.HEADER_TOKEN, session.Token)
sessionCookie := &http.Cookie{
Name: model.SESSION_TOKEN,
Value: session.Token,
Path: "/",
MaxAge: model.SESSION_TIME_WEB_IN_SECS,
HttpOnly: true,
}
http.SetCookie(w, sessionCookie)
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect) http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect)
return return
} }
...@@ -375,6 +354,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -375,6 +354,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
session := api.FindMultiSessionForTeamId(r, team.Id) session := api.FindMultiSessionForTeamId(r, team.Id)
if session == nil { if session == nil {
// redirect to login // redirect to login
fmt.Println(">>>>>>>>>>forwarding")
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else { } else {
c.Session = *session c.Session = *session
...@@ -449,7 +429,6 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -449,7 +429,6 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Props["UserId"] = c.Session.UserId page.Props["UserId"] = c.Session.UserId
page.Team = team page.Team = team
page.User = user page.User = user
page.Session = &c.Session
page.Render(c, w) page.Render(c, w)
} }
...@@ -678,7 +657,6 @@ func signupCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) ...@@ -678,7 +657,6 @@ func signupCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
page := NewHtmlTemplatePage("home", "Home") page := NewHtmlTemplatePage("home", "Home")
page.Team = team page.Team = team
page.User = ruser page.User = ruser
page.Session = &c.Session
page.Render(c, w) page.Render(c, w)
} }
} }
...@@ -745,7 +723,6 @@ func loginCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request) ...@@ -745,7 +723,6 @@ func loginCompleteOAuth(c *api.Context, w http.ResponseWriter, r *http.Request)
page := NewHtmlTemplatePage("home", "Home") page := NewHtmlTemplatePage("home", "Home")
page.Team = team page.Team = team
page.User = user page.User = user
page.Session = &c.Session
page.Render(c, w) page.Render(c, w)
root(c, w, r) root(c, w, r)
...@@ -786,7 +763,6 @@ func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) { ...@@ -786,7 +763,6 @@ func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) {
page := NewHtmlTemplatePage("admin_console", "Admin Console") page := NewHtmlTemplatePage("admin_console", "Admin Console")
page.User = user page.User = user
page.Team = team page.Team = team
page.Session = &c.Session
page.Props["ActiveTab"] = activeTab page.Props["ActiveTab"] = activeTab
page.Props["TeamId"] = teamId page.Props["TeamId"] = teamId
page.Render(c, w) page.Render(c, w)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment