diff options
author | Jack <jackdeng@gmail.com> | 2015-09-28 08:14:57 -0700 |
---|---|---|
committer | Jack <jackdeng@gmail.com> | 2015-09-28 08:14:57 -0700 |
commit | 503501ab3b616fb9fa57b046df850cfc2db734f8 (patch) | |
tree | 43e529250bab982c73816f1ffdf673e8e4b98c13 /api | |
parent | aa6da728baf2f5642293d8b56ebc1ff4e8682bce (diff) | |
parent | a055fd10b83b28bf97877e4e28a2275f74a71a4a (diff) | |
download | chat-503501ab3b616fb9fa57b046df850cfc2db734f8.tar.gz chat-503501ab3b616fb9fa57b046df850cfc2db734f8.tar.bz2 chat-503501ab3b616fb9fa57b046df850cfc2db734f8.zip |
rebase
Diffstat (limited to 'api')
-rw-r--r-- | api/context.go | 19 | ||||
-rw-r--r-- | api/export.go | 2 | ||||
-rw-r--r-- | api/file.go | 14 | ||||
-rw-r--r-- | api/import.go | 4 | ||||
-rw-r--r-- | api/team.go | 95 | ||||
-rw-r--r-- | api/team_test.go | 33 | ||||
-rw-r--r-- | api/templates/error.html | 2 | ||||
-rw-r--r-- | api/templates/signup_team_body.html | 3 | ||||
-rw-r--r-- | api/templates/welcome_body.html | 12 | ||||
-rw-r--r-- | api/templates/welcome_subject.html | 2 | ||||
-rw-r--r-- | api/user.go | 106 | ||||
-rw-r--r-- | api/user_test.go | 21 |
12 files changed, 202 insertions, 111 deletions
diff --git a/api/context.go b/api/context.go index 9a276a1a1..d90fbd9ee 100644 --- a/api/context.go +++ b/api/context.go @@ -456,18 +456,19 @@ 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 - protocol := GetProtocol(r) - SiteURL := protocol + "://" + r.Host - - m := make(map[string]string) - m["Message"] = err.Message - m["Details"] = err.DetailedError - m["SiteName"] = utils.Cfg.TeamSettings.SiteName - m["SiteURL"] = SiteURL + pathParts := strings.Split(r.URL.Path, "/") + if len(pathParts) > 1 { + props["SiteURL"] = GetProtocol(r) + "://" + r.Host + "/" + pathParts[1] + } else { + props["SiteURL"] = GetProtocol(r) + "://" + r.Host + } w.WriteHeader(err.StatusCode) - ServerTemplates.ExecuteTemplate(w, "error.html", m) + ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientProps: utils.ClientProperties}) } func Handle404(w http.ResponseWriter, r *http.Request) { diff --git a/api/export.go b/api/export.go index 73142a0e4..aff34073f 100644 --- a/api/export.go +++ b/api/export.go @@ -87,7 +87,7 @@ func ExportTeams(writer ExportWriter, options *ExportOptions) *model.AppError { // Get the teams var teams []*model.Team if len(options.TeamsToExport) == 0 { - if result := <-Srv.Store.Team().GetForExport(); result.Err != nil { + if result := <-Srv.Store.Team().GetAll(); result.Err != nil { return result.Err } else { teams = result.Data.([]*model.Team) diff --git a/api/file.go b/api/file.go index 3606e406e..be8fc5456 100644 --- a/api/file.go +++ b/api/file.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" + "github.com/mssola/user_agent" "github.com/rwcarlsen/goexif/exif" _ "golang.org/x/image/bmp" "image" @@ -407,6 +408,19 @@ 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.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(filename))) + + // attach extra headers to trigger a download on IE and Edge + ua := user_agent.New(r.UserAgent()) + bname, _ := ua.Browser() + + if bname == "Edge" || bname == "Internet Explorer" { + // trim off anything before the final / so we just get the file's name + parts := strings.Split(filename, "/") + + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", "attachment;filename=\""+parts[len(parts)-1]+"\"") + } + w.Write(f) } diff --git a/api/import.go b/api/import.go index e3f314d20..c465825b4 100644 --- a/api/import.go +++ b/api/import.go @@ -6,7 +6,6 @@ package api import ( l4g "code.google.com/p/log4go" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" ) // @@ -24,9 +23,6 @@ func ImportPost(post *model.Post) { func ImportUser(user *model.User) *model.User { user.MakeNonNil() - if len(user.Props["theme"]) == 0 { - user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor) - } if result := <-Srv.Store.User().Save(user); result.Err != nil { l4g.Error("Error saving user. err=%v", result.Err) diff --git a/api/team.go b/api/team.go index c9d2412d3..4794b66df 100644 --- a/api/team.go +++ b/api/team.go @@ -25,12 +25,12 @@ func InitTeam(r *mux.Router) { sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).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("/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_name", ApiUserRequired(updateTeamDisplayName)).Methods("POST") - sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST") sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") // These should be moved to the global admain console sr.Handle("/import_team", ApiUserRequired(importTeam)).Methods("POST") @@ -302,6 +302,53 @@ func isTreamCreationAllowed(c *Context, email string) bool { return true } +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 + } else { + teams := result.Data.([]*model.Team) + m := make(map[string]*model.Team) + for _, v := range teams { + m[v.Id] = v + } + + w.Write([]byte(model.TeamMapToJson(m))) + } +} + +func revokeAllSessions(c *Context, w http.ResponseWriter, r *http.Request) { + props := model.MapFromJson(r.Body) + id := props["id"] + + if result := <-Srv.Store.Session().Get(id); result.Err != nil { + c.Err = result.Err + return + } else { + session := result.Data.(*model.Session) + + c.LogAudit("revoked_all=" + id) + + if session.IsOAuth { + RevokeAccessToken(session.Token) + } else { + sessionCache.Remove(session.Token) + + if result := <-Srv.Store.Session().Remove(session.Id); result.Err != nil { + c.Err = result.Err + return + } else { + w.Write([]byte(model.MapToJson(props))) + return + } + } + } +} + func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) { m := model.MapFromJson(r.Body) @@ -533,52 +580,6 @@ func updateTeamDisplayName(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(props))) } -func updateValetFeature(c *Context, w http.ResponseWriter, r *http.Request) { - - props := model.MapFromJson(r.Body) - - allowValetStr := props["allow_valet"] - if len(allowValetStr) == 0 { - c.SetInvalidParam("updateValetFeature", "allow_valet") - return - } - - teamId := props["team_id"] - if len(teamId) > 0 && len(teamId) != 26 { - c.SetInvalidParam("updateValetFeature", "team_id") - return - } else if len(teamId) == 0 { - teamId = c.Session.TeamId - } - - tchan := Srv.Store.Team().Get(teamId) - - if !c.HasPermissionsToTeam(teamId, "updateValetFeature") { - return - } - - if !model.IsInRole(c.Session.Roles, model.ROLE_TEAM_ADMIN) { - c.Err = model.NewAppError("updateValetFeature", "You do not have the appropriate permissions", "userId="+c.Session.UserId) - c.Err.StatusCode = http.StatusForbidden - return - } - - var team *model.Team - if tResult := <-tchan; tResult.Err != nil { - c.Err = tResult.Err - return - } else { - team = tResult.Data.(*model.Team) - } - - if result := <-Srv.Store.Team().Update(team); result.Err != nil { - c.Err = result.Err - return - } - - w.Write([]byte(model.MapToJson(props))) -} - 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 cd39dacfe..e2a7cf430 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -132,6 +132,39 @@ func TestFindTeamByEmail(t *testing.T) { } } +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 = 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") + + if _, err := Client.GetAllTeams(); err == nil { + t.Fatal("you shouldn't have permissions") + } + + c := &Context{} + c.RequestId = model.NewId() + c.IpAddress = "cmd_line" + UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN) + + Client.LoginByEmail(team.Name, user.Email, "pwd") + + 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() + } + } +} + /* XXXXXX investigate and fix failing test diff --git a/api/templates/error.html b/api/templates/error.html index 760578896..6b643556e 100644 --- a/api/templates/error.html +++ b/api/templates/error.html @@ -23,7 +23,7 @@ <div class="error__container"> <div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div> <h2>{{ .ClientProps.SiteName }} needs your help:</h2> - <p>{{.Message}}</p> + <p>{{ .Props.Message }}</p> <a href="{{.Props.SiteURL}}">Go back to team site</a> </div> </div> diff --git a/api/templates/signup_team_body.html b/api/templates/signup_team_body.html index f5c0e62b0..e6ffb3a5b 100644 --- a/api/templates/signup_team_body.html +++ b/api/templates/signup_team_body.html @@ -22,9 +22,6 @@ <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;">Set up your team</a> </p> {{ .ClientProps.SiteName }} is one place for all your team communication, searchable and available anywhere.<br>You'll get more out of {{ .ClientProps.SiteName }} when your team is in constant communication--let's get them on board.<br></p> - <p> - Learn more by <a href="{{.Props.TourUrl}}" style="text-decoration: none; color:#2389D7;">taking a tour</a> - </p> </td> </tr> <tr> diff --git a/api/templates/welcome_body.html b/api/templates/welcome_body.html index c75e14c6a..5fe3450b7 100644 --- a/api/templates/welcome_body.html +++ b/api/templates/welcome_body.html @@ -17,15 +17,9 @@ <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;">You joined the {{.Props.TeamDisplayName}} team at {{.ClientProps.SiteName}}!</h2> - <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.Props.TeamURL}}">{{.ClientProps.SiteName}}</a>.</p> - </td> - </tr> - <tr> - <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;"> - Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br> - Best wishes,<br> - The {{.ClientProps.SiteName}} Team<br> + <h2 style="font-weight: normal; margin-top: 10px;">You can sign-in to your new team from the web address:</h2> + <a href="{{.Props.TeamURL}}">{{.Props.TeamURL}}</a> + <p>Mattermost lets you share messages and files from your PC or phone, with instant search and archiving.</p> </td> </tr> </table> diff --git a/api/templates/welcome_subject.html b/api/templates/welcome_subject.html index 2214f7a38..c3b70ef20 100644 --- a/api/templates/welcome_subject.html +++ b/api/templates/welcome_subject.html @@ -1 +1 @@ -{{define "welcome_subject"}}Welcome to {{ .ClientProps.SiteName }}{{end}}
\ No newline at end of file +{{define "welcome_subject"}}You joined {{ .Props.TeamDisplayName }}{{end}} diff --git a/api/user.go b/api/user.go index 3f26531ad..695ab2208 100644 --- a/api/user.go +++ b/api/user.go @@ -51,6 +51,7 @@ func InitUser(r *mux.Router) { sr.Handle("/me", ApiAppHandler(getMe)).Methods("GET") sr.Handle("/status", ApiUserRequiredActivity(getStatuses, false)).Methods("GET") sr.Handle("/profiles", ApiUserRequired(getProfiles)).Methods("GET") + sr.Handle("/profiles/{id:[A-Za-z0-9]+}", ApiUserRequired(getProfiles)).Methods("GET") sr.Handle("/{id:[A-Za-z0-9]+}", ApiUserRequired(getUser)).Methods("GET") sr.Handle("/{id:[A-Za-z0-9]+}/sessions", ApiUserRequired(getSessions)).Methods("GET") sr.Handle("/{id:[A-Za-z0-9]+}/audits", ApiUserRequired(getAudits)).Methods("GET") @@ -166,6 +167,19 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { if team.Email == user.Email { user.Roles = model.ROLE_TEAM_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN + + // Below is a speical case where the first user in the entire + // system is granted the system_admin role instead of admin + if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil { + c.Err = result.Err + return nil + } else { + count := result.Data.(int64) + if count <= 0 { + user.Roles = model.ROLE_SYSTEM_ADMIN + } + } + } else { user.Roles = "" } @@ -184,7 +198,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } - //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square") + fireAndForgetWelcomeEmail(ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team)) if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) @@ -204,17 +218,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { } } -func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link, siteURL string) { +func fireAndForgetWelcomeEmail(email, teamDisplayName, teamURL string) { go func() { subjectPage := NewServerTemplatePage("welcome_subject") - subjectPage.Props["SiteURL"] = siteURL + subjectPage.Props["TeamDisplayName"] = teamDisplayName bodyPage := NewServerTemplatePage("welcome_body") - bodyPage.Props["SiteURL"] = siteURL - bodyPage.Props["Nickname"] = name - bodyPage.Props["TeamDisplayName"] = teamDisplayName - bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName - bodyPage.Props["TeamURL"] = link + bodyPage.Props["TeamURL"] = teamURL if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send welcome email successfully err=%v", err) @@ -550,13 +560,26 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) { } func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id, ok := params["id"] + if ok { + // You must be system admin to access another team + if id != c.Session.TeamId { + if !c.HasSystemAdminPermissions("getProfiles") { + return + } + } + + } else { + id = c.Session.TeamId + } - etag := (<-Srv.Store.User().GetEtagForProfiles(c.Session.TeamId)).Data.(string) + etag := (<-Srv.Store.User().GetEtagForProfiles(id)).Data.(string) if HandleEtag(etag, w, r) { return } - if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.User().GetProfiles(id); result.Err != nil { c.Err = result.Err return } else { @@ -794,6 +817,9 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId) c.LogAudit("") + + // write something as the response since jQuery expects a json response + w.Write([]byte("true")) } func updateUser(c *Context, w http.ResponseWriter, r *http.Request) { @@ -924,8 +950,8 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) { return } - if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) { - c.Err = model.NewAppError("updateRoles", "The system_admin role can only be set from the command line", "") + if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() { + c.Err = model.NewAppError("updateRoles", "The system_admin role can only be set by another system admin", "") c.Err.StatusCode = http.StatusForbidden return } @@ -1155,29 +1181,35 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { return } - hash := props["hash"] - if len(hash) == 0 { - c.SetInvalidParam("resetPassword", "hash") + name := props["name"] + if len(name) == 0 { + c.SetInvalidParam("resetPassword", "name") return } - data := model.MapFromJson(strings.NewReader(props["data"])) + userId := props["user_id"] + hash := props["hash"] + timeStr := "" - userId := data["user_id"] - if len(userId) != 26 { - c.SetInvalidParam("resetPassword", "data:user_id") - return - } + if !c.IsSystemAdmin() { + if len(hash) == 0 { + c.SetInvalidParam("resetPassword", "hash") + return + } - timeStr := data["time"] - if len(timeStr) == 0 { - c.SetInvalidParam("resetPassword", "data:time") - return + data := model.MapFromJson(strings.NewReader(props["data"])) + + userId = data["user_id"] + + timeStr = data["time"] + if len(timeStr) == 0 { + c.SetInvalidParam("resetPassword", "data:time") + return + } } - name := props["name"] - if len(name) == 0 { - c.SetInvalidParam("resetPassword", "name") + if len(userId) != 26 { + c.SetInvalidParam("resetPassword", "user_id") return } @@ -1205,15 +1237,17 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) { - c.Err = model.NewAppError("resetPassword", "The reset password link does not appear to be valid", "") - return - } + if !c.IsSystemAdmin() { + if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) { + c.Err = model.NewAppError("resetPassword", "The reset password link does not appear to be valid", "") + return + } - t, err := strconv.ParseInt(timeStr, 10, 64) - if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour - c.Err = model.NewAppError("resetPassword", "The reset link has expired", "") - return + t, err := strconv.ParseInt(timeStr, 10, 64) + if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour + c.Err = model.NewAppError("resetPassword", "The reset link has expired", "") + return + } } if result := <-Srv.Store.User().UpdatePassword(userId, model.HashPassword(newPassword)); result.Err != nil { diff --git a/api/user_test.go b/api/user_test.go index 669da4d20..baa567dec 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -228,6 +228,13 @@ func TestGetUser(t *testing.T) { ruser2, _ := Client.CreateUser(&user2, "") store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)) + team2 := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + rteam2, _ := Client.CreateTeam(&team2) + + user3 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} + ruser3, _ := Client.CreateUser(&user3, "") + store.Must(Srv.Store.User().VerifyEmail(ruser3.Data.(*model.User).Id)) + Client.LoginByEmail(team.Name, user.Email, user.Password) rId := ruser.Data.(*model.User).Id @@ -276,13 +283,27 @@ func TestGetUser(t *testing.T) { t.Log(cache_result.Data) t.Fatal("cache should be empty") } + } + if _, err := Client.GetProfiles(rteam2.Data.(*model.Team).Id, ""); err == nil { + t.Fatal("shouldn't have access") } Client.AuthToken = "" if _, err := Client.GetUser(ruser2.Data.(*model.User).Id, ""); err == nil { t.Fatal("shouldn't have accss") } + + c := &Context{} + c.RequestId = model.NewId() + c.IpAddress = "cmd_line" + UpdateRoles(c, ruser.Data.(*model.User), model.ROLE_SYSTEM_ADMIN) + + Client.LoginByEmail(team.Name, user.Email, "pwd") + + if _, err := Client.GetProfiles(rteam2.Data.(*model.Team).Id, ""); err != nil { + t.Fatal(err) + } } func TestGetAudits(t *testing.T) { |