From 0162d8ad08815b9b833fc651c7e185eab48cbbb2 Mon Sep 17 00:00:00 2001 From: Ruzette Tanyag Date: Wed, 8 Feb 2017 05:00:16 -0500 Subject: Implement GET `/users/username/{username}` endpoint for APIv4 (#5310) * added get user by username endpoint * added get user by username unit test and driver * changed username length to 22 characters max * changed Params to UserName to Username * reorganized get user by username and get user by email formatting in model/client4 --- api4/api.go | 2 +- api4/apitestlib.go | 2 +- api4/context.go | 17 +++++++++++++++ api4/params.go | 5 +++++ api4/user.go | 29 +++++++++++++++++++++++++ api4/user_test.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 2 deletions(-) (limited to 'api4') diff --git a/api4/api.go b/api4/api.go index a9f92c573..6c2faa96b 100644 --- a/api4/api.go +++ b/api4/api.go @@ -90,7 +90,7 @@ func InitApi(full bool) { BaseRoutes.Users = BaseRoutes.ApiRoot.PathPrefix("/users").Subrouter() BaseRoutes.User = BaseRoutes.Users.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter() - BaseRoutes.UserByUsername = BaseRoutes.Users.PathPrefix("/username/{username:[A-Za-z0-9_-.]+}").Subrouter() + BaseRoutes.UserByUsername = BaseRoutes.Users.PathPrefix("/username/{username:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() BaseRoutes.UserByEmail = BaseRoutes.Users.PathPrefix("/email/{email}").Subrouter() BaseRoutes.Teams = BaseRoutes.ApiRoot.PathPrefix("/teams").Subrouter() diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 2eaed4fd0..25bfe6f75 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -247,7 +247,7 @@ func GenerateTestEmail() string { } func GenerateTestUsername() string { - return "fakeuser" + model.NewId() + return "fakeuser" + model.NewRandomString(13) } func GenerateTestTeamName() string { diff --git a/api4/context.go b/api4/context.go index 7ad6f30b4..6a22b4103 100644 --- a/api4/context.go +++ b/api4/context.go @@ -359,6 +359,22 @@ func (c *Context) RequireChannelId() *Context { return c } +func (c *Context) RequireUsername() *Context { + if c.Err != nil { + return c + } + + if len(c.Params.Username) < 3 { + c.SetInvalidUrlParam("username") + } + + if len(c.Params.Username) > 22 { + c.SetInvalidUrlParam("username") + } + + return c +} + func (c *Context) RequireEmail() *Context { if c.Err != nil { return c @@ -371,3 +387,4 @@ func (c *Context) RequireEmail() *Context { return c } + diff --git a/api4/params.go b/api4/params.go index a043919fa..4de1da401 100644 --- a/api4/params.go +++ b/api4/params.go @@ -26,6 +26,7 @@ type ApiParams struct { HookId string EmojiId string Email string + Username string Page int PerPage int } @@ -71,6 +72,10 @@ func ApiParamsFromRequest(r *http.Request) *ApiParams { params.Email = val } + if val, ok := props["username"]; ok { + params.Username = val + } + if val, err := strconv.Atoi(r.URL.Query().Get("page")); err != nil { params.Page = PAGE_DEFAULT } else { diff --git a/api4/user.go b/api4/user.go index 348ccf46c..d8d071cd2 100644 --- a/api4/user.go +++ b/api4/user.go @@ -30,6 +30,7 @@ func InitUser() { BaseRoutes.Users.Handle("/login", ApiHandler(login)).Methods("POST") BaseRoutes.Users.Handle("/logout", ApiHandler(logout)).Methods("POST") + BaseRoutes.UserByUsername.Handle("", ApiSessionRequired(getUserByUsername)).Methods("GET") BaseRoutes.UserByEmail.Handle("", ApiSessionRequired(getUserByEmail)).Methods("GET") } @@ -93,6 +94,34 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) { } } +func getUserByUsername(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireUsername() + if c.Err != nil { + return + } + + // No permission check required + + var user *model.User + var err *model.AppError + + if user, err = app.GetUserByUsername(c.Params.Username); err != nil { + c.Err = err + return + } + + etag := user.Etag(utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) + + if HandleEtag(etag, "Get User", w, r) { + return + } else { + app.SanitizeProfile(user, c.IsSystemAdmin()) + w.Header().Set(model.HEADER_ETAG_SERVER, etag) + w.Write([]byte(user.ToJson())) + return + } +} + func getUserByEmail(c *Context, w http.ResponseWriter, r *http.Request) { c.RequireEmail() if c.Err != nil { diff --git a/api4/user_test.go b/api4/user_test.go index 37f251c6d..82c7f921d 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -133,6 +133,70 @@ func TestGetUser(t *testing.T) { } } +func TestGetUserByUsername(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + defer TearDown() + Client := th.Client + + user := th.BasicUser + + ruser, resp := Client.GetUserByUsername(user.Username, "") + CheckNoError(t, resp) + CheckUserSanitization(t, ruser) + + if ruser.Email != user.Email { + t.Fatal("emails did not match") + } + + ruser, resp = Client.GetUserByUsername(user.Username, resp.Etag) + CheckEtag(t, ruser, resp) + + _, resp = Client.GetUserByUsername(GenerateTestUsername(), "") + CheckNotFoundStatus(t, resp) + + _, resp = Client.GetUserByUsername(model.NewRandomString(25), "") + CheckBadRequestStatus(t, resp) + + // Check against privacy config settings + emailPrivacy := utils.Cfg.PrivacySettings.ShowEmailAddress + namePrivacy := utils.Cfg.PrivacySettings.ShowFullName + defer func() { + utils.Cfg.PrivacySettings.ShowEmailAddress = emailPrivacy + utils.Cfg.PrivacySettings.ShowFullName = namePrivacy + }() + utils.Cfg.PrivacySettings.ShowEmailAddress = false + utils.Cfg.PrivacySettings.ShowFullName = false + + ruser, resp = Client.GetUserByUsername(user.Username, "") + CheckNoError(t, resp) + + if ruser.Email != "" { + t.Fatal("email should be blank") + } + if ruser.FirstName != "" { + t.Fatal("first name should be blank") + } + if ruser.LastName != "" { + t.Fatal("last name should be blank") + } + + Client.Logout() + _, resp = Client.GetUserByUsername(user.Username, "") + CheckUnauthorizedStatus(t, resp) + + // System admins should ignore privacy settings + ruser, resp = th.SystemAdminClient.GetUserByUsername(user.Username, resp.Etag) + if ruser.Email == "" { + t.Fatal("email should not be blank") + } + if ruser.FirstName == "" { + t.Fatal("first name should not be blank") + } + if ruser.LastName == "" { + t.Fatal("last name should not be blank") + } +} + func TestGetUserByEmail(t *testing.T) { th := Setup().InitBasic().InitSystemAdmin() defer TearDown() -- cgit v1.2.3-1-g7c22