Mercurial > gemma
changeset 5345:95dafb72a288 extented-report
Added a PATCH endpoint for /api/users/{user}
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 20 Jun 2021 04:17:53 +0200 |
parents | 7df6062a1371 |
children | 72469b713705 |
files | pkg/controllers/routes.go pkg/controllers/user.go pkg/models/user.go |
diffstat | 3 files changed, 149 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/pkg/controllers/routes.go Fri Jun 18 21:27:41 2021 +0200 +++ b/pkg/controllers/routes.go Sun Jun 20 04:17:53 2021 +0200 @@ -68,6 +68,11 @@ Handle: updateUser, })).Methods(http.MethodPut) + api.Handle("/users/{user:.+}", any(&mw.JSONHandler{ + Input: func(*http.Request) interface{} { return new(models.UserPatch) }, + Handle: patchUser, + })).Methods(http.MethodPatch) + api.Handle("/users/{user:.+}", sysAdmin(&mw.JSONHandler{ Handle: deleteUser, })).Methods(http.MethodDelete)
--- a/pkg/controllers/user.go Fri Jun 18 21:27:41 2021 +0200 +++ b/pkg/controllers/user.go Sun Jun 20 04:17:53 2021 +0200 @@ -21,6 +21,8 @@ "fmt" "log" "net/http" + "strconv" + "strings" "text/template" "time" @@ -245,6 +247,137 @@ return } +func patchUser(req *http.Request) (jr mw.JSONResult, err error) { + + user := models.UserName(mux.Vars(req)["user"]) + if !user.IsValid() { + err = mw.JSONError{ + Code: http.StatusBadRequest, + Message: "error: user invalid", + } + return + } + + s, ok := auth.GetSession(req) + if !ok { + err = mw.JSONError{ + Code: http.StatusUnauthorized, + Message: "error: not logged in", + } + return + } + + priv := s.Roles.Has("sys_admin") + + if !priv && s.User != string(user) { + err = mw.JSONError{ + Code: http.StatusUnauthorized, + Message: "error: not allowed to modify someone else", + } + return + } + + var ( + columns []string + positions []string + args []interface{} + ) + + update := func(column string, value interface{}) { + columns = append(columns, column) + positions = append(positions, "$"+strconv.Itoa(len(positions)+1)) + args = append(args, value) + } + + updateBox := func(column string, extent *models.BoundingBox) { + columns = append(columns, column) + pos := len(positions) + position := fmt.Sprintf("ST_MakeBox2D(ST_Point($%d, $%d), ST_Point($%d, $%d))", + pos+1, pos+2, pos+3, pos+4) + positions = append(positions, position) + args = append(args, extent.X1, extent.Y1, extent.X2, extent.Y2) + } + + patch := mw.JSONInput(req).(*models.UserPatch) + + if patch.User != nil && priv { + update("user", *patch.User) + } + if patch.Role != nil && priv { + update("rolname", *patch.Role) + } + if patch.Password != nil { + update("pw", *patch.Password) + } + if patch.Email != nil { + update("email_address", *patch.Email) + } + if patch.Country != nil && priv { + update("country", *patch.Country) + } + if patch.Reports != nil && priv { + update("report_reciever", *patch.Reports) + } + if patch.Extent != nil { + updateBox("map_extent", patch.Extent) + } + + var colsS, posS string + + switch len(columns) { + case 0: // Nothing to do + jr = mw.JSONResult{ + Code: http.StatusCreated, + Result: struct { + Result string `json:"result"` + }{"success"}, + } + return + case 1: // No brackets if there is only one argument. + colsS = columns[0] + posS = positions[0] + default: + colsS = "(" + strings.Join(columns, ",") + ")" + posS = "(" + strings.Join(positions, ",") + ")" + } + + stmt := fmt.Sprintf( + `UPDATE users.list_users SET %s = %s WHERE username = $%d`, + colsS, + posS, + len(positions)+1) + + args = append(args, user) + + db := mw.JSONConn(req) + + var res sql.Result + if res, err = db.ExecContext(req.Context(), stmt, args...); err != nil { + return + } + + if n, err2 := res.RowsAffected(); err2 == nil && n == 0 { + err = mw.JSONError{ + Code: http.StatusNotFound, + Message: fmt.Sprintf("Cannot find user %s.", user), + } + return + } + + if patch.User != nil && *patch.User != user { + // Running in a go routine should not be necessary. + go func() { auth.Sessions.Logout(string(user)) }() + } + + jr = mw.JSONResult{ + Code: http.StatusCreated, + Result: struct { + Result string `json:"result"` + }{"success"}, + } + return +} + func createUser(req *http.Request) (jr mw.JSONResult, err error) { user := mw.JSONInput(req).(*models.User)
--- a/pkg/models/user.go Fri Jun 18 21:27:41 2021 +0200 +++ b/pkg/models/user.go Sun Jun 20 04:17:53 2021 +0200 @@ -50,6 +50,17 @@ Extent *BoundingBox `json:"extent"` } + // UserPatch is used to send only partial updates. + UserPatch struct { + User *UserName `json:"user,omitempty"` + Role *Role `json:"role,omitempty"` + Password *string `json:"password,omitempty"` + Email *Email `json:"email,omitempty"` + Country *Country `json:"country,omitempty"` + Reports *bool `json:"reports,omitempty"` + Extent *BoundingBox `json:"extent,omitempty"` + } + // PWResetUser is send to request a password reset for a user. PWResetUser struct { User string `json:"user"`