From 36f43edba3481a19476943942bff1ab53cc14e0f Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Wed, 1 Feb 2017 16:13:16 -0500 Subject: Implement PUT /users/{user_id}/roles endpoint for APIv4 (#5238) --- api4/apitestlib.go | 16 +++++++++++++++- api4/user.go | 30 ++++++++++++++++++++++++++++++ api4/user_test.go | 24 ++++++++++++++++++++++++ model/client4.go | 11 +++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/api4/apitestlib.go b/api4/apitestlib.go index 44a730621..6b129dd8f 100644 --- a/api4/apitestlib.go +++ b/api4/apitestlib.go @@ -6,6 +6,7 @@ package api4 import ( "net/http" "reflect" + "runtime/debug" "strconv" "strings" "testing" @@ -213,10 +214,12 @@ func CheckUserSanitization(t *testing.T, user *model.User) { func CheckEtag(t *testing.T, data interface{}, resp *model.Response) { if !reflect.ValueOf(data).IsNil() { + debug.PrintStack() t.Fatal("etag data was not nil") } if resp.StatusCode != http.StatusNotModified { + debug.PrintStack() t.Log("actual: " + strconv.Itoa(resp.StatusCode)) t.Log("expected: " + strconv.Itoa(http.StatusNotModified)) t.Fatal("wrong status code for etag") @@ -225,17 +228,20 @@ func CheckEtag(t *testing.T, data interface{}, resp *model.Response) { func CheckNoError(t *testing.T, resp *model.Response) { if resp.Error != nil { - t.Fatal(resp.Error) + debug.PrintStack() + t.Fatal("Expected no error, got " + resp.Error.Error()) } } func CheckForbiddenStatus(t *testing.T, resp *model.Response) { if resp.Error == nil { + debug.PrintStack() t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusForbidden)) return } if resp.StatusCode != http.StatusForbidden { + debug.PrintStack() t.Log("actual: " + strconv.Itoa(resp.StatusCode)) t.Log("expected: " + strconv.Itoa(http.StatusForbidden)) t.Fatal("wrong status code") @@ -244,11 +250,13 @@ func CheckForbiddenStatus(t *testing.T, resp *model.Response) { func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) { if resp.Error == nil { + debug.PrintStack() t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusUnauthorized)) return } if resp.StatusCode != http.StatusUnauthorized { + debug.PrintStack() t.Log("actual: " + strconv.Itoa(resp.StatusCode)) t.Log("expected: " + strconv.Itoa(http.StatusUnauthorized)) t.Fatal("wrong status code") @@ -257,11 +265,13 @@ func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) { func CheckNotFoundStatus(t *testing.T, resp *model.Response) { if resp.Error == nil { + debug.PrintStack() t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusNotFound)) return } if resp.StatusCode != http.StatusNotFound { + debug.PrintStack() t.Log("actual: " + strconv.Itoa(resp.StatusCode)) t.Log("expected: " + strconv.Itoa(http.StatusNotFound)) t.Fatal("wrong status code") @@ -270,11 +280,13 @@ func CheckNotFoundStatus(t *testing.T, resp *model.Response) { func CheckBadRequestStatus(t *testing.T, resp *model.Response) { if resp.Error == nil { + debug.PrintStack() t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusBadRequest)) return } if resp.StatusCode != http.StatusBadRequest { + debug.PrintStack() t.Log("actual: " + strconv.Itoa(resp.StatusCode)) t.Log("expected: " + strconv.Itoa(http.StatusBadRequest)) t.Fatal("wrong status code") @@ -283,11 +295,13 @@ func CheckBadRequestStatus(t *testing.T, resp *model.Response) { func CheckErrorMessage(t *testing.T, resp *model.Response, errorId string) { if resp.Error == nil { + debug.PrintStack() t.Fatal("should have errored with message:" + errorId) return } if resp.Error.Id != errorId { + debug.PrintStack() t.Log("actual: " + resp.Error.Id) t.Log("expected: " + errorId) t.Fatal("incorrect error message") diff --git a/api4/user.go b/api4/user.go index 9d38df1a1..f68d01d33 100644 --- a/api4/user.go +++ b/api4/user.go @@ -18,6 +18,7 @@ func InitUser() { BaseRoutes.Users.Handle("", ApiHandler(createUser)).Methods("POST") BaseRoutes.User.Handle("", ApiSessionRequired(getUser)).Methods("GET") BaseRoutes.User.Handle("", ApiSessionRequired(updateUser)).Methods("PUT") + BaseRoutes.User.Handle("/roles", ApiSessionRequired(updateUserRoles)).Methods("PUT") BaseRoutes.Users.Handle("/login", ApiHandler(login)).Methods("POST") BaseRoutes.Users.Handle("/logout", ApiHandler(logout)).Methods("POST") @@ -109,6 +110,35 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) { } } +func updateUserRoles(c *Context, w http.ResponseWriter, r *http.Request) { + c.RequireUserId() + if c.Err != nil { + return + } + + props := model.MapFromJson(r.Body) + + newRoles := props["roles"] + if !model.IsValidUserRoles(newRoles) { + c.SetInvalidParam("roles") + return + } + + if !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_ROLES) { + c.SetPermissionError(model.PERMISSION_MANAGE_ROLES) + return + } + + if _, err := app.UpdateUserRoles(c.Params.UserId, newRoles); err != nil { + c.Err = err + return + } else { + c.LogAuditWithUserId(c.Params.UserId, "roles="+newRoles) + } + + ReturnStatusOK(w) +} + func login(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) diff --git a/api4/user_test.go b/api4/user_test.go index c55c70b36..713e0268b 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -182,3 +182,27 @@ func TestUpdateUser(t *testing.T) { _, resp = th.SystemAdminClient.UpdateUser(user) CheckNoError(t, resp) } + +func TestUpdateUserRoles(t *testing.T) { + th := Setup().InitBasic().InitSystemAdmin() + Client := th.Client + SystemAdminClient := th.SystemAdminClient + + _, resp := Client.UpdateUserRoles(th.SystemAdminUser.Id, model.ROLE_SYSTEM_USER.Id) + CheckForbiddenStatus(t, resp) + + _, resp = SystemAdminClient.UpdateUserRoles(th.BasicUser.Id, model.ROLE_SYSTEM_USER.Id) + CheckNoError(t, resp) + + _, resp = SystemAdminClient.UpdateUserRoles(th.BasicUser.Id, model.ROLE_SYSTEM_USER.Id+" "+model.ROLE_SYSTEM_ADMIN.Id) + CheckNoError(t, resp) + + _, resp = SystemAdminClient.UpdateUserRoles(th.BasicUser.Id, "junk") + CheckBadRequestStatus(t, resp) + + _, resp = SystemAdminClient.UpdateUserRoles("junk", model.ROLE_SYSTEM_USER.Id) + CheckBadRequestStatus(t, resp) + + _, resp = SystemAdminClient.UpdateUserRoles(model.NewId(), model.ROLE_SYSTEM_USER.Id) + CheckBadRequestStatus(t, resp) +} diff --git a/model/client4.go b/model/client4.go index 6752a4b60..f0adfd382 100644 --- a/model/client4.go +++ b/model/client4.go @@ -212,6 +212,17 @@ func (c *Client4) UpdateUser(user *User) (*User, *Response) { } } +// UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles. +func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) { + requestBody := map[string]string{"roles": roles} + if r, err := c.DoApiPut(c.GetUserRoute(userId)+"/roles", MapToJson(requestBody)); err != nil { + return false, &Response{StatusCode: r.StatusCode, Error: err} + } else { + defer closeBody(r) + return CheckStatusOK(r), BuildResponse(r) + } +} + // Team Section // CreateTeam creates a team in the system based on the provided team struct. -- cgit v1.2.3-1-g7c22