summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/api.go4
-rw-r--r--api/channel.go22
-rw-r--r--api/channel_benchmark_test.go2
-rw-r--r--api/channel_test.go68
-rw-r--r--api/context.go2
-rw-r--r--api/file.go20
-rw-r--r--api/post.go4
-rw-r--r--api/server.go37
-rw-r--r--api/team.go27
-rw-r--r--api/templates/email_change_body.html6
-rw-r--r--api/templates/find_teams_body.html4
-rw-r--r--api/templates/invite_body.html4
-rw-r--r--api/templates/password_change_body.html6
-rw-r--r--api/templates/post_body.html4
-rw-r--r--api/templates/reset_body.html4
-rw-r--r--api/templates/signup_team_body.html4
-rw-r--r--api/templates/verify_body.html4
-rw-r--r--api/templates/welcome_body.html6
-rw-r--r--api/user.go67
19 files changed, 222 insertions, 73 deletions
diff --git a/api/api.go b/api/api.go
index 25f3376c6..2ea27ed9f 100644
--- a/api/api.go
+++ b/api/api.go
@@ -16,10 +16,10 @@ var ServerTemplates *template.Template
type ServerTemplatePage Page
-func NewServerTemplatePage(templateName, teamURL string) *ServerTemplatePage {
+func NewServerTemplatePage(templateName, siteURL string) *ServerTemplatePage {
props := make(map[string]string)
props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl
- return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamURL: teamURL, Props: props}
+ return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, SiteURL: siteURL, Props: props}
}
func (me *ServerTemplatePage) Render() string {
diff --git a/api/channel.go b/api/channel.go
index 123fd8a35..803274d32 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -554,20 +554,29 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
id := params["id"]
sc := Srv.Store.Channel().Get(id)
- scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
- ecm := Srv.Store.Channel().GetExtraMembers(id, 20)
-
+ var channel *model.Channel
if cresult := <-sc; cresult.Err != nil {
c.Err = cresult.Err
return
- } else if cmresult := <-scm; cmresult.Err != nil {
+ } else {
+ channel = cresult.Data.(*model.Channel)
+ }
+
+ extraEtag := channel.ExtraEtag()
+ if HandleEtag(extraEtag, w, r) {
+ return
+ }
+
+ scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
+ ecm := Srv.Store.Channel().GetExtraMembers(id, 20)
+
+ if cmresult := <-scm; cmresult.Err != nil {
c.Err = cmresult.Err
return
} else if ecmresult := <-ecm; ecmresult.Err != nil {
c.Err = ecmresult.Err
return
} else {
- channel := cresult.Data.(*model.Channel)
member := cmresult.Data.(model.ChannelMember)
extraMembers := ecmresult.Data.([]model.ExtraMember)
@@ -586,6 +595,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
}
data := model.ChannelExtra{Id: channel.Id, Members: extraMembers}
+ w.Header().Set(model.HEADER_ETAG_SERVER, extraEtag)
w.Header().Set("Expires", "-1")
w.Write([]byte(data.ToJson()))
}
@@ -711,7 +721,7 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_REMOVED)
- message.Add("channel_id",id)
+ message.Add("channel_id", id)
message.Add("remover", c.Session.UserId)
PublishAndForget(message)
diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go
index 881638176..77e679c14 100644
--- a/api/channel_benchmark_test.go
+++ b/api/channel_benchmark_test.go
@@ -189,7 +189,7 @@ func BenchmarkGetChannelExtraInfo(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := range channels {
- Client.Must(Client.GetChannelExtraInfo(channels[j].Id))
+ Client.Must(Client.GetChannelExtraInfo(channels[j].Id, ""))
}
}
}
diff --git a/api/channel_test.go b/api/channel_test.go
index a8d53c4b5..d4fb11bd8 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -6,6 +6,7 @@ package api
import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
"net/http"
"testing"
"time"
@@ -543,10 +544,73 @@ func TestGetChannelExtraInfo(t *testing.T) {
channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
- rget := Client.Must(Client.GetChannelExtraInfo(channel1.Id)).Data.(*model.ChannelExtra)
- if rget.Id != channel1.Id {
+ rget := Client.Must(Client.GetChannelExtraInfo(channel1.Id, ""))
+ data := rget.Data.(*model.ChannelExtra)
+ if data.Id != channel1.Id {
t.Fatal("couldnt't get extra info")
}
+
+ //
+ // Testing etag caching
+ //
+
+ currentEtag := rget.Etag
+
+ if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, currentEtag); err != nil {
+ t.Fatal(err)
+ } else if cache_result.Data.(*model.ChannelExtra) != nil {
+ t.Log(cache_result.Data)
+ t.Fatal("response should be empty")
+ } else {
+ currentEtag = cache_result.Etag
+ }
+
+ Client2 := model.NewClient("http://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1")
+
+ user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "tester2@test.com", Nickname: "Tester 2", Password: "pwd"}
+ user2 = Client2.Must(Client2.CreateUser(user2, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user2.Id))
+
+ Client2.LoginByEmail(team.Name, user2.Email, "pwd")
+ Client2.Must(Client2.JoinChannel(channel1.Id))
+
+ if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, currentEtag); err != nil {
+ t.Fatal(err)
+ } else if cache_result.Data.(*model.ChannelExtra) == nil {
+ t.Log(cache_result.Data)
+ t.Fatal("response should not be empty")
+ } else {
+ currentEtag = cache_result.Etag
+ }
+
+ if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, currentEtag); err != nil {
+ t.Fatal(err)
+ } else if cache_result.Data.(*model.ChannelExtra) != nil {
+ t.Log(cache_result.Data)
+ t.Fatal("response should be empty")
+ } else {
+ currentEtag = cache_result.Etag
+ }
+
+ Client2.Must(Client2.LeaveChannel(channel1.Id))
+
+ if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, currentEtag); err != nil {
+ t.Fatal(err)
+ } else if cache_result.Data.(*model.ChannelExtra) == nil {
+ t.Log(cache_result.Data)
+ t.Fatal("response should not be empty")
+ } else {
+ currentEtag = cache_result.Etag
+ }
+
+ if cache_result, err := Client.GetChannelExtraInfo(channel1.Id, currentEtag); err != nil {
+ t.Fatal(err)
+ } else if cache_result.Data.(*model.ChannelExtra) != nil {
+ t.Log(cache_result.Data)
+ t.Fatal("response should be empty")
+ } else {
+ currentEtag = cache_result.Etag
+ }
}
func TestAddChannelMember(t *testing.T) {
diff --git a/api/context.go b/api/context.go
index 16da0a6eb..e3f279e90 100644
--- a/api/context.go
+++ b/api/context.go
@@ -32,7 +32,7 @@ type Page struct {
Title string
SiteName string
FeedbackEmail string
- TeamURL string
+ SiteURL string
Props map[string]string
}
diff --git a/api/file.go b/api/file.go
index 3ef50fbbd..4ec421eb9 100644
--- a/api/file.go
+++ b/api/file.go
@@ -33,7 +33,7 @@ func InitFile(r *mux.Router) {
sr := r.PathPrefix("/files").Subrouter()
sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
- sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandler(getFile)).Methods("GET")
+ sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandler(getFile)).Methods("GET", "HEAD")
sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
}
@@ -140,11 +140,18 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
// Create thumbnail
go func() {
+ thumbWidth := float64(utils.Cfg.ImageSettings.ThumbnailWidth)
+ thumbHeight := float64(utils.Cfg.ImageSettings.ThumbnailHeight)
+ imgWidth := float64(imgConfig.Width)
+ imgHeight := float64(imgConfig.Height)
+
var thumbnail image.Image
- if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) {
- thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3)
- } else {
+ if imgHeight < thumbHeight && imgWidth < thumbWidth {
thumbnail = img
+ } else if imgHeight/imgWidth < thumbHeight/thumbWidth {
+ thumbnail = resize.Resize(0, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3)
+ } else {
+ thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, 0, img, resize.Lanczos3)
}
buf := new(bytes.Buffer)
@@ -261,7 +268,10 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "max-age=2592000, public")
w.Header().Set("Content-Length", strconv.Itoa(len(f)))
- w.Write(f)
+
+ if r.Method != "HEAD" {
+ w.Write(f)
+ }
}
func asyncGetFile(path string, fileData chan []byte) {
diff --git a/api/post.go b/api/post.go
index f96320639..f6699d181 100644
--- a/api/post.go
+++ b/api/post.go
@@ -377,7 +377,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {
location, _ := time.LoadLocation("UTC")
tm := time.Unix(post.CreateAt/1000, 0).In(location)
- subjectPage := NewServerTemplatePage("post_subject", teamURL)
+ subjectPage := NewServerTemplatePage("post_subject", siteURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
subjectPage.Props["SubjectText"] = subjectText
subjectPage.Props["Month"] = tm.Month().String()[:3]
@@ -395,7 +395,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) {
continue
}
- bodyPage := NewServerTemplatePage("post_body", teamURL)
+ bodyPage := NewServerTemplatePage("post_body", siteURL)
bodyPage.Props["Nickname"] = profileMap[id].FirstName
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["ChannelName"] = channelName
diff --git a/api/server.go b/api/server.go
index 3163f79f5..3273e766c 100644
--- a/api/server.go
+++ b/api/server.go
@@ -9,7 +9,10 @@ import (
"github.com/gorilla/mux"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
+ "github.com/throttled/throttled"
+ throttledStore "github.com/throttled/throttled/store"
"net/http"
+ "strings"
"time"
)
@@ -35,10 +38,40 @@ func NewServer() {
func StartServer() {
l4g.Info("Starting Server...")
-
l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.Port)
+
+ var handler http.Handler = Srv.Router
+
+ if utils.Cfg.RateLimitSettings.UseRateLimiter {
+ l4g.Info("RateLimiter is enabled")
+
+ vary := throttled.VaryBy{}
+
+ if utils.Cfg.RateLimitSettings.VaryByRemoteAddr {
+ vary.RemoteAddr = true
+ }
+
+ if len(utils.Cfg.RateLimitSettings.VaryByHeader) > 0 {
+ vary.Headers = strings.Fields(utils.Cfg.RateLimitSettings.VaryByHeader)
+
+ if utils.Cfg.RateLimitSettings.VaryByRemoteAddr {
+ l4g.Warn("RateLimitSettings not configured properly using VaryByHeader and disabling VaryByRemoteAddr")
+ vary.RemoteAddr = false
+ }
+ }
+
+ th := throttled.RateLimit(throttled.PerSec(utils.Cfg.RateLimitSettings.PerSec), &vary, throttledStore.NewMemStore(utils.Cfg.RateLimitSettings.MemoryStoreSize))
+
+ th.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ l4g.Error("%v: code=429 ip=%v", r.URL.Path, GetIpAddress(r))
+ throttled.DefaultDeniedHandler.ServeHTTP(w, r)
+ })
+
+ handler = th.Throttle(Srv.Router)
+ }
+
go func() {
- err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, Srv.Router)
+ err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, handler)
if err != nil {
l4g.Critical("Error starting server, err:%v", err)
time.Sleep(time.Second)
diff --git a/api/team.go b/api/team.go
index c9fe42ecc..e6b8f4e14 100644
--- a/api/team.go
+++ b/api/team.go
@@ -275,11 +275,24 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
subjectPage := NewServerTemplatePage("find_teams_subject", c.GetSiteURL())
bodyPage := NewServerTemplatePage("find_teams_body", c.GetSiteURL())
- if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
- l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
- }
+ if result := <-Srv.Store.Team().GetTeamsForEmail(email); result.Err != nil {
+ c.Err = result.Err
+ } else {
+ teams := result.Data.([]*model.Team)
- w.Write([]byte(model.MapToJson(m)))
+ // the template expects Props to be a map with team names as the keys
+ props := make(map[string]string)
+ for _, team := range teams {
+ props[team.Name] = team.Name
+ }
+ bodyPage.Props = props
+
+ if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
+ l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
+ }
+
+ w.Write([]byte(model.MapToJson(m)))
+ }
}
func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -331,8 +344,6 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
func InviteMembers(c *Context, team *model.Team, user *model.User, invites []string) {
for _, invite := range invites {
if len(invite) > 0 {
- teamURL := ""
- teamURL = c.GetTeamURLFromTeam(team)
sender := user.GetDisplayName()
@@ -343,10 +354,10 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str
senderRole = "member"
}
- subjectPage := NewServerTemplatePage("invite_subject", teamURL)
+ subjectPage := NewServerTemplatePage("invite_subject", c.GetSiteURL())
subjectPage.Props["SenderName"] = sender
subjectPage.Props["TeamDisplayName"] = team.DisplayName
- bodyPage := NewServerTemplatePage("invite_body", teamURL)
+ bodyPage := NewServerTemplatePage("invite_body", c.GetSiteURL())
bodyPage.Props["TeamDisplayName"] = team.DisplayName
bodyPage.Props["SenderName"] = sender
bodyPage.Props["SenderStatus"] = senderRole
diff --git a/api/templates/email_change_body.html b/api/templates/email_change_body.html
index 439fffd5b..c4e1cf39d 100644
--- a/api/templates/email_change_body.html
+++ b/api/templates/email_change_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your email</h2>
- <p>You updated your email for {{.Props.TeamDisplayName}} on {{ .TeamURL }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
+ <p>You updated your email for {{.Props.TeamDisplayName}} on {{ .Props.TeamURL }}<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/find_teams_body.html b/api/templates/find_teams_body.html
index a73ed0ad4..bd151a819 100644
--- a/api/templates/find_teams_body.html
+++ b/api/templates/find_teams_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -42,7 +42,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/invite_body.html b/api/templates/invite_body.html
index ad0658e3d..568a0d893 100644
--- a/api/templates/invite_body.html
+++ b/api/templates/invite_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/password_change_body.html b/api/templates/password_change_body.html
index 1d4a6e1c8..6fc9f2822 100644
--- a/api/templates/password_change_body.html
+++ b/api/templates/password_change_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2>
- <p>You updated your password for {{.Props.TeamDisplayName}} on {{ .TeamURL }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
+ <p>You updated your password for {{.Props.TeamDisplayName}} on {{ .Props.TeamURL }} by {{.Props.Method}}.<br> If this change wasn't initiated by you, please reply to this email and let us know.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/post_body.html b/api/templates/post_body.html
index 0aa913db5..a1df5b4c9 100644
--- a/api/templates/post_body.html
+++ b/api/templates/post_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/reset_body.html b/api/templates/reset_body.html
index 4c2fec1e7..a6e6269c0 100644
--- a/api/templates/reset_body.html
+++ b/api/templates/reset_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/signup_team_body.html b/api/templates/signup_team_body.html
index 5e60a042b..71df0b9c8 100644
--- a/api/templates/signup_team_body.html
+++ b/api/templates/signup_team_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -40,7 +40,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/verify_body.html b/api/templates/verify_body.html
index 1a68c16f5..6ba11d845 100644
--- a/api/templates/verify_body.html
+++ b/api/templates/verify_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -37,7 +37,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/templates/welcome_body.html b/api/templates/welcome_body.html
index cc4d95fb1..f16f50e14 100644
--- a/api/templates/welcome_body.html
+++ b/api/templates/welcome_body.html
@@ -9,7 +9,7 @@
<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="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
+ <img src="{{.SiteURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
</td>
</tr>
<tr>
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamDisplayName}} team at {{.SiteName}}!</h2>
- <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.TeamURL}}">{{.SiteName}}</a>.</p>
+ <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.Props.TeamURL}}">{{.SiteName}}</a>.</p>
</td>
</tr>
<tr>
@@ -34,7 +34,7 @@
<tr>
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
<p style="margin: 25px 0;">
- <img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
+ <img width="65" src="{{.SiteURL}}/static/images/circles.png" alt="">
</p>
<p style="padding: 0 50px;">
(c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.<br>
diff --git a/api/user.go b/api/user.go
index 5d6e649cb..a42f81cf1 100644
--- a/api/user.go
+++ b/api/user.go
@@ -195,7 +195,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
l4g.Error("Failed to set email verified err=%v", cresult.Err)
}
} else {
- FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team))
+ FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team))
}
ruser.Sanitize(map[string]bool{})
@@ -209,14 +209,15 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
}
}
-func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) {
+func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link, siteURL string) {
go func() {
- subjectPage := NewServerTemplatePage("welcome_subject", link)
- bodyPage := NewServerTemplatePage("welcome_body", link)
+ subjectPage := NewServerTemplatePage("welcome_subject", siteURL)
+ bodyPage := NewServerTemplatePage("welcome_body", siteURL)
bodyPage.Props["Nickname"] = name
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName
+ bodyPage.Props["TeamURL"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send welcome email successfully err=%v", err)
@@ -225,19 +226,18 @@ func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) {
}()
}
-func FireAndForgetVerifyEmail(userId, name, email, teamDisplayName, teamURL string) {
+func FireAndForgetVerifyEmail(userId, userEmail, teamName, teamDisplayName, siteURL, teamURL string) {
go func() {
- link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s", teamURL, userId, model.HashPassword(userId))
+ link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s&teamname=%s&email=%s", siteURL, userId, model.HashPassword(userId), teamName, userEmail)
- subjectPage := NewServerTemplatePage("verify_subject", teamURL)
+ subjectPage := NewServerTemplatePage("verify_subject", siteURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
- bodyPage := NewServerTemplatePage("verify_body", teamURL)
- bodyPage.Props["Nickname"] = name
+ bodyPage := NewServerTemplatePage("verify_body", siteURL)
bodyPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage.Props["VerifyUrl"] = link
- if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
+ if err := utils.SendMail(userEmail, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send verification email successfully err=%v", err)
}
}()
@@ -284,13 +284,32 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam
}
func checkUserPassword(c *Context, user *model.User, password string) bool {
+
+ if user.FailedAttempts >= utils.Cfg.ServiceSettings.AllowedLoginAttempts {
+ c.LogAuditWithUserId(user.Id, "fail")
+ c.Err = model.NewAppError("checkUserPassword", "Your account is locked because of too many failed password attempts. Please reset your password.", "user_id="+user.Id)
+ c.Err.StatusCode = http.StatusForbidden
+ return false
+ }
+
if !model.ComparePassword(user.Password, password) {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = model.NewAppError("checkUserPassword", "Login failed because of invalid password", "user_id="+user.Id)
c.Err.StatusCode = http.StatusForbidden
+
+ if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil {
+ c.LogError(result.Err)
+ }
+
return false
+ } else {
+ if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, 0); result.Err != nil {
+ c.LogError(result.Err)
+ }
+
+ return true
}
- return true
+
}
// User MUST be validated before calling Login
@@ -801,7 +820,7 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
l4g.Error(tresult.Err.Message)
} else {
team := tresult.Data.(*model.Team)
- fireAndForgetEmailChangeEmail(rusers[1].Email, team.DisplayName, c.GetTeamURLFromTeam(team))
+ fireAndForgetEmailChangeEmail(rusers[1].Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL())
}
}
@@ -864,7 +883,7 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !model.ComparePassword(user.Password, currentPassword) {
- c.Err = model.NewAppError("updatePassword", "Update password failed because of invalid password", "")
+ c.Err = model.NewAppError("updatePassword", "The \"Current Password\" you entered is incorrect. Please check that Caps Lock is off and try again.", "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -880,7 +899,7 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
l4g.Error(tresult.Err.Message)
} else {
team := tresult.Data.(*model.Team)
- fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using the settings menu")
+ fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), "using the settings menu")
}
data := make(map[string]string)
@@ -1070,8 +1089,8 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash))
- subjectPage := NewServerTemplatePage("reset_subject", c.GetTeamURLFromTeam(team))
- bodyPage := NewServerTemplatePage("reset_body", c.GetTeamURLFromTeam(team))
+ subjectPage := NewServerTemplatePage("reset_subject", c.GetSiteURL())
+ bodyPage := NewServerTemplatePage("reset_body", c.GetSiteURL())
bodyPage.Props["ResetUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1161,19 +1180,20 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(userId, "success")
}
- fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using a reset password link")
+ fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), "using a reset password link")
props["new_password"] = ""
w.Write([]byte(model.MapToJson(props)))
}
-func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method string) {
+func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, siteURL, method string) {
go func() {
- subjectPage := NewServerTemplatePage("password_change_subject", teamURL)
+ subjectPage := NewServerTemplatePage("password_change_subject", siteURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
- bodyPage := NewServerTemplatePage("password_change_body", teamURL)
+ bodyPage := NewServerTemplatePage("password_change_body", siteURL)
bodyPage.Props["TeamDisplayName"] = teamDisplayName
+ bodyPage.Props["TeamURL"] = teamURL
bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
@@ -1183,13 +1203,14 @@ func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method st
}()
}
-func fireAndForgetEmailChangeEmail(email, teamDisplayName, teamURL string) {
+func fireAndForgetEmailChangeEmail(email, teamDisplayName, teamURL, siteURL string) {
go func() {
- subjectPage := NewServerTemplatePage("email_change_subject", teamURL)
+ subjectPage := NewServerTemplatePage("email_change_subject", siteURL)
subjectPage.Props["TeamDisplayName"] = teamDisplayName
- bodyPage := NewServerTemplatePage("email_change_body", teamURL)
+ bodyPage := NewServerTemplatePage("email_change_body", siteURL)
bodyPage.Props["TeamDisplayName"] = teamDisplayName
+ bodyPage.Props["TeamURL"] = teamURL
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err)