summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/admin_test.go8
-rw-r--r--api/channel.go53
-rw-r--r--api/channel_test.go7
-rw-r--r--api/command.go73
-rw-r--r--api/command_test.go56
-rw-r--r--api/context.go2
-rw-r--r--api/post.go85
-rw-r--r--api/server.go10
-rw-r--r--api/team.go33
-rw-r--r--api/team_test.go40
-rw-r--r--api/templates/signup_team_subject.html2
-rw-r--r--api/user.go75
-rw-r--r--api/user_test.go39
13 files changed, 451 insertions, 32 deletions
diff --git a/api/admin_test.go b/api/admin_test.go
index 0db5caa4c..0a1682a99 100644
--- a/api/admin_test.go
+++ b/api/admin_test.go
@@ -235,6 +235,10 @@ func TestGetPostCount(t *testing.T) {
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
+ // manually update creation time, since it's always set to 0 upon saving and we only retrieve posts < today
+ Srv.Store.(*store.SqlStore).GetMaster().Exec("UPDATE Posts SET CreateAt = :CreateAt WHERE ChannelId = :ChannelId",
+ map[string]interface{}{"ChannelId": channel1.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())})
+
if _, err := Client.GetAnalytics(team.Id, "post_counts_day"); err == nil {
t.Fatal("Shouldn't have permissions")
}
@@ -276,6 +280,10 @@ func TestUserCountsWithPostsByDay(t *testing.T) {
post1 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
+ // manually update creation time, since it's always set to 0 upon saving and we only retrieve posts < today
+ Srv.Store.(*store.SqlStore).GetMaster().Exec("UPDATE Posts SET CreateAt = :CreateAt WHERE ChannelId = :ChannelId",
+ map[string]interface{}{"ChannelId": channel1.Id, "CreateAt": utils.MillisFromTime(utils.Yesterday())})
+
if _, err := Client.GetAnalytics(team.Id, "user_counts_with_posts_day"); err == nil {
t.Fatal("Shouldn't have permissions")
}
diff --git a/api/channel.go b/api/channel.go
index 44be1cf97..f17594c0a 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -205,9 +205,11 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
if oldChannel.Name == model.DEFAULT_CHANNEL {
- c.Err = model.NewAppError("updateChannel", "Cannot update the default channel "+model.DEFAULT_CHANNEL, "")
- c.Err.StatusCode = http.StatusForbidden
- return
+ if (len(channel.Name) > 0 && channel.Name != oldChannel.Name) || (len(channel.Type) > 0 && channel.Type != oldChannel.Type) {
+ c.Err = model.NewAppError("updateChannel", "Tried to perform an invalid update of the default channel "+model.DEFAULT_CHANNEL, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
}
oldChannel.Header = channel.Header
@@ -266,19 +268,51 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(channel.TeamId, "updateChannelHeader") {
return
}
-
+ oldChannelHeader := channel.Header
channel.Header = channelHeader
if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil {
c.Err = ucresult.Err
return
} else {
+ PostUpdateChannelHeaderMessageAndForget(c, channel.Id, oldChannelHeader, channelHeader)
c.LogAudit("name=" + channel.Name)
w.Write([]byte(channel.ToJson()))
}
}
}
+func PostUpdateChannelHeaderMessageAndForget(c *Context, channelId string, oldChannelHeader, newChannelHeader string) {
+ go func() {
+ uc := Srv.Store.User().Get(c.Session.UserId)
+
+ if uresult := <-uc; uresult.Err != nil {
+ l4g.Error("Failed to retrieve user while trying to save update channel header message %v", uresult.Err)
+ return
+ } else {
+ user := uresult.Data.(*model.User)
+
+ var message string
+ if oldChannelHeader == "" {
+ message = fmt.Sprintf("%s updated the channel header to: %s", user.Username, newChannelHeader)
+ } else if newChannelHeader == "" {
+ message = fmt.Sprintf("%s removed the channel header (was: %s)", user.Username, oldChannelHeader)
+ } else {
+ message = fmt.Sprintf("%s updated the channel header from: %s to: %s", user.Username, oldChannelHeader, newChannelHeader)
+ }
+
+ post := &model.Post{
+ ChannelId: channelId,
+ Message: message,
+ Type: model.POST_HEADER_CHANGE,
+ }
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Failed to post join/leave message %v", err)
+ }
+ }
+ }()
+}
+
func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
channelId := props["channel_id"]
@@ -419,7 +453,7 @@ func JoinChannel(c *Context, channelId string, role string) {
c.Err = err
return
}
- PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`User %v has joined this channel.`, user.Username))
+ PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v has joined the channel.`, user.Username))
} else {
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden
@@ -706,7 +740,8 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
}
scm := Srv.Store.Channel().GetMember(id, c.Session.UserId)
- ecm := Srv.Store.Channel().GetExtraMembers(id, 20)
+ ecm := Srv.Store.Channel().GetExtraMembers(id, 100)
+ ccm := Srv.Store.Channel().GetMemberCount(id)
if cmresult := <-scm; cmresult.Err != nil {
c.Err = cmresult.Err
@@ -714,9 +749,13 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
} else if ecmresult := <-ecm; ecmresult.Err != nil {
c.Err = ecmresult.Err
return
+ } else if ccmresult := <-ccm; ccmresult.Err != nil {
+ c.Err = ccmresult.Err
+ return
} else {
member := cmresult.Data.(model.ChannelMember)
extraMembers := ecmresult.Data.([]model.ExtraMember)
+ memberCount := ccmresult.Data.(int64)
if !c.HasPermissionsToTeam(channel.TeamId, "getChannelExtraInfo") {
return
@@ -732,7 +771,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- data := model.ChannelExtra{Id: channel.Id, Members: extraMembers}
+ data := model.ChannelExtra{Id: channel.Id, Members: extraMembers, MemberCount: memberCount}
w.Header().Set(model.HEADER_ETAG_SERVER, extraEtag)
w.Header().Set("Expires", "-1")
w.Write([]byte(data.ToJson()))
diff --git a/api/channel_test.go b/api/channel_test.go
index a41f63b1b..e7e1f4eb0 100644
--- a/api/channel_test.go
+++ b/api/channel_test.go
@@ -215,8 +215,9 @@ func TestUpdateChannel(t *testing.T) {
for _, c := range data.Channels {
if c.Name == model.DEFAULT_CHANNEL {
c.Header = "new header"
+ c.Name = "pseudo-square"
if _, err := Client.UpdateChannel(c); err == nil {
- t.Fatal("should have errored on updating default channel")
+ t.Fatal("should have errored on updating default channel name")
}
break
}
@@ -677,6 +678,10 @@ func TestGetChannelExtraInfo(t *testing.T) {
data := rget.Data.(*model.ChannelExtra)
if data.Id != channel1.Id {
t.Fatal("couldnt't get extra info")
+ } else if len(data.Members) != 1 {
+ t.Fatal("got incorrect members")
+ } else if data.MemberCount != 1 {
+ t.Fatal("got incorrect member count")
}
//
diff --git a/api/command.go b/api/command.go
index 50ca41155..db57f0bae 100644
--- a/api/command.go
+++ b/api/command.go
@@ -4,7 +4,9 @@
package api
import (
+ "io"
"net/http"
+ "path"
"strconv"
"strings"
"time"
@@ -325,6 +327,9 @@ func loadTestCommand(c *Context, command *model.Command) bool {
if loadTestPostsCommand(c, command) {
return true
}
+ if loadTestUrlCommand(c, command) {
+ return true
+ }
} else if strings.Index(cmd, command.Command) == 0 {
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Debug Load Testing"})
}
@@ -571,3 +576,71 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
return false
}
+
+func loadTestUrlCommand(c *Context, command *model.Command) bool {
+ cmd := cmds["loadTestCommand"] + " url"
+
+ if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
+ url := ""
+
+ parameters := strings.SplitN(command.Command, " ", 3)
+ if len(parameters) != 3 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Command must contain a url", "")
+ return true
+ } else {
+ url = parameters[2]
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".md"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", err.Error())
+ return false
+ } else if r.StatusCode > 400 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", r.Status)
+ return false
+ } else {
+ contents = r.Body
+ }
+
+ bytes := make([]byte, 4000)
+
+ // break contents into 4000 byte posts
+ for {
+ length, err := contents.Read(bytes)
+ if err != nil && err != io.EOF {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Encountered error reading file", err.Error())
+ return false
+ }
+
+ if length == 0 {
+ break
+ }
+
+ post := &model.Post{}
+ post.Message = string(bytes[:length])
+ post.ChannelId = command.ChannelId
+
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Unable to create post, err=%v", err)
+ return false
+ }
+ }
+
+ command.Response = model.RESP_EXECUTED
+
+ return true
+ } else if strings.Index(cmd, command.Command) == 0 && strings.Index(command.Command, "/loadtest posts") != 0 {
+ command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Add a post containing the text from a given url to current channel <Url>"})
+ }
+
+ return false
+}
diff --git a/api/command_test.go b/api/command_test.go
index 476748c6b..9327850f3 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -10,6 +10,7 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
)
func TestSuggestRootCommands(t *testing.T) {
@@ -184,3 +185,58 @@ func TestEchoCommand(t *testing.T) {
t.Fatal("Echo command failed to send")
}
}
+
+func TestLoadTestUrlCommand(t *testing.T) {
+ Setup()
+
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ 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() + "corey@test.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")
+
+ channel := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ command := "/loadtest url "
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with no url should've failed")
+ }
+
+ command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with invalid url should've failed")
+ }
+
+ command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for README.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons should've executed")
+ }
+
+ posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
+ // note that this may make more than 3 posts if files are too long to fit in an individual post
+ if len(posts.Order) < 3 {
+ t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
+ }
+}
diff --git a/api/context.go b/api/context.go
index a5d4169cb..a6f9bc1e1 100644
--- a/api/context.go
+++ b/api/context.go
@@ -37,6 +37,8 @@ type Page struct {
ClientCfg map[string]string
User *model.User
Team *model.Team
+ Channel *model.Channel
+ PostID string
SessionTokenIndex int64
}
diff --git a/api/post.go b/api/post.go
index ffb69f382..5fbacc906 100644
--- a/api/post.go
+++ b/api/post.go
@@ -23,6 +23,7 @@ func InitPost(r *mux.Router) {
l4g.Debug("Initializing post api routes")
r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET")
+ r.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET")
sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter()
sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
@@ -152,9 +153,6 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
- linkRegex := regexp.MustCompile(`<\s*(\S*)\s*>`)
- text = linkRegex.ReplaceAllString(text, "${1}")
-
post := &model.Post{UserId: c.Session.UserId, ChannelId: channelId, Message: text, Type: postType}
post.AddProp("from_webhook", "true")
@@ -176,7 +174,21 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
if len(props) > 0 {
for key, val := range props {
- if key != "override_icon_url" && key != "override_username" && key != "from_webhook" {
+ if key == "attachments" {
+ if list, success := val.([]interface{}); success {
+ // parse attachment links into Markdown format
+ for i, aInt := range list {
+ attachment := aInt.(map[string]interface{})
+ if _, ok := attachment["text"]; ok {
+ aText := attachment["text"].(string)
+ aText = linkWithTextRegex.ReplaceAllString(aText, "[${2}](${1})")
+ attachment["text"] = aText
+ list[i] = attachment
+ }
+ }
+ post.AddProp(key, list)
+ }
+ } else if key != "override_icon_url" && key != "override_username" && key != "from_webhook" {
post.AddProp(key, val)
}
}
@@ -406,9 +418,9 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
}
// Add @all to keywords if user has them turned on
- if profile.NotifyProps["all"] == "true" {
- keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
- }
+ // if profile.NotifyProps["all"] == "true" {
+ // keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
+ // }
// Add @channel to keywords if user has them turned on
if profile.NotifyProps["channel"] == "true" {
@@ -535,7 +547,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
}
- if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 {
+ if *utils.Cfg.EmailSettings.SendPushNotifications {
sessionChan := Srv.Store.Session().GetSessions(id)
if result := <-sessionChan; result.Err != nil {
l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
@@ -544,11 +556,27 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
alreadySeen := make(map[string]string)
for _, session := range sessions {
- if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" {
-
+ if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") {
alreadySeen[session.DeviceId] = session.DeviceId
- utils.SendAppleNotifyAndForget(session.DeviceId, subjectPage.Render(), 1)
+ msg := model.PushNotification{}
+ msg.Platform = model.PUSH_NOTIFY_APPLE
+ msg.Badge = 1
+ msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:")
+ msg.ServerId = utils.CfgDiagnosticId
+
+ if channel.Type == model.CHANNEL_DIRECT {
+ msg.Message = channelName + " sent you a direct message"
+ } else {
+ msg.Message = profileMap[id].FirstName + " mentioned you in " + channelName
+ }
+
+ httpClient := http.Client{}
+ request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
+
+ if _, err := httpClient.Do(request); err != nil {
+ l4g.Error("Failed to send push notificationid=%v, err=%v", id, err)
+ }
}
}
}
@@ -768,6 +796,41 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+func getPostById(c *Context, w http.ResponseWriter, r *http.Request) {
+ params := mux.Vars(r)
+
+ postId := params["post_id"]
+ if len(postId) != 26 {
+ c.SetInvalidParam("getPostById", "postId")
+ return
+ }
+
+ if result := <-Srv.Store.Post().Get(postId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ list := result.Data.(*model.PostList)
+
+ if len(list.Order) != 1 {
+ c.Err = model.NewAppError("getPostById", "Unable to get post", "")
+ return
+ }
+ post := list.Posts[list.Order[0]]
+
+ cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId)
+ if !c.HasPermissionsToChannel(cchan, "getPostById") {
+ return
+ }
+
+ if HandleEtag(list.Etag(), w, r) {
+ return
+ }
+
+ w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
+ w.Write([]byte(list.ToJson()))
+ }
+}
+
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
diff --git a/api/server.go b/api/server.go
index 347f0e2c9..2bab62fac 100644
--- a/api/server.go
+++ b/api/server.go
@@ -9,15 +9,14 @@ 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"
+ "gopkg.in/throttled/throttled.v1"
+ throttledStore "gopkg.in/throttled/throttled.v1/store"
"net/http"
"strings"
"time"
)
type Server struct {
- Server *manners.GracefulServer
Store store.Store
Router *mux.Router
}
@@ -29,7 +28,6 @@ func NewServer() {
l4g.Info("Server is initializing...")
Srv = &Server{}
- Srv.Server = manners.NewServer()
Srv.Store = store.NewSqlStore()
Srv.Router = mux.NewRouter()
@@ -71,7 +69,7 @@ func StartServer() {
}
go func() {
- err := Srv.Server.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handler)
+ err := manners.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handler)
if err != nil {
l4g.Critical("Error starting server, err:%v", err)
time.Sleep(time.Second)
@@ -84,7 +82,7 @@ func StopServer() {
l4g.Info("Stopping Server...")
- Srv.Server.Shutdown <- true
+ manners.Close()
Srv.Store.Close()
hub.Stop()
diff --git a/api/team.go b/api/team.go
index 862970887..2cc7106dc 100644
--- a/api/team.go
+++ b/api/team.go
@@ -582,6 +582,39 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(oldTeam.ToJson()))
}
+func PermanentDeleteTeam(c *Context, team *model.Team) *model.AppError {
+ l4g.Warn("Attempting to permanently delete team %v id=%v", team.Name, team.Id)
+ c.Path = "/teams/permanent_delete"
+ c.LogAuditWithUserId("", fmt.Sprintf("attempt teamId=%v", team.Id))
+
+ team.DeleteAt = model.GetMillis()
+ if result := <-Srv.Store.Team().Update(team); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.User().GetForExport(team.Id); result.Err != nil {
+ return result.Err
+ } else {
+ users := result.Data.([]*model.User)
+ for _, user := range users {
+ PermanentDeleteUser(c, user)
+ }
+ }
+
+ if result := <-Srv.Store.Channel().PermanentDeleteByTeam(team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Team().PermanentDelete(team.Id); result.Err != nil {
+ return result.Err
+ }
+
+ l4g.Warn("Permanently deleted team %v id=%v", team.Name, team.Id)
+ c.LogAuditWithUserId("", fmt.Sprintf("success teamId=%v", team.Id))
+
+ return nil
+}
+
func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) {
if len(c.Session.TeamId) == 0 {
diff --git a/api/team_test.go b/api/team_test.go
index 7a3b092ce..0b7d2ed9c 100644
--- a/api/team_test.go
+++ b/api/team_test.go
@@ -168,6 +168,45 @@ func TestGetAllTeams(t *testing.T) {
}
}
+func TestTeamPermDelete(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)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ post1 := &model.Post{ChannelId: channel1.Id, Message: "search for post1"}
+ post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
+
+ post2 := &model.Post{ChannelId: channel1.Id, Message: "search for post2"}
+ post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
+
+ post3 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag search for post3"}
+ post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
+
+ post4 := &model.Post{ChannelId: channel1.Id, Message: "hashtag for post4"}
+ post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
+
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "test"
+
+ err := PermanentDeleteTeam(c, team)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ Client.ClearOAuthToken()
+}
+
/*
XXXXXX investigate and fix failing test
@@ -221,6 +260,7 @@ func TestFindTeamByEmailSend(t *testing.T) {
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.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)
diff --git a/api/templates/signup_team_subject.html b/api/templates/signup_team_subject.html
index 236b288fa..4fc5b3d72 100644
--- a/api/templates/signup_team_subject.html
+++ b/api/templates/signup_team_subject.html
@@ -1 +1 @@
-{{define "signup_team_subject"}}Invitation to {{ .ClientCfg.SiteName }}{{end}} \ No newline at end of file
+{{define "signup_team_subject"}}{{ .ClientCfg.SiteName }} Team Setup{{end}} \ No newline at end of file
diff --git a/api/user.go b/api/user.go
index 4a52cf88b..886e38c91 100644
--- a/api/user.go
+++ b/api/user.go
@@ -334,6 +334,7 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam
if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil {
c.Err = result.Err
+ c.Err.StatusCode = http.StatusForbidden
return nil
} else {
user := result.Data.(*model.User)
@@ -660,7 +661,7 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) {
profiles := result.Data.(map[string]*model.User)
for k, p := range profiles {
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
if c.IsSystemAdmin() {
@@ -669,6 +670,7 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) {
}
p.Sanitize(options)
+ p.ClearNonProfileFields()
profiles[k] = p
}
@@ -1100,7 +1102,7 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
w.Write([]byte(ruser.ToJson()))
@@ -1195,6 +1197,14 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
+ ruser := UpdateActive(c, user, active)
+
+ if c.Err == nil {
+ w.Write([]byte(ruser.ToJson()))
+ }
+}
+
+func UpdateActive(c *Context, user *model.User, active bool) *model.User {
if active {
user.DeleteAt = 0
} else {
@@ -1203,7 +1213,7 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
if result := <-Srv.Store.User().Update(user, true); result.Err != nil {
c.Err = result.Err
- return
+ return nil
} else {
c.LogAuditWithUserId(user.Id, fmt.Sprintf("active=%v", active))
@@ -1212,11 +1222,64 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
}
ruser := result.Data.([2]*model.User)[0]
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
- w.Write([]byte(ruser.ToJson()))
+ return ruser
+ }
+}
+
+func PermanentDeleteUser(c *Context, user *model.User) *model.AppError {
+ l4g.Warn("Attempting to permanently delete account %v id=%v", user.Email, user.Id)
+ c.Path = "/users/permanent_delete"
+ c.LogAuditWithUserId(user.Id, fmt.Sprintf("attempt userId=%v", user.Id))
+ c.LogAuditWithUserId("", fmt.Sprintf("attempt userId=%v", user.Id))
+ if user.IsInRole(model.ROLE_SYSTEM_ADMIN) {
+ l4g.Warn("You are deleting %v that is a system administrator. You may need to set another account as the system administrator using the command line tools.", user.Email)
+ }
+
+ UpdateActive(c, user, false)
+
+ if result := <-Srv.Store.Session().PermanentDeleteSessionsByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.OAuth().PermanentDeleteAuthDataByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Webhook().PermanentDeleteIncomingByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Webhook().PermanentDeleteOutgoingByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Preference().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Channel().PermanentDeleteMembersByUser(user.Id); result.Err != nil {
+ return result.Err
}
+
+ if result := <-Srv.Store.Post().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.User().PermanentDelete(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ if result := <-Srv.Store.Audit().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
+ l4g.Warn("Permanently deleted account %v id=%v", user.Email, user.Id)
+ c.LogAuditWithUserId("", fmt.Sprintf("success userId=%v", user.Id))
+
+ return nil
}
func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -1485,7 +1548,7 @@ func updateUserNotify(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAuditWithUserId(user.Id, "")
ruser := result.Data.([2]*model.User)[0]
- options := utils.SanitizeOptions
+ options := utils.Cfg.GetSanitizeOptions()
options["passwordupdate"] = false
ruser.Sanitize(options)
w.Write([]byte(ruser.ToJson()))
diff --git a/api/user_test.go b/api/user_test.go
index f067182cb..63a1e337b 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -767,6 +767,45 @@ func TestUserUpdateActive(t *testing.T) {
}
}
+func TestUserPermDelete(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)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ post1 := &model.Post{ChannelId: channel1.Id, Message: "search for post1"}
+ post1 = Client.Must(Client.CreatePost(post1)).Data.(*model.Post)
+
+ post2 := &model.Post{ChannelId: channel1.Id, Message: "search for post2"}
+ post2 = Client.Must(Client.CreatePost(post2)).Data.(*model.Post)
+
+ post3 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag search for post3"}
+ post3 = Client.Must(Client.CreatePost(post3)).Data.(*model.Post)
+
+ post4 := &model.Post{ChannelId: channel1.Id, Message: "hashtag for post4"}
+ post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post)
+
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "test"
+
+ err := PermanentDeleteUser(c, user1)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ Client.ClearOAuthToken()
+}
+
func TestSendPasswordReset(t *testing.T) {
Setup()