Convering client to react-router.
Этот коммит содержится в:
43
api/api.go
43
api/api.go
@@ -4,47 +4,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
_ "github.com/cloudfoundry/jibber_jabber"
|
||||
_ "github.com/nicksnyder/go-i18n/i18n"
|
||||
)
|
||||
|
||||
var ServerTemplates *template.Template
|
||||
|
||||
type ServerTemplatePage Page
|
||||
|
||||
func NewServerTemplatePage(templateName, locale string) *ServerTemplatePage {
|
||||
return &ServerTemplatePage{
|
||||
TemplateName: templateName,
|
||||
Props: make(map[string]string),
|
||||
Extra: make(map[string]string),
|
||||
Html: make(map[string]template.HTML),
|
||||
ClientCfg: utils.ClientCfg,
|
||||
Locale: locale,
|
||||
}
|
||||
}
|
||||
|
||||
func (me *ServerTemplatePage) Render() string {
|
||||
var text bytes.Buffer
|
||||
|
||||
T := utils.GetUserTranslations(me.Locale)
|
||||
me.Props["Footer"] = T("api.templates.email_footer")
|
||||
me.Html["EmailInfo"] = template.HTML(T("api.templates.email_info",
|
||||
map[string]interface{}{"SupportEmail": me.ClientCfg["SupportEmail"], "SiteName": me.ClientCfg["SiteName"]}))
|
||||
|
||||
if err := ServerTemplates.ExecuteTemplate(&text, me.TemplateName, me); err != nil {
|
||||
l4g.Error(utils.T("api.api.render.error"), me.TemplateName, err)
|
||||
}
|
||||
|
||||
return text.String()
|
||||
}
|
||||
|
||||
func InitApi() {
|
||||
r := Srv.Router.PathPrefix("/api/v1").Subrouter()
|
||||
InitUser(r)
|
||||
@@ -60,12 +28,7 @@ func InitApi() {
|
||||
InitPreference(r)
|
||||
InitLicense(r)
|
||||
|
||||
templatesDir := utils.FindDir("api/templates")
|
||||
l4g.Debug(utils.T("api.api.init.parsing_templates.debug"), templatesDir)
|
||||
var err error
|
||||
if ServerTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
||||
l4g.Error(utils.T("api.api.init.parsing_templates.error"), err)
|
||||
}
|
||||
utils.InitHTML()
|
||||
}
|
||||
|
||||
func HandleEtag(etag string, w http.ResponseWriter, r *http.Request) bool {
|
||||
|
||||
137
api/context.go
137
api/context.go
@@ -5,11 +5,9 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
@@ -31,33 +29,16 @@ var allowedMethods []string = []string{
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
Session model.Session
|
||||
RequestId string
|
||||
IpAddress string
|
||||
Path string
|
||||
Err *model.AppError
|
||||
teamURLValid bool
|
||||
teamURL string
|
||||
siteURL string
|
||||
SessionTokenIndex int64
|
||||
T goi18n.TranslateFunc
|
||||
Locale string
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
TemplateName string
|
||||
Props map[string]string
|
||||
Extra map[string]string
|
||||
Html map[string]template.HTML
|
||||
ClientCfg map[string]string
|
||||
ClientLicense map[string]string
|
||||
User *model.User
|
||||
Team *model.Team
|
||||
Channel *model.Channel
|
||||
Preferences *model.Preferences
|
||||
PostID string
|
||||
SessionTokenIndex int64
|
||||
Locale string
|
||||
Session model.Session
|
||||
RequestId string
|
||||
IpAddress string
|
||||
Path string
|
||||
Err *model.AppError
|
||||
teamURLValid bool
|
||||
teamURL string
|
||||
siteURL string
|
||||
T goi18n.TranslateFunc
|
||||
Locale string
|
||||
}
|
||||
|
||||
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
|
||||
@@ -121,37 +102,8 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Attempt to parse the token from the cookie
|
||||
if len(token) == 0 {
|
||||
tokens := GetMultiSessionCookieTokens(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 {
|
||||
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
|
||||
if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
|
||||
token = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,8 +137,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if session == nil || session.IsExpired() {
|
||||
c.RemoveSessionCookie(w, r)
|
||||
c.Err = model.NewLocAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token)
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
if h.requireUser || h.requireSystemAdmin {
|
||||
c.Err = model.NewLocAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token)
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
}
|
||||
} else if !session.IsOAuth && isTokenFromQueryString {
|
||||
c.Err = model.NewLocAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token)
|
||||
c.Err.StatusCode = http.StatusUnauthorized
|
||||
@@ -390,22 +344,6 @@ 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
|
||||
// }
|
||||
|
||||
// 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: "",
|
||||
@@ -538,23 +476,25 @@ func IsPrivateIpAddress(ipAddress string) bool {
|
||||
}
|
||||
|
||||
func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) {
|
||||
props := make(map[string]string)
|
||||
props["Message"] = err.Message
|
||||
props["Details"] = err.DetailedError
|
||||
T, locale := utils.GetTranslationsAndLocale(w, r)
|
||||
page := utils.NewHTMLTemplate("error", locale)
|
||||
page.Props["Message"] = err.Message
|
||||
page.Props["Details"] = err.DetailedError
|
||||
|
||||
pathParts := strings.Split(r.URL.Path, "/")
|
||||
if len(pathParts) > 1 {
|
||||
props["SiteURL"] = GetProtocol(r) + "://" + r.Host + "/" + pathParts[1]
|
||||
page.Props["SiteURL"] = GetProtocol(r) + "://" + r.Host + "/" + pathParts[1]
|
||||
} else {
|
||||
props["SiteURL"] = GetProtocol(r) + "://" + r.Host
|
||||
page.Props["SiteURL"] = GetProtocol(r) + "://" + r.Host
|
||||
}
|
||||
|
||||
T, _ := utils.GetTranslationsAndLocale(w, r)
|
||||
props["Title"] = T("api.templates.error.title", map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||
props["Link"] = T("api.templates.error.link")
|
||||
page.Props["Title"] = T("api.templates.error.title", map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||
page.Props["Link"] = T("api.templates.error.link")
|
||||
|
||||
w.WriteHeader(err.StatusCode)
|
||||
ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientCfg: utils.ClientCfg})
|
||||
if rErr := page.RenderToWriter(w); rErr != nil {
|
||||
l4g.Error("Failed to create error page: " + rErr.Error() + ", Original error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func Handle404(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -588,29 +528,6 @@ func GetSession(token string) *model.Session {
|
||||
return session
|
||||
}
|
||||
|
||||
func GetMultiSessionCookieTokens(r *http.Request) []string {
|
||||
if multiCookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil {
|
||||
multiToken := multiCookie.Value
|
||||
|
||||
if len(multiToken) > 0 {
|
||||
return strings.Split(multiToken, " ")
|
||||
}
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func FindMultiSessionForTeamId(r *http.Request, teamId string) (int64, *model.Session) {
|
||||
for index, token := range GetMultiSessionCookieTokens(r) {
|
||||
s := GetSession(token)
|
||||
if s != nil && !s.IsExpired() && s.TeamId == teamId {
|
||||
return int64(index), s
|
||||
}
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func AddSessionToCache(session *model.Session) {
|
||||
sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*utils.Cfg.ServiceSettings.SessionCacheInMinutes*60))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func InitLicense(r *mux.Router) {
|
||||
sr := r.PathPrefix("/license").Subrouter()
|
||||
sr.Handle("/add", ApiAdminSystemRequired(addLicense)).Methods("POST")
|
||||
sr.Handle("/remove", ApiAdminSystemRequired(removeLicense)).Methods("POST")
|
||||
sr.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET")
|
||||
}
|
||||
|
||||
func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -130,3 +131,22 @@ func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
rdata["status"] = "ok"
|
||||
w.Write([]byte(model.MapToJson(rdata)))
|
||||
}
|
||||
|
||||
func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
config := utils.ClientLicense
|
||||
|
||||
var etag string
|
||||
if config["IsLicensed"] == "false" {
|
||||
etag = model.Etag(config["IsLicensed"])
|
||||
} else {
|
||||
etag = model.Etag(config["IsLicensed"], config["IssuedAt"])
|
||||
}
|
||||
|
||||
if HandleEtag(etag, w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set(model.HEADER_ETAG_SERVER, etag)
|
||||
|
||||
w.Write([]byte(model.MapToJson(config)))
|
||||
}
|
||||
|
||||
22
api/license_test.go
Обычный файл
22
api/license_test.go
Обычный файл
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetLicenceConfig(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
if result, err := Client.GetClientLicenceConfig(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
cfg := result.Data.(map[string]string)
|
||||
|
||||
if _, ok := cfg["IsLicensed"]; !ok {
|
||||
t.Fatal(cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
343
api/oauth.go
343
api/oauth.go
@@ -5,12 +5,15 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func InitOAuth(r *mux.Router) {
|
||||
@@ -20,6 +23,17 @@ func InitOAuth(r *mux.Router) {
|
||||
|
||||
sr.Handle("/register", ApiUserRequired(registerOAuthApp)).Methods("POST")
|
||||
sr.Handle("/allow", ApiUserRequired(allowOAuth)).Methods("GET")
|
||||
sr.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET")
|
||||
sr.Handle("/{service:[A-Za-z]+}/login", AppHandlerIndependent(loginWithOAuth)).Methods("GET")
|
||||
sr.Handle("/{service:[A-Za-z]+}/signup", AppHandlerIndependent(signupWithOAuth)).Methods("GET")
|
||||
sr.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET")
|
||||
sr.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST")
|
||||
|
||||
// Also handle this a the old routes remove soon apiv2?
|
||||
mr := Srv.Router
|
||||
mr.Handle("/authorize", ApiUserRequired(authorizeOAuth)).Methods("GET")
|
||||
mr.Handle("/access_token", ApiAppHandler(getAccessToken)).Methods("POST")
|
||||
mr.Handle("/{service:[A-Za-z]+}/complete", AppHandlerIndependent(completeOAuth)).Methods("GET")
|
||||
}
|
||||
|
||||
func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -163,3 +177,328 @@ func GetAuthData(code string) *model.AuthData {
|
||||
return result.Data.(*model.AuthData)
|
||||
}
|
||||
}
|
||||
|
||||
func completeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
|
||||
code := r.URL.Query().Get("code")
|
||||
state := r.URL.Query().Get("state")
|
||||
|
||||
uri := c.GetSiteURL() + "/api/v1/oauth/" + service + "/complete"
|
||||
|
||||
if body, team, props, err := AuthorizeOAuthUser(service, code, state, uri); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
action := props["action"]
|
||||
switch action {
|
||||
case model.OAUTH_ACTION_SIGNUP:
|
||||
CreateOAuthUser(c, w, r, service, body, team)
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect)
|
||||
}
|
||||
break
|
||||
case model.OAUTH_ACTION_LOGIN:
|
||||
LoginByOAuth(c, w, r, service, body, team)
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect)
|
||||
}
|
||||
break
|
||||
case model.OAUTH_ACTION_EMAIL_TO_SSO:
|
||||
CompleteSwitchWithOAuth(c, w, r, service, body, team, props["email"])
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name+"/login?extra=signin_change", http.StatusTemporaryRedirect)
|
||||
}
|
||||
break
|
||||
case model.OAUTH_ACTION_SSO_TO_EMAIL:
|
||||
LoginByOAuth(c, w, r, service, body, team)
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name+"/"+"/claim?email="+url.QueryEscape(props["email"]), http.StatusTemporaryRedirect)
|
||||
}
|
||||
break
|
||||
default:
|
||||
LoginByOAuth(c, w, r, service, body, team)
|
||||
if c.Err == nil {
|
||||
http.Redirect(w, r, GetProtocol(r)+"://"+r.Host+"/"+team.Name, http.StatusTemporaryRedirect)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func authorizeOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||
c.Err = model.NewLocAppError("authorizeOAuth", "web.authorize_oauth.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
responseType := r.URL.Query().Get("response_type")
|
||||
clientId := r.URL.Query().Get("client_id")
|
||||
redirect := r.URL.Query().Get("redirect_uri")
|
||||
scope := r.URL.Query().Get("scope")
|
||||
state := r.URL.Query().Get("state")
|
||||
|
||||
if len(responseType) == 0 || len(clientId) == 0 || len(redirect) == 0 {
|
||||
c.Err = model.NewLocAppError("authorizeOAuth", "web.authorize_oauth.missing.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
var app *model.OAuthApp
|
||||
if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
app = result.Data.(*model.OAuthApp)
|
||||
}
|
||||
|
||||
var team *model.Team
|
||||
if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
page := utils.NewHTMLTemplate("authorize", c.Locale)
|
||||
page.Props["Title"] = c.T("web.authorize_oauth.title")
|
||||
page.Props["TeamName"] = team.Name
|
||||
page.Props["AppName"] = app.Name
|
||||
page.Props["ResponseType"] = responseType
|
||||
page.Props["ClientId"] = clientId
|
||||
page.Props["RedirectUri"] = redirect
|
||||
page.Props["Scope"] = scope
|
||||
page.Props["State"] = state
|
||||
if err := page.RenderToWriter(w); err != nil {
|
||||
c.SetUnknownError(page.TemplateName, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func getAccessToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
grantType := r.FormValue("grant_type")
|
||||
if grantType != model.ACCESS_TOKEN_GRANT_TYPE {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.bad_grant.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
clientId := r.FormValue("client_id")
|
||||
if len(clientId) != 26 {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.bad_client_id.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
secret := r.FormValue("client_secret")
|
||||
if len(secret) == 0 {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.bad_client_secret.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
code := r.FormValue("code")
|
||||
if len(code) == 0 {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.missing_code.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
redirectUri := r.FormValue("redirect_uri")
|
||||
|
||||
achan := Srv.Store.OAuth().GetApp(clientId)
|
||||
tchan := Srv.Store.OAuth().GetAccessDataByAuthCode(code)
|
||||
|
||||
authData := GetAuthData(code)
|
||||
|
||||
if authData == nil {
|
||||
c.LogAudit("fail - invalid auth code")
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.expired_code.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
uchan := Srv.Store.User().Get(authData.UserId)
|
||||
|
||||
if authData.IsExpired() {
|
||||
c.LogAudit("fail - auth code expired")
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.expired_code.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
if authData.RedirectUri != redirectUri {
|
||||
c.LogAudit("fail - redirect uri provided did not match previous redirect uri")
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.redirect_uri.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
if !model.ComparePassword(code, fmt.Sprintf("%v:%v:%v:%v", clientId, redirectUri, authData.CreateAt, authData.UserId)) {
|
||||
c.LogAudit("fail - auth code is invalid")
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.expired_code.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
var app *model.OAuthApp
|
||||
if result := <-achan; result.Err != nil {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.credentials.app_error", nil, "")
|
||||
return
|
||||
} else {
|
||||
app = result.Data.(*model.OAuthApp)
|
||||
}
|
||||
|
||||
if !model.ComparePassword(app.ClientSecret, secret) {
|
||||
c.LogAudit("fail - invalid client credentials")
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.credentials.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
callback := redirectUri
|
||||
if len(callback) == 0 {
|
||||
callback = app.CallbackUrls[0]
|
||||
}
|
||||
|
||||
if result := <-tchan; result.Err != nil {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.internal.app_error", nil, "")
|
||||
return
|
||||
} else if result.Data != nil {
|
||||
c.LogAudit("fail - auth code has been used previously")
|
||||
accessData := result.Data.(*model.AccessData)
|
||||
|
||||
// Revoke access token, related auth code, and session from DB as well as from cache
|
||||
if err := RevokeAccessToken(accessData.Token); err != nil {
|
||||
l4g.Error(utils.T("web.get_access_token.revoking.error") + err.Message)
|
||||
}
|
||||
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.exchanged.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if result := <-uchan; result.Err != nil {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.internal_user.app_error", nil, "")
|
||||
return
|
||||
} else {
|
||||
user = result.Data.(*model.User)
|
||||
}
|
||||
|
||||
session := &model.Session{UserId: user.Id, TeamId: user.TeamId, Roles: user.Roles, IsOAuth: true}
|
||||
|
||||
if result := <-Srv.Store.Session().Save(session); result.Err != nil {
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.internal_session.app_error", nil, "")
|
||||
return
|
||||
} else {
|
||||
session = result.Data.(*model.Session)
|
||||
AddSessionToCache(session)
|
||||
}
|
||||
|
||||
accessData := &model.AccessData{AuthCode: authData.Code, Token: session.Token, RedirectUri: callback}
|
||||
|
||||
if result := <-Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil {
|
||||
l4g.Error(result.Err)
|
||||
c.Err = model.NewLocAppError("getAccessToken", "web.get_access_token.internal_saving.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
accessRsp := &model.AccessResponse{AccessToken: session.Token, TokenType: model.ACCESS_TOKEN_TYPE, ExpiresIn: int32(*utils.Cfg.ServiceSettings.SessionLengthSSOInDays * 60 * 60 * 24)}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
|
||||
c.LogAuditWithUserId(user.Id, "success")
|
||||
|
||||
w.Write([]byte(accessRsp.ToJson()))
|
||||
}
|
||||
|
||||
func loginWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
loginHint := r.URL.Query().Get("login_hint")
|
||||
teamName := r.URL.Query().Get("team")
|
||||
|
||||
if len(teamName) == 0 {
|
||||
c.Err = model.NewLocAppError("loginWithOAuth", "web.login_with_oauth.invalid_team.app_error", nil, "team_name="+teamName)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure team exists
|
||||
if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
}
|
||||
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAUTH_ACTION_LOGIN
|
||||
|
||||
if authUrl, err := GetAuthorizationCode(c, service, teamName, stateProps, loginHint); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, authUrl, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func signupWithOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
service := params["service"]
|
||||
teamName := r.URL.Query().Get("team")
|
||||
|
||||
if !utils.Cfg.TeamSettings.EnableUserCreation {
|
||||
c.Err = model.NewLocAppError("signupTeam", "web.singup_with_oauth.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if len(teamName) == 0 {
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_team.app_error", nil, "team_name="+teamName)
|
||||
c.Err.StatusCode = http.StatusBadRequest
|
||||
return
|
||||
}
|
||||
|
||||
hash := r.URL.Query().Get("h")
|
||||
|
||||
var team *model.Team
|
||||
if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
if IsVerifyHashRequired(nil, team, hash) {
|
||||
data := r.URL.Query().Get("d")
|
||||
props := model.MapFromJson(strings.NewReader(data))
|
||||
|
||||
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_link.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
t, err := strconv.ParseInt(props["time"], 10, 64)
|
||||
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.expired_link.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
if team.Id != props["id"] {
|
||||
c.Err = model.NewLocAppError("signupWithOAuth", "web.singup_with_oauth.invalid_team.app_error", nil, data)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stateProps := map[string]string{}
|
||||
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
|
||||
|
||||
if authUrl, err := GetAuthorizationCode(c, service, teamName, stateProps, ""); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
} else {
|
||||
http.Redirect(w, r, authUrl, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,7 +419,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, 0, c.T, c.Locale}
|
||||
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, c.T, c.Locale}
|
||||
|
||||
if text, ok := respProps["text"]; ok {
|
||||
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil {
|
||||
@@ -604,12 +604,13 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
year := fmt.Sprintf("%d", tm.Year())
|
||||
zone, _ := tm.Zone()
|
||||
|
||||
subjectPage := NewServerTemplatePage("post_subject", profileMap[id].Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("post_subject", profileMap[id].Locale)
|
||||
subjectPage.Props["Subject"] = userLocale("api.templates.post_subject",
|
||||
map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName,
|
||||
"Month": month[:3], "Day": day, "Year": year})
|
||||
subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName
|
||||
|
||||
bodyPage := NewServerTemplatePage("post_body", profileMap[id].Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("post_body", profileMap[id].Locale)
|
||||
bodyPage.Props["SiteURL"] = c.GetSiteURL()
|
||||
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
|
||||
bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
|
||||
|
||||
114
api/team.go
114
api/team.go
@@ -29,13 +29,12 @@ func InitTeam(r *mux.Router) {
|
||||
sr.Handle("/create_with_ldap", ApiAppHandler(createTeamWithLdap)).Methods("POST")
|
||||
sr.Handle("/create_with_sso/{service:[A-Za-z]+}", ApiAppHandler(createTeamFromSSO)).Methods("POST")
|
||||
sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST")
|
||||
sr.Handle("/all", ApiUserRequired(getAll)).Methods("GET")
|
||||
sr.Handle("/all", ApiAppHandler(getAll)).Methods("GET")
|
||||
sr.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST")
|
||||
sr.Handle("/find_teams", ApiAppHandler(findTeams)).Methods("POST")
|
||||
sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST")
|
||||
sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
|
||||
sr.Handle("/update", ApiUserRequired(updateTeam)).Methods("POST")
|
||||
sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET")
|
||||
sr.Handle("/get_invite_info", ApiAppHandler(getInviteInfo)).Methods("POST")
|
||||
// These should be moved to the global admain console
|
||||
sr.Handle("/import_team", ApiUserRequired(importTeam)).Methods("POST")
|
||||
sr.Handle("/export_team", ApiUserRequired(exportTeam)).Methods("GET")
|
||||
@@ -60,11 +59,11 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
subjectPage := NewServerTemplatePage("signup_team_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("signup_team_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.signup_team_subject",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("signup_team_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("signup_team_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = c.GetSiteURL()
|
||||
bodyPage.Props["Title"] = c.T("api.templates.signup_team_body.title")
|
||||
bodyPage.Props["Button"] = c.T("api.templates.signup_team_body.button")
|
||||
@@ -86,7 +85,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if !utils.Cfg.EmailSettings.RequireEmailVerification {
|
||||
m["follow_link"] = bodyPage.Props["Link"]
|
||||
m["follow_link"] = fmt.Sprintf("/signup_team_complete/?d=%s&h=%s", url.QueryEscape(data), url.QueryEscape(hash))
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", " *")
|
||||
@@ -147,7 +146,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
data := map[string]string{"follow_link": c.GetSiteURL() + "/" + rteam.Name + "/signup/" + service}
|
||||
data := map[string]string{"follow_link": c.GetSiteURL() + "/api/v1/oauth/" + service + "/signup?team=" + rteam.Name}
|
||||
w.Write([]byte(model.MapToJson(data)))
|
||||
|
||||
}
|
||||
@@ -391,10 +390,6 @@ func isTeamCreationAllowed(c *Context, email string) bool {
|
||||
}
|
||||
|
||||
func getAll(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !c.HasSystemAdminPermissions("getLogs") {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().GetAll(); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
@@ -403,6 +398,9 @@ func getAll(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
m := make(map[string]*model.Team)
|
||||
for _, v := range teams {
|
||||
m[v.Id] = v
|
||||
if !c.IsSystemAdmin() {
|
||||
m[v.Id].SanitizeForNotLoggedIn()
|
||||
}
|
||||
}
|
||||
|
||||
w.Write([]byte(model.TeamMapToJson(m)))
|
||||
@@ -473,74 +471,6 @@ func FindTeamByName(c *Context, name string, all string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func findTeams(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
email := strings.ToLower(strings.TrimSpace(m["email"]))
|
||||
|
||||
if email == "" {
|
||||
c.SetInvalidParam("findTeam", "email")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
teams := result.Data.([]*model.Team)
|
||||
m := make(map[string]*model.Team)
|
||||
for _, v := range teams {
|
||||
v.Sanitize()
|
||||
m[v.Id] = v
|
||||
}
|
||||
|
||||
w.Write([]byte(model.TeamMapToJson(m)))
|
||||
}
|
||||
}
|
||||
|
||||
func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
m := model.MapFromJson(r.Body)
|
||||
|
||||
email := strings.ToLower(strings.TrimSpace(m["email"]))
|
||||
|
||||
if email == "" {
|
||||
c.SetInvalidParam("findTeam", "email")
|
||||
return
|
||||
}
|
||||
|
||||
siteURL := c.GetSiteURL()
|
||||
subjectPage := NewServerTemplatePage("find_teams_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.find_teams_subject",
|
||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("find_teams_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.find_teams_body.title")
|
||||
bodyPage.Props["Found"] = c.T("api.templates.find_teams_body.found")
|
||||
bodyPage.Props["NotFound"] = c.T("api.templates.find_teams_body.not_found")
|
||||
|
||||
if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
} else {
|
||||
teams := result.Data.([]*model.Team)
|
||||
|
||||
// the template expects Props to be a map with team names as the keys and the team url as the value
|
||||
props := make(map[string]string)
|
||||
for _, team := range teams {
|
||||
props[team.Name] = c.GetTeamURLFromTeam(team)
|
||||
}
|
||||
bodyPage.Extra = props
|
||||
|
||||
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
|
||||
l4g.Error(utils.T("api.team.email_teams.sending.error"), err)
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(m)))
|
||||
}
|
||||
}
|
||||
|
||||
func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
invites := model.InvitesFromJson(r.Body)
|
||||
if len(invites.Invites) == 0 {
|
||||
@@ -600,11 +530,11 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str
|
||||
senderRole = c.T("api.team.invite_members.member")
|
||||
}
|
||||
|
||||
subjectPage := NewServerTemplatePage("invite_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("invite_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.invite_subject",
|
||||
map[string]interface{}{"SenderName": sender, "TeamDisplayName": team.DisplayName, "SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("invite_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("invite_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = c.GetSiteURL()
|
||||
bodyPage.Props["Title"] = c.T("api.templates.invite_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(c.T("api.templates.invite_body.info",
|
||||
@@ -813,3 +743,25 @@ func exportTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
m := model.MapFromJson(r.Body)
|
||||
inviteId := m["invite_id"]
|
||||
|
||||
if result := <-Srv.Store.Team().GetByInviteId(inviteId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team := result.Data.(*model.Team)
|
||||
if !(team.Type == model.TEAM_OPEN) {
|
||||
c.Err = model.NewLocAppError("getInviteInfo", "api.team.get_invite_info.not_open_team", nil, "id="+inviteId)
|
||||
return
|
||||
}
|
||||
|
||||
result := map[string]string{}
|
||||
result["display_name"] = team.DisplayName
|
||||
result["name"] = team.Name
|
||||
result["id"] = team.Id
|
||||
w.Write([]byte(model.MapToJson(result)))
|
||||
}
|
||||
}
|
||||
|
||||
119
api/team_test.go
119
api/team_test.go
@@ -108,37 +108,10 @@ func TestCreateTeam(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindTeamByEmail(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
if r1, err := Client.FindTeams(user.Email); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
teams := r1.Data.(map[string]*model.Team)
|
||||
if teams[team.Id].Name != team.Name {
|
||||
t.Fatal()
|
||||
}
|
||||
if teams[team.Id].DisplayName != team.DisplayName {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := Client.FindTeams("missing"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllTeams(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN, AllowTeamListing: true}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
@@ -147,8 +120,22 @@ func TestGetAllTeams(t *testing.T) {
|
||||
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
if _, err := Client.GetAllTeams(); err == nil {
|
||||
t.Fatal("you shouldn't have permissions")
|
||||
enableIncomingHooks := *utils.Cfg.TeamSettings.EnableTeamListing
|
||||
defer func() {
|
||||
*utils.Cfg.TeamSettings.EnableTeamListing = enableIncomingHooks
|
||||
}()
|
||||
*utils.Cfg.TeamSettings.EnableTeamListing = true
|
||||
|
||||
if r1, err := Client.GetAllTeams(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
teams := r1.Data.(map[string]*model.Team)
|
||||
if teams[team.Id].Name != team.Name {
|
||||
t.Fatal()
|
||||
}
|
||||
if teams[team.Id].Email != "" {
|
||||
t.Fatal("Non admin users shoudn't get full listings")
|
||||
}
|
||||
}
|
||||
|
||||
c := &Context{}
|
||||
@@ -165,6 +152,9 @@ func TestGetAllTeams(t *testing.T) {
|
||||
if teams[team.Id].Name != team.Name {
|
||||
t.Fatal()
|
||||
}
|
||||
if teams[team.Id].Email != team.Email {
|
||||
t.Fatal()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,75 +197,6 @@ func TestTeamPermDelete(t *testing.T) {
|
||||
Client.ClearOAuthToken()
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
XXXXXX investigate and fix failing test
|
||||
|
||||
func TestFindTeamByDomain(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
if r1, err := Client.FindTeamByDomain(team.Name, false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if !val {
|
||||
t.Fatal("should be a valid domain")
|
||||
}
|
||||
}
|
||||
|
||||
if r1, err := Client.FindTeamByDomain(team.Name, true); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if !val {
|
||||
t.Fatal("should be a valid domain")
|
||||
}
|
||||
}
|
||||
|
||||
if r1, err := Client.FindTeamByDomain("a"+model.NewId()+"a", false); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
val := r1.Data.(bool)
|
||||
if val {
|
||||
t.Fatal("shouldn't be a valid domain")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func TestFindTeamByEmailSend(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
if _, err := Client.FindTeamsSendEmail(user.Email); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
}
|
||||
|
||||
if _, err := Client.FindTeamsSendEmail("missing"); err != nil {
|
||||
|
||||
// It should actually succeed at sending the email since it doesn't exist
|
||||
if !strings.Contains(err.DetailedError, "Failed to add to email address") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInviteMembers(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
{{define "email_change_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Html.Info}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
@@ -1 +0,0 @@
|
||||
{{define "email_change_subject"}}[{{.ClientCfg.SiteName}}] {{.Props.Subject}}{{end}}
|
||||
@@ -1,44 +0,0 @@
|
||||
{{define "email_change_verify_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Props.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.VerifyUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.VerifyButton}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
@@ -1 +0,0 @@
|
||||
{{define "email_change_verify_subject"}}[{{.ClientCfg.SiteName}}] {{.Props.Subject}}{{end}}
|
||||
@@ -1,13 +0,0 @@
|
||||
{{define "email_footer"}}
|
||||
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.Props.SiteURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
<p style="padding: 0 50px;">
|
||||
(c) 2015 Mattermost, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
|
||||
{{.Props.Footer}}
|
||||
</p>
|
||||
</td>
|
||||
|
||||
{{end}}
|
||||
@@ -1,7 +0,0 @@
|
||||
{{define "email_info"}}
|
||||
|
||||
<td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
|
||||
{{.Html.EmailInfo}}
|
||||
</td>
|
||||
|
||||
{{end}}
|
||||
@@ -1,37 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title><span class='fa fa-chevron-left'></span>Back - Error</title>
|
||||
|
||||
<link rel="stylesheet" href="/static/css/bootstrap-3.3.5.min.css">
|
||||
<link rel="stylesheet" href="/static/css/jasny-bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<script src="/static/js/react-with-addons-0.13.3.min.js"></script>
|
||||
<script src="/static/js/jquery-1.11.1.min.js"></script>
|
||||
<script src="/static/js/bootstrap-3.3.5.min.js"></script>
|
||||
<script src="/static/js/react-bootstrap-0.25.1.min.js"></script>
|
||||
|
||||
<link id="favicon" rel="icon" href="/static/images/favicon/favicon-16x16.png" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="/static/images/favicon/favicon-16x16.png" type="image/x-icon">
|
||||
<link href='/static/css/google-fonts.css' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body class="white error">
|
||||
<div class="container-fluid">
|
||||
<div class="error__container">
|
||||
<div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div>
|
||||
<h2>{{.Props.Title}}</h2>
|
||||
<p>{{ .Props.Message }}</p>
|
||||
<a href="{{.Props.SiteURL}}">{{.Props.Link}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
var details = "{{ .Details }}";
|
||||
if (details.length > 0) {
|
||||
console.log("error details: " + details);
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,52 +0,0 @@
|
||||
{{define "find_teams_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.ClientCfg.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{ if .Extra }}
|
||||
{{.Props.Found}}<br>
|
||||
{{range $index, $element := .Extra}}
|
||||
<a href="{{ $element }}" style="text-decoration: none; color:#2389D7;">{{ $index }}</a><br>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{.Props.NotFound}}
|
||||
{{ end }}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "find_teams_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,46 +0,0 @@
|
||||
{{define "invite_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Html.Info}}</p>
|
||||
<p style="margin: 30px 0 15px">
|
||||
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
<br/>
|
||||
<p>{{.Html.ExtraInfo}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
@@ -1 +0,0 @@
|
||||
{{define "invite_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,43 +0,0 @@
|
||||
{{define "password_change_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Html.Info}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "password_change_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,45 +0,0 @@
|
||||
{{define "post_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.BodyText}}</h2>
|
||||
<p>{{.Html.Info}}<br><pre style="text-align:left;font-family: 'Lato', sans-serif; white-space: pre-wrap; white-space: -moz-pre-wrap; white-space: -pre-wrap; white-space: -o-pre-wrap; word-wrap: break-word;">{{.Props.PostMessage}}</pre></p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.TeamLink}}" style="background: #2389D7; display: inline-block; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 170px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "post_subject"}}[{{.ClientCfg.SiteName}}] {{.Props.Subject}}{{end}}
|
||||
@@ -1,46 +0,0 @@
|
||||
{{define "reset_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Html.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.ResetUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "reset_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,43 +0,0 @@
|
||||
{{define "signin_change_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Html.Info}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "signin_change_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,44 +0,0 @@
|
||||
{{define "signup_team_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p style="margin: 20px 0 25px">
|
||||
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
{{.Html.Info}}<br></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
@@ -1 +0,0 @@
|
||||
{{define "signup_team_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,44 +0,0 @@
|
||||
{{define "verify_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Props.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.VerifyUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_info" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
@@ -1 +0,0 @@
|
||||
{{define "verify_subject"}}{{.Props.Subject}}{{end}}
|
||||
@@ -1,51 +0,0 @@
|
||||
{{define "welcome_body"}}
|
||||
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="margin-top: 20px; line-height: 1.7; color: #555;">
|
||||
<tr>
|
||||
<td>
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 660px; font-family: Helvetica, Arial, sans-serif; font-size: 14px; background: #FFF;">
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.Props.SiteURL}}/static/images/logo-email.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
|
||||
{{if .Props.VerifyUrl }}
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
|
||||
<p>{{.Props.Info}}</p>
|
||||
<p style="margin: 20px 0 15px">
|
||||
<a href="{{.Props.VerifyUrl}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">{{.Props.Button}}</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
<tr>
|
||||
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
|
||||
<h2 style="font-weight: normal; margin-top: 25px; line-height: 1.5;">{{.Props.Info2}}</h2>
|
||||
<a href="{{.Props.TeamURL}}">{{.Props.TeamURL}}</a>
|
||||
<p>{{.Props.Info3}}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{template "email_footer" . }}
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{end}}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{define "welcome_subject"}}{{.Props.Subject}}{{end}}
|
||||
146
api/user.go
146
api/user.go
@@ -53,10 +53,13 @@ func InitUser(r *mux.Router) {
|
||||
sr.Handle("/attach_device", ApiUserRequired(attachDeviceId)).Methods("POST")
|
||||
sr.Handle("/switch_to_sso", ApiAppHandler(switchToSSO)).Methods("POST")
|
||||
sr.Handle("/switch_to_email", ApiUserRequired(switchToEmail)).Methods("POST")
|
||||
sr.Handle("/verify_email", ApiAppHandler(verifyEmail)).Methods("POST")
|
||||
sr.Handle("/resend_verification", ApiAppHandler(resendVerification)).Methods("POST")
|
||||
|
||||
sr.Handle("/newimage", ApiUserRequired(uploadProfileImage)).Methods("POST")
|
||||
|
||||
sr.Handle("/me", ApiAppHandler(getMe)).Methods("GET")
|
||||
sr.Handle("/me_logged_in", ApiAppHandler(getMeLoggedIn)).Methods("GET")
|
||||
sr.Handle("/status", ApiUserRequiredActivity(getStatuses, false)).Methods("POST")
|
||||
sr.Handle("/profiles", ApiUserRequired(getProfiles)).Methods("GET")
|
||||
sr.Handle("/profiles/{id:[A-Za-z0-9]+}", ApiUserRequired(getProfiles)).Methods("GET")
|
||||
@@ -315,10 +318,10 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
|
||||
func sendWelcomeEmailAndForget(c *Context, userId, email, teamName, teamDisplayName, siteURL, teamURL string, verified bool) {
|
||||
go func() {
|
||||
|
||||
subjectPage := NewServerTemplatePage("welcome_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("welcome_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.welcome_subject", map[string]interface{}{"TeamDisplayName": teamDisplayName})
|
||||
|
||||
bodyPage := NewServerTemplatePage("welcome_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("welcome_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.welcome_body.title", map[string]interface{}{"TeamDisplayName": teamDisplayName})
|
||||
bodyPage.Props["Info"] = c.T("api.templates.welcome_body.info")
|
||||
@@ -328,7 +331,7 @@ func sendWelcomeEmailAndForget(c *Context, userId, email, teamName, teamDisplayN
|
||||
bodyPage.Props["TeamURL"] = teamURL
|
||||
|
||||
if !verified {
|
||||
link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, email)
|
||||
link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, email)
|
||||
bodyPage.Props["VerifyUrl"] = link
|
||||
}
|
||||
|
||||
@@ -380,13 +383,13 @@ func addDirectChannelsAndForget(user *model.User) {
|
||||
func SendVerifyEmailAndForget(c *Context, userId, userEmail, teamName, teamDisplayName, siteURL, teamURL string) {
|
||||
go func() {
|
||||
|
||||
link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, userEmail)
|
||||
link := fmt.Sprintf("%s/do_verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, userEmail)
|
||||
|
||||
subjectPage := NewServerTemplatePage("verify_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("verify_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.verify_subject",
|
||||
map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("verify_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("verify_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.verify_body.title", map[string]interface{}{"TeamDisplayName": teamDisplayName})
|
||||
bodyPage.Props["Info"] = c.T("api.templates.verify_body.info")
|
||||
@@ -621,31 +624,17 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
|
||||
w.Header().Set(model.HEADER_TOKEN, session.Token)
|
||||
|
||||
tokens := GetMultiSessionCookieTokens(r)
|
||||
multiToken := ""
|
||||
seen := make(map[string]string)
|
||||
seen[session.TeamId] = session.TeamId
|
||||
for _, token := range tokens {
|
||||
s := GetSession(token)
|
||||
if s != nil && !s.IsExpired() && seen[s.TeamId] == "" {
|
||||
multiToken += " " + token
|
||||
seen[s.TeamId] = s.TeamId
|
||||
}
|
||||
}
|
||||
|
||||
multiToken = strings.TrimSpace(multiToken + " " + session.Token)
|
||||
expiresAt := time.Unix(model.GetMillis()/1000+int64(maxAge), 0)
|
||||
|
||||
multiSessionCookie := &http.Cookie{
|
||||
sessionCookie := &http.Cookie{
|
||||
Name: model.SESSION_COOKIE_TOKEN,
|
||||
Value: multiToken,
|
||||
Value: session.Token,
|
||||
Path: "/",
|
||||
MaxAge: maxAge,
|
||||
Expires: expiresAt,
|
||||
HttpOnly: true,
|
||||
}
|
||||
|
||||
http.SetCookie(w, multiSessionCookie)
|
||||
http.SetCookie(w, sessionCookie)
|
||||
|
||||
c.Session = *session
|
||||
c.LogAuditWithUserId(user.Id, "success")
|
||||
@@ -902,6 +891,26 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func getMeLoggedIn(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
data := make(map[string]string)
|
||||
data["logged_in"] = "false"
|
||||
data["team_name"] = ""
|
||||
|
||||
if len(c.Session.UserId) != 0 {
|
||||
teamChan := Srv.Store.Team().Get(c.Session.TeamId)
|
||||
var team *model.Team
|
||||
if tr := <-teamChan; tr.Err != nil {
|
||||
c.Err = tr.Err
|
||||
return
|
||||
} else {
|
||||
team = tr.Data.(*model.Team)
|
||||
}
|
||||
data["logged_in"] = "true"
|
||||
data["team_name"] = team.Name
|
||||
}
|
||||
w.Write([]byte(model.MapToJson(data)))
|
||||
}
|
||||
|
||||
func getUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
id := params["id"]
|
||||
@@ -1622,12 +1631,12 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
data := model.MapToJson(newProps)
|
||||
hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.PasswordResetSalt))
|
||||
|
||||
link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash))
|
||||
link := fmt.Sprintf("%s/reset_password_complete?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash))
|
||||
|
||||
subjectPage := NewServerTemplatePage("reset_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("reset_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.reset_subject")
|
||||
|
||||
bodyPage := NewServerTemplatePage("reset_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("reset_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = c.GetSiteURL()
|
||||
bodyPage.Props["Title"] = c.T("api.templates.reset_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(c.T("api.templates.reset_body.info"))
|
||||
@@ -1743,11 +1752,11 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
func sendPasswordChangeEmailAndForget(c *Context, email, teamDisplayName, teamURL, siteURL, method string) {
|
||||
go func() {
|
||||
|
||||
subjectPage := NewServerTemplatePage("password_change_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("password_change_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.password_change_subject",
|
||||
map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("password_change_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("password_change_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.password_change_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(c.T("api.templates.password_change_body.info",
|
||||
@@ -1763,11 +1772,12 @@ func sendPasswordChangeEmailAndForget(c *Context, email, teamDisplayName, teamUR
|
||||
func sendEmailChangeEmailAndForget(c *Context, oldEmail, newEmail, teamDisplayName, teamURL, siteURL string) {
|
||||
go func() {
|
||||
|
||||
subjectPage := NewServerTemplatePage("email_change_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("email_change_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.email_change_subject",
|
||||
map[string]interface{}{"TeamDisplayName": teamDisplayName})
|
||||
subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName
|
||||
|
||||
bodyPage := NewServerTemplatePage("email_change_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("email_change_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.email_change_body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(c.T("api.templates.email_change_body.info",
|
||||
@@ -1785,11 +1795,12 @@ func SendEmailChangeVerifyEmailAndForget(c *Context, userId, newUserEmail, teamN
|
||||
|
||||
link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, newUserEmail)
|
||||
|
||||
subjectPage := NewServerTemplatePage("email_change_verify_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("email_change_verify_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.email_change_verify_subject",
|
||||
map[string]interface{}{"TeamDisplayName": teamDisplayName})
|
||||
subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName
|
||||
|
||||
bodyPage := NewServerTemplatePage("email_change_verify_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("email_change_verify_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.email_change_verify_body.title")
|
||||
bodyPage.Props["Info"] = c.T("api.templates.email_change_verify_body.info",
|
||||
@@ -1918,7 +1929,7 @@ func GetAuthorizationCode(c *Context, service, teamName string, props map[string
|
||||
props["team"] = teamName
|
||||
state := b64.StdEncoding.EncodeToString([]byte(model.MapToJson(props)))
|
||||
|
||||
redirectUri := c.GetSiteURL() + "/signup/" + service + "/complete" // Remove /signup after a few releases (~1.8)
|
||||
redirectUri := c.GetSiteURL() + "/api/v1/oauth/" + service + "/complete"
|
||||
|
||||
authUrl := endpoint + "?response_type=code&client_id=" + clientId + "&redirect_uri=" + url.QueryEscape(redirectUri) + "&state=" + url.QueryEscape(state)
|
||||
|
||||
@@ -2216,11 +2227,11 @@ func switchToEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
func sendSignInChangeEmailAndForget(c *Context, email, teamDisplayName, teamURL, siteURL, method string) {
|
||||
go func() {
|
||||
|
||||
subjectPage := NewServerTemplatePage("signin_change_subject", c.Locale)
|
||||
subjectPage := utils.NewHTMLTemplate("signin_change_subject", c.Locale)
|
||||
subjectPage.Props["Subject"] = c.T("api.templates.singin_change_email.subject",
|
||||
map[string]interface{}{"TeamDisplayName": teamDisplayName, "SiteName": utils.ClientCfg["SiteName"]})
|
||||
|
||||
bodyPage := NewServerTemplatePage("signin_change_body", c.Locale)
|
||||
bodyPage := utils.NewHTMLTemplate("signin_change_body", c.Locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = c.T("api.templates.signin_change_email.body.title")
|
||||
bodyPage.Html["Info"] = template.HTML(c.T("api.templates.singin_change_email.body.info",
|
||||
@@ -2232,3 +2243,68 @@ func sendSignInChangeEmailAndForget(c *Context, email, teamDisplayName, teamURL,
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
func verifyEmail(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
userId := props["uid"]
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("verifyEmail", "uid")
|
||||
return
|
||||
}
|
||||
|
||||
hashedId := props["hid"]
|
||||
if len(hashedId) == 0 {
|
||||
c.SetInvalidParam("verifyEmail", "hid")
|
||||
return
|
||||
}
|
||||
|
||||
if model.ComparePassword(hashedId, userId) {
|
||||
if c.Err = (<-Srv.Store.User().VerifyEmail(userId)).Err; c.Err != nil {
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("Email Verified")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Err = model.NewLocAppError("verifyEmail", "api.user.verify_email.bad_link.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
}
|
||||
|
||||
func resendVerification(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
teamName := props["team_name"]
|
||||
if len(teamName) == 0 {
|
||||
c.SetInvalidParam("resendVerification", "team_name")
|
||||
return
|
||||
}
|
||||
|
||||
email := props["email"]
|
||||
if len(email) == 0 {
|
||||
c.SetInvalidParam("resendVerification", "email")
|
||||
return
|
||||
}
|
||||
|
||||
var team *model.Team
|
||||
if result := <-Srv.Store.Team().GetByName(teamName); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
user := result.Data.(*model.User)
|
||||
|
||||
if user.LastActivityAt > 0 {
|
||||
SendEmailChangeVerifyEmailAndForget(c, user.Id, user.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team))
|
||||
} else {
|
||||
SendVerifyEmailAndForget(c, user.Id, user.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1263,3 +1263,38 @@ func TestSwitchToEmail(t *testing.T) {
|
||||
t.Fatal("should have failed - wrong user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeLoggedIn(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
rteam, _ := Client.CreateTeam(&team)
|
||||
|
||||
user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(ruser.Id))
|
||||
|
||||
Client.AuthToken = "invalid"
|
||||
|
||||
if result, err := Client.GetMeLoggedIn(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
meLoggedIn := result.Data.(map[string]string)
|
||||
|
||||
if val, ok := meLoggedIn["logged_in"]; !ok || val != "false" {
|
||||
t.Fatal("Got: " + val)
|
||||
}
|
||||
}
|
||||
|
||||
Client.LoginByEmail(team.Name, user.Email, user.Password)
|
||||
|
||||
if result, err := Client.GetMeLoggedIn(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
meLoggedIn := result.Data.(map[string]string)
|
||||
|
||||
if val, ok := meLoggedIn["logged_in"]; !ok || val != "true" {
|
||||
t.Fatal("Got: " + val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user