Multi-session login
Этот коммит содержится в:
126
api/context.go
126
api/context.go
@@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
l4g "code.google.com/p/log4go"
|
l4g "code.google.com/p/log4go"
|
||||||
@@ -19,23 +20,24 @@ import (
|
|||||||
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
|
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Session model.Session
|
Session model.Session
|
||||||
RequestId string
|
RequestId string
|
||||||
IpAddress string
|
IpAddress string
|
||||||
Path string
|
Path string
|
||||||
Err *model.AppError
|
Err *model.AppError
|
||||||
teamURLValid bool
|
teamURLValid bool
|
||||||
teamURL string
|
teamURL string
|
||||||
siteURL string
|
siteURL string
|
||||||
|
SessionTokenIndex int64
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
SessionTokenHash string
|
SessionTokenIndex int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
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
|
// Attempt to parse the token from the cookie
|
||||||
if len(token) == 0 {
|
if len(token) == 0 {
|
||||||
if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
|
tokens := GetMultiSessionCookie(r)
|
||||||
multiToken := cookie.Value
|
if len(tokens) > 0 {
|
||||||
|
// If there is only 1 token in the cookie then just use it like normal
|
||||||
fmt.Println(">>>>>>>> multiToken: " + multiToken)
|
if len(tokens) == 1 {
|
||||||
|
token = tokens[0]
|
||||||
if len(multiToken) > 0 {
|
} else {
|
||||||
tokens := strings.Split(multiToken, " ")
|
// If it is a multi-session token then find the correct session
|
||||||
|
sessionTokenIndexStr := r.URL.Query().Get(model.SESSION_TOKEN_INDEX)
|
||||||
// If there is only 1 token in the cookie then just use it like normal
|
sessionTokenIndex := int64(-1)
|
||||||
if len(tokens) == 1 {
|
if len(sessionTokenIndexStr) > 0 {
|
||||||
token = multiToken
|
if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
|
||||||
|
sessionTokenIndex = index
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If it is a multi-session token then find the correct session
|
sessionTokenIndexStr := r.Header.Get(model.HEADER_MM_SESSION_TOKEN_INDEX)
|
||||||
sessionTokenHash := r.Header.Get(model.HEADER_MM_SESSION_TOKEN_HASH)
|
if len(sessionTokenIndexStr) > 0 {
|
||||||
fmt.Println(">>>>>>>> sessionHash: " + sessionTokenHash + " url=" + r.URL.Path)
|
if index, err := strconv.ParseInt(sessionTokenIndexStr, 10, 64); err == nil {
|
||||||
for _, t := range tokens {
|
sessionTokenIndex = index
|
||||||
if sessionTokenHash == model.HashPassword(t) {
|
|
||||||
token = token
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()))
|
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)
|
||||||
@@ -332,20 +341,30 @@ 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) {
|
||||||
|
|
||||||
multiToken := ""
|
// multiToken := ""
|
||||||
if oldMultiCookie, err := r.Cookie(model.SESSION_COOKIE_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.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,
|
Name: model.SESSION_COOKIE_TOKEN,
|
||||||
Value: strings.TrimSpace(strings.Replace(multiToken, c.Session.Token, "", -1)),
|
Value: "",
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: model.SESSION_TIME_WEB_IN_SECS,
|
MaxAge: -1,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, multiCookie)
|
http.SetCookie(w, cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) SetInvalidParam(where string, name string) {
|
func (c *Context) SetInvalidParam(where string, name string) {
|
||||||
@@ -508,24 +527,27 @@ func GetSession(token string) *model.Session {
|
|||||||
return 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 {
|
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 {
|
||||||
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 nil
|
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 -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddSessionToCache(session *model.Session) {
|
func AddSessionToCache(session *model.Session) {
|
||||||
|
|||||||
@@ -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
|
// copy the context and create a mock session for posting the message
|
||||||
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false}
|
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 text, ok := respProps["text"]; ok {
|
||||||
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"]); err != nil {
|
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"]); err != nil {
|
||||||
|
|||||||
@@ -434,8 +434,6 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
|||||||
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, " ")
|
||||||
@@ -454,9 +452,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
multiToken = strings.TrimSpace(session.Token + " " + multiToken)
|
multiToken = strings.TrimSpace(multiToken + " " + session.Token)
|
||||||
|
|
||||||
fmt.Println("new: " + multiToken)
|
|
||||||
|
|
||||||
multiSessionCookie := &http.Cookie{
|
multiSessionCookie := &http.Cookie{
|
||||||
Name: model.SESSION_COOKIE_TOKEN,
|
Name: model.SESSION_COOKIE_TOKEN,
|
||||||
|
|||||||
@@ -16,18 +16,19 @@ 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"
|
||||||
HEADER_MM_SESSION_TOKEN_HASH = "X-MM-TokenHash"
|
HEADER_MM_SESSION_TOKEN_INDEX = "X-MM-TokenIndex"
|
||||||
API_URL_SUFFIX = "/api/v1"
|
SESSION_TOKEN_INDEX = "session_token_index"
|
||||||
|
API_URL_SUFFIX = "/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Result struct {
|
type Result struct {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default class SidebarHeader extends React.Component {
|
|||||||
profilePicture = (
|
profilePicture = (
|
||||||
<img
|
<img
|
||||||
className='user__picture'
|
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()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export default class UserItem extends React.Component {
|
|||||||
<div className='row member-div'>
|
<div className='row member-div'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-img pull-left'
|
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'
|
height='36'
|
||||||
width='36'
|
width='36'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default class FileAttachment extends React.Component {
|
|||||||
|
|
||||||
if (type === 'image') {
|
if (type === 'image') {
|
||||||
var self = this; // Need this reference since we use the given "this"
|
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() {
|
return function loader() {
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
if (name in self.refs) {
|
if (name in self.refs) {
|
||||||
@@ -62,7 +62,7 @@ export default class FileAttachment extends React.Component {
|
|||||||
var re2 = new RegExp('\\(', 'g');
|
var re2 = new RegExp('\\(', 'g');
|
||||||
var re3 = new RegExp('\\)', 'g');
|
var re3 = new RegExp('\\)', 'g');
|
||||||
var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29');
|
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));
|
}(fileInfo.path, filename));
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default class FilePreview extends React.Component {
|
|||||||
if (filename.indexOf('/api/v1/files/get') !== -1) {
|
if (filename.indexOf('/api/v1/files/get') !== -1) {
|
||||||
filename = filename.split('/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') {
|
if (type === 'image') {
|
||||||
previews.push(
|
previews.push(
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default class MemberListItem extends React.Component {
|
|||||||
<div className='row member-div'>
|
<div className='row member-div'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-img pull-left'
|
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'
|
height='36'
|
||||||
width='36'
|
width='36'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export default class MemberListTeamItem extends React.Component {
|
|||||||
<div className='row member-div'>
|
<div className='row member-div'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-img pull-left'
|
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'
|
height='36'
|
||||||
width='36'
|
width='36'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default class Mention extends React.Component {
|
|||||||
<span>
|
<span>
|
||||||
<img
|
<img
|
||||||
className='mention-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>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ export default class MoreDirectChannels extends React.Component {
|
|||||||
className='profile-img pull-left'
|
className='profile-img pull-left'
|
||||||
width='38'
|
width='38'
|
||||||
height='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'>
|
<div className='more-name'>
|
||||||
{user.username}
|
{user.username}
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export default class Post extends React.Component {
|
|||||||
|
|
||||||
var profilePic = null;
|
var profilePic = null;
|
||||||
if (!this.props.hideProfilePic) {
|
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 && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
||||||
if (post.props.override_icon_url) {
|
if (post.props.override_icon_url) {
|
||||||
src = post.props.override_icon_url;
|
src = post.props.override_icon_url;
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ export default class PostList extends React.Component {
|
|||||||
<div className='post-profile-img__container channel-intro-img'>
|
<div className='post-profile-img__container channel-intro-img'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-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'
|
height='50'
|
||||||
width='50'
|
width='50'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ export default class RhsComment extends React.Component {
|
|||||||
<div className='post-profile-img__container'>
|
<div className='post-profile-img__container'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-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'
|
height='36'
|
||||||
width='36'
|
width='36'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default class RhsRootPost extends React.Component {
|
|||||||
botIndicator = <li className='post-header-col post-header__name bot-indicator'>{'BOT'}</li>;
|
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 && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
||||||
if (post.props.override_icon_url) {
|
if (post.props.override_icon_url) {
|
||||||
src = post.props.override_icon_url;
|
src = post.props.override_icon_url;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default class SearchResultsItem extends React.Component {
|
|||||||
<div className='post-profile-img__container'>
|
<div className='post-profile-img__container'>
|
||||||
<img
|
<img
|
||||||
className='post-profile-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'
|
height='36'
|
||||||
width='36'
|
width='36'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default class SidebarHeader extends React.Component {
|
|||||||
profilePicture = (
|
profilePicture = (
|
||||||
<img
|
<img
|
||||||
className='user__picture'
|
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()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default class UserProfile extends React.Component {
|
|||||||
dataContent.push(
|
dataContent.push(
|
||||||
<img
|
<img
|
||||||
className='user-popover__image'
|
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'
|
height='128'
|
||||||
width='128'
|
width='128'
|
||||||
key='user-popover-image'
|
key='user-popover-image'
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
|||||||
<SettingPicture
|
<SettingPicture
|
||||||
title='Profile Picture'
|
title='Profile Picture'
|
||||||
submit={this.submitPicture}
|
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}
|
server_error={serverError}
|
||||||
client_error={clientError}
|
client_error={clientError}
|
||||||
updateSection={function clearSection(e) {
|
updateSection={function clearSection(e) {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export default class ViewImageModal extends React.Component {
|
|||||||
}
|
}
|
||||||
fileInfo.path = Utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
|
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
|
// 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}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
>
|
>
|
||||||
<source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename} />
|
<source src={Utils.getWindowLocationOrigin() + '/api/v1/files/get' + filename + '?' + Utils.getSessionIndex()} />
|
||||||
</video>
|
</video>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ class SocketStoreClass extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!global.window.mm_session_token_index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setMaxListeners(0);
|
this.setMaxListeners(0);
|
||||||
|
|
||||||
if (window.WebSocket && !conn) {
|
if (window.WebSocket && !conn) {
|
||||||
@@ -45,7 +49,9 @@ class SocketStoreClass extends EventEmitter {
|
|||||||
if (window.location.protocol === 'https:') {
|
if (window.location.protocol === 'https:') {
|
||||||
protocol = 'wss://';
|
protocol = 'wss://';
|
||||||
}
|
}
|
||||||
var connUrl = protocol + location.host + '/api/v1/websocket';
|
|
||||||
|
var connUrl = protocol + location.host + '/api/v1/websocket?' + Utils.getSessionIndex();
|
||||||
|
|
||||||
if (this.failCount === 0) {
|
if (this.failCount === 0) {
|
||||||
console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
|
console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -872,7 +872,7 @@ export function getFileUrl(filename) {
|
|||||||
if (url.indexOf('/api/v1/files/get') !== -1) {
|
if (url.indexOf('/api/v1/files/get') !== -1) {
|
||||||
url = filename.split('/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;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -883,6 +883,14 @@ export function getFileName(path) {
|
|||||||
return split[split.length - 1];
|
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.
|
// Generates a RFC-4122 version 4 compliant globally unique identifier.
|
||||||
export function generateId() {
|
export function generateId() {
|
||||||
// implementation taken from http://stackoverflow.com/a/2117523
|
// implementation taken from http://stackoverflow.com/a/2117523
|
||||||
|
|||||||
@@ -43,10 +43,13 @@
|
|||||||
window.mm_config = {{ .ClientCfg }};
|
window.mm_config = {{ .ClientCfg }};
|
||||||
window.mm_team = {{ .Team }};
|
window.mm_team = {{ .Team }};
|
||||||
window.mm_user = {{ .User }};
|
window.mm_user = {{ .User }};
|
||||||
window.mm_session_token_hash = {{ .SessionTokenHash }};
|
|
||||||
$.ajaxSetup({
|
if ({{.SessionTokenIndex}} >= 0) {
|
||||||
headers: { 'X-MM-TokenHash': mm_session_token_hash }
|
window.mm_session_token_index = {{.SessionTokenIndex}};
|
||||||
});
|
$.ajaxSetup({
|
||||||
|
headers: { 'X-MM-TokenIndex': mm_session_token_index }
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
11
web/web.go
11
web/web.go
@@ -44,9 +44,7 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) {
|
|||||||
me.User.Sanitize(map[string]bool{})
|
me.User.Sanitize(map[string]bool{})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Session.Token) > 0 {
|
me.SessionTokenIndex = c.SessionTokenIndex
|
||||||
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 {
|
||||||
c.SetUnknownError(me.TemplateName, err.Error())
|
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
|
// 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)
|
||||||
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)
|
||||||
@@ -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
|
// We are logged into a different team. Lets see if we have another
|
||||||
// session in the cookie that will give us access.
|
// session in the cookie that will give us access.
|
||||||
if c.Session.TeamId != team.Id {
|
if c.Session.TeamId != team.Id {
|
||||||
session := api.FindMultiSessionForTeamId(r, team.Id)
|
index, 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
|
||||||
|
c.SessionTokenIndex = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1028,6 +1026,7 @@ func incomingWebhook(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// create a mock session
|
// create a mock session
|
||||||
c.Session = model.Session{UserId: hook.UserId, TeamId: hook.TeamId, IsOAuth: false}
|
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 {
|
if !c.HasPermissionsToChannel(pchan, "createIncomingHook") && channel.Type != model.CHANNEL_OPEN {
|
||||||
c.Err = model.NewAppError("incomingWebhook", "Inappropriate channel permissions", "")
|
c.Err = model.NewAppError("incomingWebhook", "Inappropriate channel permissions", "")
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user