Merge pull request #903 from mattermost/PLT-44
PLT-44 allow team switching without the need to login
Этот коммит содержится в:
@@ -281,7 +281,7 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
// lets make sure the user is valid
|
// lets make sure the user is valid
|
||||||
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w, r)
|
||||||
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if session == nil || session.IsExpired() {
|
if session == nil || session.IsExpired() {
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w, r)
|
||||||
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token)
|
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token)
|
||||||
c.Err.StatusCode = http.StatusUnauthorized
|
c.Err.StatusCode = http.StatusUnauthorized
|
||||||
} else if !session.IsOAuth && isTokenFromQueryString {
|
} else if !session.IsOAuth && isTokenFromQueryString {
|
||||||
@@ -303,7 +303,6 @@ func (c *Context) HasSystemAdminPermissions(where string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) IsSystemAdmin() bool {
|
func (c *Context) IsSystemAdmin() bool {
|
||||||
// TODO XXX FIXME && IsPrivateIpAddress(c.IpAddress)
|
|
||||||
if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) {
|
if model.IsInRole(c.Session.Roles, model.ROLE_SYSTEM_ADMIN) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -317,7 +316,7 @@ func (c *Context) IsTeamAdmin() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
|
func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
sessionCache.Remove(c.Session.Token)
|
sessionCache.Remove(c.Session.Token)
|
||||||
|
|
||||||
@@ -330,6 +329,21 @@ func (c *Context) RemoveSessionCookie(w http.ResponseWriter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
|
|
||||||
|
multiToken := ""
|
||||||
|
if oldMultiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
|
||||||
|
multiToken = oldMultiCookie.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
multiCookie := &http.Cookie{
|
||||||
|
Name: model.MULTI_SESSION_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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) SetInvalidParam(where string, name string) {
|
func (c *Context) SetInvalidParam(where string, name string) {
|
||||||
@@ -346,7 +360,7 @@ func (c *Context) setTeamURL(url string, valid bool) {
|
|||||||
c.teamURLValid = valid
|
c.teamURLValid = valid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) setTeamURLFromSession() {
|
func (c *Context) SetTeamURLFromSession() {
|
||||||
if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil {
|
if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil {
|
||||||
c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true)
|
c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true)
|
||||||
}
|
}
|
||||||
@@ -362,7 +376,7 @@ func (c *Context) GetTeamURLFromTeam(team *model.Team) string {
|
|||||||
|
|
||||||
func (c *Context) GetTeamURL() string {
|
func (c *Context) GetTeamURL() string {
|
||||||
if !c.teamURLValid {
|
if !c.teamURLValid {
|
||||||
c.setTeamURLFromSession()
|
c.SetTeamURLFromSession()
|
||||||
if !c.teamURLValid {
|
if !c.teamURLValid {
|
||||||
l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
|
l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
|
||||||
}
|
}
|
||||||
|
|||||||
39
api/user.go
39
api/user.go
@@ -394,6 +394,41 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
|||||||
|
|
||||||
http.SetCookie(w, sessionCookie)
|
http.SetCookie(w, sessionCookie)
|
||||||
|
|
||||||
|
multiToken := ""
|
||||||
|
if originalMultiSessionCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
|
||||||
|
multiToken = originalMultiSessionCookie.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to clean all the old tokens or duplicate tokens
|
||||||
|
if len(multiToken) > 0 {
|
||||||
|
tokens := strings.Split(multiToken, " ")
|
||||||
|
|
||||||
|
multiToken = ""
|
||||||
|
seen := make(map[string]string)
|
||||||
|
seen[session.TeamId] = session.TeamId
|
||||||
|
for _, token := range tokens {
|
||||||
|
if sr := <-Srv.Store.Session().Get(token); sr.Err == nil {
|
||||||
|
s := sr.Data.(*model.Session)
|
||||||
|
if !s.IsExpired() && seen[s.TeamId] == "" {
|
||||||
|
multiToken += " " + token
|
||||||
|
seen[s.TeamId] = s.TeamId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multiToken = strings.TrimSpace(session.Token + " " + multiToken)
|
||||||
|
|
||||||
|
multiSessionCookie := &http.Cookie{
|
||||||
|
Name: model.MULTI_SESSION_TOKEN,
|
||||||
|
Value: multiToken,
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: maxAge,
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
http.SetCookie(w, multiSessionCookie)
|
||||||
|
|
||||||
c.Session = *session
|
c.Session = *session
|
||||||
c.LogAuditWithUserId(user.Id, "success")
|
c.LogAuditWithUserId(user.Id, "success")
|
||||||
}
|
}
|
||||||
@@ -514,7 +549,7 @@ func logout(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func Logout(c *Context, w http.ResponseWriter, r *http.Request) {
|
func Logout(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
c.LogAudit("")
|
c.LogAudit("")
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w, r)
|
||||||
if result := <-Srv.Store.Session().Remove(c.Session.Id); result.Err != nil {
|
if result := <-Srv.Store.Session().Remove(c.Session.Id); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
return
|
return
|
||||||
@@ -529,7 +564,7 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w, r)
|
||||||
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
||||||
return
|
return
|
||||||
} else if HandleEtag(result.Data.(*model.User).Etag(), w, r) {
|
} else if HandleEtag(result.Data.(*model.User).Etag(), w, r) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SESSION_TOKEN = "MMSID"
|
SESSION_TOKEN = "MMSID"
|
||||||
|
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
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ var TeamStore = require('../stores/team_store.jsx');
|
|||||||
var Constants = require('../utils/constants.jsx');
|
var Constants = require('../utils/constants.jsx');
|
||||||
|
|
||||||
function getStateFromStores() {
|
function getStateFromStores() {
|
||||||
return {teams: UserStore.getTeams(), currentTeam: TeamStore.getCurrent()};
|
return {teams: UserStore.getTeams()};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class NavbarDropdown extends React.Component {
|
export default class NavbarDropdown extends React.Component {
|
||||||
@@ -142,10 +142,10 @@ export default class NavbarDropdown extends React.Component {
|
|||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
if (this.state.teams.length > 1 && this.state.currentTeam) {
|
|
||||||
var curTeamName = this.state.currentTeam.name;
|
if (this.state.teams.length > 1) {
|
||||||
this.state.teams.forEach((teamName) => {
|
this.state.teams.forEach((teamName) => {
|
||||||
if (teamName !== curTeamName) {
|
if (teamName !== this.props.teamName) {
|
||||||
teams.push(<li key={teamName}><a href={Utils.getWindowLocationOrigin() + '/' + teamName}>{'Switch to ' + teamName}</a></li>);
|
teams.push(<li key={teamName}><a href={Utils.getWindowLocationOrigin() + '/' + teamName}>{'Switch to ' + teamName}</a></li>);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -234,5 +234,7 @@ NavbarDropdown.defaultProps = {
|
|||||||
teamType: ''
|
teamType: ''
|
||||||
};
|
};
|
||||||
NavbarDropdown.propTypes = {
|
NavbarDropdown.propTypes = {
|
||||||
teamType: React.PropTypes.string
|
teamType: React.PropTypes.string,
|
||||||
|
teamDisplayName: React.PropTypes.string,
|
||||||
|
teamName: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ export default class Sidebar extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<SidebarHeader
|
<SidebarHeader
|
||||||
teamDisplayName={this.props.teamDisplayName}
|
teamDisplayName={this.props.teamDisplayName}
|
||||||
|
teamName={this.props.teamName}
|
||||||
teamType={this.props.teamType}
|
teamType={this.props.teamType}
|
||||||
/>
|
/>
|
||||||
<SearchBox />
|
<SearchBox />
|
||||||
@@ -591,5 +592,6 @@ Sidebar.defaultProps = {
|
|||||||
};
|
};
|
||||||
Sidebar.propTypes = {
|
Sidebar.propTypes = {
|
||||||
teamType: React.PropTypes.string,
|
teamType: React.PropTypes.string,
|
||||||
teamDisplayName: React.PropTypes.string
|
teamDisplayName: React.PropTypes.string,
|
||||||
|
teamName: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export default class SidebarHeader extends React.Component {
|
|||||||
<NavbarDropdown
|
<NavbarDropdown
|
||||||
ref='dropdown'
|
ref='dropdown'
|
||||||
teamType={this.props.teamType}
|
teamType={this.props.teamType}
|
||||||
|
teamDisplayName={this.props.teamDisplayName}
|
||||||
|
teamName={this.props.teamName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -64,5 +66,6 @@ SidebarHeader.defaultProps = {
|
|||||||
};
|
};
|
||||||
SidebarHeader.propTypes = {
|
SidebarHeader.propTypes = {
|
||||||
teamDisplayName: React.PropTypes.string,
|
teamDisplayName: React.PropTypes.string,
|
||||||
|
teamName: React.PropTypes.string,
|
||||||
teamType: React.PropTypes.string
|
teamType: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,11 +36,14 @@ var RemovedFromChannelModal = require('../components/removed_from_channel_modal.
|
|||||||
var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
|
var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
|
||||||
var RegisterAppModal = require('../components/register_app_modal.jsx');
|
var RegisterAppModal = require('../components/register_app_modal.jsx');
|
||||||
var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx');
|
var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx');
|
||||||
|
var TeamStore = require('../stores/team_store.jsx');
|
||||||
|
|
||||||
var Constants = require('../utils/constants.jsx');
|
var Constants = require('../utils/constants.jsx');
|
||||||
var ActionTypes = Constants.ActionTypes;
|
var ActionTypes = Constants.ActionTypes;
|
||||||
|
|
||||||
function setupChannelPage(props) {
|
function setupChannelPage(props) {
|
||||||
|
TeamStore.setCurrentId(props.TeamId);
|
||||||
|
|
||||||
AppDispatcher.handleViewAction({
|
AppDispatcher.handleViewAction({
|
||||||
type: ActionTypes.CLICK_CHANNEL,
|
type: ActionTypes.CLICK_CHANNEL,
|
||||||
name: props.ChannelName,
|
name: props.ChannelName,
|
||||||
@@ -71,6 +74,7 @@ function setupChannelPage(props) {
|
|||||||
React.render(
|
React.render(
|
||||||
<Sidebar
|
<Sidebar
|
||||||
teamDisplayName={props.TeamDisplayName}
|
teamDisplayName={props.TeamDisplayName}
|
||||||
|
teamName={props.TeamName}
|
||||||
teamType={props.TeamType}
|
teamType={props.TeamType}
|
||||||
/>,
|
/>,
|
||||||
document.getElementById('sidebar-left')
|
document.getElementById('sidebar-left')
|
||||||
|
|||||||
63
web/web.go
63
web/web.go
@@ -189,9 +189,40 @@ func login(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We still might be able to switch to this team because we've logged in before
|
||||||
|
if multiCookie, err := r.Cookie(model.MULTI_SESSION_TOKEN); err == nil {
|
||||||
|
multiToken := multiCookie.Value
|
||||||
|
|
||||||
|
if len(multiToken) > 0 {
|
||||||
|
tokens := strings.Split(multiToken, " ")
|
||||||
|
|
||||||
|
for _, token := range tokens {
|
||||||
|
if sr := <-api.Srv.Store.Session().Get(token); sr.Err == nil {
|
||||||
|
s := sr.Data.(*model.Session)
|
||||||
|
|
||||||
|
if !s.IsExpired() && s.TeamId == team.Id {
|
||||||
|
w.Header().Set(model.HEADER_TOKEN, s.Token)
|
||||||
|
sessionCookie := &http.Cookie{
|
||||||
|
Name: model.SESSION_TOKEN,
|
||||||
|
Value: s.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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
page := NewHtmlTemplatePage("login", "Login")
|
page := NewHtmlTemplatePage("login", "Login")
|
||||||
page.Props["TeamDisplayName"] = team.DisplayName
|
page.Props["TeamDisplayName"] = team.DisplayName
|
||||||
page.Props["TeamName"] = teamName
|
page.Props["TeamName"] = team.Name
|
||||||
page.Render(c, w)
|
page.Render(c, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,6 +319,10 @@ func logout(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
name := params["channelname"]
|
name := params["channelname"]
|
||||||
|
teamName := params["team"]
|
||||||
|
|
||||||
|
var team *model.Team
|
||||||
|
teamChan := api.Srv.Store.Team().Get(c.Session.TeamId)
|
||||||
|
|
||||||
var channelId string
|
var channelId string
|
||||||
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
|
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
|
||||||
@@ -297,6 +332,19 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
channelId = result.Data.(string)
|
channelId = result.Data.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tResult := <-teamChan; tResult.Err != nil {
|
||||||
|
c.Err = tResult.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
team = tResult.Data.(*model.Team)
|
||||||
|
}
|
||||||
|
|
||||||
|
if team.Name != teamName {
|
||||||
|
l4g.Error("It appears you are logged into " + team.Name + ", but are trying to access " + teamName)
|
||||||
|
http.Redirect(w, r, c.GetSiteURL()+"/"+team.Name+"/channels/town-square", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(channelId) == 0 {
|
if len(channelId) == 0 {
|
||||||
if strings.Index(name, "__") > 0 {
|
if strings.Index(name, "__") > 0 {
|
||||||
// It's a direct message channel that doesn't exist yet so let's create it
|
// It's a direct message channel that doesn't exist yet so let's create it
|
||||||
@@ -319,7 +367,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
// lets make sure the user is valid
|
// lets make sure the user is valid
|
||||||
if result := <-api.Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
if result := <-api.Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
c.RemoveSessionCookie(w)
|
c.RemoveSessionCookie(w, r)
|
||||||
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -332,18 +380,10 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var team *model.Team
|
|
||||||
|
|
||||||
if tResult := <-api.Srv.Store.Team().Get(c.Session.TeamId); tResult.Err != nil {
|
|
||||||
c.Err = tResult.Err
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
team = tResult.Data.(*model.Team)
|
|
||||||
}
|
|
||||||
|
|
||||||
page := NewHtmlTemplatePage("channel", "")
|
page := NewHtmlTemplatePage("channel", "")
|
||||||
page.Props["Title"] = name + " - " + team.DisplayName + " " + page.ClientProps["SiteName"]
|
page.Props["Title"] = name + " - " + team.DisplayName + " " + page.ClientProps["SiteName"]
|
||||||
page.Props["TeamDisplayName"] = team.DisplayName
|
page.Props["TeamDisplayName"] = team.DisplayName
|
||||||
|
page.Props["TeamName"] = team.Name
|
||||||
page.Props["TeamType"] = team.Type
|
page.Props["TeamType"] = team.Type
|
||||||
page.Props["TeamId"] = team.Id
|
page.Props["TeamId"] = team.Id
|
||||||
page.Props["ChannelName"] = name
|
page.Props["ChannelName"] = name
|
||||||
@@ -451,6 +491,7 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
page := NewHtmlTemplatePage("password_reset", "")
|
page := NewHtmlTemplatePage("password_reset", "")
|
||||||
page.Props["Title"] = "Reset Password " + page.ClientProps["SiteName"]
|
page.Props["Title"] = "Reset Password " + page.ClientProps["SiteName"]
|
||||||
page.Props["TeamDisplayName"] = teamDisplayName
|
page.Props["TeamDisplayName"] = teamDisplayName
|
||||||
|
page.Props["TeamName"] = teamName
|
||||||
page.Props["Hash"] = hash
|
page.Props["Hash"] = hash
|
||||||
page.Props["Data"] = data
|
page.Props["Data"] = data
|
||||||
page.Props["TeamName"] = teamName
|
page.Props["TeamName"] = teamName
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user