summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2015-10-20 14:49:42 -0700
committer=Corey Hulen <corey@hulen.com>2015-10-20 14:49:42 -0700
commit1fc12dd8ba2238eba7d154eee55e1381e7415372 (patch)
treedf92c9501d91b29801c87d38e7d0a0ec234bbcee
parentfa3a0df2b63d3f1bbbad44bf20afa48fed42aa06 (diff)
downloadchat-1fc12dd8ba2238eba7d154eee55e1381e7415372.tar.gz
chat-1fc12dd8ba2238eba7d154eee55e1381e7415372.tar.bz2
chat-1fc12dd8ba2238eba7d154eee55e1381e7415372.zip
Multi-session login
-rw-r--r--api/context.go124
-rw-r--r--api/post.go2
-rw-r--r--api/user.go6
-rw-r--r--model/client.go25
-rw-r--r--web/react/components/admin_console/admin_sidebar_header.jsx2
-rw-r--r--web/react/components/admin_console/user_item.jsx2
-rw-r--r--web/react/components/file_attachment.jsx4
-rw-r--r--web/react/components/file_preview.jsx2
-rw-r--r--web/react/components/member_list_item.jsx2
-rw-r--r--web/react/components/member_list_team_item.jsx2
-rw-r--r--web/react/components/mention.jsx2
-rw-r--r--web/react/components/more_direct_channels.jsx2
-rw-r--r--web/react/components/post.jsx2
-rw-r--r--web/react/components/post_list.jsx2
-rw-r--r--web/react/components/rhs_comment.jsx2
-rw-r--r--web/react/components/rhs_root_post.jsx2
-rw-r--r--web/react/components/search_results_item.jsx2
-rw-r--r--web/react/components/sidebar_header.jsx2
-rw-r--r--web/react/components/user_profile.jsx2
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx2
-rw-r--r--web/react/components/view_image.jsx4
-rw-r--r--web/react/stores/socket_store.jsx8
-rw-r--r--web/react/utils/client.jsx16
-rw-r--r--web/react/utils/utils.jsx10
-rw-r--r--web/templates/head.html11
-rw-r--r--web/web.go11
26 files changed, 143 insertions, 108 deletions
diff --git a/api/context.go b/api/context.go
index e5ef8b312..28d6951da 100644
--- a/api/context.go
+++ b/api/context.go
@@ -8,6 +8,7 @@ import (
"net"
"net/http"
"net/url"
+ "strconv"
"strings"
l4g "code.google.com/p/log4go"
@@ -19,23 +20,24 @@ import (
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
type Context struct {
- Session model.Session
- RequestId string
- IpAddress string
- Path string
- Err *model.AppError
- teamURLValid bool
- teamURL string
- siteURL string
+ Session model.Session
+ RequestId string
+ IpAddress string
+ Path string
+ Err *model.AppError
+ teamURLValid bool
+ teamURL string
+ siteURL string
+ SessionTokenIndex int64
}
type Page struct {
- TemplateName string
- Props map[string]string
- ClientCfg map[string]string
- User *model.User
- Team *model.Team
- SessionTokenHash string
+ TemplateName string
+ Props map[string]string
+ ClientCfg map[string]string
+ User *model.User
+ Team *model.Team
+ SessionTokenIndex int64
}
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
@@ -99,29 +101,37 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Attempt to parse the token from the cookie
if len(token) == 0 {
- if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
- 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
+ tokens := GetMultiSessionCookie(r)
+ if len(tokens) > 0 {
+ // If there is only 1 token in the cookie then just use it like normal
+ if len(tokens) == 1 {
+ token = tokens[0]
+ } else {
+ // If it is a multi-session token then find the correct session
+ sessionTokenIndexStr := r.URL.Query().Get(model.SESSION_TOKEN_INDEX)
+ sessionTokenIndex := int64(-1)
+ if len(sessionTokenIndexStr) > 0 {
+ if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
+ sessionTokenIndex = index
+ }
} 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
+ sessionTokenIndexStr := r.Header.Get(model.HEADER_MM_SESSION_TOKEN_INDEX)
+ if len(sessionTokenIndexStr) > 0 {
+ if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
+ sessionTokenIndex = index
}
}
}
+
+ if sessionTokenIndex >= 0 && sessionTokenIndex < int64(len(tokens)) {
+ token = tokens[sessionTokenIndex]
+ c.SessionTokenIndex = sessionTokenIndex
+ } else {
+ c.SessionTokenIndex = -1
+ }
}
+ } else {
+ c.SessionTokenIndex = -1
}
}
@@ -200,7 +210,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(c.Err.ToJson()))
} else {
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)
} else {
RenderWebError(c.Err, w, r)
@@ -332,20 +341,30 @@ func (c *Context) IsTeamAdmin() bool {
func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
- multiToken := ""
- if oldMultiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
- multiToken = oldMultiCookie.Value
- }
+ // multiToken := ""
+ // if oldMultiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
+ // multiToken = oldMultiCookie.Value
+ // }
- multiCookie := &http.Cookie{
+ // multiCookie := &http.Cookie{
+ // Name: model.SESSION_COOKIE_TOKEN,
+ // Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
+ // Path: "/",
+ // MaxAge: model.SESSION_TIME_WEB_IN_SECS,
+ // HttpOnly: true,
+ // }
+
+ //http.SetCookie(w, multiCookie)
+
+ cookie := &http.Cookie{
Name: model.SESSION_COOKIE_TOKEN,
- Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
+ Value: "",
Path: "/",
- MaxAge: model.SESSION_TIME_WEB_IN_SECS,
+ MaxAge: -1,
HttpOnly: true,
}
- http.SetCookie(w, multiCookie)
+ http.SetCookie(w, cookie)
}
func (c *Context) SetInvalidParam(where string, name string) {
@@ -508,24 +527,27 @@ func GetSession(token string) *model.Session {
return session
}
-func FindMultiSessionForTeamId(r *http.Request, teamId string) *model.Session {
-
+func GetMultiSessionCookie(r *http.Request) []string {
if multiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
multiToken := multiCookie.Value
if len(multiToken) > 0 {
- tokens := strings.Split(multiToken, " ")
+ return strings.Split(multiToken, " ")
+ }
+ }
- for _, token := range tokens {
- s := GetSession(token)
- if s != nil && !s.IsExpired() && s.TeamId == teamId {
- return s
- }
- }
+ return []string{}
+}
+
+func FindMultiSessionForTeamId(r *http.Request, teamId string) (int64, *model.Session) {
+ for index, token := range GetMultiSessionCookie(r) {
+ s := GetSession(token)
+ if s != nil && !s.IsExpired() && s.TeamId == teamId {
+ return int64(index), s
}
}
- return nil
+ return -1, nil
}
func AddSessionToCache(session *model.Session) {
diff --git a/api/post.go b/api/post.go
index 73a63cb72..ded71f727 100644
--- a/api/post.go
+++ b/api/post.go
@@ -281,7 +281,7 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
// copy the context and create a mock session for posting the message
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false}
- newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL}
+ newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0}
if text, ok := respProps["text"]; ok {
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"]); err != nil {
diff --git a/api/user.go b/api/user.go
index 1216dd30d..3770baa76 100644
--- a/api/user.go
+++ b/api/user.go
@@ -434,8 +434,6 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
multiToken = originalMultiSessionCookie.Value
}
- fmt.Println("original: " + multiToken)
-
// Attempt to clean all the old tokens or duplicate tokens
if len(multiToken) > 0 {
tokens := strings.Split(multiToken, " ")
@@ -454,9 +452,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
}
}
- multiToken = strings.TrimSpace(session.Token + " " + multiToken)
-
- fmt.Println("new: " + multiToken)
+ multiToken = strings.TrimSpace(multiToken + " " + session.Token)
multiSessionCookie := &http.Cookie{
Name: model.SESSION_COOKIE_TOKEN,
diff --git a/model/client.go b/model/client.go
index 79480d667..48a560838 100644
--- a/model/client.go
+++ b/model/client.go
@@ -16,18 +16,19 @@ import (
)
const (
- HEADER_REQUEST_ID = "X-Request-ID"
- HEADER_VERSION_ID = "X-Version-ID"
- HEADER_ETAG_SERVER = "ETag"
- HEADER_ETAG_CLIENT = "If-None-Match"
- HEADER_FORWARDED = "X-Forwarded-For"
- HEADER_REAL_IP = "X-Real-IP"
- HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
- HEADER_TOKEN = "token"
- HEADER_BEARER = "BEARER"
- HEADER_AUTH = "Authorization"
- HEADER_MM_SESSION_TOKEN_HASH = "X-MM-TokenHash"
- API_URL_SUFFIX = "/api/v1"
+ HEADER_REQUEST_ID = "X-Request-ID"
+ HEADER_VERSION_ID = "X-Version-ID"
+ HEADER_ETAG_SERVER = "ETag"
+ HEADER_ETAG_CLIENT = "If-None-Match"
+ HEADER_FORWARDED = "X-Forwarded-For"
+ HEADER_REAL_IP = "X-Real-IP"
+ HEADER_FORWARDED_PROTO = "X-Forwarded-Proto"
+ HEADER_TOKEN = "token"
+ HEADER_BEARER = "BEARER"
+ HEADER_AUTH = "Authorization"
+ HEADER_MM_SESSION_TOKEN_INDEX = "X-MM-TokenIndex"
+ SESSION_TOKEN_INDEX = "session_token_index"
+ API_URL_SUFFIX = "/api/v1"
)
type Result struct {
diff --git a/web/react/components/admin_console/admin_sidebar_header.jsx b/web/react/components/admin_console/admin_sidebar_header.jsx
index c80811bcd..e66beaf35 100644
--- a/web/react/components/admin_console/admin_sidebar_header.jsx
+++ b/web/react/components/admin_console/admin_sidebar_header.jsx
@@ -36,7 +36,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
+ src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at + '&' + Utils.getSessionIndex()}
/>
);
}
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
index 395e22e6c..f7e92672d 100644
--- a/web/react/components/admin_console/user_item.jsx
+++ b/web/react/components/admin_console/user_item.jsx
@@ -215,7 +215,7 @@ export default class UserItem extends React.Component {
<div className='row member-div'>
<img
className='post-profile-img pull-left'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
+ src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
height='36'
width='36'
/>
diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx
index c6dff6550..307c543a2 100644
--- a/web/react/components/file_attachment.jsx
+++ b/web/react/components/file_attachment.jsx
@@ -39,7 +39,7 @@ export default class FileAttachment extends React.Component {
if (type === 'image') {
var self = this; // Need this reference since we use the given "this"
- $('<img/>').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) {
+ $('<img/>').attr('src', fileInfo.path + '_thumb.jpg?' + utils.getSessionIndex()).load(function loadWrapper(path, name) {
return function loader() {
$(this).remove();
if (name in self.refs) {
@@ -62,7 +62,7 @@ export default class FileAttachment extends React.Component {
var re2 = new RegExp('\\(', 'g');
var re3 = new RegExp('\\)', 'g');
var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
- $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)');
+ $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg?' + utils.getSessionIndex() + ')');
}
};
}(fileInfo.path, filename));
diff --git a/web/react/components/file_preview.jsx b/web/react/components/file_preview.jsx
index a40ed1dcf..df5deb8bc 100644
--- a/web/react/components/file_preview.jsx
+++ b/web/react/components/file_preview.jsx
@@ -34,7 +34,7 @@ export default class FilePreview extends React.Component {
if (filename.indexOf('/api/v1/files/get') !== -1) {
filename = filename.split('/api/v1/files/get')[1];
}
- filename = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename;
+ filename = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename + '?' + Utils.getSessionIndex();
if (type === 'image') {
previews.push(
diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx
index 5c3695ad4..8ed94680e 100644
--- a/web/react/components/member_list_item.jsx
+++ b/web/react/components/member_list_item.jsx
@@ -105,7 +105,7 @@ export default class MemberListItem extends React.Component {
<div className='row member-div'>
<img
className='post-profile-img pull-left'
- src={'/api/v1/users/' + member.id + '/image?time=' + timestamp}
+ src={'/api/v1/users/' + member.id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
height='36'
width='36'
/>
diff --git a/web/react/components/member_list_team_item.jsx b/web/react/components/member_list_team_item.jsx
index 3af1d3800..14db05cdb 100644
--- a/web/react/components/member_list_team_item.jsx
+++ b/web/react/components/member_list_team_item.jsx
@@ -169,7 +169,7 @@ export default class MemberListTeamItem extends React.Component {
<div className='row member-div'>
<img
className='post-profile-img pull-left'
- src={`/api/v1/users/${user.id}/image?time=${timestamp}`}
+ src={`/api/v1/users/${user.id}/image?time=${timestamp}&${Utils.getSessionIndex()}`}
height='36'
width='36'
/>
diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx
index aeed724a8..09035523a 100644
--- a/web/react/components/mention.jsx
+++ b/web/react/components/mention.jsx
@@ -25,7 +25,7 @@ export default class Mention extends React.Component {
<span>
<img
className='mention-img'
- src={'/api/v1/users/' + this.props.id + '/image?time=' + timestamp}
+ src={'/api/v1/users/' + this.props.id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
/>
</span>
);
diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx
index 105199035..21f9a53a0 100644
--- a/web/react/components/more_direct_channels.jsx
+++ b/web/react/components/more_direct_channels.jsx
@@ -179,7 +179,7 @@ export default class MoreDirectChannels extends React.Component {
className='profile-img pull-left'
width='38'
height='38'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
+ src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
/>
<div className='more-name'>
{user.username}
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index 3b3b0383c..bc3144dbc 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -158,7 +158,7 @@ export default class Post extends React.Component {
var profilePic = null;
if (!this.props.hideProfilePic) {
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
+ let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
if (post.props.override_icon_url) {
src = post.props.override_icon_url;
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
index 4402745e1..29cd22c44 100644
--- a/web/react/components/post_list.jsx
+++ b/web/react/components/post_list.jsx
@@ -323,7 +323,7 @@ export default class PostList extends React.Component {
<div className='post-profile-img__container channel-intro-img'>
<img
className='post-profile-img'
- src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at}
+ src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at + '&' + utils.getSessionIndex()}
height='50'
width='50'
/>
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index d3a4cfaeb..cfff04fa2 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -199,7 +199,7 @@ export default class RhsComment extends React.Component {
<div className='post-profile-img__container'>
<img
className='post-profile-img'
- src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
+ src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
height='36'
width='36'
/>
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 979c56036..deef389e2 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -134,7 +134,7 @@ export default class RhsRootPost extends React.Component {
botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>;
}
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
+ let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
if (post.props.override_icon_url) {
src = post.props.override_icon_url;
diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx
index 75d2e7a45..a7d4bb229 100644
--- a/web/react/components/search_results_item.jsx
+++ b/web/react/components/search_results_item.jsx
@@ -77,7 +77,7 @@ export default class SearchResultsItem extends React.Component {
<div className='post-profile-img__container'>
<img
className='post-profile-img'
- src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp}
+ src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex()}
height='36'
width='36'
/>
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 6b29da622..f5d2ed3b4 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -32,7 +32,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
+ src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at + '&' + Utils.getSessionIndex()}
/>
);
}
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index 4a759bb21..38d15b7f8 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -67,7 +67,7 @@ export default class UserProfile extends React.Component {
dataContent.push(
<img
className='user-popover__image'
- src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at}
+ src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '&' + Utils.getSessionIndex()}
height='128'
width='128'
key='user-popover-image'
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index e6430841f..70e559c30 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -542,7 +542,7 @@ export default class UserSettingsGeneralTab extends React.Component {
<SettingPicture
title='Profile Picture'
submit={this.submitPicture}
- src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update}
+ src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update + '&' + utils.getSessionIndex()}
server_error={serverError}
client_error={clientError}
updateSection={function clearSection(e) {
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 322e68c17..766edf0ac 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -160,7 +160,7 @@ export default class ViewImageModal extends React.Component {
}
fileInfo.path = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
- return fileInfo.path + '_preview.jpg';
+ return fileInfo.path + '_preview.jpg' + '?' + Utils.getSessionIndex();
}
// only images have proper previews, so just use a placeholder icon for non-images
@@ -219,7 +219,7 @@ export default class ViewImageModal extends React.Component {
width={width}
height={height}
>
- <source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename} />
+ <source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename + '?' + Utils.getSessionIndex()} />
</video>
);
} else {
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index 77951f214..33cdc79fb 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -38,6 +38,10 @@ class SocketStoreClass extends EventEmitter {
return;
}
+ if (!global.window.mm_session_token_index) {
+ return;
+ }
+
this.setMaxListeners(0);
if (window.WebSocket && !conn) {
@@ -45,7 +49,9 @@ class SocketStoreClass extends EventEmitter {
if (window.location.protocol === 'https:') {
protocol = 'wss://';
}
- var connUrl = protocol + location.host + '/api/v1/websocket';
+
+ var connUrl = protocol + location.host + '/api/v1/websocket?' + Utils.getSessionIndex();
+
if (this.failCount === 0) {
console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
}
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index fab0640fb..ee1f9ad27 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -48,14 +48,14 @@ function handleError(methodName, xhr, status, err) {
track('api', 'api_weberror', methodName, 'message', msg);
- // if (xhr.status === 401) {
- // if (window.location.href.indexOf('/channels') === 0) {
- // window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
- // } else {
- // var teamURL = window.location.href.split('/channels')[0];
- // window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
- // }
- // }
+ if (xhr.status === 401) {
+ if (window.location.href.indexOf('/channels') === 0) {
+ window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
+ } else {
+ var teamURL = window.location.href.split('/channels')[0];
+ window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname + window.location.search);
+ }
+ }
return e;
}
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 38ac68d58..f17a55142 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -872,7 +872,7 @@ export function getFileUrl(filename) {
if (url.indexOf('/api/v1/files/get') !== -1) {
url = filename.split('/api/v1/files/get')[1];
}
- url = getWindowLocationOrigin() + '/api/v1/files/get' + url;
+ url = getWindowLocationOrigin() + '/api/v1/files/get' + url + '?' + getSessionIndex();
return url;
}
@@ -883,6 +883,14 @@ export function getFileName(path) {
return split[split.length - 1];
}
+export function getSessionIndex() {
+ if (global.window.mm_session_token_index >= 0) {
+ return 'session_token_index=' + global.window.mm_session_token_index;
+ }
+
+ return '';
+}
+
// Generates a RFC-4122 version 4 compliant globally unique identifier.
export function generateId() {
// implementation taken from http://stackoverflow.com/a/2117523
diff --git a/web/templates/head.html b/web/templates/head.html
index 731bcd691..041831ed7 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -43,10 +43,13 @@
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 }
- });
+
+ if ({{.SessionTokenIndex}} >= 0) {
+ window.mm_session_token_index = {{.SessionTokenIndex}};
+ $.ajaxSetup({
+ headers: { 'X-MM-TokenIndex': mm_session_token_index }
+ });
+ }
</script>
<script>
diff --git a/web/web.go b/web/web.go
index 56db99733..e379bf35c 100644
--- a/web/web.go
+++ b/web/web.go
@@ -44,9 +44,7 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) {
me.User.Sanitize(map[string]bool{})
}
- if len(c.Session.Token) > 0 {
- me.SessionTokenHash = model.HashPassword(c.Session.Token)
- }
+ me.SessionTokenIndex = c.SessionTokenIndex
if err := Templates.ExecuteTemplate(w, me.TemplateName, me); err != nil {
c.SetUnknownError(me.TemplateName, err.Error())
@@ -232,7 +230,7 @@ func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
}
// 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 {
w.Header().Set(model.HEADER_TOKEN, session.Token)
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusTemporaryRedirect)
@@ -351,13 +349,13 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
// We are logged into a different team. Lets see if we have another
// session in the cookie that will give us access.
if c.Session.TeamId != team.Id {
- session := api.FindMultiSessionForTeamId(r, team.Id)
+ index, session := api.FindMultiSessionForTeamId(r, team.Id)
if session == nil {
// redirect to login
- fmt.Println(">>>>>>>>>>forwarding")
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect)
} else {
c.Session = *session
+ c.SessionTokenIndex = index
}
}
@@ -1028,6 +1026,7 @@ func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) {
// create a mock session
c.Session = model.Session{UserId: hook.UserId, TeamId: hook.TeamId, IsOAuth: false}
+ c.SessionTokenIndex = 0
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN {
c.Err = model.NewAppError("incomingWebhook", "Inappropriate channel permissions", "")